rgen 0.5.1 → 0.5.2

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,89 @@
1
+ class JsonParser
2
+
3
+ rule
4
+
5
+ json: value { result = val[0] }
6
+
7
+ array: "[" valueList "]" { result = val[1] }
8
+ | "[" "]" { result = [] }
9
+
10
+ valueList: value { result = [ val[0] ] }
11
+ | value "," valueList { result = [ val[0] ] + val[2] }
12
+
13
+ object: "{" memberList "}" { result = @instantiator.createObject(val[1]) }
14
+ | "{" "}" { result = nil }
15
+
16
+ memberList: member { result = val[0] }
17
+ | member "," memberList { result = val[0].merge(val[2]) }
18
+
19
+ member: STRING ":" value { result = {val[0].value => val[2]} }
20
+
21
+ value: array { result = val[0] }
22
+ | object { result = val[0] }
23
+ | STRING { result = val[0].value }
24
+ | INTEGER { result = val[0].value.to_i }
25
+ | FLOAT { result = val[0].value.to_f }
26
+ | "true" { result = true }
27
+ | "false" { result = false }
28
+
29
+ end
30
+
31
+ ---- header
32
+
33
+ module RGen
34
+
35
+ module Instantiator
36
+
37
+ ---- inner
38
+
39
+ ParserToken = Struct.new(:line, :file, :value)
40
+
41
+ def initialize(instantiator)
42
+ @instantiator = instantiator
43
+ end
44
+
45
+ def parse(str, file=nil)
46
+ @q = []
47
+ line = 1
48
+
49
+ until str.empty?
50
+ case str
51
+ when /\A\n/
52
+ str = $'
53
+ line +=1
54
+ when /\A\s+/
55
+ str = $'
56
+ when /\A([-+]?\d+\.\d+)/
57
+ str = $'
58
+ @q << [:FLOAT, ParserToken.new(line, file, $1)]
59
+ when /\A([-+]?\d+)/
60
+ str = $'
61
+ @q << [:INTEGER, ParserToken.new(line, file, $1)]
62
+ when /\A"((?:[^"\\]|\\"|\\\\|\\[^"\\])*)"/
63
+ str = $'
64
+ sval = $1
65
+ sval.gsub!('\\\\','\\')
66
+ sval.gsub!('\\"','"')
67
+ @q << [:STRING, ParserToken.new(line, file, sval)]
68
+ when /\A(\{|\}|\[|\]|,|:|true|false)/
69
+ str = $'
70
+ @q << [$1, ParserToken.new(line, file, $1)]
71
+ else
72
+ raise "parse error in line #{line} on "+str[0..20].inspect+"..."
73
+ end
74
+ end
75
+ @q.push [false, ParserToken.new(line, file, '$end')]
76
+ do_parse
77
+ end
78
+
79
+ def next_token
80
+ r = @q.shift
81
+ r
82
+ end
83
+
84
+ ---- footer
85
+
86
+ end
87
+
88
+ end
89
+
@@ -0,0 +1,93 @@
1
+ require 'rgen/instantiator/reference_resolver'
2
+
3
+ module RGen
4
+
5
+ module Instantiator
6
+
7
+ # This is a resolver resolving element identifiers which are qualified names.
8
+ class QualifiedNameResolver
9
+ include ReferenceResolver
10
+
11
+ attr_reader :nameAttribute
12
+ attr_reader :separator
13
+ attr_reader :leadingSeparator
14
+
15
+ def initialize(rootElements, options={})
16
+ @rootElements = rootElements
17
+ @nameAttribute = options[:nameAttribute] || "name"
18
+ @separator = options[:separator] || "/"
19
+ @leadingSeparator = options.has_key?(:leadingSeparator) ? options[:leadingSeparator] : true
20
+ @elementByQName = {}
21
+ @visitedQName = {}
22
+ @childReferences = {}
23
+ end
24
+
25
+ def resolveIdentifier(qualifiedName)
26
+ return @elementByQName[qualifiedName] if @elementByQName.has_key?(qualifiedName)
27
+ path = qualifiedName.split(separator).reject{|s| s == ""}
28
+ if path.size > 1
29
+ parentQName = (leadingSeparator ? separator : "") + path[0..-2].join(separator)
30
+ parents = resolveIdentifier(parentQName)
31
+ parents = [parents].compact unless parents.is_a?(Array)
32
+ children = parents.collect{|p| allNamedChildren(p)}.flatten
33
+ elsif path.size == 1
34
+ parentQName = ""
35
+ children = allRootNamedChildren
36
+ else
37
+ return @elementByQName[qualifiedName] = nil
38
+ end
39
+ # if the parent was already visited all matching elements are the hash
40
+ if !@visitedQName[parentQName]
41
+ children.each do |c|
42
+ name = c.send(nameAttribute)
43
+ if name
44
+ qname = parentQName + ((parentQName != "" || leadingSeparator) ? separator : "") + name
45
+ existing = @elementByQName[qname]
46
+ if existing
47
+ @elementByQName[qname] = [existing] unless existing.is_a?(Array)
48
+ @elementByQName[qname] << c
49
+ else
50
+ @elementByQName[qname] = c
51
+ end
52
+ end
53
+ end
54
+ # all named children of praent have been checked and hashed
55
+ @visitedQName[parentQName] = true
56
+ end
57
+ @elementByQName[qualifiedName] ||= nil
58
+ end
59
+
60
+ private
61
+
62
+ def allNamedChildren(element)
63
+ childReferences(element.class).collect do |r|
64
+ element.getGenericAsArray(r.name).collect do |c|
65
+ if c.respond_to?(nameAttribute)
66
+ c
67
+ else
68
+ allNamedChildren(c)
69
+ end
70
+ end
71
+ end.flatten
72
+ end
73
+
74
+ def allRootNamedChildren
75
+ @rootElements.collect do |e|
76
+ if e.respond_to?(nameAttribute)
77
+ e
78
+ else
79
+ allNamedChildren(e)
80
+ end
81
+ end.flatten
82
+ end
83
+
84
+ def childReferences(clazz)
85
+ @childReferences[clazz] ||= clazz.ecore.eAllReferences.select{|r| r.containment}
86
+ end
87
+
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
@@ -0,0 +1,44 @@
1
+ module RGen
2
+
3
+ module Instantiator
4
+
5
+ # This module is meant to be mixed into a resolver class providing the method +resolveIdentifier+
6
+ module ReferenceResolver
7
+
8
+ # Instances of this class represent information about not yet resolved references.
9
+ # This consists of the +element+ and metamodel +featureName+ which hold/is to hold the reference
10
+ # and the proxy object which is the placeholder for the reference.
11
+ UnresolvedReference = Struct.new(:element, :featureName, :proxy)
12
+
13
+ # tries to resolve the given +unresolvedReferences+
14
+ # if resolution is successful, the proxy object will be removed
15
+ # otherwise there will be an error description in +problems+
16
+ # returns an array of the references which are still unresolved
17
+ def resolveReferences(unresolvedReferences, problems=[])
18
+ stillUnresolvedReferences = []
19
+ unresolvedReferences.each do |ur|
20
+ target = resolveIdentifier(ur.proxy.targetIdentifier)
21
+ if target && !target.is_a?(Array)
22
+ if ur.element.hasManyMethods(ur.featureName)
23
+ ur.element.removeGeneric(ur.featureName, ur.proxy)
24
+ ur.element.addGeneric(ur.featureName, target)
25
+ else
26
+ # this will replace the proxy
27
+ ur.element.setGeneric(ur.featureName, target)
28
+ end
29
+ elsif target
30
+ problems << "identifier #{ur.proxy.targetIdentifier} not uniq"
31
+ stillUnresolvedReferences << ur
32
+ else
33
+ problems << "identifier #{ur.proxy.targetIdentifier} not found"
34
+ stillUnresolvedReferences << ur
35
+ end
36
+ end
37
+ stillUnresolvedReferences
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -192,6 +192,17 @@ module MetamodelBuilder
192
192
  end
193
193
  end
194
194
 
195
+ # Instances of the proxy class can be used as values in any reference feature
196
+ # They can be used instead of the real target elements in case references should be resolved later on
197
+ class MMProxy
198
+ # The +targetIdentifer+ is an object identifying the element the proxy represents
199
+ attr_accessor :targetIdentifier
200
+
201
+ def initialize(ident=nil)
202
+ @targetIdentifier = ident
203
+ end
204
+ end
205
+
195
206
  end
196
207
 
197
- end
208
+ end
@@ -36,6 +36,15 @@ module BuilderExtensions
36
36
  end
37
37
  end
38
38
 
39
+ # Add an attribute which can hold a single value.
40
+ # 'role' specifies the name which is used to access the attribute.
41
+ # 'target_class' specifies the type of objects which can be held by this attribute.
42
+ # If no target class is given, String will be default.
43
+ #
44
+ # This class method adds the following instance methods, where 'role' is to be
45
+ # replaced by the given role name:
46
+ # class#role # getter
47
+ # class#role=(value) # setter
39
48
  def has_attr(role, target_class=nil, raw_props={}, &block)
40
49
  props = AttributeDescription.new(target_class, _ownProps(raw_props).merge({
41
50
  :name=>role, :upperBound=>1}))
@@ -43,14 +52,34 @@ module BuilderExtensions
43
52
  FeatureBlockEvaluator.eval(block, props)
44
53
  _build_internal(props)
45
54
  end
46
-
47
- # Add a single attribute or unidirectional association.
55
+
56
+ # Add an attribute which can hold multiple values.
48
57
  # 'role' specifies the name which is used to access the attribute.
49
58
  # 'target_class' specifies the type of objects which can be held by this attribute.
50
59
  # If no target class is given, String will be default.
51
60
  #
52
61
  # This class method adds the following instance methods, where 'role' is to be
53
62
  # replaced by the given role name:
63
+ # class#addRole(value)
64
+ # class#removeRole(value)
65
+ # class#role # getter, returns an array
66
+ # class#role= # setter, sets multiple values at once
67
+ # Note that the first letter of the role name is turned into an uppercase
68
+ # for the add and remove methods.
69
+ def has_many_attr(role, target_class=nil, raw_props={}, &block)
70
+ props = AttributeDescription.new(target_class, _setManyUpperBound(_ownProps(raw_props).merge({
71
+ :name=>role})))
72
+ raise "No opposite available" unless _oppositeProps(raw_props).empty?
73
+ FeatureBlockEvaluator.eval(block, props)
74
+ _build_internal(props)
75
+ end
76
+
77
+ # Add a single unidirectional association.
78
+ # 'role' specifies the name which is used to access the association.
79
+ # 'target_class' specifies the type of objects which can be held by this association.
80
+ #
81
+ # This class method adds the following instance methods, where 'role' is to be
82
+ # replaced by the given role name:
54
83
  # class#role # getter
55
84
  # class#role=(value) # setter
56
85
  #
@@ -245,7 +274,7 @@ module BuilderExtensions
245
274
  #
246
275
  def _build_internal(props1, props2=nil)
247
276
  _add_metamodel_description(props1)
248
- if props1.is_a?(ReferenceDescription) && props1.many?
277
+ if props1.many?
249
278
  _build_many_methods(props1, props2)
250
279
  else
251
280
  _build_one_methods(props1, props2)
@@ -301,15 +330,15 @@ module BuilderExtensions
301
330
  oldval = @<%= name %>
302
331
  @<%= name %> = val
303
332
  <% if other_role %>
304
- oldval._unregister<%= firstToUpper(other_role) %>(self) unless oldval.nil?
305
- val._register<%= firstToUpper(other_role) %>(self) unless val.nil?
333
+ oldval._unregister<%= firstToUpper(other_role) %>(self) unless oldval.nil? || oldval.is_a?(MMProxy)
334
+ val._register<%= firstToUpper(other_role) %>(self) unless val.nil? || val.is_a?(MMProxy)
306
335
  <% end %>
307
336
  end
308
337
  alias set<%= firstToUpper(name) %> <%= name %>=
309
338
 
310
339
  def _register<%= firstToUpper(name) %>(val)
311
340
  <% if other_role %>
312
- @<%= name %>._unregister<%= firstToUpper(other_role) %>(self) unless @<%= name %>.nil?
341
+ @<%= name %>._unregister<%= firstToUpper(other_role) %>(self) unless @<%= name %>.nil? || @<%= name %>.is_a?(MMProxy)
313
342
  <% end %>
314
343
  @<%= name %> = val
315
344
  end
@@ -353,7 +382,7 @@ module BuilderExtensions
353
382
  <%= type_check_code("val", props) %>
354
383
  @<%= name %>.push val
355
384
  <% if other_role %>
356
- val._register<%= firstToUpper(other_role) %>(self)
385
+ val._register<%= firstToUpper(other_role) %>(self) unless val.is_a?(MMProxy)
357
386
  <% end %>
358
387
  end
359
388
 
@@ -363,7 +392,7 @@ module BuilderExtensions
363
392
  if e.object_id == val.object_id
364
393
  @<%= name %>.delete_at(i)
365
394
  <% if other_role %>
366
- val._unregister<%= firstToUpper(other_role) %>(self)
395
+ val._unregister<%= firstToUpper(other_role) %>(self) unless val.is_a?(MMProxy)
367
396
  <% end %>
368
397
  return
369
398
  end
@@ -428,10 +457,10 @@ module BuilderExtensions
428
457
  def type_check_code(varname, props)
429
458
  code = ""
430
459
  if props.impl_type.is_a?(Class)
431
- code << "unless #{varname}.nil? or #{varname}.is_a? #{props.impl_type}\n"
460
+ code << "unless #{varname}.nil? || #{varname}.is_a?(#{props.impl_type}) || #{varname}.is_a?(MMProxy)\n"
432
461
  expected = props.impl_type.to_s
433
462
  elsif props.impl_type.is_a?(RGen::MetamodelBuilder::DataTypes::Enum)
434
- code << "unless #{varname}.nil? or [#{props.impl_type.literals_as_strings.join(',')}].include?(#{varname})\n"
463
+ code << "unless #{varname}.nil? || [#{props.impl_type.literals_as_strings.join(',')}].include?(#{varname})\n"
435
464
  expected = "["+props.impl_type.literals_as_strings.join(',')+"]"
436
465
  else
437
466
  raise StandardError.new("Unkown type "+props.impl_type.to_s)
@@ -464,4 +493,4 @@ end
464
493
 
465
494
  end
466
495
 
467
- end
496
+ end
@@ -30,10 +30,28 @@ module BuilderRuntime
30
30
  send("#{role}=",value)
31
31
  end
32
32
 
33
+ def hasManyMethods(role)
34
+ respond_to?("add#{firstToUpper(role)}")
35
+ end
36
+
37
+ def setOrAddGeneric(role, value)
38
+ if hasManyMethods(role)
39
+ addGeneric(role, value)
40
+ else
41
+ setGeneric(role, value)
42
+ end
43
+ end
44
+
33
45
  def getGeneric(role)
34
46
  send("#{role}")
35
47
  end
36
48
 
49
+ def getGenericAsArray(role)
50
+ result = getGeneric(role)
51
+ result = [result].compact unless result.is_a?(Array)
52
+ result
53
+ end
54
+
37
55
  def _assignmentTypeError(target, value, expected)
38
56
  text = ""
39
57
  if target
@@ -52,4 +70,4 @@ end
52
70
 
53
71
  end
54
72
 
55
- end
73
+ end
@@ -0,0 +1,120 @@
1
+ module RGen
2
+
3
+ module Serializer
4
+
5
+ class JsonSerializer
6
+
7
+ def initialize(writer, opts={})
8
+ @writer = writer
9
+ @elementIdentifiers = {}
10
+ @identAttrName = opts[:identAttrName] || "name"
11
+ @separator = opts[:separator] || "/"
12
+ @leadingSeparator = opts.has_key?(:leadingSeparator) ? opts[:leadingSeparator] : true
13
+ @featureFilter = opts[:featureFilter]
14
+ @identifierProvider = opts[:identifierProvider]
15
+ end
16
+
17
+ def elementIdentifier(element)
18
+ ident = @identifierProvider && @identifierProvider.call(element)
19
+ ident || (element.is_a?(RGen::MetamodelBuilder::MMProxy) && element.targetIdentifier) || qualifiedElementName(element)
20
+ end
21
+
22
+ # simple identifier calculation based on qualified names
23
+ # prerequisits:
24
+ # * containment relations must be bidirectionsl
25
+ # * local name stored in single attribute +@identAttrName+ for all classes
26
+ #
27
+ def qualifiedElementName(element)
28
+ return @elementIdentifiers[element] if @elementIdentifiers[element]
29
+ localIdent = ((element.respond_to?(@identAttrName) && element.getGeneric(@identAttrName)) || "").strip
30
+ parentRef = element.class.ecore.eAllReferences.select{|r| r.eOpposite && r.eOpposite.containment}.first
31
+ parent = parentRef && element.getGeneric(parentRef.name)
32
+ if parent
33
+ if localIdent.size > 0
34
+ parentIdent = qualifiedElementName(parent)
35
+ result = parentIdent + @separator + localIdent
36
+ else
37
+ result = qualifiedElementName(parent)
38
+ end
39
+ else
40
+ result = (@leadingSeparator ? @separator : "") + localIdent
41
+ end
42
+ @elementIdentifiers[element] = result
43
+ end
44
+
45
+ def serialize(elements)
46
+ if elements.is_a?(Array)
47
+ write("[ ")
48
+ elements.each_with_index do |e, i|
49
+ serializeElement(e)
50
+ write(",\n") unless i == elements.size-1
51
+ end
52
+ write("]")
53
+ else
54
+ serializeElement(elements)
55
+ end
56
+ end
57
+
58
+ def serializeElement(element, indent="")
59
+ write(indent + "{ \"_class\": \""+element.class.ecore.name+"\"")
60
+ element.class.ecore.eAllStructuralFeatures.each do |f|
61
+ next if f.derived
62
+ value = element.getGeneric(f.name)
63
+ unless value == [] || value.nil? ||
64
+ (f.is_a?(RGen::ECore::EReference) && f.eOpposite && f.eOpposite.containment) ||
65
+ (@featureFilter && !@featureFilter.call(f))
66
+ write(", ")
67
+ writeFeature(f, value, indent)
68
+ end
69
+ end
70
+ write(" }")
71
+ end
72
+
73
+ def writeFeature(feat, value, indent)
74
+ write("\""+feat.name+"\": ")
75
+ if feat.is_a?(RGen::ECore::EAttribute)
76
+ if value.is_a?(Array)
77
+ write("[ "+value.collect{|v| attributeValue(v, feat)}.join(", ")+" ]")
78
+ else
79
+ write(attributeValue(value, feat))
80
+ end
81
+ elsif !feat.containment
82
+ if value.is_a?(Array)
83
+ write("[ "+value.collect{|v| "\""+elementIdentifier(v)+"\""}.join(", ")+" ]")
84
+ else
85
+ write("\""+elementIdentifier(value)+"\"")
86
+ end
87
+ else
88
+ if value.is_a?(Array)
89
+ write("[ \n")
90
+ value.each_with_index do |v, i|
91
+ serializeElement(v, indent+" ")
92
+ write(",\n") unless i == value.size-1
93
+ end
94
+ write("]")
95
+ else
96
+ write("\n")
97
+ serializeElement(value, indent+" ")
98
+ end
99
+ end
100
+ end
101
+
102
+ def attributeValue(value, a)
103
+ if a.eType == RGen::ECore::EString || a.eType.is_a?(RGen::ECore::EEnum)
104
+ "\""+value.to_s.gsub('"','\\"').gsub('\\','\\\\')+"\""
105
+ else
106
+ value.to_s
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def write(s)
113
+ @writer.write(s)
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+