rgen 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/CHANGELOG +28 -0
  2. data/Rakefile +3 -4
  3. data/lib/ea_support/uml13_ea_metamodel.rb +3 -3
  4. data/lib/ea_support/uml13_ea_to_uml13.rb +33 -2
  5. data/lib/ea_support/uml13_to_uml13_ea.rb +7 -0
  6. data/lib/mmgen/mm_ext/ecore_mmgen_ext.rb +4 -4
  7. data/lib/mmgen/templates/metamodel_generator.tpl +143 -143
  8. data/lib/rgen/ecore/ecore.rb +11 -1
  9. data/lib/rgen/ecore/ecore_interface.rb +47 -0
  10. data/lib/rgen/ecore/ecore_to_ruby.rb +166 -0
  11. data/lib/rgen/ecore/{ecore_transformer.rb → ruby_to_ecore.rb} +11 -11
  12. data/lib/rgen/environment.rb +15 -2
  13. data/lib/rgen/fragment/dump_file_cache.rb +63 -0
  14. data/lib/rgen/fragment/fragmented_model.rb +139 -0
  15. data/lib/rgen/fragment/model_fragment.rb +268 -0
  16. data/lib/rgen/instantiator/abstract_xml_instantiator.rb +44 -72
  17. data/lib/rgen/instantiator/default_xml_instantiator.rb +2 -2
  18. data/lib/rgen/instantiator/ecore_xml_instantiator.rb +16 -1
  19. data/lib/rgen/instantiator/json_instantiator.rb +16 -2
  20. data/lib/rgen/instantiator/nodebased_xml_instantiator.rb +118 -138
  21. data/lib/rgen/instantiator/qualified_name_resolver.rb +5 -1
  22. data/lib/rgen/instantiator/reference_resolver.rb +126 -24
  23. data/lib/rgen/instantiator/xmi11_instantiator.rb +6 -2
  24. data/lib/rgen/metamodel_builder.rb +18 -6
  25. data/lib/rgen/metamodel_builder/builder_extensions.rb +431 -407
  26. data/lib/rgen/metamodel_builder/builder_runtime.rb +8 -8
  27. data/lib/rgen/metamodel_builder/constant_order_helper.rb +4 -4
  28. data/lib/rgen/metamodel_builder/data_types.rb +5 -1
  29. data/lib/rgen/metamodel_builder/intermediate/feature.rb +167 -0
  30. data/lib/rgen/metamodel_builder/module_extension.rb +2 -2
  31. data/lib/rgen/model_builder.rb +10 -5
  32. data/lib/rgen/model_builder/builder_context.rb +17 -1
  33. data/lib/rgen/serializer/opposite_reference_filter.rb +18 -0
  34. data/lib/rgen/serializer/qualified_name_provider.rb +45 -0
  35. data/lib/rgen/template_language/template_container.rb +3 -1
  36. data/lib/rgen/{auto_class_creator.rb → util/auto_class_creator.rb} +6 -1
  37. data/lib/rgen/util/cached_glob.rb +67 -0
  38. data/lib/rgen/util/file_cache_map.rb +104 -0
  39. data/lib/rgen/util/file_change_detector.rb +78 -0
  40. data/lib/rgen/{method_delegation.rb → util/method_delegation.rb} +18 -3
  41. data/lib/rgen/{model_comparator.rb → util/model_comparator.rb} +17 -5
  42. data/lib/rgen/{model_comparator_base.rb → util/model_comparator_base.rb} +6 -1
  43. data/lib/rgen/{model_dumper.rb → util/model_dumper.rb} +6 -1
  44. data/lib/rgen/{name_helper.rb → util/name_helper.rb} +6 -1
  45. data/lib/rgen/util/pattern_matcher.rb +329 -0
  46. data/lib/transformers/uml13_to_ecore.rb +103 -60
  47. data/test/ecore_self_test.rb +43 -42
  48. data/test/json_test.rb +15 -0
  49. data/test/metamodel_builder_test.rb +361 -206
  50. data/test/metamodel_from_ecore_test.rb +45 -0
  51. data/test/metamodel_order_test.rb +10 -4
  52. data/test/metamodel_roundtrip_test.rb +2 -2
  53. data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +1 -1
  54. data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +50 -50
  55. data/test/method_delegation_test.rb +9 -9
  56. data/test/model_builder/ecore_internal.rb +19 -9
  57. data/test/model_builder/serializer_test.rb +1 -1
  58. data/test/reference_resolver_test.rb +79 -12
  59. data/test/rgen_test.rb +2 -0
  60. data/test/template_language_test.rb +7 -0
  61. data/test/template_language_test/templates/callback_indent_test/a.tpl +12 -0
  62. data/test/template_language_test/templates/callback_indent_test/b.tpl +5 -0
  63. data/test/testmodel/ea_testmodel_regenerated.xml +588 -583
  64. data/test/transformer_test.rb +3 -3
  65. data/test/util/file_cache_map_test.rb +91 -0
  66. data/test/util/file_cache_map_test/testdir/fileA +1 -0
  67. data/test/util_test.rb +4 -0
  68. data/test/xml_instantiator_test.rb +139 -135
  69. metadata +49 -104
  70. data/lib/rgen/ecore/ecore_instantiator.rb +0 -31
  71. data/lib/rgen/metamodel_builder/metamodel_description.rb +0 -232
  72. data/redist/xmlscan/ChangeLog +0 -1301
  73. data/redist/xmlscan/README +0 -34
  74. data/redist/xmlscan/THANKS +0 -11
  75. data/redist/xmlscan/doc/changes.html +0 -74
  76. data/redist/xmlscan/doc/changes.rd +0 -80
  77. data/redist/xmlscan/doc/en/conformance.html +0 -136
  78. data/redist/xmlscan/doc/en/conformance.rd +0 -152
  79. data/redist/xmlscan/doc/en/manual.html +0 -356
  80. data/redist/xmlscan/doc/en/manual.rd +0 -402
  81. data/redist/xmlscan/doc/ja/conformance.ja.html +0 -118
  82. data/redist/xmlscan/doc/ja/conformance.ja.rd +0 -134
  83. data/redist/xmlscan/doc/ja/manual.ja.html +0 -325
  84. data/redist/xmlscan/doc/ja/manual.ja.rd +0 -370
  85. data/redist/xmlscan/doc/src/Makefile +0 -41
  86. data/redist/xmlscan/doc/src/conformance.rd.src +0 -256
  87. data/redist/xmlscan/doc/src/langsplit.rb +0 -110
  88. data/redist/xmlscan/doc/src/manual.rd.src +0 -614
  89. data/redist/xmlscan/install.rb +0 -41
  90. data/redist/xmlscan/lib/xmlscan/encoding.rb +0 -311
  91. data/redist/xmlscan/lib/xmlscan/htmlscan.rb +0 -289
  92. data/redist/xmlscan/lib/xmlscan/namespace.rb +0 -352
  93. data/redist/xmlscan/lib/xmlscan/parser.rb +0 -299
  94. data/redist/xmlscan/lib/xmlscan/scanner.rb +0 -1109
  95. data/redist/xmlscan/lib/xmlscan/version.rb +0 -22
  96. data/redist/xmlscan/lib/xmlscan/visitor.rb +0 -158
  97. data/redist/xmlscan/lib/xmlscan/xmlchar.rb +0 -441
  98. data/redist/xmlscan/memo/CONFORMANCE +0 -1249
  99. data/redist/xmlscan/memo/PRODUCTIONS +0 -195
  100. data/redist/xmlscan/memo/contentspec.ry +0 -335
  101. data/redist/xmlscan/samples/chibixml.rb +0 -105
  102. data/redist/xmlscan/samples/getxmlchar.rb +0 -122
  103. data/redist/xmlscan/samples/rexml.rb +0 -159
  104. data/redist/xmlscan/samples/xmlbench.rb +0 -88
  105. data/redist/xmlscan/samples/xmlbench/parser/chibixml.rb +0 -22
  106. data/redist/xmlscan/samples/xmlbench/parser/nqxml.rb +0 -29
  107. data/redist/xmlscan/samples/xmlbench/parser/rexml.rb +0 -62
  108. data/redist/xmlscan/samples/xmlbench/parser/xmlparser.rb +0 -22
  109. data/redist/xmlscan/samples/xmlbench/parser/xmlscan-0.0.10.rb +0 -62
  110. data/redist/xmlscan/samples/xmlbench/parser/xmlscan-chibixml.rb +0 -22
  111. data/redist/xmlscan/samples/xmlbench/parser/xmlscan-rexml.rb +0 -22
  112. data/redist/xmlscan/samples/xmlbench/parser/xmlscan.rb +0 -99
  113. data/redist/xmlscan/samples/xmlbench/xmlbench-lib.rb +0 -116
  114. data/redist/xmlscan/samples/xmlconftest.rb +0 -200
  115. data/redist/xmlscan/test.rb +0 -7
  116. data/redist/xmlscan/tests/deftestcase.rb +0 -73
  117. data/redist/xmlscan/tests/runtest.rb +0 -47
  118. data/redist/xmlscan/tests/testall.rb +0 -14
  119. data/redist/xmlscan/tests/testencoding.rb +0 -438
  120. data/redist/xmlscan/tests/testhtmlscan.rb +0 -752
  121. data/redist/xmlscan/tests/testnamespace.rb +0 -457
  122. data/redist/xmlscan/tests/testparser.rb +0 -591
  123. data/redist/xmlscan/tests/testscanner.rb +0 -1749
  124. data/redist/xmlscan/tests/testxmlchar.rb +0 -143
  125. data/redist/xmlscan/tests/visitor.rb +0 -34
@@ -21,6 +21,8 @@ class JsonInstantiator
21
21
  # classes are looked for in metamodel package module +mm+,
22
22
  # +options+ include:
23
23
  # short_class_names: if true subpackages will be searched for unqualifed class names (default: true)
24
+ # ignore_keys: an array of json object key names which are to be ignored (default: none)
25
+ #
24
26
  # The options are also passed to the underlying QualifiedNameResolver.
25
27
  #
26
28
  def initialize(env, mm, options={})
@@ -28,6 +30,7 @@ class JsonInstantiator
28
30
  @mm = mm
29
31
  @options = options
30
32
  @short_class_names = !@options.has_key?(:short_class_names) || @options[:short_class_names]
33
+ @ignore_keys = @options[:ignore_keys] || []
31
34
  @unresolvedReferences = []
32
35
  @classes = {}
33
36
  @classes_flat = {}
@@ -42,15 +45,23 @@ class JsonInstantiator
42
45
  # Returns an array of ReferenceResolver::UnresolvedReference
43
46
  # describing the references which could not be resolved
44
47
  #
45
- def instantiate(str)
48
+ # Options:
49
+ # :root_elements: if an array is provided, it will be filled with the root elements
50
+ #
51
+ def instantiate(str, options={})
46
52
  root = @parser.parse(str)
53
+ if options[:root_elements].is_a?(Array)
54
+ options[:root_elements].clear
55
+ root.each{|r| options[:root_elements] << r}
56
+ end
47
57
  resolver = QualifiedNameResolver.new(root, @options)
48
58
  resolver.resolveReferences(@unresolvedReferences)
49
59
  end
50
60
 
51
61
  def createObject(hash)
52
62
  className = hash["_class"]
53
- raise "no class information" unless className
63
+ # hashes without a _class key are returned as is
64
+ return hash unless className
54
65
  if @classes[className]
55
66
  clazz = @classes[className].instanceClass
56
67
  elsif @short_class_names && @classes_flat[className]
@@ -59,6 +70,9 @@ class JsonInstantiator
59
70
  raise "class not found: #{className}"
60
71
  end
61
72
  hash.delete("_class")
73
+ @ignore_keys.each do |k|
74
+ hash.delete(k)
75
+ end
62
76
  urefs = []
63
77
  hash.keys.each do |k|
64
78
  f = eFeature(k, clazz)
@@ -1,157 +1,137 @@
1
- $:.unshift File.join(File.dirname(__FILE__),"..","..","..","redist","xmlscan","lib")
2
-
3
1
  require 'rgen/metamodel_builder'
4
2
  require 'rgen/instantiator/abstract_instantiator'
5
- require 'xmlscan/namespace'
3
+ require 'nokogiri'
6
4
 
7
5
  module RGen
8
6
 
9
7
  module Instantiator
10
8
 
11
9
  class NodebasedXMLInstantiator < AbstractInstantiator
12
-
13
- class << self
10
+
11
+ class << self
14
12
 
15
- # The prune level is the number of parent/children associations which
16
- # is kept when the instantiator ascents the XML tree.
17
- # If the level is 2, information for the node's children and the childrens'
18
- # children will be available as an XMLNodeDescriptor object.
19
- # If the level is 0 no pruning will take place, i.e. the whole information
20
- # is kept until the end of the instantiation process. 0 is default.
21
- def set_prune_level(level)
22
- @prune_level = level
23
- end
13
+ # The prune level is the number of parent/children associations which
14
+ # is kept when the instantiator ascents the XML tree.
15
+ # If the level is 2, information for the node's children and the childrens'
16
+ # children will be available as an XMLNodeDescriptor object.
17
+ # If the level is 0 no pruning will take place, i.e. the whole information
18
+ # is kept until the end of the instantiation process. 0 is default.
19
+ def set_prune_level(level)
20
+ @prune_level = level
21
+ end
24
22
 
25
- def prune_level # :nodoc:
26
- @prune_level ||= 0
27
- end
23
+ def prune_level # :nodoc:
24
+ @prune_level ||= 0
25
+ end
28
26
 
29
- end
27
+ end
30
28
 
31
- class XMLNodeDescriptor
32
- attr_reader :namespace, :qtag, :prefix, :tag, :parent, :attributes, :chardata
33
- attr_accessor :object, :children
34
-
35
- def initialize(ns, qtag, prefix, tag, parent, children, attributes)
36
- @namespace, @qtag, @prefix, @tag, @parent, @children, @attributes =
37
- ns, qtag, prefix, tag, parent, children, attributes
38
- @parent.children << self if @parent
39
- @chardata = []
40
- end
41
- end
42
-
43
- class XMLScanVisitor
44
- attr_reader :namespaces
45
-
46
- include XMLScan::NSVisitor
47
-
48
- def initialize(inst)
49
- @current_attributes = {}
50
- @instantiator = inst
51
- end
52
-
53
- def on_attribute_ns(qname, prefix, localpart)
54
- @current_attr_name = qname
55
- end
56
-
57
- def on_attr_value(str)
58
- @current_attributes[@current_attr_name] = str
59
- end
60
-
61
- def on_stag_end_ns(qname, namespaces)
62
- @namespaces = namespaces
63
- if qname =~ /^([^:]+):([^:]+)$/
64
- prefix, tag = $1, $2
65
- else
66
- prefix, tag = nil, qname
67
- end
68
- @instantiator.start_element(namespaces[prefix], qname, prefix, tag, @current_attributes)
69
- @current_attributes = {}
70
- end
71
-
72
- def on_stag_end_empty_ns(qname, namespaces)
73
- on_stag_end_ns(qname, namespaces)
74
- @instantiator.end_element
75
- end
76
-
77
- def on_etag(name)
78
- @instantiator.end_element
79
- end
80
-
81
- def on_chardata(str)
82
- @instantiator.on_chardata(str)
83
- end
84
- end
85
-
86
- def initialize(env)
87
- super
88
- @env = env
89
- @stack = []
90
- end
91
-
92
- def instantiate_file(file)
93
- File.open(file) { |f| parse(f.read)}
94
- resolve
95
- end
96
-
97
- def instantiate(text)
98
- parse(text)
99
- resolve
100
- end
101
-
102
- def parse(src)
103
- @visitor = XMLScanVisitor.new(self)
104
- parser = XMLScan::XMLParserNS.new(@visitor)
105
- parser.parse(src)
106
- @visitor = nil
107
- end
108
-
109
- def start_element(ns, qtag, prefix, tag, attributes)
110
- node = XMLNodeDescriptor.new(ns, qtag, prefix, tag, @stack[-1], [], attributes)
111
- @stack.push node
112
- on_descent(node)
113
- end
114
-
115
- def end_element
116
- node = @stack.pop
117
- on_ascent(node)
118
- prune_children(node, self.class.prune_level - 1) if self.class.prune_level > 0
119
- end
120
-
121
- def on_chardata(str)
122
- node = @stack.last
123
- node.chardata << str
124
- end
29
+ class XMLNodeDescriptor
30
+ attr_reader :namespace, :qtag, :prefix, :tag, :parent, :attributes, :chardata
31
+ attr_accessor :object, :children
32
+
33
+ def initialize(ns, qtag, prefix, tag, parent, children, attributes)
34
+ @namespace, @qtag, @prefix, @tag, @parent, @children, @attributes =
35
+ ns, qtag, prefix, tag, parent, children, attributes
36
+ @parent.children << self if @parent
37
+ @chardata = []
38
+ end
39
+ end
40
+
41
+ class Visitor < Nokogiri::XML::SAX::Document
42
+ attr_reader :namespaces
43
+
44
+ def initialize(inst)
45
+ @instantiator = inst
46
+ @namespaces = {}
47
+ end
48
+
49
+ def start_element_namespace(tag, attributes, prefix, uri, ns)
50
+ ns.each{|n| @namespaces[n[0]] = n[1]}
51
+ attrs = {}
52
+ attributes.each{|a| attrs[a.prefix ? a.prefix+":"+a.localname : a.localname] = a.value}
53
+ qname = prefix ? prefix+":"+tag : tag
54
+ @instantiator.start_element(uri, qname, prefix, tag, attrs)
55
+ end
56
+
57
+ def end_element(name)
58
+ @instantiator.end_element
59
+ end
60
+
61
+ def characters(str)
62
+ @instantiator.on_chardata(str)
63
+ end
64
+ end
65
+
66
+ def initialize(env)
67
+ super
68
+ @env = env
69
+ @stack = []
70
+ end
71
+
72
+ def instantiate_file(file)
73
+ File.open(file) { |f| parse(f.read)}
74
+ resolve
75
+ end
76
+
77
+ def instantiate(text)
78
+ parse(text)
79
+ resolve
80
+ end
81
+
82
+ def parse(src)
83
+ @visitor = Visitor.new(self)
84
+ parser = Nokogiri::XML::SAX::Parser.new(@visitor)
85
+ parser.parse(src)
86
+ @visitor = nil
87
+ end
88
+
89
+ def start_element(ns, qtag, prefix, tag, attributes)
90
+ node = XMLNodeDescriptor.new(ns, qtag, prefix, tag, @stack[-1], [], attributes)
91
+ @stack.push node
92
+ on_descent(node)
93
+ end
94
+
95
+ def end_element
96
+ node = @stack.pop
97
+ on_ascent(node)
98
+ prune_children(node, self.class.prune_level - 1) if self.class.prune_level > 0
99
+ end
100
+
101
+ def on_chardata(str)
102
+ node = @stack.last
103
+ node.chardata << str
104
+ end
125
105
 
126
- # This method is called when the XML parser goes down the tree.
127
- # An XMLNodeDescriptor +node+ describes the current node.
128
- # Implementing classes must overwrite this method.
129
- def on_descent(node)
130
- raise "Overwrite this method !"
131
- end
132
-
133
- # This method is called when the XML parser goes up the tree.
134
- # An XMLNodeDescriptor +node+ describes the current node.
135
- # Implementing classes must overwrite this method.
136
- def on_ascent(node)
137
- raise "Overwrite this method !"
138
- end
106
+ # This method is called when the XML parser goes down the tree.
107
+ # An XMLNodeDescriptor +node+ describes the current node.
108
+ # Implementing classes must overwrite this method.
109
+ def on_descent(node)
110
+ raise "Overwrite this method !"
111
+ end
112
+
113
+ # This method is called when the XML parser goes up the tree.
114
+ # An XMLNodeDescriptor +node+ describes the current node.
115
+ # Implementing classes must overwrite this method.
116
+ def on_ascent(node)
117
+ raise "Overwrite this method !"
118
+ end
139
119
 
140
- def namespaces
141
- @visitor.namespaces if @visitor
142
- end
120
+ def namespaces
121
+ @visitor.namespaces if @visitor
122
+ end
143
123
 
144
- private
145
-
146
- def prune_children(node, level)
147
- if level == 0
148
- node.children = nil
149
- else
150
- node.children.each { |c| prune_children(c, level-1) }
151
- end
152
- end
124
+ private
125
+
126
+ def prune_children(node, level)
127
+ if level == 0
128
+ node.children = nil
129
+ else
130
+ node.children.each { |c| prune_children(c, level-1) }
131
+ end
132
+ end
153
133
  end
154
134
 
155
135
  end
156
136
 
157
- end
137
+ end
@@ -6,7 +6,6 @@ module Instantiator
6
6
 
7
7
  # This is a resolver resolving element identifiers which are qualified names.
8
8
  class QualifiedNameResolver
9
- include ReferenceResolver
10
9
 
11
10
  attr_reader :nameAttribute
12
11
  attr_reader :separator
@@ -20,6 +19,7 @@ class QualifiedNameResolver
20
19
  @elementByQName = {}
21
20
  @visitedQName = {}
22
21
  @childReferences = {}
22
+ @resolverDelegate = ReferenceResolver.new(:identifier_resolver => method(:resolveIdentifier))
23
23
  end
24
24
 
25
25
  def resolveIdentifier(qualifiedName)
@@ -57,6 +57,10 @@ class QualifiedNameResolver
57
57
  @elementByQName[qualifiedName] ||= nil
58
58
  end
59
59
 
60
+ def resolveReferences(unresolvedReferences, problems=[])
61
+ @resolverDelegate.resolve(unresolvedReferences, :problems => problems)
62
+ end
63
+
60
64
  private
61
65
 
62
66
  def allNamedChildren(element)
@@ -2,41 +2,143 @@ module RGen
2
2
 
3
3
  module Instantiator
4
4
 
5
- # This module is meant to be mixed into a resolver class providing the method +resolveIdentifier+
6
- module ReferenceResolver
5
+ # The ReferenceResolver can be used to resolve unresolved references, i.e. instances
6
+ # of class UnresolvedReference
7
+ #
8
+ # There are two ways how this can be used:
9
+ # 1. the identifiers and associated model elements are added upfront using +add_identifier+
10
+ # 2. register an :identifier_resolver with the constructor, which will be invoked
11
+ # for every unresolved identifier
12
+ #
13
+ class ReferenceResolver
7
14
 
8
15
  # 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)
16
+ # This consists of the +element+ and metamodel +feature_name+ which hold/is to hold the
17
+ # reference and the +proxy+ object which is the placeholder for the reference.
18
+ # If the reference could not be resolved because the target type does not match the
19
+ # feature type, the flag +target_type_error+ will be set.
20
+ #
21
+ class UnresolvedReference
22
+ attr_reader :feature_name, :proxy
23
+ attr_accessor :element, :target_type_error
24
+ def initialize(element, feature_name, proxy)
25
+ @element = element
26
+ @feature_name = feature_name
27
+ @proxy = proxy
28
+ end
29
+ end
30
+
31
+ # Create a reference resolver, options:
32
+ #
33
+ # :identifier_resolver:
34
+ # a proc which is called with an identifier and which should return the associated element
35
+ # in case the identifier is not uniq, the proc may return multiple values
36
+ # default: lookup element in internal map
37
+ #
38
+ def initialize(options={})
39
+ @identifier_resolver = options[:identifier_resolver]
40
+ @identifier_map = {}
41
+ end
42
+
43
+ # Add an +identifer+ / +element+ pair which will be used for looking up unresolved identifers
44
+ def add_identifier(ident, element)
45
+ map_entry = @identifier_map[ident]
46
+ if map_entry
47
+ if map_entry.is_a?(Array)
48
+ map_entry << element
49
+ else
50
+ @identifier_map[ident] = [map_entry, element]
51
+ end
52
+ else
53
+ @identifier_map[ident] = element
54
+ end
55
+ end
56
+
57
+ # Tries to resolve the given +unresolved_refs+. If resolution is successful, the proxy object
58
+ # will be removed, otherwise there will be an error description in the problems array.
59
+ # In case the resolved target element's type is not valid for the given feature, the
60
+ # +target_type_error+ flag will be set on the unresolved reference.
61
+ # Returns an array of the references which are still unresolved. Options:
62
+ #
63
+ # :problems
64
+ # an array to which problems will be appended
65
+ #
66
+ # :on_resolve
67
+ # a proc which will be called for every sucessful resolution, receives the unresolved
68
+ # reference as well as to new target element
69
+ #
70
+ # :use_target_type
71
+ # use the expected target type to narrow the set of possible targets
72
+ # (i.e. ignore targets with wrong type)
73
+ #
74
+ def resolve(unresolved_refs, options={})
75
+ problems = options[:problems] || []
76
+ still_unresolved_refs = []
77
+ unresolved_refs.each do |ur|
78
+ if @identifier_resolver
79
+ target = @identifier_resolver.call(ur.proxy.targetIdentifier)
80
+ else
81
+ target = @identifier_map[ur.proxy.targetIdentifier]
82
+ end
83
+ target = [target].compact unless target.is_a?(Array)
84
+ if options[:use_target_type]
85
+ feature = ur.element.class.ecore.eAllReferences.find{|r| r.name == ur.feature_name}
86
+ target = target.select{|e| e.is_a?(feature.eType.instanceClass)}
87
+ end
88
+ if target.size == 1
89
+ refs = ur.element.getGeneric(ur.feature_name)
90
+ if refs.is_a?(Array)
91
+ index = refs.index(ur.proxy)
92
+ ur.element.removeGeneric(ur.feature_name, ur.proxy)
93
+ begin
94
+ ur.element.addGeneric(ur.feature_name, target[0], index)
95
+ options[:on_resolve] && options[:on_resolve].call(ur, target[0])
96
+ rescue StandardError => e
97
+ if is_type_error?(e)
98
+ ur.element.addGeneric(ur.feature_name, ur.proxy, index)
99
+ ur.target_type_error = true
100
+ problems << type_error_message(target[0])
101
+ still_unresolved_refs << ur
102
+ else
103
+ raise
104
+ end
105
+ end
25
106
  else
26
- # this will replace the proxy
27
- ur.element.setGeneric(ur.featureName, target)
107
+ begin
108
+ # this will replace the proxy
109
+ ur.element.setGeneric(ur.feature_name, target[0])
110
+ options[:on_resolve] && options[:on_resolve].call(ur, target[0])
111
+ rescue StandardError => e
112
+ if is_type_error?(e)
113
+ ur.target_type_error = true
114
+ problems << type_error_message(target[0])
115
+ still_unresolved_refs << ur
116
+ else
117
+ raise
118
+ end
119
+ end
28
120
  end
29
- elsif target
121
+ elsif target.size > 1
30
122
  problems << "identifier #{ur.proxy.targetIdentifier} not uniq"
31
- stillUnresolvedReferences << ur
123
+ still_unresolved_refs << ur
32
124
  else
33
125
  problems << "identifier #{ur.proxy.targetIdentifier} not found"
34
- stillUnresolvedReferences << ur
126
+ still_unresolved_refs << ur
35
127
  end
36
128
  end
37
- stillUnresolvedReferences
129
+ still_unresolved_refs
38
130
  end
39
131
 
132
+ private
133
+
134
+ def is_type_error?(e)
135
+ e.message =~ /Can not use a .* where a .* is expected/
136
+ end
137
+
138
+ def type_error_message(target)
139
+ "invalid target type #{target.class}"
140
+ end
141
+
40
142
  end
41
143
 
42
144
  end