rgen 0.4.6 → 0.5.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 +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
|