rgen 0.4.3 → 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/lib/mmgen/metamodel_generator.rb +2 -1
- data/lib/mmgen/mm_ext/{ecore_ext.rb → ecore_mmgen_ext.rb} +3 -7
- data/lib/mmgen/templates/metamodel_generator.tpl +10 -8
- data/lib/rgen/ecore/ecore_ext.rb +69 -0
- data/lib/rgen/ecore/ecore_transformer.rb +1 -1
- data/lib/rgen/metamodel_builder/builder_extensions.rb +10 -2
- data/lib/rgen/serializer/xmi11_serializer.rb +101 -0
- data/lib/rgen/serializer/xmi20_serializer.rb +7 -7
- data/lib/rgen/serializer/xml_serializer.rb +47 -14
- data/test/ea_serializer_test.rb +29 -0
- data/test/ea_serializer_test/ea_testmodel_regenerated.xml +821 -0
- data/test/metamodel_roundtrip_test.rb +1 -2
- data/test/metamodel_roundtrip_test/TestModel.rb +1 -0
- data/test/metamodel_roundtrip_test/TestModel_Regenerated.rb +34 -35
- data/test/metamodel_roundtrip_test/houseMetamodel_Regenerated.ecore +128 -236
- data/test/rgen_test.rb +1 -0
- metadata +93 -95
- data/lib/rgen/metamodel_builder.rb.bak +0 -196
- data/lib/rgen/metamodel_builder/builder_extensions.rb.bak +0 -437
- data/lib/rgen/metamodel_builder/builder_runtime.rb.bak +0 -73
- data/lib/rgen/name_helper.rb.bak +0 -37
- data/lib/rgen/template_language.rb.bak +0 -289
- data/lib/rgen/template_language/directory_template_container.rb.bak +0 -69
- data/lib/rgen/template_language/output_handler.rb.bak +0 -88
- data/lib/rgen/template_language/template_container.rb.bak +0 -196
- data/lib/rgen/transformer.rb.bak +0 -381
- data/test/environment_test.rb.bak +0 -52
- data/test/metamodel_builder_test.rb.bak +0 -443
- data/test/output_handler_test.rb.bak +0 -50
- data/test/template_language_test.rb.bak +0 -72
- data/test/transformer_test.rb.bak +0 -223
data/CHANGELOG
CHANGED
@@ -64,3 +64,9 @@
|
|
64
64
|
* Bugfix: Use object identity for metamodel to-many add/remove methods
|
65
65
|
* Bugfix: If expand's :for expression evaluates to nil an error is generated (silently used current context before)
|
66
66
|
* Template language indentation string can be set on DirectoryTemplateContainer and with the "file" command
|
67
|
+
|
68
|
+
=0.4.4 (Sep 10th, 2008)
|
69
|
+
|
70
|
+
* Added "abstract" metamodel DSL command
|
71
|
+
* Added ecore_ext.rb with convenience methods
|
72
|
+
* Added XMI1.1 serializer, revised XMLSerializer super class
|
@@ -16,10 +16,6 @@ module RGen
|
|
16
16
|
eSuperPackage.qualifiedModuleName(rootPackage) + "::" + moduleName
|
17
17
|
end
|
18
18
|
|
19
|
-
def allClassifiers
|
20
|
-
eSubpackages.inject(eClassifiers) {|r,p| r.concat(p.allClassifiers) }
|
21
|
-
end
|
22
|
-
|
23
19
|
def ancestorPackages
|
24
20
|
return [] unless eSuperPackage
|
25
21
|
[eSuperPackage] + eSuperPackage.ancestorPackages
|
@@ -66,17 +62,17 @@ module RGen
|
|
66
62
|
def classifierName
|
67
63
|
firstToUpper(name)
|
68
64
|
end
|
69
|
-
def
|
65
|
+
def qualifiedClassifierName(rootPackage)
|
70
66
|
(ePackage ? ePackage.qualifiedModuleName(rootPackage) + "::" : "") + classifierName
|
71
67
|
end
|
72
68
|
def ancestorPackages
|
73
69
|
return [] unless ePackage
|
74
70
|
[ePackage] + ePackage.ancestorPackages
|
75
71
|
end
|
76
|
-
def
|
72
|
+
def qualifiedClassifierNameIfRequired(package)
|
77
73
|
if ePackage != package
|
78
74
|
commonSuper = (package.ancestorPackages & ancestorPackages).first
|
79
|
-
|
75
|
+
qualifiedClassifierName(commonSuper)
|
80
76
|
else
|
81
77
|
classifierName
|
82
78
|
end
|
@@ -23,6 +23,7 @@
|
|
23
23
|
<% expand 'EnumTypes' %>
|
24
24
|
<% for c in ownClasses %><%nl%>
|
25
25
|
<% expand 'ClassHeader', this, :for => c %><%iinc%>
|
26
|
+
<% if c.abstract %>abstract<% end %>
|
26
27
|
<% expand 'annotations::Annotations', :for => c %>
|
27
28
|
<% expand 'Attribute', this, :foreach => c.eAttributes %>
|
28
29
|
<%idec%>
|
@@ -37,6 +38,7 @@
|
|
37
38
|
<% define 'GenerateClassesReordered', :for => EPackage do %>
|
38
39
|
<% for c in allClassesSorted %><%nl%>
|
39
40
|
<% expand 'ClassHeaderFullyQualified', this, :for => c %><%iinc%>
|
41
|
+
<% if c.abstract %>abstract<% end %>
|
40
42
|
<% expand 'annotations::Annotations', :for => c %>
|
41
43
|
<% expand 'Attribute', this, :foreach => c.eAttributes %>
|
42
44
|
<%idec%>
|
@@ -61,7 +63,7 @@
|
|
61
63
|
<% if upperBound == 1%>
|
62
64
|
has_attr '<%= name %>', <%nows%>
|
63
65
|
<% if eType.is_a?(EEnum) %><%nows%>
|
64
|
-
<%= eType.
|
66
|
+
<%= eType.qualifiedClassifierName(rootp) %><%nows%>
|
65
67
|
<% else %><%nows%>
|
66
68
|
<%= eType && eType.instanceClass.to_s %><%nows%>
|
67
69
|
<% end %><%nows%>
|
@@ -90,7 +92,7 @@
|
|
90
92
|
|
91
93
|
<% define 'GenerateAssocs', :for => EPackage do %>
|
92
94
|
<% refDone = {} %>
|
93
|
-
<% for ref in
|
95
|
+
<% for ref in eAllClassifiers.select{|c| c.is_a?(EClass)}.eReferences %>
|
94
96
|
<% if !refDone[ref] && ref.eOpposite %>
|
95
97
|
<% ref = ref.eOpposite if ref.eOpposite.containment %>
|
96
98
|
<% refDone[ref] = refDone[ref.eOpposite] = true %>
|
@@ -133,7 +135,7 @@
|
|
133
135
|
<% end %>
|
134
136
|
|
135
137
|
<% define 'Reference', :for => EReference do |cmd, rootpackage| %>
|
136
|
-
<%= eContainingClass.
|
138
|
+
<%= eContainingClass.qualifiedClassifierName(rootpackage) %>.<%= cmd %> '<%= name %>', <%= eType && eType.qualifiedClassifierName(rootpackage) %><%nows%>
|
137
139
|
<% if eOpposite %><%nows%>
|
138
140
|
, '<%= eOpposite.name%>'<%nows%>
|
139
141
|
<% end %><%nows%>
|
@@ -151,20 +153,20 @@
|
|
151
153
|
<% define 'ClassHeader', :for => EClass do |rootp| %>
|
152
154
|
class <%= classifierName %> < <% nows %>
|
153
155
|
<% if eSuperTypes.size > 1 %><% nows %>
|
154
|
-
RGen::MetamodelBuilder::MMMultiple(<%= eSuperTypes.collect{|t| t.
|
156
|
+
RGen::MetamodelBuilder::MMMultiple(<%= eSuperTypes.collect{|t| t.qualifiedClassifierNameIfRequired(rootp)}.join(', ') %>)
|
155
157
|
<% elsif eSuperTypes.size > 0 %><% nows %>
|
156
|
-
<%= eSuperTypes.first.
|
158
|
+
<%= eSuperTypes.first.qualifiedClassifierNameIfRequired(rootp) %>
|
157
159
|
<% else %><% nows %>
|
158
160
|
RGen::MetamodelBuilder::MMBase
|
159
161
|
<% end %>
|
160
162
|
<% end %>
|
161
163
|
|
162
164
|
<% define 'ClassHeaderFullyQualified', :for => EClass do |rootp| %>
|
163
|
-
class <%=
|
165
|
+
class <%= qualifiedClassifierName(rootp) %> < <% nows %>
|
164
166
|
<% if eSuperTypes.size > 1 %><% nows %>
|
165
|
-
RGen::MetamodelBuilder::MMMultiple(<%= eSuperTypes.collect{|t| t.
|
167
|
+
RGen::MetamodelBuilder::MMMultiple(<%= eSuperTypes.collect{|t| t.qualifiedClassifierName(rootp)}.join(', ') %>)
|
166
168
|
<% elsif eSuperTypes.size > 0 %><% nows %>
|
167
|
-
<%= eSuperTypes.first.
|
169
|
+
<%= eSuperTypes.first.qualifiedClassifierName(rootp) %>
|
168
170
|
<% else %><% nows %>
|
169
171
|
RGen::MetamodelBuilder::MMBase
|
170
172
|
<% end %>
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rgen/array_extensions'
|
2
|
+
require 'rgen/ecore/ecore'
|
3
|
+
|
4
|
+
module RGen
|
5
|
+
module ECore
|
6
|
+
|
7
|
+
# make super type reference bidirectional
|
8
|
+
EClass.many_to_many 'eSuperTypes', ECore::EClass, 'eSubTypes'
|
9
|
+
|
10
|
+
module EModelElement::ClassModule
|
11
|
+
|
12
|
+
def annotationValue(source, tag)
|
13
|
+
detail = eAnnotations.select{ |a| a.source == source }.details.find{ |d| d.key == tag }
|
14
|
+
detail && detail.value
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
module EPackage::ClassModule
|
20
|
+
|
21
|
+
def qualifiedName
|
22
|
+
if eSuperPackage
|
23
|
+
eSuperPackage.qualifiedName+"::"+name
|
24
|
+
else
|
25
|
+
name
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def eAllClassifiers
|
30
|
+
eClassifiers + eSubpackages.eAllClassifiers
|
31
|
+
end
|
32
|
+
def eAllSubpackages
|
33
|
+
eSubpackages + eSubpackages.eAllSubpackages
|
34
|
+
end
|
35
|
+
|
36
|
+
def eClasses
|
37
|
+
eClassifiers.select{|c| c.is_a?(ECore::EClass)}
|
38
|
+
end
|
39
|
+
|
40
|
+
def eAllClasses
|
41
|
+
eClasses + eSubpackages.eAllClasses
|
42
|
+
end
|
43
|
+
|
44
|
+
def eDataTypes
|
45
|
+
eClassifiers.select{|c| c.is_a?(ECore::EDataType)}
|
46
|
+
end
|
47
|
+
|
48
|
+
def eAllDataTypes
|
49
|
+
eDataTypes + eSubpackages.eAllDataTypes
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module EClass::ClassModule
|
54
|
+
|
55
|
+
def qualifiedName
|
56
|
+
if ePackage
|
57
|
+
ePackage.qualifiedName+"::"+name
|
58
|
+
else
|
59
|
+
name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def eAllSubTypes
|
64
|
+
eSubTypes + eSubTypes.eAllSubTypes
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -12,7 +12,7 @@ class ECoreTransformer < Transformer
|
|
12
12
|
|
13
13
|
transform Class, :to => EClass, :if => :convert? do
|
14
14
|
{ :name => name.gsub(/.*::(\w+)$/,'\1'),
|
15
|
-
:abstract =>
|
15
|
+
:abstract => _abstract_class,
|
16
16
|
:interface => false,
|
17
17
|
:eStructuralFeatures => trans(_metamodel_description),
|
18
18
|
:ePackage => trans(name =~ /(.*)::\w+$/ ? eval($1) : nil),
|
@@ -218,9 +218,17 @@ module BuilderExtensions
|
|
218
218
|
def _metamodel_description # :nodoc:
|
219
219
|
@metamodel_description ||= []
|
220
220
|
end
|
221
|
+
|
222
|
+
def abstract
|
223
|
+
@abstract = true
|
224
|
+
end
|
225
|
+
|
226
|
+
def _abstract_class
|
227
|
+
@abstract || false
|
228
|
+
end
|
221
229
|
|
222
|
-
|
223
|
-
|
230
|
+
def inherited(c)
|
231
|
+
c.send(:include, c.const_set(:ClassModule, Module.new))
|
224
232
|
end
|
225
233
|
|
226
234
|
protected
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'rgen/serializer/xml_serializer'
|
2
|
+
|
3
|
+
module RGen
|
4
|
+
|
5
|
+
module Serializer
|
6
|
+
|
7
|
+
class XMI11Serializer < XMLSerializer
|
8
|
+
|
9
|
+
def initialize(file)
|
10
|
+
super
|
11
|
+
@namespacePrefix = ""
|
12
|
+
end
|
13
|
+
|
14
|
+
def setNamespace(shortcut, url)
|
15
|
+
@namespaceShortcut = shortcut
|
16
|
+
@namespaceUrl = url
|
17
|
+
@namespacePrefix = shortcut+":"
|
18
|
+
end
|
19
|
+
|
20
|
+
def serialize(rootElement, headerInfo=nil)
|
21
|
+
attrs = {}
|
22
|
+
attrs['xmi.version'] = "1.1"
|
23
|
+
attrs['xmlns:'+@namespaceShortcut] = @namespaceUrl if @namespaceUrl
|
24
|
+
attrs['timestamp'] = Time.now.to_s
|
25
|
+
startTag("XMI", attrs)
|
26
|
+
if headerInfo
|
27
|
+
startTag("XMI.header")
|
28
|
+
writeHeaderInfo(headerInfo)
|
29
|
+
endTag("XMI.header")
|
30
|
+
end
|
31
|
+
startTag("XMI.content")
|
32
|
+
writeElement(rootElement)
|
33
|
+
endTag("XMI.content")
|
34
|
+
endTag("XMI")
|
35
|
+
end
|
36
|
+
|
37
|
+
def writeHeaderInfo(hash)
|
38
|
+
hash.each_pair do |k,v|
|
39
|
+
tag = "XMI." + k.to_s
|
40
|
+
startTag(tag)
|
41
|
+
if v.is_a?(Hash)
|
42
|
+
writeHeaderInfo(v)
|
43
|
+
else
|
44
|
+
writeText(v.to_s)
|
45
|
+
end
|
46
|
+
endTag(tag)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def writeElement(element)
|
51
|
+
tag = @namespacePrefix + element.class.ecore.name
|
52
|
+
attrs = attributeHash(element)
|
53
|
+
startTag(tag, attrs)
|
54
|
+
containmentReferences(element).each do |r|
|
55
|
+
roletag = @namespacePrefix + r.eContainingClass.name + "." + r.name
|
56
|
+
targets = element.getGeneric(r.name)
|
57
|
+
targets = [ targets ] unless targets.is_a?(Array)
|
58
|
+
targets.compact!
|
59
|
+
next if targets.empty?
|
60
|
+
startTag(roletag)
|
61
|
+
targets.each do |t|
|
62
|
+
writeElement(t)
|
63
|
+
end
|
64
|
+
endTag(roletag)
|
65
|
+
end
|
66
|
+
endTag(tag)
|
67
|
+
end
|
68
|
+
|
69
|
+
def attributeHash(element)
|
70
|
+
result = {"xmi.id" => xmiId(element)}
|
71
|
+
eAllAttributes(element).select{|a| !a.derived}.each do |a|
|
72
|
+
val = element.getGeneric(a.name)
|
73
|
+
result[a.name] = val unless val.nil? || val == ""
|
74
|
+
end
|
75
|
+
eAllReferences(element).each do |r|
|
76
|
+
next if r.derived
|
77
|
+
next if r.containment
|
78
|
+
next if r.eOpposite && r.eOpposite.containment
|
79
|
+
next if r.eOpposite && r.many && !r.eOpposite.many
|
80
|
+
targetElements = element.getGeneric(r.name)
|
81
|
+
targetElements = [targetElements] unless targetElements.is_a?(Array)
|
82
|
+
targetElements.compact!
|
83
|
+
val = targetElements.collect{|te| xmiId(te)}.compact.join(' ')
|
84
|
+
result[r.name] = val unless val == ""
|
85
|
+
end
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
def xmiId(element)
|
90
|
+
if element.respond_to?(:_xmi_id) && element._xmi_id
|
91
|
+
element._xmi_id.to_s
|
92
|
+
else
|
93
|
+
element.object_id.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -14,19 +14,19 @@ class XMI20Serializer < XMLSerializer
|
|
14
14
|
attrs['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
|
15
15
|
attrs['xmlns:ecore'] = "http://www.eclipse.org/emf/2002/Ecore"
|
16
16
|
tag = "ecore:"+rootElement.class.ecore.name
|
17
|
-
startTag(tag, attrs
|
18
|
-
writeComposites(rootElement
|
19
|
-
endTag(tag
|
17
|
+
startTag(tag, attrs)
|
18
|
+
writeComposites(rootElement)
|
19
|
+
endTag(tag)
|
20
20
|
end
|
21
21
|
|
22
|
-
def writeComposites(element
|
22
|
+
def writeComposites(element)
|
23
23
|
eachReferencedElement(element, containmentReferences(element)) do |r,te|
|
24
24
|
attrs = attributeHash(te)
|
25
25
|
attrs['xsi:type'] = "ecore:"+te.class.ecore.name
|
26
26
|
tag = r.name
|
27
|
-
startTag(tag, attrs
|
28
|
-
writeComposites(te
|
29
|
-
endTag(tag
|
27
|
+
startTag(tag, attrs)
|
28
|
+
writeComposites(te)
|
29
|
+
endTag(tag)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -4,25 +4,41 @@ module Serializer
|
|
4
4
|
|
5
5
|
class XMLSerializer
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
INDENT_SPACE = 2
|
8
|
+
|
9
|
+
def initialize(file)
|
10
|
+
@indent = 0
|
11
|
+
@lastStartTag = nil
|
12
|
+
@textContent = false
|
13
|
+
@file = file
|
13
14
|
end
|
14
15
|
|
15
16
|
def serialize(rootElement)
|
16
17
|
raise "Abstract class, overwrite method in subclass!"
|
17
18
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
|
20
|
+
def startTag(tag, attributes={})
|
21
|
+
@textContent = false
|
22
|
+
handleLastStartTag(false, true)
|
23
|
+
@lastStartTag = " "*@indent*INDENT_SPACE +
|
24
|
+
"<#{tag} "+attributes.keys.collect{|k| "#{k}=\"#{attributes[k]}\""}.join(" ")
|
25
|
+
@indent += 1
|
26
|
+
end
|
27
|
+
|
28
|
+
def endTag(tag)
|
29
|
+
@indent -= 1
|
30
|
+
unless handleLastStartTag(true, true)
|
31
|
+
output " "*@indent*INDENT_SPACE unless @textContent
|
32
|
+
output "</#{tag}>\n"
|
33
|
+
end
|
34
|
+
@textContent = false
|
35
|
+
end
|
22
36
|
|
23
|
-
|
24
|
-
|
25
|
-
|
37
|
+
def writeText(text)
|
38
|
+
handleLastStartTag(false, false)
|
39
|
+
output "#{text}"
|
40
|
+
@textContent = true
|
41
|
+
end
|
26
42
|
|
27
43
|
protected
|
28
44
|
|
@@ -52,8 +68,25 @@ class XMLSerializer
|
|
52
68
|
end
|
53
69
|
|
54
70
|
def containmentReferences(element)
|
55
|
-
|
71
|
+
@containmentReferences ||= {}
|
72
|
+
@containmentReferences[element.class] ||= eAllReferences(element).select{|r| r.containment}
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def handleLastStartTag(close, newline)
|
78
|
+
return false unless @lastStartTag
|
79
|
+
output @lastStartTag
|
80
|
+
output close ? "/>" : ">"
|
81
|
+
output "\n" if newline
|
82
|
+
@lastStartTag = nil
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
def output(text)
|
87
|
+
@file.write(text)
|
56
88
|
end
|
89
|
+
|
57
90
|
end
|
58
91
|
|
59
92
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__),"..","lib")
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'rgen/environment'
|
5
|
+
require 'metamodels/uml13_metamodel'
|
6
|
+
require 'instantiators/ea_instantiator'
|
7
|
+
require 'rgen/serializer/xmi11_serializer'
|
8
|
+
|
9
|
+
class EASerializerTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
MODEL_DIR = File.join(File.dirname(__FILE__),"testmodel")
|
12
|
+
TEST_DIR = File.join(File.dirname(__FILE__),"ea_serializer_test")
|
13
|
+
|
14
|
+
def test_serializer
|
15
|
+
envUML = RGen::Environment.new
|
16
|
+
File.open(MODEL_DIR+"/ea_testmodel.xml") { |f|
|
17
|
+
inst = EAInstantiator.new(envUML, EAInstantiator::ERROR)
|
18
|
+
inst.instantiate(f.read)
|
19
|
+
}
|
20
|
+
models = envUML.find(:class => UML13::Model)
|
21
|
+
assert_equal 1, models.size
|
22
|
+
|
23
|
+
File.open(TEST_DIR+"/ea_testmodel_regenerated.xml", "w") do |f|
|
24
|
+
ser = RGen::Serializer::XMI11Serializer.new(f)
|
25
|
+
ser.serialize(models.first, {:documentation => {:exporter => "Enterprise Architect", :exporterVersion => "2.5"}})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|