import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.reflect.*;

/** This class provides information about a class
 *  jobs- .class, .java, uppercase 1st letter
 *  @author matth3wbishop<at>yahoo!<dot>com 
 */
 
  
public class ClassInfo extends Object
{
  //--------------------------------------------
  private static String NEWLINE = System.getProperty("line.separator");

  //--------------------------------------------
  /** the given name of the class */
  private String className;
  //--------------------------------------------
  /** the full name of the class */
  private String originalName;
  //--------------------------------------------
  /** the public constructors as a set of strings */
  private ArrayList publicConstructors;
  //--------------------------------------------
  /** the public methods as a set of strings */
  private ArrayList publicMethods;
  //--------------------------------------------
  /** the public fields as a set of strings */
  private ArrayList publicFields;
  //--------------------------------------------
  /** error messages */
  private StringBuffer errors;
  //--------------------------------------------
  /** flags whether the class name could be found */
  private boolean isGood;
  //--------------------------------------------
  /** was the class name changed */
  private boolean nameAltered;
  //--------------------------------------------
  /** the time it takes to load the info */
  private long loadTime;
  
  //--------------------------------------------
  /** constructs a class with no name */
  public ClassInfo()
  {
    this.publicConstructors = new ArrayList();
    this.publicMethods = new ArrayList();
    this.publicFields = new ArrayList();
    this.errors = new StringBuffer("");
    this.isGood = true;
    this.nameAltered = false;
    this.loadTime = -1;

  } //-- constr: ()

  //--------------------------------------------
  /** constructs a class with a name */
  public ClassInfo(String sClassName)
  {
    this();
    this.className = sClassName;
    this.originalName = sClassName;
    long lStartTime;
    long lFinishTime;
    long lDuration;

    String sNewName;
    sNewName = ClassInfo.removeSuffix(sClassName, ".java");
    sNewName = ClassInfo.removeSuffix(sNewName, ".class");
    this.className = sNewName;

    if (!ClassInfo.isGoodName(sNewName))
    {
      sNewName = ClassInfo.findName(sNewName);
      if (sNewName == "")
      {
        this.isGood = false;
        return;
      }
      else
      {
        this.isGood = true;
        this.className = sNewName;
      }
    }

    lStartTime = System.currentTimeMillis();
    this.loadPublicConstructors();
    this.loadPublicMethods();
    lFinishTime = System.currentTimeMillis();

    lDuration = lFinishTime - lStartTime;    
    this.loadTime = lDuration;
  } //-- constr: (string)


  //--------------------------------------------
  /** removes a given string suffix  */
  public static String removeSuffix(String sText, String sSuffix)
  {
    String sNewText = new String();
    if (sText.toLowerCase().endsWith(sSuffix.toLowerCase()))
    {
      sNewText = sText.substring(0, sText.length() - sSuffix.length());
    }
    else
    { sNewText = sText; }
    
    return sNewText;
  } //-- method: removeSuffix


  //--------------------------------------------
  /** reports whether the virtual machine can find the class */
  public static boolean isGoodName(String sName)
  {
    try
    {
      Class cTest = Class.forName(sName);
      return true;
    }
    catch (ClassNotFoundException e)
    { return false; }

  } //-- method: isGoodName

  //--------------------------------------------
  public static String findName(String sName)
  {
    StringBuffer sbNewName = new StringBuffer("");

    if (ClassInfo.isGoodName(sName))
     { return sName; }


    if (sName.indexOf(".") != -1)
     { return ""; }

    HashSet hhCommonJavaPackages = new HashSet();
    hhCommonJavaPackages.add("java.lang.");
    hhCommonJavaPackages.add("java.util.");
    hhCommonJavaPackages.add("java.io.");
    hhCommonJavaPackages.add("java.net.");
    hhCommonJavaPackages.add("javax.swing.");
    
    Iterator ii = hhCommonJavaPackages.iterator();
    while (ii.hasNext()) 
    {
      sbNewName.setLength(0);
      sbNewName.append((String)ii.next());
      sbNewName.append(sName);

      if (ClassInfo.isGoodName(sbNewName.toString()))
      {
        return sbNewName.toString();
      }
    } //-- while

    return "";
  } //-- method: findName


  //--------------------------------------------
  /**  */
  public void findPublicMethods()
  {
    Method[] mmMethods;
  }

  //--------------------------------------------
  /** gives a short report about stuff */
  public String printStatistics()
  {
    StringBuffer sbReturn = new StringBuffer("");	  
    sbReturn.append(NEWLINE);
    sbReturn.append("class name   :" + this.className);
    sbReturn.append(NEWLINE);
    sbReturn.append("original name:" + this.originalName);
    sbReturn.append(NEWLINE);
    sbReturn.append("class name ok:" + this.isGood);
    sbReturn.append(NEWLINE);
    sbReturn.append("load time    :" + this.loadTime);
    sbReturn.append(NEWLINE);
    if (!this.isGood)
    {
      sbReturn.append("error messages:" + this.errors);
      sbReturn.append(NEWLINE);
    }
    return sbReturn.toString();     
  }

  //--------------------------------------------
  /** returns all the public method signatures concatenated  */
  public String printPublicMethods()
  {
    StringBuffer sbReturn = new StringBuffer("");	  
    String sCurrentMethod;
    Iterator ii = this.publicMethods.iterator();
    while (ii.hasNext()) 
    {
      sCurrentMethod = (String)ii.next();
      sbReturn.append(" " + sCurrentMethod + NEWLINE);
    }
    return sbReturn.toString();     
  }

  //--------------------------------------------
  /** returns all the public constructor signatures concatenated  */
  public String printPublicConstructors()
  {
    StringBuffer sbReturn = new StringBuffer("");	  
    String sCurrentConstructor;
    Iterator ii = this.publicConstructors.iterator();
    while (ii.hasNext()) 
    {
      sCurrentConstructor = (String)ii.next();
      sbReturn.append(" " + sCurrentConstructor + NEWLINE);
    }
    return sbReturn.toString();     
  }

  //--------------------------------------------
  /** returns all the public method signatures concatenated  */
  public String printPublicFields()
  {
    StringBuffer sbReturn = new StringBuffer("");	  
    String sCurrentField;
    Iterator ii = this.publicFields.iterator();
    while (ii.hasNext()) 
    {
      sCurrentField = (String)ii.next();
      sbReturn.append(sCurrentField);
    }
    return sbReturn.toString();     
  }


  //--------------------------------------------
  /** load the public constructors into the member variable */
  public void loadPublicConstructors()
  {
   
    StringBuffer sbReturn = new StringBuffer("");
    StringBuffer sbCurrentConstructor = new StringBuffer("");
    Class cClass;
    Class[] ccParameterTypes;
    Constructor[] ccPublicConstructors;

    try
    {
      cClass = Class.forName(this.className);
    }
    catch (ClassNotFoundException e)
    {
      this.isGood = false;
      this.errors.append(e.toString());
      return;
    }

    
    ccPublicConstructors = cClass.getConstructors();
    Constructor cCurrentConstructor;

    for (int ii = 0; ii < ccPublicConstructors.length; ii++)
    {
        //-- construct a clean string of the constructor
        cCurrentConstructor = ccPublicConstructors[ii];

        sbCurrentConstructor.append(cCurrentConstructor.getName());
        sbCurrentConstructor.append("(");
        ccParameterTypes = cCurrentConstructor.getParameterTypes();
        for (int jj = 0; jj < ccParameterTypes.length; jj++)
        {
          sbCurrentConstructor.append(ccParameterTypes[jj].getName());
          if (jj != (ccParameterTypes.length-1))
           { sbCurrentConstructor.append(", "); }
        }
        sbCurrentConstructor.append(")");
        //sbCurrentConstructor.append(" ----> ");
        //sbCurrentConstructor.append(mCurrentMethod.getReturnType().getName());
        //sbCurrentConstructor.append("");

        this.publicConstructors.add(sbCurrentConstructor.toString());
        sbCurrentConstructor.setLength(0);
    }
   
  } //-- method: loadPublicConstructors

  //--------------------------------------------
  /** load the public methods into the member variable */
  public void loadPublicMethods()
  {
   
    StringBuffer sbReturn = new StringBuffer("");
    StringBuffer sbCurrentMethod = new StringBuffer("");
    Class cClass;
    Class[] ccParameterTypes;
    Method[] mmPublicMethods;
    Constructor[] mmPublicConstructors;

    try
    {
      cClass = Class.forName(this.className);
    }
    catch (ClassNotFoundException e)
    {
      this.isGood = false;
      this.errors.append(e.toString());
      return;
    }

    
    mmPublicMethods = cClass.getMethods();
    Method mCurrentMethod;

    for (int ii = 0; ii < mmPublicMethods.length; ii++)
    {
        //-- construct a clean string of the method
        mCurrentMethod = mmPublicMethods[ii];

        sbCurrentMethod.append(mCurrentMethod.getName());
        sbCurrentMethod.append("(");
        ccParameterTypes = mCurrentMethod.getParameterTypes();
        for (int jj = 0; jj < ccParameterTypes.length; jj++)
        {
          sbCurrentMethod.append(ccParameterTypes[jj].getName());
          if (jj != (ccParameterTypes.length-1))
           { sbCurrentMethod.append(", "); }
        }
        sbCurrentMethod.append(")");
        sbCurrentMethod.append(" ----> ");
        sbCurrentMethod.append(mCurrentMethod.getReturnType().getName());
        sbCurrentMethod.append("");

        this.publicMethods.add(sbCurrentMethod.toString());
        sbCurrentMethod.setLength(0);
    }
   
  } //-- method: loadPublicMethods

  public String show()
  {
    StringBuffer sbReturn = new StringBuffer("");

    try
    {
      Class c = Class.forName(this.className);
      sbReturn.append("Class Name: " + c.getName() + NEWLINE);
      int m = c.getModifiers();
      sbReturn.append("Modifiers: ");
      if (Modifier.isPublic(m))
         sbReturn.append("public ");
      if (Modifier.isAbstract(m))
         sbReturn.append("abstract ");
      if (Modifier.isFinal(m))
         sbReturn.append("final ");
      sbReturn.append(NEWLINE);

      Class subclass;
      Class superclass = c.getSuperclass();
      sbReturn.append("Super classes: ");
      while (superclass != null)
      {
         String className = superclass.getName();
         sbReturn.append(className + " ");
         subclass = superclass;
         superclass = subclass.getSuperclass();
      }
      sbReturn.append(NEWLINE);

    }
    catch (ClassNotFoundException e)
    {
      sbReturn.append(e.toString());
    }
    return sbReturn.toString();
  } //-- method: show


  //--------------------------------------------
  //-- get somethings from the web
  public static void main(String[] args) throws Exception
  {
    StringBuffer sbUsageMessage = new StringBuffer("");
    sbUsageMessage.append("usage: java ClassInfo [classname]");
    sbUsageMessage.append(NEWLINE);


    if (args.length == 0)
    {	    
      System.out.println(sbUsageMessage);
      System.exit(-1);
    }

  
    ClassInfo ciTest = new ClassInfo(args[0]);
    
    StringBuffer sMessage = new StringBuffer("");

    System.out.println(ciTest.printPublicConstructors());
    System.out.println(ciTest.printPublicMethods());
    System.out.println(ciTest.printStatistics());
  } //-- main()
  
} //-- ClassInfo class
