//=====================================================================
// File:    Manager.java
// Class:   Manager
// Package: AFLPcore
//
// Author:  James J. Benham
// Date:    August 10, 1998
// Contact: james_benham@hmc.edu
//
// Genographer v1.0 - Computer assisted scoring of gels.
// Copyright (C) 1998  Montana State University
// 
// 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; version 2
// of the License.
//
// 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.
//
// The GNU General Public License is distributed in the file GPL
//=====================================================================

package AFLPcore;

import java.util.Hashtable;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.io.IOException;

/**
 * This class contains a list of <code>Operation</code>s which can be used
 * to perform different functions in the program. Each op is associated with a 
 * <code>String</code>, which represents its name. The associations are
 * currently mantained in a hash table.
 *
 * <p>Both this class and <code>Operation</code> are abstract and must be
 * extended. The op should be similar in some way, like 
 * <code>ImportFilter</code> which extends <code>Operation</code> and
 * is an abstract class for filtering in input files. The 
 * <code>ImportMgr</code> is a subclass of this class and checks to see
 * that all of the <code>Operations</code> are of type
 * <code>ImportFilter</code>. Therefore, the only method that needs to
 * be implemented by a subclass is the <code>classTypeOk</code>, which
 * checks to make sure that the operation is a valid one.
 *
 * <p>Classes that extend this class should call this classes constructor
 * to set up the hashtable.
 *
 * <p>The ops can be retrieved based on their name, and a lists of
 * names can be obtained from the Manager. The Op will contain
 * addtional information.
 *
 * <p>Currently, ops can be added with methods, but ideally the 
 * Manager would be able to analyze something like a directory and add
 * all of the ops it found there. This would mean that different operations
 * could be add by simply dropping a class that extended the appropriate
 * <code>Operation</code> subclass into the appropriate directory.
 *
 * @see Operation
 * @see java.util.Hashtable
 * @see ImportMgr
 * @see ImportFilter
 *
 * @author James J. Benham
 * @version 1.0.0
 * @date August 10, 1998
 */

public abstract class Manager
{
  private Hashtable ops;        // maintains the table of String/Operations
                                // values and used to access the Operations
  private String defaultName;       // the name of the op to use by default

  /**
   * Creates a new Manager, with a default capacity of 4 and a 
   * loadFactor of 1.0. Sets up the Hashtable for the String/Operation
   * pairs.
   */
  public Manager()
  {
    defaultName = null;

    try
      {
	ops = new Hashtable(4, (float)1.0);
      }
    catch (IllegalArgumentException e)
      {
	// both the arguments are fine, so we won't ever get here.
      }
  }

  /**
   * Creates a new Manager with the specified values.
   *
   * @param capacity  the initial number of operations that can be stored.
   * @param loadFactor controls memory efficiency vs. lookup speed, it
   *  must be between 0.0 and 1.0, where 1.0 has the greatest memory efficiency
   *
   * @exception IllegalArgumentException when <code>capacitly</code> is less
   *    than zero or the <code>loadFactor</code> is out of bounds.
   */
  public Manager(int capacity, float loadFactor)
       throws IllegalArgumentException
  {
    defaultName = null;

    try
      {
	// Make the hashtable
	ops = new Hashtable(capacity, loadFactor);
      }
    catch (IllegalArgumentException e)
      {
	// pass along the error
	throw e;
      }
  }

  /**
   * Gets the names of all of the Operations known to the Manager. It
   * works be creating an <code>Enumeration</code> from the hash table.
   * The <code>Enumeration</code> is turned into an array be simply
   * retrieveing the next element and storing it in an array. The size of
   * the hashtable becomes the size of the array.
   * 
   * @returns an array of Strings, with each entry representing an operation in
   *  the Manager.
   *
   * @see java.util.Enumeration
   */
  public String[] getNames()
  {
    String names[];                   // the names in an array

    // Get the names from the Hashtable
    Enumeration namesEnum = ops.keys();

    // Create an array of the approriate size
    names = new String[ops.size()];

    // Copy the names from the Enumeration into the array
    for(int i=0; i < ops.size(); i++)
      {
	names[i] = (String) namesEnum.nextElement();
      }

    return names;
  }

  /**
   * Retrieves the operation associated with the given name. This can be used
   * to analyze an operation and to retrieve specific information about it.
   *
   * @param filterName  the name of a filter known to this Manager
   *
   * @returns  the operation associated with <code>opName</code>
   *
   * @exception NoSuchElementException  the specified operation does not exist
   *   In other words, <code>opName</code> does not have an associated
   *   value.
   */
  public Operation get(String opName) throws NoSuchElementException
  {
    Operation op = (Operation) ops.get(opName);

    if(op == null)
      throw new NoSuchElementException();

    return op;
  }

  /**
   * Adds the specified <code>operation</code> with the given <code>name</code>
   * to the Manager. The operation must be compatible with the type
   * of operation that this class handles.
   *
   * @param name   the name of the operation to be associated with it
   * @param operation  the operation object
   *
   * @exception IllegalArgumentException  occurs when the given
   *     operation is not compatible with the Manager.
   */
  public void add(String name, Operation operation)
       throws IllegalArgumentException
  {
    if( !classTypeOk(operation) )
      {
	throw new IllegalArgumentException("Given operation is not" +
					   "compatible with this Manger");
      }
    else
      {
	ops.put(name, operation);
      }
  }

  /**
   * Checks to see if the specified operation is compatible with the manager.
   * For example, here is the implementation used by ImportMgr.
   * <pre>
   *   return (op instanceof ImportFilter);
   * </pre>
   * 
   * @return <code>true</code> if it is compatible, <code>false</code> if 
   *      it is not.
   */
  public abstract boolean classTypeOk(Operation op);

  /**
   * Sets the default operation to the one specified. 
   *
   * @param name the name of the operation to use as the default. If it is 
   *   <code>null</code>, then no default method will be set and an 
   *   exception will be thrown when <code>getDefault</code> is called.
   *
   * @exception NoSuchElementException when the specified name is not <code>
   *   null</code> and does not match any filter known to the ImportMgr,
   *   this will be thrown.
   */
  public void setDefault(String name) throws NoSuchElementException
  {
    if(name == null)
      defaultName = null;
    else if(ops.containsKey(name))
      defaultName = name;
    else
      throw new NoSuchElementException();
  }

  /**
   * Gives the default operation for this manager.
   *
   * @return the operation, or <code>null</code> if the default is not set.
   */
  public Operation getDefault()
  {
    return (Operation) ops.get(defaultName);
  }

  /**
   * Gives the name of the default operation for this manager.
   *
   * @return the name of the operation, or <code>null</code> if the default
   * is not set.
   */
  public String getDefaultName()
  {
    return defaultName;
  }

}
