rgen-xsd 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+