001package serp.bytecode.lowlevel;
002
003import java.io.*;
004import java.util.*;
005
006import serp.bytecode.visitor.*;
007
008/**
009 * Base type for all constant pool entries. Entries should generally be
010 * considered immutable; modifying an entry directly can have dire
011 * consequences, and often renders the resulting class file invalid.
012 *
013 * <p>Entries cannot be shared among constant pools.</p>
014 *
015 * @author Abe White
016 */
017public abstract class Entry implements VisitAcceptor {
018    public static final int UTF8 = 1;
019    public static final int INT = 3;
020    public static final int FLOAT = 4;
021    public static final int LONG = 5;
022    public static final int DOUBLE = 6;
023    public static final int CLASS = 7;
024    public static final int STRING = 8;
025    public static final int FIELD = 9;
026    public static final int METHOD = 10;
027    public static final int INTERFACEMETHOD = 11;
028    public static final int NAMEANDTYPE = 12;
029    private ConstantPool _pool = null;
030    private int _index = 0;
031
032    /**
033     * Read a single entry from the given bytecode stream and returns it.
034     */
035    public static Entry read(DataInput in) throws IOException {
036        Entry entry = create(in.readUnsignedByte());
037        entry.readData(in);
038        return entry;
039    }
040
041    /**
042     * Write the given entry to the given bytecode stream.
043     */
044    public static void write(Entry entry, DataOutput out)
045        throws IOException {
046        out.writeByte(entry.getType());
047        entry.writeData(out);
048    }
049
050    /**
051     * Create an entry based on its type code.
052     */
053    public static Entry create(int type) {
054        switch (type) {
055        case CLASS:
056            return new ClassEntry();
057        case FIELD:
058            return new FieldEntry();
059        case METHOD:
060            return new MethodEntry();
061        case INTERFACEMETHOD:
062            return new InterfaceMethodEntry();
063        case STRING:
064            return new StringEntry();
065        case INT:
066            return new IntEntry();
067        case FLOAT:
068            return new FloatEntry();
069        case LONG:
070            return new LongEntry();
071        case DOUBLE:
072            return new DoubleEntry();
073        case NAMEANDTYPE:
074            return new NameAndTypeEntry();
075        case UTF8:
076            return new UTF8Entry();
077        default:
078            throw new IllegalArgumentException("type = " + type);
079        }
080    }
081
082    /**
083     * Return the type code for this entry type.
084     */
085    public abstract int getType();
086
087    /**
088     * Return true if this is a wide entry -- i.e. if it takes up two
089     * places in the constant pool. Returns false by default.
090     */
091    public boolean isWide() {
092        return false;
093    }
094
095    /**
096     * Returns the constant pool containing this entry, or null if none.
097     */
098    public ConstantPool getPool() {
099        return _pool;
100    }
101
102    /**
103     * Returns the index of the entry in the owning constant pool, or 0.
104     */
105    public int getIndex() {
106        return _index;
107    }
108
109    /**
110     * This method is called after reading the entry type from bytecode.
111     * It should read all the data for this entry from the given stream.
112     */
113    abstract void readData(DataInput in) throws IOException;
114
115    /**
116     * This method is called after writing the entry type to bytecode.
117     * It should write all data for this entry to the given stream.
118     */
119    abstract void writeData(DataOutput out) throws IOException;
120
121    /**
122     * Subclasses must call this method before their state is mutated.
123     */
124    Object beforeModify() {
125        if (_pool == null)
126            return null;
127        return _pool.getKey(this);
128    }
129
130    /**
131     * Subclasses must call this method when their state is mutated.
132     */
133    void afterModify(Object key) {
134        if (_pool != null)
135            _pool.modifyEntry(key, this);
136    }
137
138    /**
139     * Sets the owning pool of the entry.
140     */
141    void setPool(ConstantPool pool) {
142        // attempting to overwrite current pool?
143        if (_pool != null && pool != null && _pool != pool)
144            throw new IllegalStateException("Entry already belongs to a pool");
145        _pool = pool;
146    }
147
148    /**
149     * Set the index of this entry within the pool.
150     */
151    void setIndex(int index) {
152        _index = index;
153    }
154}