rgen-xsd 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +4 -0
- data/MIT-LICENSE +21 -0
- data/README +16 -0
- data/Rakefile +45 -0
- data/lib/rgen/xsd/1.0/datatypes.xml +1117 -0
- data/lib/rgen/xsd/1.0/metamodel.rb +413 -0
- data/lib/rgen/xsd/1.0/rakefile +3 -0
- data/lib/rgen/xsd/1.0/readme.txt +6 -0
- data/lib/rgen/xsd/1.0/structures.xml +1235 -0
- data/lib/rgen/xsd/1.1/datatypes.xml +491 -0
- data/lib/rgen/xsd/1.1/metamodel.rb +488 -0
- data/lib/rgen/xsd/1.1/rakefile +3 -0
- data/lib/rgen/xsd/1.1/readme.txt +6 -0
- data/lib/rgen/xsd/1.1/structures.xml +1479 -0
- data/lib/rgen/xsd/metamodel_generator.rb +110 -0
- data/lib/rgen/xsd/metamodel_modification_helper.rb +41 -0
- data/lib/rgen/xsd/particle.rb +169 -0
- data/lib/rgen/xsd/simple_type.rb +87 -0
- data/lib/rgen/xsd/xml/readme.txt +6 -0
- data/lib/rgen/xsd/xml/xml.xsd +145 -0
- data/lib/rgen/xsd/xml_schema_metamodel_ext.rb +54 -0
- data/lib/rgen/xsd/xsd_instantiator.rb +83 -0
- data/lib/rgen/xsd/xsd_to_ecore.rb +325 -0
- data/lib/rgen/xsd/xsi_instantiator.rb +230 -0
- metadata +87 -0
@@ -0,0 +1,83 @@
|
|
1
|
+
require "rgen/xsd/xsi_instantiator"
|
2
|
+
require "rgen/instantiator/reference_resolver"
|
3
|
+
|
4
|
+
module RGen
|
5
|
+
module XSD
|
6
|
+
|
7
|
+
class XSDInstantiator
|
8
|
+
attr_reader :unresolved_refs
|
9
|
+
|
10
|
+
def initialize(env, mm)
|
11
|
+
@env = env
|
12
|
+
@mm = mm
|
13
|
+
@unresolved_refs = []
|
14
|
+
@resolver = RGen::Instantiator::ReferenceResolver.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def instantiate(file_name)
|
18
|
+
inst = XSIInstantiator.new(@mm, @env)
|
19
|
+
schema = inst.instantiate(file_name,
|
20
|
+
:root_class => XMLSchemaMetamodel::SchemaTYPE
|
21
|
+
)
|
22
|
+
urefs = inst.unresolved_refs
|
23
|
+
urefs.each do |ur|
|
24
|
+
ti = ur.proxy.targetIdentifier
|
25
|
+
href, name = inst.resolve_namespace(ti)
|
26
|
+
if href
|
27
|
+
ur.proxy.targetIdentifier = "#{href}:#{name}"
|
28
|
+
else
|
29
|
+
# either namespace not found (warning in resolve_namespace) or no namespace at all (not even default namespace)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
@unresolved_refs += urefs
|
33
|
+
(schema.element +
|
34
|
+
schema.group +
|
35
|
+
schema.complexType +
|
36
|
+
schema.simpleType +
|
37
|
+
schema.attribute +
|
38
|
+
schema.attributeGroup).each do |e|
|
39
|
+
if e.name
|
40
|
+
if schema.targetNamespace
|
41
|
+
@resolver.add_identifier("#{schema.targetNamespace}:#{e.name}", e)
|
42
|
+
else
|
43
|
+
@resolver.add_identifier(e.name, e)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def resolve(problems=nil)
|
50
|
+
@unresolved_refs = @resolver.resolve(unresolved_refs, :problems => [], :use_target_type => true)
|
51
|
+
add_missing_builtin_types
|
52
|
+
@unresolved_refs = @resolver.resolve(unresolved_refs, :problems => problems, :use_target_type => true)
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_missing_builtin_types
|
56
|
+
unresolved_refs.each do |ur|
|
57
|
+
if ur.proxy.targetIdentifier =~ /^http:\/\/www\.w3\.org\/2001\/XMLSchema:(\w+)$/
|
58
|
+
name = $1
|
59
|
+
if [ "anyType", "anySimpleType", "string", "normalizedString", "token", "language", "Name", "NCName",
|
60
|
+
"ID", "IDREF", "ENTITY", "NMTOKEN", "base64Binary", "hexBinary", "anyURI", "QName",
|
61
|
+
"NOTATION", "duration", "dateTime", "time", "date", "gYearMonth", "gYear", "gMonthDay",
|
62
|
+
"gDay", "gMonth", "IDREFS", "ENTITIES", "NMTOKENS", "float", "double",
|
63
|
+
"decimal", "integer", "nonPositiveInteger", "negativeInteger", "long", "int", "short",
|
64
|
+
"byte", "nonNegativeInteger", "unsignedLong", "unsignedInt", "unsignedShort",
|
65
|
+
"unsignedByte", "positiveInteger", "boolean" ].include?(name)
|
66
|
+
add_builtin_type(ur.proxy.targetIdentifier, name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_builtin_type(target_identifier, name)
|
73
|
+
@builtin_type_added ||= {}
|
74
|
+
return if @builtin_type_added[target_identifier]
|
75
|
+
type = @env.new(XMLSchemaMetamodel::TopLevelSimpleType, :name => name)
|
76
|
+
@resolver.add_identifier(target_identifier, type)
|
77
|
+
@builtin_type_added[target_identifier] = true
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,325 @@
|
|
1
|
+
require 'rgen/ecore/ecore'
|
2
|
+
require 'rgen/util/name_helper'
|
3
|
+
require 'rgen/transformer'
|
4
|
+
require 'rgen/xsd/particle'
|
5
|
+
require 'rgen/xsd/simple_type'
|
6
|
+
|
7
|
+
module RGen
|
8
|
+
module XSD
|
9
|
+
|
10
|
+
# Mapping Strategy:
|
11
|
+
#
|
12
|
+
# 1. Complex Types are mapped to classes
|
13
|
+
#
|
14
|
+
# * this is true even for Complex Types with simple content
|
15
|
+
# (because they may contain attributes and the simple value)
|
16
|
+
# * the name of the class is the name of the Complex Type or
|
17
|
+
# a derived name (TODO) in case of an anonymous Complex Type
|
18
|
+
# * if the Complex Type is abstract, the class will be abstract
|
19
|
+
#
|
20
|
+
# 1.1 Extension is mapped to supertype relationship
|
21
|
+
#
|
22
|
+
# * the extending Complex Type contributes features according to its content
|
23
|
+
# (simple or complex content, simple content for example contributes attribtes)
|
24
|
+
#
|
25
|
+
# 1.2 Restriction is mapped to supertype relationship
|
26
|
+
#
|
27
|
+
# * the restriction expresses a "is a" relationship
|
28
|
+
# ("it is a <super type> but with restrictions")
|
29
|
+
# in general, restrictions can not be expressed in ECore, so
|
30
|
+
# we accept to have a superset of the original constraints
|
31
|
+
# * in some cases however, the restricting Complex Type many also contribute features
|
32
|
+
# e.g. in case the base complex type has a 'any' wildcard, then the
|
33
|
+
# restricting type may narrow this by specifying specific elements
|
34
|
+
# * the implementation idea is to add all features introduced by the restriction, just
|
35
|
+
# in the same way as with the extension, but exclude any features which have
|
36
|
+
# already been defined in a super class (i.e. new features may be added but
|
37
|
+
# existing features can not be restricted)
|
38
|
+
#
|
39
|
+
# 1.3 Element Particles
|
40
|
+
#
|
41
|
+
# * element particles are collected recursively through the type's content model:
|
42
|
+
# - Group references are replaced by the target group's child and min/max occurrence
|
43
|
+
# from the Group reference are applied to that replacement
|
44
|
+
# - Element references are replaced by the target element and min/max occurrence
|
45
|
+
# from the Element reference are applied to that replacement
|
46
|
+
# - recursion for model groups "all", "sequence", "choice"
|
47
|
+
# * multiplicities are calculated from the element particles' min/max occurrence
|
48
|
+
# and the min/max occurrence of the group particles they are contained in:
|
49
|
+
# - the min multiplicity of an element within a group is the element's min multiplicity
|
50
|
+
# multiplied by the groups min multiplicity
|
51
|
+
# - in case of a "choice" group, if an element occurrs in only one of several choices,
|
52
|
+
# its min multiplicity is 0
|
53
|
+
# - the max multiplicity of an element within a group is the element's max multiplicity
|
54
|
+
# multiplied by the groups max multiplicity
|
55
|
+
# * particles referencing elements which are substitution group heads are augmented
|
56
|
+
# by one other particle for each substitutable element, min/max occurrences are the
|
57
|
+
# same as for the heading element
|
58
|
+
#
|
59
|
+
# 1.3.1 Element Particles with Complex Type are mapped to containment references
|
60
|
+
#
|
61
|
+
# 1.3.2 Element Particles with Simple Type are mapped to to attributes
|
62
|
+
#
|
63
|
+
# 1.4 Wildcards (any)
|
64
|
+
#
|
65
|
+
# * there can be one wildcard attribute for each class created from a complex type
|
66
|
+
# by convention, the attribute is called "anyObject"; the attribute type is EObject
|
67
|
+
#
|
68
|
+
# * 'any' particles are collected recursively through the type's content model just
|
69
|
+
# as with element particles; however, in the end all of them will be condensed into one
|
70
|
+
# the multiplicity is calculated in the same way as for element particles
|
71
|
+
#
|
72
|
+
# * Note: depending on the value of the "processContents" attribute, XML instances may
|
73
|
+
# be forced to use only existing types in the "any" section (value "strict"); this
|
74
|
+
# means that all possible target types of the attribute would be known; so in theory
|
75
|
+
# the target type could be a superclass of all possible complex types (using "strict",
|
76
|
+
# XML instances may either make use of a toplevel element definition or any complex
|
77
|
+
# type using the xsi:type attribute)
|
78
|
+
#
|
79
|
+
# 1.5 Attributes are mapped to attributes
|
80
|
+
#
|
81
|
+
# 2. Simples Types are mapped to datatypes
|
82
|
+
#
|
83
|
+
# 2.1 Simple Types with an enumeration restriction are mapped to enums
|
84
|
+
#
|
85
|
+
# Mapping Variants:
|
86
|
+
#
|
87
|
+
# * Use content of complex type restrictions to create the actual class
|
88
|
+
# this means that superclasses can not also contain the same features
|
89
|
+
# so either the superclasses must be abstract or the superclass must be split into an
|
90
|
+
# abstract class and a concrete class derived from it
|
91
|
+
# UseCase: Facets in XSD, e.g. a NumFacet has a value of type integer
|
92
|
+
#
|
93
|
+
# * Use substitution groups to derive a class hierarchy
|
94
|
+
# In XSD, elements which are substitutes must have a derived type which is either a restriction
|
95
|
+
# or extension of the group head element or the same type
|
96
|
+
# Problem: substitution is defined on roles (elements), there can be substitutions which
|
97
|
+
# only differ in the role/element name, not in the type. As a solution there could be
|
98
|
+
# dedicated subclasses for this situation which hold the role information
|
99
|
+
# UseCase: Facets in XSD
|
100
|
+
#
|
101
|
+
class XSDToEcoreTransformer < RGen::Transformer
|
102
|
+
include RGen::ECore
|
103
|
+
include RGen::Util::NameHelper
|
104
|
+
include Particle
|
105
|
+
include SimpleType
|
106
|
+
|
107
|
+
# Options:
|
108
|
+
#
|
109
|
+
# :class_name_provider:
|
110
|
+
# a proc which receives a ComplexType and should return the EClass name
|
111
|
+
#
|
112
|
+
# :feature_name_provider:
|
113
|
+
# a proc which receives an Element or Attribute and should return the EStructuralFeature name
|
114
|
+
#
|
115
|
+
# :enum_name_provider:
|
116
|
+
# a proc which receives a SimpleType and should return the EEnum name
|
117
|
+
#
|
118
|
+
def initialize(env_in, env_out, options={})
|
119
|
+
super(env_in, env_out)
|
120
|
+
@package_by_source_element = {}
|
121
|
+
@class_name_provider = options[:class_name_provider] || proc do |type|
|
122
|
+
firstToUpper(type.name || type.containingElement.name+"TYPE")
|
123
|
+
end
|
124
|
+
@feature_name_provider = options[:feature_name_provider] || proc do |ea|
|
125
|
+
firstToLower(ea.name)
|
126
|
+
end
|
127
|
+
@enum_name_provider = options[:enum_name_provider] || proc do |type|
|
128
|
+
uniq_classifier_name(@package_by_source_element[type],
|
129
|
+
type.name ? firstToUpper(type.name)+"Enum" :
|
130
|
+
type.containingAttribute ? firstToUpper(type.containingAttribute.effectiveAttribute.name)+"Enum" :
|
131
|
+
"Unknown")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def transform
|
136
|
+
root = @env_out.new(RGen::ECore::EPackage, :name => "MM")
|
137
|
+
schemas = @env_in.find(:class => XMLSchemaMetamodel::SchemaTYPE)
|
138
|
+
schemas.each do |s|
|
139
|
+
tns = s.targetNamespace || "Default"
|
140
|
+
name = tns.sub(/http:\/\/(www\.)?/,"").split("/").
|
141
|
+
collect{|p| p.split(/\W/).collect{|t| firstToUpper(t)}.join }.join
|
142
|
+
puts "empty package name" if name.strip.empty?
|
143
|
+
p = package_by_name(name, root, tns)
|
144
|
+
child_elements(s).each{|e| @package_by_source_element[e] = p}
|
145
|
+
end
|
146
|
+
trans(schemas.complexType)
|
147
|
+
trans(schemas.element.effectiveType.select do |t|
|
148
|
+
t.is_a?(XMLSchemaMetamodel::ComplexType) ||
|
149
|
+
build_type_desc(t).type.is_a?(Array)
|
150
|
+
end)
|
151
|
+
|
152
|
+
# remove duplicate features created by restrictions
|
153
|
+
# TODO: find a way to do this while transformation or make sure unused elements will also be removed (EClasses, EAnnotation, ..)
|
154
|
+
root.eAllClasses.each do |c|
|
155
|
+
super_features = c.eAllStructuralFeatures - c.eStructuralFeatures
|
156
|
+
c.eStructuralFeatures.each do |f|
|
157
|
+
if super_features.any?{|sf| sf.name == f.name}
|
158
|
+
f.eContainingClass = nil
|
159
|
+
f.eType = nil
|
160
|
+
f.eOpposite = nil if f.is_a?(RGen::ECore::EReference)
|
161
|
+
@env_out.delete(f)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# unique classifier names
|
167
|
+
# TODO: smarter way to modify names
|
168
|
+
root.eAllSubpackages.each do |p|
|
169
|
+
names = {}
|
170
|
+
p.eClassifiers.each do |c|
|
171
|
+
while names[c.name]
|
172
|
+
c.name = c.name+"X"
|
173
|
+
end
|
174
|
+
names[c.name] = true
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
root
|
179
|
+
end
|
180
|
+
|
181
|
+
def package_by_name(name, root, ns)
|
182
|
+
@package_by_name ||= {}
|
183
|
+
@package_by_name[name] ||= @env_out.new(RGen::ECore::EPackage, :name => name, :eSuperPackage => root,
|
184
|
+
:eAnnotations => [create_annotation("xmlName", ns)])
|
185
|
+
end
|
186
|
+
|
187
|
+
transform XMLSchemaMetamodel::ComplexType, :to => EClass do
|
188
|
+
_features = []
|
189
|
+
if complexContent.andand.extension
|
190
|
+
_particles = element_particles(complexContent.extension)
|
191
|
+
elsif complexContent.andand.restriction
|
192
|
+
_particles = element_particles(complexContent.restriction)
|
193
|
+
else
|
194
|
+
_particles = element_particles(@current_object)
|
195
|
+
end
|
196
|
+
_particles = add_substitution_particles(_particles)
|
197
|
+
_particles.each do |p|
|
198
|
+
if p.kind == :element
|
199
|
+
e = p.node
|
200
|
+
if e.effectiveType.is_a?(XMLSchemaMetamodel::ComplexType)
|
201
|
+
fn = @feature_name_provider.call(e)
|
202
|
+
_features << @env_out.new(RGen::ECore::EReference, :name => fn, :containment => true,
|
203
|
+
:upperBound => p.maxOccurs == "unbounded" ? -1 : p.maxOccurs,
|
204
|
+
:lowerBound => p.minOccurs,
|
205
|
+
:eType => trans(e.effectiveType))
|
206
|
+
if fn != e.name
|
207
|
+
_features.last.eAnnotations = [create_annotation("xmlName", e.name)]
|
208
|
+
end
|
209
|
+
elsif e.effectiveType.is_a?(XMLSchemaMetamodel::SimpleType)
|
210
|
+
_features << create_attribute(e)
|
211
|
+
end
|
212
|
+
else
|
213
|
+
# any
|
214
|
+
_features << @env_out.new(RGen::ECore::EAttribute, :name => "anyObject",
|
215
|
+
:lowerBound => p.minOccurs,
|
216
|
+
:upperBound => p.maxOccurs == "unbounded" ? -1 : p.maxOccurs,
|
217
|
+
:eType => RGen::ECore::ERubyObject)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
allAttributes.effectiveAttribute.each do |a|
|
221
|
+
_features << create_attribute(a)
|
222
|
+
end
|
223
|
+
if mixed
|
224
|
+
_features << @env_out.new(RGen::ECore::EAttribute, :name => "text",
|
225
|
+
:eType => RGen::ECore::EString,
|
226
|
+
:eAnnotations => [create_annotation("simpleContent", "true")])
|
227
|
+
end
|
228
|
+
if simpleContent.andand.extension.andand.base || simpleContent.andand.restriction.andand.base
|
229
|
+
_features << @env_out.new(RGen::ECore::EAttribute, :name => "simpleValue",
|
230
|
+
:eType => get_datatype(simple_content_base_type(@current_object)),
|
231
|
+
:eAnnotations => [create_annotation("simpleContent", "true")])
|
232
|
+
end
|
233
|
+
{ :name => @class_name_provider.call(@current_object),
|
234
|
+
:abstract => abstract,
|
235
|
+
:eStructuralFeatures => _features,
|
236
|
+
:eSuperTypes => trans([complexContent.andand.extension.andand.base ||
|
237
|
+
complexContent.andand.restriction.andand.base].compact.select{|t| t.is_a?(XMLSchemaMetamodel::ComplexType)}),
|
238
|
+
:ePackage => @package_by_source_element[@current_object],
|
239
|
+
:eAnnotations => name ? [create_annotation("xmlName", name)] : []
|
240
|
+
}
|
241
|
+
end
|
242
|
+
|
243
|
+
def simple_content_base_type(type)
|
244
|
+
base = type.simpleContent.andand.extension.andand.base || type.simpleContent.andand.restriction.andand.base
|
245
|
+
while base.is_a?(XMLSchemaMetamodel::ComplexType)
|
246
|
+
base = base.simpleContent.andand.extension.andand.base || base.simpleContent.andand.restriction.andand.base
|
247
|
+
end
|
248
|
+
base
|
249
|
+
end
|
250
|
+
|
251
|
+
def create_annotation(key, value)
|
252
|
+
@env_out.new(RGen::ECore::EAnnotation, :source => "xsd", :details =>
|
253
|
+
[@env_out.new(RGen::ECore::EStringToStringMapEntry, :key => key, :value => value)])
|
254
|
+
end
|
255
|
+
|
256
|
+
def create_attribute(e)
|
257
|
+
td = build_type_desc(e.effectiveType)
|
258
|
+
fn = @feature_name_provider.call(e)
|
259
|
+
result = @env_out.new(RGen::ECore::EAttribute, :name => fn,
|
260
|
+
:lowerBound => td.minOccurs,
|
261
|
+
:upperBound => td.maxOccurs,
|
262
|
+
:eType => get_datatype(e.effectiveType))
|
263
|
+
if fn != e.name
|
264
|
+
result.eAnnotations = [create_annotation("xmlName", e.name)]
|
265
|
+
end
|
266
|
+
result
|
267
|
+
end
|
268
|
+
|
269
|
+
def get_datatype(type)
|
270
|
+
td = build_type_desc(type)
|
271
|
+
case td.type
|
272
|
+
when :string
|
273
|
+
RGen::ECore::EString
|
274
|
+
when :int
|
275
|
+
RGen::ECore::EInt
|
276
|
+
when :float
|
277
|
+
RGen::ECore::EFloat
|
278
|
+
when :boolean
|
279
|
+
RGen::ECore::EBoolean
|
280
|
+
when :object
|
281
|
+
RGen::ECore::ERubyObject
|
282
|
+
when Array
|
283
|
+
trans(type)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
transform XMLSchemaMetamodel::SimpleType, :to => EEnum do
|
288
|
+
_literals = build_type_desc(@current_object).type
|
289
|
+
raise "not an enum: #{@current_object.class}" unless _literals.is_a?(Array)
|
290
|
+
{ :name => @enum_name_provider.call(@current_object),
|
291
|
+
:eLiterals => _literals.collect{|l| @env_out.new(RGen::ECore::EEnumLiteral, :name => l)},
|
292
|
+
:ePackage => @package_by_source_element[@current_object]
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
def uniq_classifier_name(package, base)
|
297
|
+
try = base
|
298
|
+
index = 2
|
299
|
+
while package.eClassifiers.any?{|c| c.name == try}
|
300
|
+
try = base + index.to_s
|
301
|
+
index += 1
|
302
|
+
end
|
303
|
+
try
|
304
|
+
end
|
305
|
+
|
306
|
+
def child_elements(element, opts={})
|
307
|
+
result = []
|
308
|
+
element.class.ecore.eAllReferences.each do |r|
|
309
|
+
if r.containment
|
310
|
+
element.getGenericAsArray(r.name).each do |e|
|
311
|
+
if !opts[:class] || e.is_a?(opts[:class])
|
312
|
+
result << e
|
313
|
+
end
|
314
|
+
result += child_elements(e, opts)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
result
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'andand'
|
3
|
+
require 'rgen/ecore/ecore'
|
4
|
+
require 'rgen/instantiator/reference_resolver'
|
5
|
+
|
6
|
+
module RGen
|
7
|
+
module XSD
|
8
|
+
|
9
|
+
class XSIInstantiator
|
10
|
+
attr_reader :unresolved_refs
|
11
|
+
attr_reader :namespaces
|
12
|
+
|
13
|
+
def initialize(mm, env)
|
14
|
+
@mm = mm
|
15
|
+
@env = env
|
16
|
+
@classes_by_xml_name = nil
|
17
|
+
@namespaces = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def instantiate(file, options={})
|
21
|
+
@unresolved_refs = []
|
22
|
+
root = nil
|
23
|
+
root_class = options[:root_class].andand.ecore || root_class(doc.root.name)
|
24
|
+
File.open(file) do |f|
|
25
|
+
doc = Nokogiri::XML(f)
|
26
|
+
@namespaces = doc.root.namespace_definitions
|
27
|
+
root =instantiate_node(doc.root, root_class)
|
28
|
+
end
|
29
|
+
root
|
30
|
+
end
|
31
|
+
|
32
|
+
def resolve_namespace(str)
|
33
|
+
if str =~ /:/
|
34
|
+
prefix, name = str.split(":")
|
35
|
+
else
|
36
|
+
prefix, name = nil, str
|
37
|
+
end
|
38
|
+
# the default namespace has a prefix of nil
|
39
|
+
href = namespaces.find{|ns| ns.prefix == prefix}.andand.href
|
40
|
+
# built in xml schema namespace
|
41
|
+
if !href
|
42
|
+
if prefix == "xml"
|
43
|
+
href = "http://www.w3.org/XML/1998/namespace"
|
44
|
+
elsif prefix
|
45
|
+
puts "WARN: Can not resolve namespace prefix #{prefix}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
[href, name]
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def xsi_type_value(node)
|
54
|
+
node.attribute_nodes.find{|n| is_xsi_type?(n)}.andand.text
|
55
|
+
end
|
56
|
+
|
57
|
+
def instantiate_node(node, eclass)
|
58
|
+
element = eclass.instanceClass.new
|
59
|
+
@env << element
|
60
|
+
set_attribute_values(element, node)
|
61
|
+
simple_content = ""
|
62
|
+
can_take_any = eclass.eAllAttributes.any?{|a| a.name == "anyObject"}
|
63
|
+
node.children.each do |c|
|
64
|
+
if c.text?
|
65
|
+
simple_content << c.text
|
66
|
+
elsif c.element?
|
67
|
+
feats = features_by_xml_name(c.name).select{|f|
|
68
|
+
f.eContainingClass == element.class.ecore ||
|
69
|
+
f.eContainingClass.eAllSubTypes.include?(element.class.ecore)
|
70
|
+
}
|
71
|
+
if feats.size == 1
|
72
|
+
begin
|
73
|
+
if feats.first.is_a?(RGen::ECore::EReference)
|
74
|
+
element.setOrAddGeneric(feats.first.name,
|
75
|
+
instantiate_node(c, reference_target_type(feats.first, xsi_type_value(c))))
|
76
|
+
else
|
77
|
+
element.setOrAddGeneric(feats.first.name, value_from_string(element, feats.first, c.text))
|
78
|
+
end
|
79
|
+
rescue Exception => e
|
80
|
+
puts "Line: #{node.line}: #{e}"
|
81
|
+
end
|
82
|
+
else
|
83
|
+
if can_take_any
|
84
|
+
begin
|
85
|
+
# currently the XML node is added to the model
|
86
|
+
element.setOrAddGeneric("anyObject", c)
|
87
|
+
rescue Exception => e
|
88
|
+
puts "Line: #{node.line}: #{e}"
|
89
|
+
end
|
90
|
+
else
|
91
|
+
problem "could not determine reference for tag #{c.name}, #{feats.size} options", node
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
if simple_content.strip.size > 0
|
97
|
+
set_simple_content(element, simple_content.strip)
|
98
|
+
end
|
99
|
+
element
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_simple_content(element, value)
|
103
|
+
a = element.class.ecore.eAllAttributes.find{|a| annotation_value(a, "simpleContent") == "true"}
|
104
|
+
if a
|
105
|
+
element.setGeneric(a.name, value_from_string(element, a, value))
|
106
|
+
else
|
107
|
+
raise "could not set simple content for element #{element.class.name}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def is_xsi_type?(attr_node)
|
112
|
+
attr_node.namespace.andand.href == "http://www.w3.org/2001/XMLSchema-instance" && attr_node.node_name == "type"
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_attribute_values(element, node)
|
116
|
+
node.attribute_nodes.each do |attrnode|
|
117
|
+
next if is_xsi_type?(attrnode)
|
118
|
+
name = attrnode.node_name
|
119
|
+
feats = (features_by_xml_name(name) || []).select{|f|
|
120
|
+
f.eContainingClass == element.class.ecore ||
|
121
|
+
f.eContainingClass.eAllSubTypes.include?(element.class.ecore)}
|
122
|
+
if feats.size == 1
|
123
|
+
f = feats.first
|
124
|
+
str = node.attr(name)
|
125
|
+
if f.many
|
126
|
+
# list datatype implies whitespace handling method "collapse",
|
127
|
+
# removing white space in the front and back and reducing multiple whitespaces to one
|
128
|
+
# list items are separated by spaces
|
129
|
+
str.strip.split(/\s+/).each do |s|
|
130
|
+
element.addGeneric(f.name, value_from_string(element, f, s))
|
131
|
+
end
|
132
|
+
else
|
133
|
+
element.setGeneric(f.name, value_from_string(element, f, str))
|
134
|
+
end
|
135
|
+
elsif name == "schemaLocation" || name == "noNamespaceSchemaLocation"
|
136
|
+
# ignore, this may occure with any XML element
|
137
|
+
else
|
138
|
+
problem "could not determine feature for attribute #{name}, #{feats.size} options", node
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def value_from_string(element, f, str)
|
144
|
+
if f.is_a?(RGen::ECore::EAttribute)
|
145
|
+
case f.eType
|
146
|
+
when RGen::ECore::EInt
|
147
|
+
str.to_i
|
148
|
+
when RGen::ECore::EFloat
|
149
|
+
str.to_f
|
150
|
+
when RGen::ECore::EEnum
|
151
|
+
str.to_sym
|
152
|
+
when RGen::ECore::EBoolean
|
153
|
+
(str == "true")
|
154
|
+
else
|
155
|
+
str
|
156
|
+
end
|
157
|
+
else
|
158
|
+
proxy = RGen::MetamodelBuilder::MMProxy.new(str)
|
159
|
+
@unresolved_refs <<
|
160
|
+
RGen::Instantiator::ReferenceResolver::UnresolvedReference.new(element, f.name, proxy)
|
161
|
+
proxy
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def reference_target_type(ref, typename)
|
166
|
+
if typename
|
167
|
+
href, name = resolve_namespace(typename)
|
168
|
+
href ||= "Default"
|
169
|
+
types = (ref.eType.eAllSubTypes + [ref.eType]) & classes_by_xml_name(name).select{|c| xml_name(c.ePackage) == href}
|
170
|
+
if types.size == 1
|
171
|
+
types.first
|
172
|
+
elsif types.size > 1
|
173
|
+
raise "ambiguous type name #{typename}: #{types.collect{|t| t.name}.join(",")}"
|
174
|
+
else
|
175
|
+
raise "type name #{typename} not found"
|
176
|
+
end
|
177
|
+
else
|
178
|
+
ref.eType
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def root_class(tag_name)
|
183
|
+
classes = classes_by_xml_name(tag_name) || []
|
184
|
+
if classes.size == 1
|
185
|
+
classes.first
|
186
|
+
else
|
187
|
+
raise "could not determine root class, #{classes.size} options"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def problem(desc, node)
|
192
|
+
raise desc + " at [#{node.name}]"
|
193
|
+
end
|
194
|
+
|
195
|
+
def classes_by_xml_name(name)
|
196
|
+
return @classes_by_xml_name[name] || [] if @classes_by_xml_name
|
197
|
+
@classes_by_xml_name = {}
|
198
|
+
@mm.ecore.eAllClasses.each do |c|
|
199
|
+
n = xml_name(c)
|
200
|
+
@classes_by_xml_name[n] ||= []
|
201
|
+
@classes_by_xml_name[n] << c
|
202
|
+
end
|
203
|
+
@classes_by_xml_name[name]
|
204
|
+
end
|
205
|
+
|
206
|
+
def features_by_xml_name(name)
|
207
|
+
return @features_by_xml_name[name] || [] if @features_by_xml_name
|
208
|
+
@features_by_xml_name = {}
|
209
|
+
@mm.ecore.eAllClasses.eStructuralFeatures.each do |f|
|
210
|
+
n = xml_name(f)
|
211
|
+
@features_by_xml_name[n] ||= []
|
212
|
+
@features_by_xml_name[n] << f
|
213
|
+
end
|
214
|
+
@features_by_xml_name[name]
|
215
|
+
end
|
216
|
+
|
217
|
+
def xml_name(o)
|
218
|
+
annotation_value(o, "xmlName") || o.name
|
219
|
+
end
|
220
|
+
|
221
|
+
def annotation_value(o, key)
|
222
|
+
anno = o.eAnnotations.find{|a| a.source == "xsd"}
|
223
|
+
anno.andand.details.andand.find{|d| d.key == key}.andand.value
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|