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.
Files changed (125) hide show
  1. data/CHANGELOG +28 -0
  2. data/Rakefile +3 -4
  3. data/lib/ea_support/uml13_ea_metamodel.rb +3 -3
  4. data/lib/ea_support/uml13_ea_to_uml13.rb +33 -2
  5. data/lib/ea_support/uml13_to_uml13_ea.rb +7 -0
  6. data/lib/mmgen/mm_ext/ecore_mmgen_ext.rb +4 -4
  7. data/lib/mmgen/templates/metamodel_generator.tpl +143 -143
  8. data/lib/rgen/ecore/ecore.rb +11 -1
  9. data/lib/rgen/ecore/ecore_interface.rb +47 -0
  10. data/lib/rgen/ecore/ecore_to_ruby.rb +166 -0
  11. data/lib/rgen/ecore/{ecore_transformer.rb → ruby_to_ecore.rb} +11 -11
  12. data/lib/rgen/environment.rb +15 -2
  13. data/lib/rgen/fragment/dump_file_cache.rb +63 -0
  14. data/lib/rgen/fragment/fragmented_model.rb +139 -0
  15. data/lib/rgen/fragment/model_fragment.rb +268 -0
  16. data/lib/rgen/instantiator/abstract_xml_instantiator.rb +44 -72
  17. data/lib/rgen/instantiator/default_xml_instantiator.rb +2 -2
  18. data/lib/rgen/instantiator/ecore_xml_instantiator.rb +16 -1
  19. data/lib/rgen/instantiator/json_instantiator.rb +16 -2
  20. data/lib/rgen/instantiator/nodebased_xml_instantiator.rb +118 -138
  21. data/lib/rgen/instantiator/qualified_name_resolver.rb +5 -1
  22. data/lib/rgen/instantiator/reference_resolver.rb +126 -24
  23. data/lib/rgen/instantiator/xmi11_instantiator.rb +6 -2
  24. data/lib/rgen/metamodel_builder.rb +18 -6
  25. data/lib/rgen/metamodel_builder/builder_extensions.rb +431 -407
  26. data/lib/rgen/metamodel_builder/builder_runtime.rb +8 -8
  27. data/lib/rgen/metamodel_builder/constant_order_helper.rb +4 -4
  28. data/lib/rgen/metamodel_builder/data_types.rb +5 -1
  29. data/lib/rgen/metamodel_builder/intermediate/feature.rb +167 -0
  30. data/lib/rgen/metamodel_builder/module_extension.rb +2 -2
  31. data/lib/rgen/model_builder.rb +10 -5
  32. data/lib/rgen/model_builder/builder_context.rb +17 -1
  33. data/lib/rgen/serializer/opposite_reference_filter.rb +18 -0
  34. data/lib/rgen/serializer/qualified_name_provider.rb +45 -0
  35. data/lib/rgen/template_language/template_container.rb +3 -1
  36. data/lib/rgen/{auto_class_creator.rb → util/auto_class_creator.rb} +6 -1
  37. data/lib/rgen/util/cached_glob.rb +67 -0
  38. data/lib/rgen/util/file_cache_map.rb +104 -0
  39. data/lib/rgen/util/file_change_detector.rb +78 -0
  40. data/lib/rgen/{method_delegation.rb → util/method_delegation.rb} +18 -3
  41. data/lib/rgen/{model_comparator.rb → util/model_comparator.rb} +17 -5
  42. data/lib/rgen/{model_comparator_base.rb → util/model_comparator_base.rb} +6 -1
  43. data/lib/rgen/{model_dumper.rb → util/model_dumper.rb} +6 -1
  44. data/lib/rgen/{name_helper.rb → util/name_helper.rb} +6 -1
  45. data/lib/rgen/util/pattern_matcher.rb +329 -0
  46. data/lib/transformers/uml13_to_ecore.rb +103 -60
  47. data/test/ecore_self_test.rb +43 -42
  48. data/test/json_test.rb +15 -0
  49. data/test/metamodel_builder_test.rb +361 -206
  50. data/test/metamodel_from_ecore_test.rb +45 -0
  51. data/test/metamodel_order_test.rb +10 -4
  52. data/test/metamodel_roundtrip_test.rb +2 -2
  53. data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +1 -1
  54. data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +50 -50
  55. data/test/method_delegation_test.rb +9 -9
  56. data/test/model_builder/ecore_internal.rb +19 -9
  57. data/test/model_builder/serializer_test.rb +1 -1
  58. data/test/reference_resolver_test.rb +79 -12
  59. data/test/rgen_test.rb +2 -0
  60. data/test/template_language_test.rb +7 -0
  61. data/test/template_language_test/templates/callback_indent_test/a.tpl +12 -0
  62. data/test/template_language_test/templates/callback_indent_test/b.tpl +5 -0
  63. data/test/testmodel/ea_testmodel_regenerated.xml +588 -583
  64. data/test/transformer_test.rb +3 -3
  65. data/test/util/file_cache_map_test.rb +91 -0
  66. data/test/util/file_cache_map_test/testdir/fileA +1 -0
  67. data/test/util_test.rb +4 -0
  68. data/test/xml_instantiator_test.rb +139 -135
  69. metadata +49 -104
  70. data/lib/rgen/ecore/ecore_instantiator.rb +0 -31
  71. data/lib/rgen/metamodel_builder/metamodel_description.rb +0 -232
  72. data/redist/xmlscan/ChangeLog +0 -1301
  73. data/redist/xmlscan/README +0 -34
  74. data/redist/xmlscan/THANKS +0 -11
  75. data/redist/xmlscan/doc/changes.html +0 -74
  76. data/redist/xmlscan/doc/changes.rd +0 -80
  77. data/redist/xmlscan/doc/en/conformance.html +0 -136
  78. data/redist/xmlscan/doc/en/conformance.rd +0 -152
  79. data/redist/xmlscan/doc/en/manual.html +0 -356
  80. data/redist/xmlscan/doc/en/manual.rd +0 -402
  81. data/redist/xmlscan/doc/ja/conformance.ja.html +0 -118
  82. data/redist/xmlscan/doc/ja/conformance.ja.rd +0 -134
  83. data/redist/xmlscan/doc/ja/manual.ja.html +0 -325
  84. data/redist/xmlscan/doc/ja/manual.ja.rd +0 -370
  85. data/redist/xmlscan/doc/src/Makefile +0 -41
  86. data/redist/xmlscan/doc/src/conformance.rd.src +0 -256
  87. data/redist/xmlscan/doc/src/langsplit.rb +0 -110
  88. data/redist/xmlscan/doc/src/manual.rd.src +0 -614
  89. data/redist/xmlscan/install.rb +0 -41
  90. data/redist/xmlscan/lib/xmlscan/encoding.rb +0 -311
  91. data/redist/xmlscan/lib/xmlscan/htmlscan.rb +0 -289
  92. data/redist/xmlscan/lib/xmlscan/namespace.rb +0 -352
  93. data/redist/xmlscan/lib/xmlscan/parser.rb +0 -299
  94. data/redist/xmlscan/lib/xmlscan/scanner.rb +0 -1109
  95. data/redist/xmlscan/lib/xmlscan/version.rb +0 -22
  96. data/redist/xmlscan/lib/xmlscan/visitor.rb +0 -158
  97. data/redist/xmlscan/lib/xmlscan/xmlchar.rb +0 -441
  98. data/redist/xmlscan/memo/CONFORMANCE +0 -1249
  99. data/redist/xmlscan/memo/PRODUCTIONS +0 -195
  100. data/redist/xmlscan/memo/contentspec.ry +0 -335
  101. data/redist/xmlscan/samples/chibixml.rb +0 -105
  102. data/redist/xmlscan/samples/getxmlchar.rb +0 -122
  103. data/redist/xmlscan/samples/rexml.rb +0 -159
  104. data/redist/xmlscan/samples/xmlbench.rb +0 -88
  105. data/redist/xmlscan/samples/xmlbench/parser/chibixml.rb +0 -22
  106. data/redist/xmlscan/samples/xmlbench/parser/nqxml.rb +0 -29
  107. data/redist/xmlscan/samples/xmlbench/parser/rexml.rb +0 -62
  108. data/redist/xmlscan/samples/xmlbench/parser/xmlparser.rb +0 -22
  109. data/redist/xmlscan/samples/xmlbench/parser/xmlscan-0.0.10.rb +0 -62
  110. data/redist/xmlscan/samples/xmlbench/parser/xmlscan-chibixml.rb +0 -22
  111. data/redist/xmlscan/samples/xmlbench/parser/xmlscan-rexml.rb +0 -22
  112. data/redist/xmlscan/samples/xmlbench/parser/xmlscan.rb +0 -99
  113. data/redist/xmlscan/samples/xmlbench/xmlbench-lib.rb +0 -116
  114. data/redist/xmlscan/samples/xmlconftest.rb +0 -200
  115. data/redist/xmlscan/test.rb +0 -7
  116. data/redist/xmlscan/tests/deftestcase.rb +0 -73
  117. data/redist/xmlscan/tests/runtest.rb +0 -47
  118. data/redist/xmlscan/tests/testall.rb +0 -14
  119. data/redist/xmlscan/tests/testencoding.rb +0 -438
  120. data/redist/xmlscan/tests/testhtmlscan.rb +0 -752
  121. data/redist/xmlscan/tests/testnamespace.rb +0 -457
  122. data/redist/xmlscan/tests/testparser.rb +0 -591
  123. data/redist/xmlscan/tests/testscanner.rb +0 -1749
  124. data/redist/xmlscan/tests/testxmlchar.rb +0 -143
  125. data/redist/xmlscan/tests/visitor.rb +0 -34
@@ -93,6 +93,14 @@ module RGen
93
93
  class EDataType < EClassifier
94
94
  has_attr 'serializable', Boolean
95
95
  end
96
+
97
+ class EGenericType < EDataType
98
+ has_one 'eClassifier', EDataType
99
+ end
100
+
101
+ class ETypeArgument < EModelElement
102
+ has_one 'eClassifier', EDataType
103
+ end
96
104
 
97
105
  class EEnum < EDataType
98
106
  end
@@ -173,7 +181,7 @@ module RGen
173
181
 
174
182
  EString = EDataType.new(:name => "EString", :instanceClassName => "String")
175
183
  EInt = EDataType.new(:name => "EInt", :instanceClassName => "Integer")
176
- EBoolean = EDataType.new(:name => "EBoolean", :instanceClassName => "RGen::MetamodelBuilder::DataTypes::Boolean")
184
+ EBoolean = EDataType.new(:name => "EBoolean", :instanceClassName => "Boolean")
177
185
  EFloat = EDataType.new(:name => "EFloat", :instanceClassName => "Float")
178
186
  ERubyObject = EDataType.new(:name => "ERubyObject", :instanceClassName => "Object")
179
187
  EJavaObject = EDataType.new(:name => "EJavaObject")
@@ -200,5 +208,7 @@ module RGen
200
208
 
201
209
  ECore::EAttribute.has_one 'eAttributeType', ECore::EDataType, :lowerBound=>1, :derived=>true
202
210
  ECore::EReference.has_one 'eReferenceType', ECore::EClass, :lowerBound=>1, :derived=>true
211
+ ECore::EParameter.contains_one 'eGenericType', ECore::EGenericType, 'eParameter'
212
+ ECore::EGenericType.contains_many 'eTypeArguments', ECore::ETypeArgument, 'eGenericType'
203
213
 
204
214
  end
@@ -0,0 +1,47 @@
1
+ module RGen
2
+
3
+ module ECore
4
+
5
+ # Mixin to provide access to the ECore model describing a Ruby class or module
6
+ # built using MetamodelBuilder.
7
+ # The module should be used to +extend+ a class or module, i.e. to make its
8
+ # methods class methods.
9
+ #
10
+ module ECoreInterface
11
+
12
+ # This method will lazily build to ECore model element belonging to the calling
13
+ # class or module using RubyToECore.
14
+ # Alternatively, the ECore model element can be provided up front. This is used
15
+ # when the Ruby metamodel classes and modules are created from ECore.
16
+ #
17
+ def ecore
18
+ if defined?(@ecore)
19
+ @ecore
20
+ else
21
+ unless defined?(@@transformer)
22
+ require 'rgen/ecore/ruby_to_ecore'
23
+ @@transformer = RubyToECore.new
24
+ end
25
+ @@transformer.trans(self)
26
+ end
27
+ end
28
+
29
+ # This method can be used to clear the ecore cache after the metamodel classes
30
+ # or modules have been changed; the ecore model will be recreated on next access
31
+ # to the +ecore+ method
32
+ # Beware, the ecore cache is global, i.e. for all metamodels.
33
+ #
34
+ def self.clear_ecore_cache
35
+ require 'rgen/ecore/ruby_to_ecore'
36
+ @@transformer = RubyToECore.new
37
+ end
38
+
39
+ def _set_ecore_internal(ecore) # :nodoc:
40
+ @ecore = ecore
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,166 @@
1
+ require 'rgen/ecore/ecore'
2
+
3
+ module RGen
4
+
5
+ module ECore
6
+
7
+ class ECoreToRuby
8
+
9
+ def initialize
10
+ @modules = {}
11
+ @classifiers = {}
12
+ @features_added = {}
13
+ @in_create_module = false
14
+ end
15
+
16
+ def create_module(epackage)
17
+ return @modules[epackage] if @modules[epackage]
18
+
19
+ top = (@in_create_module == false)
20
+ @in_create_module = true
21
+
22
+ m = Module.new do
23
+ extend RGen::MetamodelBuilder::ModuleExtension
24
+ end
25
+ @modules[epackage] = m
26
+
27
+ epackage.eSubpackages.each{|p| create_module(p)}
28
+ m._set_ecore_internal(epackage)
29
+
30
+ create_module(epackage.eSuperPackage).const_set(epackage.name, m) if epackage.eSuperPackage
31
+
32
+ # create classes only after all modules have been created
33
+ # otherwise classes may be created multiple times
34
+ if top
35
+ epackage.eAllClassifiers.each do |c|
36
+ if c.is_a?(RGen::ECore::EClass)
37
+ create_class(c)
38
+ elsif c.is_a?(RGen::ECore::EEnum)
39
+ create_enum(c)
40
+ end
41
+ end
42
+ @in_create_module = false
43
+ end
44
+ m
45
+ end
46
+
47
+ def create_class(eclass)
48
+ return @classifiers[eclass] if @classifiers[eclass]
49
+
50
+ c = Class.new(super_class(eclass)) do
51
+ abstract if eclass.abstract
52
+ class << self
53
+ attr_accessor :_ecore_to_ruby
54
+ end
55
+ end
56
+ class << eclass
57
+ attr_accessor :instanceClass
58
+ def instanceClassName
59
+ instanceClass.to_s
60
+ end
61
+ end
62
+ eclass.instanceClass = c
63
+ c::ClassModule.module_eval do
64
+ alias _method_missing method_missing
65
+ def method_missing(m, *args)
66
+ if self.class._ecore_to_ruby.add_features(self.class.ecore)
67
+ send(m, *args)
68
+ else
69
+ _method_missing(m, *args)
70
+ end
71
+ end
72
+ alias _respond_to respond_to?
73
+ def respond_to?(m)
74
+ self.class._ecore_to_ruby.add_features(self.class.ecore)
75
+ _respond_to(m)
76
+ end
77
+ end
78
+ @classifiers[eclass] = c
79
+ c._set_ecore_internal(eclass)
80
+ c._ecore_to_ruby = self
81
+
82
+ create_module(eclass.ePackage).const_set(eclass.name, c)
83
+ c
84
+ end
85
+
86
+ def create_enum(eenum)
87
+ return @classifiers[eenum] if @classifiers[eenum]
88
+
89
+ e = RGen::MetamodelBuilder::DataTypes::Enum.new(eenum.eLiterals.collect{|l| l.name.to_sym})
90
+ @classifiers[eenum] = e
91
+
92
+ create_module(eenum.ePackage).const_set(eenum.name, e)
93
+ e
94
+ end
95
+
96
+ class FeatureWrapper
97
+ def initialize(efeature, classifiers)
98
+ @efeature = efeature
99
+ @classifiers = classifiers
100
+ end
101
+ def value(prop)
102
+ @efeature.send(prop)
103
+ end
104
+ def many
105
+ @efeature.many
106
+ end
107
+ def reference?
108
+ @efeature.is_a?(RGen::ECore::EReference)
109
+ end
110
+ def opposite
111
+ @efeature.eOpposite
112
+ end
113
+ def impl_type
114
+ etype = @efeature.eType
115
+ if etype.is_a?(RGen::ECore::EClass) || etype.is_a?(RGen::ECore::EEnum)
116
+ @classifiers[etype]
117
+ else
118
+ ic = etype.instanceClass
119
+ if ic
120
+ ic
121
+ else
122
+ raise "unknown type: #{etype.name}"
123
+ end
124
+ end
125
+ end
126
+ end
127
+
128
+ def add_features(eclass)
129
+ return false if @features_added[eclass]
130
+ c = @classifiers[eclass]
131
+ eclass.eStructuralFeatures.each do |f|
132
+ w1 = FeatureWrapper.new(f, @classifiers)
133
+ w2 = FeatureWrapper.new(f.eOpposite, @classifiers) if f.is_a?(RGen::ECore::EReference) && f.eOpposite
134
+ c.module_eval do
135
+ if w1.many
136
+ _build_many_methods(w1, w2)
137
+ else
138
+ _build_one_methods(w1, w2)
139
+ end
140
+ end
141
+ end
142
+ @features_added[eclass] = true
143
+ eclass.eSuperTypes.each do |t|
144
+ add_features(t)
145
+ end
146
+ true
147
+ end
148
+
149
+ def super_class(eclass)
150
+ super_types = eclass.eSuperTypes
151
+ case super_types.size
152
+ when 0
153
+ RGen::MetamodelBuilder::MMBase
154
+ when 1
155
+ create_class(super_types.first)
156
+ else
157
+ RGen::MetamodelBuilder::MMMultiple(*super_types.collect{|t| create_class(t)})
158
+ end
159
+ end
160
+
161
+ end
162
+
163
+ end
164
+
165
+ end
166
+
@@ -8,7 +8,7 @@ module ECore
8
8
  # This transformer creates an ECore model from Ruby classes built
9
9
  # by RGen::MetamodelBuilder.
10
10
  #
11
- class ECoreTransformer < Transformer
11
+ class RubyToECore < Transformer
12
12
 
13
13
  transform Class, :to => EClass, :if => :convert? do
14
14
  { :name => name.gsub(/.*::(\w+)$/,'\1'),
@@ -31,13 +31,13 @@ class ECoreTransformer < Transformer
31
31
  end
32
32
 
33
33
  transform Module, :to => EPackage, :if => :convert? do
34
- @enumParentModule ||= {}
34
+ @enumParentModule ||= {}
35
35
  _constants = _constantOrder + (constants - _constantOrder)
36
- _constants.select {|c| const_get(c).is_a?(MetamodelBuilder::DataTypes::Enum)}.
37
- each {|c| @enumParentModule[const_get(c)] = @current_object}
36
+ _constants.select {|c| const_get(c).is_a?(MetamodelBuilder::DataTypes::Enum)}.
37
+ each {|c| @enumParentModule[const_get(c)] = @current_object}
38
38
  { :name => name.gsub(/.*::(\w+)$/,'\1'),
39
39
  :eClassifiers => trans(_constants.collect{|c| const_get(c)}.select{|c| c.is_a?(Class) ||
40
- (c.is_a?(MetamodelBuilder::DataTypes::Enum) && c != MetamodelBuilder::DataTypes::Boolean) }),
40
+ (c.is_a?(MetamodelBuilder::DataTypes::Enum) && c != MetamodelBuilder::DataTypes::Boolean) }),
41
41
  :eSuperPackage => trans(name =~ /(.*)::\w+$/ ? eval($1) : nil),
42
42
  :eSubpackages => trans(_constants.collect{|c| const_get(c)}.select{|c| c.is_a?(Module) && !c.is_a?(Class)}),
43
43
  :eAnnotations => trans(_annotations)
@@ -48,15 +48,15 @@ class ECoreTransformer < Transformer
48
48
  @current_object.respond_to?(:ecore) && @current_object != RGen::MetamodelBuilder::MMBase
49
49
  end
50
50
 
51
- transform MetamodelBuilder::AttributeDescription, :to => EAttribute do
52
- Hash[*MetamodelBuilder::AttributeDescription.propertySet.collect{|p| [p, value(p)]}.flatten].merge({
51
+ transform MetamodelBuilder::Intermediate::Attribute, :to => EAttribute do
52
+ Hash[*MetamodelBuilder::Intermediate::Attribute.properties.collect{|p| [p, value(p)]}.flatten].merge({
53
53
  :eType => (etype == :EEnumerable ? trans(impl_type) : RGen::ECore.const_get(etype)),
54
54
  :eAnnotations => trans(annotations)
55
55
  })
56
56
  end
57
57
 
58
- transform MetamodelBuilder::ReferenceDescription, :to => EReference do
59
- Hash[*MetamodelBuilder::ReferenceDescription.propertySet.collect{|p| [p, value(p)]}.flatten].merge({
58
+ transform MetamodelBuilder::Intermediate::Reference, :to => EReference do
59
+ Hash[*MetamodelBuilder::Intermediate::Reference.properties.collect{|p| [p, value(p)]}.flatten].merge({
60
60
  :eType => trans(impl_type),
61
61
  :eOpposite => trans(opposite),
62
62
  :eAnnotations => trans(annotations)
@@ -66,7 +66,7 @@ class ECoreTransformer < Transformer
66
66
  transform MetamodelBuilder::Intermediate::Annotation, :to => EAnnotation do
67
67
  { :source => source,
68
68
  :details => details.keys.collect do |k|
69
- e = EStringToStringMapEntry.new
69
+ e = RGen::ECore::EStringToStringMapEntry.new
70
70
  e.key = k
71
71
  e.value = details[k]
72
72
  e
@@ -78,7 +78,7 @@ class ECoreTransformer < Transformer
78
78
  { :name => name,
79
79
  :instanceClassName => @enumParentModule && @enumParentModule[@current_object] && @enumParentModule[@current_object].name+"::"+name,
80
80
  :eLiterals => literals.collect do |l|
81
- lit = EEnumLiteral.new
81
+ lit = RGen::ECore::EEnumLiteral.new
82
82
  lit.name = l.to_s
83
83
  lit
84
84
  end }
@@ -8,6 +8,8 @@ class Environment
8
8
  @elements = {}
9
9
  @subClasses = {}
10
10
  @subClassesUpdated = {}
11
+ @deleted = {}
12
+ @deletedClasses = {}
11
13
  end
12
14
 
13
15
  # Add a model element. Returns the environment so <code><<</code> can be chained.
@@ -22,19 +24,21 @@ class Environment
22
24
 
23
25
  # Removes model element from environment.
24
26
  def delete(el)
25
- return unless @elements[el.class]
26
- @elements[el.class].delete(el)
27
+ @deleted[el] = true
28
+ @deletedClasses[el.class] = true
27
29
  end
28
30
 
29
31
  # Iterates each element
30
32
  #
31
33
  def each(&b)
34
+ removeDeleted
32
35
  @elements.values.flatten.each(&b)
33
36
  end
34
37
 
35
38
  # Return the elements of the environment as an array
36
39
  #
37
40
  def elements
41
+ removeDeleted
38
42
  @elements.values.flatten
39
43
  end
40
44
 
@@ -57,6 +61,7 @@ class Environment
57
61
  # class. In this case an array of possible classes can optionally be given.
58
62
  #
59
63
  def find(desc)
64
+ removeDeleted
60
65
  result = []
61
66
  classes = desc[:class] if desc[:class] and desc[:class].is_a?(Array)
62
67
  classes = [ desc[:class] ] if !classes and desc[:class]
@@ -79,6 +84,14 @@ class Environment
79
84
  end
80
85
 
81
86
  private
87
+
88
+ def removeDeleted
89
+ @deletedClasses.keys.each do |c|
90
+ @elements[c].reject!{|e| @deleted[e]}
91
+ end
92
+ @deletedClasses.clear
93
+ @deleted.clear
94
+ end
82
95
 
83
96
  def updateSubClasses(clazz)
84
97
  return if @subClassesUpdated[clazz]
@@ -0,0 +1,63 @@
1
+ module RGen
2
+
3
+ module Fragment
4
+
5
+ # Caches model fragments in Ruby dump files.
6
+ #
7
+ # Dump files are created per each fragment file.
8
+ #
9
+ # The main goal is to support fast loading and joining of fragments. Therefore the cache
10
+ # stores additional information which makes the joining process faster (adding to
11
+ # environment, resolving references)
12
+ #
13
+ class DumpFileCache
14
+
15
+ # +cache_map+ must be an object responding to +load_data+ and +store_data+
16
+ # for loading or storing data associated with a file;
17
+ # this can be an instance of Util::FileCacheMap
18
+ def initialize(cache_map)
19
+ @cache_map = cache_map
20
+ end
21
+
22
+ # Note that the fragment must not be connected to other fragments by resolved references
23
+ # unresolve the fragment if necessary
24
+ def store(fragment)
25
+ fref = fragment.fragment_ref
26
+ # temporarily remove the reference to the fragment to avoid dumping the fragment
27
+ fref.fragment = nil
28
+ @cache_map.store_data(fragment.location,
29
+ Marshal.dump({
30
+ :root_elements => fragment.root_elements,
31
+ :elements => fragment.elements,
32
+ :index => fragment.index,
33
+ :unresolved_refs => fragment.unresolved_refs,
34
+ :fragment_ref => fref,
35
+ :data => fragment.data
36
+ }))
37
+ fref.fragment = fragment
38
+ end
39
+
40
+ def load(fragment)
41
+ dump = @cache_map.load_data(fragment.location)
42
+ return :invalid if dump == :invalid
43
+ header = Marshal.load(dump)
44
+ fragment.set_root_elements(header[:root_elements],
45
+ :elements => header[:elements],
46
+ :index => header[:index],
47
+ :unresolved_refs => header[:unresolved_refs])
48
+ fragment.data = header[:data]
49
+ if header[:fragment_ref]
50
+ fragment.fragment_ref = header[:fragment_ref]
51
+ fragment.fragment_ref.fragment = fragment
52
+ else
53
+ raise "no fragment_ref in fragment loaded from cache"
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+
63
+
@@ -0,0 +1,139 @@
1
+ require 'rgen/instantiator/reference_resolver'
2
+
3
+ module RGen
4
+
5
+ module Fragment
6
+
7
+ # A FragmentedModel represents a model which consists of fragments (ModelFragment).
8
+ #
9
+ # The main purpose of this class is to resolve references across fragments and
10
+ # to keep the references consistent while fragments are added or removed.
11
+ # This way it also plays an important role in keeping the model fragments consistent
12
+ # and thus ModelFragment objects should only be accessed via this interface.
13
+ # Overall unresolved references after the resolution step are also maintained.
14
+ #
15
+ # A FragmentedModel can also keep an RGen::Environment object up to date while fragments
16
+ # are added or removed. The environment must be registered with the constructor.
17
+ #
18
+ # Reference resolution is based on arbitrary identifiers. The identifiers must be
19
+ # provided in the fragments' indices. The FragmentedModel takes care to maintain
20
+ # the overall index.
21
+ #
22
+ class FragmentedModel
23
+ attr_reader :fragments
24
+ attr_reader :environment
25
+
26
+ # Creates a fragmented model. Options:
27
+ #
28
+ # :env
29
+ # environment which will be updated as model elements are added and removed
30
+ #
31
+ def initialize(options={})
32
+ @environment = options[:env]
33
+ @fragments = []
34
+ @index = nil
35
+ @fragment_change_listeners = []
36
+ @fragment_index = {}
37
+ end
38
+
39
+ # Adds a proc which is called when a fragment is added or removed
40
+ # The proc receives the fragment and one of :added, :removed
41
+ #
42
+ def add_fragment_change_listener(listener)
43
+ @fragment_change_listeners << listener
44
+ end
45
+
46
+ def remove_fragment_change_listener(listener)
47
+ @fragment_change_listeners.delete(listener)
48
+ end
49
+
50
+ # Add a fragment.
51
+ #
52
+ def add_fragment(fragment)
53
+ invalidate_cache
54
+ @fragments << fragment
55
+ fragment.elements.each{|e| @environment << e} if @environment
56
+ @fragment_change_listeners.each{|l| l.call(fragment, :added)}
57
+ end
58
+
59
+ # Removes the fragment. The fragment will be unresolved using unresolve_fragment.
60
+ #
61
+ def remove_fragment(fragment)
62
+ raise "fragment not part of model" unless @fragments.include?(fragment)
63
+ invalidate_cache
64
+ @fragments.delete(fragment)
65
+ @fragment_index.delete(fragment)
66
+ unresolve_fragment(fragment)
67
+ fragment.elements.each{|e| @environment.delete(e)} if @environment
68
+ @fragment_change_listeners.each{|l| l.call(fragment, :removed)}
69
+ end
70
+
71
+ # Resolve references between fragments.
72
+ # It is assumed that references within fragments have already been resolved.
73
+ # This method can be called several times. It will update the overall unresolved references.
74
+ #
75
+ # Options:
76
+ #
77
+ # :fragment_provider:
78
+ # Only if a +fragment_provider+ is given, the resolve step can be reverted later on
79
+ # by a call to unresolve_fragment. The fragment provider is a proc which receives a model
80
+ # element and must return the fragment in which the element is contained.
81
+ #
82
+ # :use_target_type:
83
+ # reference resolver uses the expected target type to narrow the set of possible targets
84
+ #
85
+ def resolve(options)
86
+ @fragments.each do |f|
87
+ f.resolve_external(index, options)
88
+ end
89
+ end
90
+
91
+ # Remove all references between this fragment and all other fragments.
92
+ # The references will be replaced with unresolved references (MMProxy objects).
93
+ #
94
+ def unresolve_fragment(fragment)
95
+ fragment.unresolve_external
96
+ @fragments.each do |f|
97
+ if f != fragment
98
+ f.unresolve_external_fragment(fragment)
99
+ end
100
+ end
101
+ end
102
+
103
+ # Returns the overall unresolved references.
104
+ #
105
+ def unresolved_refs
106
+ @fragments.collect{|f| f.unresolved_refs}.flatten
107
+ end
108
+
109
+ # Returns the overall index.
110
+ # This is a Hash mapping identifiers to model elements accessible via the identifier.
111
+ #
112
+ def index
113
+ fragments.each do |f|
114
+ if !@fragment_index[f] || (@fragment_index[f].object_id != f.index.object_id)
115
+ @fragment_index[f] = f.index
116
+ invalidate_cache
117
+ end
118
+ end
119
+ return @index if @index
120
+ @index = {}
121
+ fragments.each do |f|
122
+ f.index.each do |i|
123
+ (@index[i[0]] ||= []) << i[1]
124
+ end
125
+ end
126
+ @index
127
+ end
128
+
129
+ private
130
+
131
+ def invalidate_cache
132
+ @index = nil
133
+ end
134
+
135
+ end
136
+
137
+ end
138
+
139
+ end