001    package serp.bytecode.lowlevel;
002    
003    import java.io.*;
004    import java.util.*;
005    
006    import 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     */
017    public 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    }