rgen 0.4.6 → 0.5.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.
- 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
|