/* --- Copyright (c) Chris Rathman 1999. All rights reserved. -----------------
 > File:        jasper/Pool_Collection.java
 > Purpose:     Constant pool collection - symbol table
 > Author:      Chris Rathman, 12 June 1999
 > Version:     1.00
 */
package jasper;
import java.io.*;
import java.lang.reflect.*;

/*=======================================================================
 = Class:         Pool_Collection                                       =
 =                                                                      =
 = Desc:          Constant Table Pool                                   =
 =======================================================================*/
class Pool_Collection {
   private short count;                // number of entries in the constant pool table (no zero entry)
   private int[] poolType;             // type of pool constant
   private Pool[] pool;                // constant pool table

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read constants from the input stream                  -
    -----------------------------------------------------------------------*/
   Pool_Collection(DataInputStream ios) throws IOException {
      // read in the number of entries in the table
      count = ios.readShort();

      // allocate the arrays to hold the pool constants
      poolType = new int[count];
      pool = new Pool[count];

      // read in the pool constants from the input stream
      for (int i = 1; i < count; i++) {
         // get the type of constant
         poolType[i] = ios.read();

         try {
            Constructor myConstructor;

            // get subclass that handles the constant
            Class newClass = (Class)dispatch[poolType[i]][2];
            try {
               // for some reason this technique is not working in JDK1.2?
               myConstructor = newClass.getConstructor(new Class[] {ios.getClass(), Pool_Collection.class});
            } catch (NoSuchMethodException e) {
               // since there is only one constructor, we can use this technique if above fails
               myConstructor = newClass.getDeclaredConstructors()[0];
            }

            // read in the constant
            pool[i] = (Pool)myConstructor.newInstance(new Object[] {ios, this});

            // skip over entries for types long and double (they occupy two indexes in constant pool)
            for (int n = 1; n < Integer.parseInt((String)dispatch[poolType[i]][1]); n++) {
               pool[i+1] = pool[i];
               i = i + 1;
            }

         } catch (InstantiationException e) {
            throw new IOException("InstantiationException");
         } catch (IllegalAccessException e) {
            throw new IOException("IllegalAccessException");
         } catch (InvocationTargetException e) {
            throw new IOException("InvocationTargetException");
         }
      }
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   String toString(int cptIndex) {
      if ((cptIndex == 0) || (cptIndex >= count)) {
         System.out.println("Index error for constant pool table: " + cptIndex);
         return "";
      }
      return pool[cptIndex].toString();
   }

   /*-----------------------------------------------------------------------
    - Method:        isFieldref                                            -
    -                                                                      -
    - Desc:          test if a class field is being referenced             -
    -----------------------------------------------------------------------*/
   boolean isFieldref(int cptIndex) {
      return pool[cptIndex].isFieldref();
   }

   /*-----------------------------------------------------------------------
    - Method:        isMethodref                                           -
    -                                                                      -
    - Desc:          test if a class method is being referenced            -
    -----------------------------------------------------------------------*/
   boolean isMethodref(int cptIndex) {
      return pool[cptIndex].isMethodref();
   }

   /*-----------------------------------------------------------------------
    - Method:        isInterfaceMethodref                                  -
    -                                                                      -
    - Desc:          test if an interface method is being referenced       -
    -----------------------------------------------------------------------*/
   boolean isInterfaceMethodref(int cptIndex) {
      return pool[cptIndex].isInterfaceMethodref();
   }

   /*-----------------------------------------------------------------------
    - Method:        browseString                                          -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   String browseString(int cptIndex) {
      return pool[cptIndex].browseString();
   }

   /*-----------------------------------------------------------------------
    - Method:        browseDescriptor                                      -
    -                                                                      -
    - Desc:          return string representation of type descriptor       -
    -----------------------------------------------------------------------*/
   String browseDescriptor(int cptIndex) {
      return pool[cptIndex].browseDescriptor();
   }

   /*-----------------------------------------------------------------------
    - Field:         dispatch                                              -
    -                                                                      -
    - Desc:          array used to dispatch for object instantiation       -
    -                   element[i][0] = constant type                      -
    -                   element[i][1] = entries occupied constant          -
    -                   element[i][2] = class name that handles attribute  -
    -----------------------------------------------------------------------*/
   private static Object[][] dispatch = {
      {"0",  "1", null},
      {"1",  "1", Pool_Utf8.class},
      {"2",  "1", Pool_Unicode.class},
      {"3",  "1", Pool_Integer.class},
      {"4",  "1", Pool_Float.class},
      {"5",  "2", Pool_Long.class},
      {"6",  "2", Pool_Double.class},
      {"7",  "1", Pool_Class.class},
      {"8",  "1", Pool_String.class},
      {"9",  "1", Pool_Fieldref.class},
      {"10", "1", Pool_Methodref.class},
      {"11", "1", Pool_InterfaceMethodref.class},
      {"12", "1", Pool_NamedType.class}
   };
}

/*=======================================================================
 = Class:         Pool                                                  =
 =                                                                      =
 = Desc:          abstract class for constant entries                   =
 =======================================================================*/
abstract class Pool {
   protected Pool_Collection pool;     // store of constant pool table for use by cross-ref'd constants

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read constant from the input stream                   -
    -----------------------------------------------------------------------*/
   Pool(Pool_Collection pool) {
      this.pool = pool;
   }

   /*-----------------------------------------------------------------------
    - Method:        escapeString                                          -
    -                                                                      -
    - Desc:          replace unprintable chars with octal constant (\xxx)  -
    -----------------------------------------------------------------------*/
   static String escapeString(String raw) {
      String retVal = raw;
      for (int i = 0; i < retVal.length(); i++) {
         if ((retVal.charAt(i) < ' ') || (retVal.charAt(i) > '~') || (retVal.charAt(i) == '\"')) {
            String s = Integer.toOctalString(retVal.charAt(i));
            while (s.length() < 3) s = '0' + s;
            s = '\\' + s;
            if (i > 0) {
               retVal = retVal.substring(0, i) + s + retVal.substring(i+1);
            } else {
               retVal = s + retVal.substring(i+1);
            }
         }
      }
      return retVal;
   }

   /*-----------------------------------------------------------------------
    - Method:        browseDescriptor                                      -
    -                                                                      -
    - Desc:          convert the descriptor to resemble java code          -
    -----------------------------------------------------------------------*/
   String browseDescriptor(String s) {
      String funstring = "";           // function or field name
      String typestring = "";          // type of the function or field
      String arraystring = "";         // array indexes of the type descriptor
      int i = 0;                       // index used to traverse the string

      // pull off the function parameters
      if (s.charAt(0) == '(') {
         funstring = " (" + browseDescriptor(s.substring(1)) + ")";
         s = s.substring(s.indexOf(')') + 1);
      }

      // pull off the array indexes
      while (s.charAt(i) == '[') {
         i++;
         arraystring = arraystring + "[]";
      }

      // get the types
      switch (s.charAt(i++)) {
         case 'Z': typestring = "boolean"; break;
         case 'B': typestring = "byte";    break;
         case 'C': typestring = "char";    break;
         case 'S': typestring = "short";   break;
         case 'I': typestring = "int";     break;
         case 'J': typestring = "long";    break;
         case 'F': typestring = "float";   break;
         case 'D': typestring = "double";  break;
         case 'V': typestring = "void";    break;
         case 'L':
            // object type
            int j = s.indexOf(';');
            typestring = s.substring(i, j).replace('/', '.');
            i = j;
            break;
         case ')':
            // end of the function parameters
            i = i - 1;
            break;
      }

      // go past semicolons
      while ((s.length() > i) && (s.charAt(i) == ';')) i++;

      // if end of string then return
      if (s.length() <= i) return typestring + arraystring + funstring;

      // if end of function parameters then return
      if (s.charAt(i) == ')') return typestring + arraystring + funstring;

      // return the results
      return typestring + arraystring + ", " + browseDescriptor(s.substring(i)) + funstring;
   }

   /*-----------------------------------------------------------------------
    - Method:        browseDescriptor                                      -
    -                                                                      -
    - Desc:          overload the function to allow external calls         -
    -----------------------------------------------------------------------*/
   String browseDescriptor() {
      return browseDescriptor(this.browseString());
   }

   /*-----------------------------------------------------------------------
    - Method:        browseString                                          -
    -                                                                      -
    - Desc:          default browse string to same as toString method      -
    -----------------------------------------------------------------------*/
   String browseString() {
      return toString();
   }

   /*-----------------------------------------------------------------------
    - Method:        isFieldref                                            -
    -                                                                      -
    - Desc:          default to class field not being referenced           -
    -----------------------------------------------------------------------*/
   boolean isFieldref() {
      return false;
   }

   /*-----------------------------------------------------------------------
    - Method:        isMethodref                                           -
    -                                                                      -
    - Desc:          default to class method not being referenced          -
    -----------------------------------------------------------------------*/
   boolean isMethodref() {
      return false;
   }

   /*-----------------------------------------------------------------------
    - Method:        isInterfaceMethodref                                  -
    -                                                                      -
    - Desc:          default to interface method not being referenced      -
    -----------------------------------------------------------------------*/
   boolean isInterfaceMethodref() {
      return false;
   }
}

/*=======================================================================
 = Class:         Pool_Utf8                                             =
 =                                                                      =
 = Desc:          UTF-8 encoded string constant                         =
 =======================================================================*/
class Pool_Utf8 extends Pool {
   private String value = "";          // value of UTF-8 string constant

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read UTF-8 constant from the input stream             -
    -----------------------------------------------------------------------*/
   Pool_Utf8(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      short length = ios.readShort();
      for (int i = 0; i < length; i++) {
         int a = ios.read();
         if ((a & 0x80) == 0) {
            value = value + (char)a;
         } else if ((a & 0x20) == 0) {
            int b = ios.read();
            value = value + (char)(((a & 0x1f) << 6) + (b & 0x3f));
            i++;
         } else {
            int b = ios.read();
            int c = ios.read();
            value = value + (char)(((a & 0xf) << 12) + ((b & 0x3f) << 6) + (c & 0x3f));
            i += 2;
         }
      }
      value = escapeString(value);
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return value;
   }
}

/*=======================================================================
 = Class:         Pool_Unicode                                          =
 =                                                                      =
 = Desc:          Unicode string constant                               =
 =======================================================================*/
class Pool_Unicode extends Pool {
   private String value = "";          // value of unicode string constant

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read unicode constant from the input stream           -
    -----------------------------------------------------------------------*/
   Pool_Unicode(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      short length = ios.readShort();
      for (int i = 0; i < length; i++) {
         value = value + ios.readChar();
      }
      value = escapeString(value);
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return value;
   }
}

/*=======================================================================
 = Class:         Pool_Integer                                          =
 =                                                                      =
 = Desc:          Integer constant                                      =
 =======================================================================*/
class Pool_Integer extends Pool {
   private int value;                  // value of int constant

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read integer constant from the input stream           -
    -----------------------------------------------------------------------*/
   Pool_Integer(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      value = ios.readInt();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return Integer.toString(value);
   }
}

/*=======================================================================
 = Class:         Pool_Float                                            =
 =                                                                      =
 = Desc:          Float constant                                        =
 =======================================================================*/
class Pool_Float extends Pool {
   private float value;                // value of float constant

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read float constant from the input stream             -
    -----------------------------------------------------------------------*/
   Pool_Float(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      value = ios.readFloat();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return Float.toString(value);
   }
}

/*=======================================================================
 = Class:         Pool_Long                                             =
 =                                                                      =
 = Desc:          Long constant                                         =
 =======================================================================*/
class Pool_Long extends Pool {
   private long value;                 // value of long constant

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read long constant from the input stream              -
    -----------------------------------------------------------------------*/
   Pool_Long(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      value = ios.readLong();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return Long.toString(value);
   }
}

/*=======================================================================
 = Class:         Pool_Double                                           =
 =                                                                      =
 = Desc:          Double constant                                       =
 =======================================================================*/
class Pool_Double extends Pool {
   private double value;               // value of double constant

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read double constant from the input stream            -
    -----------------------------------------------------------------------*/
   Pool_Double(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      value = ios.readDouble();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return Double.toString(value);
   }
}

/*=======================================================================
 = Class:         Pool_Class                                            =
 =                                                                      =
 = Desc:          Class constant                                        =
 =======================================================================*/
class Pool_Class extends Pool {
   private short index;                // class constant (index into constant pool table)

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read class constant from the input stream             -
    -----------------------------------------------------------------------*/
   Pool_Class(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      index = ios.readShort();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return pool.toString(index);
   }

   /*-----------------------------------------------------------------------
    - Method:        browseString                                          -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   String browseString() {
      return toString().replace('/', '.');
   }
}

/*=======================================================================
 = Class:         Pool_String                                           =
 =                                                                      =
 = Desc:          String constant                                       =
 =======================================================================*/
class Pool_String extends Pool {
   private short index;                // constant string (index into constant pool table)

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read string constant from the input stream            -
    -----------------------------------------------------------------------*/
   Pool_String(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      index = ios.readShort();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return "\"" + pool.toString(index) + "\"";
   }
}

/*=======================================================================
 = Class:         Pool_Fieldref                                         =
 =                                                                      =
 = Desc:          Class field reference constant                        =
 =======================================================================*/
class Pool_Fieldref extends Pool {
   private short classIndex;           // class name (index into the constant pool table)
   private short namedtypeIndex;       // return type and parameters (index into the constant pool table)

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read fieldref constant from the input stream          -
    -----------------------------------------------------------------------*/
   Pool_Fieldref(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      classIndex = ios.readShort();
      namedtypeIndex = ios.readShort();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return pool.toString(classIndex) + "/" + pool.toString(namedtypeIndex);
   }

   /*-----------------------------------------------------------------------
    - Method:        browseString                                          -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   String browseString() {
      int i;
      String s;
      String fieldType;
      String fieldName;
      String className;

      s = pool.browseString(namedtypeIndex);
      i = s.indexOf(' ');

      fieldType = s.substring(0, i);
      fieldName = s.substring(i+1);
      className = pool.browseString(classIndex);

      return fieldType + " " + className + "." + fieldName;
   }

   /*-----------------------------------------------------------------------
    - Method:        isFieldref                                            -
    -                                                                      -
    - Desc:          flag that a class field is referenced                 -
    -----------------------------------------------------------------------*/
   boolean isFieldref() {
      return true;
   }
}

/*=======================================================================
 = Class:         Pool_Methodref                                        =
 =                                                                      =
 = Desc:          Class method reference constant                       =
 =======================================================================*/
class Pool_Methodref extends Pool {
   private short classIndex;           // class name (index into the constant pool table)
   private short namedtypeIndex;       // return type and parameters (index into the constant pool table)

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read methodref constant from the input stream         -
    -----------------------------------------------------------------------*/
   Pool_Methodref(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      classIndex = ios.readShort();
      namedtypeIndex = ios.readShort();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return pool.toString(classIndex) + "/" + pool.toString(namedtypeIndex);
   }

   /*-----------------------------------------------------------------------
    - Method:        browseString                                          -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   String browseString() {
      String s = pool.browseString(namedtypeIndex);
      int i = s.indexOf(' ');
      int j = s.indexOf('(');

      String returnType = s.substring(0, i);
      String functionName = s.substring(i+1, j);
      String functionParams = s.substring(j);
      String className = pool.browseString(classIndex);

      if (functionName.equals("<init>")) {
         return "new " + className + functionParams;
      } else {
         return returnType + " " + className + "." + functionName + functionParams;
      }
   }

   /*-----------------------------------------------------------------------
    - Method:        isMethodref                                           -
    -                                                                      -
    - Desc:          flag that a class method is referenced                -
    -----------------------------------------------------------------------*/
   boolean isMethodref() {
      return true;
   }
}

/*=======================================================================
 = Class:         Pool_InterfaceMethodref                               =
 =                                                                      =
 = Desc:          Interface method reference constant                   =
 =======================================================================*/
class Pool_InterfaceMethodref extends Pool {
   private short classIndex;           // class name (index into the constant pool table)
   private short namedtypeIndex;       // return type and parameters (index into the constant pool table)

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read interfacemethodref constant from input stream    -
    -----------------------------------------------------------------------*/
   Pool_InterfaceMethodref(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      classIndex = ios.readShort();
      namedtypeIndex = ios.readShort();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      return pool.toString(classIndex) + "/" + pool.toString(namedtypeIndex);
   }

   /*-----------------------------------------------------------------------
    - Method:        browseString                                          -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   String browseString() {
      String s = pool.browseString(namedtypeIndex);
      int i = s.indexOf(' ');
      int j = s.indexOf('(');

      String returnType = s.substring(0, i);
      String functionName = s.substring(i+1, j);
      String functionParams = s.substring(j);
      String className = pool.browseString(classIndex);

      if (functionName.equals("<init>")) {
         return "new " + className + functionParams;
      } else {
         return returnType + " " + className + "." + functionName + functionParams;
      }
   }

   /*-----------------------------------------------------------------------
    - Method:        isFieldref                                            -
    -                                                                      -
    - Desc:          flag that an interface method is referenced           -
    -----------------------------------------------------------------------*/
   boolean isInterfaceMethodref() {
      return true;
   }
}

/*=======================================================================
 = Class:         Pool_NamedType                                        =
 =                                                                      =
 = Desc:          Class name and type constant                          =
 =======================================================================*/
class Pool_NamedType extends Pool {
   private short nameIndex;            // field or class name (index into the constant pool table)
   private short descriptorIndex;      // return type and parameters (index into the constant pool table)

   /*-----------------------------------------------------------------------
    - Method:        Class Constructor                                     -
    -                                                                      -
    - Desc:          read namedtype constant from the input stream         -
    -----------------------------------------------------------------------*/
   Pool_NamedType(DataInputStream ios, Pool_Collection pool) throws IOException {
      super(pool);
      nameIndex = ios.readShort();
      descriptorIndex = ios.readShort();
   }

   /*-----------------------------------------------------------------------
    - Method:        toString                                              -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   public String toString() {
      String s = pool.toString(descriptorIndex);
      if (s.charAt(0) != '(') s = " " + s;
      return pool.toString(nameIndex) + s;
   }

   /*-----------------------------------------------------------------------
    - Method:        browseString                                          -
    -                                                                      -
    - Desc:          return string representation of constant              -
    -----------------------------------------------------------------------*/
   String browseString() {
      String s = pool.browseDescriptor(descriptorIndex);
      int i = s.indexOf('(');
      if (i > 0) {
         // this is a method
         return s.substring(0, i) + pool.browseString(nameIndex) + s.substring(i);
      } else {
         // this is a field
         return s + " " + pool.browseString(nameIndex);
      }
   }
}

Chris Rathman / Chris.Rathman@gmail.com