rgen 0.4.0 → 0.4.1

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 (30) hide show
  1. data/CHANGELOG +15 -0
  2. data/lib/instantiators/ea_instantiator.rb +5 -2
  3. data/lib/metamodels/uml13_metamodel.rb +10 -10
  4. data/lib/mmgen/templates/annotations.tpl +1 -1
  5. data/lib/mmgen/templates/metamodel_generator.tpl +1 -1
  6. data/lib/rgen/ecore/ecore.rb +2 -2
  7. data/lib/rgen/ecore/ecore_instantiator.rb +4 -2
  8. data/lib/rgen/ecore/ecore_transformer.rb +6 -1
  9. data/lib/rgen/environment.rb +5 -0
  10. data/lib/rgen/environment.rb.bak +42 -0
  11. data/lib/rgen/instantiator/abstract_xml_instantiator.rb +40 -5
  12. data/lib/rgen/instantiator/ecore_xml_instantiator.rb +1 -1
  13. data/lib/rgen/instantiator/xmi11_instantiator.rb +1 -1
  14. data/lib/rgen/metamodel_builder/builder_extensions.rb +57 -39
  15. data/lib/rgen/serializer/xmi20_serializer.rb +60 -0
  16. data/lib/rgen/serializer/xml_serializer.rb +61 -0
  17. data/lib/rgen/template_language/output_handler.rb +15 -27
  18. data/lib/rgen/template_language/template_container.rb +1 -1
  19. data/lib/rgen/transformer.rb +50 -8
  20. data/test/metamodel_builder_test.rb +22 -0
  21. data/test/metamodel_roundtrip_test.rb +10 -0
  22. data/test/metamodel_roundtrip_test/TestModel.rb +2 -0
  23. data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +10 -8
  24. data/test/metamodel_roundtrip_test/houseMetamodel.ecore +8 -0
  25. data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +268 -0
  26. data/test/metamodel_roundtrip_test/houseMetamodel_from_ecore.rb +8 -5
  27. data/test/output_handler_test.rb +10 -0
  28. data/test/transformer_test.rb +13 -1
  29. metadata +7 -3
  30. data/lib/rgen/metamodel_builder/build_helper.rb +0 -51
data/CHANGELOG CHANGED
@@ -35,3 +35,18 @@
35
35
  * Added UML1.3 Metamodel
36
36
  * Added tranformation from UML1.3 to ECore
37
37
 
38
+ =0.4.1 (Nov 25th, 2007)
39
+
40
+ * Template language performance improvement
41
+ * Bugfix: use true/false instead of symbols for boolean attribute default values in metamodel classes
42
+ * Minor fixes on metamodel generator and ecore primitive type handling
43
+ * Made transformer implementation non-recursive to prevent "stack level too deep" exception for large models
44
+ * Minor fixes on EAInstantiator
45
+ * Made transformer search for matching rules for superclasses
46
+ * Bugfix: Enums are now added to EPackages created using the "ecore" method on a module
47
+ * Bugfix: Metamodel generator now writes enum names
48
+ * Performance improvement: don't require ecore transformer every time someone calls "ecore"
49
+ * Major performance improvement of template engine (no Regexps to check \n at end of line)
50
+ * Major performance improvement: AbstractXMLInstantiator optionally controls the garbage collector
51
+ * Major performance improvement: ERB templates are reused in metamodel_builder
52
+ * Added delete method to Environment
@@ -8,13 +8,16 @@ class EAInstantiator < XMI11Instantiator
8
8
  :tags => {
9
9
  "EAStub" => proc { |tag, attr| UML13::Class.new(
10
10
  :name => attr["name"]
11
- )}
11
+ )},
12
+ "ActivityModel" => "ActivityGraph",
13
+ "PseudoState" => "Pseudostate"
12
14
  },
13
15
  :feature_names => {
14
16
  "isOrdered" => "ordering",
15
17
  "subtype" => "child",
16
18
  "supertype" => "parent",
17
- "changeable" => "changeability"
19
+ "changeable" => "changeability",
20
+ "substate" => "subvertex"
18
21
  },
19
22
  :feature_values => {
20
23
  "ordering" => {"true" => "ordered", "false" => "unordered"},
@@ -4,16 +4,16 @@ module UML13
4
4
  extend RGen::MetamodelBuilder::ModuleExtension
5
5
  include RGen::MetamodelBuilder::DataTypes
6
6
 
7
- AggregationKind = Enum.new([ :none, :aggregate, :composite ])
8
- ChangeableKind = Enum.new([ :changeable, :frozen, :addOnly ])
9
- OperationDirectionKind = Enum.new([ ])
10
- ParameterDirectionKind = Enum.new([ :in, :inout, :out, :return ])
11
- MessageDirectionKind = Enum.new([ ])
12
- ScopeKind = Enum.new([ :instance, :classifier ])
13
- VisibilityKind = Enum.new([ :public, :protected, :private ])
14
- PseudostateKind = Enum.new([ :initial, :deepHistory, :shallowHistory, :join, :fork, :branch, :junction, :final ])
15
- CallConcurrencyKind = Enum.new([ :sequential, :guarded, :concurrent ])
16
- OrderingKind = Enum.new([ :unordered, :ordered, :sorted ])
7
+ AggregationKind = Enum.new(:name => "AggregationKind", :literals =>[ :none, :aggregate, :composite ])
8
+ ChangeableKind = Enum.new(:name => "ChangeableKind", :literals =>[ :changeable, :frozen, :addOnly ])
9
+ OperationDirectionKind = Enum.new(:name => "OperationDirectionKind", :literals =>[ ])
10
+ ParameterDirectionKind = Enum.new(:name => "ParameterDirectionKind", :literals =>[ :in, :inout, :out, :return ])
11
+ MessageDirectionKind = Enum.new(:name => "MessageDirectionKind", :literals =>[ ])
12
+ ScopeKind = Enum.new(:name => "ScopeKind", :literals =>[ :instance, :classifier ])
13
+ VisibilityKind = Enum.new(:name => "VisibilityKind", :literals =>[ :public, :protected, :private ])
14
+ PseudostateKind = Enum.new(:name => "PseudostateKind", :literals =>[ :initial, :deepHistory, :shallowHistory, :join, :fork, :branch, :junction, :final ])
15
+ CallConcurrencyKind = Enum.new(:name => "CallConcurrencyKind", :literals =>[ :sequential, :guarded, :concurrent ])
16
+ OrderingKind = Enum.new(:name => "OrderingKind", :literals =>[ :unordered, :ordered, :sorted ])
17
17
 
18
18
  class Element < RGen::MetamodelBuilder::MMBase
19
19
  end
@@ -33,5 +33,5 @@
33
33
  <% end %>
34
34
 
35
35
  <% define 'Details', :for => EAnnotation do %>
36
- <%= details.sort{|a,b| a.key<=>b.key}.collect{ |d| "\'" + d.key + "\' => \'"+ d.value.gsub('\'','\\\'').to_s + "\'"}.join(', ') %><%nows%>
36
+ <%= details.sort{|a,b| a.key<=>b.key}.collect{ |d| "\'" + d.key + "\' => \'"+ (d.value || "").gsub('\'','\\\'').to_s + "\'"}.join(', ') %><%nows%>
37
37
  <% end %>
@@ -83,7 +83,7 @@
83
83
 
84
84
  <% define 'EnumTypes', :for => EPackage do %>
85
85
  <% for enum in eClassifiers.select{|c| c.is_a?(EEnum)} %>
86
- <%= enum.name %> = Enum.new([ <%nows%>
86
+ <%= enum.name %> = Enum.new(:name => '<%= enum.name %>', :literals =>[ <%nows%>
87
87
  <%= enum.eLiterals.collect { |lit| ":"+lit.name }.join(', ') %> ])
88
88
  <% end %>
89
89
  <% end %>
@@ -67,10 +67,10 @@ module RGen
67
67
  has_attr 'instanceClassName', String
68
68
  module ClassModule
69
69
  def instanceClass_derived
70
- map = {"java.lang.String" => "String", "boolean" => "RGen::MetamodelBuilder::DataTypes::Boolean", "int" => "Integer"}
70
+ map = {"java.lang.string" => "String", "boolean" => "RGen::MetamodelBuilder::DataTypes::Boolean", "int" => "Integer"}
71
71
  icn = instanceClassName
72
72
  icn = "NilClass" if icn.nil?
73
- icn = map[icn] if map[icn]
73
+ icn = map[icn.downcase] if map[icn.downcase]
74
74
  eval(icn)
75
75
  end
76
76
  end
@@ -13,8 +13,10 @@ module ECoreInstantiator
13
13
  # class or module using ECoreTransformer.
14
14
  #
15
15
  def ecore
16
- require 'rgen/ecore/ecore_transformer'
17
- @@transformer ||= ECoreTransformer.new
16
+ unless defined?(@@transformer)
17
+ require 'rgen/ecore/ecore_transformer'
18
+ @@transformer = ECoreTransformer.new
19
+ end
18
20
  @@transformer.trans(self)
19
21
  end
20
22
 
@@ -31,8 +31,12 @@ class ECoreTransformer < Transformer
31
31
  end
32
32
 
33
33
  transform Module, :to => EPackage, :if => :convert? do
34
+ @enumParentModule ||= {}
35
+ constants.select {|c| const_get(c).is_a?(MetamodelBuilder::DataTypes::Enum)}.
36
+ each {|c| @enumParentModule[const_get(c)] = @current_object}
34
37
  { :name => name.gsub(/.*::(\w+)$/,'\1'),
35
- :eClassifiers => trans(constants.collect{|c| const_get(c)}.select{|c| c.is_a?(Class)}),
38
+ :eClassifiers => trans(constants.collect{|c| const_get(c)}.select{|c| c.is_a?(Class) ||
39
+ (c.is_a?(MetamodelBuilder::DataTypes::Enum) && c != MetamodelBuilder::DataTypes::Boolean) }),
36
40
  :eSuperPackage => trans(name =~ /(.*)::\w+$/ ? eval($1) : nil),
37
41
  :eSubpackages => trans(constants.collect{|c| const_get(c)}.select{|c| c.is_a?(Module) && !c.is_a?(Class)}),
38
42
  :eAnnotations => trans(_annotations)
@@ -71,6 +75,7 @@ class ECoreTransformer < Transformer
71
75
 
72
76
  transform MetamodelBuilder::DataTypes::Enum, :to => EEnum do
73
77
  { :name => name,
78
+ :instanceClassName => @enumParentModule && @enumParentModule[@current_object] && @enumParentModule[@current_object].name+"::"+name,
74
79
  :eLiterals => literals.collect do |l|
75
80
  lit = EEnumLiteral.new
76
81
  lit.name = l.to_s
@@ -18,6 +18,11 @@ class Environment
18
18
  self
19
19
  end
20
20
 
21
+ # Removes model element from environment.
22
+ def delete(el)
23
+ @elements.delete(el)
24
+ end
25
+
21
26
  # Iterates each element
22
27
  #
23
28
  def each(&b)
@@ -0,0 +1,42 @@
1
+ require 'rgen/find_helper'
2
+
3
+ module RGen
4
+
5
+ # An Environment is used to hold model elements.
6
+ #
7
+ class Environment
8
+ include RGen::FindHelper
9
+
10
+ def initialize
11
+ @elements = []
12
+ end
13
+
14
+ # Add a model element. Returns the environment so <code><<</code> can be chained.
15
+ #
16
+ def <<(el)
17
+ @elements << el
18
+ self
19
+ end
20
+
21
+ # Iterates each element
22
+ #
23
+ def each(&b)
24
+ @elements.each(&b)
25
+ end
26
+
27
+ # Return the elements of the environment as an array
28
+ #
29
+ def elements
30
+ @elements.dup
31
+ end
32
+
33
+ # This method can be used to instantiate a class and automatically put it into
34
+ # the environment. The new instance is returned.
35
+ #
36
+ def new(clazz, *args)
37
+ @elements << clazz.new(*args)
38
+ @elements[-1]
39
+ end
40
+ end
41
+
42
+ end
@@ -7,9 +7,10 @@ class AbstractXMLInstantiator
7
7
  class XMLScanVisitor
8
8
  include XMLScan::NSVisitor
9
9
 
10
- def initialize(inst)
10
+ def initialize(inst, gcSuspendCount)
11
11
  @current_attributes = {}
12
12
  @instantiator = inst
13
+ @gcSuspendCount = gcSuspendCount
13
14
  end
14
15
 
15
16
  def on_attribute_ns(qname, prefix, localpart)
@@ -30,6 +31,7 @@ class AbstractXMLInstantiator
30
31
  end
31
32
 
32
33
  def on_stag_end_ns(qname, namespaces)
34
+ controlGC
33
35
  prefix, tag = split_qname(qname)
34
36
  @instantiator.start_tag(prefix, tag, namespaces, @current_attributes)
35
37
  @current_attributes.each_pair { |k,v| @instantiator.set_attribute(k, v) }
@@ -37,6 +39,7 @@ class AbstractXMLInstantiator
37
39
  end
38
40
 
39
41
  def on_stag_end_empty_ns(qname, namespaces)
42
+ controlGC
40
43
  prefix, tag = split_qname(qname)
41
44
  @instantiator.start_tag(prefix, tag, namespaces, @current_attributes)
42
45
  @current_attributes.each_pair { |k,v| @instantiator.set_attribute(k, v) }
@@ -48,12 +51,44 @@ class AbstractXMLInstantiator
48
51
  prefix, tag = split_qname(qname)
49
52
  @instantiator.end_tag(prefix, tag)
50
53
  end
54
+
55
+ def on_chardata(str)
56
+ @instantiator.text(str)
57
+ end
58
+
59
+ def controlGC
60
+ return unless @gcSuspendCount > 0
61
+ @gcCounter ||= 0
62
+ @gcCounter += 1
63
+ if @gcCounter == @gcSuspendCount
64
+ @gcCounter = 0
65
+ GC.enable
66
+ ObjectSpace.garbage_collect
67
+ GC.disable
68
+ end
69
+ end
51
70
  end
52
71
 
53
- def instantiate(str)
54
- visitor = XMLScanVisitor.new(self)
55
- parser = XMLScan::XMLParserNS.new(visitor)
56
- parser.parse(str)
72
+ # Parses str and calls start_tag, end_tag, set_attribute and text methods of a subclass.
73
+ #
74
+ # If gcSuspendCount is specified, the garbage collector will be disabled for that
75
+ # number of start or end tags. After that period it will clean up and then be disabled again.
76
+ # A value of about 1000 can significantly improve overall performance.
77
+ # The memory usage normally does not increase.
78
+ # Depending on the work done for every xml tag the value might have to be adjusted.
79
+ #
80
+ def instantiate(str, gcSuspendCount=0)
81
+ gcDisabledBefore = GC.disable
82
+ gcSuspendCount = 0 if gcDisabledBefore
83
+ begin
84
+ visitor = XMLScanVisitor.new(self, gcSuspendCount)
85
+ parser = XMLScan::XMLParserNS.new(visitor)
86
+ parser.parse(str)
87
+ ensure
88
+ GC.enable unless gcDisabledBefore
89
+ end
57
90
  end
58
91
 
92
+ def text(str)
93
+ end
59
94
  end
@@ -81,7 +81,7 @@ class ECoreXMLInstantiator < AbstractXMLInstantiator
81
81
  def instantiate(str)
82
82
  @resolver_descs = []
83
83
  # puts "Instantiating ..."
84
- super
84
+ super(str, 1000)
85
85
  rootpackage = @env.find(:class => EPackage).first
86
86
  # puts "Resolving ..."
87
87
  @resolver_descs.each do |rd|
@@ -28,7 +28,7 @@ class XMI11Instantiator < AbstractXMLInstantiator
28
28
  def instantiate(str)
29
29
  @resolver_descs = []
30
30
  @element_by_id = {}
31
- super(str)
31
+ super(str, 1000)
32
32
  @resolver_descs.each do |rd|
33
33
  if rd.many
34
34
  newval = rd.value.split(" ").collect{|v| @element_by_id[v]}
@@ -2,7 +2,6 @@
2
2
  # (c) Martin Thiede, 2006
3
3
 
4
4
  require 'erb'
5
- require 'rgen/metamodel_builder/build_helper'
6
5
  require 'rgen/metamodel_builder/metamodel_description.rb'
7
6
 
8
7
  module RGen
@@ -268,39 +267,41 @@ module BuilderExtensions
268
267
  if props.value(:derived)
269
268
  build_derived_method(name, props, :one)
270
269
  else
271
- BuildHelper.build _class_module, binding, <<-CODE
270
+ @@one_read_builder ||= ERB.new <<-CODE
272
271
 
273
- def #{name}
272
+ def <%= name %>
274
273
  <% if props.is_a?(AttributeDescription) && props.value(:defaultValueLiteral) %>
275
274
  <% defVal = props.value(:defaultValueLiteral) %>
276
275
  <% defVal = '"'+defVal+'"' if props.impl_type == String %>
277
- <% defVal = ':'+defVal if props.impl_type.is_a?(DataTypes::Enum) %>
278
- @#{name}.nil? ? <%= defVal %> : @#{name}
276
+ <% defVal = ':'+defVal if props.impl_type.is_a?(DataTypes::Enum) && props.impl_type != DataTypes::Boolean %>
277
+ @<%= name %>.nil? ? <%= defVal %> : @<%= name %>
279
278
  <% else %>
280
- @#{name}
279
+ @<%= name %>
281
280
  <% end %>
282
281
  end
283
- alias get<%= firstToUpper(name) %> #{name}
282
+ alias get<%= firstToUpper(name) %> <%= name %>
284
283
 
285
284
  CODE
285
+ _class_module.module_eval(@@one_read_builder.result(binding))
286
286
  end
287
287
 
288
288
  if props.value(:changeable)
289
- BuildHelper.build _class_module, binding, <<-CODE
289
+ @@one_write_builder ||= ERB.new <<-CODE
290
290
 
291
- def #{name}=(val)
292
- return if val == @#{name}
291
+ def <%= name %>=(val)
292
+ return if val == @<%= name %>
293
293
  <%= type_check_code("val", props) %>
294
- oldval = @#{name}
295
- @#{name} = val
294
+ oldval = @<%= name %>
295
+ @<%= name %> = val
296
296
  <% if other_role && other_kind %>
297
- _unregister(self,oldval,"#{other_role}","#{other_kind}")
298
- _register(self,val,"#{other_role}","#{other_kind}")
297
+ _unregister(self,oldval,"<%= other_role %>","<%= other_kind %>")
298
+ _register(self,val,"<%= other_role %>","<%= other_kind %>")
299
299
  <% end %>
300
300
  end
301
- alias set<%= firstToUpper(name) %> #{name}=
301
+ alias set<%= firstToUpper(name) %> <%= name %>=
302
302
 
303
303
  CODE
304
+ _class_module.module_eval(@@one_write_builder.result(binding))
304
305
 
305
306
  end
306
307
  end
@@ -315,51 +316,53 @@ module BuilderExtensions
315
316
  if props.value(:derived)
316
317
  build_derived_method(name, props, :many)
317
318
  else
318
- BuildHelper.build _class_module, binding, <<-CODE
319
+ @@many_read_builder ||= ERB.new <<-CODE
319
320
 
320
- def #{name}
321
- ( @#{name} ? @#{name}.dup : [] )
321
+ def <%= name %>
322
+ ( @<%= name %> ? @<%= name %>.dup : [] )
322
323
  end
323
- alias get<%= firstToUpper(name) %> #{name}
324
+ alias get<%= firstToUpper(name) %> <%= name %>
324
325
 
325
326
  CODE
327
+ _class_module.module_eval(@@many_read_builder.result(binding))
326
328
  end
327
329
 
328
330
  if props.value(:changeable)
329
- BuildHelper.build _class_module, binding, <<-CODE
331
+ @@many_write_builder ||= ERB.new <<-CODE
330
332
 
331
333
  def add<%= firstToUpper(name) %>(val)
332
- @#{name} = [] unless @#{name}
333
- return if val.nil? or @#{name}.include?(val)
334
+ @<%= name %> = [] unless @<%= name %>
335
+ return if val.nil? or @<%= name %>.include?(val)
334
336
  <%= type_check_code("val", props) %>
335
- @#{name}.push val
337
+ @<%= name %>.push val
336
338
  <% if other_role && other_kind %>
337
- _register(self, val, "#{other_role}", "#{other_kind}")
339
+ _register(self, val, "<%= other_role %>", "<%= other_kind %>")
338
340
  <% end %>
339
341
  end
340
342
 
341
343
  def remove<%= firstToUpper(name) %>(val)
342
- @#{name} = [] unless @#{name}
343
- return unless @#{name}.include?(val)
344
- @#{name}.delete val
344
+ @<%= name %> = [] unless @<%= name %>
345
+ return unless @<%= name %>.include?(val)
346
+ @<%= name %>.delete val
345
347
  <% if other_role && other_kind %>
346
- _unregister(self, val, "#{other_role}", "#{other_kind}")
348
+ _unregister(self, val, "<%= other_role %>", "<%= other_kind %>")
347
349
  <% end %>
348
350
  end
349
351
 
350
- def #{name}=(val)
352
+ def <%= name %>=(val)
351
353
  return if val.nil?
352
354
  raise _assignmentTypeError(self, val, Array) unless val.is_a? Array
353
- getGeneric(:#{name}).each {|e|
355
+ getGeneric(:<%= name %>).each {|e|
354
356
  remove<%= firstToUpper(name) %>(e)
355
357
  }
356
358
  val.each {|v|
357
359
  add<%= firstToUpper(name) %>(v)
358
360
  }
359
361
  end
360
- alias set<%= firstToUpper(name) %> #{name}=
362
+ alias set<%= firstToUpper(name) %> <%= name %>=
361
363
 
362
364
  CODE
365
+ _class_module.module_eval(@@many_write_builder.result(binding))
363
366
  end
364
367
 
365
368
  end
@@ -369,12 +372,12 @@ module BuilderExtensions
369
372
  def build_derived_method(name, props, kind)
370
373
  raise "Implement method #{name}_derived instead of method #{name}" \
371
374
  if (public_instance_methods+protected_instance_methods+private_instance_methods).include?(name)
372
- BuildHelper.build _class_module, binding, <<-CODE
375
+ @@derived_builder ||= ERB.new <<-CODE
373
376
 
374
- def #{name}
375
- raise "Derived feature requires public implementation of method #{name}_derived" \
376
- unless respond_to?(:#{name+"_derived"})
377
- val = #{name}_derived
377
+ def <%= name %>
378
+ raise "Derived feature requires public implementation of method <%= name %>_derived" \
379
+ unless respond_to?(:<%= name+"_derived" %>)
380
+ val = <%= name %>_derived
378
381
  <% if kind == :many %>
379
382
  raise _assignmentTypeError(self,val,Array) unless val && val.is_a?(Array)
380
383
  val.each do |v|
@@ -385,13 +388,28 @@ module BuilderExtensions
385
388
  <% end %>
386
389
  val
387
390
  end
388
- alias get#{firstToUpper(name)} #{name}
389
- #TODO final_method :#{name}
391
+ alias get<%= firstToUpper(name) %> <%= name %>
392
+ #TODO final_method :<%= name %>
390
393
 
391
394
  CODE
395
+ _class_module.module_eval(@@derived_builder.result(binding))
392
396
  end
393
397
 
394
- private
398
+ def type_check_code(varname, props)
399
+ code = ""
400
+ if props.impl_type.is_a?(Class)
401
+ code << "unless #{varname}.nil? or #{varname}.is_a? #{props.impl_type}\n"
402
+ expected = props.impl_type.to_s
403
+ elsif props.impl_type.is_a?(RGen::MetamodelBuilder::DataTypes::Enum)
404
+ code << "unless #{varname}.nil? or [#{props.impl_type.literals_as_strings.join(',')}].include?(#{varname})\n"
405
+ expected = "["+props.impl_type.literals_as_strings.join(',')+"]"
406
+ else
407
+ raise StandardError.new("Unkown type "+props.impl_type.to_s)
408
+ end
409
+ code << "raise _assignmentTypeError(self,#{varname},\"#{expected}\")\n"
410
+ code << "end"
411
+ code
412
+ end
395
413
 
396
414
  def _ownProps(props)
397
415
  Hash[*(props.select{|k,v| !(k.to_s =~ /^opposite_/)}.flatten)]