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.
Files changed (60) hide show
  1. data/CHANGELOG +95 -83
  2. data/Rakefile +4 -3
  3. data/lib/ea_support/ea_support.rb +54 -0
  4. data/lib/ea_support/id_store.rb +32 -0
  5. data/lib/ea_support/uml13_ea_metamodel.rb +562 -0
  6. data/lib/ea_support/uml13_ea_metamodel_ext.rb +45 -0
  7. data/lib/ea_support/uml13_ea_metamodel_generator.rb +43 -0
  8. data/lib/ea_support/uml13_ea_to_uml13.rb +72 -0
  9. data/lib/ea_support/uml13_to_uml13_ea.rb +82 -0
  10. data/lib/rgen/ecore/ecore.rb +16 -2
  11. data/lib/rgen/ecore/ecore_builder_methods.rb +81 -0
  12. data/lib/rgen/ecore/ecore_instantiator.rb +5 -1
  13. data/lib/rgen/metamodel_builder/builder_extensions.rb +11 -3
  14. data/lib/rgen/metamodel_builder/module_extension2.rb +205 -0
  15. data/lib/rgen/method_delegation.rb +99 -0
  16. data/lib/rgen/model_builder.rb +27 -0
  17. data/lib/rgen/model_builder/builder_context.rb +318 -0
  18. data/lib/rgen/model_builder/model_serializer.rb +201 -0
  19. data/lib/rgen/model_builder/reference_resolver.rb +156 -0
  20. data/lib/rgen/template_language/directory_template_container.rb +6 -2
  21. data/lib/rgen/template_language/output_handler.rb +2 -4
  22. data/lib/rgen/template_language/template_container.rb +212 -195
  23. data/lib/rgen/transformer.rb +95 -4
  24. data/lib/transformers/ecore_to_uml13.rb +66 -0
  25. data/lib/transformers/uml13_to_ecore.rb +16 -7
  26. data/test/ea_instantiator_test.rb +8 -14
  27. data/test/ea_serializer_test.rb +3 -9
  28. data/test/ea_serializer_test/ea_testmodel_regenerated.xml +2 -2
  29. data/test/ea_serializer_test/ea_testmodel_regenerated_import.log +3 -0
  30. data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +19 -19
  31. data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +44 -44
  32. data/test/method_delegation_test.rb +178 -0
  33. data/test/model_builder/builder_context_test.rb +59 -0
  34. data/test/model_builder/builder_test.rb +284 -0
  35. data/test/model_builder/ecore_internal.rb +103 -0
  36. data/test/model_builder/ecore_original.rb +163 -0
  37. data/test/model_builder/ecore_original_regenerated.rb +163 -0
  38. data/test/model_builder/reference_resolver_test.rb +156 -0
  39. data/test/model_builder/serializer_test.rb +63 -0
  40. data/test/model_builder_test.rb +4 -0
  41. data/test/rgen_test.rb +2 -0
  42. data/test/template_language_test.rb +41 -1
  43. data/test/template_language_test/expected_result1.txt +1 -3
  44. data/test/template_language_test/templates/define_local_test/local.tpl +8 -0
  45. data/test/template_language_test/templates/define_local_test/test.tpl +8 -0
  46. data/test/template_language_test/templates/evaluate_test/test.tpl +7 -0
  47. data/test/template_language_test/templates/no_indent_test/no_indent.tpl +3 -0
  48. data/test/template_language_test/templates/no_indent_test/sub1/no_indent.tpl +3 -0
  49. data/test/template_language_test/templates/no_indent_test/test.tpl +24 -0
  50. data/test/template_language_test/templates/no_indent_test/test2.tpl +13 -0
  51. data/test/template_language_test/templates/no_indent_test/test3.tpl +10 -0
  52. data/test/template_language_test/templates/template_resolution_test/sub1.tpl +9 -0
  53. data/test/template_language_test/templates/template_resolution_test/sub1/sub1.tpl +3 -0
  54. data/test/template_language_test/templates/template_resolution_test/test.tpl +4 -0
  55. data/test/template_language_test/testout.txt +1 -3
  56. data/test/testmodel/ea_testmodel_import.log +1 -0
  57. data/test/testmodel/ea_testmodel_regenerated.xml +808 -0
  58. data/test/transformer_test.rb +3 -5
  59. metadata +52 -3
  60. 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 -&gt; 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
@@ -52,7 +52,21 @@ module RGen
52
52
  has_attr 'volatile', Boolean, :defaultValueLiteral => "false"
53
53
  module ClassModule
54
54
  def defaultValue_derived
55
- eval(defaultValueLiteral) unless defaultValueLiteral.nil?
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
@@ -19,7 +19,11 @@ module ECoreInstantiator
19
19
  end
20
20
  @@transformer.trans(self)
21
21
  end
22
-
22
+
23
+ def self.clear_ecore_cache
24
+ require 'rgen/ecore/ecore_transformer'
25
+ @@transformer = ECoreTransformer.new
26
+ end
23
27
  end
24
28
 
25
29
  end
@@ -217,7 +217,15 @@ module BuilderExtensions
217
217
 
218
218
  def _metamodel_description # :nodoc:
219
219
  @metamodel_description ||= []
220
- end
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
- _metamodel_description << props1
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._metamodel_description << props2
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