blob: 75baf0db3fa89a55f946208b10f72ab9d18832c1 [file] [log] [blame]
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 java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.internal.core.Assert;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.Util;
import org.eclipse.jdt.internal.core.builder.IDevelopmentContext;
import org.eclipse.jdt.internal.core.builder.IImageContext;
import org.eclipse.jdt.internal.core.builder.IPackage;
import org.eclipse.jdt.internal.core.builder.IProblemReporter;
import org.eclipse.jdt.internal.core.builder.IState;
import org.eclipse.jdt.internal.core.builder.IType;
import org.eclipse.jdt.internal.core.util.LookupTable;
/**
* StateSnap for state file format version 5.
* @see StateSnap
*/
public class StateSnapV5 {
/**
* Table from IType to IBuilderType, used only when reading.
*/
Hashtable fBuilderTypeTable;
IPackage currentPackage;
static final int MAGIC = 0x53544154; // magic = "STAT"e
static final int VERSION = 0x0005;
/**
* Add pool constants for the build context.
*/
protected void addBuildContextToPool(IState state, StateSnapConstantPool pool) {
IImageContext ctx = state.getBuildContext();
if (ctx != null) {
IPackage[] pkgs = ctx.getPackages();
for (int i = 0; i < pkgs.length; ++i) {
pool.add(pkgs[i]);
}
}
}
/**
* Add pool constants for the dependency graph.
*/
protected void addDependencyGraphToPool(StateImpl state, StateSnapConstantPool pool) {
DependencyGraph graph = state.getInternalDependencyGraph();
for (Enumeration e = graph.getNodes(); e.hasMoreElements(); ) {
INode node = (INode)e.nextElement();
switch (node.getKind()) {
case INode.JCU_NODE:
JCUNode jcuNode = (JCUNode) node;
pool.add(state.getSourceEntry(jcuNode.getPackageElement()));
IType[] types = jcuNode.getTypes();
for (int i = 0; i < types.length; ++i) {
pool.add(types[i]);
}
break;
case INode.TYPE_NODE:
TypeNode typeNode = (TypeNode)node;
pool.add(state.getSourceEntry(typeNode.getPackageElement()));
break;
case INode.NAMESPACE_NODE:
pool.add(((NamespaceNode)node).getPackage());
break;
case INode.ZIP_NODE:
pool.add(((ZipNode)node).getZipFile());
break;
default:
Assert.isTrue(false, "Unexpected kind of node"); //$NON-NLS-1$
}
// Don't need to process node dependents here, since they're nodes as well
// and will have their info added to the pool above.
}
}
/**
* Add pool constants for the package map.
*/
protected void addPackageMapToPool(StateImpl state, StateSnapConstantPool pool) {
PackageMap map = state.getPackageMap();
for (Enumeration e = map.getAllPackages(); e.hasMoreElements(); ) {
IPackage pkg = (IPackage)e.nextElement();
pool.add(pkg);
IPath[] fragments = map.getFragments(pkg);
for (int i = 0; i < fragments.length; ++i) {
pool.add(fragments[i]);
}
}
}
/**
* Add pool constants for the principal structure table.
*/
protected void addPrincipalStructureTableToPool(StateImpl state, StateSnapConstantPool pool) {
Hashtable table = state.getPrincipalStructureTable();
for (Enumeration e = table.elements(); e.hasMoreElements(); ) {
TypeStructureEntry tsEntry = (TypeStructureEntry) e.nextElement();
addTypeStructureEntryToPool(tsEntry, pool);
}
}
/**
* Add pool constants for the problem table.
*/
protected void addProblemTableToPool(StateImpl state, StateSnapConstantPool pool) {
IProblemReporter reporter = state.getProblemReporter();
if (reporter instanceof ProblemTable) {
for (Enumeration e = reporter.getAllProblems(); e.hasMoreElements();) {
addProblemToPool((ProblemDetailImpl) e.nextElement(), pool);
}
for (Enumeration e = reporter.getImageProblems(); e.hasMoreElements();) {
addProblemToPool((ProblemDetailImpl) e.nextElement(), pool);
}
}
}
/**
* Add pool constants for a problem.
*/
protected void addProblemToPool(ProblemDetailImpl pb, StateSnapConstantPool pool) {
SourceEntry sourceEntry = pb.getSourceEntry();
if (sourceEntry != null) {
pool.add(sourceEntry);
}
pool.add(pb.getMessage());
}
/**
* Add pool constants for the source element table.
*/
protected void addSourceElementTableToPool(StateImpl state, StateSnapConstantPool pool) {
SourceElementTable table = state.getSourceElementTable();
for (Enumeration e = state.getPackageMap().getAllPackages(); e.hasMoreElements();) {
IPackage pkg = (IPackage) e.nextElement();
// Assume pkg has already been added to the pool
LookupTable entryTable = table.getPackageTable(pkg);
for (Enumeration keys = entryTable.keys(); keys.hasMoreElements();) {
String fileName = (String) keys.nextElement();
pool.add(fileName);
SourceEntry sEntry = (SourceEntry) entryTable.get(fileName);
pool.add(sEntry);
}
}
}
/**
* Add pool constants for a type structure entry.
*/
protected void addTypeStructureEntryToPool(TypeStructureEntry tsEntry, StateSnapConstantPool pool) {
pool.add(tsEntry.getType());
}
protected void badFormat() throws IOException {
throw new IOException("Error in format"); //$NON-NLS-1$
}
/**
* Check that the next section has the given name.
*/
protected void checkSection(DataInputStream in, String name) throws IOException {
String toCheck = in.readUTF();
if (!toCheck.equals(name))
badFormat();
}
protected PackageElement getPackageElement(int entryNum, PackageElement[] packageElements, StateSnapConstantPool pool, StateImpl state) throws IOException {
if (packageElements[entryNum] == null) {
SourceEntry sourceEntry = pool.getSourceEntry(entryNum);
packageElements[entryNum] = state.packageElementFromSourceEntry(sourceEntry);
}
return packageElements[entryNum];
}
/**
* Reads and reconstructs a state from the given input stream.
*/
public StateImpl read(JavaDevelopmentContextImpl dc, IProject project, DataInputStream in) throws IOException {
/* magic number and version have already been read */
StateImpl state = new StateImpl(dc, project);
int fingerprintLen = in.readShort();
byte[] fingerprint = new byte[fingerprintLen];
in.readFully(fingerprint);
state.setFingerprint(fingerprint);
// Read pool.
StateSnapConstantPool pool = new StateSnapConstantPool(dc, in, this);
state.readClassPath(); // Read classpath from the project
state.setBuildContext(readBuildContext(dc, pool, in));
PackageMap packageMap = readPackageMap(pool, in);
state.setPackageMap(packageMap);
state.setSourceElementTable(readSourceElementTable(pool, in));
state.setPrincipalStructureTable(readPrincipalStructureTable(pool, in, state));
state.setProblemReporter(readProblemReporter(project, pool, in));
state.setInternalDependencyGraph(readDependencyGraph(pool, in, state));
state.buildPrincipalStructureByPackageTable();
// We don't need the project any more
//state.resetProject();
return state;
}
/**
* Read the build context.
*/
protected IImageContext readBuildContext(IDevelopmentContext dc, StateSnapConstantPool pool, DataInputStream in) throws IOException {
checkSection(in, "BuildContext"); //$NON-NLS-1$
int numPkgs = in.readInt();
if (numPkgs == -1) {
return null;
}
IPackage[] pkgs = new IPackage[numPkgs];
for (int i = 0; i < pkgs.length; ++i) {
pkgs[i] = pool.getPackage(in.readInt());
}
return dc.getImage().createImageContext(pkgs);
}
/**
* Read the dependency graph.
* The state is needed to create CompilationUnit objects from SourceEntry objects.
*/
protected DependencyGraph readDependencyGraph(StateSnapConstantPool pool, DataInputStream in, StateImpl state) throws IOException {
checkSection(in, "DependencyGraph"); //$NON-NLS-1$
DependencyGraph graph = new DependencyGraph();
// Avoid creating package elements for each node and dependency.
PackageElement[] packageElements = new PackageElement[pool.size()];
while (in.readBoolean()) {
JCUNode jcu = null;
try {
int entryNum = in.readInt();
PackageElement unit = getPackageElement(entryNum, packageElements, pool, state);
jcu = (JCUNode)graph.getNodeFor(unit);
int numTypes = in.readByte() & 0xFF;
IType[] types = new IType[numTypes];
for (int j = 0; j < numTypes; ++j) {
types[j] = pool.getType(in.readInt());
}
jcu.setTypes(types);
}
catch (ClassCastException e) { // the source entry should be a CU
badFormat();
}
// Read dependencies
int numDep = in.readShort() & 0xFFFF;
INode[] dependencies = new INode[numDep];
for (int j = 0; j < numDep; ++j) {
int entryNum = in.readInt();
Object obj = pool.getObject(entryNum);
if (obj instanceof SourceEntry) {
PackageElement unit = getPackageElement(entryNum, packageElements, pool, state);
dependencies[j] = graph.getNodeFor(unit);
} else if (obj instanceof IPackage) {
dependencies[j] = graph.getNodeFor((IPackage) obj);
} else if (obj instanceof IPath) {
dependencies[j] = graph.getNodeFor((IPath) obj);
} else {
// Unexpected referrent
badFormat();
}
}
jcu.setDependencies(dependencies);
}
skipAttributes(in);
graph.integrityCheck();
return graph;
}
/**
* Read the package map.
*/
protected PackageMap readPackageMap(StateSnapConstantPool pool, DataInputStream in) throws IOException {
checkSection(in, "PackageMap"); //$NON-NLS-1$
PackageMap map = new PackageMap();
int size = in.readInt();
for (int i = 0; i < size; ++i) {
IPackage pkg = pool.getPackage(in.readInt());
int numFragments = in.readShort() & 0xFFFF;
IPath[] fragments = new IPath[numFragments];
for (int j = 0; j < numFragments; ++j) {
fragments[j] = pool.getPath(in.readInt());
}
map.putFragments(pkg, fragments);
}
return map;
}
/**
* Read the principal structure table.
*/
protected Hashtable readPrincipalStructureTable(StateSnapConstantPool pool, DataInputStream in, StateImpl state) throws IOException {
checkSection(in, "PrincipalStructureTable"); //$NON-NLS-1$
int numEntries = in.readInt();
Hashtable table = new Hashtable(numEntries * 2 + 1);
for (int i = 0; i < numEntries; ++i) {
TypeStructureEntry entry = readTypeStructureEntry(pool, in, state);
table.put(entry.getType(), entry);
}
return table;
}
/**
* Read a problem.
*/
protected ProblemDetailImpl readProblem(StateSnapConstantPool pool, DataInputStream in) throws IOException {
String msg = pool.getString(in.readInt());
int id = in.readInt();
int severity = in.readInt();
int temp = in.readInt();
SourceEntry sourceEntry = (temp == 0) ? null : pool.getSourceEntry(temp);
int startPos = in.readInt();
int endPos = in.readInt();
int lineNumber = in.readInt();
skipAttributes(in);
return new ProblemDetailImpl(msg, id, severity, sourceEntry, startPos, endPos, lineNumber);
}
/**
* Read the problem reporter.
*/
protected IProblemReporter readProblemReporter(IProject project, StateSnapConstantPool pool, DataInputStream in) throws IOException {
checkSection(in, "Problems"); //$NON-NLS-1$
IProblemReporter reporter = null;
boolean isProblemTable = in.readBoolean();
if (isProblemTable) {
reporter = new ProblemTable();
int numProblems = in.readInt();
for (int i = 0; i < numProblems; ++i) {
ProblemDetailImpl pb = readProblem(pool, in);
reporter.putProblem(pb.getSourceEntry(), pb);
}
} else {
String className = in.readUTF();
Class clazz = null;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new IOException("Class " + className + " was not found."); //$NON-NLS-2$ //$NON-NLS-1$
}
try {
reporter = (IProblemReporter) clazz.newInstance();
} catch (InstantiationException e) {
throw new IOException("Could not instanciate " + clazz.getName()); //$NON-NLS-1$
} catch (IllegalAccessException e) {
throw new IOException("Could not instanciate " + clazz.getName()); //$NON-NLS-1$
}
reporter.initialize(project, JavaModelManager.getJavaModelManager().getDevelopmentContext(project));
}
return reporter;
}
/**
* Read the source element table.
*/
protected SourceElementTable readSourceElementTable(StateSnapConstantPool pool, DataInputStream in) throws IOException {
checkSection(in, "SourceElementTable"); //$NON-NLS-1$
SourceElementTable table = new SourceElementTable();
int numPackages = in.readInt();
for (int i = 0; i < numPackages; ++i) {
IPackage pkg = pool.getPackage(in.readInt());
int numEntries = in.readInt();
LookupTable entryTable = new LookupTable(numEntries);
for (int j = 0; j < numEntries; ++j) {
String fileName = pool.getString(in.readInt());
SourceEntry sEntry = pool.getSourceEntry(in.readInt());
entryTable.put(fileName, sEntry);
}
table.putPackageTable(pkg, entryTable);
}
return table;
}
/**
* Read the next source entry.
*/
protected SourceEntry readSourceEntry(StateSnapConstantPool pool, DataInputStream in) throws IOException {
IPath path = pool.getPath(in.readInt());
String zipEntryName = pool.getStringOrNull(in.readInt());
String zipEntryPath = null, zipEntryFileName = null;
if (zipEntryName != null) {
int pos = zipEntryName.lastIndexOf('/');
if (pos != -1) {
zipEntryPath = zipEntryName.substring(0, pos);
zipEntryFileName = zipEntryName.substring(pos + 1);
} else {
zipEntryPath = null;
zipEntryFileName = zipEntryName;
}
}
return new SourceEntry(path, zipEntryPath, zipEntryFileName);
}
/**
* Read a type handle.
* Allow null if allowNull is true.
*/
IType readTypeHandle(StateSnapConstantPool pool, DataInputStream in, boolean allowNull) throws IOException {
int index = in.readInt();
if (index == 0) {
if (!allowNull)
badFormat();
return null;
}
return pool.getType(index);
}
/**
* Read a class or interface type handle and return its name.
* Allow null if allowNull is true.
*/
String readTypeHandleAsName(StateSnapConstantPool pool, DataInputStream in, boolean allowNull) throws IOException {
IType typeHandle = readTypeHandle(pool, in, allowNull);
if (typeHandle == null) {
if (!allowNull)
badFormat();
return null;
}
Assert.isTrue(!typeHandle.isPrimitive() && !typeHandle.isArray()); // must not be a class or interface handle
return typeHandle.getName(); //.intern(); // intern it
}
/**
* Read a class or interface type handle and return its name.
* Allow null if allowNull is true.
*/
String readTypeHandleAsSignature(StateSnapConstantPool pool, DataInputStream in) throws IOException {
IType typeHandle = readTypeHandle(pool, in, false);
return ((TypeImpl) typeHandle).getVMSignature(); //.intern(); // intern it
}
/**
* Read a type structure entry.
*/
protected TypeStructureEntry readTypeStructureEntry(StateSnapConstantPool pool, DataInputStream in, StateImpl state) throws IOException {
SourceEntry sEntry = pool.getSourceEntry(in.readInt());
IType type = readTypeHandle(pool, in, false);
return new TypeStructureEntry(sEntry, type);
}
/**
* Saves key information about the given state
* to the given output stream. This snapshot can be used
* subsequently in reconstructing the state.
*/
public void save(StateImpl state, DataOutputStream out) throws IOException {
// Build up pool.
IDevelopmentContext dc = state.getDevelopmentContext();
StateSnapConstantPool pool = new StateSnapConstantPool(dc);
addBuildContextToPool(state, pool);
addPackageMapToPool(state, pool);
addSourceElementTableToPool(state, pool);
addPrincipalStructureTableToPool(state, pool);
addProblemTableToPool(state, pool);
addDependencyGraphToPool(state, pool);
// Write all.
out.writeInt(MAGIC);
out.writeShort(VERSION);
byte[] fingerprint = state.getFingerprint();
// regression test for 1F9M2KH: RQIB:ALL - Problem saving incrementally built state
Assert.isNotNull(fingerprint);
out.writeShort(fingerprint.length);
out.write(fingerprint);
pool.write(out);
writeBuildContext(state, pool, out);
writePackageMap(state, pool, out);
writeSourceElementTable(state, pool, out);
writePrincipalStructureTable(state, pool, out);
writeProblemReporter(state, pool, out);
writeDependencyGraph(state, pool, out);
}
/**
* Skip over the attributes on read.
*/
protected void skipAttributes(DataInputStream in) throws IOException {
int numAttributes = in.readShort() & 0xFFFF;
for (int i = 0; i < numAttributes; ++i) {
int nameIndex = in.readInt();
int len = in.readShort() & 0xFFFF;
in.skipBytes(len);
}
}
/**
* Write the build context.
*/
protected void writeBuildContext(StateImpl state, StateSnapConstantPool pool, DataOutputStream out) throws IOException {
out.writeUTF("BuildContext"); //$NON-NLS-1$
IImageContext ctx = state.getBuildContext();
if (ctx == null) {
out.writeInt(-1);
}
else {
IPackage[] pkgs = ctx.getPackages();
out.writeInt(pkgs.length);
for (int i = 0; i < pkgs.length; ++i) {
out.writeInt(pool.index(pkgs[i]));
}
}
}
/**
* Write the dependency graph.
*/
protected void writeDependencyGraph(StateImpl state, StateSnapConstantPool pool, DataOutputStream out) throws IOException {
out.writeUTF("DependencyGraph"); //$NON-NLS-1$
DependencyGraph graph = state.getInternalDependencyGraph();
graph.integrityCheck();
/**
* We only care about serializing JCU nodes, since they
* are the only nodes with dependency information. All
* other nodes will be serialized indirectly as dependencies
* of JCU nodes.
* Do we care about Type/Zip/Namespace nodes that have no dependents???
*/
for (Enumeration e = graph.getJCUNodes(); e.hasMoreElements();) {
JCUNode jcu = (JCUNode) e.nextElement();
out.writeBoolean(true);
SourceEntry sEntry = state.getSourceEntry(jcu.getPackageElement());
if (sEntry == null) {
System.out.println("Warning: StatSnap: Attempt to serialize dependency graph node for missing JCU: " + jcu.getPackageElement() + ". Skipping..."); //$NON-NLS-1$ //$NON-NLS-2$
} else {
Assert.isNotNull(sEntry);
out.writeInt(pool.index(sEntry));
IType[] types = jcu.getTypes();
Assert.isTrue(types.length < 256);
out.writeByte(types.length);
for (int i = 0; i < types.length; ++i) {
out.writeInt(pool.index(types[i]));
}
// Write dependencies
INode[] dependencies = jcu.getDependencies();
int numDep = dependencies.length;
Assert.isTrue(numDep < 65536);
out.writeShort(numDep);
for (int i = 0; i < numDep; ++i) {
INode dep = dependencies[i];
switch (dep.getKind()) {
case INode.JCU_NODE :
{
PackageElement element = ((JCUNode) dep).getPackageElement();
SourceEntry depEntry = state.getSourceEntry(element);
out.writeInt(pool.index(depEntry));
break;
}
case INode.TYPE_NODE :
{
PackageElement element = ((TypeNode) dep).getPackageElement();
SourceEntry depEntry = state.getSourceEntry(element);
out.writeInt(pool.index(depEntry));
break;
}
case INode.NAMESPACE_NODE :
{
IPackage pkg = ((NamespaceNode) dep).getPackage();
out.writeInt(pool.index(pkg));
break;
}
case INode.ZIP_NODE :
{
IPath path = ((ZipNode) dep).getZipFile();
out.writeInt(pool.index(path));
break;
}
default :
badFormat();
}
}
}
}
out.writeBoolean(false);
writeEmptyAttributes(out);
}
/**
* Write an empty set of attributes.
*/
protected void writeEmptyAttributes(DataOutputStream out) throws IOException {
out.writeShort(0);
}
/**
* Write the package map.
*/
protected void writePackageMap(StateImpl state, StateSnapConstantPool pool, DataOutputStream out) throws IOException {
out.writeUTF("PackageMap"); //$NON-NLS-1$
PackageMap map = state.getPackageMap();
out.writeInt(map.size());
int count = 0;
for (Enumeration e = map.getAllPackages(); e.hasMoreElements(); ) {
++count;
IPackage pkg = (IPackage)e.nextElement();
IPath[] fragments = map.getFragments(pkg);
out.writeInt(pool.index(pkg));
out.writeShort(fragments.length);
for (int i = 0; i < fragments.length; ++i) {
out.writeInt(pool.index(fragments[i]));
}
}
Assert.isTrue(count == map.size()); // Sanity check
}
/**
* Write the principal structure table.
*/
protected void writePrincipalStructureTable(StateImpl state, StateSnapConstantPool pool, DataOutputStream out) throws IOException {
out.writeUTF("PrincipalStructureTable"); //$NON-NLS-1$
Hashtable table = state.getPrincipalStructureTable();
int num = table.size();
out.writeInt(num);
int count = 0;
for (Enumeration e = table.elements(); e.hasMoreElements();) {
++count;
TypeStructureEntry tsEntry = (TypeStructureEntry) e.nextElement();
writeTypeStructureEntry(tsEntry, pool, out);
}
Assert.isTrue(count == num); // Sanity check
}
/**
* Write a problem.
*/
protected void writeProblem(ProblemDetailImpl pb, StateSnapConstantPool pool, DataOutputStream out) throws IOException {
out.writeInt(pool.index(pb.getMessage()));
out.writeInt(pb.getID());
out.writeInt(pb.getSeverity());
out.writeInt(pool.index(pb.getSourceEntry()));
out.writeInt(pb.getStartPos());
out.writeInt(pb.getEndPos());
out.writeInt(pb.getLineNumber());
writeEmptyAttributes(out);
}
/**
* Write the problem reporter.
*/
protected void writeProblemReporter(StateImpl state, StateSnapConstantPool pool, DataOutputStream out) throws IOException {
out.writeUTF("Problems"); //$NON-NLS-1$
IProblemReporter reporter = state.getProblemReporter();
if (reporter instanceof ProblemTable) {
out.writeBoolean(true);
Vector problems = new Vector();
for (Enumeration e = reporter.getAllProblems(); e.hasMoreElements();) {
problems.addElement(e.nextElement());
}
for (Enumeration e = reporter.getImageProblems(); e.hasMoreElements();) {
problems.addElement(e.nextElement());
}
out.writeInt(problems.size());
for (Enumeration e = problems.elements(); e.hasMoreElements();) {
writeProblem((ProblemDetailImpl) e.nextElement(), pool, out);
}
} else {
out.writeBoolean(false);
out.writeUTF(reporter.getClass().getName());
}
}
/**
* Write the source element table.
*/
protected void writeSourceElementTable(StateImpl state, StateSnapConstantPool pool, DataOutputStream out) throws IOException {
out.writeUTF("SourceElementTable"); //$NON-NLS-1$
SourceElementTable table = state.getSourceElementTable();
int num = table.numPackages();
out.writeInt(num);
for (Enumeration e = state.getPackageMap().getAllPackages(); e.hasMoreElements();) {
IPackage pkg = (IPackage) e.nextElement();
out.writeInt(pool.index(pkg));
LookupTable entryTable = table.getPackageTable(pkg);
out.writeInt(entryTable.size());
for (Enumeration keys = entryTable.keys(); keys.hasMoreElements();) {
String fileName = (String) keys.nextElement();
out.writeInt(pool.index(fileName));
SourceEntry sEntry = (SourceEntry) entryTable.get(fileName);
out.writeInt(pool.index(sEntry));
}
}
}
/**
* Write a type structure entry.
*/
protected void writeTypeStructureEntry(TypeStructureEntry tsEntry, StateSnapConstantPool pool, DataOutputStream out) throws IOException {
out.writeInt(pool.index(tsEntry.getSourceEntry()));
out.writeInt(pool.index(tsEntry.getType()));
}
}