package configuration;
import java.util.Vector;
import java.lang.System;

/**
  * A class representing the dependencies between Java Objects.
  * It implements the <b>ComponentConfigurator</b> interface.
  * For more information on the ComponentConfigurator concept and ideas related to it, 
  * visit <a href="http://choices.cs.uiuc.edu/2k/ComponentConfigurator">the ComponentConfigurator pages</a>.
  * @author  Fabio Kon
  * @version 0.2
  * @see     ComponentConfigurator
  * @see     DependencySpecification
 */
public class SimpleConfigurator implements ComponentConfigurator {

  /////////////////////////////////////////////////////////////////
  // VARIABLES
  /////////////////////////////////////////////////////////////////
  /**
   * The Java object implementating this component.
   */
  protected Object implementation_;
  /**
   * A vector containing the hooks for this component.
   */
  protected Vector hookVector_;
  /**
   * A vector containing the clients for this component.
   */
  protected Vector clientVector_;
  /** 
   * The name of this component.
   */
  protected String name_;
  /** 
   * Information string about this component.
   */
  protected String info_;

  /////////////////////////////////////////////////////////////////
  // METHODS    
  /////////////////////////////////////////////////////////////////

  /**
   * Creates a new configurator for the given object.
   *
   * @param     implementation   the object implementation.
   * @see       java.lang.Object
   */
  public SimpleConfigurator (String name, Object implementation)
  {
    name_ = name;
    implementation_ = implementation;
    hookVector_   = new Vector(4, 8);
    clientVector_ = new Vector(4, 8);
  }

  /**
   * Deletes the references to all internal data structures.
   */
  public void destroyComponentConfigurator ()
  {
    // Let's give a little hand to the garbage collector:
    implementation_ = null;
    hookVector_.removeAllElements(); 
    hookVector_ = null;
    clientVector_.removeAllElements(); 
    clientVector_ = null;
    name_ = null;
    info_ = null;
  }

  //////////////////////////////////////////////////////////////////////
  // Returns the index of the first hook with the name provided.
  // Returns -1 if such element is not found.
  //////////////////////////////////////////////////////////////////////
  private int indexOf (String hookName)
  {
    int i, size = hookVector_.size();

    for (i = 0; i < size; i++)
      if(hookName.equals(((DependencySpecification)hookVector_.elementAt(i)).hook_))
	return i;

    return -1; // element not found.
  }

  /**
   * Adds a hook to the configurator.
   * @param   hookName  the name of the hook to be added.
   * @param   attributes a vector of attributes (may be null).
   * @exception ElementExists if the hook already exists.
   */
  public void addHook (String hookName, DependencyAttributes attributes) throws ElementExists
  {
    int i = indexOf (hookName);

    if (i != -1)
      throw new ElementExists(hookName); // This hook exists already.

    hookVector_.addElement(new DependencySpecification(hookName, null, attributes));
  }

  /**
   * Deletes a hook from the configurator.
   * @param   hookName  the name of the hook to be deleted.
   * @exception NotFound if the hook is not found.
   */
  public void deleteHook (String hookName) throws NotFound
  {
    int i = indexOf (hookName);

    if (i == -1)
      throw new NotFound (hookName);
    else
      hookVector_.removeElementAt (i);

  }
        
  /**
   * Attaches a ComponentConfigurator to the given hook changing the attributes
   * associated with the hook.
   * @param  hookName  the name of the hook.
   * @param  cc        the ComponentConfigurator to be attached to the hook.
   * @param  attributes a vector of attributes (may be null).
   * @exception HookBusy if there is already a component attached to the hook.
   * @exception NotFound if the hook does not exist.
   */
  public void hook (String hookName, ComponentConfigurator cc, DependencyAttributes attributes) 
       throws HookBusy, NotFound
  {
    int i = indexOf (hookName);

    if (i == -1)
      throw new NotFound (hookName);

    if (((DependencySpecification)hookVector_.elementAt(i)).component_!= null)
      throw new HookBusy (hookName);
    else
      {
	((DependencySpecification)hookVector_.elementAt(i)).component_ = cc;
	((DependencySpecification)hookVector_.elementAt(i)).attributes_ = attributes;
      }
  }

  /**
   * Attaches a ComponentConfigurator to the given hook. It does not change the
   * attributes associated with this hook.
   * @param  hookName  the name of the hook.
   * @param  cc        the ComponentConfigurator to be attached to the hook.
   * @exception HookBusy if there is already a component attached to the hook.
   * @exception NotFound if the hook does not exist.
   */
  public void hook (String hookName, ComponentConfigurator cc) 
       throws HookBusy, NotFound
  {
    int i = indexOf (hookName);
      
    if (i == -1)
      {
	throw new NotFound (hookName);
      }
      
    if (((DependencySpecification)hookVector_.elementAt(i)).component_!= null)
      {
	throw new HookBusy (hookName);
      }
    else
      {
	((DependencySpecification)hookVector_.elementAt(i)).component_ = cc;
      }
      
  }
  

  /**
   * Detaches a ComponentConfigurator from the given hook.
   * @param hookName  the name of the hook.
   * @param cc        the ComponentConfigurator to be detached.
   * @exception HookVacant if there are no components attached to the hook.
   * @exception NotFound if the hook does not exist.
   */
  public void unhook (String hookName)
       throws HookVacant, NotFound
  {
    int i = indexOf (hookName);

    if (i == -1)
      throw new NotFound (hookName);
        
    if (((DependencySpecification)hookVector_.elementAt(i)).component_ == null)
      throw new HookVacant (hookName);
    else
      {
	((DependencySpecification)hookVector_.elementAt(i)).component_ = null;
	((DependencySpecification)hookVector_.elementAt(i)).attributes_ = null;
      }
  }
    
  /**
   * Adds a new component to the list of clients.
   * @param  client  the ComponentConfigurator representing the new client.
   * @param  hookNameInClient the name of the hook to which this component is attached.
   * @param  attributes a vector of attributes (may be null).
   * @exception ElementExists if the given pair (client, hookNameInClient) is 
   *                          already present in the client list.
   * 
   */
  public void registerClient (ComponentConfigurator client, String hookNameInClient,
			      DependencyAttributes attributes)
       throws ElementExists
  {
    DependencySpecification hs = new DependencySpecification (hookNameInClient, client, 
							      attributes);

    if (clientVector_.contains (hs))
      throw new ElementExists ();
    else
      clientVector_.addElement (hs);
  }
    
  /**
   * Removes a pair (client, hook) from the list of clients.
   * @param  client  the ComponentConfigurator representing the new client.
   * @param  hookNameInClient the name of the hook to which this component is attached.
   * @exception NotFound if the given pair (client, hookNameInClient) is not 
   *                     present in the client list.
   * 
   */
  public void unregisterClient (ComponentConfigurator client, 
				String hookNameInClient)
       throws NotFound
    
  {
    int i;
        
    DependencySpecification hs = new DependencySpecification (hookNameInClient, client, null);

    if (clientVector_.removeElement (hs) == false)
      throw new NotFound ();
  }
    
  /**
   * This implementation simply prints the event to the standard output.
   * @param  hookedComponent the ComponentConfigurator that generated the event.
   * @param  e               the event that was generated. 
   */
  public void eventOnHookedComponent (ComponentConfigurator hookedComponent,
				      ComponentEvent e)
  {
    System.out.println("Component " + name () + 
		       "received the following event from hooked component " +
		       hookedComponent.name ()+ ": " + e.toString ());
  }
    
  /**
   * This implementation simply prints the event to the standard output.
   * @param  client  the ComponentConfigurator that generated the event.
   * @param  e       the event that was generated. 
   */
  public void eventOnClient (ComponentConfigurator client,
			     ComponentEvent e)
  {
    System.out.println("Component " + name () + 
		       "received the following event from client " +
		       client.name () + ": " + e.toString ());
  }
    
    
  /**
   * Provides a list of the hooks in this component.
   * Note that this implementation trusts its clients because it does not return
   * a copy of the vector, it returns a reference to the vector itself.
   * @returns a vector of DependencySpecifications.
   * @see DependencySpecification
   * @see java.util.Vector 
   */
  public Vector listHooks ()
  {
    return hookVector_;
    // Note, if we wanted to return a copy we could do hookVector.clone()
    // another interesting option would be to use copyInto ()
  }

  /** 
   * Provides a reference to the component attached to the given hook. 
   * @return a reference to the ComponentConfigurator. 
   * @exception NotFound if the hook does not exist.
   */ 
  public ComponentConfigurator getHookedComponent(String hookName) 
       throws NotFound
  {
    int i = indexOf (hookName);
    
    if (i == -1)
      throw new NotFound (hookName);
    
    return ((DependencySpecification)(hookVector_.elementAt (i))).component_;
  }


  /**
   * @return the number of clients registered with this component.
   */
  public int numberOfClients ()
  {
    return clientVector_.size ();
  }

  /**
   * Provides a list of the clients of this component.
   * Note that this implementation trusts its clients because it does not return
   * a copy of the vector, it returns a reference to the vector itself.
   * @returns a vector of DependencySpecifications.
   * @see DependencySpecification
   * @see java.util.Vector 
   */
  public Vector listClients ()
  {
    return clientVector_;
    // Note, if we wanted to return a copy we could do clientVector.clone()
    // another interesting option would be to use copyInto ()
  }
    
    
  /**
   * Sets the name of this component.
   */
  public void name (String s)
  {
    name_ = s;
  }
  /**
   * Accessor for the component name.
   * @return a string containing the component name. 
   */
  public String name () 
  {
    return name_;
  }
    
  /**
   * Sets the information string for this component.
   */
  public void info (String s)
  {
    info_ = s;
  }

  /**
   * Accessor for the component information string.
   * @return a string containing component information.
   */
  public String info ()
  {
    return info_;
  }
        
  /**
   * Accessor for getting the component implementation.
   * @return the object implementation.
   */
  public Object implementation ()
  {
    return implementation_;
  }

  /**
   * Sets the component implementation.
   */
  public void implementation (Object implementation)
  {
    implementation_ = implementation;
  }


  /**
   * Dumps information about the hooks of this Configurator to an output stream.
   * @param   out an output PrintStream in which the information is printed.
   */
  public void printHooks (java.io.PrintStream out)
  {
    Vector v = listHooks ();
    
    int i, size = v.size();
    
    out.println ("Component " + name () + ":");
    for (i = 0; i < size; i++)
      {
	out.print ("\thook = " + 
		   ((DependencySpecification)v.elementAt (i)).hook_);
	if (((DependencySpecification)v.elementAt (i)).component_ != null)
	  out.println (" is attached to component " + 
		       ((DependencySpecification)v.elementAt (i)).component_.name ());
	else
	  out.println (" is not attached to any component at this time.");
	
	// The following 2 lines could be condensed in a single line.
	// It's this way for pedagogical reasons.
	DependencySpecification ds = (DependencySpecification)v.elementAt (i);
	DependencyAttributes atribs = ds.attributes_;
	
	if (atribs == null)
	  out.println ("\tThis dependency has no attributes");
	else
	  {
	    out.println ("\tAttributes of this dependency:");
	    
	    Vector atribVector = atribs.getAttributes ();
	    
	    int j, atribSize = atribVector.size ();
	    
	    for (j = 0; j < atribSize; j++)
	      {
		out.println ("\t\tname = " + 
			     ((DependencyAttributes.NameValue)atribVector.elementAt(j)).name_ +
			     " value = "   + 
			     ((DependencyAttributes.NameValue)atribVector.elementAt(j)).value_);
	      }
	  }
      }
  }// printHooks
  

  /**
   * Dumps information about the clients of this Component to an output stream.
   * @param   out an output PrintStream in which the information is printed.
   */
  public void printClients (java.io.PrintStream out)
  {
    Vector v = listClients ();
    
    int i, size = v.size();
    
    out.println ("Component " + name () + ":");
    for (i = 0; i < size; i++)
      {
        if (((DependencySpecification)v.elementAt (i)).component_ != null)
          {
            out.println ("client <" + 
                         ((DependencySpecification)v.elementAt (i)).component_.name ()
                         + "> (hook in the client = <" + 
                         ((DependencySpecification)v.elementAt (i)).hook_ + ">)");                
          }
	else
          out.println ("warning: null client specification, " + 
                     "may be an indication of inconsistent state.");

	// The following 2 lines could be condensed in a single line.
	// It's this way for pedagogical reasons.
	DependencySpecification ds = (DependencySpecification)v.elementAt (i);
	DependencyAttributes atribs = ds.attributes_;
	
	if (atribs == null)
	  out.println ("\tThis dependency has no attributes");
	else
	  {
	    out.println ("\tAttributes of this dependency:");
	    
	    Vector atribVector = atribs.getAttributes ();
	    
	    int j, atribSize = atribVector.size ();
	    
	    for (j = 0; j < atribSize; j++)
	      {
		out.println ("\t\tname = " + 
			     ((DependencyAttributes.NameValue)atribVector.elementAt(j)).name_ +
			     " value = "   + 
			     ((DependencyAttributes.NameValue)atribVector.elementAt(j)).value_);
	      }
	  }
      }
  }// printClients
  
    
};

