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
|
require 'rgen/ecore/ecore'
require 'rgen/instantiator/abstract_xml_instantiator'
require 'rgen/array_extensions'
class XMI11Instantiator < AbstractXMLInstantiator
include RGen::ECore
ResolverDescription = Struct.new(:object, :attribute, :value, :many)
INFO = 0
WARN = 1
ERROR = 2
def initialize(env, fix_map={}, loglevel=ERROR)
@env = env
@fix_map = fix_map
@loglevel = loglevel
@rolestack = []
@elementstack = []
end
def add_metamodel(ns, mod)
@ns_module_map ||={}
@ns_module_map[ns] = mod
end
def instantiate(str)
@resolver_descs = []
@element_by_id = {}
super(str, 1000)
@resolver_descs.each do |rd|
if rd.many
newval = rd.value.split(" ").collect{|v| @element_by_id[v]}
else
newval = @element_by_id[rd.value]
end
log WARN, "Could not resolve reference #{rd.attribute} on #{rd.object}" unless newval
begin
rd.object.setGeneric(rd.attribute,newval)
rescue Exception
log WARN, "Could not set reference #{rd.attribute} on #{rd.object}"
end
end
end
def start_tag(prefix, tag, namespaces, attributes)
if tag =~ /\w+\.(\w+)/
# XMI role
role_name = map_feature_name($1) || $1
eRef = @elementstack.last && eAllReferences(@elementstack.last).find{|r|r.name == role_name}
log WARN, "No reference found for #{role_name} on #{@elementstack.last}" unless eRef
@rolestack.push eRef
elsif attributes["xmi.idref"]
# reference
rd = ResolverDescription.new
rd.object = @elementstack.last
rd.attribute = @rolestack.last.name
rd.value = attributes["xmi.idref"]
rd.many = @rolestack.last.many
@resolver_descs << rd
@elementstack.push nil
else
# model element
value = map_tag(tag, attributes) || tag
if value.is_a?(String)
mod = @ns_module_map[namespaces[prefix]]
unless mod
log WARN, "Ignoring tag #{tag}"
return
end
value = mod.const_get(value).new
end
@env << value
eRef = @rolestack.last
if eRef && eRef.many
@elementstack.last.addGeneric(eRef.name, value)
elsif eRef
@elementstack.last.setGeneric(eRef.name, value)
end
@elementstack.push value
end
end
def end_tag(prefix, tag)
if tag =~ /\w+\.(\w+)/
@rolestack.pop
else
@elementstack.pop
end
end
def set_attribute(attr, value)
return unless @elementstack.last
if attr == "xmi.id"
@element_by_id[value] = @elementstack.last
else
attr_name = map_feature_name(attr) || attr
eFeat = eAllStructuralFeatures(@elementstack.last).find{|a| a.name == attr_name}
unless eFeat
log WARN, "No structural feature found for #{attr_name} on #{@elementstack.last}"
return
end
if eFeat.is_a?(RGen::ECore::EReference)
if map_feature_value(attr_name, value).is_a?(eFeat.eType.instanceClass)
@elementstack.last.setGeneric(attr_name, map_feature_value(attr_name, value))
else
rd = ResolverDescription.new
rd.object = @elementstack.last
rd.attribute = attr_name
rd.value = value
rd.many = eFeat.many
@resolver_descs << rd
end
else
value = map_feature_value(attr_name, value) || value
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
value = value.to_f if eFeat.eType == EFloat
value = value.to_sym if eFeat.eType.is_a?(EEnum)
@elementstack.last.setGeneric(attr_name, value)
end
end
end
private
def map_tag(tag, attributes)
tag_map = @fix_map[:tags] || {}
value = tag_map[tag]
if value.is_a?(Proc)
value.call(tag, attributes)
else
value
end
end
def map_feature_name(name)
name_map = @fix_map[:feature_names] || {}
name_map[name]
end
def map_feature_value(attr_name, value)
value_map = @fix_map[:feature_values] || {}
map = value_map[attr_name]
if map.is_a?(Hash)
map[value]
elsif map.is_a?(Proc)
map.call(value)
end
end
def log(level, msg)
puts %w(INFO WARN ERROR)[level] + ": " + msg if level >= @loglevel
end
def eAllReferences(element)
@eAllReferences ||= {}
@eAllReferences[element.class] ||= element.class.ecore.eAllReferences
end
def eAllStructuralFeatures(element)
@eAllStructuralFeatures ||= {}
@eAllStructuralFeatures[element.class] ||= element.class.ecore.eAllStructuralFeatures
end
end
|