
# This class exists for parsing functionality only.
# The derived Connector will get a static Array with all Deployments weaved in
# and an accessor method: get_all_deployments.
class Connector

  # Logging capabilities for Connector-class itself, because it is only static
  class << self
    include Logging::Log
  end
  
  # temporal static variable
  @@active_deploy = nil
  # All deployments. Teamclass => Array of deployments
  ClassDeployments = Hash.new
  # RoleClass of BaseClass. TeamClass => [BaseClass => RoleClass]
  TeamRoleclassOf = Hash.new
  # This regular expression is used to split namespaces
  NamespaceRE = /::/

  # This singleton method is called only by derived classes from singleton
  # methods. Self must be a derived class!
  # Returns a list of all deployments that are suitable for the
  # connectorclass (all deployments climbing from connectorclass until
  # Connector).
  # 
  # [return] list of deployments.
  def Connector.get_all_deployments()
    result = Array.new
    connector = self
    while (connector != Connector)
      deploys = ClassDeployments[connector]
      if (!deploys.nil?)
        result = result.concat(deploys)
      end
      connector = connector.superclass
    end
    return result
  end

  # Add deployment in the context of a specific Connector.
  # This deployment is remembered and returned by get_all_deployments
  # called from given connector.
  #
  # [connector] the connector in which the deployment is active
  # [deployment] the deployment to remember
  # [return] nil
  def Connector.add_deployment(connector, deployment)
    deployments = ClassDeployments[connector]
    if (deployments.nil?)
      deployments = Array.new
      ClassDeployments[connector] = deployments
      ClassDeployments.rehash
    end
    deployments.push(deployment)
  end

  # Returns the roleclass of a given baseclass, if called from
  # a connector.
  # [baseclass] the relational baseclass
  # [return] roleclass of the given baseclass in context of this connector(self) or nil.
  def Connector.get_roleclass_of(baseclass)
    result = nil
    connector= self
    savebase = baseclass
    #climb in connector hierarchy until Connector class itself.
    while (connector!=Connector and result.nil?)
      teamhash = TeamRoleclassOf[connector]
      #climb in baseclass hierarchy until Object
      while (!teamhash.nil? and baseclass!=Object and result.nil?)
        result = teamhash[baseclass]
        baseclass = baseclass.superclass
      end
      connector = connector.superclass
      baseclass = savebase
    end
    #if a classname is found, get the real class: <myTeam>::<result>
    if (result.instance_of?(String))
      result = self.const_get(result)
    end
    #this is a class or nil
    return result
  end

  # Remember the given roleclass to the given baseclass in context of a given
  # connector.
  # [connector] context of the relation
  # [baseclass] a baseclass
  # [roleclass] the relational roleclass as string
  def Connector.set_roleclass_of(connector, baseclass, roleclass)
    teamhash = TeamRoleclassOf[connector]
    if (teamhash.nil?)
      teamhash = Hash.new
      TeamRoleclassOf[connector] = teamhash
      TeamRoleclassOf.rehash
    end
    # If the given roleclass is defined (->scoped) in the team, forget about
    # the scope and remember just the classname. Through implicit inheritance
    # the most specific type of this role is used -> myTeam::roleclass.
    # If the given role is not defined in the team, remeber just the plain
    # class, which will get instantiated during lifting.
    classname = roleclass.name.split(NamespaceRE)[-1]
    if (self.const_defined?(classname) and self.const_get(classname).instance_of?(Class))
      teamhash[baseclass] = classname
    else
      teamhash[baseclass] = roleclass
    end
  end
    

  # Given baseclass shall play the role of the given roleclass in the
  # context of this connector (self).
  # Rememeber relation from baseclass to roleclass in this context (self).
  # Intercept baseclass.
  # 
  # [roleclass] the role to apply
  # [baseclass] the baseclass that gets the role applied
  def Connector.play_role(roleclass, baseclass)
    #check for valid classes
    ot_assert(roleclass.instance_of?(Class), "play_role: need a class for role. got: #{roleclass.to_s}")
    ot_assert(baseclass.instance_of?(Class), "play_role: need a class for base. got: #{baseclass.to_s}")
    debug(self, "#{baseclass.name} played by #{roleclass.name}") 

    #create a deploy holder
    @@active_deploy = Deploy.new(baseclass, roleclass)
    if (block_given?)
      yield
    end

    #add deployment descripor to list
    add_deployment(self, @@active_deploy)
    set_roleclass_of(self, baseclass, roleclass)

    #generate default ctor for role class
    Interceptor.generate_ctor(roleclass)
  end

  # Check if the given methods are valid.
  # [rolemethod] the rolemethod to call
  # [basemethod] the basemethod that gets called
  def Connector.check_validity(rolemethod, basemethod)
    #check for an active deployment descriptor
    if (@@active_deploy.nil?)
      raise ObjectTeam::TeamException, "no deployment active. call play_role with valid values first.", caller
    end
    #check for nil
    if (rolemethod.nil?  or basemethod.nil?)
      raise ObjectTeam::TeamException, "check_validity: weave rolemethod (#{rolemethod}) to basemethod (#{basemethod}) - got nil!", caller
    end
  end

  # Add a callin binding in the context of this connector.
  # [mode] Mode::{AFTER|BEFORE|REPLACE|CALLIN}
  # [weavemethod] method definition of rolemethod (String|Symbol)
  # [basemethod] method definition of basemethod (String|Symbol|Regexp)
  # [match_parameter] a proc object that adapts input parameter of basemethod.
  def Connector.add_callin(mode, weavemethod, basemethod, match_parameter)
    #check validity of given params and state
    check_validity(weavemethod, basemethod)
    debug(self, "new callin binding: #{weavemethod} #{Mode.to_string(mode)} #{basemethod}") 
    #intercept the method to make this callin work
    Interceptor.intercept(@@active_deploy.baseclass, as_str(basemethod))
    #create deployment and append to list
    @@active_deploy.add_callin(Callin.new(as_str(basemethod), as_str(weavemethod), mode, match_parameter))
  end
  
  # Call role.weavemethod before base.basemethod (role.weavemethod <-before base.basemethod)
  # [weavemethod] method definition of rolemethod (String|Symbol)
  # [basemethod] method definition of basemethod (String|Symbol|Regexp)
  # [match_parameter] a proc object that adapts input parameter of basemethod.
  def Connector.before(weavemethod, basemethod, &match_parameter)
    add_callin(Mode::BEFORE, weavemethod, basemethod, match_parameter)
  end

  # Call role.weavemethod after base.basemethod (role.weavemethod <-after base.basemethod)
  # [weavemethod] method definition of rolemethod (String|Symbol)
  # [basemethod] method definition of basemethod (String|Symbol|Regexp)
  # [match_parameter] a proc object that adapts input parameter of basemethod.
  def Connector.after(weavemethod, basemethod, &match_parameter)
    add_callin(Mode::AFTER, weavemethod, basemethod, match_parameter)
  end

  # Make Role.weavemethod call available under base.basemethod. (role.weavemethod <- base.basemethod)
  # [weavemethod] method definition of rolemethod (String|Symbol)
  # [basemethod] method definition of basemethod (String|Symbol|Regexp)
  # [match_parameter] a proc object that adapts input parameter of basemethod.
  def Connector.callin(weavemethod, basemethod=weavemethod, &match_parameter)
    #TODO: the weaved method could registered by a different name
    add_callin(Mode::CALLIN, weavemethod, basemethod, match_parameter)
  end

  # Replace base.basemethod with role.weavemethod. (role.weavemethod <= base.basemethod)
  # [weavemethod] method definition of rolemethod (String|Symbol)
  # [basemethod] method definition of basemethod (String|Symbol|Regexp)
  # [match_parameter] a proc object that adapts input parameter of basemethod.
  def Connector.replace(weavemethod, basemethod, &match_parameter)
    add_callin(Mode::REPLACE, weavemethod, basemethod, match_parameter)
  end

  # Delegate expected role method to the given basemethod. (role.method -> base.method)
  # [weavemethod] method definition of rolemethod (String|Symbol)
  # [basemethod] method definition of basemethod (String|Symbol)
  # [match_parameter] a proc object that adapts input parameter of basemethod.
  def Connector.delegate_to(weavemethod, basemethod, &match_parameter)
    check_validity(weavemethod, basemethod)
    debug(self, "new callout binding: #{weavemethod} delegate_to #{basemethod}") 
    #create deployment and append to list
    @@active_deploy.add_callout(Callout.new(as_str(weavemethod), as_str(basemethod), match_parameter))
  end

  # Returns the string representation from given object.
  # [object] is a {String|Symbol} => String
  # [return] string representation of object.
  def Connector.as_str(object)
    result = object
    if (object.is_a?(String) or object.is_a?(Array) or object.is_a?(Regexp))
      result = object
    elsif (object.is_a?(Symbol))
      result = object.id2name
    else
      raise ObjectTeam::TeamException, "Need String - got #{object.class.name}: #{object.inspect}", caller
    end
    return result
  end

  #Java syntax compatibility --(start)-----------------------------------------------------------
  def Connector.playRole(roleclass, baseclass, &block)
    play_role(roleclass, baseclass, &block)
  end
  def Connector.delegateTo(weavemethod, basemethod, &match_parameter)
    delegate_to(weavemethod, basemethod, &match_parameter)
  end
  #Java syntax compatibility --(end  )-----------------------------------------------------------
end

# Connector.rb   April 2002
#
# Copyright (c) 2002 by Matthias Veit <matthias_veit@yahoo.de>
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option) 
# any later version.
#  
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
# GNU General Public License for more details.
#  
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
# 02111-1307, USA.
