rgen 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +21 -0
  3. data/Project.yaml +22 -0
  4. data/Rakefile +21 -25
  5. data/lib/rgen/ecore/ecore.rb +7 -1
  6. data/lib/rgen/ecore/ecore_ext.rb +9 -1
  7. data/lib/rgen/ecore/ecore_to_json.rb +189 -0
  8. data/lib/rgen/ecore/ecore_to_ruby.rb +46 -10
  9. data/lib/rgen/fragment/fragmented_model.rb +2 -0
  10. data/lib/rgen/fragment/model_fragment.rb +3 -0
  11. data/lib/rgen/metamodel_builder/builder_extensions.rb +14 -6
  12. data/lib/rgen/metamodel_builder/builder_runtime.rb +12 -5
  13. data/lib/rgen/template_language/output_handler.rb +10 -0
  14. data/lib/rgen/template_language/template_container.rb +7 -3
  15. data/lib/rgen/template_language.rb +2 -1
  16. data/test/array_extensions_test.rb +7 -7
  17. data/test/ea_instantiator_test.rb +2 -2
  18. data/test/ea_serializer_test.rb +2 -2
  19. data/test/ecore_self_test.rb +2 -2
  20. data/test/ecore_to_ruby_test.rb +2 -2
  21. data/test/environment_test.rb +2 -2
  22. data/test/json_test.rb +4 -4
  23. data/test/metamodel_builder_test.rb +81 -46
  24. data/test/metamodel_from_ecore_test.rb +1 -1
  25. data/test/metamodel_order_test.rb +2 -2
  26. data/test/metamodel_roundtrip_test.rb +2 -2
  27. data/test/method_delegation_test.rb +5 -5
  28. data/test/model_builder/builder_context_test.rb +3 -3
  29. data/test/model_builder/builder_test.rb +11 -11
  30. data/test/model_builder/reference_resolver_test.rb +3 -3
  31. data/test/model_builder/serializer_test.rb +2 -2
  32. data/test/model_fragment_test.rb +2 -2
  33. data/test/output_handler_test.rb +2 -2
  34. data/test/qualified_name_provider_test.rb +2 -2
  35. data/test/qualified_name_resolver_test.rb +4 -4
  36. data/test/reference_resolver_test.rb +4 -4
  37. data/test/rgen_test.rb +1 -1
  38. data/test/template_language_test/templates/line_endings/mixed.tpl +6 -0
  39. data/test/template_language_test/templates/line_endings/unix.tpl +6 -0
  40. data/test/template_language_test/templates/line_endings/windows.tpl +6 -0
  41. data/test/template_language_test/templates/ws_test.tpl +21 -0
  42. data/test/template_language_test.rb +45 -8
  43. data/test/testmodel/class_model_checker.rb +17 -17
  44. data/test/testmodel/ecore_model_checker.rb +13 -13
  45. data/test/testmodel/object_model_checker.rb +6 -6
  46. data/test/transformer_test.rb +3 -3
  47. data/test/util/file_cache_map_test.rb +2 -2
  48. data/test/util/pattern_matcher_test.rb +7 -7
  49. data/test/xml_instantiator_test/simple_ecore_model_checker.rb +12 -12
  50. data/test/xml_instantiator_test.rb +8 -8
  51. metadata +106 -51
  52. data/test/coverage/assets/0.10.0/application.css +0 -799
  53. data/test/coverage/assets/0.10.0/application.js +0 -1707
  54. data/test/coverage/assets/0.10.0/colorbox/border.png +0 -0
  55. data/test/coverage/assets/0.10.0/colorbox/controls.png +0 -0
  56. data/test/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
  57. data/test/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
  58. data/test/coverage/assets/0.10.0/favicon_green.png +0 -0
  59. data/test/coverage/assets/0.10.0/favicon_red.png +0 -0
  60. data/test/coverage/assets/0.10.0/favicon_yellow.png +0 -0
  61. data/test/coverage/assets/0.10.0/loading.gif +0 -0
  62. data/test/coverage/assets/0.10.0/magnify.png +0 -0
  63. data/test/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  64. data/test/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  65. data/test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  66. data/test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  67. data/test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  68. data/test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  69. data/test/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  70. data/test/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  71. data/test/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  72. data/test/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  73. data/test/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  74. data/test/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  75. data/test/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  76. data/test/coverage/index.html +0 -72
  77. data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +0 -71
  78. data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +0 -162
  79. data/test/metamodel_roundtrip_test/using_builtin_types_serialized.ecore +0 -9
  80. data/test/model_builder/ecore_internal.rb +0 -113
  81. data/test/testmodel/ea_testmodel_regenerated.xml +0 -813
  82. data/test/util/file_cache_map_test/testdir/fileA +0 -1
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5091748f0290a5b0c82cf66e3bf2fea91ac774f6
4
+ data.tar.gz: db0663f172064272ed0bcd037e6715c12729429b
5
+ SHA512:
6
+ metadata.gz: 1355aa9282e5c3a18a93b65788f4fb5a4f92b26bb5cf89fa50e7ec71bd63dad090af06b73e292e959a7750fe08d75a5e0e4a5f0010fe84bb162c6eed1af338d1
7
+ data.tar.gz: 3f8755a969d35c22d4d8b3bcabeb6c75799b48b416d242a83530afbdc8b7364e682ba59750eddc375a454b450ef1ed5d32941951d7397430f4172d37aa1841a7
data/CHANGELOG CHANGED
@@ -205,3 +205,24 @@
205
205
  * Improved performance of setXXX and addXXX methods (pull request #22 from thallgren)
206
206
  * Use a value larger than Fixnum max to test Bignum support (pull request #18 from graaff)
207
207
 
208
+ =0.8.1
209
+
210
+ * Improved performance of ECoreToRuby
211
+ * Fixed <%ws%> command to trigger indentation if starting a new line
212
+
213
+ =0.8.2
214
+
215
+ * Added helper methods
216
+
217
+ =0.8.3
218
+
219
+ * Performance improvement: getGeneric made a lot faster
220
+
221
+ =0.8.4
222
+
223
+ * Add early loading for types which have attributes conflicting with Ruby reserved words.
224
+ * Change type checking code to use ObjectSpace to find class objects. This is to find classes with an unbound name.
225
+
226
+ =0.9.0
227
+
228
+ * Update to support Ruby 2.7, drop support for older Rubies
data/Project.yaml ADDED
@@ -0,0 +1,22 @@
1
+ name: rgen
2
+ gemspec: rgen.gemspec
3
+ git: https://github.com/mthiede/rgen.git
4
+ version: 0.9.0
5
+ summary: Ruby Modelling and Generator Framework
6
+ email: martin dot thiede at gmx de
7
+ homepage: http://ruby-gen.org
8
+ rubyforge_project: rgen
9
+ description: RGen is a framework for Model Driven Software Development (MDSD) in Ruby. This means that it helps you build Metamodels, instantiate Models, modify and transform Models and finally generate arbitrary textual content from it.
10
+ authors: [Martin Thiede]
11
+ rdoc_options: [--main, README.rdoc, -x, test, -x, metamodels, -x, ea_support/uml13*]
12
+ extra_rdoc_files: [README.rdoc, CHANGELOG, MIT-LICENSE]
13
+ include_files: ['lib/**/*', 'test/**/*', README.rdoc, CHANGELOG, MIT-LICENSE, Rakefile]
14
+ exclude_files: ['**/*.bak']
15
+ encrypt_sources: false
16
+ dependencies:
17
+ https://rubygems.org:
18
+ - {name: nokogiri, version: ['>= 1.11.2', '< 1.12'], development: true}
19
+ - {name: rake, version: '~> 12.0', development: true}
20
+ - {name: minitest, version: ['~> 5.0', '>= 5.10.1'], development: true}
21
+ - {name: minitest-fail-fast, version: '~> 0.1.0', development: true}
22
+ - {name: andand, version: '1.3.3', development: true}
data/Rakefile CHANGED
@@ -1,41 +1,37 @@
1
1
  require 'rubygems/package_task'
2
2
  require 'rdoc/task'
3
+ require 'rake/testtask'
3
4
 
4
- RGenGemSpec = Gem::Specification.new do |s|
5
- s.name = %q{rgen}
6
- s.version = "0.8.0"
7
- s.date = Time.now.strftime("%Y-%m-%d")
8
- s.summary = %q{Ruby Modelling and Generator Framework}
9
- s.email = %q{martin dot thiede at gmx de}
10
- s.homepage = %q{http://ruby-gen.org}
11
- s.rubyforge_project = %q{rgen}
12
- s.description = %q{RGen is a framework for Model Driven Software Development (MDSD) in Ruby. This means that it helps you build Metamodels, instantiate Models, modify and transform Models and finally generate arbitrary textual content from it.}
13
- s.authors = ["Martin Thiede"]
14
- gemfiles = Rake::FileList.new
15
- gemfiles.include("{lib,test}/**/*")
16
- gemfiles.include("README.rdoc", "CHANGELOG", "MIT-LICENSE", "Rakefile")
17
- gemfiles.exclude(/\b\.bak\b/)
18
- s.files = gemfiles
19
- s.rdoc_options = ["--main", "README.rdoc", "-x", "test", "-x", "metamodels", "-x", "ea_support/uml13*"]
20
- s.extra_rdoc_files = ["README.rdoc", "CHANGELOG", "MIT-LICENSE"]
21
- end
5
+ RGenGemSpec = eval(File.read('rgen.gemspec'))
22
6
 
23
7
  RDoc::Task.new do |rd|
24
- rd.main = "README.rdoc"
25
- rd.rdoc_files.include("README.rdoc", "CHANGELOG", "MIT-LICENSE", "lib/**/*.rb")
26
- rd.rdoc_files.exclude("lib/metamodels/*")
27
- rd.rdoc_files.exclude("lib/ea_support/uml13*")
28
- rd.rdoc_dir = "doc"
8
+ rd.main = 'README.rdoc'
9
+ rd.rdoc_files.include('README.rdoc', 'CHANGELOG', 'MIT-LICENSE', 'lib/**/*.rb')
10
+ rd.rdoc_files.exclude('lib/metamodels/*')
11
+ rd.rdoc_files.exclude('lib/ea_support/uml13*')
12
+ rd.rdoc_dir = 'doc'
29
13
  end
30
14
 
31
15
  RGenPackageTask = Gem::PackageTask.new(RGenGemSpec) do |p|
32
16
  p.need_zip = false
33
- end
17
+ end
18
+
19
+ ::Rake::TestTask.new(:test) do |t|
20
+ t.test_files = ['test/rgen_test.rb']
21
+ t.warning = false
22
+ end
34
23
 
35
24
  task :prepare_package_rdoc => :rdoc do
36
- RGenPackageTask.package_files.include("doc/**/*")
25
+ RGenPackageTask.package_files.include('doc/**/*')
37
26
  end
38
27
 
39
28
  task :release => [:prepare_package_rdoc, :package]
40
29
 
41
30
  task :clobber => [:clobber_rdoc, :clobber_package]
31
+
32
+ task :ecore_to_json do
33
+ require 'rgen/ecore/ecore_to_json'
34
+
35
+ exporter = RGen::ECore::ECoreToJson.new
36
+ File.write('ecore.json', exporter.epackage_to_json_string(RGen.ecore, exporter.ecore_datatypes))
37
+ end
@@ -12,9 +12,11 @@ module RGen
12
12
  extend RGen::MetamodelBuilder::ModuleExtension
13
13
 
14
14
  class EObject < RGen::MetamodelBuilder::MMBase
15
+ abstract
15
16
  end
16
17
 
17
18
  class EModelElement < RGen::MetamodelBuilder::MMBase
19
+ abstract
18
20
  end
19
21
 
20
22
  class EAnnotation < RGen::MetamodelBuilder::MMMultiple(EModelElement, EObject)
@@ -22,10 +24,12 @@ module RGen
22
24
  end
23
25
 
24
26
  class ENamedElement < EModelElement
27
+ abstract
25
28
  has_attr 'name', String
26
29
  end
27
30
 
28
31
  class ETypedElement < ENamedElement
32
+ abstract
29
33
  has_attr 'lowerBound', Integer, :defaultValueLiteral => "0"
30
34
  has_attr 'ordered', Boolean, :defaultValueLiteral => "true"
31
35
  has_attr 'unique', Boolean, :defaultValueLiteral => "true"
@@ -43,6 +47,7 @@ module RGen
43
47
  end
44
48
 
45
49
  class EStructuralFeature < ETypedElement
50
+ abstract
46
51
  has_attr 'changeable', Boolean, :defaultValueLiteral => "true"
47
52
  has_attr 'defaultValue', Object, :derived=>true
48
53
  has_attr 'defaultValueLiteral', String
@@ -76,6 +81,7 @@ module RGen
76
81
  end
77
82
 
78
83
  class EClassifier < ENamedElement
84
+ abstract
79
85
  has_attr 'defaultValue', Object, :derived=>true
80
86
  has_attr 'instanceClass', Object, :derived=>true
81
87
  has_attr 'instanceClassName', String
@@ -155,7 +161,7 @@ module RGen
155
161
  has_many 'eAttributes', ECore::EAttribute, :derived=>true
156
162
  has_many 'eReferences', ECore::EReference, :derived=>true
157
163
 
158
- module ClassModule
164
+ module ClassModule
159
165
  def eAllAttributes_derived
160
166
  eAttributes + eSuperTypes.eAllAttributes
161
167
  end
@@ -63,7 +63,15 @@ module RGen
63
63
  def eAllSubTypes
64
64
  eSubTypes + eSubTypes.eAllSubTypes
65
65
  end
66
+
67
+ def concrete
68
+ !(abstract || interface)
69
+ end
70
+
71
+ def isAssignableFrom(cls)
72
+ cls == self || cls.eAllSuperTypes.any? { |super_type| super_type == self }
73
+ end
66
74
 
67
- end
75
+ end
68
76
  end
69
77
  end
@@ -0,0 +1,189 @@
1
+ require 'rgen/ecore/ecore'
2
+ require 'json'
3
+ require 'andand'
4
+
5
+ module RGen
6
+
7
+ module ECore
8
+
9
+ # ECoreToJson can turn ECore models into their JSON metamodel representations
10
+ class ECoreToJson
11
+
12
+ def initialize
13
+
14
+ end
15
+
16
+ def root_elements_to_json_string(root_elements)
17
+ JSON.pretty_generate(root_elements.map do |el|
18
+ if el.is_a?(RGen::ECore::EPackage)
19
+ epackage(el)
20
+ elsif el.is_a?(RGen::ECore::EClass)
21
+ eclass(el)
22
+ else
23
+ raise "Not implemented for #{el}"
24
+ end
25
+ end)
26
+ end
27
+
28
+ def epackage_to_json(package)
29
+ epackage(package)
30
+ end
31
+
32
+ def ecore_datatypes
33
+ [RGen::ECore::EString, RGen::ECore::EInt, RGen::ECore::ELong, RGen::ECore::EBoolean, RGen::ECore::EFloat,
34
+ RGen::ECore::ERubyObject, RGen::ECore::EJavaObject, RGen::ECore::ERubyClass, RGen::ECore::EJavaClass]
35
+ .map {|dt| edatatype(dt)}
36
+ end
37
+
38
+ def epackage_to_json_pretty_string(package, append = [])
39
+ JSON.pretty_generate([epackage_to_json(package)] + append)
40
+ end
41
+
42
+ def epackage_to_json_string(package, append = [])
43
+ JSON.generate([epackage_to_json(package)] + append)
44
+ end
45
+
46
+ def emodelelement(me)
47
+ {
48
+ :eAnnotations => me.eAnnotations.map { |e| eannotation(e) }
49
+ }
50
+ end
51
+
52
+
53
+ def enamedelement(ne)
54
+ merge(emodelelement(ne), {:name => ne.name})
55
+ end
56
+
57
+ def epackage(package)
58
+ merge(enamedelement(package), {
59
+ :_class_ref => 'RGen.ECore.EPackage',
60
+ :eClassifiers => package.eClassifiers.map do |classifier|
61
+ if classifier.is_a?(RGen::ECore::EClass)
62
+ eclass(classifier)
63
+ elsif classifier.is_a?(RGen::ECore::EEnum)
64
+ eenum(classifier)
65
+ else
66
+ edatatype(classifier)
67
+ end
68
+ end,
69
+ :eSubpackages => package.eSubpackages.map { |sp| epackage(sp) },
70
+ :nsURI => package.nsURI,
71
+ :nsPrefix => package.nsPrefix
72
+ })
73
+ end
74
+
75
+ def eclassifier(classifier)
76
+ enamedelement(classifier).merge({
77
+ # omit :instanceClassName => classifier.instanceClassName
78
+ })
79
+ end
80
+
81
+ def eclass(_class)
82
+ merge(eclassifier(_class), {
83
+ :_class_ref => 'RGen.ECore.EClass',
84
+ :abstract => _class.abstract,
85
+ :interface => _class.interface,
86
+ :eStructuralFeatures => _class.eStructuralFeatures.map do |sf|
87
+ if sf.is_a?(RGen::ECore::EReference)
88
+ ereference(sf)
89
+ else
90
+ eattribute(sf)
91
+ end
92
+ end,
93
+ :eSuperTypes => _class.eSuperTypes.map { |st| {:_ref => ref_id(st)} }
94
+ })
95
+ end
96
+
97
+ def edatatype(_datatype)
98
+ merge(eclassifier(_datatype), {
99
+ :_class_ref => 'RGen.ECore.EDataType',
100
+ :serializable => _datatype.serializable,
101
+ :instanceClassName => _datatype.instanceClassName
102
+ })
103
+ end
104
+
105
+ def eenum(enum)
106
+ merge(edatatype(enum), {
107
+ :_class_ref => 'RGen.ECore.EEnum',
108
+ :eLiterals => enum.eLiterals.map do |l|
109
+ merge(enamedelement(l), {
110
+ :_class_ref => 'RGen.ECore.EEnumLiteral',
111
+ :value => l.value,
112
+ })
113
+ end
114
+ })
115
+ end
116
+
117
+ def eannotation(e)
118
+ merge(emodelelement(e), {
119
+ :_class_ref => 'RGen.ECore.EAnnotation',
120
+ :source => e.source,
121
+ :details => e.details.map do |d|
122
+ merge({}, {
123
+ :_class_ref => 'RGen.ECore.EStringToStringMapEntry',
124
+ :key => d.key,
125
+ :value => d.value
126
+ })
127
+ end
128
+ })
129
+ end
130
+
131
+ def etypedelement(te)
132
+ merge(enamedelement(te), {
133
+ :ordered => te.ordered,
134
+ :unique => te.unique,
135
+ :lowerBound => te.lowerBound,
136
+ :upperBound => te.upperBound,
137
+ :many => te.many,
138
+ :required => te.required,
139
+ :eType => {:_ref => te.eType ? ref_id(te.eType) : nil}
140
+ })
141
+ end
142
+
143
+ def estructuralfeature(sf)
144
+ merge(etypedelement(sf), {
145
+ :changeable => sf.changeable,
146
+ :volatile => sf.volatile,
147
+ :transient => sf.transient,
148
+ :defaultValueLiteral => sf.defaultValueLiteral,
149
+ :unsettable => sf.unsettable,
150
+ :derived => sf.derived,
151
+ })
152
+ end
153
+
154
+ def eattribute(attr)
155
+ merge(estructuralfeature(attr), {
156
+ :_class_ref => 'RGen.ECore.EAttribute',
157
+ :iD => attr.iD
158
+ })
159
+ end
160
+
161
+ def ereference(ref)
162
+ merge(estructuralfeature(ref), {
163
+ :_class_ref => 'RGen.ECore.EReference',
164
+ :containment => ref.containment,
165
+ :resolveProxies => ref.resolveProxies,
166
+ :eOpposite => ref.eOpposite ? {:_ref => "#{ref_id(ref.eOpposite.eContainer)}.#{ref.eOpposite.name}"} : nil
167
+ })
168
+ end
169
+
170
+ def ref_id(obj)
171
+ res = ref_parts(obj)
172
+ res.join('.')
173
+ end
174
+
175
+ def ref_parts(obj)
176
+ return [obj.name] unless obj.andand.eContainer
177
+ ref_parts(obj.eContainer) << obj.name
178
+ end
179
+
180
+ def merge(hash, values)
181
+ values.each { |k, v| hash[k] = v unless v.nil? }
182
+ hash
183
+ end
184
+
185
+ end
186
+
187
+ end
188
+
189
+ end
@@ -1,3 +1,4 @@
1
+ require 'set'
1
2
  require 'rgen/ecore/ecore'
2
3
 
3
4
  module RGen
@@ -11,6 +12,7 @@ class ECoreToRuby
11
12
  @modules = {}
12
13
  @classifiers = {}
13
14
  @features_added = {}
15
+ @reserved = Set.new(Object.methods)
14
16
  end
15
17
 
16
18
  # Create a Ruby module representing +epackage+.
@@ -38,22 +40,35 @@ class ECoreToRuby
38
40
  # of a class/module with a temporary name is queried.
39
41
  #
40
42
  def create_module(epackage, under=Module.new)
41
- temp = under.to_s.start_with?("#")
42
- mod = create_module_internal(epackage, under, temp)
43
-
44
- epackage.eAllClassifiers.each do |c|
45
- if c.is_a?(RGen::ECore::EClass)
46
- create_class(c, temp)
47
- elsif c.is_a?(RGen::ECore::EEnum)
48
- create_enum(c)
43
+ with_empty_constant_order_helper do
44
+ temp = under.to_s.start_with?("#")
45
+ mod = create_module_internal(epackage, under, temp)
46
+
47
+ epackage.eAllClassifiers.each do |c|
48
+ if c.is_a?(RGen::ECore::EClass)
49
+ create_class(c, temp)
50
+ elsif c.is_a?(RGen::ECore::EEnum)
51
+ create_enum(c)
52
+ end
49
53
  end
50
- end
51
54
 
52
- mod
55
+ load_classes_with_reserved_keywords(epackage)
56
+ mod
57
+ end
53
58
  end
54
59
 
55
60
  private
56
61
 
62
+ def load_classes_with_reserved_keywords(epackage)
63
+ epackage.eAllClassifiers.each do |eclass|
64
+ # we early load classes which have ruby reserved keywords
65
+ if eclass.is_a?(RGen::ECore::EClass)
66
+ reserved_used = eclass.eStructuralFeatures.any? { |f| @reserved.include?(f.name.to_sym) }
67
+ add_features(eclass) if reserved_used
68
+ end
69
+ end
70
+ end
71
+
57
72
  def create_module_internal(epackage, under, temp)
58
73
  return @modules[epackage] if @modules[epackage]
59
74
 
@@ -199,6 +214,27 @@ class ECoreToRuby
199
214
  end
200
215
  end
201
216
 
217
+ class EmptyConstantOrderHelper
218
+ def classCreated(c); end
219
+ def moduleCreated(m); end
220
+ def enumCreated(e); end
221
+ end
222
+
223
+ def with_empty_constant_order_helper
224
+ orig_coh = RGen::MetamodelBuilder::ConstantOrderHelper
225
+ RGen::MetamodelBuilder.instance_eval { remove_const(:ConstantOrderHelper) }
226
+ RGen::MetamodelBuilder.const_set(:ConstantOrderHelper, EmptyConstantOrderHelper.new)
227
+
228
+ begin
229
+ result = yield
230
+ ensure
231
+ RGen::MetamodelBuilder.instance_eval { remove_const(:ConstantOrderHelper) }
232
+ RGen::MetamodelBuilder.const_set(:ConstantOrderHelper, orig_coh)
233
+ end
234
+
235
+ result
236
+ end
237
+
202
238
  public
203
239
 
204
240
  def add_features(eclass)
@@ -111,6 +111,8 @@ class FragmentedModel
111
111
  # This is a Hash mapping identifiers to model elements accessible via the identifier.
112
112
  #
113
113
  def index
114
+ # Invalidate the cache when any fragment's local index changes.
115
+ # Assumption: If the local index content changes, there is a new index object.
114
116
  fragments.each do |f|
115
117
  if !@fragment_index[f] || (@fragment_index[f].object_id != f.index.object_id)
116
118
  @fragment_index[f] = f.index
@@ -117,6 +117,9 @@ class ModelFragment
117
117
  end
118
118
 
119
119
  # Returns the index of the element contained in this fragment.
120
+ #
121
+ # FragmentedModel's index caching depends on the fact that any change
122
+ # of a fragment's index contents implies a new index object.
120
123
  #
121
124
  def index
122
125
  build_index unless @index
@@ -319,6 +319,10 @@ module BuilderExtensions
319
319
  end
320
320
  <% if name != "class" %>
321
321
  alias <%= name %> get<%= firstToUpper(name) %>
322
+ <% else %>
323
+ def getGeneric(role)
324
+ send("get\#{firstToUpper(role.to_s)}")
325
+ end
322
326
  <% end %>
323
327
 
324
328
  CODE
@@ -380,10 +384,14 @@ module BuilderExtensions
380
384
  @@many_read_builder ||= ERB.new <<-CODE
381
385
 
382
386
  def get<%= firstToUpper(name) %>
383
- ( @<%= name %> ? @<%= name %>.dup : [] )
387
+ ( defined?(@<%= name %>) ? @<%= name %>.dup : [] )
384
388
  end
385
389
  <% if name != "class" %>
386
390
  alias <%= name %> get<%= firstToUpper(name) %>
391
+ <% else %>
392
+ def getGeneric(role)
393
+ send("get\#{firstToUpper(role.to_s)}")
394
+ end
387
395
  <% end %>
388
396
 
389
397
  CODE
@@ -394,7 +402,7 @@ module BuilderExtensions
394
402
  @@many_write_builder ||= ERB.new <<-CODE
395
403
 
396
404
  def add<%= firstToUpper(name) %>(val, index=-1)
397
- @<%= name %> = [] unless @<%= name %>
405
+ @<%= name %> = [] unless defined?(@<%= name %>)
398
406
  return if val.nil? || (val.is_a?(MMBase) || val.is_a?(MMGeneric)) && @<%= name %>.any? {|e| e.equal?(val)}
399
407
  <%= type_check_code("val", props) %>
400
408
  @<%= name %>.insert(index, val)
@@ -407,7 +415,7 @@ module BuilderExtensions
407
415
  end
408
416
 
409
417
  def remove<%= firstToUpper(name) %>(val)
410
- @<%= name %> = [] unless @<%= name %>
418
+ @<%= name %> = [] unless defined?(@<%= name %>)
411
419
  @<%= name %>.each_with_index do |e,i|
412
420
  if e.equal?(val)
413
421
  @<%= name %>.delete_at(i)
@@ -428,7 +436,7 @@ module BuilderExtensions
428
436
  get<%= firstToUpper(name) %>.each {|e|
429
437
  remove<%= firstToUpper(name) %>(e)
430
438
  }
431
- @<%= name %> = [] unless @<%= name %>
439
+ @<%= name %> = [] unless defined?(@<%= name %>)
432
440
  <% if props.reference? %>
433
441
  val.uniq {|elem| elem.object_id }.each {|elem|
434
442
  next if elem.nil?
@@ -451,7 +459,7 @@ module BuilderExtensions
451
459
  alias <%= name %>= set<%= firstToUpper(name) %>
452
460
 
453
461
  def _register<%= firstToUpper(name) %>(val)
454
- @<%= name %> = [] unless @<%= name %>
462
+ @<%= name %> = [] unless defined?(@<%= name %>)
455
463
  @<%= name %>.push val
456
464
  <% if props.reference? && props.value(:containment) %>
457
465
  val._set_container(self, :<%= name %>)
@@ -531,7 +539,7 @@ module BuilderExtensions
531
539
  code << "\n"
532
540
  expected = "Integer"
533
541
  elsif props.impl_type.is_a?(Class)
534
- code << "unless #{varname}.nil? || #{varname}.is_a?(#{props.impl_type}) || #{varname}.is_a?(MMGeneric)"
542
+ code << "unless #{varname}.nil? || #{varname}.is_a?(ObjectSpace._id2ref(#{props.impl_type.object_id})) || #{varname}.is_a?(MMGeneric)"
535
543
  code << " || #{varname}.is_a?(BigDecimal)" if props.impl_type == Float && defined?(BigDecimal)
536
544
  code << "\n"
537
545
  expected = props.impl_type.to_s
@@ -58,14 +58,17 @@ module BuilderRuntime
58
58
  end
59
59
  end
60
60
 
61
- def getGeneric(role)
62
- send("get#{firstToUpper(role.to_s)}")
63
- end
61
+ alias getGeneric send
64
62
 
65
63
  def getGenericAsArray(role)
66
64
  result = getGeneric(role)
67
- result = [result].compact unless result.is_a?(Array)
68
- result
65
+ if result.nil?
66
+ []
67
+ elsif result.is_a?(Array)
68
+ result
69
+ else
70
+ [result]
71
+ end
69
72
  end
70
73
 
71
74
  def eIsSet(role)
@@ -100,6 +103,10 @@ module BuilderRuntime
100
103
 
101
104
  # if a block is given, calls the block on every contained element in depth first order.
102
105
  # if the block returns :prune, recursion will stop at this point.
106
+ #
107
+ # BEWARE of concurrent modification of contained elements while iterating!
108
+ # (adding/removing containers or contained elements)
109
+ # if you need to do such modifications, use the variant without a block instead.
103
110
  #
104
111
  # if no block is given builds and returns a list of all contained elements.
105
112
  #
@@ -91,6 +91,16 @@ module TemplateLanguage
91
91
  @output.concat(s)
92
92
  end
93
93
 
94
+ def direct_concat_allow_indent(s)
95
+ unless @noIndentNextLine || (@output[-1] && @output[-1] != NL_CHAR)
96
+ @output.concat(@indent_string)
97
+ else
98
+ @noIndentNextLine = false
99
+ end
100
+ @state = :wait_for_nl
101
+ @output.concat(s)
102
+ end
103
+
94
104
  def ignoreNextNL
95
105
  @ignoreNextNL = true
96
106
  end
@@ -84,7 +84,7 @@ module RGen
84
84
  end
85
85
 
86
86
  def ws
87
- _direct_concat(" ")
87
+ _direct_concat(" ", true)
88
88
  end
89
89
 
90
90
  def iinc
@@ -210,9 +210,13 @@ module RGen
210
210
  raise StandardError.new("Template class not matching: #{tpl} for #{context.class.name}") unless found
211
211
  end
212
212
 
213
- def _direct_concat(s)
213
+ def _direct_concat(s, allow_indent=false)
214
214
  if @output.is_a? OutputHandler
215
- @output.direct_concat(s)
215
+ if allow_indent
216
+ @output.direct_concat_allow_indent(s)
217
+ else
218
+ @output.direct_concat(s)
219
+ end
216
220
  else
217
221
  @output << s
218
222
  end
@@ -129,6 +129,7 @@ module RGen
129
129
  # Starting from this point one can add indentation and newlines as required by using
130
130
  # explicit formatting commands:
131
131
  # * <code><%nl%></code> (newline) starts a new line
132
+ # * <code><%ws%></code> (whitespace) adds an explicit space
132
133
  # * <code><%iinc%></code> (indentation increment) increases the current indentation
133
134
  # * <code><%idec%></code> (indentation decrement) decreases the current indentation
134
135
  # * <code><%nonl%></code> (no newline) ignore next newline
@@ -294,4 +295,4 @@ module TemplateLanguage
294
295
 
295
296
  end
296
297
 
297
- end
298
+ end