rgen 0.4.6 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +95 -83
- data/Rakefile +4 -3
- data/lib/ea_support/ea_support.rb +54 -0
- data/lib/ea_support/id_store.rb +32 -0
- data/lib/ea_support/uml13_ea_metamodel.rb +562 -0
- data/lib/ea_support/uml13_ea_metamodel_ext.rb +45 -0
- data/lib/ea_support/uml13_ea_metamodel_generator.rb +43 -0
- data/lib/ea_support/uml13_ea_to_uml13.rb +72 -0
- data/lib/ea_support/uml13_to_uml13_ea.rb +82 -0
- data/lib/rgen/ecore/ecore.rb +16 -2
- data/lib/rgen/ecore/ecore_builder_methods.rb +81 -0
- data/lib/rgen/ecore/ecore_instantiator.rb +5 -1
- data/lib/rgen/metamodel_builder/builder_extensions.rb +11 -3
- data/lib/rgen/metamodel_builder/module_extension2.rb +205 -0
- data/lib/rgen/method_delegation.rb +99 -0
- data/lib/rgen/model_builder.rb +27 -0
- data/lib/rgen/model_builder/builder_context.rb +318 -0
- data/lib/rgen/model_builder/model_serializer.rb +201 -0
- data/lib/rgen/model_builder/reference_resolver.rb +156 -0
- data/lib/rgen/template_language/directory_template_container.rb +6 -2
- data/lib/rgen/template_language/output_handler.rb +2 -4
- data/lib/rgen/template_language/template_container.rb +212 -195
- data/lib/rgen/transformer.rb +95 -4
- data/lib/transformers/ecore_to_uml13.rb +66 -0
- data/lib/transformers/uml13_to_ecore.rb +16 -7
- data/test/ea_instantiator_test.rb +8 -14
- data/test/ea_serializer_test.rb +3 -9
- data/test/ea_serializer_test/ea_testmodel_regenerated.xml +2 -2
- data/test/ea_serializer_test/ea_testmodel_regenerated_import.log +3 -0
- data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +19 -19
- data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +44 -44
- data/test/method_delegation_test.rb +178 -0
- data/test/model_builder/builder_context_test.rb +59 -0
- data/test/model_builder/builder_test.rb +284 -0
- data/test/model_builder/ecore_internal.rb +103 -0
- data/test/model_builder/ecore_original.rb +163 -0
- data/test/model_builder/ecore_original_regenerated.rb +163 -0
- data/test/model_builder/reference_resolver_test.rb +156 -0
- data/test/model_builder/serializer_test.rb +63 -0
- data/test/model_builder_test.rb +4 -0
- data/test/rgen_test.rb +2 -0
- data/test/template_language_test.rb +41 -1
- data/test/template_language_test/expected_result1.txt +1 -3
- data/test/template_language_test/templates/define_local_test/local.tpl +8 -0
- data/test/template_language_test/templates/define_local_test/test.tpl +8 -0
- data/test/template_language_test/templates/evaluate_test/test.tpl +7 -0
- data/test/template_language_test/templates/no_indent_test/no_indent.tpl +3 -0
- data/test/template_language_test/templates/no_indent_test/sub1/no_indent.tpl +3 -0
- data/test/template_language_test/templates/no_indent_test/test.tpl +24 -0
- data/test/template_language_test/templates/no_indent_test/test2.tpl +13 -0
- data/test/template_language_test/templates/no_indent_test/test3.tpl +10 -0
- data/test/template_language_test/templates/template_resolution_test/sub1.tpl +9 -0
- data/test/template_language_test/templates/template_resolution_test/sub1/sub1.tpl +3 -0
- data/test/template_language_test/templates/template_resolution_test/test.tpl +4 -0
- data/test/template_language_test/testout.txt +1 -3
- data/test/testmodel/ea_testmodel_import.log +1 -0
- data/test/testmodel/ea_testmodel_regenerated.xml +808 -0
- data/test/transformer_test.rb +3 -5
- metadata +52 -3
- data/lib/instantiators/ea_instantiator.rb +0 -39
@@ -0,0 +1,45 @@
|
|
1
|
+
module UML13EA
|
2
|
+
class << self
|
3
|
+
attr_accessor :idStore
|
4
|
+
end
|
5
|
+
module ModelElement::ClassModule
|
6
|
+
def qualifiedName
|
7
|
+
_name = (respond_to?(:_name) ? self._name : name) || "unnamed"
|
8
|
+
_namespace = respond_to?(:_namespace) ? self._namespace : namespace
|
9
|
+
_namespace && _namespace.qualifiedName ? _namespace.qualifiedName+"::"+_name : _name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
module XmiIdProvider::ClassModule
|
13
|
+
def _xmi_id
|
14
|
+
UML13EA.idStore.idHash[qualifiedName] ||= "EAID_"+object_id.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
module Package::ClassModule
|
18
|
+
def _xmi_id
|
19
|
+
UML13EA.idStore.idHash[qualifiedName] ||= "EAPK_"+object_id.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
module Generalization::ClassModule
|
23
|
+
def _name
|
24
|
+
"#{subtype.name}_#{supertype.name}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
module Association::ClassModule
|
28
|
+
def _name
|
29
|
+
connection.collect{|c| "#{c.getType.name}_#{c.name}"}.sort.join("_")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
module AssociationEnd::ClassModule
|
33
|
+
def _name
|
34
|
+
"#{getType.name}_#{name}"
|
35
|
+
end
|
36
|
+
def _namespace
|
37
|
+
association
|
38
|
+
end
|
39
|
+
end
|
40
|
+
module StateVertex::ClassModule
|
41
|
+
def _namespace
|
42
|
+
container
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'metamodels/uml13_metamodel'
|
2
|
+
require 'mmgen/metamodel_generator'
|
3
|
+
require 'rgen/transformer'
|
4
|
+
require 'rgen/environment'
|
5
|
+
require 'rgen/ecore/ecore'
|
6
|
+
|
7
|
+
include MMGen::MetamodelGenerator
|
8
|
+
|
9
|
+
class ECoreCopyTransformer < RGen::Transformer
|
10
|
+
copy_all RGen::ECore
|
11
|
+
end
|
12
|
+
|
13
|
+
eaMMRoot = ECoreCopyTransformer.new.trans(UML13.ecore)
|
14
|
+
|
15
|
+
eaMMRoot.name = "UML13EA"
|
16
|
+
eaMMRoot.eClassifiers.find{|c| c.name == "ActivityGraph"}.name = "ActivityModel"
|
17
|
+
eaMMRoot.eClassifiers.find{|c| c.name == "Pseudostate"}.name = "PseudoState"
|
18
|
+
|
19
|
+
compositeState = eaMMRoot.eClassifiers.find{|c| c.name == "CompositeState"}
|
20
|
+
compositeState.eReferences.find{|r| r.name == "subvertex"}.name = "substate"
|
21
|
+
|
22
|
+
generalization = eaMMRoot.eClassifiers.find{|c| c.name == "Generalization"}
|
23
|
+
generalization.eReferences.find{|r| r.name == "parent"}.name = "supertype"
|
24
|
+
generalization.eReferences.find{|r| r.name == "child"}.name = "subtype"
|
25
|
+
|
26
|
+
assocEnd = eaMMRoot.eClassifiers.find{|c| c.name == "AssociationEnd"}
|
27
|
+
assocEnd.eAttributes.find{|r| r.name == "ordering"}.name = "isOrdered"
|
28
|
+
assocEnd.eAttributes.find{|r| r.name == "changeability"}.name = "changeable"
|
29
|
+
assocEnd.eAttributes.find{|r| r.name == "isOrdered"}.eType = RGen::ECore::EBoolean
|
30
|
+
assocEnd.eAttributes.find{|r| r.name == "changeable"}.eType.eLiterals.find{|l| l.name == "frozen"}.name = "none"
|
31
|
+
multRef = assocEnd.eStructuralFeatures.find{|f| f.name == "multiplicity"}
|
32
|
+
multRef.eType = nil
|
33
|
+
assocEnd.removeEStructuralFeatures(multRef)
|
34
|
+
assocEnd.addEStructuralFeatures(RGen::ECore::EAttribute.new(:name => "multiplicity", :eType => RGen::ECore::EString))
|
35
|
+
|
36
|
+
xmiIdProvider = RGen::ECore::EClass.new(:name => "XmiIdProvider", :ePackage => eaMMRoot)
|
37
|
+
eaMMRoot.eClassifiers.each do |c|
|
38
|
+
if %w(Package Class Generalization Association AssociationEnd StateVertex).include?(c.name)
|
39
|
+
c.addESuperTypes(xmiIdProvider)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
generateMetamodel(eaMMRoot, File.dirname(__FILE__)+"/uml13_ea_metamodel.rb")
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rgen/transformer'
|
2
|
+
require 'metamodels/uml13_metamodel'
|
3
|
+
require 'ea_support/uml13_ea_metamodel'
|
4
|
+
|
5
|
+
class UML13EAToUML13 < RGen::Transformer
|
6
|
+
include UML13EA
|
7
|
+
|
8
|
+
def transform
|
9
|
+
trans(:class => Package)
|
10
|
+
trans(:class => Class)
|
11
|
+
end
|
12
|
+
|
13
|
+
def cleanModel
|
14
|
+
@env_out.find(:class => UML13::ModelElement).each do |me|
|
15
|
+
me.taggedValue = []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
copy_all UML13EA, :to => UML13, :except => %w(
|
20
|
+
XmiIdProvider
|
21
|
+
AssociationEnd AssociationEndRole
|
22
|
+
StructuralFeature
|
23
|
+
Generalization
|
24
|
+
ActivityModel
|
25
|
+
CompositeState
|
26
|
+
PseudoState
|
27
|
+
)
|
28
|
+
|
29
|
+
transform AssociationEndRole, :to => UML13::AssociationEndRole do
|
30
|
+
copyAssociationEnd
|
31
|
+
end
|
32
|
+
|
33
|
+
transform AssociationEnd, :to => UML13::AssociationEnd do
|
34
|
+
copyAssociationEnd
|
35
|
+
end
|
36
|
+
|
37
|
+
def copyAssociationEnd
|
38
|
+
copy_features :except => [:isOrdered, :changeable] do
|
39
|
+
{:ordering => isOrdered ? :ordered : :unordered,
|
40
|
+
:changeability => {:none => :frozen}[changeable] || changeable,
|
41
|
+
:aggregation => {:shared => :aggregate}[aggregation] || aggregation,
|
42
|
+
:multiplicity => UML13::Multiplicity.new(
|
43
|
+
:range => [UML13::MultiplicityRange.new(
|
44
|
+
:lower => multiplicity && multiplicity.split("..").first,
|
45
|
+
:upper => multiplicity && multiplicity.split("..").last)])}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
transform StructuralFeature, :to => UML13::StructuralFeature do
|
50
|
+
copy_features :except => [:changeable] do
|
51
|
+
{:changeability => {:none => :frozen}[changeable] }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
transform Generalization, :to => UML13::Generalization do
|
56
|
+
copy_features :except => [:subtype, :supertype] do
|
57
|
+
{ :child => trans(subtype),
|
58
|
+
:parent => trans(supertype) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
copy ActivityModel, :to => UML13::ActivityGraph
|
63
|
+
|
64
|
+
transform CompositeState, :to => UML13::CompositeState do
|
65
|
+
copy_features :except => [:substate] do
|
66
|
+
{ :subvertex => trans(substate) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
copy PseudoState, :to => UML13::Pseudostate
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rgen/transformer'
|
2
|
+
require 'metamodels/uml13_metamodel'
|
3
|
+
require 'ea_support/uml13_ea_metamodel'
|
4
|
+
require 'ea_support/uml13_ea_metamodel_ext'
|
5
|
+
|
6
|
+
class UML13ToUML13EA < RGen::Transformer
|
7
|
+
include UML13
|
8
|
+
|
9
|
+
def transform
|
10
|
+
trans(:class => Package)
|
11
|
+
trans(:class => Class)
|
12
|
+
end
|
13
|
+
|
14
|
+
copy_all UML13, :to => UML13EA, :except => %w(
|
15
|
+
ActivityGraph
|
16
|
+
CompositeState SimpleState
|
17
|
+
Class
|
18
|
+
Association AssociationEnd AssociationEndRole
|
19
|
+
Generalization
|
20
|
+
Pseudostate
|
21
|
+
)
|
22
|
+
|
23
|
+
copy ActivityGraph, :to => UML13EA::ActivityModel
|
24
|
+
|
25
|
+
copy Pseudostate, :to => UML13EA::PseudoState
|
26
|
+
|
27
|
+
transform CompositeState, :to => UML13EA::CompositeState do
|
28
|
+
copy_features :except => [:subvertex] do
|
29
|
+
{ :substate => trans(subvertex) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
transform SimpleState, :to => UML13EA::SimpleState do
|
34
|
+
copy_features :except => [:container] do
|
35
|
+
{ :taggedValue => trans(taggedValue) +
|
36
|
+
[@env_out.new(UML13EA::TaggedValue, :tag => "ea_stype", :value => "State")] +
|
37
|
+
(container ? [ @env_out.new(UML13EA::TaggedValue, :tag => "owner", :value => trans(container)._xmi_id)] : []) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
transform Class, :to => UML13EA::Class do
|
42
|
+
copy_features do
|
43
|
+
{ :taggedValue => trans(taggedValue) + [@env_out.new(UML13EA::TaggedValue, :tag => "ea_stype", :value => "Class")]}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
transform Association, :to => UML13EA::Association do
|
48
|
+
copy_features do
|
49
|
+
{ :connection => trans(connection[1].isNavigable ? [connection[0], connection[1]] : [connection[1], connection[0]]),
|
50
|
+
:taggedValue => trans(taggedValue) + [
|
51
|
+
@env_out.new(UML13EA::TaggedValue, :tag => "ea_type", :value => "Association"),
|
52
|
+
@env_out.new(UML13EA::TaggedValue, :tag => "direction", :value =>
|
53
|
+
connection.all?{|c| c.isNavigable} ? "Bi-Directional" : "Source -> Destination")] }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
transform AssociationEnd, :to => UML13EA::AssociationEnd do
|
58
|
+
copyAssociationEnd
|
59
|
+
end
|
60
|
+
|
61
|
+
transform AssociationEndRole, :to => UML13EA::AssociationEndRole do
|
62
|
+
copyAssociationEnd
|
63
|
+
end
|
64
|
+
|
65
|
+
def copyAssociationEnd
|
66
|
+
_lower = multiplicity && multiplicity.range.first.lower
|
67
|
+
_upper = multiplicity && multiplicity.range.first.upper
|
68
|
+
copy_features :except => [:multiplicity, :ordering, :changeability] do
|
69
|
+
{ :multiplicity => _lower == _upper ? _lower : "#{_lower}..#{_upper}",
|
70
|
+
:isOrdered => ordering == :ordered,
|
71
|
+
:changeable => :none } #{:frozen => :none}[changeability] || changeability}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
transform Generalization, :to => UML13EA::Generalization do
|
76
|
+
copy_features :except => [:child, :parent] do
|
77
|
+
{ :taggedValue => trans(taggedValue) + [@env_out.new(UML13EA::TaggedValue, :tag => "ea_type", :value => "Generalization")],
|
78
|
+
:subtype => trans(child),
|
79
|
+
:supertype => trans(parent)}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/rgen/ecore/ecore.rb
CHANGED
@@ -52,7 +52,21 @@ module RGen
|
|
52
52
|
has_attr 'volatile', Boolean, :defaultValueLiteral => "false"
|
53
53
|
module ClassModule
|
54
54
|
def defaultValue_derived
|
55
|
-
|
55
|
+
return nil if defaultValueLiteral.nil?
|
56
|
+
case eType
|
57
|
+
when EInt
|
58
|
+
defaultValueLiteral.to_i
|
59
|
+
when EFloat
|
60
|
+
defaultValueLiteral.to_f
|
61
|
+
when EEnum
|
62
|
+
defaultValueLiteral.to_sym
|
63
|
+
when EBoolean
|
64
|
+
defaultValueLiteral == "true"
|
65
|
+
when EString
|
66
|
+
defaultValueLiteral
|
67
|
+
else
|
68
|
+
raise "Unhandled type"
|
69
|
+
end
|
56
70
|
end
|
57
71
|
end
|
58
72
|
end
|
@@ -187,4 +201,4 @@ module RGen
|
|
187
201
|
ECore::EAttribute.has_one 'eAttributeType', ECore::EDataType, :lowerBound=>1, :derived=>true
|
188
202
|
ECore::EReference.has_one 'eReferenceType', ECore::EClass, :lowerBound=>1, :derived=>true
|
189
203
|
|
190
|
-
end
|
204
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module RGen
|
2
|
+
|
3
|
+
module ECore
|
4
|
+
|
5
|
+
module ECoreBuilderMethods
|
6
|
+
def eAttr(name, type, argHash={}, &block)
|
7
|
+
eAttribute(name, {:eType => type}.merge(argHash), &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def eRef(name, type, argHash={}, &block)
|
11
|
+
eReference(name, {:eType => type}.merge(argHash), &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
# create bidirectional reference at once
|
15
|
+
def eBiRef(name, type, oppositeName, argHash={})
|
16
|
+
raise BuilderError.new("eOpposite attribute not allowed for bidirectional references") \
|
17
|
+
if argHash[:eOpposite] || argHash[:opposite_eOpposite]
|
18
|
+
eReference(name, {:eType => type}.merge(argHash.reject{|k,v| k.to_s =~ /^opposite_/})) do
|
19
|
+
eReference oppositeName, {:eContainingClass => type, :eType => _context(2),
|
20
|
+
:as => :eOpposite, :eOpposite => _context(1)}.
|
21
|
+
merge(Hash[*(argHash.select{|k,v| k.to_s =~ /^opposite_/}.
|
22
|
+
collect{|p| [p[0].to_s.sub(/^opposite_/,"").to_sym, p[1]]}.flatten)])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# reference shortcuts
|
27
|
+
|
28
|
+
alias references_1 eRef
|
29
|
+
alias references_one eRef
|
30
|
+
|
31
|
+
def references_N(name, type, argHash={})
|
32
|
+
eRef(name, type, {:upperBound => -1}.merge(argHash))
|
33
|
+
end
|
34
|
+
alias references_many references_N
|
35
|
+
|
36
|
+
def references_1to1(name, type, oppositeName, argHash={})
|
37
|
+
eBiRef(name, type, oppositeName, {:upperBound => 1, :opposite_upperBound => 1}.merge(argHash))
|
38
|
+
end
|
39
|
+
alias references_one_to_one references_1to1
|
40
|
+
|
41
|
+
def references_1toN(name, type, oppositeName, argHash={})
|
42
|
+
eBiRef(name, type, oppositeName, {:upperBound => -1, :opposite_upperBound => 1}.merge(argHash))
|
43
|
+
end
|
44
|
+
alias references_one_to_many references_1toN
|
45
|
+
|
46
|
+
def references_Nto1(name, type, oppositeName, argHash={})
|
47
|
+
eBiRef(name, type, oppositeName, {:upperBound => 1, :opposite_upperBound => -1}.merge(argHash))
|
48
|
+
end
|
49
|
+
alias references_many_to_one references_Nto1
|
50
|
+
|
51
|
+
def references_MtoN(name, type, oppositeName, argHash={})
|
52
|
+
eBiRef(name, type, oppositeName, {:upperBound => -1, :opposite_upperBound => -1}.merge(argHash))
|
53
|
+
end
|
54
|
+
alias references_many_to_many references_MtoN
|
55
|
+
|
56
|
+
# containment reference shortcuts
|
57
|
+
|
58
|
+
def contains_1(name, type, argHash={})
|
59
|
+
references_1(name, type, {:containment => true}.merge(argHash))
|
60
|
+
end
|
61
|
+
alias contains_one contains_1
|
62
|
+
|
63
|
+
def contains_N(name, type, argHash={})
|
64
|
+
references_N(name, type, {:containment => true}.merge(argHash))
|
65
|
+
end
|
66
|
+
alias contains_many contains_N
|
67
|
+
|
68
|
+
def contains_1to1(name, type, oppositeName, argHash={})
|
69
|
+
references_1to1(name, type, oppositeName, {:containment => true}.merge(argHash))
|
70
|
+
end
|
71
|
+
alias contains_one_to_one contains_1to1
|
72
|
+
|
73
|
+
def contains_1toN(name, type, oppositeName, argHash={})
|
74
|
+
references_1toN(name, type, oppositeName, {:containment => true}.merge(argHash))
|
75
|
+
end
|
76
|
+
alias contains_one_to_many contains_1toN
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -217,7 +217,15 @@ module BuilderExtensions
|
|
217
217
|
|
218
218
|
def _metamodel_description # :nodoc:
|
219
219
|
@metamodel_description ||= []
|
220
|
-
|
220
|
+
end
|
221
|
+
|
222
|
+
def _add_metamodel_description(desc) # :nodoc
|
223
|
+
@metamodel_description ||= []
|
224
|
+
@metamodelDescriptionByName ||= {}
|
225
|
+
@metamodel_description.delete(@metamodelDescriptionByName[desc.value(:name)])
|
226
|
+
@metamodel_description << desc
|
227
|
+
@metamodelDescriptionByName[desc.value(:name)] = desc
|
228
|
+
end
|
221
229
|
|
222
230
|
def abstract
|
223
231
|
@abstract = true
|
@@ -236,7 +244,7 @@ module BuilderExtensions
|
|
236
244
|
# Central builder method
|
237
245
|
#
|
238
246
|
def _build_internal(props1, props2=nil)
|
239
|
-
|
247
|
+
_add_metamodel_description(props1)
|
240
248
|
if props1.is_a?(ReferenceDescription) && props1.many?
|
241
249
|
_build_many_methods(props1, props2)
|
242
250
|
else
|
@@ -246,7 +254,7 @@ module BuilderExtensions
|
|
246
254
|
# this is a bidirectional reference
|
247
255
|
props1.opposite, props2.opposite = props2, props1
|
248
256
|
other_class = props1.impl_type
|
249
|
-
other_class.
|
257
|
+
other_class._add_metamodel_description(props2)
|
250
258
|
raise "Internal error: second description must be a ReferenceDescription" \
|
251
259
|
unless props2.is_a?(ReferenceDescription)
|
252
260
|
if props2.many?
|
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'rgen/ecore/ecore_ext'
|
2
|
+
|
3
|
+
module RGen
|
4
|
+
|
5
|
+
module ModelBuilder
|
6
|
+
|
7
|
+
class BuilderContext
|
8
|
+
attr_accessor :contextElement, :resolver
|
9
|
+
end
|
10
|
+
|
11
|
+
class ReferenceResolver
|
12
|
+
ResolverJob = Struct.new(:receiver, :reference, :namespace, :string)
|
13
|
+
|
14
|
+
class ResolverException < Exception
|
15
|
+
end
|
16
|
+
|
17
|
+
def addJob(job)
|
18
|
+
@jobs ||= []
|
19
|
+
@jobs << job
|
20
|
+
end
|
21
|
+
|
22
|
+
def resolve(env=nil)
|
23
|
+
(@jobs || []).each do |job|
|
24
|
+
begin
|
25
|
+
target = resolveReference(env, job.namespace, job.string.split("."), job.reference.eType.instanceClass, true)
|
26
|
+
rescue ResolverException => e
|
27
|
+
raise ResolverException.new("Can not resolve reference #{job.string}: #{e.message}")
|
28
|
+
end
|
29
|
+
if job.reference.many
|
30
|
+
job.receiver.addGeneric(job.reference.name, target)
|
31
|
+
else
|
32
|
+
job.receiver.setGeneric(job.reference.name, target)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def resolveReference(env, namespace, nameParts, targetClass, toplevel=false)
|
40
|
+
#puts "resolveReferences nameParts " + nameParts.inspect + " namespace #{namespace}"
|
41
|
+
firstPart, *restParts = nameParts
|
42
|
+
if namespace
|
43
|
+
children = elementChildren(namespace)
|
44
|
+
#puts children.inspect
|
45
|
+
#puts "looking for a element named #{firstPart}"
|
46
|
+
elements = children.select{|e| e.respond_to?(:name) && e.name == firstPart}
|
47
|
+
#puts elements.inspect
|
48
|
+
#puts elementParents(namespace).inspect
|
49
|
+
if elements.empty? && elementParents(namespace).size > 0 && toplevel
|
50
|
+
#puts "Trying parent"
|
51
|
+
raise ResolverException.new("Element #{namespace} has multiple parents") if elementParents(namespace).size > 1
|
52
|
+
elements << resolveReference(env, elementParents(namespace).first, nameParts, targetClass, true)
|
53
|
+
else
|
54
|
+
where = "within children of #{namespace}"
|
55
|
+
where += " named #{namespace.name}" if namespace.respond_to?(:name)
|
56
|
+
end
|
57
|
+
elsif env
|
58
|
+
elements = env.find(:class => targetClass, :name => firstPart)
|
59
|
+
where = "in environment"
|
60
|
+
else
|
61
|
+
raise ResolverException.new("Neither namespace nor environment specified")
|
62
|
+
end
|
63
|
+
raise ResolverException.new("Can not find element named #{firstPart} #{where}") if elements.size == 0
|
64
|
+
raise ResolverException.new("Multiple elements named #{firstPart} found #{where}") if elements.size > 1
|
65
|
+
if restParts.size > 0
|
66
|
+
resolveReference(env, elements.first, restParts, targetClass)
|
67
|
+
else
|
68
|
+
elements.first
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def elementChildren(element)
|
73
|
+
@elementChildren ||= {}
|
74
|
+
return @elementChildren[element] if @elementChildren[element]
|
75
|
+
children = element.class.ecore.eAllReferences.select{|r| r.containment}.collect do |r|
|
76
|
+
element.getGeneric(r.name)
|
77
|
+
end.flatten.compact
|
78
|
+
@elementChildren[element] = children
|
79
|
+
end
|
80
|
+
|
81
|
+
def elementParents(element)
|
82
|
+
@elementParents ||= {}
|
83
|
+
return @elementParents[element] if @elementParents[element]
|
84
|
+
parents = element.class.ecore.eAllReferences.select{|r| r.eOpposite && r.eOpposite.containment}.collect do |r|
|
85
|
+
element.getGeneric(r.name)
|
86
|
+
end.flatten.compact
|
87
|
+
@elementParents[element] = parents
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# this module takes up helper methods to avoid littering the package modules
|
92
|
+
module Helper
|
93
|
+
class << self
|
94
|
+
def processArguments(args)
|
95
|
+
unless (args.size == 2 && args.first.is_a?(String) && args.last.is_a?(Hash)) ||
|
96
|
+
(args.size == 1 && (args.first.is_a?(String) || args.first.is_a?(Hash)))
|
97
|
+
raise "Provide a Hash to set feature values, " +
|
98
|
+
"optionally the first argument may be a String specifying " +
|
99
|
+
"the value of the \"name\" attribute."
|
100
|
+
end
|
101
|
+
if args.last.is_a?(Hash)
|
102
|
+
argHash = args.last
|
103
|
+
else
|
104
|
+
argHash = {}
|
105
|
+
end
|
106
|
+
argHash[:name] ||= args.first if args.first.is_a?(String)
|
107
|
+
argHash
|
108
|
+
end
|
109
|
+
|
110
|
+
def processArgHash(argHash, eClass)
|
111
|
+
resolverJobs = []
|
112
|
+
asRole = nil
|
113
|
+
argHash.each_pair do |k,v|
|
114
|
+
if k == :as
|
115
|
+
asRole = v
|
116
|
+
argHash.delete(k)
|
117
|
+
elsif v.is_a?(String)
|
118
|
+
ref = eClass.eAllReferences.find{|r| r.name == k.to_s}
|
119
|
+
if ref
|
120
|
+
argHash.delete(k)
|
121
|
+
resolverJobs << ReferenceResolver::ResolverJob.new(nil, ref, nil, v)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
[ resolverJobs, asRole ]
|
126
|
+
end
|
127
|
+
|
128
|
+
def associateWithContextElement(element, contextElement, asRole)
|
129
|
+
return unless contextElement
|
130
|
+
contextClass = contextElement.class.ecore
|
131
|
+
if asRole
|
132
|
+
asRoleRef = contextClass.eAllReferences.find{|r| r.name == asRole.to_s}
|
133
|
+
raise "Context class #{contextClass.name} has no reference named #{asRole}" unless asRoleRef
|
134
|
+
ref = asRoleRef
|
135
|
+
else
|
136
|
+
possibleContainmentRefs = contextClass.eAllReferences.select { |r| r.containment &&
|
137
|
+
(element.class.ecore.eAllSuperTypes << element.class.ecore).include?(r.eType) }
|
138
|
+
if possibleContainmentRefs.size == 1
|
139
|
+
ref = possibleContainmentRefs.first
|
140
|
+
elsif possibleContainmentRefs.size == 0
|
141
|
+
raise "Context class #{contextClass.name} can not contain a #{element.class.ecore.name}"
|
142
|
+
else
|
143
|
+
raise "Context class #{contextClass.name} has several containment references to a #{element.class.ecore.name}." +
|
144
|
+
" Clearify using \":as => <role>\""
|
145
|
+
end
|
146
|
+
end
|
147
|
+
if ref.many
|
148
|
+
contextElement.addGeneric(ref.name, element)
|
149
|
+
else
|
150
|
+
contextElement.setGeneric(ref.name, element)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
module MetamodelBuilder
|
159
|
+
|
160
|
+
module ModuleExtension
|
161
|
+
|
162
|
+
def method_missing(m, *args, &block)
|
163
|
+
return super unless self.ecore.is_a?(RGen::ECore::EPackage)
|
164
|
+
className = m.to_s[0..0].upcase + m.to_s[1..-1]
|
165
|
+
eClass = self.ecore.eClasses.find{|c| c.name == className}
|
166
|
+
return super unless eClass
|
167
|
+
argHash = ModelBuilder::Helper.processArguments(args)
|
168
|
+
resolverJobs, asRole = ModelBuilder::Helper.processArgHash(argHash, eClass)
|
169
|
+
element = eClass.instanceClass.new(argHash)
|
170
|
+
if @builderContext
|
171
|
+
ModelBuilder::Helper.associateWithContextElement(element, @builderContext.contextElement, asRole)
|
172
|
+
resolver = @builderContext.resolver
|
173
|
+
else
|
174
|
+
resolver = ModelBuilder::ReferenceResolver.new
|
175
|
+
end
|
176
|
+
resolverJobs.each do |job|
|
177
|
+
job.receiver = element
|
178
|
+
job.namespace = @builderContext && @builderContext.contextElement
|
179
|
+
resolver.addJob(job)
|
180
|
+
end
|
181
|
+
# process block
|
182
|
+
if block
|
183
|
+
if @builderContext
|
184
|
+
@builderContext, oldContext = @builderContext.dup, @builderContext
|
185
|
+
else
|
186
|
+
@builderContext = ModelBuilder::BuilderContext.new
|
187
|
+
@builderContext.resolver = resolver
|
188
|
+
oldContext = nil
|
189
|
+
end
|
190
|
+
@builderContext.contextElement = element
|
191
|
+
instance_eval(&block)
|
192
|
+
@builderContext = oldContext
|
193
|
+
if oldContext.nil?
|
194
|
+
# we are back on toplevel
|
195
|
+
resolver.resolve
|
196
|
+
end
|
197
|
+
end
|
198
|
+
element
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|