/*
 *This library is free software; you can redistribute it and/or
 *modify it under the terms of the GNU Lesser General Public
 *License as published by the Free Software Foundation; either
 *version 2.1 of the License, or (at your option) any later version.
 *
 *This library 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
 *Lesser General Public License for more details.
 *
 *You should have received a copy of the GNU Lesser General Public
 *License along with this library; if not, write to the Free Software
 *Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*
 * This software was written by Julian Selley (j.selley@man.ac.uk)
 * whilst at the University of Manchester as a Research Assistant.
 *
 * The initial code base is copyright by the University of Manchester.
 * Modifications to the initial code base are copyright of their
 * respective authors, or their employers as appropriate. Authorship
 * of the modifications may be determined from the ChangeLog placed
 * at the end of this file
 */

package uk.ac.man.bioinf.module; // Package name inserted by JPack

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import uk.ac.man.bioinf.debug.Debug;


/**
 * Module.java
 *
 * Represents a module, the main constituent of a modular
 * architectured program. It is associated with the module identifier
 * and context classes.
 *
 * Created: Wed Apr 19 14:17:05 2000
 *
 * @author J Selley
 * @version $Id: Module.java,v 1.10 2001/04/11 17:04:43 lord Exp $
 */

public abstract class Module
{
  /**
   * Returns a module identifier for this module. Simply performs a
   * delegation to the module context.
   *
   * @return this modules identifier
   */
  public ModuleIdentifier getIdentifier()
  {
    // convenience - delegates function to module context
    return getContext().getIdentifier();
  }

  /**
   * Returns the required modules for this module (ie: the modules
   * that are necessary in order for this module to function). Simple
   * delegation to the module context.
   *
   * @return the required modules
   */
  public ModuleList getRequiredModules()
  {
    return getContext().getRequiredModules();
  }

  public Module getRequiredModule( ModuleIdentifier ident )
  {
    ModuleList list = getRequiredModules();
    int size = list.size();
    for( int i = 0; i < size; i++ ){
      if( list.get( i ).getIdentifier() == ident ){
	return list.get( i );
      }
    }
    throw new NoSuchRequiredModule( ident );
  }
  
  /**
   * Returns the module identifiers for the required modules. This
   * places a call on getRequiredModules, which is delegated to the
   * module context. It may require over-riding inorder to improve the
   * efficiency of the method.
   *
   * @return the required module identifiers
   */
  public ModuleIdentifierList getRequiredIdentifiers()
  {
    return new ModuleIdentifierList( new ArrayList() );
  }
  
  /**
   * Returns the modules context.
   *
   * @return the modules context
   */
  public final ModuleContext getContext()
  {
    return context;
  }

  private ModuleContext context;
  public final void setContext( ModuleContext context )
  {
    this.context = context;
  }

  /**
   * Returns the version of this module.
   *
   * @return the version
   */
  public abstract String getVersion();

  /**
   * The method called for the load of this module.
   *
   * @exception ModuleLoadException if a loading error occurs
   */
  public void load() throws ModuleException {}

  private boolean started = false;
  /**
   * The method called externally to initiate the modules function.
   */
  public void start()
  {
  }
  
  public void runStart()
  {
    if( Debug.debug )
      Debug.message( this, "Module: Starting module " + getIdentifier() );
    started = true;
    start();
  }
  
  /**
   * Returns whether the module has been started (ie: the start method
   * run).
   *
   * @return T/F status of call on start() method
   */
  public final boolean isStarted() 
  {
    return this.started;
  }
  
  private boolean destroyed = false;
  public boolean isDestroyed()
  {
    return destroyed;
  }
  
  /**
   * The method called, from an external source, to remove this module
   * and prepare it for garbage collection.
   */
  public void destroy() 
  {
    if( Debug.debug ){
      Debug.message( this, "MODULE: Destroying module " + getIdentifier() );
    }
    
    context = null;
    destroyed = true;
  }

  public void finalize() throws Throwable
  {
    //System.out.println( "FINALIZE: " + this );
  }
  
  // Property Change Support
  // do lazy instantiation. I don't really want to increase the over
  // head of this class
  private PropertyChangeSupport supp;
  public synchronized void addPropertyChangeListener( PropertyChangeListener listener )
  {
    if( supp == null ){
      supp = new PropertyChangeSupport( this );
    }
    supp.addPropertyChangeListener( listener );
  }

  public synchronized void removePropertyChangeListener( PropertyChangeListener listener )
  {
    if( supp == null ){
      supp = new PropertyChangeSupport( this );
    }
    supp.removePropertyChangeListener( listener );
  }

  public synchronized void addPropertyChangeListener( String propertyName, PropertyChangeListener listener )
  {
    if( supp == null ){
      supp = new PropertyChangeSupport( this );
    }
    supp.addPropertyChangeListener( propertyName, listener );
  }

  public synchronized void removePropertyChangeListener
    ( String propertyName, PropertyChangeListener listener)
  {
    if( supp == null ){
      supp = new PropertyChangeSupport( this );
    }
    supp.addPropertyChangeListener( propertyName, listener );
  }

  protected synchronized void firePropertyChange( String propertyName, Object oldValue, Object newValue )
  {
    if( supp == null ){
      supp = new PropertyChangeSupport( this );
    }
    supp.firePropertyChange( propertyName, oldValue, newValue );
  }

  protected synchronized void firePropertyChange( String propertyName, int oldValue, int newValue )
  {
    if( supp == null ){
      supp = new PropertyChangeSupport( this );
    }
    supp.firePropertyChange( propertyName, oldValue, newValue );
  }

  protected synchronized void firePropertyChange( PropertyChangeEvent event )
  {
    if( supp == null ){
      supp = new PropertyChangeSupport( this );
    }
    supp.firePropertyChange( event );
  }

  public synchronized boolean hasListeners( String propertyName )
  {
    if( supp == null ){
      supp = new PropertyChangeSupport( this );
    }
    return supp.hasListeners( propertyName );
  }
} // Module



/*
 * ChangeLog
 * $Log: Module.java,v $
 * Revision 1.10  2001/04/11 17:04:43  lord
 * Added License agreements to all code
 *
 * Revision 1.9  2000/12/18 12:14:15  jns
 * o getting rid of system.out.println to avoid noisy output out of debug
 * mode
 *
 * Revision 1.8  2000/11/13 15:35:58  lord
 * Now throws a RuntimeException if a required module is not found.
 * This is better than getting the NPE that invariably resulted
 * from just returning a null.
 *
 * Revision 1.7  2000/09/15 16:31:23  lord
 * Modifications to remove the ModuleFactory static class. This turned
 * out to be rather limiting and also entirely unnecessary so I removed
 * it.
 *
 * Revision 1.6  2000/06/16 09:40:07  jns
 * o removed existence of BioInterface stuff, as it isn't really relevent here.
 * Plus, in the pipe work is a reworking of what the BioInterface will
 * represent and do.
 *
 * Revision 1.5  2000/05/30 16:26:23  lord
 * runStart() method.
 *
 * Revision 1.4  2000/05/24 20:30:49  jns
 * o created an isStarted() function [final] to check whether the module has been
 * started.
 *
 * Revision 1.3  2000/05/24 19:20:30  lord
 * getRequiredIdentifiers made concrete
 * Context methods coded
 *
 * Revision 1.2  2000/05/24 15:38:37  jns
 * o added ModuleList and MIList stuff and altered java to return module lists
 * adn MILists rather than arrays.
 *
 * Revision 1.1  2000/05/24 13:09:42  jns
 * o initial coding of module package
 *
 */
