public class ConstantClassInfo extends ConstantInfo {
    private final TypeDesc mType;
    private final ConstantUTFInfo mNameConstant;
    
    public ConstantClassInfo(ConstantUTFInfo nameConstant) {
        super(TAG_CLASS);
        String name = nameConstant.getValue();
        if (!name.endsWith(";") && !name.startsWith("[")) {
            mType = TypeDesc.forClass(name);
        } else {
            mType = TypeDesc.forDescriptor(name);
        }
        mNameConstant = nameConstant;
    }

    public ConstantClassInfo(ConstantPool cp, String className) {
        super(TAG_CLASS);
        String desc = className.replace('.', '/');
        mType = TypeDesc.forClass(className);
        mNameConstant = cp.addConstantUTF(desc);
    }
    
    /**
     * Used to describe an array class.
     */
    public ConstantClassInfo(ConstantPool cp, String className, int dim) {
        super(TAG_CLASS);
        TypeDesc type = TypeDesc.forClass(className);
        String desc;
        if (dim > 0) {
            while (--dim >= 0) {
                type = type.toArrayType();
            }
            desc = type.getDescriptor();
        } else {
            desc = className.replace('.', '/');
        }
        mType = type;
        mNameConstant = cp.addConstantUTF(desc);
    }
    
    public ConstantClassInfo(ConstantPool cp, TypeDesc type) {
        super(TAG_CLASS);
        String desc;
        if (type.isArray()) {
            desc = type.getDescriptor();
        } else {
            desc = type.getRootName().replace('.', '/');
        }
        mType = type;
        mNameConstant = cp.addConstantUTF(desc);
    }

    public TypeDesc getType() {
        return mType;
    }

    public int hashCode() {
        return mType.hashCode();
    }
    
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof ConstantClassInfo) {
            ConstantClassInfo other = (ConstantClassInfo)obj;
            return mType.equals(other.mType);
        }
        return false;
    }
    
    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeShort(mNameConstant.getIndex());
    }

    public String toString() {
        return "CONSTANT_Class_info: ".concat(getType().getFullName());
    }
} public class ConstantDoubleInfo extends ConstantInfo {
    private final double mValue;
    
    public ConstantDoubleInfo(double value) {
        super(TAG_DOUBLE);
        mValue = value;
    }

    public double getValue() {
        return mValue;
    }

    public int hashCode() {
        long bits = Double.doubleToLongBits(mValue);
        return (int)(bits ^ (bits >>> 32));
    }
    
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof ConstantDoubleInfo) {
            ConstantDoubleInfo other = (ConstantDoubleInfo)obj;
            return mValue == other.mValue;
        }
        return false;
    }
    
    protected int getEntryCount() {
        return 2;
    }

    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeDouble(mValue);
    }

    public String toString() {
        return "CONSTANT_Double_info: " + mValue;
    }
} public class ConstantFieldInfo extends ConstantInfo {
    private final ConstantClassInfo mParentClass;
    private final ConstantNameAndTypeInfo mNameAndType;
    
    public ConstantFieldInfo(ConstantClassInfo parentClass,
                             ConstantNameAndTypeInfo nameAndType) {
        super(TAG_FIELD);
        
        mParentClass = parentClass;
        mNameAndType = nameAndType;
    }
    
    public ConstantClassInfo getParentClass() {
        return mParentClass;
    }
    
    public ConstantNameAndTypeInfo getNameAndType() {
        return mNameAndType;
    }

    public int hashCode() {
        return mNameAndType.hashCode();
    }
    
    public boolean equals(Object obj) {
        if (obj instanceof ConstantFieldInfo) {
            ConstantFieldInfo other = (ConstantFieldInfo)obj;
            return (mParentClass.equals(other.mParentClass) && 
                    mNameAndType.equals(other.mNameAndType));
        }
        
        return false;
    }
    
    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeShort(mParentClass.getIndex());
        dout.writeShort(mNameAndType.getIndex());
    }

    public String toString() {
        StringBuffer buf = new StringBuffer("CONSTANT_Fieldref_info: ");
        buf.append(getParentClass().getType().getFullName());

        ConstantNameAndTypeInfo cnati = getNameAndType();

        buf.append(' ');
        buf.append(cnati.getName());
        buf.append(' ');
        buf.append(cnati.getType());

        return buf.toString();
    }
} public class ConstantFloatInfo extends ConstantInfo {
    private final float mValue;
    
    public ConstantFloatInfo(float value) {
        super(TAG_FLOAT);
        mValue = value;
    }

    public float getValue() {
        return mValue;
    }

    public int hashCode() {
        return Float.floatToIntBits(mValue);
    }
    
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof ConstantFloatInfo) {
            ConstantFloatInfo other = (ConstantFloatInfo)obj;
            return mValue == other.mValue;
        }
        return false;
    }
    
    protected boolean hasPriority() {
        return true;
    }

    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeFloat(mValue);
    }

    public String toString() {
        return "CONSTANT_Float_info: " + mValue;
    }
} public class ConstantIntegerInfo extends ConstantInfo {
    private final int mValue;
    
    public ConstantIntegerInfo(int value) {
        super(TAG_INTEGER);
        mValue = value;
    }
    
    public int getValue() {
        return mValue;
    }

    public int hashCode() {
        return mValue;
    }
    
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof ConstantIntegerInfo) {
            ConstantIntegerInfo other = (ConstantIntegerInfo)obj;
            return mValue == other.mValue;
        }
        return false;
    }
    
    protected boolean hasPriority() {
        return true;
    }

    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeInt(mValue);
    }

    public String toString() {
        return "CONSTANT_Integer_info: " + mValue;
    }
} public class ConstantInterfaceMethodInfo extends ConstantInfo {
    private final ConstantClassInfo mParentClass;
    private final ConstantNameAndTypeInfo mNameAndType;
    
    public ConstantInterfaceMethodInfo(ConstantClassInfo parentClass,
                                       ConstantNameAndTypeInfo nameAndType) {
        super(TAG_INTERFACE_METHOD);
        mParentClass = parentClass;
        mNameAndType = nameAndType;
    }

    public ConstantClassInfo getParentClass() {
        return mParentClass;
    }
    
    public ConstantNameAndTypeInfo getNameAndType() {
        return mNameAndType;
    }
    
    public int hashCode() {
        return mNameAndType.hashCode();
    }
    
    public boolean equals(Object obj) {
        if (obj instanceof ConstantInterfaceMethodInfo) {
            ConstantInterfaceMethodInfo other = 
                (ConstantInterfaceMethodInfo)obj;
            return (mParentClass.equals(other.mParentClass) && 
                    mNameAndType.equals(other.mNameAndType));
        }
        
        return false;
    }
    
    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeShort(mParentClass.getIndex());
        dout.writeShort(mNameAndType.getIndex());
    }

    public String toString() {
        StringBuffer buf = 
            new StringBuffer("CONSTANT_InterfaceMethodref_info: ");
        buf.append(getParentClass().getType().getFullName());

        ConstantNameAndTypeInfo cnati = getNameAndType();

        buf.append(' ');
        buf.append(cnati.getName());
        buf.append(' ');
        buf.append(cnati.getType());

        return buf.toString();
    }
} public class ConstantLongInfo extends ConstantInfo {
    private final long mValue;
    
    public ConstantLongInfo(long value) {
        super(TAG_LONG);
        mValue = value;
    }
    
    public long getValue() {
        return mValue;
    }

    public int hashCode() {
        return (int)(mValue ^ (mValue >>> 32));
    }
    
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof ConstantLongInfo) {
            ConstantLongInfo other = (ConstantLongInfo)obj;
            return mValue == other.mValue;
        }
        return false;
    }
    
    protected int getEntryCount() {
        return 2;
    }

    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeLong(mValue);
    }

    public String toString() {
        return "CONSTANT_Long_info: " + mValue;
    }
} public class ConstantMethodInfo extends ConstantInfo {
    private final ConstantClassInfo mParentClass;
    private final ConstantNameAndTypeInfo mNameAndType;
    
    public ConstantMethodInfo(ConstantClassInfo parentClass,
                              ConstantNameAndTypeInfo nameAndType) {
        super(TAG_METHOD);
        mParentClass = parentClass;
        mNameAndType = nameAndType;
    }

    public ConstantClassInfo getParentClass() {
        return mParentClass;
    }
    
    public ConstantNameAndTypeInfo getNameAndType() {
        return mNameAndType;
    }

    public int hashCode() {
        return mNameAndType.hashCode();
    }
    
    public boolean equals(Object obj) {
        if (obj instanceof ConstantMethodInfo) {
            ConstantMethodInfo other = (ConstantMethodInfo)obj;
            return (mParentClass.equals(other.mParentClass) && 
                    mNameAndType.equals(other.mNameAndType));
        }
        
        return false;
    }
    
    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeShort(mParentClass.getIndex());
        dout.writeShort(mNameAndType.getIndex());
    }

    public String toString() {
        StringBuffer buf = new StringBuffer("CONSTANT_Methodref_info: ");
        buf.append(getParentClass().getType().getFullName());

        ConstantNameAndTypeInfo cnati = getNameAndType();

        buf.append(' ');
        buf.append(cnati.getName());
        buf.append(' ');
        buf.append(cnati.getType());

        return buf.toString();
    }
} public class ConstantNameAndTypeInfo extends ConstantInfo {
    private final ConstantUTFInfo mNameConstant;
    private final ConstantUTFInfo mDescriptorConstant;
    
    private final Descriptor mType;
    
    public ConstantNameAndTypeInfo(ConstantUTFInfo nameConstant,
                                   ConstantUTFInfo descConstant) {
        super(TAG_NAME_AND_TYPE);
        mNameConstant = nameConstant;
        mDescriptorConstant = descConstant;
        mType = Descriptor.parse(descConstant.getValue());
    }

    public ConstantNameAndTypeInfo(ConstantPool cp, 
                                   String name, 
                                   Descriptor type) {
        super(TAG_NAME_AND_TYPE);
        mNameConstant = cp.addConstantUTF(name);
        mDescriptorConstant = cp.addConstantUTF(type.getDescriptor());
        mType = type;
    }
    
    public String getName() {
        return mNameConstant.getValue();
    }

    public Descriptor getType() {
        return mType;
    }

    public int hashCode() {
        return mNameConstant.hashCode();
    }
    
    public boolean equals(Object obj) {
        if (obj instanceof ConstantNameAndTypeInfo) {
            ConstantNameAndTypeInfo other = (ConstantNameAndTypeInfo)obj;
            return mNameConstant.equals(other.mNameConstant) && 
                mType.getDescriptor().equals(other.mType.getDescriptor());
        }
        
        return false;
    }
    
    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeShort(mNameConstant.getIndex());
        dout.writeShort(mDescriptorConstant.getIndex());
    }

    public String toString() {
        return "CONSTANT_NameAndType_info: " + getName() + ", " + getType();
    }
} public class ConstantStringInfo extends ConstantInfo {
    private final ConstantUTFInfo mStringConstant;
    
    public ConstantStringInfo(ConstantUTFInfo constant) {
        super(TAG_STRING);
        mStringConstant = constant;
    }

    public ConstantStringInfo(ConstantPool cp, String str) {
        super(TAG_STRING);
        mStringConstant = cp.addConstantUTF(str);
    }
    
    public String getValue() {
        return mStringConstant.getValue();
    }

    public int hashCode() {
        return mStringConstant.hashCode();
    }
    
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof ConstantStringInfo) {
            ConstantStringInfo other = (ConstantStringInfo)obj;
            return mStringConstant.equals(other.mStringConstant);
        }
        return false;
    }
    
    protected boolean hasPriority() {
        return true;
    }

    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeShort(mStringConstant.getIndex());
    }

    public String toString() {
        return "CONSTANT_String_info: " + getValue();
    }
} public class ConstantUTFInfo extends ConstantInfo {
    private final String mStr;
    
    public ConstantUTFInfo(String str) {
        super(TAG_UTF8);
        mStr = str;
    }
    
    public String getValue() {
        return mStr;
    }

    public int hashCode() {
        return mStr.hashCode();
    }
    
    public boolean equals(Object obj) {
        if (obj instanceof ConstantUTFInfo) {
            ConstantUTFInfo other = (ConstantUTFInfo)obj;
            return mStr.equals(other.mStr);
        }
        
        return false;
    }
    
    public void writeTo(DataOutput dout) throws IOException {
        super.writeTo(dout);
        dout.writeUTF(mStr);
    }

    public String toString() {
        return "CONSTANT_Utf8_info: " + getValue();
    }
}
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.cojen.classfile.constant.ConstantClassInfo;
import org.cojen.classfile.constant.ConstantDoubleInfo;
import org.cojen.classfile.constant.ConstantFieldInfo;
import org.cojen.classfile.constant.ConstantFloatInfo;
import org.cojen.classfile.constant.ConstantIntegerInfo;
import org.cojen.classfile.constant.ConstantInterfaceMethodInfo;
import org.cojen.classfile.constant.ConstantLongInfo;
import org.cojen.classfile.constant.ConstantMethodInfo;
import org.cojen.classfile.constant.ConstantNameAndTypeInfo;
import org.cojen.classfile.constant.ConstantStringInfo;
import org.cojen.classfile.constant.ConstantUTFInfo;
/*
 *  Copyright 2004-2010 Brian S O'Neill
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.cojen.classfile;


/**
 * This class corresponds to the constant_pool structure as defined in <i>The
 * Java Virtual Machine Specification</i>.
 * 
 * <p>ConstantPool entries are not written out in the order in which they were
 * added to it. Instead, their ordering is changed such that String, Integer
 * and Float constants are written out first. This provides a slight 
 * optimization for referencing these constants from a code attribute.
 * It means that Opcode.LDC will more likely be used (one-byte index) than 
 * Opcode.LDC_W (two-byte index).
 * 
 * @author Brian S O'Neill
 * @see Opcode
 */
public class ConstantPool {
    // A set of ConstantInfo objects.
    private Map<ConstantInfo, ConstantInfo> mConstants = new HashMap<ConstantInfo, ConstantInfo>();
    // Indexed list of constants.
    private Vector<ConstantInfo> mIndexedConstants;
    private int mEntries;

    // Preserve the order only if the constant pool was read in.
    private boolean mPreserveOrder;

    ConstantPool() {
    }

    private ConstantPool(Vector<ConstantInfo> indexedConstants) {
        mIndexedConstants = indexedConstants;

        int size = indexedConstants.size();
        for (int i=1; i<size; i++) {
            ConstantInfo ci = indexedConstants.get(i);
            if (ci != null) {
                mConstants.put(ci, ci);
                mEntries += ci.getEntryCount();
            }
        }

        mPreserveOrder = true;
    }

    /**
     * Returns a constant from the pool by index, or null if not found. If this
     * constant pool has not yet been written or was not created by the read
     * method, indexes are not assigned, and an IllegalStateException is
     * thrown.
     *
     * @throws ArrayIndexOutOfBoundsException if index is out of range.
     * @throws IllegalStateException if indexes are not assigned
     */
    public ConstantInfo getConstant(int index) {
        if (mIndexedConstants == null) {
            throw new IllegalStateException
                ("Constant pool indexes have not been assigned");
        }

        return mIndexedConstants.get(index);
    }

    /**
     * Returns all the constants in the pool, in no particular order.
     */
    public Set<ConstantInfo> getAllConstants() {
        return Collections.unmodifiableSet(mConstants.keySet());
    }

    /**
     * Returns the number of constants in the pool.
     */    
    public int getSize() {
        return mEntries;
    }

    /**
     * Get or create a constant from the constant pool representing a class.
     */
    public ConstantClassInfo addConstantClass(String className) {
        return (ConstantClassInfo)addConstant(new ConstantClassInfo(this, className));
    }

    /**
     * Get or create a constant from the constant pool representing an array
     * class.
     *
     * @param dim Number of array dimensions.
     */
    public ConstantClassInfo addConstantClass(String className, int dim) {
        return (ConstantClassInfo)addConstant(new ConstantClassInfo(this, className, dim));
    }
    
    /**
     * Get or create a constant from the constant pool representing a class.
     */
    public ConstantClassInfo addConstantClass(TypeDesc type) {
        return (ConstantClassInfo)addConstant(new ConstantClassInfo(this, type));
    }

    /**
     * Get or create a constant from the constant pool representing a field in
     * any class.
     */
    public ConstantFieldInfo addConstantField(String className,
                                              String fieldName, 
                                              TypeDesc type) {
        ConstantInfo ci = new ConstantFieldInfo
            (addConstantClass(className), addConstantNameAndType(fieldName, type));
        return (ConstantFieldInfo)addConstant(ci);
    }

    /**
     * Get or create a constant from the constant pool representing a method
     * in any class. If the method returns void, set ret to null.
     */
    public ConstantMethodInfo addConstantMethod(String className,
                                                String methodName,
                                                TypeDesc ret,
                                                TypeDesc[] params) {
        
        MethodDesc md = MethodDesc.forArguments(ret, params);
        ConstantInfo ci = new ConstantMethodInfo
            (addConstantClass(className), addConstantNameAndType(methodName, md));
        return (ConstantMethodInfo)addConstant(ci);
    }
    
    /**
     * Get or create a constant from the constant pool representing an
     * interface method in any interface.
     */
    public ConstantInterfaceMethodInfo addConstantInterfaceMethod(String className,
                                                                  String methodName,
                                                                  TypeDesc ret,
                                                                  TypeDesc[] params) {
        
        MethodDesc md = MethodDesc.forArguments(ret, params);
        ConstantInfo ci = new ConstantInterfaceMethodInfo
            (addConstantClass(className), addConstantNameAndType(methodName, md));
        return (ConstantInterfaceMethodInfo)addConstant(ci);
    }

    /**
     * Get or create a constant from the constant pool representing a
     * constructor in any class.
     */
    public ConstantMethodInfo addConstantConstructor(String className,
                                                     TypeDesc[] params) {
        return addConstantMethod(className, "<init>", null, params);
    }

    /**
     * Get or create a constant integer from the constant pool.
     */
    public ConstantIntegerInfo addConstantInteger(int value) {
        return (ConstantIntegerInfo)addConstant(new ConstantIntegerInfo(value));
    }

    /**
     * Get or create a constant long from the constant pool.
     */
    public ConstantLongInfo addConstantLong(long value) {
        return (ConstantLongInfo)addConstant(new ConstantLongInfo(value));
    }

    /**
     * Get or create a constant float from the constant pool.
     */
    public ConstantFloatInfo addConstantFloat(float value) {
        return (ConstantFloatInfo)addConstant(new ConstantFloatInfo(value));
    }

    /**
     * Get or create a constant double from the constant pool.
     */
    public ConstantDoubleInfo addConstantDouble(double value) {