dataMetaDom 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|