Reflection Java

/*
 * ClassFile.java Chuck McManis
 *
 * Copyright (c) 1996 Chuck McManis, All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies.
 *
 * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
 * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
/**
 * This class is used to manipulate Java class files in strange and
 * mysterious ways.
 *
 * Usage it typically to feed it an array of bytes that are a class
 * file, manipulate the class, then convert the class back into bytes,
 * and feed the final result to defineClass().
 *
 * @version   1.6, 19 Aug 1995
 * @author  Chuck McManis
 * @see   AttributeInfo
 * @see   ConstantPoolInfo
 * @see   MethodInfo
 * @see   FieldInfo
 */
public class ClassFile {
    int             magic;
    short           majorVersion;
    short           minorVersion;
    ConstantPoolInfo  constantPool[];
    short           accessFlags;
    ConstantPoolInfo  thisClass;
    ConstantPoolInfo  superClass;
    ConstantPoolInfo  interfaces[];
    FieldInfo       fields[];
    MethodInfo        methods[];
    AttributeInfo     attributes[];
    boolean           isValidClass = false;
    public static final int ACC_PUBLIC    = 0x1;
    public static final int ACC_PRIVATE   = 0x2;
    public static final int ACC_PROTECTED   = 0x4;
    public static final int ACC_STATIC    = 0x8;
    public static final int ACC_FINAL     = 0x10;
    public static final int ACC_SYNCHRONIZED  = 0x20;
    public static final int ACC_THREADSAFE  = 0x40;
    public static final int ACC_TRANSIENT   = 0x80;
    public static final int ACC_NATIVE    = 0x100;
    public static final int ACC_INTERFACE   = 0x200;
    public static final int ACC_ABSTRACT  = 0x400;
    public boolean debug = false;
    public boolean dumpConstants = false;
    /**
     * Read a class from InputStream in.
     */
    public boolean read(InputStream in)
  throws IOException {
      DataInputStream di = new DataInputStream(in);
      int count;
      magic = di.readInt();
      if (magic != (int) 0xCAFEBABE) {
          return (false);
      }
      majorVersion = di.readShort();
      minorVersion = di.readShort();
      count = di.readShort();
      constantPool = new ConstantPoolInfo[count];
      if (debug)
          System.out.println("read(): Read header...");
      constantPool[0] = new ConstantPoolInfo();
      for (int i = 1; i < constantPool.length; i++) {
          constantPool[i] = new ConstantPoolInfo();
          if (! constantPool[i].read(di)) {
            return (false);
          }
          // These two types take up "two" spots in the table
          if ((constantPool[i].type == ConstantPoolInfo.LONG) ||
            (constantPool[i].type == ConstantPoolInfo.DOUBLE))
            i++;
      }
      /*
       * Update pointers in the constant table. This turns the
       * table into a real datastructure.
       *
       * TODO: Have it verify that the right arguments are present
       */
      for (int i = 1; i < constantPool.length; i++) {
          if (constantPool[i] == null)
            continue;
          if (constantPool[i].index1 > 0)
            constantPool[i].arg1 = constantPool[constantPool[i].index1];
          if (constantPool[i].index2 > 0)
            constantPool[i].arg2 = constantPool[constantPool[i].index2];
      }
      if (dumpConstants) {
          for (int i = 1; i < constantPool.length; i++) {
            System.out.println("C"+i+" - "+constantPool[i]);
          }
        }
      accessFlags = di.readShort();
      thisClass = constantPool[di.readShort()];
      superClass = constantPool[di.readShort()];
      if (debug)
          System.out.println("read(): Read class info...");
      /*
       * Identify all of the interfaces implemented by this class
       */
      count = di.readShort();
      if (count != 0) {
          if (debug)
              System.out.println("Class implements "+count+" interfaces.");
          interfaces = new ConstantPoolInfo[count];
          for (int i = 0; i < count; i++) {
            int iindex = di.readShort();
            if ((iindex < 1) || (iindex > constantPool.length - 1))
                return (false);
            interfaces[i] = constantPool[iindex];
            if (debug)
                System.out.println("I"+i+": "+interfaces[i]);
          }
      }
      if (debug)
          System.out.println("read(): Read interface info...");
      /*
       * Identify all fields in this class.
       */
      count = di.readShort();
      if (debug)
          System.out.println("This class has "+count+" fields.");
      if (count != 0) {
          fields = new FieldInfo[count];
          for (int i = 0; i < count; i++) {
            fields[i] = new FieldInfo();
            if (! fields[i].read(di, constantPool)) {
               return (false);
            }
            if (debug)
                System.out.println("F"+i+": "+
              fields[i].toString(constantPool));
          }
      }
      if (debug)
          System.out.println("read(): Read field info...");
      /*
         * Identify all the methods in this class.
       */
      count = di.readShort();
      if (count != 0) {
          methods = new MethodInfo[count];
          for (int i = 0; i < count; i++) {
            methods[i] = new MethodInfo();
            if (! methods[i].read(di, constantPool)) {
                return (false);
            }
            if (debug)
                System.out.println("M"+i+": "+methods[i].toString());
          }
      }
      if (debug)
          System.out.println("read(): Read method info...");
      /*
       * Identify all of the attributes in this class
       */
      count = di.readShort();
      if (count != 0) {
          attributes = new AttributeInfo[count];
          for (int i = 0; i < count; i++) {
            attributes[i] = new AttributeInfo();
            if (! attributes[i].read(di, constantPool)) {
                return (false);
            }
          }
      }
      if (debug) {
          System.out.println("read(): Read attribute info...");
          System.out.println("done.");
      }
      isValidClass = true;
      return(true);
    }
    /**
     * Write the class out as a stream of bytes to the output
     * stream.
     *
     * Generally you will read a class file, manipulate
     * it in some way, and then write it out again before passing
     * it to defineClass in some class loader.
     */
    public void write(OutputStream out)
  throws IOException, Exception {
      DataOutputStream dos = new DataOutputStream(out);
      if (! isValidClass) {
          throw new Exception("ClassFile::write() - Invalid Class");
      }
      dos.writeInt(magic);
      dos.writeShort(majorVersion);
      dos.writeShort(minorVersion);
      dos.writeShort(constantPool.length);
      for (int i = 1; i < constantPool.length; i++) {
          if (constantPool[i] != null)
              constantPool[i].write(dos, constantPool);
      }
      dos.writeShort(accessFlags);
      dos.writeShort(ConstantPoolInfo.indexOf(thisClass, constantPool));
      dos.writeShort(ConstantPoolInfo.indexOf(superClass, constantPool));
      if (interfaces == null) {
          dos.writeShort(0);
      } else {
          dos.writeShort(interfaces.length);
          for (int i = 0; i < interfaces.length; i++) {
              dos.writeShort(ConstantPoolInfo.indexOf(interfaces[i],
              constantPool));
          }
      }
      if (fields == null) {
          dos.writeShort(0);
      } else {
          dos.writeShort(fields.length);
          for (int i = 0; i < fields.length; i++) {
              fields[i].write(dos, constantPool);
          }
      }
      if (methods == null) {
          dos.writeShort(0);
      } else {
          dos.writeShort(methods.length);
          for (int i = 0; i < methods.length; i++) {
              methods[i].write(dos, constantPool);
          }
      }
      if (attributes == null) {
          dos.writeShort(0);
      } else {
          dos.writeShort(attributes.length);
          for (int i = 0; i < attributes.length; i++) {
              attributes[i].write(dos, constantPool);
          }
      }
    }
    /**
     * Returns a string that represents what the access flags
     * are set for. So 0x14 returns "public final "
     */
    public static String accessString(short flags) {
      StringBuffer x = new StringBuffer();
      if ((flags & ACC_PUBLIC) != 0) {
          x.append("public ");
      }
      if ((flags & ACC_PRIVATE) != 0) {
          x.append("private ");
      }
      if ((flags & ACC_PROTECTED) != 0) {
          x.append("protected ");
      }
      if ((flags & ACC_STATIC) != 0) {
          x.append("static ");
      }
      if ((flags & ACC_FINAL) != 0) {
          x.append("final ");
      }
      if ((flags & ACC_SYNCHRONIZED) != 0) {
          x.append("synchronized ");
      }
      if ((flags & ACC_THREADSAFE) != 0) {
          x.append("threadsafe ");
      }
      if ((flags & ACC_TRANSIENT) != 0) {
          x.append("transient ");
      }
      if ((flags & ACC_NATIVE) != 0) {
          x.append("native ");
      }
      if ((flags & ACC_INTERFACE) != 0) {
          x.append("interface ");
      }
      if ((flags & ACC_ABSTRACT) != 0) {
          x.append("abstract ");
      }
      return (x.toString());
    }
    /**
     * Takes a type signature and a string representing a variable name
     * and returns a declaration for that variable name.
     *
     * For example, passing this the strings "[B" and "myArray" will
     * return the string "byte myArray[]"
     */
    public static String typeString(String typeString, String varName) {
      int isArray = 0;
      int ndx = 0;
      StringBuffer x = new StringBuffer();
      while (typeString.charAt(ndx) == '[') {
          isArray++;
          ndx++;
      }
      switch (typeString.charAt(ndx)) {
          case 'B' :
            x.append("byte ");
            break;
          case 'C' :
            x.append("char ");
            break;
          case 'D' :
            x.append("double ");
            break;
          case 'F' :
            x.append("float ");
            break;
          case 'I' :
            x.append("int ");
            break;
          case 'J' :
            x.append("long ");
            break;
          case 'L' :
            for (int i = ndx+1; i < typeString.indexOf(';'); i++) {
                if (typeString.charAt(i) != '/')
                  x.append(typeString.charAt(i));
                else
              x.append('.');
            }
            x.append(" ");
            break;
          case 'V':
            x.append("void ");
            break;
          case 'S' :
            x.append("short ");
            break;
          case 'Z' :
            x.append("boolean ");
            break;
      }
      x.append(varName);
      while (isArray > 0) {
          x.append("[]");
          isArray--;
      }
      return (x.toString());
    }
    /**
     * Returns the next signature from a string of concatenated signatures.
     * For example if the signature was "[BII", this method would return
     * "II"
     */
    public static String nextSig(String sig) {
      int ndx = 0;
      String  x;
      while (sig.charAt(ndx) == '[')
          ndx++;
      if (sig.charAt(ndx) == 'L') {
          while (sig.charAt(ndx) != ';')
            ndx++;
      }
      ndx++;
      x =  (sig.substring(ndx));
      return (x);
    }
    /**
     * Print the name of a class in "canonical form"
     */
    private String printClassName(String s) {
      StringBuffer x;
      if (s.charAt(0) == '[') {
          return(typeString(s, ""));
      }
      x = new StringBuffer();
      for (int j = 0; j < s.length(); j++) {
          if (s.charAt(j) == '/')
            x.append('.');
          else
            x.append(s.charAt(j));
      }
      return (x.toString());
    }
    public String getClassName() {
      return printClassName(thisClass.arg1.strValue);
    }
    /**
     * The boring version of display().
     */
    public String toString() {
      return("Class File (Version "+majorVersion+"."+minorVersion+
          ") for class "+thisClass.arg1);
    }
    /**
     * Write out a text version of this class.
     */
    public void display(PrintStream ps)
  throws Exception {
      int i;
      String myClassName;
      String mySuperClassName;
      String packageName = null;
      if (! isValidClass) {
          ps.println("Not a valid class");
      }
      myClassName = printClassName(thisClass.arg1.strValue);
      mySuperClassName = printClassName(superClass.arg1.strValue);
      if (myClassName.indexOf('.') > 0) {
          packageName =
            myClassName.substring(0, myClassName.lastIndexOf('.'));
          myClassName = myClassName.substring(myClassName.lastIndexOf('.')+1);
          ps.println("package "+packageName+"\n");
      }
      for (i = 1; i < constantPool.length; i++) {
          if (constantPool[i] == null)
            continue;
          if ((constantPool[i] == thisClass) ||
            (constantPool[i] == superClass))
            continue;
          if (constantPool[i].type == ConstantPoolInfo.CLASS) {
            String s = constantPool[i].arg1.strValue;
                if (s.charAt(0) == '[')
                    continue;
            s = printClassName(constantPool[i].arg1.strValue);
            if ((packageName != null) && (s.startsWith(packageName)))
                continue;
            ps.println("import "+printClassName(s)+";");
          }
      }
      ps.println();
      ps.println("/*");
      DataInputStream dis;
      ConstantPoolInfo cpi;
      if (attributes != null) {
          ps.println(" * This class has "+attributes.length+
          " optional class attributes.");
          ps.println(" * These attributes are: ");
          for (i = 0; i < attributes.length; i++) {
              String attrName = attributes[i].name.strValue;
              dis = new DataInputStream(new ByteArrayInputStream(attributes[i].data));
              ps.println(" * Attribute "+(i+1)+" is of type "+attributes[i].name);
              if (attrName.compareTo("SourceFile") == 0) {
                cpi = null;
                try {
                    cpi = constantPool[dis.readShort()];
                } catch (IOException e) { }
                ps.println(" *  SourceFile : "+cpi);
              } else {
                ps.println(" *  TYPE ("+attrName+")");
              }
            }
      } else {
          ps.println(" * This class has NO optional class attributes.");
      }
      ps.println(" */\n");
      ps.print(accessString(accessFlags)+"class "+myClassName+" extends "+
        mySuperClassName);
      if (interfaces != null) {
          ps.print(" implements ");
          for (i = 0; i < interfaces.length - 1; i++) {
            ps.print(interfaces[i].arg1.strValue+", ");
          }
          ps.print(interfaces[interfaces.length-1].arg1.strValue);
      }
      ps.println(" {\n");
      if (fields != null) {
          ps.println("/* Instance Variables */");
          for (i = 0; i < fields.length; i++) {
              ps.println("    "+fields[i].toString(constantPool)+";");
          }
      }
      if (methods != null) {
          ps.println("\n/* Methods */");
          for (i = 0; i < methods.length; i++) {
              ps.println("    "+methods[i].toString(myClassName));
          }
      }
      ps.println("\n}");
    }
    public ConstantPoolInfo getConstantRef(short index) {
      return (constantPool[index]);
    }
    /**
     * Add a single constant pool item and return its index.
     * If the item is already in the pool then the index of
     * the preexisting item is returned. Thus you cannot
     * assume that a pointer to your item will be useful.
     */
    public short addConstantPoolItem(ConstantPoolInfo item)
  throws Exception {
      ConstantPoolInfo newConstantPool[];
      ConstantPoolInfo cp;
      cp = item.inPool(constantPool);
      if (cp != null)
          return ConstantPoolInfo.indexOf(cp, constantPool);
      newConstantPool = new ConstantPoolInfo[constantPool.length+1];
      for (int i = 1; i < constantPool.length; i++) {
          newConstantPool[i] = constantPool[i];
      }
      newConstantPool[constantPool.length] = item;
      constantPool = newConstantPool;
      return ConstantPoolInfo.indexOf(item, constantPool);
    }
    /**
     * Add some items to the constant pool. This is used to add new
     * items to the constant pool. The items references in arg1 and
     * arg2 are expected to be valid pointers (if necessary). Pruning
     * is done to prevent adding redundant items to the list and to
     * preserve string space.
     *
     * The algorithm is simple, first identify pool items containing
     * constants in the list of items to be added that are already
     * in the constant pool. If any are found to already exist, change
     * the pointers in the non-constant items to point to the ones in
     * the pool rather than the ones in the list. Next check to see
     * if any of the non-constant items are already in the pool and
     * if so fix up the others in the list to point to the ones in
     * the pool. Finally, add any items (there must be at least one)
     * from the item list that aren't already in the pool, all of
     * the pointers will already be fixed.
     *
     * NOTE: Since constants in the constant pool may be referenced
     * inside the opaque portion of attributes the constant
     * table cannot be re-ordered, only extended.
     */
    public void addConstantPoolItems(ConstantPoolInfo items[]) {
      ConstantPoolInfo newArg;
      ConstantPoolInfo newConstantPool[];
      boolean delete[] = new boolean[items.length];
      /* Step one, look for matching constants */
      for (int j = 0; j < items.length; j++) {
          if ( (items[j].type == ConstantPoolInfo.ASCIZ) ||
               (items[j].type == ConstantPoolInfo.UNICODE) ||
               (items[j].type == ConstantPoolInfo.INTEGER) ||
               (items[j].type == ConstantPoolInfo.LONG) ||
               (items[j].type == ConstantPoolInfo.FLOAT) ||
               (items[j].type == ConstantPoolInfo.DOUBLE)) {
        // Look for this item in the constant pool
        delete[j] = false;
        newArg = items[j].inPool(constantPool);
        if (newArg != null) {
            // replace the references in our list.
            delete[j] = true; // mark it for deletion
            for (int i = 0; i < items.length; i++) {
              if (items[i].arg1 == items[j])
                  items[i].arg1 = newArg;
                  if (items[i].arg2 == items[j])
                      items[i].arg2 = newArg;
                }
            }
          }
      }
      /* Step two : now match everything else */
      for (int j = 0; j < items.length; j++) {
          if ( (items[j].type == ConstantPoolInfo.CLASS) ||
               (items[j].type == ConstantPoolInfo.FIELDREF) ||
               (items[j].type == ConstantPoolInfo.METHODREF) ||
               (items[j].type == ConstantPoolInfo.STRING) ||
               (items[j].type == ConstantPoolInfo.INTERFACE) ||
               (items[j].type == ConstantPoolInfo.NAMEANDTYPE)) {
        // Look for this item in the constant pool
        delete[j] = false;
        newArg = items[j].inPool(constantPool);
        if (newArg != null) {
            // replace the references in our list.
            delete[j] = true; // mark it for deletion
            for (int i = 0; i < items.length; i++) {
              if (items[i].arg1 == items[j])
                  items[i].arg1 = newArg;
                  if (items[i].arg2 == items[j])
                      items[i].arg2 = newArg;
                }
            }
          }
      }
      /* Step three: Add the surviving items to the pool */
      int count = 0;
      for (int i = 0; i < items.length; i++) {
          if (! delete[i])
            count++;
      }
      // count == # of survivors
      newConstantPool = new ConstantPoolInfo[constantPool.length + count];
      for (int i = 1; i < constantPool.length; i++) {
          newConstantPool[i] = constantPool[i];
      }
      // newConstantPool == existing constantPool
      int ndx = 0;
      for (int i = constantPool.length; i < newConstantPool.length; i++) {
          while (delete[ndx])
            ndx++;
          newConstantPool[i] = items[ndx];
          ndx++;
      }
      // newConstantPool == existing + new
      constantPool = newConstantPool;
      // all done.
    }
    /**
     * Add a new optional class Attribute.
     *
     * Items is an array of constant pool items that are first added
     * to the constant pool. At a minimum items[0] must be an ASCIZ
     * item with the name of the attribute. If the body of the attribute
     * references constant pool items these should be in the item list
     * as well.
     */
    public void addAttribute(AttributeInfo newAttribute) {
      if (attributes == null) {
          attributes = new AttributeInfo[1];
          attributes[0] = newAttribute;
      } else {
          AttributeInfo newAttrList[] = new AttributeInfo[1+attributes.length];
          for (int i = 0; i < attributes.length; i++) {
            newAttrList[i] = attributes[i];
          }
          newAttrList[attributes.length] = newAttribute;
          attributes = newAttrList;
      }
    }
    /**
     * Return the attribute named 'name' from the class file.
     */
    public AttributeInfo getAttribute(String name) {
      if (attributes == null)
          return null;
      for (int i = 0; i < attributes.length; i++) {
          if (name.compareTo(attributes[i].name.toString()) == 0)
        return attributes[i];
      }
      return (null);
    }
    /**
     * Return a constant pool item from this class. (note does fixup
     * of indexes to facilitate extracting nested or linked items.
     */
    public ConstantPoolInfo getConstantPoolItem(short index)
  throws Exception {
      ConstantPoolInfo cp;
      if ((index <= 0) || (index > (constantPool.length - 1)))
          return (null);
      cp = constantPool[index];
      if (cp.arg1 != null)
          cp.index1 = ConstantPoolInfo.indexOf(cp.arg1, constantPool);
      if (cp.arg2 != null)
          cp.index2 = ConstantPoolInfo.indexOf(cp.arg2, constantPool);
      return cp;
    }
    /* Examples of mysterious things you can do to a class file before
     * writing it back out. These methods are not currently functional.
     * (that would be too easy :-)
     */
    /**
     * Map occurences of class oldClass to occurrences of
     * class newClass. This method is used to retarget
     * accesses to one class, seamlessly to another.
     *
     * The format for the class name is slash (/) separated so
     * the class util.ClassFile would be represented as
     * util/ClassFile
     */
    public void mapClass(String oldClass, String newClass) {
      if (debug)
          System.out.println("Mapping class name "+oldClass+" ==> "+
          newClass+" for class "+thisClass.arg1);
      for (int i = 0; i < constantPool.length; i++) {
          if (constantPool[i].type == ConstantPoolInfo.CLASS) {
            String cname = constantPool[i].arg1.strValue;
            if (cname.compareTo(oldClass) == 0) {
                if (debug) {
                    System.out.println("REPLACING "+cname+" with "+newClass);
                }
                constantPool[i].arg1.strValue = newClass;
            }
          }
      }
    }
    /**
     * Map occurences of package oldPackage to package
     * newPackage.
     *
     * The format for the package name is slash (/) separated so
     * the package java.util would be represented as
     * java/util
     */
     public void mapPackage(String oldPackage, String newPackage) {
      for (int i = 0; i < constantPool.length; i++) {
          if (constantPool[i].type == ConstantPoolInfo.CLASS) {
            String cname = constantPool[i].arg1.strValue;
            if (cname.startsWith(oldPackage)) {
                constantPool[i].arg1.strValue = newPackage +
              cname.substring(cname.lastIndexOf('/'));
            }
          }
      }
    }
    /**
     * Delete a named method from this class. This method is used
     * to excise specific methods from the loaded class. The actual
     * method code remains, however the method signature is deleted
     * from the constant pool. If this method is called by a class
     * the exception IncompatibleClassChangeException is generated
     * by the runtime.
     */
    public void deleteMethod(String name, String signature) {
      for (int i = 0; i < constantPool.length; i++) {
          if (constantPool[i].type == ConstantPoolInfo.CLASS) {
          }
      }
    }
}
/*
 * @(#)ConstantPoolInfo.java  1.5 95/08/16 Chuck McManis
 *
 * Copyright (c) 1996 Chuck McManis, All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies.
 *
 * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
 * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
 * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */
/**
 * This class defines an entry in the constant pool for a Java class.
 * The class file is primarily composed of ConstantPool entries and
 * manipulation is done by modifying those entries.
 *
 * @version   1.5, 16 Aug 1995
 * @author  Chuck McManis
 * @see   ClassFile
 */
 class ConstantPoolInfo{
    int type;     // type of this item
    String name;    // String for the type
    ConstantPoolInfo  arg1; // index to first argument
    ConstantPoolInfo  arg2; // index to second argument
    short index1, index2;
    String  strValue;     // ASCIZ String value
    int   intValue;
    long  longValue;
    float floatValue;
    double  doubleValue;
    public static final int CLASS = 7;
    public static final int FIELDREF = 9;
    public static final int METHODREF = 10;
    public static final int STRING = 8;
    public static final int INTEGER = 3;
    public static final int FLOAT = 4;
    public static final int LONG = 5;
    public static final int DOUBLE = 6;
    public static final int INTERFACE = 11;
    public static final int NAMEANDTYPE = 12;
    public static final int ASCIZ = 1;
    public static final int UNICODE = 2;
    /**
     * Construct a new ConstantPoolInfo object that is of type ASCIZ
     */
    public ConstantPoolInfo(String value) {
  index1 = -1;
  index2 = -1;
  arg1 = null;
  arg2 = null;
  type = ASCIZ;
  strValue = value;
    }
    /**
     * Construct a new ConstantPoolInfo object that is of type INTEGER
     */
    public ConstantPoolInfo(int value) {
  index1 = -1;
  index2 = -1;
  arg1 = null;
  arg2 = null;
  type = INTEGER;
  intValue = value;
    }
    /**
     * Construct a new ConstantPoolInfo object that is of type FLOAT
     */
    public ConstantPoolInfo(float value) {
  index1 = -1;
  index2 = -1;
  arg1 = null;
  arg2 = null;
  type = FLOAT;
  floatValue = value;
    }
    /**
     * Construct a new ConstantPoolInfo object that is of type LONG
     */
    public ConstantPoolInfo(long value) {
  index1 = -1;
  index2 = -1;
  arg1 = null;
  arg2 = null;
  type = LONG;
  longValue = value;
    }
    /**
     * Construct a new ConstantPoolInfo object that is of type DOUBLE
     */
    public ConstantPoolInfo(double value) {
  index1 = -1;
  index2 = -1;
  arg1 = null;
  arg2 = null;
  type = DOUBLE;
  doubleValue = value;
    }
    /**
     * Generic constructor
     */
    public ConstantPoolInfo() {
  index1 = -1;
  index2 = -1;
  arg1 = null;
  arg2 = null;
  type = -1;
    }
    /**
     * return the type of this constant pool item.
     */
    public int isType() {
  return (type);
    }
    public boolean read(DataInputStream dis)
  throws IOException {
  int len;
  char  c;
  type = dis.readByte();
  switch (type) {
      case CLASS:
    name = "Class";
    index1 = dis.readShort();
    index2 = -1;
    break;
      case FIELDREF:
    name = "Field Reference";
    index1 = dis.readShort();
    index2 = dis.readShort();
    break;
      case METHODREF:
    name = "Method Reference";
    index1 = dis.readShort();
    index2 = dis.readShort();
    break;
      case INTERFACE:
    name = "Interface Method Reference";
    index1 = dis.readShort();
    index2 = dis.readShort();
    break;
      case NAMEANDTYPE:
    name = "Name and Type";
    index1 = dis.readShort();
    index2 = dis.readShort();
    break;
      case STRING:
    name = "String";
    index1 = dis.readShort();
    index2 = -1;
    break;
      case INTEGER:
    name = "Integer";
    intValue = dis.readInt();
    break;
      case FLOAT:
    name = "Float";
    floatValue = dis.readFloat();
    break;
      case LONG:
    name = "Long";
    longValue = dis.readLong();
    break;
      case DOUBLE:
    name = "Double";
    doubleValue = dis.readDouble();
    break;
      case ASCIZ:
      case UNICODE:
    if (type == ASCIZ)
        name = "ASCIZ";
    else
        name = "UNICODE";
    StringBuffer xxBuf = new StringBuffer();
    len = dis.readShort();
    while (len > 0) {
        c = (char) (dis.readByte());
        xxBuf.append(c);
        len--;
    }
    strValue = xxBuf.toString();
    break;
      default:
    System.out.println("Warning bad type.");
  }
  return (true);
    }
    public void write(DataOutputStream dos, ConstantPoolInfo pool[])
  throws IOException, Exception {
  dos.write(type);
  switch (type) {
      case CLASS:
      case STRING:
    dos.writeShort(indexOf(arg1, pool));
    break;
      case FIELDREF:
      case METHODREF:
      case INTERFACE:
      case NAMEANDTYPE:
    dos.writeShort(indexOf(arg1, pool));
    dos.writeShort(indexOf(arg2, pool));
    break;
      case INTEGER:
    dos.writeInt(intValue);
    break;
      case FLOAT:
    dos.writeFloat(floatValue);
    break;
      case LONG:
    dos.writeLong(longValue);
    break;
      case DOUBLE:
    dos.writeDouble(doubleValue);
    break;
      case ASCIZ:
      case UNICODE:
    dos.writeShort(strValue.length());
    dos.writeBytes(strValue);
    break;
      default:
    throw new Exception("ConstantPoolInfo::write() - bad type.");
  }
    }
    public String toString() {
  StringBuffer s;
  if (type == ASCIZ) {
      return(strValue);
  }
  if (type == INTEGER) {
      return("= "+intValue);
  }
  if (type == LONG) {
      return("= "+longValue);
  }
  if (type == FLOAT) {
      return("= "+floatValue);
  }
  if (type == DOUBLE) {
      return("= "+doubleValue);
  }
  s = new StringBuffer();
  s.append(name);
  s.append(":");
  if (arg1 != null)
      s.append(arg1.toString());
  else if (index1 != -1)
      s.append("I1["+index1+"], ");
  if (arg2 != null)
      s.append(arg2.toString());
  else if (index2 != -1)
      s.append("I2["+index2+"], ");
  return (s.toString());
    }
    public static short indexOf(ConstantPoolInfo item,
          ConstantPoolInfo pool[])
  throws Exception {
  for (int i = 0; i < pool.length; i++) {
      if (item == pool[i])
    return (short) i;
  }
  throw new Exception("ConstantPoolInfo:: indexOf() - item not in pool.");
    }
    /**
     * Returns true if these constant pool items are identical.
     */
    public boolean isEqual(ConstantPoolInfo cp) {
  if (cp == null)
      return false;
  if (cp.type != type)
      return (false);
  switch (cp.type) {
      case CLASS:
      case STRING:
    return (arg1 == cp.arg1);
      case FIELDREF:
      case METHODREF:
      case INTERFACE:
      case NAMEANDTYPE:
    return ((arg1 == cp.arg1) && (arg2 == cp.arg2));
      case INTEGER:
    return (cp.intValue == intValue);
      case FLOAT:
    return (cp.floatValue == floatValue);
      case LONG:
    return (cp.longValue == longValue);
      case DOUBLE:
    return (cp.doubleValue == doubleValue);
      case ASCIZ:
      case UNICODE:
    return (cp.strValue.compareTo(strValue) == 0);
  }
  return (false);
    }
    /**
     * Returns the reference to the constant pool item that is
     * already in pool, that matches this one.
     */
    public ConstantPoolInfo inPool(ConstantPoolInfo pool[]) {
  for (int i = 1; i < pool.length; i++) {
      if (isEqual(pool[i]))
    return (pool[i]);
  }
  return null;
    }
}
 /*
  * @(#)MethodInfo.java 1.4 95/08/16 Chuck McManis
  *
  * Copyright (c) 1996 Chuck McManis, All Rights Reserved.
  *
  * Permission to use, copy, modify, and distribute this software
  * and its documentation for NON-COMMERCIAL purposes and without
  * fee is hereby granted provided that this copyright notice
  * appears in all copies.
  *
  * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
  * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE
  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
  * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
  */
 /**
  * This class describes a Method as it is stored in the class file.
  * The attribute associated with method is the code that actually implements
  * the method. Since we don't need to manipulate the byte codes directly
  * we leave them as an opaque chunk in the attributes[] array. References
  * in the code are all references into the constant table so when we are
  * modifing a class to use a different object we needn't get into the code
  * level.
  *
  * @version  1.4, 16 Aug 1995
  * @author Chuck McManis
  * @see    ClassFile
  */
  class MethodInfo {
     short    accessFlags;
     ConstantPoolInfo   name;
     ConstantPoolInfo   signature;
     AttributeInfo  attributes[];
     /**
      * Read a method_info from the data stream.
      */
     public boolean read(DataInputStream di, ConstantPoolInfo pool[])
  throws IOException {
  int count;
  accessFlags = di.readShort();
  name = pool[di.readShort()];
  signature = pool[di.readShort()];
  count = di.readShort();
  if (count != 0) {
      attributes = new AttributeInfo[count];
      for (int i = 0; i < count; i++) {
    attributes[i] = new AttributeInfo(); // "code"
    if (! attributes[i].read(di, pool)) {
        return (false);
    }
      }
  }
  return (true);
     }
     /**
      * Write out a method_info, do constant table fixups on the write.
      */
     public void write(DataOutputStream dos, ConstantPoolInfo pool[])
  throws IOException, Exception {
  dos.writeShort(accessFlags);
  dos.writeShort(ConstantPoolInfo.indexOf(name, pool));
  dos.writeShort(ConstantPoolInfo.indexOf(signature, pool));
  if (attributes == null) {
      dos.writeShort(0);
  } else {
      dos.writeShort(attributes.length);
      for (int i = 0; i < attributes.length; i++)
    attributes[i].write(dos, pool);
  }
     }
     /**
      * print out the method, much as you would see it in the source
      * file. The string ClassName is substituted for <init> when
      * printing.
      */
     public String toString(String className) {
  StringBuffer x = new StringBuffer();
  boolean isArray = false;
  String paramSig;
  String returnSig;
  int ndx = 0;
  StringBuffer parameterList = new StringBuffer();
  char  initialParameter = 'a';
  StringBuffer varName = new StringBuffer();
  String s = signature.strValue;
  paramSig = s.substring(s.indexOf('(')+1, s.indexOf(')'));
  returnSig = s.substring(s.indexOf(')')+1);
  x.append(ClassFile.accessString(accessFlags));
  /* catch constructors */
  if ((className != null) && (name.toString().startsWith("")))
      parameterList.append(className);
  else
      parameterList.append(name.toString());
  parameterList.append("(");
  if ((paramSig.length() > 0) && paramSig.charAt(0) != 'V') {
      while (paramSig.length() > 0) {
    varName.setLength(0);
    varName.append(initialParameter);
    initialParameter++;
    parameterList.append(
      ClassFile.typeString(paramSig, varName.toString()));
    paramSig = ClassFile.nextSig(paramSig);
    if (paramSig.length() > 0)
        parameterList.append(", ");
      }
         }
  parameterList.append(")");
  x.append(ClassFile.typeString(returnSig, parameterList.toString()));
  x.append(";");
  return (x.toString());
     }
     /**
      * Generic toString method, init method is unchanged.
      */
     public String toString() {
  return (toString((String)null));
     }
 }
  /*
   * @(#)AttributeInfo.java 1.4 95/08/16 Chuck McManis
   *
   * Copyright (c) 1996 Chuck McManis, All Rights Reserved.
   *
   * Permission to use, copy, modify, and distribute this software
   * and its documentation for NON-COMMERCIAL purposes and without
   * fee is hereby granted provided that this copyright notice
   * appears in all copies.
   *
   * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
   * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
   * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE
   * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
   * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
   */
  /**
   * This class defines the generic Attribute type for Java class files.
   * It is a little bit smart in that for some Attributes it can display
   * them intelligently if it also has access to the constant pool of the
   * current class.
   *
   * @version   1.4, 16 Aug 1995
   * @author  Chuck McManis
   * @see   ClassFile
   */
  class AttributeInfo {
      ConstantPoolInfo  name; // attribute name
      byte    data[]; // attribute's contents
      public AttributeInfo(ConstantPoolInfo newName, byte newData[]) {
    name = name;
    data = newData;
      }
      public AttributeInfo() {
      }
      public boolean read(DataInputStream di, ConstantPoolInfo pool[])
    throws IOException {
    int len;
    name = pool[di.readShort()];
    len = di.readInt();
    data = new byte[len];
    len  = di.read(data);
    if (len != data.length)
        return (false);
    return (true);
      }
      public void write(DataOutputStream dos, ConstantPoolInfo pool[])
    throws IOException, Exception {
    dos.writeShort(ConstantPoolInfo.indexOf(name, pool));
    dos.writeInt(data.length);
    dos.write(data, 0, data.length);
      }
      short indexFromBytes(byte a[]) {
    return (short)(((a[0] << 8) & (0xff << 8)) |
             ((a[1] << 0) & (0xff << 0)));
      }
      public String toString(ConstantPoolInfo pool[]) {
    StringBuffer x = new StringBuffer();
    String type = name.toString();
    ConstantPoolInfo item;
    if (type.compareTo("ConstantValue") == 0) {
        item = pool[indexFromBytes(data)];
        return (item.toString());
    } else if (type.compareTo("SourceFile") == 0) {
        item = pool[indexFromBytes(data)];
        return (item.toString());
    } else {
        x.append(type+"<"+data.length+" bytes>");
    }
    return (x.toString());
      }
      public String toBoolean(ConstantPoolInfo pool[]) {
    ConstantPoolInfo item = pool[indexFromBytes(data)];
    if (item.intValue == 0)
        return ("= false");
    return ("= true");
      }
      public String toString() {
    return (name.toString()+" <"+data.length+" bytes>");
      }
  }
  /*
   * @(#)FieldInfo.java 1.3 95/08/16 Chuck McManis
   *
   * Copyright (c) 1996 Chuck McManis, All Rights Reserved.
   *
   * Permission to use, copy, modify, and distribute this software
   * and its documentation for NON-COMMERCIAL purposes and without
   * fee is hereby granted provided that this copyright notice
   * appears in all copies.
   *
   * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
   * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
   * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS SHALL NOT BE
   * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING,
   * MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
   */
  /**
   * This class defines a FieldInfo in the class file. Fields are used to
   * describe instance variables in a class. The toString() method is
   * augmented by a version that takes an array of ConstantPoolInfo
   * objects (a constant pool). When a constant pool is available the
   * toString() method generates a declaration for the field as it would
   * appear in Java source.
   *
   * @version   1.3, 16 Aug 1995
   * @author  Chuck McManis
   * @see   ClassFile
   */
   class FieldInfo {
      short   accessFlags;
      ConstantPoolInfo  name;
      ConstantPoolInfo  signature;
      AttributeInfo attributes[];
      public boolean read(DataInputStream di, ConstantPoolInfo pool[])
    throws IOException {
    int count;
    accessFlags = di.readShort();
    name = pool[di.readShort()];
    signature = pool[di.readShort()];
    count = di.readShort();
    if (count != 0) {
        attributes = new AttributeInfo[count];
        for (int i = 0; i < count; i++) {
      attributes[i] = new AttributeInfo();
      if (! attributes[i].read(di, pool))
          return (false);
        }
    }
    return (true);
      }
      public void write(DataOutputStream dos, ConstantPoolInfo pool[])
    throws IOException, Exception {
    dos.writeShort(accessFlags);
    dos.writeShort(ConstantPoolInfo.indexOf(name, pool));
    dos.writeShort(ConstantPoolInfo.indexOf(signature, pool));
    if (attributes == null) {
        dos.writeShort(0);
    } else {
        dos.writeShort(attributes.length);
        for (int i = 0; i < attributes.length; i++) {
          attributes[i].write(dos, pool);
        }
    }
      }
      public String toString() {
    StringBuffer x = new StringBuffer();
    x.append(ClassFile.accessString(accessFlags));
    x.append(ClassFile.typeString(signature.toString(), name.toString()));
    if (attributes != null) {
        x.append(" = "+attributes[0].toString());
    }
    return (x.toString());
      }
      public String toString(ConstantPoolInfo pool[]) {
    StringBuffer x = new StringBuffer();
    String  mytype;
    x.append(ClassFile.accessString(accessFlags));
    mytype = ClassFile.typeString(signature.toString(), name.toString());
    x.append(mytype);
    if (attributes != null) {
        if (mytype.startsWith("boolean")) {
      x.append(" "+attributes[0].toBoolean(pool));
        } else
            x.append(" "+attributes[0].toString(pool));
    }
    return (x.toString());
      }
  }