rgen-xsd 0.1.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 +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
|
+
|