require 'mmgen/metamodel_generator' require 'andand' require 'tempfile' module Concrete class IndexBuilder include MMGen::MetamodelGenerator def initialize(mm, options={}) @mm = mm @identName = options[:identifierName] || "name" @ignoreReferences = options[:ignoreReferences] @moduleClassName = options[:moduleClassName] || "Module" end def indexMetamodel @indexMM ||= buildIndexMetamodel end def buildIndex(root) buildIndexInternal(root) end private def buildIndexMetamodel env = RGen::Environment.new allContainersCache = {} processedClasses = {} referencedClasses = [] @mm.ecore.eAllClasses.eReferences.each do |r| next if r.containment || r.derived next if @ignoreReferences.andand.call(r) next if processedClasses[r.eType] processedClasses[r.eType] = true allNamedSubclasses(r.eType).each do |c| referencedClasses |= ([c] + allContainers(c, allContainersCache)) end end package = env.new(RGen::ECore::EPackage, :name => "IndexMetamodel") indexElement = env.new(RGen::ECore::EClass, :name => "IndexElement", :ePackage => package) env.new(RGen::ECore::EAttribute, :name => "name", :eType => RGen::ECore::EString, :eContainingClass => indexElement) env.new(RGen::ECore::EReference, :name => "elements", :upperBound => -1, :containment => true, :eType => indexElement, :eContainingClass => indexElement) env.new(RGen::ECore::EClass, :name => @moduleClassName, :eSuperTypes => [indexElement], :ePackage => package) @mm.ecore.eAllClasses.each do |c| if !c.abstract && referencedClasses.include?(c) env.new(RGen::ECore::EClass, :name => c.name, :eSuperTypes => [indexElement], :ePackage => package) end end loadIndexMetamodel(package) end def allNamedSubclasses(clazz) if clazz.eAllStructuralFeatures.any?{|f| f.name == @identName} [clazz] + clazz.eAllSubTypes else clazz.eAllSubTypes.select{|t| t.eAllStructuralFeatures.any?{|f| f.name == @identName}} end end def loadIndexMetamodel(indexMMPackage) mmFile = Tempfile.new("index_metamodel") mmFile.close generateMetamodel(indexMMPackage, mmFile.path) containerName = "MMContainer"+self.object_id.to_s eval("module "+containerName+"; end") container = self.class.const_get(containerName) container.module_eval(File.read(mmFile.path)) mmFile.unlink container.const_get(indexMMPackage.name) end def buildIndexInternal(element) return unless indexMetamodel.const_defined?(element.class.ecore.name) return unless element.respond_to?(@identName) ie = indexMetamodel.const_get(element.class.ecore.name).new ie.name = element.getGeneric(@identName) element.class.ecore.eAllReferences.select{|r| r.containment}.each do |r| values = element.getGenericAsArray(r.name).compact values.each do |v| ie.addElements(buildIndexInternal(v)) end end ie end def containers(c) types = c.eAllReferences.select{|r| r.eOpposite && r.eOpposite.containment}.eType types + types.eAllSubTypes end def allContainers(c, cache) return cache[c] if cache[c] cache[c] = [] cons = containers(c) cache[c] = cons | cons.collect{|cn| allContainers(cn, cache)}.flatten end end end