1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
require 'rgen/ecore/ecore'
require 'rgen/instantiator/abstract_xml_instantiator'
require 'rgen/array_extensions'
class ECoreXMLInstantiator < AbstractXMLInstantiator
include RGen::ECore
INFO = 0
WARN = 1
ERROR = 2
def initialize(env, loglevel=ERROR)
@env = env
@rolestack = []
@elementstack = []
@element_by_id = {}
@loglevel = loglevel
end
def start_tag(prefix, tag, namespaces, attributes)
eRef = nil
if @elementstack.last
eRef = eAllReferences(@elementstack.last).find{|r|r.name == tag}
if eRef
if attributes["xsi:type"] && attributes["xsi:type"] =~ /ecore:(\w+)/
class_name = $1
attributes.delete("xsi:type")
else
class_name = eRef.eType.name
end
else
raise "Reference not found: #{tag} on #{@elementstack.last}"
end
else
class_name = tag
end
eClass = RGen::ECore.ecore.eClassifiers.find{|c| c.name == class_name}
if eClass
obj = RGen::ECore.const_get(class_name).new
if attributes["xmi:id"]
@element_by_id[attributes["xmi:id"]] = obj
attributes.delete("xmi:id")
end
if eRef
if eRef.many
@elementstack.last.addGeneric(eRef.name, obj)
else
@elementstack.last.setGeneric(eRef.name, obj)
end
end
@env << obj
@elementstack.push obj
else
log WARN, "Class not found: #{class_name}"
@elementstack.push nil
end
attributes.each_pair do |attr, value|
set_attribute_internal(attr, value)
end
end
def end_tag(prefix, tag)
@elementstack.pop
end
ResolverDescription = Struct.new(:object, :attribute, :value)
def set_attribute(attr, value)
# do nothing, already handled by start_tag/set_attribute_internal
end
def set_attribute_internal(attr, value)
return unless @elementstack.last
eFeat = eAllStructuralFeatures(@elementstack.last).find{|a| a.name == attr}
if eFeat.is_a?(EReference)
rd = ResolverDescription.new
rd.object = @elementstack.last
rd.attribute = attr
rd.value = value
@resolver_descs << rd
elsif eFeat
value = true if value == "true" && eFeat.eType == EBoolean
value = false if value == "false" && eFeat.eType == EBoolean
value = value.to_i if eFeat.eType == EInt || eFeat.eType == ELong
@elementstack.last.setGeneric(attr, value)
else
log WARN, "Feature not found: #{attr} on #{@elementstack.last}"
end
end
def instantiate(str)
@resolver_descs = []
# puts "Instantiating ..."
super(str, 1000)
rootpackage = @env.find(:class => EPackage).first
# puts "Resolving ..."
@resolver_descs.each do |rd|
refed = find_referenced(rootpackage, rd.value)
feature = eAllStructuralFeatures(rd.object).find{|f| f.name == rd.attribute}
raise StandardError.new("StructuralFeature not found: #{rd.attribute}") unless feature
if feature.many
rd.object.setGeneric(feature.name, refed)
else
rd.object.setGeneric(feature.name, refed.first)
end
end
end
def eAllReferences(element)
@eAllReferences ||= {}
@eAllReferences[element.class] ||= element.class.ecore.eAllReferences
end
def eAllAttributes(element)
@eAllAttributes ||= {}
@eAllAttributes[element.class] ||= element.class.ecore.eAllAttributes
end
def eAllStructuralFeatures(element)
@eAllStructuralFeatures ||= {}
@eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures
end
def find_referenced(context, desc)
desc.split(/\s+/).collect do |r|
if r =~ /^#([^\/]+)$/
@element_by_id[$1]
elsif r =~ /^#\/\d*\/([\w\/]+)/
find_in_context(context, $1.split('/'))
elsif r =~ /#\/\/(\w+)$/
case $1
when "EString"; RGen::ECore::EString
when "EInt"; RGen::ECore::EInt
when "ELong"; RGen::ECore::ELong
when "EBoolean"; RGen::ECore::EBoolean
when "EFloat"; RGen::ECore::EFloat
when "EJavaObject"; RGen::ECore::EJavaObject
when "EJavaClass"; RGen::ECore::EJavaClass
end
end
end.compact
end
def find_in_context(context, desc_elements)
if context.is_a?(EPackage)
r = (context.eClassifiers + context.eSubpackages).find{|c| c.name == desc_elements.first}
elsif context.is_a?(EClass)
r = context.eStructuralFeatures.find{|s| s.name == desc_elements.first}
else
raise StandardError.new("Don't know how to find #{desc_elements.join('/')} in context #{context}")
end
if r
if desc_elements.size > 1
find_in_context(r, desc_elements[1..-1])
else
r
end
else
log WARN, "Can not follow path, element #{desc_elements.first} not found within #{context}(#{context.name})"
end
end
def log(level, msg)
puts %w(INFO WARN ERROR)[level] + ": " + msg if level >= @loglevel
end
end
|