rgen 0.2.0

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