package org.eclipse.jdt.internal.core.builder.impl; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import java.io.DataInputStream; | |
import java.io.DataOutputStream; | |
import java.io.IOException; | |
import org.eclipse.core.runtime.IPath; | |
import org.eclipse.core.runtime.Path; | |
import org.eclipse.jdt.internal.core.Assert; | |
import org.eclipse.jdt.internal.core.Util; | |
import org.eclipse.jdt.internal.core.builder.IConstructor; | |
import org.eclipse.jdt.internal.core.builder.IDevelopmentContext; | |
import org.eclipse.jdt.internal.core.builder.IField; | |
import org.eclipse.jdt.internal.core.builder.IHandle; | |
import org.eclipse.jdt.internal.core.builder.IImage; | |
import org.eclipse.jdt.internal.core.builder.IMethod; | |
import org.eclipse.jdt.internal.core.builder.IPackage; | |
import org.eclipse.jdt.internal.core.builder.IType; | |
class StateSnapConstantPool { | |
OrderedSet fSet; | |
/** | |
* The number of special pool entries at the beginning. | |
*/ | |
static final int NUM_SPECIAL = 11; | |
/** | |
* StateSnapConstantPool constructor comment. | |
*/ | |
StateSnapConstantPool(IDevelopmentContext dc) { | |
init(dc, 500); | |
} | |
/** | |
* Read a StateSnapConstantPool from an input stream. | |
*/ | |
StateSnapConstantPool(IDevelopmentContext dc, DataInputStream in, StateSnapV5 snap) throws IOException { | |
read(dc, in, snap); | |
} | |
/** | |
* Add a Number to the pool. | |
*/ | |
public void add(Number num) { | |
fSet.put(num); | |
} | |
/** | |
* Add a String to the pool. | |
*/ | |
public void add(String str) { | |
fSet.put(str); | |
} | |
/** | |
* Add an IPath to the pool. | |
*/ | |
public void add(IPath path) { | |
if (!fSet.includes(path)) { | |
// Special handling needed to preserve device | |
if (path.isRoot()) { | |
fSet.put(path.toString()); | |
} else { | |
IPath parent = path.removeLastSegments(1); | |
add(parent); | |
fSet.put(path.lastSegment()); | |
} | |
fSet.put(path); | |
} | |
} | |
/** | |
* Add an IPackage to the pool. | |
*/ | |
public void add(IHandle handle) { | |
switch (handle.kind()) { | |
case IHandle.K_JAVA_PACKAGE: | |
add((IPackage)handle); | |
break; | |
case IHandle.K_JAVA_TYPE: | |
add((IType)handle); | |
break; | |
case IHandle.K_JAVA_FIELD: | |
if (!fSet.includes(handle)) { | |
IField f = (IField)handle; | |
add(f.getDeclaringClass()); | |
add(f.getName()); | |
fSet.put(handle); | |
} | |
break; | |
case IHandle.K_JAVA_CONSTRUCTOR: | |
if (!fSet.includes(handle)) { | |
IConstructor c = (IConstructor)handle; | |
add(c.getDeclaringClass()); | |
IType[] parms = c.getParameterTypes(); | |
for (int i = 0; i < parms.length; ++i) { | |
add(parms[i]); | |
} | |
fSet.put(handle); | |
} | |
break; | |
case IHandle.K_JAVA_METHOD: | |
if (!fSet.includes(handle)) { | |
IMethod m = (IMethod)handle; | |
add(m.getDeclaringClass()); | |
add(m.getName()); | |
IType[] parms = m.getParameterTypes(); | |
for (int i = 0; i < parms.length; ++i) { | |
add(parms[i]); | |
} | |
fSet.put(handle); | |
} | |
break; | |
case IHandle.K_JAVA_IMAGE: | |
// NOP | |
break; | |
default: | |
Assert.isTrue(false, "Unknown kind of handle"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Add a SourceEntry to the pool. | |
*/ | |
public void add(SourceEntry entry) { | |
Assert.isNotNull(entry, "Null SourceEntry being added to StateSnapConstantPool"); //$NON-NLS-1$ | |
if (!fSet.includes(entry)) { | |
add(entry.getPath()); | |
String zipEntryPath = entry.fZipEntryPath; | |
if (zipEntryPath != null) { | |
fSet.put(zipEntryPath); | |
} | |
String zipEntryFileName = entry.fZipEntryFileName; | |
if (zipEntryFileName != null) { | |
fSet.put(zipEntryFileName); | |
} | |
fSet.put(entry); | |
} | |
} | |
/** | |
* Add an IPackage to the pool. | |
*/ | |
public void add(IPackage pkg) { | |
if (!fSet.includes(pkg)) { | |
fSet.put(pkg.getName()); | |
fSet.put(pkg); | |
} | |
} | |
/** | |
* Add an IType to the pool. | |
*/ | |
public void add(IType type) { | |
Assert.isTrue(!type.isStateSpecific()); | |
if (type.isPrimitive()) { | |
return; // Already added | |
} | |
if (!fSet.includes(type)) { | |
if (type.isArray()) { | |
add(((ArrayTypeHandleImpl)type).getElementType()); | |
} | |
else { | |
ClassOrInterfaceHandleImpl cls = (ClassOrInterfaceHandleImpl)type; | |
add(cls.getPackage()); | |
fSet.put(cls.getSimpleName()); | |
} | |
fSet.put(type); | |
} | |
} | |
/** | |
* Returns the IHandle at the given index. It must not be null. | |
*/ | |
public IHandle getHandle(int index) throws IOException { | |
try { | |
IHandle result = (IHandle)fSet.get(index); | |
if (result == null) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
return result; | |
} | |
catch (ClassCastException e) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Returns the Object at the given index. It may be null. | |
*/ | |
public Object getObject(int index) throws IOException { | |
return fSet.get(index); | |
} | |
/** | |
* Returns the IPackage at the given index. It must not be null. | |
*/ | |
public IPackage getPackage(int index) throws IOException { | |
try { | |
IPackage result = (IPackage)fSet.get(index); | |
if (result == null) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
return result; | |
} | |
catch (ClassCastException e) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Returns the IPath at the given index. It must not be null. | |
*/ | |
public IPath getPath(int index) throws IOException { | |
try { | |
IPath result = (IPath)fSet.get(index); | |
if (result == null) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
return result; | |
} | |
catch (ClassCastException e) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Returns the SourceEntry at the given index. It must not be null. | |
*/ | |
public SourceEntry getSourceEntry(int index) throws IOException { | |
try { | |
SourceEntry result = (SourceEntry)fSet.get(index); | |
if (result == null) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
return result; | |
} | |
catch (ClassCastException e) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Returns the String at the given index. It must not be null. | |
*/ | |
public String getString(int index) throws IOException { | |
try { | |
String result = (String)fSet.get(index); | |
if (result == null) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
return result; | |
} | |
catch (ClassCastException e) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Returns the String at the given index. It may be null. | |
*/ | |
public String getStringOrNull(int index) throws IOException { | |
return index == 0 ? null : getString(index); | |
} | |
/** | |
* Returns the String or Number at the given index. It may be null. | |
*/ | |
public Object getStringOrNumber(int index) throws IOException { | |
if (index == 0) { | |
return null; | |
} | |
Object result = (Object)fSet.get(index); | |
if (result != null && ((result instanceof Number) || (result instanceof String))) { | |
return result; | |
} | |
else { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Returns the IType at the given index. It must not be null. | |
*/ | |
public IType getType(int index) throws IOException { | |
try { | |
IType result = (IType)fSet.get(index); | |
if (result == null) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
return result; | |
} | |
catch (ClassCastException e) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* For debugging. | |
*/ | |
public String histogram() { | |
int nStr = 0, nID = 0, nSE = 0, nH = 0, nN = 0; | |
int n = fSet.size(); | |
for (int i = 10; i < n; ++i) { | |
Object obj = fSet.get(i); | |
if (obj instanceof String) { | |
++nStr; | |
} | |
else if (obj instanceof IPath) { | |
++nID; | |
} | |
else if (obj instanceof SourceEntry) { | |
++nSE; | |
} | |
else if (obj instanceof IHandle) { | |
++nH; | |
} | |
else if (obj instanceof Number) { | |
++nN; | |
} | |
else { | |
Assert.isTrue(false, "Unexpected pool item"); //$NON-NLS-1$ | |
} | |
} | |
return "nStr=" + nStr + ", nID=" + nID + ", nSE=" + nSE + ", nH=" + nH + ",nN=" + nN; //$NON-NLS-1$ //$NON-NLS-5$ //$NON-NLS-3$ //$NON-NLS-2$ //$NON-NLS-4$ | |
} | |
/** | |
* Returns the index of the given object. | |
*/ | |
public int index(Object obj) { | |
if (obj == null) { | |
return 0; | |
} | |
try { | |
return fSet.index(obj); | |
} | |
catch (IllegalArgumentException e) { | |
throw new IllegalArgumentException("Internal error in state serialization. Expected object missing from constant pool: " + obj); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Initialize the constant pool for the given DC and initial size estimate. | |
*/ | |
private void init (IDevelopmentContext dc, int initSize) { | |
// Set up constant pool with the special entries. | |
// Number of entries must correspond with NUM_SPECIAL | |
IImage image = dc.getImage(); | |
fSet = new OrderedSet(initSize*2+1, 0.5f); | |
fSet.put(new Object()); // 0: placeholder for null | |
fSet.put(image.booleanType()); // 1: type boolean | |
fSet.put(image.byteType()); // 2: type byte | |
fSet.put(image.charType()); // 3: type char | |
fSet.put(image.doubleType()); // 4: type double | |
fSet.put(image.floatType()); // 5: type float | |
fSet.put(image.intType()); // 6: type int | |
fSet.put(image.longType()); // 7: type long | |
fSet.put(image.shortType()); // 8: type short | |
fSet.put(image.voidType()); // 9: type void | |
fSet.put(image); // 10: image handle | |
} | |
/** | |
* Read a StateSnapConstantPool from an input stream. | |
*/ | |
private void read(IDevelopmentContext dc, DataInputStream in, StateSnapV5 snap) throws IOException { | |
int n = in.readInt(); // Actual size of pool is (NUM_SPECIAL + n) | |
init(dc, n); | |
int i = NUM_SPECIAL; | |
for (int j = 0; j < n; ++j, ++i) { | |
int tag = in.readByte(); | |
switch (tag) { | |
case 1: | |
// String | |
String str = in.readUTF(); | |
fSet.put(i, str); | |
break; | |
case 2: | |
// IPath | |
int temp = in.readInt(); | |
IPath parent = temp == 0 ? null : getPath(temp); | |
String elementName = getString(in.readInt()); | |
if (parent == null) { | |
fSet.put(i, new Path(elementName)); | |
} | |
else { | |
fSet.put(i, parent.append(elementName)); | |
} | |
break; | |
case 3: | |
// SourceEntry | |
SourceEntry entry = snap.readSourceEntry(this, in); | |
fSet.put(i, entry); | |
break; | |
case 4: | |
case 5: | |
case 6: | |
case 7: | |
case 8: | |
case 9: | |
case 10: | |
case 11: | |
fSet.put(i, readHandle(dc, in, tag)); | |
break; | |
case 12: | |
case 13: | |
case 14: | |
case 15: | |
fSet.put(i, readNumber(in, tag)); | |
break; | |
default: | |
throw new IOException("Unexpected kind of pool item"); //$NON-NLS-1$ | |
} | |
} | |
} | |
/** | |
* Internal -- Read an IHandle from an input stream into the pool. | |
*/ | |
private IHandle readHandle(IDevelopmentContext dc, DataInputStream in, int tag) throws IOException { | |
switch (tag) { | |
case 4: { | |
// package | |
String name = getString(in.readInt()); | |
boolean isUnnamed = in.readBoolean(); | |
return dc.getImage().getPackageHandle(name, isUnnamed); | |
} | |
case 5: { | |
// primitive type | |
// Should not occur since primitive types are well known and not written | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
case 6: { | |
// array type | |
TypeImpl elementType = (TypeImpl) getType(in.readInt()); | |
int nesting = in.readByte() & 0xFF; | |
return new ArrayTypeHandleImpl(elementType, nesting); | |
} | |
case 7: { | |
// class or interface type | |
IPackage pkg = getPackage(in.readInt()); | |
String simpleName = getString(in.readInt()); | |
return pkg.getClassHandle(simpleName); | |
} | |
case 8: { | |
// method | |
IType declaringClass = getType(in.readInt()); | |
if (declaringClass.isPrimitive() || declaringClass.isArray()) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
String name = getString(in.readInt()); | |
int numParams = in.readByte() & 0xFF; | |
IType[] params = new IType[numParams]; | |
for (int i = 0; i < numParams; ++i) { | |
params[i] = getType(in.readInt()); | |
} | |
return declaringClass.getMethodHandle(name, params); | |
} | |
case 9: { | |
// constructor | |
IType declaringClass = getType(in.readInt()); | |
if (declaringClass.isPrimitive() || declaringClass.isArray()) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
int numParams = in.readByte() & 0xFF; | |
IType[] params = new IType[numParams]; | |
for (int i = 0; i < numParams; ++i) { | |
params[i] = getType(in.readInt()); | |
} | |
return declaringClass.getConstructorHandle(params); | |
} | |
case 10: { | |
// field | |
IType declaringClass = getType(in.readInt()); | |
if (declaringClass.isPrimitive() || declaringClass.isArray()) { | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
String name = getString(in.readInt()); | |
return declaringClass.getFieldHandle(name); | |
} | |
case 11: | |
// image | |
return dc.getImage(); | |
default: | |
throw new IOException("Unexpected kind of pool item"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Internal -- Read a Number from an input stream into the pool. | |
*/ | |
private Number readNumber(DataInputStream in, int tag) throws IOException { | |
switch (tag) { | |
case 12: | |
// Integer | |
return new Integer(in.readInt()); | |
case 13: | |
// Long | |
return new Long(in.readLong()); | |
case 14: | |
// Float | |
return new Float(Float.intBitsToFloat(in.readInt())); | |
case 15: | |
// Double | |
return new Double(Double.longBitsToDouble(in.readLong())); | |
default: | |
throw new IOException("Unexpected type of number"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Returns the number of entries in the pool. | |
*/ | |
public int size() { | |
return fSet.size(); | |
} | |
/** | |
* Write the constant pool to the given stream. | |
*/ | |
public void write(DataOutputStream out) throws IOException { | |
int n = fSet.size(); | |
out.writeInt(n - NUM_SPECIAL); | |
for (int i = NUM_SPECIAL; i < n; ++i) { | |
Object obj = fSet.get(i); | |
if (obj instanceof String) { | |
out.writeByte(1); | |
out.writeUTF((String)obj); | |
} | |
else if (obj instanceof IHandle) { | |
writeHandle((IHandle)obj, out); // tags 4 through 11 | |
} | |
else if (obj instanceof IPath) { | |
IPath path = (IPath)obj; | |
out.writeByte(2); | |
// Special handling needed to preserve device | |
if (path.isRoot()) { | |
out.writeInt(0); | |
out.writeInt(index(path.toString())); | |
} | |
else { | |
IPath parent = path.removeLastSegments(1); | |
out.writeInt(index(parent)); | |
out.writeInt(index(path.lastSegment())); | |
} | |
} | |
else if (obj instanceof SourceEntry) { | |
SourceEntry e = (SourceEntry)obj; | |
out.writeByte(3); | |
out.writeInt(index(e.getPath())); | |
out.writeInt(index(e.fZipEntryPath)); | |
out.writeInt(index(e.fZipEntryFileName)); | |
} | |
else if (obj instanceof Number) { | |
writeNumber((Number)obj, out); // tags 12 through 15 | |
} | |
else { | |
Assert.isTrue(false, "Unexpected kind of pool item"); //$NON-NLS-1$ | |
} | |
} | |
} | |
/** | |
* Write a handle to the given stream. | |
*/ | |
private void writeHandle(IHandle h, DataOutputStream out) throws IOException { | |
switch (h.kind()) { | |
case IHandle.K_JAVA_PACKAGE: | |
IPackage pkg = (IPackage)h; | |
out.writeByte(4); | |
out.writeInt(index(pkg.getName())); | |
out.writeBoolean(pkg.isUnnamed()); | |
break; | |
case IHandle.K_JAVA_TYPE: | |
IType t = (IType)h; | |
if (t.isPrimitive()) { | |
// tag=5 | |
// Primitive types should not show up since they are well known and are not written. | |
throw new IOException("Error in format"); //$NON-NLS-1$ | |
} | |
else if (t.isArray()) { | |
ArrayTypeHandleImpl at = (ArrayTypeHandleImpl)t; | |
out.writeByte(6); | |
out.writeInt(index(at.getElementType())); | |
int nesting = at.getNestingDepth(); | |
Assert.isTrue(nesting < 256); | |
out.writeByte(nesting); | |
} | |
else { | |
Assert.isTrue(t instanceof ClassOrInterfaceHandleImpl); | |
out.writeByte(7); | |
out.writeInt(index(t.getPackage())); | |
out.writeInt(index(t.getSimpleName())); | |
} | |
break; | |
case IHandle.K_JAVA_METHOD: { | |
IMethod m = (IMethod)h; | |
out.writeByte(8); | |
out.writeInt(index(m.getDeclaringClass())); | |
out.writeInt(index(m.getName())); | |
IType[] params = m.getParameterTypes(); | |
Assert.isTrue(params.length < 256); | |
out.writeByte(params.length); | |
for (int j = 0; j < params.length; ++j) { | |
out.writeInt(index(params[j])); | |
} | |
break; | |
} | |
case IHandle.K_JAVA_CONSTRUCTOR: { | |
IConstructor c = (IConstructor)h; | |
out.writeByte(9); | |
out.writeInt(index(c.getDeclaringClass())); | |
IType[] params = c.getParameterTypes(); | |
Assert.isTrue(params.length < 256); | |
out.writeByte(params.length); | |
for (int j = 0; j < params.length; ++j) { | |
out.writeInt(index(params[j])); | |
} | |
break; | |
} | |
case IHandle.K_JAVA_FIELD: | |
IField f = (IField)h; | |
out.writeByte(10); | |
out.writeInt(index(f.getDeclaringClass())); | |
out.writeInt(index(f.getName())); | |
break; | |
case IHandle.K_JAVA_IMAGE: | |
out.writeByte(11); | |
break; | |
default: | |
Assert.isTrue(false, "Unknown kind of handle"); //$NON-NLS-1$ | |
} | |
} | |
/** | |
* Write a Number to the given stream. | |
*/ | |
private void writeNumber(Number num, DataOutputStream out) throws IOException { | |
if (num instanceof Integer) { | |
out.writeByte(12); | |
out.writeInt(num.intValue()); | |
} | |
else if (num instanceof Long) { | |
out.writeByte(13); | |
out.writeLong(num.longValue()); | |
} | |
else if (num instanceof Float) { | |
out.writeByte(14); | |
out.writeInt(Float.floatToIntBits(num.floatValue())); | |
} | |
else if (num instanceof Double) { | |
out.writeByte(15); | |
out.writeLong(Double.doubleToLongBits(num.doubleValue())); | |
} | |
else { | |
Assert.isTrue(false, "Unexpected type of number"); //$NON-NLS-1$ | |
} | |
} | |
} |