rgen 0.5.4 → 0.6.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.
- data/CHANGELOG +28 -0
- data/Rakefile +3 -4
- data/lib/ea_support/uml13_ea_metamodel.rb +3 -3
- data/lib/ea_support/uml13_ea_to_uml13.rb +33 -2
- data/lib/ea_support/uml13_to_uml13_ea.rb +7 -0
- data/lib/mmgen/mm_ext/ecore_mmgen_ext.rb +4 -4
- data/lib/mmgen/templates/metamodel_generator.tpl +143 -143
- data/lib/rgen/ecore/ecore.rb +11 -1
- data/lib/rgen/ecore/ecore_interface.rb +47 -0
- data/lib/rgen/ecore/ecore_to_ruby.rb +166 -0
- data/lib/rgen/ecore/{ecore_transformer.rb → ruby_to_ecore.rb} +11 -11
- data/lib/rgen/environment.rb +15 -2
- data/lib/rgen/fragment/dump_file_cache.rb +63 -0
- data/lib/rgen/fragment/fragmented_model.rb +139 -0
- data/lib/rgen/fragment/model_fragment.rb +268 -0
- data/lib/rgen/instantiator/abstract_xml_instantiator.rb +44 -72
- data/lib/rgen/instantiator/default_xml_instantiator.rb +2 -2
- data/lib/rgen/instantiator/ecore_xml_instantiator.rb +16 -1
- data/lib/rgen/instantiator/json_instantiator.rb +16 -2
- data/lib/rgen/instantiator/nodebased_xml_instantiator.rb +118 -138
- data/lib/rgen/instantiator/qualified_name_resolver.rb +5 -1
- data/lib/rgen/instantiator/reference_resolver.rb +126 -24
- data/lib/rgen/instantiator/xmi11_instantiator.rb +6 -2
- data/lib/rgen/metamodel_builder.rb +18 -6
- data/lib/rgen/metamodel_builder/builder_extensions.rb +431 -407
- data/lib/rgen/metamodel_builder/builder_runtime.rb +8 -8
- data/lib/rgen/metamodel_builder/constant_order_helper.rb +4 -4
- data/lib/rgen/metamodel_builder/data_types.rb +5 -1
- data/lib/rgen/metamodel_builder/intermediate/feature.rb +167 -0
- data/lib/rgen/metamodel_builder/module_extension.rb +2 -2
- data/lib/rgen/model_builder.rb +10 -5
- data/lib/rgen/model_builder/builder_context.rb +17 -1
- data/lib/rgen/serializer/opposite_reference_filter.rb +18 -0
- data/lib/rgen/serializer/qualified_name_provider.rb +45 -0
- data/lib/rgen/template_language/template_container.rb +3 -1
- data/lib/rgen/{auto_class_creator.rb → util/auto_class_creator.rb} +6 -1
- data/lib/rgen/util/cached_glob.rb +67 -0
- data/lib/rgen/util/file_cache_map.rb +104 -0
- data/lib/rgen/util/file_change_detector.rb +78 -0
- data/lib/rgen/{method_delegation.rb → util/method_delegation.rb} +18 -3
- data/lib/rgen/{model_comparator.rb → util/model_comparator.rb} +17 -5
- data/lib/rgen/{model_comparator_base.rb → util/model_comparator_base.rb} +6 -1
- data/lib/rgen/{model_dumper.rb → util/model_dumper.rb} +6 -1
- data/lib/rgen/{name_helper.rb → util/name_helper.rb} +6 -1
- data/lib/rgen/util/pattern_matcher.rb +329 -0
- data/lib/transformers/uml13_to_ecore.rb +103 -60
- data/test/ecore_self_test.rb +43 -42
- data/test/json_test.rb +15 -0
- data/test/metamodel_builder_test.rb +361 -206
- data/test/metamodel_from_ecore_test.rb +45 -0
- data/test/metamodel_order_test.rb +10 -4
- data/test/metamodel_roundtrip_test.rb +2 -2
- data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +1 -1
- data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +50 -50
- data/test/method_delegation_test.rb +9 -9
- data/test/model_builder/ecore_internal.rb +19 -9
- data/test/model_builder/serializer_test.rb +1 -1
- data/test/reference_resolver_test.rb +79 -12
- data/test/rgen_test.rb +2 -0
- data/test/template_language_test.rb +7 -0
- data/test/template_language_test/templates/callback_indent_test/a.tpl +12 -0
- data/test/template_language_test/templates/callback_indent_test/b.tpl +5 -0
- data/test/testmodel/ea_testmodel_regenerated.xml +588 -583
- data/test/transformer_test.rb +3 -3
- data/test/util/file_cache_map_test.rb +91 -0
- data/test/util/file_cache_map_test/testdir/fileA +1 -0
- data/test/util_test.rb +4 -0
- data/test/xml_instantiator_test.rb +139 -135
- metadata +49 -104
- data/lib/rgen/ecore/ecore_instantiator.rb +0 -31
- data/lib/rgen/metamodel_builder/metamodel_description.rb +0 -232
- data/redist/xmlscan/ChangeLog +0 -1301
- data/redist/xmlscan/README +0 -34
- data/redist/xmlscan/THANKS +0 -11
- data/redist/xmlscan/doc/changes.html +0 -74
- data/redist/xmlscan/doc/changes.rd +0 -80
- data/redist/xmlscan/doc/en/conformance.html +0 -136
- data/redist/xmlscan/doc/en/conformance.rd +0 -152
- data/redist/xmlscan/doc/en/manual.html +0 -356
- data/redist/xmlscan/doc/en/manual.rd +0 -402
- data/redist/xmlscan/doc/ja/conformance.ja.html +0 -118
- data/redist/xmlscan/doc/ja/conformance.ja.rd +0 -134
- data/redist/xmlscan/doc/ja/manual.ja.html +0 -325
- data/redist/xmlscan/doc/ja/manual.ja.rd +0 -370
- data/redist/xmlscan/doc/src/Makefile +0 -41
- data/redist/xmlscan/doc/src/conformance.rd.src +0 -256
- data/redist/xmlscan/doc/src/langsplit.rb +0 -110
- data/redist/xmlscan/doc/src/manual.rd.src +0 -614
- data/redist/xmlscan/install.rb +0 -41
- data/redist/xmlscan/lib/xmlscan/encoding.rb +0 -311
- data/redist/xmlscan/lib/xmlscan/htmlscan.rb +0 -289
- data/redist/xmlscan/lib/xmlscan/namespace.rb +0 -352
- data/redist/xmlscan/lib/xmlscan/parser.rb +0 -299
- data/redist/xmlscan/lib/xmlscan/scanner.rb +0 -1109
- data/redist/xmlscan/lib/xmlscan/version.rb +0 -22
- data/redist/xmlscan/lib/xmlscan/visitor.rb +0 -158
- data/redist/xmlscan/lib/xmlscan/xmlchar.rb +0 -441
- data/redist/xmlscan/memo/CONFORMANCE +0 -1249
- data/redist/xmlscan/memo/PRODUCTIONS +0 -195
- data/redist/xmlscan/memo/contentspec.ry +0 -335
- data/redist/xmlscan/samples/chibixml.rb +0 -105
- data/redist/xmlscan/samples/getxmlchar.rb +0 -122
- data/redist/xmlscan/samples/rexml.rb +0 -159
- data/redist/xmlscan/samples/xmlbench.rb +0 -88
- data/redist/xmlscan/samples/xmlbench/parser/chibixml.rb +0 -22
- data/redist/xmlscan/samples/xmlbench/parser/nqxml.rb +0 -29
- data/redist/xmlscan/samples/xmlbench/parser/rexml.rb +0 -62
- data/redist/xmlscan/samples/xmlbench/parser/xmlparser.rb +0 -22
- data/redist/xmlscan/samples/xmlbench/parser/xmlscan-0.0.10.rb +0 -62
- data/redist/xmlscan/samples/xmlbench/parser/xmlscan-chibixml.rb +0 -22
- data/redist/xmlscan/samples/xmlbench/parser/xmlscan-rexml.rb +0 -22
- data/redist/xmlscan/samples/xmlbench/parser/xmlscan.rb +0 -99
- data/redist/xmlscan/samples/xmlbench/xmlbench-lib.rb +0 -116
- data/redist/xmlscan/samples/xmlconftest.rb +0 -200
- data/redist/xmlscan/test.rb +0 -7
- data/redist/xmlscan/tests/deftestcase.rb +0 -73
- data/redist/xmlscan/tests/runtest.rb +0 -47
- data/redist/xmlscan/tests/testall.rb +0 -14
- data/redist/xmlscan/tests/testencoding.rb +0 -438
- data/redist/xmlscan/tests/testhtmlscan.rb +0 -752
- data/redist/xmlscan/tests/testnamespace.rb +0 -457
- data/redist/xmlscan/tests/testparser.rb +0 -591
- data/redist/xmlscan/tests/testscanner.rb +0 -1749
- data/redist/xmlscan/tests/testxmlchar.rb +0 -143
- 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
|
-
|
|
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
|
-
|
|
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 '
|
|
3
|
+
require 'nokogiri'
|
|
6
4
|
|
|
7
5
|
module RGen
|
|
8
6
|
|
|
9
7
|
module Instantiator
|
|
10
8
|
|
|
11
9
|
class NodebasedXMLInstantiator < AbstractInstantiator
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
|
|
11
|
+
class << self
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
def prune_level # :nodoc:
|
|
24
|
+
@prune_level ||= 0
|
|
25
|
+
end
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
end
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
120
|
+
def namespaces
|
|
121
|
+
@visitor.namespaces if @visitor
|
|
122
|
+
end
|
|
143
123
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
#
|
|
6
|
-
|
|
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 +
|
|
10
|
-
# and the proxy object which is the placeholder for the reference.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
123
|
+
still_unresolved_refs << ur
|
|
32
124
|
else
|
|
33
125
|
problems << "identifier #{ur.proxy.targetIdentifier} not found"
|
|
34
|
-
|
|
126
|
+
still_unresolved_refs << ur
|
|
35
127
|
end
|
|
36
128
|
end
|
|
37
|
-
|
|
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
|