rgen 0.4.6 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +95 -83
- data/Rakefile +4 -3
- data/lib/ea_support/ea_support.rb +54 -0
- data/lib/ea_support/id_store.rb +32 -0
- data/lib/ea_support/uml13_ea_metamodel.rb +562 -0
- data/lib/ea_support/uml13_ea_metamodel_ext.rb +45 -0
- data/lib/ea_support/uml13_ea_metamodel_generator.rb +43 -0
- data/lib/ea_support/uml13_ea_to_uml13.rb +72 -0
- data/lib/ea_support/uml13_to_uml13_ea.rb +82 -0
- data/lib/rgen/ecore/ecore.rb +16 -2
- data/lib/rgen/ecore/ecore_builder_methods.rb +81 -0
- data/lib/rgen/ecore/ecore_instantiator.rb +5 -1
- data/lib/rgen/metamodel_builder/builder_extensions.rb +11 -3
- data/lib/rgen/metamodel_builder/module_extension2.rb +205 -0
- data/lib/rgen/method_delegation.rb +99 -0
- data/lib/rgen/model_builder.rb +27 -0
- data/lib/rgen/model_builder/builder_context.rb +318 -0
- data/lib/rgen/model_builder/model_serializer.rb +201 -0
- data/lib/rgen/model_builder/reference_resolver.rb +156 -0
- data/lib/rgen/template_language/directory_template_container.rb +6 -2
- data/lib/rgen/template_language/output_handler.rb +2 -4
- data/lib/rgen/template_language/template_container.rb +212 -195
- data/lib/rgen/transformer.rb +95 -4
- data/lib/transformers/ecore_to_uml13.rb +66 -0
- data/lib/transformers/uml13_to_ecore.rb +16 -7
- data/test/ea_instantiator_test.rb +8 -14
- data/test/ea_serializer_test.rb +3 -9
- data/test/ea_serializer_test/ea_testmodel_regenerated.xml +2 -2
- data/test/ea_serializer_test/ea_testmodel_regenerated_import.log +3 -0
- data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +19 -19
- data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +44 -44
- data/test/method_delegation_test.rb +178 -0
- data/test/model_builder/builder_context_test.rb +59 -0
- data/test/model_builder/builder_test.rb +284 -0
- data/test/model_builder/ecore_internal.rb +103 -0
- data/test/model_builder/ecore_original.rb +163 -0
- data/test/model_builder/ecore_original_regenerated.rb +163 -0
- data/test/model_builder/reference_resolver_test.rb +156 -0
- data/test/model_builder/serializer_test.rb +63 -0
- data/test/model_builder_test.rb +4 -0
- data/test/rgen_test.rb +2 -0
- data/test/template_language_test.rb +41 -1
- data/test/template_language_test/expected_result1.txt +1 -3
- data/test/template_language_test/templates/define_local_test/local.tpl +8 -0
- data/test/template_language_test/templates/define_local_test/test.tpl +8 -0
- data/test/template_language_test/templates/evaluate_test/test.tpl +7 -0
- data/test/template_language_test/templates/no_indent_test/no_indent.tpl +3 -0
- data/test/template_language_test/templates/no_indent_test/sub1/no_indent.tpl +3 -0
- data/test/template_language_test/templates/no_indent_test/test.tpl +24 -0
- data/test/template_language_test/templates/no_indent_test/test2.tpl +13 -0
- data/test/template_language_test/templates/no_indent_test/test3.tpl +10 -0
- data/test/template_language_test/templates/template_resolution_test/sub1.tpl +9 -0
- data/test/template_language_test/templates/template_resolution_test/sub1/sub1.tpl +3 -0
- data/test/template_language_test/templates/template_resolution_test/test.tpl +4 -0
- data/test/template_language_test/testout.txt +1 -3
- data/test/testmodel/ea_testmodel_import.log +1 -0
- data/test/testmodel/ea_testmodel_regenerated.xml +808 -0
- data/test/transformer_test.rb +3 -5
- metadata +52 -3
- data/lib/instantiators/ea_instantiator.rb +0 -39
@@ -0,0 +1,99 @@
|
|
1
|
+
module RGen
|
2
|
+
|
3
|
+
module MethodDelegation
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def registerDelegate(delegate, object, method)
|
8
|
+
method = method.to_sym
|
9
|
+
createDelegateStore(object)
|
10
|
+
if object._methodDelegates[method]
|
11
|
+
object._methodDelegates[method] << delegate
|
12
|
+
else
|
13
|
+
object._methodDelegates[method] = [delegate]
|
14
|
+
createDelegatingMethod(object, method)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def unregisterDelegate(delegate, object, method)
|
19
|
+
method = method.to_sym
|
20
|
+
return unless object.respond_to?(:_methodDelegates)
|
21
|
+
return unless object._methodDelegates[method]
|
22
|
+
object._methodDelegates[method].delete(delegate)
|
23
|
+
if object._methodDelegates[method].empty?
|
24
|
+
object._methodDelegates[method] = nil
|
25
|
+
removeDelegatingMethod(object, method)
|
26
|
+
removeDelegateStore(object)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def createDelegateStore(object)
|
33
|
+
return if object.respond_to?(:_methodDelegates)
|
34
|
+
class << object
|
35
|
+
def _methodDelegates
|
36
|
+
@_methodDelegates ||= {}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def removeDelegateStore(object)
|
42
|
+
return unless object.respond_to?(:_methodDelegates)
|
43
|
+
class << object
|
44
|
+
remove_method(:_methodDelegates)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def createDelegatingMethod(object, method)
|
49
|
+
if object.methods.include?(method.to_s)
|
50
|
+
object.instance_eval <<-END
|
51
|
+
class << self
|
52
|
+
alias #{aliasMethodName(method)} #{method}
|
53
|
+
end
|
54
|
+
END
|
55
|
+
end
|
56
|
+
|
57
|
+
# define the delegating method
|
58
|
+
object.instance_eval <<-END
|
59
|
+
class << self
|
60
|
+
def #{method}(*args, &block)
|
61
|
+
@_methodDelegates[:#{method}].each do |d|
|
62
|
+
catch(:continue) do
|
63
|
+
return d.#{method}_delegated(self, *args, &block)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
# if aliased method does not exist, we want an exception
|
67
|
+
#{aliasMethodName(method)}(*args, &block)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
END
|
71
|
+
end
|
72
|
+
|
73
|
+
def removeDelegatingMethod(object, method)
|
74
|
+
if object.methods.include?(aliasMethodName(method))
|
75
|
+
# there is an aliased original, restore it
|
76
|
+
object.instance_eval <<-END
|
77
|
+
class << self
|
78
|
+
alias #{method} #{aliasMethodName(method)}
|
79
|
+
remove_method(:#{aliasMethodName(method)})
|
80
|
+
end
|
81
|
+
END
|
82
|
+
else
|
83
|
+
# just delete the delegating method
|
84
|
+
object.instance_eval <<-END
|
85
|
+
class << self
|
86
|
+
remove_method(:#{method})
|
87
|
+
end
|
88
|
+
END
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def aliasMethodName(method)
|
93
|
+
"#{method}_delegate_original"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rgen/model_builder/builder_context'
|
2
|
+
require 'rgen/method_delegation'
|
3
|
+
#require 'ruby-prof'
|
4
|
+
|
5
|
+
module RGen
|
6
|
+
|
7
|
+
module ModelBuilder
|
8
|
+
|
9
|
+
def self.build(package, env=nil, builderMethodsModule=nil, &block)
|
10
|
+
resolver = ReferenceResolver.new
|
11
|
+
bc = BuilderContext.new(package, builderMethodsModule, resolver, env)
|
12
|
+
contextModule = eval("Module.nesting", block.binding).first
|
13
|
+
MethodDelegation.registerDelegate(bc, contextModule, "const_missing")
|
14
|
+
#RubyProf.start
|
15
|
+
bc.instance_eval(&block)
|
16
|
+
#prof = RubyProf.stop
|
17
|
+
#File.open("profile_flat.txt","w+") do |f|
|
18
|
+
# RubyProf::FlatPrinter.new(prof).print(f, 0)
|
19
|
+
# end
|
20
|
+
MethodDelegation.unregisterDelegate(bc, contextModule, "const_missing")
|
21
|
+
#puts "Resolving..."
|
22
|
+
resolver.resolve(bc.toplevelElements)
|
23
|
+
bc.toplevelElements
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,318 @@
|
|
1
|
+
require 'rgen/ecore/ecore_ext'
|
2
|
+
require 'rgen/model_builder/reference_resolver'
|
3
|
+
|
4
|
+
module RGen
|
5
|
+
|
6
|
+
module ModelBuilder
|
7
|
+
|
8
|
+
class BuilderContext
|
9
|
+
attr_reader :toplevelElements
|
10
|
+
|
11
|
+
def initialize(package, extensionsModule, resolver, env=nil)
|
12
|
+
package = package.ecore unless package.is_a?(RGen::ECore::EPackage)
|
13
|
+
raise "First argument must be a metamodel package" \
|
14
|
+
unless package.is_a?(RGen::ECore::EPackage)
|
15
|
+
@rootPackage, @env = package, env
|
16
|
+
@commandResolver = CommandResolver.new(package, extensionsModule, self)
|
17
|
+
@package = @rootPackage
|
18
|
+
@resolver = resolver
|
19
|
+
@contextStack = []
|
20
|
+
@toplevelElements = []
|
21
|
+
@helperNames = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def const_missing_delegated(delegator, const)
|
25
|
+
ConstPathElement.new(const, self)
|
26
|
+
end
|
27
|
+
|
28
|
+
class CommandResolver
|
29
|
+
def initialize(rootPackage, extensionsModule, builderContext)
|
30
|
+
@extensionFactory = ExtensionContainerFactory.new(rootPackage, extensionsModule, builderContext)
|
31
|
+
@packageResolver = PackageResolver.new(rootPackage, @extensionFactory)
|
32
|
+
@resolveCommand = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def resolveCommand(cmd, parentPackage)
|
36
|
+
return @resolveCommand[[parentPackage, cmd]] if @resolveCommand.has_key?([parentPackage, cmd])
|
37
|
+
package = @packageResolver.packageByCommand(parentPackage, cmd)
|
38
|
+
result = nil
|
39
|
+
if package
|
40
|
+
extensionContainer = @extensionFactory.extensionContainer(package)
|
41
|
+
if extensionContainer.respond_to?(cmd)
|
42
|
+
result = extensionContainer
|
43
|
+
else
|
44
|
+
className = cmd.to_s[0..0].upcase + cmd.to_s[1..-1]
|
45
|
+
result = package.eClasses.find{|c| c.name == className}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
@resolveCommand[[parentPackage, cmd]] = [package, result]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(m, *args, &block)
|
53
|
+
package, classOrContainer = @commandResolver.resolveCommand(m, @package)
|
54
|
+
return super if package.nil?
|
55
|
+
return classOrContainer.send(m, *args, &block) if classOrContainer.is_a?(ExtensionContainerFactory::ExtensionContainer)
|
56
|
+
eClass = classOrContainer
|
57
|
+
nameArg, argHash = self.class.processArguments(args)
|
58
|
+
internalName = nameArg || argHash[:name]
|
59
|
+
argHash[:name] ||= nameArg if nameArg && self.class.hasNameAttribute(eClass)
|
60
|
+
resolverJobs, asRole, helperName = self.class.filterArgHash(argHash, eClass)
|
61
|
+
element = eClass.instanceClass.new(argHash)
|
62
|
+
@resolver.setElementName(element, internalName)
|
63
|
+
@env << element if @env
|
64
|
+
contextElement = @contextStack.last
|
65
|
+
if contextElement
|
66
|
+
self.class.associateWithContextElement(element, contextElement, asRole)
|
67
|
+
else
|
68
|
+
@toplevelElements << element
|
69
|
+
end
|
70
|
+
resolverJobs.each do |job|
|
71
|
+
job.receiver = element
|
72
|
+
job.namespace = contextElement
|
73
|
+
@resolver.addJob(job)
|
74
|
+
end
|
75
|
+
# process block
|
76
|
+
if block
|
77
|
+
@contextStack.push(element)
|
78
|
+
@package, oldPackage = package, @package
|
79
|
+
instance_eval(&block)
|
80
|
+
@package = oldPackage
|
81
|
+
@contextStack.pop
|
82
|
+
end
|
83
|
+
element
|
84
|
+
end
|
85
|
+
|
86
|
+
def _using(constPathElement, &block)
|
87
|
+
@package, oldPackage =
|
88
|
+
self.class.resolvePackage(@package, @rootPackage, constPathElement.constPath), @package
|
89
|
+
instance_eval(&block)
|
90
|
+
@package = oldPackage
|
91
|
+
end
|
92
|
+
|
93
|
+
def _context(depth=1)
|
94
|
+
@contextStack[-depth]
|
95
|
+
end
|
96
|
+
|
97
|
+
class ExtensionContainerFactory
|
98
|
+
|
99
|
+
class ExtensionContainer
|
100
|
+
def initialize(builderContext)
|
101
|
+
@builderContext = builderContext
|
102
|
+
end
|
103
|
+
def method_missing(m, *args, &block)
|
104
|
+
@builderContext.send(m, *args, &block)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def initialize(rootPackage, extensionsModule, builderContext)
|
109
|
+
@rootPackage, @extensionsModule, @builderContext = rootPackage, extensionsModule, builderContext
|
110
|
+
@extensionContainer = {}
|
111
|
+
end
|
112
|
+
|
113
|
+
def moduleForPackage(package)
|
114
|
+
qName = package.qualifiedName
|
115
|
+
rqName = @rootPackage.qualifiedName
|
116
|
+
raise "Package #{qName} is not contained within #{rqName}" unless qName.index(rqName) == 0
|
117
|
+
path = qName.sub(rqName,'').split('::')
|
118
|
+
path.shift if path.first == ""
|
119
|
+
mod = @extensionsModule
|
120
|
+
path.each do |p|
|
121
|
+
if mod && mod.const_defined?(p)
|
122
|
+
mod = mod.const_get(p)
|
123
|
+
else
|
124
|
+
mod = nil
|
125
|
+
break
|
126
|
+
end
|
127
|
+
end
|
128
|
+
mod
|
129
|
+
end
|
130
|
+
|
131
|
+
def extensionContainer(package)
|
132
|
+
return @extensionContainer[package] if @extensionContainer[package]
|
133
|
+
container = ExtensionContainer.new(@builderContext)
|
134
|
+
extensionModule = moduleForPackage(package)
|
135
|
+
container.extend(extensionModule) if extensionModule
|
136
|
+
@extensionContainer[package] = container
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class PackageResolver
|
141
|
+
def initialize(rootPackage, extensionFactory)
|
142
|
+
@rootPackage = rootPackage
|
143
|
+
@extensionFactory = extensionFactory
|
144
|
+
@packageByCommand = {}
|
145
|
+
end
|
146
|
+
|
147
|
+
def packageByCommand(contextPackage, name)
|
148
|
+
return @packageByCommand[[contextPackage, name]] if @packageByCommand.has_key?([contextPackage, name])
|
149
|
+
if @extensionFactory.extensionContainer(contextPackage).respond_to?(name)
|
150
|
+
result = contextPackage
|
151
|
+
else
|
152
|
+
className = name.to_s[0..0].upcase + name.to_s[1..-1]
|
153
|
+
eClass = contextPackage.eClasses.find{|c| c.name == className}
|
154
|
+
if eClass
|
155
|
+
result = contextPackage
|
156
|
+
elsif contextPackage != @rootPackage
|
157
|
+
result = packageByCommand(contextPackage.eSuperPackage, name)
|
158
|
+
else
|
159
|
+
result = nil
|
160
|
+
end
|
161
|
+
end
|
162
|
+
@packageByCommand[[contextPackage, name]] = result
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class ConstPathElement < Module
|
167
|
+
def initialize(name, builderContext, parent=nil)
|
168
|
+
@name = name.to_s
|
169
|
+
@builderContext = builderContext
|
170
|
+
@parent = parent
|
171
|
+
end
|
172
|
+
|
173
|
+
def const_missing(const)
|
174
|
+
ConstPathElement.new(const, @builderContext, self)
|
175
|
+
end
|
176
|
+
|
177
|
+
def method_missing(m, *args, &block)
|
178
|
+
@builderContext._using(self) do
|
179
|
+
send(m, *args, &block)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def constPath
|
184
|
+
if @parent
|
185
|
+
@parent.constPath << @name
|
186
|
+
else
|
187
|
+
[@name]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# helper methods put in the class object to be out of the way of
|
193
|
+
# method evaluation in the builder context
|
194
|
+
class << self
|
195
|
+
class PackageNotFoundException < Exception
|
196
|
+
end
|
197
|
+
|
198
|
+
def resolvePackage(contextPackage, rootPackage, path)
|
199
|
+
begin
|
200
|
+
return resolvePackageDownwards(contextPackage, path)
|
201
|
+
rescue PackageNotFoundException
|
202
|
+
if contextPackage.eSuperPackage && contextPackage != rootPackage
|
203
|
+
return resolvePackage(contextPackage.eSuperPackage, rootPackage, path)
|
204
|
+
else
|
205
|
+
raise
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def resolvePackageDownwards(contextPackage, path)
|
211
|
+
first, *rest = path
|
212
|
+
package = contextPackage.eSubpackages.find{|p| p.name == first}
|
213
|
+
raise PackageNotFoundException.new("Could not resolve package: #{first} is not a subpackage of #{contextPackage.name}") unless package
|
214
|
+
if rest.empty?
|
215
|
+
package
|
216
|
+
else
|
217
|
+
resolvePackageDownwards(package, rest)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def processArguments(args)
|
222
|
+
unless (args.size == 2 && args.first.is_a?(String) && args.last.is_a?(Hash)) ||
|
223
|
+
(args.size == 1 && (args.first.is_a?(String) || args.first.is_a?(Hash))) ||
|
224
|
+
args.size == 0
|
225
|
+
raise "Provide a Hash to set feature values, " +
|
226
|
+
"optionally the first argument may be a String specifying " +
|
227
|
+
"the value of the \"name\" attribute."
|
228
|
+
end
|
229
|
+
if args.last.is_a?(Hash)
|
230
|
+
argHash = args.last
|
231
|
+
else
|
232
|
+
argHash = {}
|
233
|
+
end
|
234
|
+
nameArg = args.first if args.first.is_a?(String)
|
235
|
+
[nameArg, argHash]
|
236
|
+
end
|
237
|
+
|
238
|
+
def filterArgHash(argHash, eClass)
|
239
|
+
resolverJobs = []
|
240
|
+
asRole, helperName = nil, nil
|
241
|
+
refByName = {}
|
242
|
+
eAllReferences(eClass).each {|r| refByName[r.name] = r}
|
243
|
+
argHash.each_pair do |k,v|
|
244
|
+
if k == :as
|
245
|
+
asRole = v
|
246
|
+
argHash.delete(k)
|
247
|
+
elsif k == :name && !hasNameAttribute(eClass)
|
248
|
+
helperName = v
|
249
|
+
argHash.delete(k)
|
250
|
+
elsif v.is_a?(String)
|
251
|
+
ref = refByName[k.to_s]#eAllReferences(eClass).find{|r| r.name == k.to_s}
|
252
|
+
if ref
|
253
|
+
argHash.delete(k)
|
254
|
+
resolverJobs << ReferenceResolver::ResolverJob.new(nil, ref, nil, v)
|
255
|
+
end
|
256
|
+
elsif v.is_a?(Array)
|
257
|
+
ref = refByName[k.to_s] #eAllReferences(eClass).find{|r| r.name == k.to_s}
|
258
|
+
ref && v.dup.each do |e|
|
259
|
+
if e.is_a?(String)
|
260
|
+
v.delete(e)
|
261
|
+
resolverJobs << ReferenceResolver::ResolverJob.new(nil, ref, nil, e)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
[ resolverJobs, asRole, helperName ]
|
267
|
+
end
|
268
|
+
|
269
|
+
def hasNameAttribute(eClass)
|
270
|
+
@hasNameAttribute ||= {}
|
271
|
+
@hasNameAttribute[eClass] ||= eClass.eAllAttributes.any?{|a| a.name == "name"}
|
272
|
+
end
|
273
|
+
|
274
|
+
def eAllReferences(eClass)
|
275
|
+
@eAllReferences ||= {}
|
276
|
+
@eAllReferences[eClass] ||= eClass.eAllReferences
|
277
|
+
end
|
278
|
+
|
279
|
+
def containmentRefs(contextClass, eClass)
|
280
|
+
@containmentRefs ||= {}
|
281
|
+
@containmentRefs[[contextClass, eClass]] ||=
|
282
|
+
eAllReferences(contextClass).select do |r|
|
283
|
+
r.containment && (eClass.eAllSuperTypes << eClass).include?(r.eType)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def associateWithContextElement(element, contextElement, asRole)
|
288
|
+
return unless contextElement
|
289
|
+
contextClass = contextElement.class.ecore
|
290
|
+
if asRole
|
291
|
+
asRoleRef = eAllReferences(contextClass).find{|r| r.name == asRole.to_s}
|
292
|
+
raise "Context class #{contextClass.name} has no reference named #{asRole}" unless asRoleRef
|
293
|
+
ref = asRoleRef
|
294
|
+
else
|
295
|
+
possibleContainmentRefs = containmentRefs(contextClass, element.class.ecore)
|
296
|
+
if possibleContainmentRefs.size == 1
|
297
|
+
ref = possibleContainmentRefs.first
|
298
|
+
elsif possibleContainmentRefs.size == 0
|
299
|
+
raise "Context class #{contextClass.name} can not contain a #{element.class.ecore.name}"
|
300
|
+
else
|
301
|
+
raise "Context class #{contextClass.name} has several containment references to a #{element.class.ecore.name}." +
|
302
|
+
" Clearify using \":as => <role>\""
|
303
|
+
end
|
304
|
+
end
|
305
|
+
if ref.many
|
306
|
+
contextElement.addGeneric(ref.name, element)
|
307
|
+
else
|
308
|
+
contextElement.setGeneric(ref.name, element)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
|
318
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
require 'rgen/array_extensions'
|
2
|
+
require 'rgen/ecore/ecore_ext'
|
3
|
+
|
4
|
+
module RGen
|
5
|
+
|
6
|
+
module ModelBuilder
|
7
|
+
|
8
|
+
class ModelSerializer
|
9
|
+
|
10
|
+
def initialize(writable, rootPackage)
|
11
|
+
@writable = writable
|
12
|
+
@currentPackage = rootPackage
|
13
|
+
@qualifiedElementName = {}
|
14
|
+
@internalElementName = {}
|
15
|
+
@relativeQualifiedElementName = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def serialize(elements)
|
19
|
+
calcQualifiedElementNames(elements)
|
20
|
+
unifyQualifiedElementNames
|
21
|
+
elements = [elements] unless elements.is_a?(Enumerable)
|
22
|
+
elements.each do |e|
|
23
|
+
serializeElement(e)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def serializeElement(element, viaRef=nil, namePath=[], indent=0)
|
30
|
+
className = element.class.ecore.name
|
31
|
+
cmd = className[0..0].downcase+className[1..-1]
|
32
|
+
args = ["\"#{@internalElementName[element]}\""]
|
33
|
+
namePath = namePath + [@internalElementName[element]]
|
34
|
+
childs = {}
|
35
|
+
eAllStructuralFeatures(element).each do |f|
|
36
|
+
next if f.derived
|
37
|
+
if f.is_a?(RGen::ECore::EAttribute)
|
38
|
+
next if f.name == "name" && element.name == @internalElementName[element]
|
39
|
+
val = element.getGeneric(f.name)
|
40
|
+
#puts f.defaultValue.inspect if f.name == "isRoot"
|
41
|
+
args << ":#{f.name} => #{serializeAttribute(val)}" unless val == f.defaultValue || val.nil?
|
42
|
+
elsif !f.containment
|
43
|
+
next if f.eOpposite && f.eOpposite == viaRef
|
44
|
+
val = element.getGeneric(f.name)
|
45
|
+
refString = serializeReference(element, f, val)
|
46
|
+
args << ":#{f.name} => #{refString}" if refString
|
47
|
+
else
|
48
|
+
cs = element.getGeneric(f.name)
|
49
|
+
refString = nil
|
50
|
+
if cs.is_a?(Array)
|
51
|
+
cs.compact!
|
52
|
+
rcs = cs.select{|c| serializeChild?(c, namePath)}
|
53
|
+
childs[f] = rcs unless rcs.empty?
|
54
|
+
refString = serializeReference(element, f, cs-rcs)
|
55
|
+
else
|
56
|
+
if cs && serializeChild?(cs, namePath)
|
57
|
+
childs[f] = [cs]
|
58
|
+
else
|
59
|
+
refString = serializeReference(element, f, cs)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
args << ":#{f.name} => #{refString}" if refString
|
63
|
+
end
|
64
|
+
end
|
65
|
+
cmd = elementPackage(element)+"."+cmd if elementPackage(element).size > 0
|
66
|
+
@writable.write " " * indent + cmd + " " + args.join(", ")
|
67
|
+
if childs.size > 0
|
68
|
+
@writable.write " do\n"
|
69
|
+
oldPackage, @currentPackage = @currentPackage, element.class.ecore.ePackage
|
70
|
+
childs.each_pair do |f,cs|
|
71
|
+
cs.each {|c| serializeElement(c, f, namePath, indent+1) }
|
72
|
+
end
|
73
|
+
@currentPackage = oldPackage
|
74
|
+
@writable.write " " * indent + "end\n"
|
75
|
+
else
|
76
|
+
@writable.write "\n"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def serializeChild?(child, namePath)
|
81
|
+
@qualifiedElementName[child][0..-2] == namePath
|
82
|
+
end
|
83
|
+
|
84
|
+
def serializeAttribute(value)
|
85
|
+
if value.is_a?(String)
|
86
|
+
"\"#{value.gsub("\"","\\\"")}\""
|
87
|
+
elsif value.is_a?(Symbol)
|
88
|
+
":#{value}"
|
89
|
+
elsif value.nil?
|
90
|
+
"nil"
|
91
|
+
else
|
92
|
+
value.to_s
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def serializeReference(element, ref, value)
|
97
|
+
if value.is_a?(Array)
|
98
|
+
value = value.compact
|
99
|
+
value = value.select{|v| compareWithOppositeReference(element, v) > 0} if ref.eOpposite
|
100
|
+
qualNames = value.collect do |v|
|
101
|
+
relativeQualifiedElementName(v, element).join(".")
|
102
|
+
end
|
103
|
+
!qualNames.empty? && ("[" + qualNames.collect { |v| "\"#{v}\"" }.join(", ") + "]")
|
104
|
+
elsif value && (!ref.eOpposite || compareWithOppositeReference(element, value) > 0)
|
105
|
+
qualName = relativeQualifiedElementName(value, element).join(".")
|
106
|
+
("\"#{qualName}\"")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def compareWithOppositeReference(element, target)
|
111
|
+
result = relativeQualifiedElementName(element, target).size <=>
|
112
|
+
relativeQualifiedElementName(target, element).size
|
113
|
+
return result if result != 0
|
114
|
+
result = element.class.name <=> target.class.name
|
115
|
+
return result if result != 0
|
116
|
+
element.object_id <=> target.object_id
|
117
|
+
end
|
118
|
+
|
119
|
+
def elementPackage(element)
|
120
|
+
@elementPackage ||= {}
|
121
|
+
return @elementPackage[element] if @elementPackage[element]
|
122
|
+
eNames = element.class.ecore.ePackage.qualifiedName.split("::")
|
123
|
+
rNames = @currentPackage.qualifiedName.split("::")
|
124
|
+
while eNames.first == rNames.first && !eNames.first.nil?
|
125
|
+
eNames.shift
|
126
|
+
rNames.shift
|
127
|
+
end
|
128
|
+
@elementPackage[element] = eNames.join("::")
|
129
|
+
end
|
130
|
+
|
131
|
+
def relativeQualifiedElementName(element, context)
|
132
|
+
return @relativeQualifiedElementName[[element, context]] if @relativeQualifiedElementName[[element, context]]
|
133
|
+
# elements which are not in the @qualifiedElementName Hash are not in the scope
|
134
|
+
# of this serialization and will be ignored
|
135
|
+
return [] if element.nil? || @qualifiedElementName[element].nil?
|
136
|
+
return [] if context.nil? || @qualifiedElementName[context].nil?
|
137
|
+
eNames = @qualifiedElementName[element].dup
|
138
|
+
cNames = @qualifiedElementName[context].dup
|
139
|
+
while eNames.first == cNames.first && eNames.size > 1
|
140
|
+
eNames.shift
|
141
|
+
cNames.shift
|
142
|
+
end
|
143
|
+
@relativeQualifiedElementName[[element, context]] = eNames
|
144
|
+
end
|
145
|
+
|
146
|
+
def calcQualifiedElementNames(elements, prefix=[], takenNames=[])
|
147
|
+
elements = [elements] unless elements.is_a?(Array)
|
148
|
+
elements.compact!
|
149
|
+
elements.each do |element|
|
150
|
+
qualifiedNamePath = prefix + [calcInternalElementName(element, takenNames)]
|
151
|
+
@qualifiedElementName[element] ||= []
|
152
|
+
@qualifiedElementName[element] << qualifiedNamePath
|
153
|
+
takenChildNames = []
|
154
|
+
eAllStructuralFeatures(element).each do |f|
|
155
|
+
if f.is_a?(RGen::ECore::EReference) && f.containment
|
156
|
+
childs = element.getGeneric(f.name)
|
157
|
+
calcQualifiedElementNames(childs, qualifiedNamePath, takenChildNames)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def unifyQualifiedElementNames
|
164
|
+
@qualifiedElementName.keys.each do |k|
|
165
|
+
@qualifiedElementName[k] = @qualifiedElementName[k].sort{|a,b| a.size <=> b.size}.first
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def calcInternalElementName(element, takenNames)
|
170
|
+
return @internalElementName[element] if @internalElementName[element]
|
171
|
+
name = if element.respond_to?(:name) && element.name && !element.name.empty?
|
172
|
+
element.name
|
173
|
+
else
|
174
|
+
nextElementHelperName(element)
|
175
|
+
end
|
176
|
+
while takenNames.include?(name)
|
177
|
+
name = nextElementHelperName(element)
|
178
|
+
end
|
179
|
+
takenNames << name
|
180
|
+
@internalElementName[element] = name
|
181
|
+
end
|
182
|
+
|
183
|
+
def nextElementHelperName(element)
|
184
|
+
eClass = element.class.ecore
|
185
|
+
@nextElementNameId ||= {}
|
186
|
+
@nextElementNameId[eClass] ||= 1
|
187
|
+
result = "_#{eClass.name}#{@nextElementNameId[eClass]}"
|
188
|
+
@nextElementNameId[eClass] += 1
|
189
|
+
result
|
190
|
+
end
|
191
|
+
|
192
|
+
def eAllStructuralFeatures(element)
|
193
|
+
@eAllStructuralFeatures ||= {}
|
194
|
+
@eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|