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,67 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ require 'rgen/name_helper'
5
+
6
+ module RGen
7
+
8
+ module MetamodelBuilder
9
+
10
+ # This module is mixed into MetamodelBuilder::MMBase.
11
+ # The methods provided by this module are used by the methods generated
12
+ # by the class methods of MetamodelBuilder::BuilderExtensions
13
+ module BuilderRuntime
14
+ include NameHelper
15
+
16
+ def addGeneric(role, value)
17
+ send("add#{firstToUpper(role)}",value)
18
+ end
19
+
20
+ def removeGeneric(role, value)
21
+ send("remove#{firstToUpper(role)}",value)
22
+ end
23
+
24
+ def setGeneric(role, value)
25
+ send("#{role}=",value)
26
+ end
27
+
28
+ def getGeneric(role)
29
+ send("#{role}")
30
+ end
31
+
32
+ def _unregister(element, target, target_role, kind)
33
+ return unless element and target and target_role
34
+ if kind == 'one'
35
+ target.send("#{target_role}=",nil)
36
+ elsif kind == 'many'
37
+ target.send("remove#{firstToUpper(target_role)}",element)
38
+ end
39
+ end
40
+
41
+ def _register(element, target, target_role, kind)
42
+ return unless element and target and target_role
43
+ if kind == 'one'
44
+ target.send("#{target_role}=",element)
45
+ elsif kind == 'many'
46
+ target.send("add#{firstToUpper(target_role)}",element)
47
+ end
48
+ end
49
+
50
+ def _assignmentTypeError(target, value, expected)
51
+ text = ""
52
+ if target
53
+ targetId = target.class.name
54
+ targetId += "(" + target.name + ")" if target.respond_to?(:name) and target.name
55
+ text += "In #{targetId} : "
56
+ end
57
+ valueId = value.class.name
58
+ valueId += "(" + value.name + ")" if value.respond_to?(:name) and value.name
59
+ text += "Can not put a #{valueId} where a #{expected} is expected"
60
+ StandardError.new(text)
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,18 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ module RGen
5
+
6
+ module NameHelper
7
+ def normalizeName(name)
8
+ name.gsub(/[\.:]/,'_')
9
+ end
10
+ def className(object)
11
+ object.class.name =~ /::(\w+)$/; $1
12
+ end
13
+ def firstToUpper(str)
14
+ str[0..0].upcase + ( str[1..-1] || "" )
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,169 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ require 'rgen/template_language/directory_template_container'
5
+ require 'rgen/template_language/template_container'
6
+
7
+ module RGen
8
+
9
+ # The RGen template language has been designed to build complex generators.
10
+ # It is very similar to the EXPAND language of the Java based
11
+ # OpenArchitectureWare framework.
12
+ #
13
+ # =Templates
14
+ #
15
+ # The basic idea is to allow "templates" not only being template files
16
+ # but smaller parts. Those parts can be expanded from other parts very
17
+ # much like Ruby methods are called from other methods.
18
+ # Thus the term "template" refers to such a part within a "template file".
19
+ #
20
+ # Template files used by the RGen template language should have a
21
+ # filename with the postfix ".tpl". Those files can reside within (nested)
22
+ # template file directories.
23
+ #
24
+ # As an example a template directory could look like the following:
25
+ #
26
+ # templates/root.tpl
27
+ # templates/dbaccess/dbaccess.tpl
28
+ # templates/dbaccess/schema.tpl
29
+ # templates/headers/generic_headers.tpl
30
+ # templates/headers/specific/component.tpl
31
+ #
32
+ # A template is always called for a <i>context object</i>. The context object
33
+ # serves as the receiver of methods called within the template. Details are given
34
+ # below.
35
+ #
36
+ #
37
+ # =Defining Templates
38
+ #
39
+ # One or more templates can be defined in a template file using the +define+
40
+ # keyword as in the following example:
41
+ #
42
+ # <% define 'GenerateDBAdapter', :for => DBDescription do |dbtype| %>
43
+ # Content to be generated; use ERB syntax here
44
+ # <% end %>
45
+ #
46
+ # The template definition takes three kinds of parameters:
47
+ # 1. The name of the template within the template file as a String or Symbol
48
+ # 2. An optional class object describing the class of context objects for which
49
+ # this template is valid.
50
+ # 3. An arbitrary number of template parameters
51
+ # See RGen::TemplateLanguage::TemplateContainer for details about the syntax of +define+.
52
+ #
53
+ # Within a template, regular ERB syntax can be used. This is
54
+ # * <code><%</code> and <code>%></code> are used to embed Ruby code
55
+ # * <code><%=</code> and <code>%></code> are used to embed Ruby expressions with
56
+ # the expression result being written to the template output
57
+ # * <code><%#</code> and <code>%></code> are used for comments
58
+ # All content not within these tags is written to the template output verbatim.
59
+ # See below for details about output files and output formatting.
60
+ #
61
+ # All methods which are called from within the template are sent to the context
62
+ # object.
63
+ #
64
+ #
65
+ # =Expanding Templates
66
+ #
67
+ # Templates are normally expanded from within other templates. The only
68
+ # exception is the root template, which is expanded from the surrounding code.
69
+ #
70
+ # Template names can be specified in the following ways:
71
+ # * Non qualified name: use the template with the given name in the current template file
72
+ # * Relative qualified name: use the template within the template file specified by the relative path
73
+ # * Absolute qualified name: use the template within the template file specified by the absolute path
74
+ #
75
+ # The +expand+ keyword is used to expand templates.
76
+ #
77
+ # Here are some examples:
78
+ #
79
+ # <% expand 'GenerateDBAdapter', dbtype, :for => dbDesc %>
80
+ #
81
+ # <i>Non qualified</i>. Must be called within the file where 'GenerateDBAdapter' is defined.
82
+ # There is one template parameter passed in via variable +dbtype+.
83
+ # The context object is provided in variable +dbDesc+.
84
+ #
85
+ # <% expand 'dbaccess::ExampleSQL' %>
86
+ #
87
+ # <i>Qualified with filename</i>. Must be called from a file in the same directory as 'dbaccess.tpl'
88
+ # There are no parameters. The current context object will be used as the context
89
+ # object for this template expansion.
90
+ #
91
+ # <% expand '../headers/generic_headers::CHeader', :foreach => modules %>
92
+ #
93
+ # <i>Relatively qualified</i>. Must be called from a location from which the file
94
+ # 'generic_headers.tpl' is accessible via the relative path '../headers'.
95
+ # The template is expanded for each module in +modules+ (which has to be an Array).
96
+ # Each element of +modules+ will be the context object in turn.
97
+ #
98
+ # <% expand '/headers/generic_headers::CHeader', :foreach => modules %>
99
+ #
100
+ # Absolutely qualified: The same behaviour as before but with an absolute path from
101
+ # the template directory root (which in this example is 'templates', see above)
102
+ #
103
+ #
104
+ # =Output Files and Formatting
105
+ #
106
+ # Normally the generated content is to be written into one or more output files.
107
+ # The RGen template language facilitates this by means of the +file+ keyword.
108
+ #
109
+ # When the +file+ keyword is used to define a block, all output generated
110
+ # from template code within this block will be written to the specified file.
111
+ # This includes output generated from template expansions.
112
+ # Thus all output from templates expanded within this block is written to
113
+ # the same file as long as those templates do not use the +file+ keyword to
114
+ # define a new file context.
115
+ #
116
+ # Here is an example:
117
+ #
118
+ # <% file 'dbadapter/'+adapter.name+'.c' do %>
119
+ # all content within this block will be written to the specified file
120
+ # <% end %>
121
+ #
122
+ # Note that the filename itself can be calculated dynamically by an arbitrary
123
+ # Ruby expression.
124
+ #
125
+ # The absolute position where the output file is created depends on the output
126
+ # root directory passed to DirectoryTemplateContainer as described below.
127
+ #
128
+ # =Setting up the Generator
129
+ #
130
+ # Setting up the generator consists of 3 steps:
131
+ # * Instantiate DirectoryTemplateContainer passing the metamodel and the output
132
+ # directory to the constructor.
133
+ # * Load the templates into the template container
134
+ # * Expand the root template to start generation
135
+ #
136
+ # Here is an example:
137
+ #
138
+ # module MyMM
139
+ # # metaclasses are defined here, e.g. using RGen::MetamodelBuilder
140
+ # end
141
+ #
142
+ # OUTPUT_DIR = File.dirname(__FILE__)+"/output"
143
+ # TEMPLATES_DIR = File.dirname(__FILE__)+"/templates"
144
+ #
145
+ # tc = RGen::TemplateLanguage::DirectoryTemplateContainer.new(MyMM, OUTPUT_DIR)
146
+ # tc.load(TEMPLATES_DIR)
147
+ # # testModel should hold an instance of the metamodel class expected by the root template
148
+ # # the following line starts generation
149
+ # tc.expand('root::Root', :for => testModel, :indent => 1)
150
+ #
151
+ # The metamodel is the Ruby module which contains the metaclasses.
152
+ # This information is required for the template container in order to resolve the
153
+ # metamodel classes used within the template file.
154
+ #
155
+ # The output path is prepended to the relative paths provided to the +file+
156
+ # definitions in the template files.
157
+ #
158
+ # The template directory should contain template files as described above.
159
+ #
160
+ # Finally the generation process is started by calling +expand+ in the same way as it
161
+ # is used from within templates.
162
+ #
163
+ # Also see the unit tests for more examples.
164
+ #
165
+ module TemplateLanguage
166
+
167
+ end
168
+
169
+ end
@@ -0,0 +1,51 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ require 'rgen/template_language/template_container'
5
+ require 'rgen/template_language/template_helper'
6
+
7
+ module RGen
8
+
9
+ module TemplateLanguage
10
+
11
+ class DirectoryTemplateContainer
12
+ include TemplateHelper
13
+
14
+ def initialize(metamodel=nil, output_path=nil, parent=nil)
15
+ @containers = {}
16
+ @parent = parent
17
+ @metamodel = metamodel
18
+ @output_path = output_path
19
+ end
20
+
21
+ def load(dir)
22
+ #print "Loading templates in #{dir} ...\n"
23
+ Dir.foreach(dir) { |f|
24
+ qf = dir+"/"+f
25
+ if !File.directory?(qf) && f =~ /^(.*)\.tpl$/
26
+ (@containers[$1] = TemplateContainer.dup.new(@metamodel, @output_path, self)).load(qf)
27
+ elsif File.directory?(qf) && f != "." && f != ".."
28
+ (@containers[f] = DirectoryTemplateContainer.new(@metamodel, @output_path, self)).load(qf)
29
+ end
30
+ }
31
+ end
32
+
33
+ def expand(template, *all_args)
34
+ args, params = _splitArgsAndOptions(all_args)
35
+ element = params[:for]
36
+ if template =~ /^\// && @parent
37
+ @parent.expand(template, *all_args)
38
+ elsif template =~ /^[\/]*(\w+)[:\/]+(.*)/
39
+ throw "Template not found: #{$1}" unless @containers[$1]
40
+ @containers[$1].expand($2, *all_args)
41
+ elsif @parent
42
+ @parent.expand(template, *all_args)
43
+ else
44
+ throw "Template not found: #{template}"
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,84 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ module RGen
5
+
6
+ module TemplateLanguage
7
+
8
+ class OutputHandler
9
+ attr_writer :indent
10
+
11
+ def initialize(indent=0, mode=:explicit)
12
+ self.mode = mode
13
+ @indent = indent
14
+ @state = :wait_for_nonws
15
+ @output = ""
16
+ end
17
+
18
+ def concat(s)
19
+ return @output.concat(s) if s.is_a? OutputHandler
20
+ s = s.to_str.gsub(/^[\t ]*\r?\n/,'') if @ignoreNextNL
21
+ s = s.to_str.gsub(/^\s+/,'') if @ignoreNextWS
22
+ @ignoreNextNL = @ignoreNextWS = false if s =~ /\S/
23
+ if @mode == :direct
24
+ @output.concat(s)
25
+ elsif @mode == :explicit
26
+ while s.size > 0
27
+ #puts "DEGUB: #{@state} #{s.dump}"
28
+ # s starts with whitespace
29
+ if s =~ /\A(\s+)(.*)/m
30
+ ws = $1; rest = $2
31
+ #puts "DEGUB: ws #{ws.dump} rest #{rest.dump}"
32
+ if @state == :wait_for_nl
33
+ # ws contains a newline
34
+ if ws =~ /\A[\t ]*(\r?\n)(\s*)/m
35
+ @output.concat($1)
36
+ @state = :wait_for_nonws
37
+ s = $2 + rest
38
+ else
39
+ @output.concat(ws)
40
+ s = rest
41
+ end
42
+ else
43
+ s = rest
44
+ end
45
+ # s starts with non-whitespace
46
+ elsif s =~ /\A(\S+)(.*)/m
47
+ nonws = $1; rest = $2
48
+ #puts "DEGUB: nonws #{nonws.dump} rest #{rest.dump}"
49
+ @output.concat(" "*@indent) if @state == :wait_for_nonws
50
+ @output.concat(nonws)
51
+ @state = :wait_for_nl
52
+ s = rest
53
+ end
54
+ end
55
+ end
56
+ end
57
+ alias << concat
58
+
59
+ def to_str
60
+ @output
61
+ end
62
+ alias to_s to_str
63
+
64
+ def direct_concat(s)
65
+ @output.concat(s)
66
+ end
67
+
68
+ def ignoreNextNL
69
+ @ignoreNextNL = true
70
+ end
71
+
72
+ def ignoreNextWS
73
+ @ignoreNextWS = true
74
+ end
75
+
76
+ def mode=(m)
77
+ raise StandardError.new("Unknown mode: #{m}") unless [:direct, :explicit].include?(m)
78
+ @mode = m
79
+ end
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,153 @@
1
+ # RGen Framework
2
+ # (c) Martin Thiede, 2006
3
+
4
+ require 'erb'
5
+ require 'rgen/template_language/output_handler'
6
+ require 'rgen/template_language/template_helper'
7
+
8
+ module RGen
9
+
10
+ module TemplateLanguage
11
+
12
+ class TemplateContainer
13
+ include TemplateHelper
14
+
15
+ def initialize(metamodel, output_path, parent)
16
+ @templates = {}
17
+ @parent = parent
18
+ @indent = 0
19
+ @output_path = output_path
20
+ raise StandardError.new("Can not set metamodel, dup class first") if self.class == TemplateContainer
21
+ @@metamodel = metamodel
22
+ end
23
+
24
+ def load(filename)
25
+ #print "Loading templates in #{filename} ...\n"
26
+ File.open(filename) { |f|
27
+ ERB.new(f.read,nil,nil,'@output').result(binding)
28
+ }
29
+ end
30
+
31
+ # if this container can handle the call, the expansion result is returned
32
+ # otherwise expand is called on the appropriate container and the result is added to @output
33
+ def expand(template, *all_args)
34
+ args, params = _splitArgsAndOptions(all_args)
35
+ if params[:foreach].is_a? Enumerable
36
+ _expand_foreach(template, args, params)
37
+ else
38
+ _expand(template, args, params)
39
+ end
40
+ end
41
+
42
+ def this
43
+ @context
44
+ end
45
+
46
+ def method_missing(name, *args)
47
+ @context.send(name, *args)
48
+ end
49
+
50
+ def self.const_missing(name)
51
+ super unless @@metamodel
52
+ @@metamodel.const_get(name)
53
+ end
54
+
55
+ private
56
+
57
+ def nonl
58
+ @output.ignoreNextNL
59
+ end
60
+
61
+ def nows
62
+ @output.ignoreNextWS
63
+ end
64
+
65
+ def nl
66
+ _direct_concat("\n")
67
+ end
68
+
69
+ def iinc
70
+ @indent += 1
71
+ @output.indent = @indent
72
+ end
73
+
74
+ def idec
75
+ @indent -= 1 if @indent > 0
76
+ @output.indent = @indent
77
+ end
78
+
79
+ def define(template, params={}, &block)
80
+ @templates[template] ||= {}
81
+ cls = params[:for] || Object
82
+ @templates[template][cls] = block
83
+ end
84
+
85
+ def file(name)
86
+ old_output, @output = @output, OutputHandler.new(@indent)
87
+ yield
88
+ path = ""
89
+ path += @output_path+"/" if @output_path
90
+ File.open(path+name,"w") { |f| f.write(@output) }
91
+ @output = old_output
92
+ end
93
+
94
+ # private private
95
+
96
+ def _expand_foreach(template, args, params)
97
+ params[:foreach].each {|e|
98
+ single_params = params.dup
99
+ single_params[:for] = e
100
+ _expand(template, args, single_params)
101
+ }
102
+ end
103
+
104
+ LOCAL_TEMPLATE_REGEX = /^:*(\w+)$/
105
+
106
+ def _expand(template, args, params)
107
+ context = params[:for]
108
+ @indent = params[:indent] || @indent
109
+ old_context, @context = @context, context if context
110
+ local_output = nil
111
+ if template =~ LOCAL_TEMPLATE_REGEX
112
+ throw "Template not found: #{$1}" unless @templates[$1]
113
+ old_output, @output = @output, OutputHandler.new(@indent)
114
+ _call_template($1, @context, args)
115
+ local_output, @output = @output, old_output
116
+ else
117
+ local_output = @parent.expand(template, *(args.dup << {:for => @context, :indent => @indent}))
118
+ end
119
+ _direct_concat(local_output)
120
+ @context = old_context if old_context
121
+ local_output
122
+ end
123
+
124
+ def _call_template(tpl, context, args)
125
+ found = false
126
+ @templates[tpl].each_pair { |key, value|
127
+ if context.is_a?(key)
128
+ proc = @templates[tpl][key]
129
+ arity = proc.arity
130
+ arity = 0 if arity == -1 # if no args are given
131
+ raise StandardError.new("Wrong number of arguments calling template #{tpl}: #{args.size} for #{arity} "+
132
+ "(Beware: Hashes as last arguments are taken as options and are ignored)") \
133
+ if arity != args.size
134
+ proc.call(*args)
135
+ found = true
136
+ end
137
+ }
138
+ raise StandardError.new("Template class not matching: #{tpl} for #{context.class.name}") unless found
139
+ end
140
+
141
+ def _direct_concat(s)
142
+ if @output.is_a? OutputHandler
143
+ @output.direct_concat(s)
144
+ else
145
+ @output << s
146
+ end
147
+ end
148
+
149
+ end
150
+
151
+ end
152
+
153
+ end