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) @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 rd.object.setGeneric(rd.attribute,newval) 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 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