# File lib/rgen/transformer.rb, line 264
        def self.copy(from, to=nil)
    raise StandardError.new("Specify target class either directly as second parameter or using :to => <class>") \
      unless to.nil? || to.is_a?(Class) || (to.is_a?(Hash) && to[:to].is_a?(Class))
    to = to[:to] if to.is_a?(Hash) 
                transform(from, :to => to || from) do
      copy_features
                end
  end

  # Create copy rules for all classes of metamodel package (module) +from+ and its subpackages.

  # The target classes are the classes with the same name in the metamodel package

  # specified using named parameter :to. If no target metamodel is specified, source

  # and target classes will be the same.

  # The named parameter :except can be used to specify classes by qualified name for which 

  # no copy rules should be created. Qualified names are relative to the metamodel package

  # specified.

  #

  def self.copy_all(from, hash={})
    to = hash[:to] || from
    except = hash[:except]
    fromDepth = from.ecore.qualifiedName.split("::").size
    from.ecore.eAllClasses.each do |c|
      path = c.qualifiedName.split("::")[fromDepth..-1]
      next if except && except.include?(path.join("::"))
      copy c.instanceClass, :to => path.inject(to){|m,c| m.const_get(c)}
    end
  end
        
        # Define a transformer method for the current transformer class.

        # In contrast to regular Ruby methods, a method defined this way executes in the

        # context of the object currently being transformed.

        # 

        def self.method(name, &block)
                _methods[name.to_s] = block
        end
        

        # Creates a new transformer

        # Optionally an input and output Environment can be specified.

  # If an elementMap is provided (normally a Hash) this map will be used to lookup 

  # and store transformation results. This way results can be predefined

  # and it is possible to have several transformers work on the same result map.

        # 

        def initialize(env_in=nil, env_out=nil, elementMap=nil)
                @env_in = env_in
                @env_out = env_out
                @transformer_results = elementMap || {}
                @transformer_jobs = []
        end


        # Transforms a given model element according to the rules specified by means of

        # the Transformer.transform    class method.

        # 

        # The transformation result element is created in the output environment and returned.

        # In addition, the result is cached, i.e. a second invocation with the same parameter

        # object will return the same result object without any further evaluation of the 

        # transformation rules. Nil will be transformed into nil. Ruby "singleton" objects

        # +true+, +false+, numerics and symbols will be returned without any change. Ruby strings

        # will be duplicated with the result being cached.

        # 

        # The transformation input can be given as:

        # * a single object

        # * an array each element of which is transformed in turn

        # * a hash used as input to Environment#find with the result being transformed

        # 

        def trans(obj)
                if obj.is_a?(Hash)
                        raise StandardError.new("No input environment available to find model element.") unless @env_in
                        obj = @env_in.find(obj) 
                end
                return nil if obj.nil?
                return obj if obj.is_a?(TrueClass) or obj.is_a?(FalseClass) or obj.is_a?(Numeric) or obj.is_a?(Symbol)
                return @transformer_results[obj] if @transformer_results[obj]
                return @transformer_results[obj] = obj.dup if obj.is_a?(String)
                return obj.collect{|o| trans(o)}.compact if obj.is_a? Array
                raise StandardError.new("No transformer for class #{obj.class.name}") unless _transformerBlock(obj.class)
                block_desc = _evaluateCondition(obj)
                return nil unless block_desc
                @transformer_results[obj] = _instantiateTargetClass(obj, block_desc.target)
                # we will transform the properties later

                @transformer_jobs << TransformerJob.new(self, obj, block_desc)
                # if there have been jobs in the queue before, don't process them in this call

                # this way calls to trans are not nested; a recursive implementation 

                # may cause a "Stack level too deep" exception for large models

                return @transformer_results[obj] if @transformer_jobs.size > 1
                # otherwise this is the first call of trans, process all jobs here

                # more jobs will be added during job execution

                while @transformer_jobs.size > 0
                        @transformer_jobs.first.execute
                        @transformer_jobs.shift
                end
                @transformer_results[obj]
        end
        
  # Create the hash required as return value of the block given to the Transformer.transform method.

  # The hash will assign feature values of the source class to the features of the target class,

  # assuming the features of both classes are the same. If the :except named parameter specifies

  # an Array of symbols, the listed features are not copied by the hash. In order to easily manipulate

  # the resulting hash, a block may be given which should also return a feature assignmet hash. This

  # hash should be created manually and will extend/overwrite the automatically created hash.

  #

  def copy_features(options={})
    hash = {}
    @current_object.class.ecore.eAllStructuralFeatures.each do |f|
      next if f.derived
      next if options[:except] && options[:except].include?(f.name.to_sym)
      hash[f.name.to_sym] = trans(@current_object.send(f.name))
    end
    hash.merge!(yield) if block_given?
    hash
  end
  
        def _transformProperties(obj, block_desc) #:nodoc:

                old_object, @current_object = @current_object, obj
                block_result = instance_eval(&block_desc.block)
                raise StandardError.new("Transformer must return a hash") unless block_result.is_a? Hash
                @current_object = old_object
                _attributesFromHash(@transformer_results[obj], block_result)
        end
        
        class TransformerJob #:nodoc:

                def initialize(transformer, obj, block_desc)
                        @transformer, @obj, @block_desc = transformer, obj, block_desc
                end
                def execute
                        @transformer._transformProperties(@obj, @block_desc)
                end
        end

        # Each call which is not handled by the transformer object is passed to the object

        # currently being transformed.

        # If that object also does not respond to the call, it is treated as a transformer

        # method call (see Transformer.method).

        # 

        def method_missing(m, *args) #:nodoc:

                if @current_object.respond_to?(m)
                        @current_object.send(m, *args)
                else
                        _invokeMethod(m, *args)
                end
        end

        private
        
        # returns _transformer_blocks content for clazz or one of its superclasses 

        def _transformerBlock(clazz) # :nodoc:

                block = self.class._transformer_blocks[clazz]
                block = _transformerBlock(clazz.superclass) if block.nil? && clazz != Object
                block
        end
                
        # returns the first TransformationDescription for clazz or one of its superclasses

        # for which condition is true 

        def _evaluateCondition(obj, clazz=obj.class) # :nodoc:

                tb = self.class._transformer_blocks[clazz]
                block_description = nil
                if tb.is_a?(TransformationDescription)
                        # non-conditional

                        block_description = tb
                elsif tb
                        old_object, @current_object = @current_object, obj
                        tb.each_pair {|condition, block|
                                if condition.is_a?(Proc)
                                        result = instance_eval(&condition)
                                elsif condition.is_a?(Symbol)
                                        result = _invokeMethod(condition)
                                else
                                        result = condition
                                end
                                if result
                                        block_description = block 
                                        break
                                end
                        }
                        @current_object = old_object
                end
                block_description = _evaluateCondition(obj, clazz.superclass) if block_description.nil? && clazz != Object
                block_description
        end
        
        def _instantiateTargetClass(obj, target_desc) # :nodoc:

                old_object, @current_object = @current_object, obj
                if target_desc.is_a?(Proc)
                        target_class = instance_eval(&target_desc)
                elsif target_desc.is_a?(Symbol)
                        target_class = _invokeMethod(target_desc)
                else
                        target_class = target_desc
                end
                @current_object = old_object
                result = target_class.new
                @env_out << result if @env_out
                result
        end
        
        def _invokeMethod(m) # :nodoc:

                        raise StandardError.new("Method not found: #{m}") unless self.class._methods[m.to_s]
                        instance_eval(&self.class._methods[m.to_s])
        end
                
        def _attributesFromHash(obj, hash) # :nodoc:

                hash.delete(:class)
                hash.each_pair{|k,v|
                        obj.send("#{k}=", v)
                }
                obj
        end
        
end