rgen 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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)]