package cutils.classDumper;


import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;


/**

 * 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

 */


public 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;

  }


}