rgen 0.2.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 (54) hide show
  1. data/CHANGELOG +9 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +73 -0
  4. data/lib/ea/xmi_class_instantiator.rb +45 -0
  5. data/lib/ea/xmi_helper.rb +26 -0
  6. data/lib/ea/xmi_metamodel.rb +19 -0
  7. data/lib/ea/xmi_object_instantiator.rb +42 -0
  8. data/lib/ea/xmi_to_classmodel.rb +78 -0
  9. data/lib/ea/xmi_to_objectmodel.rb +89 -0
  10. data/lib/mmgen/metamodel_generator.rb +19 -0
  11. data/lib/mmgen/mm_ext/uml_classmodel_ext.rb +71 -0
  12. data/lib/mmgen/mmgen.rb +21 -0
  13. data/lib/mmgen/templates/uml_classmodel.tpl +63 -0
  14. data/lib/rgen/array_extensions.rb +23 -0
  15. data/lib/rgen/auto_class_creator.rb +56 -0
  16. data/lib/rgen/environment.rb +57 -0
  17. data/lib/rgen/metamodel_builder.rb +102 -0
  18. data/lib/rgen/metamodel_builder/build_helper.rb +29 -0
  19. data/lib/rgen/metamodel_builder/builder_extensions.rb +191 -0
  20. data/lib/rgen/metamodel_builder/builder_runtime.rb +67 -0
  21. data/lib/rgen/name_helper.rb +18 -0
  22. data/lib/rgen/template_language.rb +169 -0
  23. data/lib/rgen/template_language/directory_template_container.rb +51 -0
  24. data/lib/rgen/template_language/output_handler.rb +84 -0
  25. data/lib/rgen/template_language/template_container.rb +153 -0
  26. data/lib/rgen/template_language/template_helper.rb +26 -0
  27. data/lib/rgen/transformer.rb +316 -0
  28. data/lib/rgen/xml_instantiator/dependency_resolver.rb +23 -0
  29. data/lib/rgen/xml_instantiator/xml_instantiator.rb +78 -0
  30. data/lib/rgen/xml_instantiator/xml_parser.rb +39 -0
  31. data/lib/uml/objectmodel_instantiator.rb +53 -0
  32. data/lib/uml/uml_classmodel.rb +92 -0
  33. data/lib/uml/uml_objectmodel.rb +65 -0
  34. data/test/array_extensions_test.rb +54 -0
  35. data/test/environment_test.rb +47 -0
  36. data/test/metamodel_builder_test.rb +175 -0
  37. data/test/metamodel_generator_test.rb +45 -0
  38. data/test/metamodel_generator_test/TestModel.rb +40 -0
  39. data/test/metamodel_generator_test/expected_result.txt +40 -0
  40. data/test/output_handler_test.rb +40 -0
  41. data/test/rgen_test.rb +13 -0
  42. data/test/template_language_test.rb +46 -0
  43. data/test/template_language_test/expected_result.txt +10 -0
  44. data/test/template_language_test/templates/content/chapter.tpl +5 -0
  45. data/test/template_language_test/templates/index/c/cmod.tpl +1 -0
  46. data/test/template_language_test/templates/index/chapter.tpl +3 -0
  47. data/test/template_language_test/templates/root.tpl +22 -0
  48. data/test/template_language_test/testout.txt +10 -0
  49. data/test/transformer_test.rb +176 -0
  50. data/test/xmi_class_instantiator_test.rb +107 -0
  51. data/test/xmi_instantiator_test/testmodel.eap +0 -0
  52. data/test/xmi_instantiator_test/testmodel.xml +962 -0
  53. data/test/xmi_object_instantiator_test.rb +65 -0
  54. metadata +117 -0
@@ -0,0 +1,21 @@
1
+ require 'ea/xmi_instantiator'
2
+ require 'mmgen/metamodel_generator'
3
+
4
+ include XMIClassInstantiator
5
+ include MMGen::MetamodelGenerator
6
+
7
+ unless ARGV.length == 1
8
+ puts "Usage: mmgen.rb <xmi_class_model_file>"
9
+ exit
10
+ else
11
+ file_name = ARGV.shift
12
+ out_file = file_name.gsub(/\.\w+$/,'')
13
+ puts out_file
14
+ end
15
+
16
+ envUML = RGen::Environment.new
17
+ File.open(file_name) { |f|
18
+ instantiateUMLClassModel(envUML, f.read)
19
+ }
20
+
21
+ generateMetamodel(envUML, out_file)
@@ -0,0 +1,63 @@
1
+
2
+ <% define 'GenerateClassModel', :for => UMLPackage do |filename, modules| %>
3
+ <% file filename do %>
4
+ require 'rgen/metamodel_builder'
5
+
6
+ <% expand 'GeneratePackage', modules %>
7
+ <%nl%>
8
+ <% expand 'GenerateAssocs' %>
9
+ <% end %>
10
+ <% end %>
11
+
12
+ <% define 'GeneratePackage', :for => UMLPackage do |modules| %>
13
+ module <%= moduleName %><% iinc %>
14
+ <% for c in sortedClasses %><%nl%>
15
+ <% expand 'ModuleHeader', modules, :for => c %><%iinc%>
16
+ <% (c.superclasses.name & modules).each do |n| %>
17
+ include <%= n %>
18
+ <% end %>
19
+ <% if modules.include?(c.className) %>
20
+ extend RGen::MetamodelBuilder::BuilderExtensions
21
+ <% end %>
22
+ has_one 'name', String
23
+ <% for a in c.attributes %>
24
+ has_one '<%= a.name %>', <%= a.RubyType %>
25
+ <% end %><%idec%>
26
+ end
27
+ <% end %><%nl%>
28
+ <% for p in subpackages %>
29
+ <%nl%><% expand 'GeneratePackage', modules, :for => p %>
30
+ <% end %><%idec%>
31
+ end
32
+ <% end %>
33
+
34
+ <% define 'GenerateAssocs', :for => UMLPackage do %>
35
+ <% for a in allClasses.assocEnds>>:assoc %>
36
+ <% if a.endA.one? %>
37
+ <% if a.endB.one? %>
38
+ <%= a.endA.clazz.qualifiedName(this) %>.one_to_one '<%= a.endB.MName %>', <%= a.endB.clazz.qualifiedName(this) %>, '<%= a.endA.MName %>'
39
+ <% elsif a.endB.many? %>
40
+ <%= a.endA.clazz.qualifiedName(this) %>.one_to_many '<%= a.endB.MName %>', <%= a.endB.clazz.qualifiedName(this) %>, '<%= a.endA.MName %>'
41
+ <% end %>
42
+ <% elsif a.endA.many? %>
43
+ <% if a.endB.one? %>
44
+ <%= a.endB.clazz.qualifiedName(this) %>.one_to_many '<%= a.endA.MName %>', <%= a.endA.clazz.qualifiedName(this) %>, '<%= a.endB.MName %>'
45
+ <% elsif a.endB.many? %>
46
+ <%= a.endB.clazz.qualifiedName(this) %>.many_to_many '<%= a.endA.MName %>', <%= a.endA.clazz.qualifiedName(this) %>, '<%= a.endB.MName %>'
47
+ <% end %>
48
+ <% end %>
49
+ <% end %>
50
+ <% end %>
51
+
52
+ <% define 'ModuleHeader' do |modules| %>
53
+ <% if modules.include?(name) %>
54
+ module <%= className %>
55
+ <% else %>
56
+ class <%= className %> < <% nows %>
57
+ <% if rsc=RubySuperclass(modules) %><% nows %>
58
+ <%= rsc.className %>
59
+ <% else %><% nows %>
60
+ RGen::MetamodelBuilder::MMBase
61
+ <% end %>
62
+ <% end %>
63
+ <% end %>
@@ -0,0 +1,23 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ require 'rgen/metamodel_builder'
5
+
6
+ class Array
7
+
8
+ def >>(method)
9
+ compact.inject([]) { |r,e| r | ( (o=e.send(method)).is_a?(Array) ? o : [o] ) }
10
+ end
11
+
12
+ def method_missing(m, *args)
13
+ super unless size == 0 or compact.any?{|e| e.is_a? RGen::MetamodelBuilder::MMBase}
14
+ compact.inject([]) { |r,e|
15
+ if e.is_a? RGen::MetamodelBuilder::MMBase
16
+ r | ( (o=e.send(m)).is_a?(Array) ? o : [o] )
17
+ else
18
+ raise StandardError.new("Trying to call a method on an array element not a RGen MMBase")
19
+ end
20
+ }.compact
21
+ end
22
+
23
+ end
@@ -0,0 +1,56 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ require 'rgen/metamodel_builder'
5
+
6
+ module RGen
7
+
8
+ class Base
9
+ extend MetamodelBuilder
10
+ def initialize(env=nil)
11
+ env << self if env
12
+ end
13
+ end
14
+
15
+ class AutoCreatedClass < Base
16
+ def method_missing(m,*args)
17
+ return super unless self.class.parent.accEnabled
18
+ if m.to_s =~ /(.*)=$/
19
+ self.class.has_one($1)
20
+ send(m,args[0])
21
+ elsif args.size == 0
22
+ self.class.has_many(m)
23
+ send(m)
24
+ end
25
+ end
26
+ end
27
+
28
+ # will be "extended" to the auto created class
29
+ module ParentAccess
30
+ def parent=(p)
31
+ @parent = p
32
+ end
33
+ def parent
34
+ @parent
35
+ end
36
+ end
37
+
38
+ module AutoClassCreator
39
+ attr_reader :accEnabled
40
+ def const_missing(className)
41
+ return super unless @accEnabled
42
+ module_eval("class "+className.to_s+" < RGen::AutoCreatedClass; end")
43
+ c = const_get(className)
44
+ c.extend(ParentAccess)
45
+ c.parent = self
46
+ c
47
+ end
48
+ def enableACC
49
+ @accEnabled = true
50
+ end
51
+ def disableACC
52
+ @accEnabled = false
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,57 @@
1
+ module RGen
2
+
3
+ # An Environment is used to hold model elements.
4
+ #
5
+ class Environment
6
+
7
+ def initialize
8
+ @elements = []
9
+ end
10
+
11
+ # Add a model element. Returns the environment so <code><<</code> can be chained.
12
+ #
13
+ def <<(el)
14
+ @elements << el
15
+ self
16
+ end
17
+
18
+ # Finds and returns model elements in the environment.
19
+ #
20
+ # The search description argument must be a hash specifying attribute/value pairs.
21
+ # Only model elements are returned which respond to the specified attribute methods
22
+ # and return the specified values as result of these attribute methods.
23
+ #
24
+ # As a special hash key :class can be used to look for model elements of a specific
25
+ # class. In this case an array of possible classes can optionally be given.
26
+ #
27
+ def find(desc)
28
+ result = []
29
+ classes = desc[:class] if desc[:class] and desc[:class].is_a?(Array)
30
+ classes = [ desc[:class] ] if !classes and desc[:class]
31
+ elements.each {|e|
32
+ failed = false
33
+ failed = true if classes and !classes.any?{ |c| e.is_a?(c) }
34
+ desc.each_pair { |k,v|
35
+ failed = true if k != :class and ( !e.respond_to?(k) or e.send(k) != v )
36
+ }
37
+ result << e unless failed
38
+ }
39
+ result
40
+ end
41
+
42
+ # Return the elements of the environment as an array
43
+ #
44
+ def elements
45
+ @elements.dup
46
+ end
47
+
48
+ # This method can be used to instantiate a class and automatically put it into
49
+ # the environment. The new instance is returned.
50
+ #
51
+ def new(clazz)
52
+ @elements << clazz.new
53
+ @elements[-1]
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,102 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ require 'rgen/metamodel_builder/builder_runtime'
5
+ require 'rgen/metamodel_builder/builder_extensions'
6
+
7
+ module RGen
8
+
9
+ # MetamodelBuilder can be used to create a metamodel, i.e. Ruby classes which
10
+ # act as metamodel elements.
11
+ #
12
+ # To create a new metamodel element, create a Ruby class which inherits from
13
+ # MetamodelBuilder::MMBase
14
+ #
15
+ # class Person < RGen::MetamodelBuilder::MMBase
16
+ # end
17
+ #
18
+ # This way a couple of class methods are made available to the new class.
19
+ # These methods can be used to:
20
+ # * add attributes to the class
21
+ # * add associations with other classes
22
+ #
23
+ # Here is an example:
24
+ #
25
+ # class Person < RGen::MetamodelBuilder::MMBase
26
+ # has_one 'name', String
27
+ # has_one 'age', Integer
28
+ # end
29
+ #
30
+ # class House < RGen::MetamodelBuilder::MMBase
31
+ # has_one 'address'
32
+ # end
33
+ #
34
+ # Person.many_to_many 'homes', House, 'inhabitants'
35
+ #
36
+ # See BuilderExtensions for details about the available class methods.
37
+ #
38
+ # =Attributes
39
+ #
40
+ # The example above creates two classes 'Person' and 'House'. Person has the attributes
41
+ # 'name' and 'age', House has the attribute 'address'. The attributes can be
42
+ # accessed on instances of the classes in the following way:
43
+ #
44
+ # p = Person.new
45
+ # p.name = "MyName"
46
+ # p.age = 22
47
+ # p.name # => "MyName"
48
+ # p.age # => 22
49
+ #
50
+ # Note that the class Person takes care of the type of its attributes. As
51
+ # declared above, a 'name' can only be a String, an 'age' must be an Integer.
52
+ # So the following would return an exception:
53
+ #
54
+ # p.name = :myName # => exception: can not put a Symbol where a String is expected
55
+ #
56
+ # If the type of an attribute should be left undefined, just leave away the
57
+ # second argument of 'has_one' as show at the attribute 'address' for House.
58
+ #
59
+ # =Associations
60
+ #
61
+ # As well as attributes show up as instance methods, associations bring their own
62
+ # accessor methods. For the Person-to-House association this would be:
63
+ #
64
+ # h1 = House.new
65
+ # h1.address = "Street1"
66
+ # h2 = House.new
67
+ # h2.address = "Street2"
68
+ # p.addHomes(h1)
69
+ # p.addHomes(h2)
70
+ # p.removeHomes(h1)
71
+ # p.homes # => [ h2 ]
72
+ #
73
+ # The Person-to-House association is _bidirectional_. This means that with the
74
+ # addition of a House to a Person, the Person is also added to the House. Thus:
75
+ #
76
+ # h1.inhabitants # => []
77
+ # h2.inhabitants # => [ p ]
78
+ #
79
+ # Note that the association is defined between two specific classes, instances of
80
+ # different classes can not be added. Thus, the following would result in an
81
+ # exception:
82
+ #
83
+ # p.addHomes(:justASymbol) # => exception: can not put a Symbol where a House is expected
84
+ #
85
+ # _Unidirectional_ associations can be thought of as attributes as shown above. This means
86
+ # that 'has_one' or 'has_many' can be used to define such associations. Again, the
87
+ # type of the attribute/association can be specified as a second argument.
88
+ #
89
+ module MetamodelBuilder
90
+
91
+ # Use this class as a start for new metamodel elements (i.e. Ruby classes)
92
+ # by inheriting for it.
93
+ #
94
+ # See MetamodelBuilder for an example.
95
+ class MMBase
96
+ include BuilderRuntime
97
+ extend BuilderExtensions
98
+ end
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,29 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ require 'rgen/name_helper'
5
+
6
+ module RGen
7
+
8
+ module MetamodelBuilder
9
+
10
+ class BuildHelper
11
+ include NameHelper
12
+
13
+ def self.build(mod,bnd,tpl)
14
+ mod.module_eval ERB.new(tpl).result(BuildHelper.new(bnd).bnd)
15
+ end
16
+ def initialize(bnd)
17
+ @outer_binding = bnd
18
+ end
19
+ def bnd
20
+ binding
21
+ end
22
+ def method_missing(m)
23
+ eval("#{m}",@outer_binding)
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,191 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ require 'erb'
5
+ require 'rgen/metamodel_builder/build_helper'
6
+
7
+ module RGen
8
+
9
+ module MetamodelBuilder
10
+
11
+ # This module provides methods which can be used to setup a metamodel element.
12
+ # The module is used to +extend+ MetamodelBuilder::MMBase, i.e. add the module's
13
+ # methods as class methods.
14
+ #
15
+ # MetamodelBuilder::MMBase should be used as a start for new metamodel elements.
16
+ # See MetamodelBuilder for an example.
17
+ #
18
+ module BuilderExtensions
19
+
20
+ # Add a single attribute or unidirectional association.
21
+ # 'role' specifies the name which is used to access the attribute.
22
+ # 'target_class' is optional and can be used to fix the type of objects which
23
+ # can be held by this attribute.
24
+ #
25
+ # This class method adds the following instance methods, where 'role' is to be
26
+ # replaced by the given role name:
27
+ # class#role # getter
28
+ # class#role=(value) # setter
29
+ #
30
+ def has_one(role, target_class=nil)
31
+ has_one_internal(role, target_class)
32
+ end
33
+
34
+ # Add an unidirectional _many_ association.
35
+ # 'role' specifies the name which is used to access the attribute.
36
+ # 'target_class' is optional and can be used to fix the type of objects which
37
+ # can be referenced by this association.
38
+ #
39
+ # This class method adds the following instance methods, where 'role' is to be
40
+ # replaced by the given role name:
41
+ # class#addRole(value)
42
+ # class#removeRole(value)
43
+ # class#role # getter, returns an array
44
+ # Note that the first letter of the role name is turned into an uppercase
45
+ # for the add and remove methods.
46
+ #
47
+ def has_many(role, target_class=nil)
48
+ has_many_internal(role, target_class)
49
+ end
50
+
51
+ # Add a bidirectional one-to-many association between two classes.
52
+ # The class this method is called on is refered to as _own_class_ in
53
+ # the following.
54
+ #
55
+ # Instances of own_class can use 'own_role' to access _many_ associated instances
56
+ # of type 'target_class'. Instances of 'target_class' can use 'target_role' to
57
+ # access _one_ associated instance of own_class.
58
+ #
59
+ # This class method adds the following instance methods where 'ownRole' and
60
+ # 'targetRole' are to be replaced by the given role names:
61
+ # own_class#addOwnRole(value)
62
+ # own_class#removeOwnRole(value)
63
+ # own_class#ownRole
64
+ # target_class#targetRole
65
+ # target_class#targetRole=(value)
66
+ # Note that the first letter of the role name is turned into an uppercase
67
+ # for the add and remove methods.
68
+ #
69
+ # When an element is added/set on either side, this element also receives the element
70
+ # is is added to as a new element.
71
+ #
72
+ def one_to_many(own_role, target_class, target_role)
73
+ has_many_internal(own_role,target_class,target_role,:one)
74
+ target_class.has_one_internal(target_role,self,own_role,:many)
75
+ end
76
+
77
+ # Add a bidirectional many-to-many association between two classes.
78
+ # The class this method is called on is refered to as _own_class_ in
79
+ # the following.
80
+ #
81
+ # Instances of own_class can use 'own_role' to access _many_ associated instances
82
+ # of type 'target_class'. Instances of 'target_class' can use 'target_role' to
83
+ # access _many_ associated instances of own_class.
84
+ #
85
+ # This class method adds the following instance methods where 'ownRole' and
86
+ # 'targetRole' are to be replaced by the given role names:
87
+ # own_class#addOwnRole(value)
88
+ # own_class#removeOwnRole(value)
89
+ # own_class#ownRole
90
+ # target_class#addTargetRole
91
+ # target_class#removeTargetRole=(value)
92
+ # target_class#targetRole
93
+ # Note that the first letter of the role name is turned into an uppercase
94
+ # for the add and remove methods.
95
+ #
96
+ # When an element is added on either side, this element also receives the element
97
+ # is is added to as a new element.
98
+ #
99
+ def many_to_many(own_role, target_class, target_role)
100
+ has_many_internal(own_role,target_class,target_role,:many)
101
+ target_class.has_many_internal(target_role,self,own_role,:many)
102
+ end
103
+
104
+ # Add a bidirectional one-to-one association between two classes.
105
+ # The class this method is called on is refered to as _own_class_ in
106
+ # the following.
107
+ #
108
+ # Instances of own_class can use 'own_role' to access _one_ associated instance
109
+ # of type 'target_class'. Instances of 'target_class' can use 'target_role' to
110
+ # access _one_ associated instance of own_class.
111
+ #
112
+ # This class method adds the following instance methods where 'ownRole' and
113
+ # 'targetRole' are to be replaced by the given role names:
114
+ # own_class#ownRole
115
+ # own_class#ownRole=(value)
116
+ # target_class#targetRole
117
+ # target_class#targetRole=(value)
118
+ #
119
+ # When an element is set on either side, this element also receives the element
120
+ # is is added to as the new element.
121
+ #
122
+ def one_to_one(own_role, target_class, target_role)
123
+ has_one_internal(own_role,target_class,target_role,:one)
124
+ target_class.has_one_internal(target_role,self,own_role,:one)
125
+ end
126
+
127
+ protected
128
+
129
+ def has_one_internal(name, cls=nil, role=nil, kind=nil)
130
+ BuildHelper.build self, binding, <<-CODE
131
+ @@one_assocs ||= []
132
+ @@one_assocs << name
133
+ def #{name}=(val)
134
+ return if val == @#{name}
135
+ <% if cls %>
136
+ raise _assignmentTypeError(self,val,#{cls}) unless val.nil? or val.is_a? #{cls}
137
+ <% end %>
138
+ oldval = @#{name}
139
+ @#{name} = val
140
+ _unregister(self,oldval,"#{role}","#{kind}")
141
+ _register(self,val,"#{role}","#{kind}")
142
+ end
143
+ def #{name}
144
+ @#{name}
145
+ end
146
+ alias get<%= firstToUpper(name) %> #{name}
147
+ alias set<%= firstToUpper(name) %> #{name}=
148
+ CODE
149
+ end
150
+
151
+ def has_many_internal(name, cls=nil, role=nil, kind = nil)
152
+ BuildHelper.build self, binding, <<-CODE
153
+ @@many_assocs ||= []
154
+ @@many_assocs << name
155
+ def add<%= firstToUpper(name) %>(val)
156
+ @#{name} = [] unless @#{name}
157
+ return if val.nil? or @#{name}.include?(val)
158
+ <% if cls %>
159
+ raise _assignmentTypeError(self,val,#{cls}) unless val.nil? or val.is_a? #{cls}
160
+ <% end %>
161
+ @#{name}.push val
162
+ _register(self, val, "#{role}", "#{kind}")
163
+ end
164
+ def remove<%= firstToUpper(name) %>(val)
165
+ @#{name} = [] unless @#{name}
166
+ return unless @#{name}.include?(val)
167
+ @#{name}.delete val
168
+ _unregister(self, val, "#{role}", "#{kind}")
169
+ end
170
+ def #{name}
171
+ ( @#{name} ? @#{name}.dup : [] )
172
+ end
173
+ def #{name}=(val)
174
+ return if val.nil?
175
+ raise _assignmentTypeError(self, val, Array) unless val.is_a? Array
176
+ #{name}.each {|e|
177
+ remove<%= firstToUpper(name) %>(e)
178
+ }
179
+ val.each {|v|
180
+ add<%= firstToUpper(name) %>(v)
181
+ }
182
+ end
183
+ alias get<%= firstToUpper(name) %> #{name}
184
+ alias set<%= firstToUpper(name) %> #{name}=
185
+ CODE
186
+ end
187
+
188
+ end
189
+ end
190
+
191
+ end