rgen 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +9 -0
- data/MIT-LICENSE +20 -0
- data/README +73 -0
- data/lib/ea/xmi_class_instantiator.rb +45 -0
- data/lib/ea/xmi_helper.rb +26 -0
- data/lib/ea/xmi_metamodel.rb +19 -0
- data/lib/ea/xmi_object_instantiator.rb +42 -0
- data/lib/ea/xmi_to_classmodel.rb +78 -0
- data/lib/ea/xmi_to_objectmodel.rb +89 -0
- data/lib/mmgen/metamodel_generator.rb +19 -0
- data/lib/mmgen/mm_ext/uml_classmodel_ext.rb +71 -0
- data/lib/mmgen/mmgen.rb +21 -0
- data/lib/mmgen/templates/uml_classmodel.tpl +63 -0
- data/lib/rgen/array_extensions.rb +23 -0
- data/lib/rgen/auto_class_creator.rb +56 -0
- data/lib/rgen/environment.rb +57 -0
- data/lib/rgen/metamodel_builder.rb +102 -0
- data/lib/rgen/metamodel_builder/build_helper.rb +29 -0
- data/lib/rgen/metamodel_builder/builder_extensions.rb +191 -0
- data/lib/rgen/metamodel_builder/builder_runtime.rb +67 -0
- data/lib/rgen/name_helper.rb +18 -0
- data/lib/rgen/template_language.rb +169 -0
- data/lib/rgen/template_language/directory_template_container.rb +51 -0
- data/lib/rgen/template_language/output_handler.rb +84 -0
- data/lib/rgen/template_language/template_container.rb +153 -0
- data/lib/rgen/template_language/template_helper.rb +26 -0
- data/lib/rgen/transformer.rb +316 -0
- data/lib/rgen/xml_instantiator/dependency_resolver.rb +23 -0
- data/lib/rgen/xml_instantiator/xml_instantiator.rb +78 -0
- data/lib/rgen/xml_instantiator/xml_parser.rb +39 -0
- data/lib/uml/objectmodel_instantiator.rb +53 -0
- data/lib/uml/uml_classmodel.rb +92 -0
- data/lib/uml/uml_objectmodel.rb +65 -0
- data/test/array_extensions_test.rb +54 -0
- data/test/environment_test.rb +47 -0
- data/test/metamodel_builder_test.rb +175 -0
- data/test/metamodel_generator_test.rb +45 -0
- data/test/metamodel_generator_test/TestModel.rb +40 -0
- data/test/metamodel_generator_test/expected_result.txt +40 -0
- data/test/output_handler_test.rb +40 -0
- data/test/rgen_test.rb +13 -0
- data/test/template_language_test.rb +46 -0
- data/test/template_language_test/expected_result.txt +10 -0
- data/test/template_language_test/templates/content/chapter.tpl +5 -0
- data/test/template_language_test/templates/index/c/cmod.tpl +1 -0
- data/test/template_language_test/templates/index/chapter.tpl +3 -0
- data/test/template_language_test/templates/root.tpl +22 -0
- data/test/template_language_test/testout.txt +10 -0
- data/test/transformer_test.rb +176 -0
- data/test/xmi_class_instantiator_test.rb +107 -0
- data/test/xmi_instantiator_test/testmodel.eap +0 -0
- data/test/xmi_instantiator_test/testmodel.xml +962 -0
- data/test/xmi_object_instantiator_test.rb +65 -0
- metadata +117 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
module RGen
|
5
|
+
|
6
|
+
module TemplateLanguage
|
7
|
+
|
8
|
+
module TemplateHelper
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def _splitArgsAndOptions(all)
|
13
|
+
if all[-1] and all[-1].is_a? Hash
|
14
|
+
args = all[0..-2] || []
|
15
|
+
options = all[-1]
|
16
|
+
else
|
17
|
+
args = all
|
18
|
+
options = {}
|
19
|
+
end
|
20
|
+
return args, options
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
module RGen
|
2
|
+
|
3
|
+
# The Transformer class can be used to specify model transformations.
|
4
|
+
#
|
5
|
+
# Model transformations take place between a <i>source model</i> (located in the <i>source
|
6
|
+
# environment</i> being an instance of the <i>source metamodel</i>) and a <i>target model</i> (located
|
7
|
+
# in the <i>target environment</i> being an instance of the <i>target metamodel</i>).
|
8
|
+
# Normally a "model" consists of several model elements associated with each other.
|
9
|
+
#
|
10
|
+
# =Transformation Rules
|
11
|
+
#
|
12
|
+
# The transformation is specified within a subclass of Transformer.
|
13
|
+
# Within the subclass, the Transformer.transform class method can be used to specify transformation
|
14
|
+
# blocks for specific metamodel classes of the source metamodel.
|
15
|
+
#
|
16
|
+
# Here is an example:
|
17
|
+
#
|
18
|
+
# class MyTransformer < RGen::Transformer
|
19
|
+
#
|
20
|
+
# transform InputClass, :to => OutputClass do
|
21
|
+
# { :name => name, :otherClass => trans(otherClass) }
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# transform OtherInputClass, :to => OtherOutputClass do
|
25
|
+
# { :name => name }
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# In this example a transformation rule is specified for model elements of class InputClass
|
30
|
+
# as well as for elements of class OtherInputClass. The former is to be transformed into
|
31
|
+
# an instance of OutputClass, the latter into an instance of OtherOutputClass.
|
32
|
+
# Note that the Ruby class objects are used to specifiy the classes.
|
33
|
+
#
|
34
|
+
# =Transforming Attributes
|
35
|
+
#
|
36
|
+
# Besides the target class of a transformation, the attributes of the result object are
|
37
|
+
# specified in the above example. This is done by providing a Ruby block with the call of
|
38
|
+
# +transform+. Within this block arbitrary Ruby code may be placed, however the block
|
39
|
+
# must return a hash. This hash object specifies the attribute assignment of the
|
40
|
+
# result object using key/value pairs: The key must be a Symbol specifying the attribute
|
41
|
+
# which is to be assigned by name, the value is the value that will be assigned.
|
42
|
+
#
|
43
|
+
# For convenience, the transformation block will be evaluated in the context of the
|
44
|
+
# source model element which is currently being converted. This way it is possible to just
|
45
|
+
# write <code>:name => name</code> in the example in order to assign the name of the source
|
46
|
+
# object to the name attribute of the target object.
|
47
|
+
#
|
48
|
+
# =Transforming References
|
49
|
+
#
|
50
|
+
# When attributes of elements are references to other elements, those referenced
|
51
|
+
# elements have to be transformed as well. As shown above, this can be done by calling
|
52
|
+
# the Transformer#trans method. This method initiates a transformation of the element
|
53
|
+
# or array of elements passed as parameter according to transformation rules specified
|
54
|
+
# using +transform+. In fact the +trans+ method is the only way to start the transformation
|
55
|
+
# at all.
|
56
|
+
#
|
57
|
+
# For convenience and performance reasons, the result of +trans+ is cached with respect
|
58
|
+
# to the parameter object. I.e. calling trans on the same source object a second time will
|
59
|
+
# return the same result object _without_ a second evaluation of the corresponding
|
60
|
+
# transformation rules.
|
61
|
+
#
|
62
|
+
# This way the +trans+ method can be used to lookup the target element for some source
|
63
|
+
# element without the need to locally store a reference to the target element. In addition
|
64
|
+
# this can be useful if it is not clear if certain element has already been transformed
|
65
|
+
# when it is required within some other transformation block. See example below.
|
66
|
+
#
|
67
|
+
# Special care has been taken to allow the transformation of elements which reference
|
68
|
+
# each other cyclically. The key issue here is that the target element of some transformation
|
69
|
+
# is created _before_ the transformation's block is evaluated, i.e before the elements
|
70
|
+
# attributes are set. Otherwise a call to +trans+ within the transformation's block
|
71
|
+
# could lead to a +trans+ of the element itself.
|
72
|
+
#
|
73
|
+
# Here is an example:
|
74
|
+
#
|
75
|
+
# transform ModelAIn, :to => ModelAOut do
|
76
|
+
# { :name => name, :modelB => trans(modelB) }
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# transform ModelBIn, :to => ModelBOut do
|
80
|
+
# { :name => name, :modelA => trans(modelA) }
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# Note that in this case it does not matter if the transformation is initiated by calling
|
84
|
+
# +trans+ with a ModelAIn element or ModelBIn element due to the caching feature described
|
85
|
+
# above.
|
86
|
+
#
|
87
|
+
# =Transformer Methods
|
88
|
+
#
|
89
|
+
# When code in transformer blocks becomes more complex it might be useful to refactor
|
90
|
+
# it into smaller methods. If regular Ruby methods within the Transformer subclass are
|
91
|
+
# used for this purpose, it is necessary to know the source element being transformed.
|
92
|
+
# This could be achieved by explicitly passing the +@current_object+ as parameter of the
|
93
|
+
# method (see Transformer#trans).
|
94
|
+
#
|
95
|
+
# A more convenient way however is to define a special kind of method using the
|
96
|
+
# Transformer.method class method. Those methods are evaluated within the context of the
|
97
|
+
# current source element being transformed just the same as transformer blocks are.
|
98
|
+
#
|
99
|
+
# Here is an example:
|
100
|
+
#
|
101
|
+
# transform ModelIn, :to => ModelOut do
|
102
|
+
# { :number => doubleNumber }
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# method :doubleNumber do
|
106
|
+
# number * 2;
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# In this example the transformation assigns the 'number' attribute of the source element
|
110
|
+
# multiplied by 2 to the target element. The multiplication is done in a dedicated method
|
111
|
+
# called 'doubleNumber'. Note that the 'number' attribute of the source element is
|
112
|
+
# accessed without an explicit reference to the source element as the method's body
|
113
|
+
# evaluates in the source element's context.
|
114
|
+
#
|
115
|
+
# =Conditional Transformations
|
116
|
+
#
|
117
|
+
# Using the transformations as described above, all elements of the same class are
|
118
|
+
# transformed the same way. Conditional transformations allow to transform elements of
|
119
|
+
# the same class into elements of different target classes as well as applying different
|
120
|
+
# transformations on the attributes.
|
121
|
+
#
|
122
|
+
# Conditional transformations are defined by specifying multiple transformer blocks for
|
123
|
+
# the same source class and providing a condition with each block. Since it is important
|
124
|
+
# to create the target object before evaluation of the transformation block (see above),
|
125
|
+
# the conditions must also be evaluated separately _before_ the transformer block.
|
126
|
+
#
|
127
|
+
# Conditions are specified using transformer methods as described above. If the return
|
128
|
+
# value is true, the corresponding block is used for transformation. If more than one
|
129
|
+
# conditions are true, only the first transformer block will be evaluated.
|
130
|
+
#
|
131
|
+
# Here is an example:
|
132
|
+
#
|
133
|
+
# transform ModelIn, :to => ModelOut, :if => :largeNumber do
|
134
|
+
# { :number => number * 2}
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# transform ModelIn, :to => ModelOut, :if => :smallNumber do
|
138
|
+
# { :number => number / 2 }
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# method :largeNumber do
|
142
|
+
# number > 1000
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# method :smallNumber do
|
146
|
+
# number < 500
|
147
|
+
# end
|
148
|
+
#
|
149
|
+
# In this case the transformation of an element of class ModelIn depends on the value
|
150
|
+
# of the element's 'number' attribute. If the value is greater than 1000, the first rule
|
151
|
+
# as taken and the number is doubled. If the value is smaller than 500, the second rule
|
152
|
+
# is taken and the number is divided by two.
|
153
|
+
#
|
154
|
+
# Note that it is up to the user to avoid cycles within the conditions. A cycle could
|
155
|
+
# occure if the condition are based on transformation target elements, i.e. if +trans+
|
156
|
+
# is used within the condition to lookup or transform other elements.
|
157
|
+
#
|
158
|
+
class Transformer
|
159
|
+
|
160
|
+
TransformationDescription = Struct.new(:block, :target) # :nodoc:
|
161
|
+
|
162
|
+
@@methods = {}
|
163
|
+
@@transformer_blocks = {}
|
164
|
+
|
165
|
+
def self._transformer_blocks # :nodoc:
|
166
|
+
@@transformer_blocks[self] ||= {}
|
167
|
+
end
|
168
|
+
|
169
|
+
def self._methods # :nodoc:
|
170
|
+
@@methods[self] ||= {}
|
171
|
+
end
|
172
|
+
|
173
|
+
# This class method is used to specify a transformation rule.
|
174
|
+
#
|
175
|
+
# The first argument specifies the class of elements for which this rule applies.
|
176
|
+
# The second argument must be a hash including the target class
|
177
|
+
# (as value of key ':to') and an optional condition (as value of key ':if').
|
178
|
+
#
|
179
|
+
# The target class is specified by passing the actual Ruby class object.
|
180
|
+
# The condition is either the name of a transformer method (see Transfomer.method) as
|
181
|
+
# a symbol or a proc object. In either case the block is evaluated at transformation
|
182
|
+
# time and its result value determines if the rule applies.
|
183
|
+
#
|
184
|
+
def self.transform(from, desc=nil, &block)
|
185
|
+
to = (desc && desc.is_a?(Hash) && desc[:to])
|
186
|
+
condition = (desc && desc.is_a?(Hash) && desc[:if])
|
187
|
+
raise StandardError.new("No transformation target specified.") unless to
|
188
|
+
block_desc = TransformationDescription.new(block, to)
|
189
|
+
if condition
|
190
|
+
_transformer_blocks[from] ||= {}
|
191
|
+
raise StandardError.new("Multiple (non-conditional) transformations for class #{from.name}.") unless _transformer_blocks[from].is_a?(Hash)
|
192
|
+
_transformer_blocks[from][condition] = block_desc
|
193
|
+
else
|
194
|
+
raise StandardError.new("Multiple (non-conditional) transformations for class #{from.name}.") unless _transformer_blocks[from].nil?
|
195
|
+
_transformer_blocks[from] = block_desc
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Define a transformer method for the current transformer class.
|
200
|
+
# In contrast to regular Ruby methods, a method defined this way executes in the
|
201
|
+
# context of the object currently being transformed.
|
202
|
+
#
|
203
|
+
def self.method(name, &block)
|
204
|
+
_methods[name.to_s] = block
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
# Creates a new transformer with the specified input and output Environment.
|
209
|
+
#
|
210
|
+
def initialize(env_in, env_out)
|
211
|
+
@env_in = env_in
|
212
|
+
@env_out = env_out
|
213
|
+
@transformer_results = {}
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
# Transforms a given model element according to the rules specified by means of
|
218
|
+
# the Transformer.transform class method.
|
219
|
+
#
|
220
|
+
# The transformation result element is created in the output environment and returned.
|
221
|
+
# In addition, the result is cached, i.e. a second invocation with the same parameter
|
222
|
+
# object will return the same result object without any further evaluation of the
|
223
|
+
# transformation rules. Nil will be transformed into nil.
|
224
|
+
#
|
225
|
+
# The transformation input can be given as:
|
226
|
+
# * a single object
|
227
|
+
# * an array each element of which is transformed in turn
|
228
|
+
# * a hash used as input to Environment#find with the result being transformed
|
229
|
+
#
|
230
|
+
def trans(obj)
|
231
|
+
return nil if obj.nil?
|
232
|
+
return @transformer_results[obj] if @transformer_results[obj]
|
233
|
+
obj = @env_in.find(obj) if obj.is_a?(Hash)
|
234
|
+
return obj.collect{|o| trans(o)}.compact if obj.is_a? Enumerable
|
235
|
+
raise StandardError.new("No transformer for class #{obj.class.name}") unless self.class._transformer_blocks[obj.class]
|
236
|
+
block_desc = _evaluateCondition(obj)
|
237
|
+
return nil unless block_desc
|
238
|
+
@transformer_results[obj] = _instantiateTargetClass(obj, block_desc.target)
|
239
|
+
old_object, @current_object = @current_object, obj
|
240
|
+
block_result = instance_eval(&block_desc.block)
|
241
|
+
raise StandardError.new("Transformer must return a hash") unless block_result.is_a? Hash
|
242
|
+
@current_object = old_object
|
243
|
+
_attributesFromHash(@transformer_results[obj], block_result)
|
244
|
+
end
|
245
|
+
|
246
|
+
# Each call which is not handled by the transformer object is passed to the object
|
247
|
+
# currently being transformed.
|
248
|
+
# If that object also does not respond to the call, it is treated as a transformer
|
249
|
+
# method call (see Transformer.method).
|
250
|
+
#
|
251
|
+
def method_missing(m) #:nodoc:
|
252
|
+
if @current_object.respond_to?(m)
|
253
|
+
@current_object.send(m)
|
254
|
+
else
|
255
|
+
_invokeMethod(m)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
private
|
260
|
+
|
261
|
+
# returns the first TransformationDescription for which condition is true :nodoc:
|
262
|
+
def _evaluateCondition(obj)
|
263
|
+
tb = self.class._transformer_blocks[obj.class]
|
264
|
+
block_description = nil
|
265
|
+
if tb.is_a?(TransformationDescription)
|
266
|
+
# non-conditional
|
267
|
+
block_description = tb
|
268
|
+
else
|
269
|
+
old_object, @current_object = @current_object, obj
|
270
|
+
tb.each_pair {|condition, block|
|
271
|
+
if condition.is_a?(Proc)
|
272
|
+
result = instance_eval(&condition)
|
273
|
+
elsif condition.is_a?(Symbol)
|
274
|
+
result = _invokeMethod(condition)
|
275
|
+
else
|
276
|
+
result = condition
|
277
|
+
end
|
278
|
+
if result
|
279
|
+
block_description = block
|
280
|
+
break
|
281
|
+
end
|
282
|
+
}
|
283
|
+
@current_object = old_object
|
284
|
+
end
|
285
|
+
block_description
|
286
|
+
end
|
287
|
+
|
288
|
+
def _instantiateTargetClass(obj, target_desc) # :nodoc:
|
289
|
+
old_object, @current_object = @current_object, obj
|
290
|
+
if target_desc.is_a?(Proc)
|
291
|
+
target_class = instance_eval(&target_desc)
|
292
|
+
elsif target_desc.is_a?(Symbol)
|
293
|
+
target_class = _invokeMethod(target_desc)
|
294
|
+
else
|
295
|
+
target_class = target_desc
|
296
|
+
end
|
297
|
+
@current_object = old_object
|
298
|
+
@env_out.new target_class
|
299
|
+
end
|
300
|
+
|
301
|
+
def _invokeMethod(m) # :nodoc:
|
302
|
+
raise StandardError.new("Method not found: #{m}") unless self.class._methods[m.to_s]
|
303
|
+
instance_eval(&self.class._methods[m.to_s])
|
304
|
+
end
|
305
|
+
|
306
|
+
def _attributesFromHash(obj, hash) # :nodoc:
|
307
|
+
hash.delete(:class)
|
308
|
+
hash.each_pair{|k,v|
|
309
|
+
obj.send("#{k}=", v)
|
310
|
+
}
|
311
|
+
obj
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'rgen/name_helper'
|
5
|
+
|
6
|
+
module RGen
|
7
|
+
|
8
|
+
module DependencyResolver
|
9
|
+
include NameHelper
|
10
|
+
|
11
|
+
def resolveById(idAttribute,refAttribute)
|
12
|
+
lookup = {}
|
13
|
+
@elements.each { |e|
|
14
|
+
lookup[e.send("get"+firstToUpper(idAttribute))] = e if e.respond_to?("get"+firstToUpper(idAttribute))
|
15
|
+
}
|
16
|
+
@elements.each { |e|
|
17
|
+
target = lookup[e.send("get"+firstToUpper(refAttribute))] if e.respond_to?("get"+firstToUpper(refAttribute))
|
18
|
+
assocObjectsOneToMany(target, className(e)+"_"+refAttribute, e, refAttribute+"_"+className(target)) if target
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'rgen/name_helper'
|
5
|
+
require 'rgen/xml_instantiator/xml_parser'
|
6
|
+
require 'rgen/xml_instantiator/dependency_resolver'
|
7
|
+
|
8
|
+
module RGen
|
9
|
+
|
10
|
+
class XMLInstantiator
|
11
|
+
include NameHelper
|
12
|
+
include DependencyResolver
|
13
|
+
|
14
|
+
def initialize(mod, createMM=false, &config)
|
15
|
+
@mod = mod
|
16
|
+
@createMM = createMM
|
17
|
+
@configBlock = config
|
18
|
+
end
|
19
|
+
|
20
|
+
def instantiate(env, str)
|
21
|
+
@elements = []
|
22
|
+
XMLParser.new(self).parse(str)
|
23
|
+
@configBlock.call(self) if @configBlock
|
24
|
+
@elements.each {|e| env << e}
|
25
|
+
end
|
26
|
+
|
27
|
+
def newObject(name)
|
28
|
+
# capitalize first letter only
|
29
|
+
className = firstToUpper(normalizeName(name))
|
30
|
+
begin
|
31
|
+
cls = @mod.const_get(className)
|
32
|
+
obj = cls.new
|
33
|
+
@elements << obj
|
34
|
+
return obj
|
35
|
+
rescue NameError
|
36
|
+
if @createMM
|
37
|
+
@mod.module_eval("class #{className} < RGen::MetamodelBuilder::MMBase; end")
|
38
|
+
retry
|
39
|
+
else
|
40
|
+
raise
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def setAttribute(obj, name, val)
|
46
|
+
m = normalizeName(name)
|
47
|
+
begin
|
48
|
+
obj.send("#{m}=", val)
|
49
|
+
rescue NoMethodError
|
50
|
+
if @createMM
|
51
|
+
obj.class.has_one(m)
|
52
|
+
retry
|
53
|
+
else
|
54
|
+
raise
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def assocObjectsParentToChild(parent, child)
|
60
|
+
assocObjectsOneToMany(parent,className(child),child,"_p_"+className(parent))
|
61
|
+
end
|
62
|
+
|
63
|
+
def assocObjectsOneToMany(objOne, roleOne, objMany, roleMany)
|
64
|
+
begin
|
65
|
+
objOne.addGeneric(roleOne, objMany)
|
66
|
+
rescue NoMethodError => nme
|
67
|
+
if @createMM
|
68
|
+
objOne.class.one_to_many(roleOne, objMany.class, roleMany)
|
69
|
+
retry
|
70
|
+
else
|
71
|
+
raise
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# RGen Framework
|
2
|
+
# (c) Martin Thiede, 2006
|
3
|
+
|
4
|
+
require 'rexml/parsers/sax2parser'
|
5
|
+
require 'rexml/sax2listener'
|
6
|
+
|
7
|
+
module RGen
|
8
|
+
|
9
|
+
class XMLParser
|
10
|
+
include REXML::SAX2Listener
|
11
|
+
include NameHelper
|
12
|
+
|
13
|
+
def initialize(instantiator)
|
14
|
+
@stack = []
|
15
|
+
@inst = instantiator
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse(src)
|
19
|
+
parser = REXML::Parsers::SAX2Parser.new(src)
|
20
|
+
parser.listen(self)
|
21
|
+
parser.parse
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_element(uri, localname, qname, attributes)
|
25
|
+
currentObject = @inst.newObject(qname)
|
26
|
+
@inst.assocObjectsParentToChild(@stack[-1],currentObject) if @stack.size > 0
|
27
|
+
attributes.each_pair { |a,v|
|
28
|
+
@inst.setAttribute(currentObject, a, v)
|
29
|
+
}
|
30
|
+
@stack.push currentObject
|
31
|
+
end
|
32
|
+
|
33
|
+
def end_element(uri, localname, qname)
|
34
|
+
@stack.pop
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|