|
|
The PoolEntry class begins with 11 constants to represent the 11 types that may appear in the constant pool. That is, every constant pool entry is preceded by one unsigned byte that signals its type.
The PoolEntry() constructor reads this tag to determine how many bytes it should read. It reads four bytes for integer, float, ClassInfo, FieldRef, MethodRef, NameAndType, and InterfaceMethodRef types. It reads eight bytes for long and double types. ClassInfo and String types take two bytes. Finally, the UTF8 type requires a variable number of bytes, so first you must read one more unsigned short to learn how many bytes are in the UTF8 structure. Once you know how many bytes you need to read, reading them is almost trivial. Just use the read(byte[] b) method of the DataInputStream.
Note: I decided to store the data as a byte array that will be converted to the appropriate type when requested. I could have performed the conversion immediately in the constructor and stored the converted values rather than the raw bytes. However, this would require many excess fields for each PoolEntry object. For example, if a PoolEntry object is a float, then the UTF8, integer, long, double, and all other fields would be empty. This seems excessively wasteful. However, if you anticipate repeatedly requesting the same entry from the constant pool, then you might want to trade off the extra memory in exchange for reduced CPU time.
Listing 4-12 The PoolEntry class
import java.io.*;
public class PoolEntry {
public final static int cUTF8 = 1;
public final static int cInteger = 3;
public final static int cFloat = 4;
public final static int cLong = 5;
public final static int cDouble = 6;
public final static int cClassInfo = 7;
public final static int cString = 8;
public final static int cFieldRef = 9;
public final static int cMethodRef = 10;
public final static int cInterfaceMethodRef = 11;
public final static int cNameAndType = 12;
int tag;
byte[] data;
public PoolEntry(DataInputStream dis) throws IOException {
tag = dis.readUnsignedByte();
int bytesToRead;
switch (tag) {
case cLong:
case cDouble:
bytesToRead = 8;
break;
case cInteger:
case cFloat:
case cFieldRef:
case cMethodRef:
case cNameAndType:
case cInterfaceMethodRef:
bytesToRead = 4;
break;
case cClassInfo:
case cString:
bytesToRead = 2;
break;
case cUTF8:
bytesToRead = dis.readUnsignedShort();
break;
default:
throw new ClassFormatError(Unrecognized Constant Type + tag);
}
data = new byte[bytesToRead];
int check = dis.read(data);
if (check != data.length) {
throw new ClassFormatError(Not enough data to fill array);
}
}
public String readUTF8() {
if (tag != cUTF8) {
throw new ClassFormatError
(This is not a UTF8 string );
}
try {
// first put length of string back in string
int len = data.length;
byte[] newdata = new byte[len+2];
newdata[0] = (byte) (len >> 8);
newdata[1] = (byte) len;
System.arraycopy(data, 0, newdata, 2, data.length);
ByteArrayInputStream bis = new ByteArrayInputStream(newdata);
DataInputStream dis = new DataInputStream(bis);
return dis.readUTF();
}
catch (IOException e) {
throw new ClassFormatError(e + Bad UTF8 string);
}
}
public int readInteger() {
if (tag != cInteger) {
throw new ClassFormatError
(This is not an integer.);
}
return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
}
public int readLong() {
if (tag != cLong) {
throw new ClassFormatError
(This is not a long.);
}
return data[0] << 56 | data[1] << 48 | data[2] << 40 |
data[3] << 32 | data[4] << 24 | data[5] << 16 | data[6] << 8 |
data[7];
}
public float readFloat() {
if (tag != cFloat) {
throw new ClassFormatError
(This is not a float);
}
int bits = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
return Float.intBitsToFloat(bits);
}
public double readDouble() {
if (tag != cDouble) {
throw new ClassFormatError
(This is not a double);
}
long bits = (long) data[0] << 56 | (long) data[1] << 48
| (long) data[2] << 40 | (long) data[3] << 32 | (long) data[4] << 24
| (long) data[5] << 16 | (long) data[6] << 8 | (long) data[7];
return Double.longBitsToDouble(bits);
}
public ClassInfo readClassInfo() {
if (tag != cClassInfo) {
throw new ClassFormatError
(This is not a ClassInfoStructure);
}
return new ClassInfo(tag, data[0] << 8 | data[1]);
}
public RefInfo readFieldRef() {
if (tag != cFieldRef) {
throw new ClassFormatError
(This is not a FieldRefStructure);
}
return new RefInfo(tag, data[0] << 8 | data[1],
data[2] << 8 | data[3]);
}
public RefInfo readMethodRef() {
if (tag != cMethodRef) {
throw new ClassFormatError
(This is not a methodRef);
}
return new RefInfo(tag, data[0] << 8 | data[1],
data[2] << 8 | data[3]);
}
public RefInfo readInterfaceMethodRef() {
if (tag != cInterfaceMethodRef) {
throw new ClassFormatError
(This is not an InterfaceMethodRef);
}
return new RefInfo(tag, data[0] << 8 | data[1],
data[2] << 8 | data[3]);
}
public NameAndType readNameAndType() {
if (tag != cNameAndType) {
throw new ClassFormatError
(This is not a Name and Type structure);
}
return new NameAndType(tag, data[0] << 8 | data[1],
data[2] << 8 | data[3]);
}
public int readString() {
if (tag != cString) {
throw new ClassFormatError
(This is not a String);
}
return data[0] << 8 | data[1];
}
public int tag() {
return tag;
}
public String toString() {
switch (tag) {
case cLong:
return long + String.valueOf(readLong());
case cDouble:
return double + String.valueOf(readDouble());
case cInteger:
return int + String.valueOf(readInteger());
case cFloat:
return float + String.valueOf(readFloat());
case cFieldRef:
return FieldRef + String.valueOf(readFieldRef());
case cMethodRef:
return MethodRef + String.valueOf(readMethodRef());
case cNameAndType:
return NameAndType + String.valueOf(readNameAndType());
case cInterfaceMethodRef:
return InterfaceMethodRef + String.valueOf(readInterfaceMethodRef());
case cClassInfo:
return ClassInfo + String.valueOf(readClassInfo());
case cString:
return String + String.valueOf(readString());
case cUTF8:
return UTF8 + readUTF8();
default:
throw new ClassFormatError(Unrecognized Constant Type);
}
}
}
|