dataMetaDom 1.0.0
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.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/History.md +6 -0
- data/PostInstall.txt +1 -0
- data/README.md +137 -0
- data/Rakefile +13 -0
- data/bin/dataMetaGvExport.rb +97 -0
- data/bin/dataMetaMySqlDdl.rb +27 -0
- data/bin/dataMetaOracleDdl.rb +26 -0
- data/bin/dataMetaPojo.rb +27 -0
- data/bin/dataMetaReVersion.rb +66 -0
- data/bin/dataMetaSameFullJ.rb +13 -0
- data/bin/dataMetaSameIdJ.rb +12 -0
- data/lib/dataMetaDom/converter.rb +70 -0
- data/lib/dataMetaDom/dataType.rb +112 -0
- data/lib/dataMetaDom/docs.rb +122 -0
- data/lib/dataMetaDom/enum.rb +125 -0
- data/lib/dataMetaDom/field.rb +182 -0
- data/lib/dataMetaDom/help.rb +41 -0
- data/lib/dataMetaDom/model.rb +274 -0
- data/lib/dataMetaDom/mySql.rb +256 -0
- data/lib/dataMetaDom/ora.rb +295 -0
- data/lib/dataMetaDom/pojo.rb +1056 -0
- data/lib/dataMetaDom/python.rb +168 -0
- data/lib/dataMetaDom/recAttr.rb +271 -0
- data/lib/dataMetaDom/record.rb +397 -0
- data/lib/dataMetaDom/ref.rb +127 -0
- data/lib/dataMetaDom/sourceFile.rb +150 -0
- data/lib/dataMetaDom/sources.rb +68 -0
- data/lib/dataMetaDom/util.rb +279 -0
- data/lib/dataMetaDom/ver.rb +244 -0
- data/lib/dataMetaDom.rb +141 -0
- data/test/test_dataMetaDom.rb +130 -0
- data/test/test_helper.rb +6 -0
- data/tmpl/java/migrationEntityEnums.erb +172 -0
- data/tmpl/python/entity.erb +50 -0
- metadata +126 -0
@@ -0,0 +1,397 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
%w(fileutils set dataMetaXtra dataMetaDom/docs dataMetaDom/ver).each { |r| require r }
|
4
|
+
|
5
|
+
module DataMetaDom
|
6
|
+
|
7
|
+
=begin rdoc
|
8
|
+
A mapping - of values from one data type to values of another data types. Supports only DataMeta DOM standard types
|
9
|
+
|
10
|
+
For command line details either check the new method's source or the README.rdoc file, the usage section.
|
11
|
+
=end
|
12
|
+
class Mapping < VerDoccable
|
13
|
+
|
14
|
+
# Empty binding for evaluation to avoid exposing class variables
|
15
|
+
BINDING_NONE = DataMetaXtra.nilBinding
|
16
|
+
|
17
|
+
=begin rdoc
|
18
|
+
Name of this mapping data type including namespace if any.
|
19
|
+
=end
|
20
|
+
attr_accessor :name
|
21
|
+
=begin rdoc
|
22
|
+
DataType "from" (source)
|
23
|
+
=end
|
24
|
+
attr_accessor :fromT
|
25
|
+
=begin rdoc
|
26
|
+
DataType "to" (target)
|
27
|
+
=end
|
28
|
+
attr_accessor :toT
|
29
|
+
|
30
|
+
=begin rdoc
|
31
|
+
The hash of the "from" (source) values to the "to" (target) values.
|
32
|
+
=end
|
33
|
+
attr_accessor :base
|
34
|
+
|
35
|
+
=begin rdoc
|
36
|
+
Creates an instance for the given full data type name, including namespace if any.
|
37
|
+
=end
|
38
|
+
def initialize(name)
|
39
|
+
#noinspection RubyArgCount
|
40
|
+
super()
|
41
|
+
@name = name.to_sym; @base = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
=begin rdoc
|
45
|
+
Returns the value for the given key as mapped.
|
46
|
+
* Parameters:
|
47
|
+
* +key+ - the value of the type "from" (source) to get the matching value of the type "to" (target) for.
|
48
|
+
=end
|
49
|
+
def [](key); @base[key] end
|
50
|
+
|
51
|
+
=begin rdoc
|
52
|
+
Assign the mapped value for the given key, useful for building a mapping from the code.
|
53
|
+
* Parameters:
|
54
|
+
* +key+ - the value of the type "from" (source)
|
55
|
+
* +val+ - the matching value of the type "to" (target).
|
56
|
+
=end
|
57
|
+
def []=(key, val); base[key] = val end
|
58
|
+
|
59
|
+
=begin rdoc
|
60
|
+
All the values of the type "to" (target) defined on the mapping, sorted
|
61
|
+
=end
|
62
|
+
def values; @base.values.sort end
|
63
|
+
|
64
|
+
=begin rdoc
|
65
|
+
All the keys of the type "from" (source) defined on the mapping, sorted
|
66
|
+
=end
|
67
|
+
def keys; @base.keys.sort end
|
68
|
+
|
69
|
+
=begin rdoc
|
70
|
+
Parses the mapping, the keys and the values from the given source.
|
71
|
+
* Parameters
|
72
|
+
* +source+ - an instance of SourceFile
|
73
|
+
=end
|
74
|
+
def parseBase(source)
|
75
|
+
hashSource = '{'
|
76
|
+
while (line = source.nextLine)
|
77
|
+
case line
|
78
|
+
when /^\s*#{END_KW}\s*$/
|
79
|
+
self.ver = source.ver unless self.ver
|
80
|
+
self.docs = source.docs.clone
|
81
|
+
source.docs.clear
|
82
|
+
raise "Version missing for the Mapping #{name}" unless self.ver
|
83
|
+
break
|
84
|
+
else
|
85
|
+
hashSource << line
|
86
|
+
end # case
|
87
|
+
end # while line
|
88
|
+
@base = eval(hashSource + '}', BINDING_NONE)
|
89
|
+
self
|
90
|
+
end # def parse
|
91
|
+
end
|
92
|
+
|
93
|
+
=begin rdoc
|
94
|
+
A Bit Set mapping, one instance holding a set of references to the values packed tightly as a bit set.
|
95
|
+
=end
|
96
|
+
class BitSet < Mapping
|
97
|
+
|
98
|
+
=begin rdoc
|
99
|
+
Attempts to consume the instance from the given source, returns it if successful, returns nil otherwise.
|
100
|
+
* Parameters
|
101
|
+
* +model+ - an instance of a Model
|
102
|
+
* +src+ - an instance of SourceFile
|
103
|
+
=end
|
104
|
+
def self.consumed?(model, src)
|
105
|
+
src.line =~ /^\s*#{BITSET}\s+(\w+)\s+.+$/ ? model.addEnum(BitSet.new(combineNsBase(DataMetaDom.nsAdjustment(src.namespace, model.options, src), $1)).parse(src)) : nil
|
106
|
+
end
|
107
|
+
|
108
|
+
=begin rdoc
|
109
|
+
Returns the keyword for this Mapping implementation, in this case "<tt>bitset</tt>"
|
110
|
+
=end
|
111
|
+
def sourceKeyWord; BITSET end
|
112
|
+
|
113
|
+
=begin rdoc
|
114
|
+
Parses the current instance from the given source.
|
115
|
+
* Parameters
|
116
|
+
* +src+ - an instance of SourceFile
|
117
|
+
=end
|
118
|
+
def parse(src)
|
119
|
+
r = src.line.scan(/^\s*\w+\s+\w+\s+(\S+)\s*$/)
|
120
|
+
raise 'Invalid bitset specification' unless r && r[0] && r[0][0]
|
121
|
+
self.fromT = INT4 #platform type is up to the concrete generator
|
122
|
+
self.toT = DataType.parse(src, r[0][0])
|
123
|
+
parseBase src
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
=begin rdoc
|
128
|
+
A single-value map of a key to a value.
|
129
|
+
=end
|
130
|
+
class Mappings < Mapping
|
131
|
+
|
132
|
+
=begin rdoc
|
133
|
+
Attempts to consume the instance from the given source, returns it if successful, returns nil otherwise.
|
134
|
+
* Parameters
|
135
|
+
* +model+ - an instance of a Model
|
136
|
+
* +src+ - an instance of SourceFile
|
137
|
+
=end
|
138
|
+
def self.consumed?(model, src)
|
139
|
+
src.line =~ /^\s*#{MAPPING}\s+(\w+)\s+.+$/ ? model.addEnum(Mappings.new(combineNsBase(DataMetaDom.nsAdjustment(src.namespace, model.options, src), $1)).parse(src)) : nil
|
140
|
+
end
|
141
|
+
|
142
|
+
=begin rdoc
|
143
|
+
Returns the keyword for this Mapping implementation, in this case "<tt>map</tt>"
|
144
|
+
=end
|
145
|
+
def sourceKeyWord; MAPPING end
|
146
|
+
|
147
|
+
=begin rdoc
|
148
|
+
Parses the current instance from the given source.
|
149
|
+
* Parameters
|
150
|
+
* +src+ - an instance of SourceFile
|
151
|
+
=end
|
152
|
+
def parse(src)
|
153
|
+
r = src.line.scan(/^\s*\w+\s+\w+\s+(\S+)\s+(\S+)\s*$/)
|
154
|
+
raise 'Invalid map specification' unless r && r[0] && r[0][0] && r[0][1]
|
155
|
+
self.fromT = DataType.parse(src, r[0][0])
|
156
|
+
self.toT = DataType.parse(src, r[0][1])
|
157
|
+
parseBase src
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
=begin rdoc
|
162
|
+
A data structure comprised of fields with the notion of identity, indexes, unique and not.
|
163
|
+
@!attribute [r] namespace
|
164
|
+
@return [String] part of the {#name}, the prefix before last dot, "package" in Java and Scala, "namespace" in C
|
165
|
+
|
166
|
+
@!attribute [r] baseName
|
167
|
+
@return [String] part of the {#name}, the suffix after last dot
|
168
|
+
|
169
|
+
=end
|
170
|
+
class Record < VerDoccable
|
171
|
+
|
172
|
+
=begin rdoc
|
173
|
+
Full Record datatype name, including namespace if any. Should be unique in the model.
|
174
|
+
=end
|
175
|
+
attr_accessor :name
|
176
|
+
|
177
|
+
=begin rdoc
|
178
|
+
The fields as a map keying a field name to the matching instance of a Field
|
179
|
+
=end
|
180
|
+
attr_reader :fields
|
181
|
+
|
182
|
+
=begin rdoc
|
183
|
+
An instance of RecIdentity.
|
184
|
+
=end
|
185
|
+
attr_reader :identity
|
186
|
+
|
187
|
+
=begin rdoc
|
188
|
+
A hash mapping to RecUnique from its key. Meaning, RecUnique.key to the matching RecUnique
|
189
|
+
=end
|
190
|
+
attr_reader :uniques
|
191
|
+
|
192
|
+
=begin rdoc
|
193
|
+
A hash mapping to RecIndex from its key. Meaning, RecIndex.key to the matching RecIndex
|
194
|
+
=end
|
195
|
+
attr_reader :indexes
|
196
|
+
|
197
|
+
=begin rdoc
|
198
|
+
An array of Reference
|
199
|
+
=end
|
200
|
+
attr_reader :refs
|
201
|
+
|
202
|
+
=begin rdoc
|
203
|
+
The unique key for the record, unique across the Model.
|
204
|
+
=end
|
205
|
+
attr_reader :key
|
206
|
+
|
207
|
+
attr_reader :namespace
|
208
|
+
|
209
|
+
attr_reader :baseName
|
210
|
+
|
211
|
+
=begin rdoc
|
212
|
+
Attempts to consume the instance from the given source, returns it if successful, returns nil otherwise.
|
213
|
+
* Parameters
|
214
|
+
* +model+ - an instance of a Model
|
215
|
+
* +src+ - an instance of SourceFile
|
216
|
+
=end
|
217
|
+
def self.consumed?(model, src)
|
218
|
+
if src.line =~ /^\s*#{RECORD}\s+(\w+)$/
|
219
|
+
newRecord = Record.new(DataMetaDom.combineNsBase(DataMetaDom.nsAdjustment(src.namespace, model.options, src).to_sym, $1)).parse(model, src)
|
220
|
+
$stderr.puts %<WARN: Record redefinition: "#{newRecord.key}"> if model.records[newRecord.key]
|
221
|
+
model.addRecord(newRecord)
|
222
|
+
else
|
223
|
+
nil
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
=begin rdoc
|
228
|
+
Creates an instance for the given full data type name, including namespace.
|
229
|
+
Namespace is required.
|
230
|
+
|
231
|
+
|
232
|
+
=end
|
233
|
+
def initialize(name)
|
234
|
+
#noinspection RubyArgCount
|
235
|
+
super()
|
236
|
+
@namespace, @baseName = DataMetaDom.splitNameSpace(name)
|
237
|
+
|
238
|
+
raise %Q<Record "#{@baseName}": no namespace; namespaces are required!> unless @namespace
|
239
|
+
@name = name.to_sym
|
240
|
+
@key = @name
|
241
|
+
@fields={}; @uniques={}; @identity=nil; @indexes = {}; @refs=[]
|
242
|
+
end
|
243
|
+
|
244
|
+
=begin rdoc
|
245
|
+
Fetches the field by the field key, i.e. Field.name
|
246
|
+
=end
|
247
|
+
def [](fieldKey); @fields[fieldKey] end
|
248
|
+
|
249
|
+
=begin rdoc
|
250
|
+
Verifies that the list of ids is valid, meaning that there is a field with the given name on this record.
|
251
|
+
* Parameter
|
252
|
+
* +ids+ - an array of strings, each should be a valid field name already defined on this Record.
|
253
|
+
=end
|
254
|
+
def assertIds(ids)
|
255
|
+
ids.each { |id|
|
256
|
+
k = id.to_sym
|
257
|
+
raise "Undeclared field '#{id}'" unless @fields.has_key?(k)
|
258
|
+
}
|
259
|
+
end
|
260
|
+
|
261
|
+
=begin rdoc
|
262
|
+
Set the identity on the Record.
|
263
|
+
* Parameter:
|
264
|
+
* +newIdy+ - an instance of RecIdentity
|
265
|
+
=end
|
266
|
+
def identity=(newIdy)
|
267
|
+
raise 'There can be only one identity statement in a record' if @identity
|
268
|
+
@identity = newIdy
|
269
|
+
assertIds(@identity.args)
|
270
|
+
@identity.args.each { |id|
|
271
|
+
f = @fields[id.to_sym]
|
272
|
+
|
273
|
+
raise ArgumentError, %|Field "#{
|
274
|
+
id}" is made identity; no aggregate types or maps can be identity| if f.aggr? || f.map?
|
275
|
+
|
276
|
+
raise ArgumentError, %|Optional field "#{
|
277
|
+
id}" is made identity; only required fields may be identity| unless f.isRequired
|
278
|
+
}
|
279
|
+
end
|
280
|
+
|
281
|
+
=begin rdoc
|
282
|
+
Add another unique index to the record.
|
283
|
+
* Parameter:
|
284
|
+
* +newUq+ - an instance of RecUnique to add ot this record.
|
285
|
+
=end
|
286
|
+
def addUnique(newUq)
|
287
|
+
assertIds(newUq.args)
|
288
|
+
raise "Duplicate unique set declaration #{newUq}" if @uniques.has_key?(newUq.key)
|
289
|
+
@uniques[newUq.key] = newUq
|
290
|
+
end
|
291
|
+
|
292
|
+
=begin rdoc
|
293
|
+
Add another non-unique index to the record.
|
294
|
+
* Parameter:
|
295
|
+
* +newIx+ - an instance of RecIndex to add to this Record
|
296
|
+
=end
|
297
|
+
def addIndex(newIx)
|
298
|
+
assertIds(newIx.args)
|
299
|
+
raise "Duplicate index declaration #{newIx}" if @indexes.has_key?(newIx.key)
|
300
|
+
@indexes[newIx.key] = newIx
|
301
|
+
end
|
302
|
+
|
303
|
+
=begin rdoc
|
304
|
+
Add another field to this Record's definition along with the source reference if any.
|
305
|
+
* Parameters:
|
306
|
+
* +newField+ - an instance of Field to add to this Record
|
307
|
+
* +source+ - a reference to the SourceFile where this field has been defined, pass +nil+ if
|
308
|
+
built from the code.
|
309
|
+
=end
|
310
|
+
def addField(newField, model, source=nil)
|
311
|
+
fieldKey = newField.name
|
312
|
+
raise "Duplicate field name '#{fieldKey}' in the Record '#{@name}'" if (@fields.key?(fieldKey))
|
313
|
+
@fields[fieldKey] = newField
|
314
|
+
unless STANDARD_TYPES.member?(newField.dataType.type)
|
315
|
+
ns, base = DataMetaDom.splitNameSpace(newField.dataType.type)
|
316
|
+
newNs = DataMetaDom.nsAdjustment(ns, model.options, source)
|
317
|
+
reRefName = "#{newNs}.#{base}".to_sym
|
318
|
+
newField.dataType.type = reRefName # adjust the type for finding the reference again
|
319
|
+
@refs << Reference.new(self, newField, reRefName, source ? source.snapshot : nil)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
=begin rdoc
|
324
|
+
Add several Field definitions to this Record.
|
325
|
+
* Parameters:
|
326
|
+
* +fields+ - an array of the instances of Field to add to this Record
|
327
|
+
=end
|
328
|
+
def addFields(fields, model, source=nil); fields.each { |f| addField f, model, source } end
|
329
|
+
|
330
|
+
=begin rdoc
|
331
|
+
Add several non-unique indexes to the record.
|
332
|
+
* Parameter:
|
333
|
+
* +ixs+ - an array of the instances of RecIndex to add to this Record
|
334
|
+
=end
|
335
|
+
def addIndexes(ixs); ixs.each { |ix| addIndex ix } end
|
336
|
+
|
337
|
+
=begin rdoc
|
338
|
+
Add several unique indexes to the record.
|
339
|
+
* Parameter:
|
340
|
+
* +uqs+ - an array of instances of RecUnique to add ot this record.
|
341
|
+
=end
|
342
|
+
def addUniques(uqs); uqs.each { |uq| addUnique uq } end
|
343
|
+
|
344
|
+
=begin rdoc
|
345
|
+
Parse the Record from the given source into this instance.
|
346
|
+
* Parameter:
|
347
|
+
* +source+ - an instance of SourceFile to parse from
|
348
|
+
=end
|
349
|
+
def parse(model, source)
|
350
|
+
while (line = source.nextLine)
|
351
|
+
next if docConsumed?(source)
|
352
|
+
case line
|
353
|
+
when /^\s*#{END_KW}\s*$/
|
354
|
+
if source.docs
|
355
|
+
self.docs = source.docs.clone
|
356
|
+
source.docs.clear
|
357
|
+
end
|
358
|
+
self.ver = source.ver unless self.ver
|
359
|
+
raise "Version missing for the Record #{name}" unless self.ver
|
360
|
+
return self
|
361
|
+
when ''
|
362
|
+
next
|
363
|
+
else
|
364
|
+
isTokenConsumed = false
|
365
|
+
RECORD_LEVEL_TOKENS.each { |t|
|
366
|
+
isTokenConsumed = t.consumed? source, self
|
367
|
+
break if isTokenConsumed
|
368
|
+
}
|
369
|
+
unless isTokenConsumed
|
370
|
+
raise ArgumentError, "Record #{@name}: all field declarations must precede identity" if @identity
|
371
|
+
Field.consume(model, source, self)
|
372
|
+
end
|
373
|
+
resetEntity
|
374
|
+
end # case line
|
375
|
+
end # while line
|
376
|
+
self # for call chaining
|
377
|
+
end
|
378
|
+
|
379
|
+
=begin rdoc
|
380
|
+
Textual representation of this instance, with all the fields, attributes and references if any.
|
381
|
+
=end
|
382
|
+
def to_s
|
383
|
+
"Record{#{@name}(#{@fields.values.join(';')},uq=[#{@uniques.map { |u| '[' + u.join(',') + ']' }.join('; ')}]"\
|
384
|
+
";idy=[#{@identity ? @identity.args.join(',') : ''}]; refs=[#{@refs.join(',')}]}, #{self.ver}"
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
=begin rdoc
|
389
|
+
DataMeta DOM source tokens on the Model level:
|
390
|
+
* Record
|
391
|
+
* Enum
|
392
|
+
* BitSet
|
393
|
+
* Map
|
394
|
+
=end
|
395
|
+
MODEL_LEVEL_TOKENS=[Record, Enum, BitSet, Mappings]
|
396
|
+
|
397
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
%w(fileutils set).each { |r| require r }
|
4
|
+
|
5
|
+
module DataMetaDom
|
6
|
+
|
7
|
+
=begin rdoc
|
8
|
+
A reference to another entity on this Model, any of:
|
9
|
+
* Record
|
10
|
+
* Enum
|
11
|
+
* BitSet
|
12
|
+
* Map
|
13
|
+
|
14
|
+
For command line details either check the new method's source or the README.rdoc file, the usage section.
|
15
|
+
=end
|
16
|
+
class Reference
|
17
|
+
|
18
|
+
=begin rdoc
|
19
|
+
Type of a reference - unresolved.
|
20
|
+
=end
|
21
|
+
UNRESOLVED = :u
|
22
|
+
|
23
|
+
=begin rdoc
|
24
|
+
Type of a reference - to a Record.
|
25
|
+
=end
|
26
|
+
RECORD = :r
|
27
|
+
|
28
|
+
=begin rdoc
|
29
|
+
Type of a reference - to an Enum, a BitSet or a Map.
|
30
|
+
=end
|
31
|
+
ENUM_REF = :e
|
32
|
+
|
33
|
+
=begin rdoc
|
34
|
+
The unique key for the reference to use in a hashmap.
|
35
|
+
=end
|
36
|
+
attr_accessor :key
|
37
|
+
|
38
|
+
=begin rdoc
|
39
|
+
The field the reference originates from, an instance of a Field
|
40
|
+
=end
|
41
|
+
attr_accessor :fromField
|
42
|
+
|
43
|
+
=begin rdoc
|
44
|
+
The Record the reference originates from, an instance of a Record
|
45
|
+
=end
|
46
|
+
attr_accessor :fromEntity
|
47
|
+
|
48
|
+
=begin rdoc
|
49
|
+
The target Field of the reference, must be the only one field in the target Record's identity,
|
50
|
+
determined by the resolve method.
|
51
|
+
=end
|
52
|
+
attr_accessor :toFields
|
53
|
+
|
54
|
+
=begin rdoc
|
55
|
+
The target entity for this Reference, determined by the resolve method. If it is a Record,
|
56
|
+
the record must have an identity consisting from only one field.
|
57
|
+
=end
|
58
|
+
attr_accessor :toEntity
|
59
|
+
|
60
|
+
=begin rdoc
|
61
|
+
Reference to the source where this reference has been defined, if any.
|
62
|
+
=end
|
63
|
+
attr_accessor :sourceRef
|
64
|
+
|
65
|
+
=begin rdoc
|
66
|
+
The type of the reference, UNRESOLVED or RECORD or ENUM_REF.
|
67
|
+
=end
|
68
|
+
attr_accessor :type
|
69
|
+
|
70
|
+
=begin rdoc
|
71
|
+
Creates an instance with the given parameters.
|
72
|
+
* Parameters:
|
73
|
+
* +sourceEntity+ - the instance of Record the reference originates from.
|
74
|
+
* +sourceField+ - the instance of Field the reference originates from
|
75
|
+
* +targetEntitySpec+ - a string, specification of the target entity to be resolved
|
76
|
+
* +sourceRef+ - an instance of SourceFile
|
77
|
+
=end
|
78
|
+
def initialize(sourceEntity, sourceField, targetEntitySpec, sourceRef = nil)
|
79
|
+
@sourceRef = sourceRef
|
80
|
+
@targetEntitySpec = targetEntitySpec.to_sym; @fromEntity = sourceEntity; @fromField = sourceField
|
81
|
+
@type = UNRESOLVED
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
=begin rdoc
|
86
|
+
Resolve the target entity and the field on the given Model.
|
87
|
+
=end
|
88
|
+
def resolve(model)
|
89
|
+
#@fromField = @fromEntity[@sourceFieldSpec.to_sym]
|
90
|
+
#raise "The field #@sourceFieldSpec is not defined on this entity: #@fromEntity, #@sourceRef" unless @fromField
|
91
|
+
@toEntity = model.records[@targetEntitySpec] || model.enums[@targetEntitySpec]
|
92
|
+
raise "Target entity #{@targetEntitySpec} is not defined; #{@sourceRef}" unless @toEntity
|
93
|
+
case # IMPORTANT!! make sure that you do not inspect and do not use Entity.to_s - this will blow up the stack
|
94
|
+
when @toEntity.kind_of?(Enum), @toEntity.kind_of?(BitSet), @toEntity.kind_of?(Mappings)
|
95
|
+
@type = ENUM_REF
|
96
|
+
@key= "#{@type}/#{@fromEntity.name}.#{@fromField.name}->#{@toEntity.name}"
|
97
|
+
when @toEntity.kind_of?(Record)
|
98
|
+
idy = @toEntity.identity
|
99
|
+
# raise "#@targetEntitySpec does not have an identity, can not be referenced to in IDL; #@sourceRef" unless idy
|
100
|
+
#
|
101
|
+
# raise "Invalid ref #{@fromEntity.name}.#{@toField.name} -> #@toEntity"\
|
102
|
+
# "- it has no singular ID; #@sourceRef" unless idy.args.length == 1
|
103
|
+
|
104
|
+
@type = RECORD
|
105
|
+
@toFields = idy ? idy.args : @toEntity.fields.keys
|
106
|
+
# raise "The field #{idy.args[0]} is not defined on this entity: #@toEntity; #@sourceRef" unless @toField
|
107
|
+
@key = "#{@type}/#{@fromEntity.name}.#{@fromField.name}->#{@toEntity.name}.#{@toFields.join(',')}"
|
108
|
+
else
|
109
|
+
raise "Unsupported target entity: #{@toEntity.name}, for #{@sourceRef}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
=begin rdoc
|
114
|
+
Textual representation of this Reference. Need to be careful because the to_s on the Record includes a list of references
|
115
|
+
and if you include a Record in this textual, this will cause infinite recursion with the stack failure.
|
116
|
+
=end
|
117
|
+
def to_s
|
118
|
+
case @type
|
119
|
+
when UNRESOLVED; "Unresolved: #{@fromEntity.name}.#{@fromField.name} ==> #@targetEntitySpec; #@sourceRef"
|
120
|
+
when RECORD; "Record Ref: #@key"
|
121
|
+
when ENUM_REF; "Enum ref: #@key"
|
122
|
+
else; raise "Unsupported reference type #@type; #@sourceRef"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
%w(fileutils set dataMetaDom/docs dataMetaDom/ver).each { |r| require r }
|
4
|
+
|
5
|
+
module DataMetaDom
|
6
|
+
=begin rdoc
|
7
|
+
A Model coupled with the DataMeta DOM source file info
|
8
|
+
|
9
|
+
For command line details either check the new method's source or the README.rdoc file, the usage section.
|
10
|
+
=end
|
11
|
+
class SourceFile < VerDoccable
|
12
|
+
=begin rdoc
|
13
|
+
The directory to this source file.
|
14
|
+
=end
|
15
|
+
attr_reader :path
|
16
|
+
|
17
|
+
=begin rdoc
|
18
|
+
The name of this source file in the directory indicated by the path property.
|
19
|
+
=end
|
20
|
+
attr_reader :name
|
21
|
+
|
22
|
+
=begin rdoc
|
23
|
+
Unique key of this source file to use in hash maps, the absolute path to avoid duplicates by
|
24
|
+
different ways to point to the file, turned into a symbol.
|
25
|
+
=end
|
26
|
+
attr_reader :key
|
27
|
+
|
28
|
+
=begin rdoc
|
29
|
+
The namespace associated with the source file.
|
30
|
+
=end
|
31
|
+
attr_reader :namespace
|
32
|
+
|
33
|
+
=begin rdoc
|
34
|
+
Current source line.
|
35
|
+
=end
|
36
|
+
attr_reader :line
|
37
|
+
|
38
|
+
=begin rdoc
|
39
|
+
Current source line number
|
40
|
+
=end
|
41
|
+
attr_reader :lineNum
|
42
|
+
|
43
|
+
=begin rdoc
|
44
|
+
Create an instance with the given parameters.
|
45
|
+
* Parameters:
|
46
|
+
* +path+ - directory where the source file is located
|
47
|
+
* +name+ - the base name of this source file
|
48
|
+
* +line+ - source line if any, useful when creating the source reference from the code when source is not trivial.
|
49
|
+
* +lineNum+ - line number, useful when creating the source reference from the code
|
50
|
+
* +namespace+ - namespace associated with this source file, useful when creating the source reference from the code
|
51
|
+
=end
|
52
|
+
def initialize(path, name, line = nil, lineNum = 0, namespace = nil)
|
53
|
+
#noinspection RubyArgCount
|
54
|
+
super()
|
55
|
+
@path = path
|
56
|
+
@name = name
|
57
|
+
@namespace = namespace
|
58
|
+
# use the Absolute Path to avoid double-dipping via different subdir references
|
59
|
+
@key = File.absolute_path("#{@path}#{File::SEPARATOR}#{@name}").to_sym
|
60
|
+
@lineNum = lineNum
|
61
|
+
@line = line # nil interpolates to an empty string
|
62
|
+
end
|
63
|
+
|
64
|
+
=begin rdoc
|
65
|
+
Create a shapshot of the source file information, useful for saving a status about an element currently parsed.
|
66
|
+
Can not use this instance - as the parsing progresses, the stateful information will change.
|
67
|
+
=end
|
68
|
+
def snapshot # for the history
|
69
|
+
snap = SourceFile.new(@path, @name, @line, @lineNum, @namespace)
|
70
|
+
snap.ver = Ver.new(self.ver.full)
|
71
|
+
snap
|
72
|
+
end
|
73
|
+
|
74
|
+
=begin rdoc
|
75
|
+
Parses this DataMeta DOM source into the given Model.
|
76
|
+
=end
|
77
|
+
def parse model
|
78
|
+
while nextLine
|
79
|
+
puts "Source: #{@line}" if $DEBUG
|
80
|
+
next if docConsumed?(self)
|
81
|
+
if (newVer = VerDoccable.verConsumed?(self))
|
82
|
+
raise RuntimeError, "Only one version definition allowed, second one found in line #{@lineNum}" if self.ver
|
83
|
+
self.ver = newVer
|
84
|
+
model.ver = newVer # plant it straight into the model per the latest design
|
85
|
+
raise ArgumentError,
|
86
|
+
%<Model version already defined as #{model.ver} but the file #{@path} tries to redefine it to #{newVer}.
|
87
|
+
This is not allowed: all included files should define same version> unless model.ver && newVer == model.ver
|
88
|
+
next
|
89
|
+
end
|
90
|
+
case @line
|
91
|
+
# treat the namespace operator as a special case
|
92
|
+
when /^\s*#{NAMESPACE}\s+([\w\.]+)$/
|
93
|
+
@namespace = $1
|
94
|
+
next
|
95
|
+
when /^\s*#{INCLUDE}\s+(\S+)$/
|
96
|
+
model.sources.queue "#{$1}.dmDom"
|
97
|
+
next
|
98
|
+
else
|
99
|
+
isTokenOk = false
|
100
|
+
MODEL_LEVEL_TOKENS.each { |c|
|
101
|
+
isTokenOk = c.consumed?(model, self)
|
102
|
+
if isTokenOk
|
103
|
+
resetEntity
|
104
|
+
break
|
105
|
+
end
|
106
|
+
}
|
107
|
+
raise "Syntax error; #{model.diagn}" unless isTokenOk
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end # while
|
112
|
+
end
|
113
|
+
|
114
|
+
=begin rdoc
|
115
|
+
Advances a line, skipping empty lines and comments.
|
116
|
+
Parameter:
|
117
|
+
* +verbatim+ - pass true to maintain formatting and keep empty lines
|
118
|
+
=end
|
119
|
+
def nextLine(verbatim = false)
|
120
|
+
@file = File.open("#{@path}#{File::SEPARATOR}#{@name}") unless defined?(@file) && @file
|
121
|
+
while (line = @file.gets)
|
122
|
+
unless line
|
123
|
+
@file.close
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
@lineNum += 1
|
127
|
+
return (@line = line) if verbatim
|
128
|
+
@line = line.chomp.strip
|
129
|
+
|
130
|
+
case @line
|
131
|
+
when '', /^\s*#.*$/ # skip comments and empty lines
|
132
|
+
next
|
133
|
+
else
|
134
|
+
return @line
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
=begin rdoc
|
140
|
+
Full name of this source file, absolute path. Derived from the key property turned into a string.
|
141
|
+
=end
|
142
|
+
def fullName; @key.to_s end
|
143
|
+
|
144
|
+
=begin rdoc
|
145
|
+
Textual representation of this source file reference, includes line number and the current source line.
|
146
|
+
=end
|
147
|
+
def to_s; "#{fullName}##{@lineNum}{{#{@line}}}" end # file name, line number and line
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|