/**
*
*   Bond Distributed Object System
*   File: bondLoader.java
*
*   Contains:
*      A centralized way for instantiating objects by name 
*
*      @author Lotzi Boloni
*      Bond Lab, Computer Science Dept. 115, Purdue University
*      created: February 16, 1999
*
*      modified by Kyungkoo Jun, Nov 13, 2000
*         --- enable to load remote classes 
*             from strategy repositories
*             given by URL
*
*  TODO: cache-ing, other stuff
*
*/

package bond.core;
import java.util.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import bond.core.probe.*;
import bond.core.util.*;
import bond.core.gui.editor.*;

public class bondLoader
extends bondObject {
  public Vector defaultpath = null;
  Vector probepath = null;
  Vector editorpath = null;
  ClassLoader cloader = null;

  public bondLoader() {
      // default loading path
      defaultpath = new Vector();
      defaultpath.addElement("bond.core.");
      defaultpath.addElement("bond.services.");
      defaultpath.addElement("bond.agent.");
      defaultpath.addElement("bond.application.");
      defaultpath.addElement("bond.application.TupleSpace.");
      defaultpath.addElement("bond.core.gui.controlpanel.");
      defaultpath.addElement("bond.agent.gui.controlpanel.");
      
      // default path for probes
      probepath = new Vector();
      probepath.addElement("bond.core.probe.");
      probepath.addElement("bond.services.probe.");
      probepath.addElement("bond.agent.");
      // default path for editors
      editorpath = new Vector();
      editorpath.addElement("bond.core.gui.editor.");
      editorpath.addElement("bond.services.gui.editor.");
      editorpath.addElement("bond.application.gui.editor.");
      editorpath.addElement("bond.agent.gui.editor.");
  }
  
    /**
       Creates an object with a default constructor
       from searchpath 
    */
  public Object load(String name, Vector searchpaths) {
      Object o = null;
      Class cl;
      try {
	  if ((cl = loadClass(name, searchpaths)) != null) {
	      o = cl.newInstance();
	  }
      } catch(IllegalAccessException cnfe) {
          Log.Debug(name +" IllegalAccess");
          return null;
      } catch(InstantiationException ie) {
          Log.Debug(name + ie);
          return null;
      }
      return o;
  }

    /**
       Creates an object with a constructor suitable for the parameter
       from searchpath 
    */
  public Object load(String name, Object par[], Vector searchpaths) {
      Object o = null;
      Class cl;
      if ((cl = loadClass(name, searchpaths)) != null) {
	 o  = construct(cl, par);
      }
      return o;
  }

    /**
       Creates an object with a default constructor
       using the default path
    */
  public Object load(String name) {
      return load(name, defaultpath);
  }

    /**
       Creates an object with a default constructor
       for a probe from the default probe directories
     */
    public bondProbe loadProbe(String name) {
	return (bondProbe) load(name, probepath);
    }

    String makeName(String name, String path) {
	//	Log.Debug("makeName with string, string called");
	return path+name;
    }

    /*
    String makeName(String name, Object namemaker) {
	Log.Debug("makeName with string, object called");
	return null;
    }
    */

    /**
        This function takes a list of parameters and calls the 
	corresponding constructor
     */
    public Object construct(Class c, Object par[]) {
	try {
	    Class fpar[] = new Class[par.length];
	    for(int i = 0; i!= fpar.length; i++) {
		fpar[i] = par[i].getClass();
	    }
	    Constructor con = c.getConstructor(fpar);
	    Object ret = con.newInstance(par);
	    return ret;
	} catch(IllegalAccessException cnfe) {
	    Log.Debug("construct:" +" IllegalAccess");
	    return null;
	} catch(InstantiationException ie) {
	    Log.Debug("construct:" + ie);
	    return null;
	} catch(NoSuchMethodException nsme) {
	    Log.Debug("construct:" + nsme);
	    return null;
	}  catch(InvocationTargetException ite) {
	    Log.Debug("construct:" + ite);
	    return null;
	}
    }

    /**
       Loads a class
     */
  public Class loadClass(String name, Vector searchpaths) {
      String completename;
      //Log.Debug("load called for: "+name);
      for(int i=0; i!=searchpaths.size(); i++) {
        try {
	  // Log.Debug("Searching for: "+(String)searchpaths.elementAt(i)+name);
          Class cl = Class.forName(
			  makeName(name, (String)searchpaths.elementAt(i)));
          return cl;
        } catch(ClassNotFoundException cnfe) {
	    // this is ok, try the next path
	    // Log.Debug(cnfe.toString());
        } catch(Exception ex) {
          Log.Debug("Some exception:");
          ex.printStackTrace();
        }
      }

      // by Kyungkoo, 10/13/2000
      // load classes remotely
      if (cloader == null) {
	String c_repository = System.getProperty("bond.current.strategy.repository");
	String repository = System.getProperty("bond.strategy.repository");
	if (c_repository != null && repository != null) {
	  try {
	    URL urlList[] = {new URL(c_repository), new URL(repository)};
	    cloader = new URLClassLoader(urlList);
	  }
	  catch (MalformedURLException e) {
	    e.printStackTrace();
	  }
	}
	else if (repository != null) {
	  try {
	    URL urlList[] = {new URL (repository)};
	    cloader = new URLClassLoader(urlList);
	  }
	  catch (MalformedURLException e) {
	    e.printStackTrace();
	  }
	}
	
      }


      for (int i = 0; i < searchpaths.size(); i++) {
	  try {
	      Class cl = Class.forName(makeName(name, (String)searchpaths.elementAt(i)),
				       true, // for initialization
				       cloader);
	      return cl;
	  }
	  catch (ClassNotFoundException cnfe) {
	      // this is ok, try the next path
	      // Log.Debug(cnfe.toString());
	  } catch(Exception ex) {
	      Log.Debug("Some exception:");
	      ex.printStackTrace();
	  }
      }

      Log.Debug("bondLoader::LoadClass: Class " + name + " not found");
      Log.Debug("Path was:"+searchpaths);
      return null;
   }

    /*
      Loads an editor
     */
    public bondEditor loadEditor(String classname) {
	String name = classname.substring(classname.lastIndexOf('.')+1);
//wheretotry[wheretotry.length-1]=myname.substring(0,myname.lastIndexOf('.'));
	return (bondEditor)load(name+"Editor", editorpath);
    }
}
