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,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