require 'rgen/instantiator/qualified_name_resolver' require 'rgen/instantiator/json_parser' module RGen module Instantiator # JsonInstantiator is used to create RGen models from JSON. # # Each JSON object needs to have an attribute "_class" which is used to find # the metamodel class to instantiate. The value of "_class" should be the # the relative qualified class name within the root package as a string. # # If the option "short_class_names" is set to true, unqualified class names can be used. # In this case, metamodel classes are searched in the metamodel root package first. # If this search is not successful, all subpackages will be searched for the class name. # class JsonInstantiator # Model elements will be created in evironment +env+, # classes are looked for in metamodel package module +mm+, # +options+ include: # short_class_names: if true subpackages will be searched for unqualifed class names (default: true) # ignore_keys: an array of json object key names which are to be ignored (default: none) # # The options are also passed to the underlying QualifiedNameResolver. # def initialize(env, mm, options={}) @env = env @mm = mm @options = options @short_class_names = !@options.has_key?(:short_class_names) || @options[:short_class_names] @ignore_keys = @options[:ignore_keys] || [] @unresolvedReferences = [] @classes = {} @classes_flat = {} mm.ecore.eAllClasses.each do |c| @classes[c.instanceClass.name.sub(mm.name+"::","")] = c @classes_flat[c.name] = c end @parser = JsonParser.new(self) end # Creates the elements described by the json string +str+. # Returns an array of ReferenceResolver::UnresolvedReference # describing the references which could not be resolved # # Options: # :root_elements: if an array is provided, it will be filled with the root elements # def instantiate(str, options={}) root = @parser.parse(str) if options[:root_elements].is_a?(Array) options[:root_elements].clear root.each{|r| options[:root_elements] << r} end resolver = QualifiedNameResolver.new(root, @options) resolver.resolveReferences(@unresolvedReferences) end def createObject(hash) className = hash["_class"] # hashes without a _class key are returned as is return hash unless className if @classes[className] clazz = @classes[className].instanceClass elsif @short_class_names && @classes_flat[className] clazz = @classes_flat[className].instanceClass else raise "class not found: #{className}" end hash.delete("_class") @ignore_keys.each do |k| hash.delete(k) end urefs = [] hash.keys.each do |k| f = eFeature(k, clazz) hash[k] = [hash[k]] if f.many && !hash[k].is_a?(Array) if f.is_a?(RGen::ECore::EReference) && !f.containment if f.many idents = hash[k] hash[k] = idents.collect do |i| proxy = RGen::MetamodelBuilder::MMProxy.new(i) urefs << ReferenceResolver::UnresolvedReference.new(nil, k, proxy) proxy end else ident = hash[k] ident = ident.first if ident.is_a?(Array) proxy = RGen::MetamodelBuilder::MMProxy.new(ident) hash[k] = proxy urefs << ReferenceResolver::UnresolvedReference.new(nil, k, proxy) end elsif f.eType.is_a?(RGen::ECore::EEnum) hash[k] = hash[k].to_sym elsif f.eType.instanceClassName == "Float" hash[k] = hash[k].to_f end end obj = @env.new(clazz, hash) urefs.each do |r| r.element = obj @unresolvedReferences << r end obj end private def eFeature(name, clazz) @eFeature ||= {} @eFeature[clazz] ||= {} unless @eFeature[clazz][name] feature = clazz.ecore.eAllStructuralFeatures.find{|f| f.name == name} raise "feature '#{name}' not found in class '#{clazz}'" unless feature end @eFeature[clazz][name] ||= feature end end end end