/*
 *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.util.HashMap;
import java.util.Iterator;
import uk.ac.man.bioinf.debug.Debug;


/**
 * DefaultModuleFactoryInstance.java
 *
 *
 * Created: Thu May 11 13:02:22 2000
 *
 * @author J Selley
 * @version $Id: DefaultModuleFactoryInstance.java,v 1.13 2001/04/11 17:04:43 lord Exp $
 */

public class DefaultModuleFactoryInstance 
  implements ModuleFactoryInstance
{
  private HashMap mods = new HashMap();
  private HashMap classnames = new HashMap();
  private HashMap moduleNames = new HashMap();
  
  public DefaultModuleFactoryInstance() {}

  public Module load(ModuleIdentifier identifier) throws ModuleException
  {
    Module module;
    
    // make sure that we have the concrete version of this identifier
    identifier = getConcreteIdentifier( identifier );
    
    
    // if the identifier has not been loaded previously
    if ( ( module = ((ModuleStorageStructure)mods.get(identifier)).getModule() ) == null ) {
      
	// try and create the module
	try {
	  Class modClass = Class.forName(identifier.getClassName());
	  module = (Module)modClass.newInstance();
	  
	  
	  ModuleStorageStructure struct = (ModuleStorageStructure)mods.get(identifier);
	  // set the context
	  module.setContext( struct.getContext() );
	  
	  // run the load procedure on this module
	  module.load();
	  
	  // store module if it has been generated
	  struct.setModule(module);
	  
	  // get all required identifiers
	  ModuleIdentifier[] reqIdents = module.getRequiredIdentifiers().toArray();
	  // check if they have been created previously...
	  Module[] requiredModules = new Module[ reqIdents.length ];
	  
	  for (int i = 0; i < reqIdents.length; i++) {
	    if (!(mods.containsKey(reqIdents[i]))) {
	      // add the identifier
	      addIdentifier(reqIdents[i]);
	    }
	    // load the required module
	    requiredModules[ i ] = load(reqIdents[i]);
	  }
	  struct.addRequiredModules( requiredModules );
	}
	catch( NullPointerException npe ){
	  npe.printStackTrace();
	  
	  throw new ModuleException( identifier.getClassName(), ModuleExceptionType.MODULE_CLASS_NOT_FOUND );
	}
	catch (ClassNotFoundException e) {
	  throw new ModuleException( identifier.getClassName(), ModuleExceptionType.MODULE_CLASS_NOT_FOUND);
	} 
	catch (InstantiationException e) {
	  throw new ModuleException( identifier.getClassName(), ModuleExceptionType.MODULE_INIT_PROBLEM);
	}
	catch (IllegalAccessException e) {
	  throw new ModuleException( identifier.getClassName(), ModuleExceptionType.MODULE_INIT_PROBLEM);
	}
      }
    return module;
  }
  
  private ModuleIdentifier getConcreteIdentifier( ModuleIdentifier ident )
  {
    if( ident.isInterface() ){
      ModuleStorageStructure value = (ModuleStorageStructure)
        this.mods.get( ident );
      
      return value.getConcreteIdentifier();
    }
    else{
      return ident;
    }
  }
  
  public void addIdentifier(ModuleIdentifier identifier)
  {
    if( mods.get( identifier ) == null ){
      
      // generate module storage structure
      ModuleStorageStructure struct = new ModuleStorageStructure();
      // and set the module context
      ModuleContext context = new DefaultModuleContext(identifier, struct);
      context.setModuleFactory( this );
      struct.setContext( context );
      
      // store identifier and storage structure
      this.mods.put(identifier, struct);
      // store class and module names in easy hash look up
      this.classnames.put(identifier.getClassName(), identifier);
      moduleNames.put( identifier.getModuleName(), identifier );
    }
  }

  public void addIdentifier(ModuleIdentifier[] identifiers)
  {
    for (int i = 0; i < identifiers.length; i++) {
      addIdentifier(identifiers[i]);
    }
  }

  public ModuleIdentifier resolveModuleName( String moduleName )
  {
    return (ModuleIdentifier)moduleNames.get( moduleName );
  }
  
  public ModuleIdentifier resolveClassName(String classname) 
  {
    return (ModuleIdentifier)this.classnames.get(classname);
  }
  
  public void setModule(ModuleIdentifier identifier, Module module)
    throws ModuleException
  {
    ModuleStorageStructure value = (ModuleStorageStructure)
      this.mods.get(identifier);
    value.setModule(module);
  }

  public Module getModule(ModuleIdentifier identifier) throws ModuleException
  {
    //ensure that we have the concrete version of this identifier
    identifier = getConcreteIdentifier( identifier );

    Module mod = ((ModuleStorageStructure)this.mods.get(identifier)).
      getModule();
    
    if( mod == null ){
      throw new ModuleException( "Attempt to start a module " + identifier.getModuleName() + " when it does not exist", 
                                 ModuleExceptionType.MODULE_DOES_NOT_EXIST );
    }
    
    // start the module if it isn't already started
    if (!mod.isStarted()) {
      if (Debug.debug)
	Debug.message(this, "Module Factory: starting module " + identifier.getModuleName());
	mod.runStart();
    }
    return mod;
  }

  public void addRequiredModules(ModuleIdentifier identifier, Module[] reqMods)
  {
    ModuleStorageStructure value = (ModuleStorageStructure)
      this.mods.get(identifier);
    value.addRequiredModules(reqMods);
  }

  public void addRequiredModule(ModuleIdentifier identifier, Module reqMod)
  {
    ModuleStorageStructure value = (ModuleStorageStructure)
      this.mods.get(identifier);
    value.addRequiredModule(reqMod);
  }

  public ModuleList getRequiredModules(ModuleIdentifier identifier)
  {
    return ((ModuleStorageStructure)this.mods.get(identifier)).
      getRequiredModules();
  }

  public void setConfig(ModuleIdentifier identifier, Object config)
  {
    ModuleStorageStructure value = (ModuleStorageStructure)
      this.mods.get(identifier);
    value.setConfig(config);
  }

  public Object getConfig(ModuleIdentifier identifier)
  {
    return ((ModuleStorageStructure)this.mods.get(identifier)).
      getConfig();
  }

  public void setConcreteIdentifier( ModuleIdentifier abstractIdentifier, ModuleIdentifier concreteIdentifier )
  {
    ModuleStorageStructure value = (ModuleStorageStructure)
      this.mods.get( abstractIdentifier );
    value.setConcreteIdentifier( concreteIdentifier );
  }
  
  public boolean isModuleAvailable(ModuleIdentifier identifier)
  {
    //ensure that we have the concrete version of this identifier
    identifier = getConcreteIdentifier( identifier );
  
    Module mod = ((ModuleStorageStructure)this.mods.get(identifier)).
      getModule();

    return (mod != null);
  }

  public void destroy()
  {
    Iterator iter = mods.values().iterator();
    while( iter.hasNext() ){
      Module mod = ((ModuleStorageStructure)iter.next()).getModule();
      if( mod != null ) mod.destroy();
    }
    
    // (PENDING:- PL) Need to put in checks for null pointer errors
    // and throw module "attempt to use destroyed factory" errors. 
    mods = null;
    classnames = null;
    moduleNames = null;
  }
} // DefaultModuleFactoryInstance



/*
 * ChangeLog
 * $Log: DefaultModuleFactoryInstance.java,v $
 * Revision 1.13  2001/04/11 17:04:43  lord
 * Added License agreements to all code
 *
 * Revision 1.12  2001/02/20 14:48:17  lord
 * removed typo
 *
 * Revision 1.11  2001/02/19 17:50:52  lord
 * Removed debug statements
 *
 * Revision 1.10  2001/01/31 18:03:53  lord
 * Changes due to removal of InterfaceIdentifier
 *
 * Revision 1.9  2001/01/26 17:13:24  lord
 * Improved exception handling
 *
 * Revision 1.8  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.7  2000/08/03 16:40:37  lord
 * Got Interface Modules working correctly which required quite a few changes
 *
 * 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:24:41  lord
 * resolveModuleName added.
 * Small changes to exception handling
 *
 * Revision 1.4  2000/05/24 20:25:00  jns
 * o load() - check that the module hasn't been loaded already
 * o addIdentifier(MI[] ) added to smooth later code writing
 * o getModule(MI ) starts the module if not already started, before returning it
 * o put in a resolveClassName method so that by having a class name you can obtain
 * the module identifier
 *
 * Revision 1.3  2000/05/24 19:19:00  lord
 * Added context object.
 * Provided Modules sorted
 *
 * 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
 *
 */
