/*
 * Copyright (c) Eric D. Friedman 1998. All Rights Reserved.
 * Copyright (c) Paul Kinnucan 1998. All Rights Reserved.
 *
 * $Revision: 1.5 $ 
 * $Date: 2002/05/14 06:38:44 $ 
 *
 * InterfaceFactory 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; either version 2, or (at
 * your option) any later version.
 *
 * InterfaceFactory 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.
 *
 * To obtain a copy of the GNU General Public License write to the
 * Free Software Foundation, Inc.,  59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.  
 */

package jde.wizards;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.io.PrintWriter;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;

/**
 * Defines a factory for creating  skeleton implementations of 
 * Java interfaces. The factory can be invoked from the command line
 * or from another program. The factory can generate implementations for
 * multiple interfaces when invoked from the command line.
 *
 * @author Eric D. Friedman
 * @author Paul Kinnucan
 * @version $Revision: 1.5 $
 */

public class MethodFactory
  implements ClassRegistry
{

  /** Unique storage of the classes to import */
  protected Hashtable imports = new Hashtable();

  /** A factory for generating parameter names */
  protected NameFactory namefactory = null;
  
  /**
   * Constructs a default method factory.
   */
  public MethodFactory()
  {
    this(new DefaultNameFactory());
  }

  /** 
   * Creates a method factory that uses the specified NameFactory
   * for generating parameter names 
   *
   * @param factory Factory for generating parameter names
   */
  public MethodFactory(NameFactory factory)
  {
    namefactory = factory;
  }

  /** 
   * Sets the factory for generating parameter names.
   *
   * @param factory Factory for generating parameter names.
   */
  public void setNameFactory(NameFactory factory)
  {
    namefactory = factory;
  }

  /** 
   * Gets the factory used to generating parameter names for 
   * methods generated by this interface factory.
   *
   * @return Name factory
   */
  public NameFactory getNameFactory()
  {
    return namefactory;
  }

  /** 
   * Gets a table containing the classes that must be imported to
   * implement an interface generated by this factory.
   *
   * @return Classes required to implement the current interface.
   */
  public Hashtable getImports()
  {
    return imports;
  }

  /**
   * Return the registered imports as an elisp list of strings.
   *
   * @return Class names as elisp list of strings.
   */
  public String getImportsAsList() {
    StringBuffer res = new StringBuffer ("(list ");
    Enumeration i = imports.keys();
    while (i.hasMoreElements()) {
      Class c = (Class) i.nextElement();
      res.append ("\"" + c.getName() + "\" ");
    }
    res.append (")");
    return res.toString();
  }

  /** 
   * Registers a class that needs to be imported by the interface 
   * implementation generated by this factory. Store the class in the 
   * import hashtable if it passes the shouldImport test.  
   * Arrays have to be handled differently here. 
   *
   * @param register Imported class candidate
   */
  public void registerImport(Class register)
  {
    if (register.isArray())
    {
      try
      {
        Class cl = register;
        while (cl.isArray())
          cl = cl.getComponentType();
        register = cl;
      }
      catch (Throwable t)
      {
        throw new RuntimeException("Caught error walking up an Array object: " + t);
      }
    }
    if (shouldImport( register ))
      imports.put( register, "");
  }

  /**
   * Tests whether a specified class needs to be imported by the interface
   * implementation generated by this factory.
   * We don't import primitives or classes from java.lang.*  Since we do
   * have to import classes from java.lang.reflect (and whatever else
   * Javasoft may throw under java.lang.*, we have to study the package name
   * in more detail if it starts with java.lang
   *
   * @return <code>true</code> if the class should be imported
   */
  private final boolean shouldImport(Class c)
  {
    if (c.isPrimitive())        // do not import primitives
      return false;
    
    String name = c.getName();
    if ( (name.startsWith("java.lang")) )
    {
      char[] n = name.toCharArray();

      // Start at position ten, or after the final '.' in java.lang.
      for (int i = 10; i < n.length; i++)
        if (n[i] == '.')
          return true;          // must import packages below java.lang 
      return false;             // but nothing in java.lang itself
    }
    return true;                // import everything else
  }


  private String[] numericTypesArray = {"char","byte","short","int","long","float","double"};
  private java.util.List numericTypesList = java.util.Arrays.asList(numericTypesArray);

  /**
   * Return a default body for the implementation of the method described
   * by <code>sig</code>.
   *
   * @param sig a <code>Signature</code> value
   * @return a <code>String</code> value
   */
  protected String getDefaultBody (Signature sig) {
    Method m = sig.getMethod();
    Class cl = m.getReturnType();
    if (numericTypesList.contains(cl.getName())) {
	return "return 0;";
    } else if (cl.getName().equals("boolean")) {
	return "return false;";
    } else if (! cl.getName().equals("void")) {
	return "return null;";
    }
    return "";
  }

  public String getMethodSkeletonExpression (Signature sig) 
  {
    StringBuffer res = new StringBuffer();

    res.append ("(jde-wiz-gen-method");
    res.append (" \"" + sig.getModifiers() + "\"");
    res.append (" \"" + sig.getReturnBaseType() + "\"");
    res.append (" \"" + sig.getMethod().getName() + "\"");
    res.append (" \"" + sig.getParameters() + "\"");
    res.append (" \"" + sig.getExceptionList() + "\"");
    res.append (" \"" + getDefaultBody (sig) + "\")\n");
    return res.toString();
  }

  /** 
   * Clears the import hashtables for this factory so it
   * can be re-used to process a new set of methods.
   */
  public void flush()
  {
    imports.clear();
  }

  public static void println(String s) {
    System.out.print(s + "\n");
    System.out.flush();
  }


} // MethodFactory

/*
 * $Log: MethodFactory.java,v $
 * Revision 1.5  2002/05/14 06:38:44  paulk
 * Enhances code generation wizards for implementing interfaces, abstract
 * classes, etc., to use customizable templates to generate skeleton methods
 * instead of hard-wired skeletons. Thanks to "Dr. Michael Lipp" <lipp@danet.de>
 * for proposing and implementing this improvement.
 *
 * Revision 1.4  2001/08/04 03:24:12  paulk
 * DefaultNameFactory.java
 *
 * Revision 1.3  2001/06/13 04:04:43  paulk
 * Now returns a valid return value for methods that return a value. 
 * Thanks to "Craig McGeachie" <craig@rhe.com.au>.
 *
 */

// End of MethodFactory.java
