dataMetaPii 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,66 @@
1
+ # PII Master Registry DSL grammar
2
+ # to debug, uncomment next line and add this anywhere: &{|s| debugger; true }
3
+ #require 'ruby-debug'
4
+
5
+ grammar PiiRegistry
6
+
7
+ # Load the commons from the DataMetaParse which lives in:
8
+ # binary: http://FIXME
9
+ # documentation: http://FIXME
10
+ # source: https://FIXME
11
+ #include DataMetaCommonsRoot
12
+ include PiiCommons
13
+
14
+ # Master rule
15
+ rule piiDef
16
+ piiWss? verDef:version piiWss? fields:piiFields piiWs?
17
+ end
18
+
19
+ # PII fields definitions - not much, just a list of PII keys with attributes one or more.
20
+ rule piiFields
21
+ piiAttrbs+ {
22
+ # the type method makes it simple to distinguish between the rules; the Treetop standard API does not provide such facility yet
23
+ def type; 'piiFields' end
24
+ }
25
+ end
26
+
27
+ # The PII key with attributes (one or more), i.e. key=value, optionally ornamented/separated by comments, whitespaces including newlines
28
+ rule piiAttrbs
29
+ piiWs? fldKey:piiKey piiWs? '{' piiWs? attrbLst:attrbList piiWs? '}' {
30
+ def type
31
+ 'piiAttrbs'
32
+ end
33
+ def pk; fldKey.text_value end
34
+ }
35
+ end
36
+
37
+ # most complicated rule in this grammar: attribute list, i.e. key=value, optionally ornamented/separated by comments, whitespaces including newlines
38
+ rule attrbList
39
+ attrb (',' w attrb)* {
40
+ def type; 'attrbList' end
41
+
42
+ # Main method for the AST traversal to collect the attributes
43
+ def attrbs
44
+ DataMetaPii::digAstElsType('keySym', elements)
45
+ end
46
+ }
47
+ end
48
+
49
+ # attribute: for now, kvSym only
50
+ rule attrb
51
+ kvs:kvSym {
52
+ def type; 'attrb' end
53
+ }
54
+ end
55
+
56
+ # Key-Value, where value is a symbol
57
+ # surrounding quotes.
58
+ rule kvSym
59
+ key:symbol w '=' w val:symbol
60
+ {
61
+ def type; 'keySym' end
62
+ def k; key.text_value end
63
+ def v; val.text_value end
64
+ }
65
+ end
66
+ end
@@ -0,0 +1,577 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require 'dataMetaDom'
4
+ require 'dataMetaDom/ver'
5
+ require 'treetop'
6
+ require 'dataMetaParse'
7
+ require 'bigdecimal'
8
+ require 'set'
9
+ require 'logger'
10
+ require 'erb'
11
+ require 'fileutils'
12
+ require 'pathname'
13
+
14
+ =begin rdoc
15
+ PII support for DataMeta
16
+
17
+ For command line details either check the new method's source or the README file, the usage section.
18
+
19
+ ""
20
+ =end
21
+ module DataMetaPii
22
+ # Logger to use for this module
23
+ L = Logger.new("#{File.basename(__FILE__)[0..-4]}.log", 0, 10_000_000)
24
+
25
+ L.datetime_format = '%Y-%m-%d %H:%M:%S'
26
+
27
+ # Determine the gem root in the local Filesystem
28
+ GEM_ROOT = Pathname.new(File.join(File.dirname(__FILE__), '..')).cleanpath
29
+ # Advance further to determine the root of the grammars
30
+ GRAMMAR_ROOT = File.join(GEM_ROOT, 'grammar')
31
+
32
+ # Current version
33
+ VERSION = '1.0.1'
34
+ # Load base rules from the DataMeta Parsing Commons
35
+ BASE_RULES = DataMetaParse.loadBaseRulz
36
+ # Load PII specific common rules from this very gem's codebase
37
+ PII_COMMONS = Treetop.load(File.join(GRAMMAR_ROOT, 'piiCommons'))
38
+ # Load the PII Registry grammar
39
+ REGISTRY = Treetop.load(File.join(GRAMMAR_ROOT, 'registry'))
40
+ # Load the PII Applications Link grammar
41
+ APP_LINK = Treetop.load(File.join(GRAMMAR_ROOT, 'appLink'))
42
+ L.info %<
43
+ Loaded base rules: #{DataMetaPii::BASE_RULES}
44
+ Loaded PII Commons Rules: #{DataMetaPii::PII_COMMONS.inspect}
45
+ Loaded Regstry Rules: #{DataMetaPii::REGISTRY}
46
+ Loaded AppLink Rules: #{DataMetaPii::APP_LINK}
47
+ >
48
+
49
+ # Create all parsers, it's not expensive. First the Registry (Abstract Defs) grammar parser:
50
+ REGISTRY_PARSER = PiiRegistryParser.new
51
+ # And the App Link grammar parser:
52
+ APP_LINK_PARSER = PiiAppLinkParser.new
53
+
54
+ # Value Object Class Key
55
+ VO_CLASS_KEY = :voClass
56
+ # Value Reference Key
57
+ REF_KEY = :ref
58
+ # Constant value key
59
+ CONST_KEY = :const
60
+ # One step for indentation of the output
61
+ INDENT = ' ' * 4
62
+ # Constant Data type: string
63
+ STR_CONST_DT = :str
64
+ # Constant Data type:
65
+ INT_CONST_DT = :int
66
+ # Constant Data type:
67
+ DECIMAL_CONST_DT = :dec
68
+
69
+ # AST Node type
70
+ ATTRB_LIST_NODE_TYPE = :attrbList
71
+ # AST Node type
72
+ ATTRB_DEF_NODE_TYPE = :attrbDef
73
+
74
+ # Module-holder of the impact constants
75
+ # In a language that support enums, that'd be an enum.
76
+ module Impact
77
+ # Impact Level: Public
78
+ PUBLIC = :public
79
+ # Impact Level: Confidential
80
+ CONFIDENTIAL = :confidential
81
+ # Impact Level: Internal
82
+ INTERNAL = :internal
83
+ # Impact Level: Restricted
84
+ RESTRICTED = :restricted
85
+ # Impact Level: Taboo
86
+ TABOO = :taboo
87
+ end
88
+
89
+ # Supported export (generation) formats
90
+ module ExportFmts
91
+ # Code generation format: Java
92
+ JAVA = :java
93
+ # Code generation format: Scala
94
+ SCALA = :scala
95
+ # Code generation format: Python
96
+ PYTHON = :python
97
+ # Code generation format: JSON
98
+ JSON = :json
99
+ end
100
+
101
+ # Collect all the constants from the module ExportFmts, that's all supported formats
102
+ ALL_FMTS = ExportFmts.constants.map{|c| ExportFmts.const_get(c)}
103
+
104
+ # All supported impacts collected from the module Impact
105
+ ALL_IMPACTS = Impact.constants.map{|c| Impact.const_get(c)}
106
+
107
+ # PII field definition scope: master vs application
108
+ module Scope
109
+ # Abstract fields scope
110
+ ABSTRACT = :abstract
111
+ # Application scope
112
+ APPLICATION = :app
113
+ end
114
+
115
+ # All supported scopes collected from the module Scope
116
+ ALL_SCOPES = Scope.constants.map{|c| Scope.const_get(c)}
117
+
118
+ =begin rdoc
119
+ PII Registry Key Value Object, encapsulates the PII Registry Key with attributes
120
+ @!attribute [r] key
121
+ @return [String] unique key for the given enum
122
+
123
+ @!attribute [r] level
124
+ @return [Symbol] the impact level
125
+
126
+ =end
127
+ class RegKeyVo
128
+ # The map key for the level
129
+ LEVEL = 'level'
130
+
131
+ attr_accessor :key, :level, :attrs
132
+ =begin rdoc
133
+ Creates an instance for the given parameters, see the properties with the same names.
134
+ =end
135
+ def initialize(key, attrs)
136
+ @key, @attrs = key, attrs
137
+ # single the level out:
138
+ levelSpec = attrs[LEVEL]
139
+
140
+ raise ArgumentError, %<Impact level missing or empty in #{@attrs.inspect}> unless levelSpec && !levelSpec.empty?
141
+
142
+ @level = levelSpec.to_sym
143
+
144
+ raise ArgumentError, %<Unsupported Impact Level #{@attrs.inspect}. Supported levels are: #{
145
+ ALL_IMPACTS.map(&:to_s).join(', ')}> unless ALL_IMPACTS.member?(@level)
146
+
147
+ raise ArgumentError, %<Invalid PII key: "#{@key}"> unless @key =~ /^\w+$/
148
+ end
149
+
150
+ # textual representation of this instance
151
+ def to_s; "#{key}(#{@level})" end
152
+
153
+ # Builds a textual tree image for logging and/or console output
154
+ def to_tree_image(indent = '')
155
+ next_ident = indent + DataMetaPii::INDENT
156
+ %<#{indent}#{@key}:
157
+ #{next_ident}#{@attrs.keys.map{|k| "#{k}=#{@attrs[k]}"}.join("\n#{next_ident}")}>
158
+ end
159
+ end
160
+
161
+ # Versioned Value Object common ancestor class
162
+ class VersionedVo
163
+ attr_accessor :ver
164
+ def initialize(verDef)
165
+ @ver = case verDef.class
166
+ when String.class
167
+ DataMetaDom::SemVer.new(verDef)
168
+ when DataMetaDom::SemVer.class
169
+ verDef
170
+ else
171
+ raise ArgumentError, %<Unsupported verDefsion type: #{verDef.class} == #{verDef.inspect}>
172
+ end
173
+
174
+ end
175
+ end
176
+
177
+ # Registry Value Object - a wrap around the PII Key to attributes map
178
+ class RegVo < VersionedVo
179
+ attr_accessor :keyVos
180
+ def initialize(ver, vos)
181
+ super(ver)
182
+ @keyVos = vos
183
+ end
184
+
185
+ # Builds a textual tree image for logging and/or console output
186
+ def to_tree_image(indent = '')
187
+ next_ident = indent + DataMetaPii::INDENT
188
+ %<#{@keyVos.keys.map{|k| @keyVos[k].to_tree_image(next_ident)}.join("\n")}>
189
+ end
190
+ end
191
+
192
+ # Attribute definition ancestor - should be abstract but Ruby does not let define abstract classes easily.
193
+ class AlAttrDef
194
+ attr_accessor :key, :val
195
+ def initialize(key, val)
196
+ @key, @val = key, val
197
+ end
198
+ end
199
+
200
+ # Defines a value for a VO Class
201
+ class AlAttrVoClass < AlAttrDef
202
+ def initialize(voClass)
203
+ super(VO_CLASS_KEY, voClass)
204
+ raise ArgumentError, %<Wrong type for VO Class: #{voClass.class}=#{voClass.inspect}> unless voClass.is_a?(String)
205
+ end
206
+ # String representation
207
+ def to_s; %<#{self.class.name}{#{val}}> end
208
+ end
209
+ # Attribute reference
210
+ class AttrRef
211
+ attr_accessor :key
212
+ def initialize(key); @key = key end
213
+ # String representation
214
+ def to_s; %<#{self.class.name}{#{@key}}> end
215
+ end
216
+
217
+ # Attribute section with constants, references and a VO class optionally defined
218
+ class AttrSect
219
+ attr_reader :key, :refs, :consts, :voClass
220
+ def initialize(key)
221
+ @key = key
222
+ @refs = Hash.new(*[])
223
+ @consts = Hash.new(*[])
224
+ @voClass = nil
225
+ end
226
+
227
+ # Add a new value to this section, depending on the incoming type
228
+ def +(val)
229
+ case val
230
+ when AlAttrVoClass
231
+ raise RuntimeError, %<Attempt to redefine VO Class on "#{@key}"> if @voClass
232
+ @voClass = val
233
+
234
+ when AttrRef
235
+ raise RuntimeError,
236
+ %<Reference to "#{val.key}" specified more than once on the attribute section "#{
237
+ @key}"> if @refs.has_key?(val.key.to_sym)
238
+
239
+ @refs[val.key.to_sym] = val
240
+
241
+ when AlAttrStr, AlAttrInt, AlAttrDec
242
+ raise RuntimeError,
243
+ %<Constant "#{val.key}" specified more than once on "#{@key}"> if @consts.has_key?(val.key.to_sym)
244
+
245
+ @consts[val.key.to_sym] = val
246
+ else
247
+ raise ArgumentError, %<Unsupported attribute type #{val.class} = #{val.inspect}>
248
+ end
249
+ end
250
+
251
+ # String representation
252
+ def to_s
253
+ %<#{self.class.name}{VO=#{@voClass}, Refs=#{@refs.inspect}, Const=#{@consts.inspect}}>
254
+ end
255
+ end
256
+
257
+ # Defines a value for a String constant
258
+ class AlAttrStr < AlAttrDef
259
+ def initialize(key, val)
260
+ super(key, val)
261
+ raise ArgumentError, %<Wrong type for a String: #{val.class}=#{val.inspect}> unless val.is_a?(String)
262
+ end
263
+ # String representation
264
+ def to_s; %<#{self.class.name}{#{key}=#{val.inspect}}> end
265
+ end
266
+
267
+ # Defines a value for an Int constant
268
+ class AlAttrInt < AlAttrDef
269
+ def initialize(key, val)
270
+ super(key, val)
271
+ raise ArgumentError, %<Wrong type for an Int: #{val.class}=#{val.inspect}> unless val.is_a?(Fixnum)
272
+ end
273
+ # String representation
274
+ def to_s; %<#{self.class.name}{#{key}=#{val}}> end
275
+ end
276
+
277
+ # Defines a value for an Decimal constant
278
+ class AlAttrDec < AlAttrDef
279
+ def initialize(key, val)
280
+ super(key, val)
281
+ raise ArgumentError, %<Wrong type for an Decimal: #{val.class}=#{val.inspect}> unless val.is_a?(BigDecimal)
282
+ end
283
+ # String representation
284
+ def to_s; %<#{self.class.name}{#{key}=#{val.to_s('F')}}> end
285
+ end
286
+
287
+ # AppLink Attribute VO
288
+ class AlAttrVo # - FIXME - not needed?
289
+ attr_accessor :key, :attrs
290
+ =begin rdoc
291
+ Creates an instance for the given parameters, see the properties with the same names.
292
+ =end
293
+ def initialize(key, attrs)
294
+ @key, @attrs = key, attrs
295
+ end
296
+ end
297
+
298
+ # Application Link VO - FIXME - not needed?
299
+ class PiiAlVo
300
+ attr_accessor :key, :attrs
301
+ =begin rdoc
302
+ Creates an instance for the given parameters, see the properties with the same names.
303
+ =end
304
+ def initialize(key, attrs)
305
+ @key, @attrs = key, attrs
306
+ end
307
+
308
+ end
309
+
310
+ =begin
311
+ AppLink Attribute Division VO, i.e. full Application Link Definition
312
+
313
+ @!attribute [rw] sectVos
314
+ @return [Hash] the Hash keyed by the application name symbol pointing to a Hash keyed by the Abstract
315
+ PII field key pointing to the instance of AttrSect.
316
+
317
+ @!attribute [rw] reusables
318
+ @return [Hash] +nil+ or the Hash keyed by reusable var name pointing to the instance of AttrSect
319
+ PII field key pointing to the instance of AttrSect.
320
+ =end
321
+ class AppLink < VersionedVo
322
+
323
+ # Use same ident as the main class:
324
+ INDENT = DataMetaPii::INDENT
325
+
326
+ attr_accessor :sectVos, :reusables
327
+ =begin rdoc
328
+ Creates an instance for the given parameters, see the properties with the same names.
329
+ =end
330
+ def initialize(ver, vos, reusables = nil)
331
+ super(ver)
332
+ @sectVos, @reusables = vos, reusables
333
+ end
334
+
335
+ # Resolves reusable variable references, reports errors
336
+ def resolveRefs()
337
+ raise ArgumentError, 'Sections are not set yet on this instance' unless @sectVos
338
+ return self unless @reusables # no reusables defined, all vars should be accounted for
339
+ @reusables.keys.each { |uk|
340
+ ref = @reusables[uk].refs
341
+ ref.keys.each { |rk|
342
+ raise ArgumentError, %<Reusable "#{uk}" references "#{rk}" which is not defined> unless @reusables[rk]
343
+ }
344
+ }
345
+ @sectVos.keys.each { |ak|
346
+ app = @sectVos[ak]
347
+ app.keys.each { |sk|
348
+ sect = app[sk]
349
+ sect.refs.keys.each { |rk|
350
+ raise ArgumentError, %<In the app "#{ak}": the field "#{sk}" references "#{
351
+ rk}" which is not defined> unless @reusables[rk]
352
+ }
353
+ }
354
+
355
+ }
356
+ self
357
+ end
358
+
359
+ # String representation
360
+ def to_s
361
+ %<#{self.class.name}{apps=#{@sectVos.inspect}>
362
+ end
363
+ end
364
+
365
+ # Builds the AppLink CST from the given Registry Grammar Parser's AST
366
+ def buildAlCst(ast, logString = nil)
367
+ # parse the ad first
368
+ reusables = {}
369
+ log = -> (what) {logString << what if logString}
370
+
371
+ log.call("AppLink CST\n")
372
+ if ast.ad&.elements
373
+ log.call("#{INDENT}#{ast.ad.type}:#{ast.ad.a.elements.size}\n")
374
+ ast.ad.a.elements.each { |as| # attrbSection
375
+ raise RuntimeError, %<The attributes set "#{as.pk} is defined more than once"> if reusables.has_key?(as.pk.to_sym)
376
+ keyVals = digAstElsType(ATTRB_DEF_NODE_TYPE, as.a.elements)
377
+ log.call(%<#{INDENT * 2}#{as.pk}:#{keyVals.size}\n>)
378
+ aSect = AttrSect.new(as.pk.to_sym)
379
+ keyVals.each { |kv|
380
+ kvVal = kv.val
381
+ log.call(%<#{INDENT * 3}#{kv.nodeType}:#{kvVal}\\#{kvVal.class}>)
382
+ log.call(" (#{kv.node.key}==#{kv.node.nodeVal})//#{kv.node.type}\\#{kv.node.dataType}") if(kv.nodeType == CONST_KEY)
383
+ log.call("\n")
384
+ # noinspection RubyCaseWithoutElseBlockInspection
385
+ aSect + case kv.nodeType # else case is caught by the AST parser
386
+ when CONST_KEY
387
+ # noinspection RubyCaseWithoutElseBlockInspection
388
+ klass = case kv.node.dataType # else case is caught by the AST parser
389
+ when STR_CONST_DT
390
+ AlAttrStr
391
+ when DECIMAL_CONST_DT
392
+ AlAttrDec
393
+ when INT_CONST_DT
394
+ AlAttrInt
395
+ end
396
+ klass.new(kv.node.key.to_sym, kv.node.nodeVal)
397
+ when REF_KEY
398
+ AttrRef.new(kvVal)
399
+ when VO_CLASS_KEY
400
+ AlAttrVoClass.new(kvVal)
401
+ end
402
+ }
403
+ reusables[as.pk.to_sym] = aSect
404
+ }
405
+ log.call(%<#{INDENT * 2}#{reusables}\n>)
406
+ else
407
+ log.call("#{INDENT * 2}No reusables\n")
408
+ end
409
+ apps = {}
410
+ if ast.al&.elements
411
+ log.call("#{INDENT}#{ast.al.type}:#{ast.al.a.elements.size}\n")
412
+ ast.al.a.elements.each { |as| # appLinkApps
413
+ log.call(%<#{INDENT * 3}#{as.ak}:#{as.a.elements.size}\n>)
414
+ appKey = as.ak.to_sym
415
+ raise RuntimeError, %<Application "#{appKey}" defined more than once> if apps.has_key?(appKey)
416
+ attrbs = {}
417
+ as.a.elements.each { |ala| #appLinkAttrbs
418
+ alis = digAstElsType(DataMetaPii::ATTRB_DEF_NODE_TYPE, ala.a.elements)
419
+ log.call(%<#{INDENT * 4}#{ala.pk} (#{ala.type}): #{alis.size}\n>)
420
+ aSect = AttrSect.new(ala.pk.to_sym)
421
+ alis.each { |ali|
422
+ kvVal = ali.val
423
+ log.call(%<#{INDENT * 5}#{ali.nodeType}: >)
424
+ if ali.nodeType == DataMetaPii::CONST_KEY
425
+ log.call(%<(#{ali.node.dataType}):: #{ali.node.key}=#{ali.node.nodeVal}>)
426
+ else
427
+ log.call(%<#{ali.val}>)
428
+ end
429
+ # noinspection RubyCaseWithoutElseBlockInspection
430
+ aSect + case ali.nodeType # else case is caught by the AST parser
431
+ when CONST_KEY
432
+ # noinspection RubyCaseWithoutElseBlockInspection
433
+ klass = case ali.node.dataType # else case is caught by the AST parser
434
+ when STR_CONST_DT
435
+ AlAttrStr
436
+ when DECIMAL_CONST_DT
437
+ AlAttrDec
438
+ when INT_CONST_DT
439
+ AlAttrInt
440
+ end
441
+ klass.new(ali.node.key.to_sym, ali.node.nodeVal)
442
+ when REF_KEY
443
+ AttrRef.new(kvVal)
444
+ when VO_CLASS_KEY
445
+ AlAttrVoClass.new(kvVal)
446
+ end
447
+ log.call("\n")
448
+ }
449
+ attrbs[ala.pk.to_sym] = aSect
450
+ }
451
+ log.call(%<#{INDENT}#{attrbs}\n>)
452
+ apps[appKey] = attrbs
453
+ }
454
+ else
455
+ raise ArgumentError, 'No Applink Division'
456
+ end
457
+ AppLink.new(ast.verDef.ver, apps, reusables)
458
+ end
459
+
460
+ # Builds the Registry CST from the given Registry Grammar Parser's AST
461
+ def buildRegCst(ast)
462
+
463
+ resultMap = {}
464
+
465
+ ast.fields.elements.each { |f|
466
+ fKey = f.pk
467
+ raise ArgumentError, %<The PII field #{fKey} is defined more than once> if resultMap.keys.member?(fKey)
468
+ attrs = {}
469
+ f.attrbLst.attrbs.each { |a|
470
+ raise ArgumentError, %<Attribute "#{a.k}" is defined more than once for #{fKey}> if attrs.keys.member?(a.k)
471
+ attrs[a.k] = a.v
472
+ }
473
+ attrVo = RegKeyVo.new(fKey, attrs)
474
+ resultMap[fKey] = attrVo
475
+ }
476
+
477
+ RegVo.new(ast.verDef.ver, resultMap)
478
+ end
479
+
480
+ # Helper method for the AST traversal to collect the attributes
481
+ # Because of the Treetop AST design, can not just flatten the elements and select of those of the needed type in one call, hence the tree traversal
482
+ def digAstElsType(type, els, result=[])
483
+ if els.nil? # is it a leaf?
484
+ nil # not a leaf - return nil
485
+ else
486
+ els.each { |e|
487
+ if e.respond_to?(:type) && e.type == type # actual attribute Key/Value?
488
+ result << e # add it
489
+ else
490
+ digAstElsType(type, e.elements, result) # dig deeper into the AST
491
+ end
492
+ }
493
+ result
494
+ end
495
+ end
496
+
497
+ def errNamespace(outFmt)
498
+ raise ArgumentError, %<For output format "#{outFmt}", the Namespace is required>
499
+ end
500
+
501
+ # API method: generate the PII code
502
+ def genCode(scope, outFmt, outDirName, source, namespace = nil)
503
+ # noinspection RubyCaseWithoutElseBlockInspection
504
+ codeIndent = ' ' * 2
505
+ raise ArgumentError, %Q<Unsupported scope definition "#{scope}", supported scopes are: #{
506
+ DataMetaPii::ALL_SCOPES.map(&:to_s).join(', ')}> unless ALL_SCOPES.member?(scope)
507
+
508
+ raise ArgumentError, %Q<Unsupported output format definition "#{outFmt}", supported formats are: #{
509
+ DataMetaPii::ALL_FMTS.map(&:to_s).join(', ')}> unless ALL_FMTS.member?(outFmt)
510
+
511
+ raise ArgumentError, %<For safety purposes, absolute path names like "#{
512
+ outDirName}" are not supported> if outDirName.start_with?('/')
513
+
514
+ raise ArgumentError, %<The output dir "#{outDirName}" is not a directory> unless File.directory?(outDirName)
515
+
516
+ # noinspection RubyCaseWithoutElseBlockInspection
517
+ case scope # else case caught up there, on argument validation
518
+ when Scope::ABSTRACT
519
+ reg = DataMetaPii.buildRegCst(DataMetaParse.parse(REGISTRY_PARSER, source))
520
+ L.info(%<PII Registry:
521
+ #{reg.to_tree_image(INDENT)}>)
522
+ tmpl = ERB.new(IO.read(File.join(GEM_ROOT, 'tpl', outFmt.to_s, 'master.erb')), $SAFE, '%<>>')
523
+ className = "PiiAbstractDef_#{reg.ver.toVarName}"
524
+ # noinspection RubyCaseWithoutElseBlockInspection
525
+ case outFmt
526
+ when ExportFmts::JAVA, ExportFmts::SCALA
527
+ errNamespace(outFmt) unless namespace.is_a?(String) && !namespace.empty?
528
+ pkgDir = namespace.gsub('.', '/')
529
+ classDest =File.join(outDirName, pkgDir)
530
+ FileUtils.mkpath classDest
531
+
532
+ IO.write(File.join(classDest, "#{className}.#{outFmt}"),
533
+ tmpl.result(binding).gsub(/\n\n+/, "\n\n"), mode: 'wb') # collapse multiple lines in 2
534
+
535
+ when ExportFmts::JSON
536
+ IO.write(File.join(outDirName, "#{className}.#{outFmt}"),
537
+ tmpl.result(binding).gsub(/\n\n+/, "\n\n"), mode: 'wb') # collapse multiple lines in 2
538
+
539
+ when ExportFmts::PYTHON
540
+ pkgDir = namespace.gsub('.', '_')
541
+ classDest =File.join(outDirName, pkgDir)
542
+ FileUtils.mkpath classDest
543
+ IO.write(File.join(classDest, "#{className[0].downcase + className[1..-1]}.py"),
544
+ tmpl.result(binding).gsub(/\n\n+/, "\n\n"), mode: 'wb') # collapse multiple lines in 2
545
+ IO.write(File.join(classDest, '__init__.py'), %q<
546
+ # see https://docs.python.org/3/library/pkgutil.html
547
+ # without this, Python will have trouble finding packages that share some common tree off the root
548
+ from pkgutil import extend_path
549
+ __path__ = extend_path(__path__, __name__)
550
+
551
+ >, mode: 'wb')
552
+ end
553
+
554
+ when DataMetaPii::Scope::APPLICATION
555
+ raise NotImplementedError, 'There is no generic code gen for AppLink, each app/svc should have their own'
556
+
557
+ end
558
+ end
559
+
560
+ =begin
561
+ Turns the given text into the instance of the AppLink object.
562
+ =end
563
+ def parseAppLink(source)
564
+
565
+ piiAppLinkParser = PiiAppLinkParser.new
566
+ ast = DataMetaParse.parse(piiAppLinkParser, source)
567
+ raise SyntaxError, 'AppLink parse unsuccessful' unless ast
568
+ if ast.is_a?(DataMetaParse::Err)
569
+ raise %<#{ast.parser.failure_line}
570
+ ast.parser.failure_reason}>
571
+ end
572
+
573
+ DataMetaPii.buildAlCst(ast).resolveRefs
574
+ end
575
+
576
+ module_function :digAstElsType, :buildRegCst, :buildAlCst, :genCode, :errNamespace, :parseAppLink
577
+ end