rgen 0.5.1 → 0.5.2

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