Eclipse 3.x compatible;
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / builder / State.java
index 057d387..06bf6ca 100644 (file)
@@ -29,585 +29,533 @@ import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.Path;
 
 public class State {
-// NOTE: this state cannot contain types that are not defined in this project
-
-String javaProjectName;
-ClasspathMultiDirectory[] sourceLocations;
-//ClasspathLocation[] binaryLocations;
-// keyed by the project relative path of the type (ie. "src1/p1/p2/A.java"), value is a ReferenceCollection or an AdditionalTypeCollection
-SimpleLookupTable references;
-// keyed by qualified type name "p1/p2/A", value is the project relative path which defines this type "src1/p1/p2/A.java"
-SimpleLookupTable typeLocators;
-
-int buildNumber;
-long lastStructuralBuildTime;
-SimpleLookupTable structuralBuildTimes;
-
-private String[] knownPackageNames; // of the form "p1/p2"
-
-static final byte VERSION = 0x0007;
-
-static final byte SOURCE_FOLDER = 1;
-static final byte BINARY_FOLDER = 2;
-static final byte EXTERNAL_JAR = 3;
-static final byte INTERNAL_JAR = 4;
-
-State() {
-}
-
-protected State(PHPBuilder javaBuilder) {
-       this.knownPackageNames = null;
-       this.javaProjectName = javaBuilder.currentProject.getName();
-       this.sourceLocations = javaBuilder.nameEnvironment.sourceLocations;
-//     this.binaryLocations = javaBuilder.nameEnvironment.binaryLocations;
-       this.references = new SimpleLookupTable(7);
-       this.typeLocators = new SimpleLookupTable(7);
-
-       this.buildNumber = 0; // indicates a full build
-       this.lastStructuralBuildTime = System.currentTimeMillis();
-       this.structuralBuildTimes = new SimpleLookupTable(3);
-}
-
-void copyFrom(State lastState) {
-       try {
-               this.knownPackageNames = null;
-               this.buildNumber = lastState.buildNumber + 1;
-               this.lastStructuralBuildTime = lastState.lastStructuralBuildTime;
-               this.references = (SimpleLookupTable) lastState.references.clone();
-               this.typeLocators = (SimpleLookupTable) lastState.typeLocators.clone();
-       } catch (CloneNotSupportedException e) {
-               this.references = new SimpleLookupTable(lastState.references.elementSize);
-               Object[] keyTable = lastState.references.keyTable;
-               Object[] valueTable = lastState.references.valueTable;
-               for (int i = 0, l = keyTable.length; i < l; i++)
-                       if (keyTable[i] != null)
-                               this.references.put(keyTable[i], valueTable[i]);
-
-               this.typeLocators = new SimpleLookupTable(lastState.typeLocators.elementSize);
-               keyTable = lastState.typeLocators.keyTable;
-               valueTable = lastState.typeLocators.valueTable;
-               for (int i = 0, l = keyTable.length; i < l; i++)
-                       if (keyTable[i] != null)
-                               this.typeLocators.put(keyTable[i], valueTable[i]);
-       }
-}
-
-char[][] getDefinedTypeNamesFor(String typeLocator) {
-       Object c = references.get(typeLocator);
-       if (c instanceof AdditionalTypeCollection)
-               return ((AdditionalTypeCollection) c).definedTypeNames;
-       return null; // means only one type is defined with the same name as the file... saves space
-}
-
-boolean isDuplicateLocator(String qualifiedTypeName, String typeLocator) {
-       String existing = (String) typeLocators.get(qualifiedTypeName);
-       return existing != null && !existing.equals(typeLocator);
-}
-
-boolean isKnownPackage(String qualifiedPackageName) {
-       if (knownPackageNames == null) {
-               ArrayList names = new ArrayList(typeLocators.elementSize);
-               Object[] keyTable = typeLocators.keyTable;
-               for (int i = 0, l = keyTable.length; i < l; i++) {
-                       if (keyTable[i] != null) {
-                               String packageName = (String) keyTable[i]; // is a type name of the form p1/p2/A
-                               int last = packageName.lastIndexOf('/');
-                               packageName = last == -1 ? null : packageName.substring(0, last);
-                               while (packageName != null && !names.contains(packageName)) {
-                                       names.add(packageName);
-                                       last = packageName.lastIndexOf('/');
-                                       packageName = last == -1 ? null : packageName.substring(0, last);
-                               }
-                       }
-               }
-               knownPackageNames = new String[names.size()];
-               names.toArray(knownPackageNames);
-       }
-       for (int i = 0, l = knownPackageNames.length; i < l; i++)
-               if (knownPackageNames[i].equals(qualifiedPackageName))
-                       return true;
-       return false;
-}
-
-void record(String typeLocator, char[][][] qualifiedRefs, char[][] simpleRefs, char[] mainTypeName, ArrayList typeNames) {
-       if (typeNames.size() == 1 && CharOperation.equals(mainTypeName, (char[]) typeNames.get(0))) {
-                       references.put(typeLocator, new ReferenceCollection(qualifiedRefs, simpleRefs));
-       } else {
-               char[][] definedTypeNames = new char[typeNames.size()][]; // can be empty when no types are defined
-               typeNames.toArray(definedTypeNames);
-               references.put(typeLocator, new AdditionalTypeCollection(definedTypeNames, qualifiedRefs, simpleRefs));
-       }
-}
-
-void recordLocatorForType(String qualifiedTypeName, String typeLocator) {
-       this.knownPackageNames = null;
-       typeLocators.put(qualifiedTypeName, typeLocator);
-}
-
-void recordStructuralDependency(IProject prereqProject, State prereqState) {
-       if (prereqState != null)
-               structuralBuildTimes.put(prereqProject.getName(), new Long(prereqState.lastStructuralBuildTime));
-}
-
-void removeLocator(String typeLocatorToRemove) {
-       this.knownPackageNames = null;
-       references.removeKey(typeLocatorToRemove);
-       typeLocators.removeValue(typeLocatorToRemove);
-}
-
-void removePackage(IResourceDelta sourceDelta) {
-       IResource resource = sourceDelta.getResource();
-       switch(resource.getType()) {
-               case IResource.FOLDER :
-                       IResourceDelta[] children = sourceDelta.getAffectedChildren();
-                       for (int i = 0, l = children.length; i < l; i++)
-                               removePackage(children[i]);
-                       return;
-               case IResource.FILE :
-                       IPath typeLocatorPath = resource.getProjectRelativePath();
-                       if (Util.isJavaFileName(typeLocatorPath.lastSegment()))
-                               removeLocator(typeLocatorPath.toString());
-       }
-}
-
-void removeQualifiedTypeName(String qualifiedTypeNameToRemove) {
-       this.knownPackageNames = null;
-       typeLocators.removeKey(qualifiedTypeNameToRemove);
-}
-
-static State read(IProject project, DataInputStream in) throws IOException {
-       if (PHPBuilder.DEBUG)
-               System.out.println("About to read state..."); //$NON-NLS-1$
-       if (VERSION != in.readByte()) {
-               if (PHPBuilder.DEBUG)
-                       System.out.println("Found non-compatible state version... answered null"); //$NON-NLS-1$
-               return null;
-       }
-
-       State newState = new State();
-       newState.javaProjectName = in.readUTF();
-       if (!project.getName().equals(newState.javaProjectName)) {
-               if (PHPBuilder.DEBUG)
-                       System.out.println("Project's name does not match... answered null"); //$NON-NLS-1$
-               return null;
-       }
-       newState.buildNumber = in.readInt();
-       newState.lastStructuralBuildTime = in.readLong();
-
-       int length = in.readInt();
-       newState.sourceLocations = new ClasspathMultiDirectory[0];
-//     newState.sourceLocations = new ClasspathMultiDirectory[length];
-//     for (int i = 0; i < length; i++) {
-//             IContainer sourceFolder = project, outputFolder = project;
-//             String folderName;
-//             if ((folderName = in.readUTF()).length() > 0) sourceFolder = project.getFolder(folderName);
-//             if ((folderName = in.readUTF()).length() > 0) outputFolder = project.getFolder(folderName);
-//             ClasspathMultiDirectory md =
-//                     (ClasspathMultiDirectory) ClasspathLocation.forSourceFolder(sourceFolder, outputFolder, readNames(in));
-//             if (in.readBoolean())
-//                     md.hasIndependentOutputFolder = true;
-//             newState.sourceLocations[i] = md;
-//     }
-
-       length = in.readInt();
-//     newState.binaryLocations = new ClasspathLocation[length];
-//     IWorkspaceRoot root = project.getWorkspace().getRoot();
-//     for (int i = 0; i < length; i++) {
-//             switch (in.readByte()) {
-//                     case SOURCE_FOLDER :
-//                             newState.binaryLocations[i] = newState.sourceLocations[in.readInt()];
-//                             break;
-//                     case BINARY_FOLDER :
-//                             IPath path = new Path(in.readUTF());
-//                             IContainer outputFolder = path.segmentCount() == 1
-//                                     ? (IContainer) root.getProject(path.toString())
-//                                     : (IContainer) root.getFolder(path);
-//                             newState.binaryLocations[i] = ClasspathLocation.forBinaryFolder(outputFolder, in.readBoolean());
-//                             break;
-//                     case EXTERNAL_JAR :
-//                             newState.binaryLocations[i] = ClasspathLocation.forLibrary(in.readUTF());
-//                             break;
-//                     case INTERNAL_JAR :
-//                             newState.binaryLocations[i] = ClasspathLocation.forLibrary(root.getFile(new Path(in.readUTF())));
-//             }
-//     }
-
-       newState.structuralBuildTimes = new SimpleLookupTable(length = in.readInt());
-       for (int i = 0; i < length; i++)
-               newState.structuralBuildTimes.put(in.readUTF(), new Long(in.readLong()));
-
-       String[] internedTypeLocators = new String[length = in.readInt()];
-       for (int i = 0; i < length; i++)
-               internedTypeLocators[i] = in.readUTF();
-
-       newState.typeLocators = new SimpleLookupTable(length = in.readInt());
-       for (int i = 0; i < length; i++)
-               newState.typeLocators.put(in.readUTF(), internedTypeLocators[in.readInt()]);
-
-       char[][] internedSimpleNames = ReferenceCollection.internSimpleNames(readNames(in), false);
-       char[][][] internedQualifiedNames = new char[length = in.readInt()][][];
-       for (int i = 0; i < length; i++) {
-               int qLength = in.readInt();
-               char[][] qName = new char[qLength][];
-               for (int j = 0; j < qLength; j++)
-                       qName[j] = internedSimpleNames[in.readInt()];
-               internedQualifiedNames[i] = qName;
-       }
-       internedQualifiedNames = ReferenceCollection.internQualifiedNames(internedQualifiedNames);
-
-       newState.references = new SimpleLookupTable(length = in.readInt());
-       for (int i = 0; i < length; i++) {
-               String typeLocator = internedTypeLocators[in.readInt()];
-               ReferenceCollection collection = null;
-               switch (in.readByte()) {
-                       case 1 :
-                               char[][] additionalTypeNames = readNames(in);
-                               char[][][] qualifiedNames = new char[in.readInt()][][];
-                               for (int j = 0, m = qualifiedNames.length; j < m; j++)
-                                       qualifiedNames[j] = internedQualifiedNames[in.readInt()];
-                               char[][] simpleNames = new char[in.readInt()][];
-                               for (int j = 0, m = simpleNames.length; j < m; j++)
-                                       simpleNames[j] = internedSimpleNames[in.readInt()];
-                               collection = new AdditionalTypeCollection(additionalTypeNames, qualifiedNames, simpleNames);
-                               break;
-                       case 2 :
-                               char[][][] qNames = new char[in.readInt()][][];
-                               for (int j = 0, m = qNames.length; j < m; j++)
-                                       qNames[j] = internedQualifiedNames[in.readInt()];
-                               char[][] sNames = new char[in.readInt()][];
-                               for (int j = 0, m = sNames.length; j < m; j++)
-                                       sNames[j] = internedSimpleNames[in.readInt()];
-                               collection = new ReferenceCollection(qNames, sNames);
-               }
-               newState.references.put(typeLocator, collection);
-       }
-       if (PHPBuilder.DEBUG)
-               System.out.println("Successfully read state for " + newState.javaProjectName); //$NON-NLS-1$
-       return newState;
-}
-
-private static char[][] readNames(DataInputStream in) throws IOException {
-       int length = in.readInt();
-       char[][] names = new char[length][];
-       for (int i = 0; i < length; i++) {
-               int nLength = in.readInt();
-               char[] name = new char[nLength];
-               for (int j = 0; j < nLength; j++)
-                       name[j] = in.readChar();
-               names[i] = name;
-       }
-       return names;
-}
-
-void tagAsNoopBuild() {
-       this.buildNumber = -1; // tag the project since it has no source folders and can be skipped
-}
-
-boolean wasNoopBuild() {
-       return buildNumber == -1;
-}
-
-void tagAsStructurallyChanged() {
-       this.lastStructuralBuildTime = System.currentTimeMillis();
-}
-
-boolean wasStructurallyChanged(IProject prereqProject, State prereqState) {
-       if (prereqState != null) {
-               Object o = structuralBuildTimes.get(prereqProject.getName());
-               long previous = o == null ? 0 : ((Long) o).longValue();
-               if (previous == prereqState.lastStructuralBuildTime) return false;
-       }
-       return true;
-}
-
-void write(DataOutputStream out) throws IOException {
-       int length;
-       Object[] keyTable;
-       Object[] valueTable;
-
-/*
- * byte                        VERSION
- * String              project name
- * int                         build number
- * int                         last structural build number
-*/
-       out.writeByte(VERSION);
-       out.writeUTF(javaProjectName);
-       out.writeInt(buildNumber);
-       out.writeLong(lastStructuralBuildTime);
-
-/*
- * ClasspathMultiDirectory[]
- * int                         id
- * String              path(s)
-*/
-       out.writeInt(length = sourceLocations.length);
-       for (int i = 0; i < length; i++) {
-               ClasspathMultiDirectory md = sourceLocations[i];
-               out.writeUTF(md.sourceFolder.getProjectRelativePath().toString());
-               out.writeUTF(md.binaryFolder.getProjectRelativePath().toString());
-               writeNames(md.exclusionPatterns, out);
-               out.writeBoolean(md.hasIndependentOutputFolder);
-       }
-
-/*
- * ClasspathLocation[]
- * int                         id
- * String              path(s)
-*/
-//     out.writeInt(length = binaryLocations.length);
-//     next : for (int i = 0; i < length; i++) {
-//             ClasspathLocation c = binaryLocations[i];
-//             if (c instanceof ClasspathMultiDirectory) {
-//                     out.writeByte(SOURCE_FOLDER);
-//                     for (int j = 0, m = sourceLocations.length; j < m; j++) {
-//                             if (sourceLocations[j] == c) {
-//                                     out.writeInt(j);
-//                                     continue next;
-//                             }
-//                     }
-//             } else if (c instanceof ClasspathDirectory) {
-//                     out.writeByte(BINARY_FOLDER);
-//                     ClasspathDirectory cd = (ClasspathDirectory) c;
-//                     out.writeUTF(cd.binaryFolder.getFullPath().toString());
-//                     out.writeBoolean(cd.isOutputFolder);
-//             } else {
-//                     ClasspathJar jar = (ClasspathJar) c;
-//                     if (jar.resource == null) {
-//                             out.writeByte(EXTERNAL_JAR);
-//                             out.writeUTF(jar.zipFilename);
-//                     } else {
-//                             out.writeByte(INTERNAL_JAR);
-//                             out.writeUTF(jar.resource.getFullPath().toString());
-//                     }
-//             }
-//     }
-
-/*
- * Structural build numbers table
- * String              prereq project name
- * int                         last structural build number
-*/
-       out.writeInt(length = structuralBuildTimes.elementSize);
-       if (length > 0) {
-               keyTable = structuralBuildTimes.keyTable;
-               valueTable = structuralBuildTimes.valueTable;
-               for (int i = 0, l = keyTable.length; i < l; i++) {
-                       if (keyTable[i] != null) {
-                               length--;
-                               out.writeUTF((String) keyTable[i]);
-                               out.writeLong(((Long) valueTable[i]).longValue());
-                       }
-               }
-               if (PHPBuilder.DEBUG && length != 0)
-                       System.out.println("structuralBuildNumbers table is inconsistent"); //$NON-NLS-1$
-       }
-
-/*
- * String[]            Interned type locators
- */
-       out.writeInt(length = references.elementSize);
-       ArrayList internedTypeLocators = new ArrayList(length);
-       if (length > 0) {
-               keyTable = references.keyTable;
-               for (int i = 0, l = keyTable.length; i < l; i++) {
-                       if (keyTable[i] != null) {
-                               length--;
-                               String key = (String) keyTable[i];
-                               out.writeUTF(key);
-                               internedTypeLocators.add(key);
-                       }
-               }
-               if (PHPBuilder.DEBUG && length != 0)
-                       System.out.println("references table is inconsistent"); //$NON-NLS-1$
-       }
-
-/*
- * Type locators table
- * String              type name
- * int                         interned locator id
- */
-       out.writeInt(length = typeLocators.elementSize);
-       if (length > 0) {
-               keyTable = typeLocators.keyTable;
-               valueTable = typeLocators.valueTable;
-               for (int i = 0, l = keyTable.length; i < l; i++) {
-                       if (keyTable[i] != null) {
-                               length--;
-                               out.writeUTF((String) keyTable[i]);
-                               out.writeInt(internedTypeLocators.indexOf((String) valueTable[i]));
-                       }
-               }
-               if (PHPBuilder.DEBUG && length != 0)
-                       System.out.println("typeLocators table is inconsistent"); //$NON-NLS-1$
-       }
-
-/*
- * char[][][]  Interned qualified names
- * char[][]            Interned simple names
- */
-       ArrayList internedQualifiedNames = new ArrayList(31);
-       ArrayList internedSimpleNames = new ArrayList(31);
-       valueTable = references.valueTable;
-       for (int i = 0, l = valueTable.length; i < l; i++) {
-               if (valueTable[i] != null) {
-                       ReferenceCollection collection = (ReferenceCollection) valueTable[i];
-                       char[][][] qNames = collection.qualifiedNameReferences;
-                       for (int j = 0, m = qNames.length; j < m; j++) {
-                               char[][] qName = qNames[j];
-                               if (!internedQualifiedNames.contains(qName)) { // remember the names have been interned
-                                       internedQualifiedNames.add(qName);
-                                       for (int k = 0, n = qName.length; k < n; k++) {
-                                               char[] sName = qName[k];
-                                               if (!internedSimpleNames.contains(sName)) // remember the names have been interned
-                                                       internedSimpleNames.add(sName);
-                                       }
-                               }
-                       }
-                       char[][] sNames = collection.simpleNameReferences;
-                       for (int j = 0, m = sNames.length; j < m; j++) {
-                               char[] sName = sNames[j];
-                               if (!internedSimpleNames.contains(sName)) // remember the names have been interned
-                                       internedSimpleNames.add(sName);
-                       }
-               }
-       }
-       char[][] internedArray = new char[internedSimpleNames.size()][];
-       internedSimpleNames.toArray(internedArray);
-       writeNames(internedArray, out);
-       // now write the interned qualified names as arrays of interned simple names
-       out.writeInt(length = internedQualifiedNames.size());
-       for (int i = 0; i < length; i++) {
-               char[][] qName = (char[][]) internedQualifiedNames.get(i);
-               int qLength = qName.length;
-               out.writeInt(qLength);
-               for (int j = 0; j < qLength; j++)
-                       out.writeInt(internedSimpleNames.indexOf(qName[j]));
-       }
-
-/*
- * References table
- * int                 interned locator id
- * ReferenceCollection
-*/
-       out.writeInt(length = references.elementSize);
-       if (length > 0) {
-               keyTable = references.keyTable;
-               for (int i = 0, l = keyTable.length; i < l; i++) {
-                       if (keyTable[i] != null) {
-                               length--;
-                               out.writeInt(internedTypeLocators.indexOf((String) keyTable[i]));
-                               ReferenceCollection collection = (ReferenceCollection) valueTable[i];
-                               if (collection instanceof AdditionalTypeCollection) {
-                                       out.writeByte(1);
-                                       AdditionalTypeCollection atc = (AdditionalTypeCollection) collection;
-                                       writeNames(atc.definedTypeNames, out);
-                               } else {
-                                       out.writeByte(2);
-                               }
-                               char[][][] qNames = collection.qualifiedNameReferences;
-                               int qLength = qNames.length;
-                               out.writeInt(qLength);
-                               for (int j = 0; j < qLength; j++)
-                                       out.writeInt(internedQualifiedNames.indexOf(qNames[j]));
-                               char[][] sNames = collection.simpleNameReferences;
-                               int sLength = sNames.length;
-                               out.writeInt(sLength);
-                               for (int j = 0; j < sLength; j++)
-                                       out.writeInt(internedSimpleNames.indexOf(sNames[j]));
-                       }
-               }
-               if (PHPBuilder.DEBUG && length != 0)
-                       System.out.println("references table is inconsistent"); //$NON-NLS-1$
-       }
-}
-
-private void writeNames(char[][] names, DataOutputStream out) throws IOException {
-       int length = names == null ? 0 : names.length;
-       out.writeInt(length);
-       for (int i = 0; i < length; i++) {
-               char[] name = names[i];
-               int nLength = name.length;
-               out.writeInt(nLength);
-               for (int j = 0; j < nLength; j++)
-                       out.writeChar(name[j]);
-       }
-}
-
-/**
- * Returns a string representation of the receiver.
- */
-public String toString() {
-       return "State for " + javaProjectName //$NON-NLS-1$
-               + " (#" + buildNumber //$NON-NLS-1$
-                       + " @ " + new Date(lastStructuralBuildTime) //$NON-NLS-1$
-                               + ")"; //$NON-NLS-1$
-}
-
-/* Debug helper
-void dump() {
-       System.out.println("State for " + javaProjectName + " (" + buildNumber + " @ " + new Date(lastStructuralBuildTime) + ")");
-       System.out.println("\tClass path source locations:");
-       for (int i = 0, l = sourceLocations.length; i < l; i++)
-               System.out.println("\t\t" + sourceLocations[i]);
-       System.out.println("\tClass path binary locations:");
-       for (int i = 0, l = binaryLocations.length; i < l; i++)
-               System.out.println("\t\t" + binaryLocations[i]);
-
-       System.out.print("\tStructural build numbers table:");
-       if (structuralBuildTimes.elementSize == 0) {
-               System.out.print(" <empty>");
-       } else {
-               Object[] keyTable = structuralBuildTimes.keyTable;
-               Object[] valueTable = structuralBuildTimes.valueTable;
-               for (int i = 0, l = keyTable.length; i < l; i++)
-                       if (keyTable[i] != null)
-                               System.out.print("\n\t\t" + keyTable[i].toString() + " -> " + valueTable[i].toString());
-       }
-
-       System.out.print("\tType locators table:");
-       if (typeLocators.elementSize == 0) {
-               System.out.print(" <empty>");
-       } else {
-               Object[] keyTable = typeLocators.keyTable;
-               Object[] valueTable = typeLocators.valueTable;
-               for (int i = 0, l = keyTable.length; i < l; i++)
-                       if (keyTable[i] != null)
-                               System.out.print("\n\t\t" + keyTable[i].toString() + " -> " + valueTable[i].toString());
-       }
-
-       System.out.print("\n\tReferences table:");
-       if (references.elementSize == 0) {
-               System.out.print(" <empty>");
-       } else {
-               Object[] keyTable = references.keyTable;
-               Object[] valueTable = references.valueTable;
-               for (int i = 0, l = keyTable.length; i < l; i++) {
-                       if (keyTable[i] != null) {
-                               System.out.print("\n\t\t" + keyTable[i].toString());
-                               ReferenceCollection c = (ReferenceCollection) valueTable[i];
-                               char[][][] qRefs = c.qualifiedNameReferences;
-                               System.out.print("\n\t\t\tqualified:");
-                               if (qRefs.length == 0)
-                                       System.out.print(" <empty>");
-                               else for (int j = 0, m = qRefs.length; j < m; j++)
-                                               System.out.print("  '" + CharOperation.toString(qRefs[j]) + "'");
-                               char[][] sRefs = c.simpleNameReferences;
-                               System.out.print("\n\t\t\tsimple:");
-                               if (sRefs.length == 0)
-                                       System.out.print(" <empty>");
-                               else for (int j = 0, m = sRefs.length; j < m; j++)
-                                               System.out.print("  " + new String(sRefs[j]));
-                               if (c instanceof AdditionalTypeCollection) {
-                                       char[][] names = ((AdditionalTypeCollection) c).definedTypeNames;
-                                       System.out.print("\n\t\t\tadditional type names:");
-                                       for (int j = 0, m = names.length; j < m; j++)
-                                               System.out.print("  " + new String(names[j]));
-                               }
-                       }
-               }
-       }
-       System.out.print("\n\n");
-}
-*/
+  // NOTE: this state cannot contain types that are not defined in this project
+
+  String javaProjectName;
+  ClasspathMultiDirectory[] sourceLocations;
+  //ClasspathLocation[] binaryLocations;
+  // keyed by the project relative path of the type (ie. "src1/p1/p2/A.java"), value is a ReferenceCollection or an
+  // AdditionalTypeCollection
+  SimpleLookupTable references;
+  // keyed by qualified type name "p1/p2/A", value is the project relative path which defines this type "src1/p1/p2/A.java"
+  SimpleLookupTable typeLocators;
+
+  int buildNumber;
+  long lastStructuralBuildTime;
+  SimpleLookupTable structuralBuildTimes;
+
+  private String[] knownPackageNames; // of the form "p1/p2"
+
+  static final byte VERSION = 0x0007;
+
+  static final byte SOURCE_FOLDER = 1;
+//  static final byte BINARY_FOLDER = 2;
+//  static final byte EXTERNAL_JAR = 3;
+//  static final byte INTERNAL_JAR = 4;
+
+  State() {
+  }
+
+  protected State(PHPBuilder javaBuilder) {
+    this.knownPackageNames = null;
+    this.javaProjectName = javaBuilder.currentProject.getName();
+    this.sourceLocations = javaBuilder.nameEnvironment.sourceLocations;
+    // this.binaryLocations = javaBuilder.nameEnvironment.binaryLocations;
+    this.references = new SimpleLookupTable(7);
+    this.typeLocators = new SimpleLookupTable(7);
+
+    this.buildNumber = 0; // indicates a full build
+    this.lastStructuralBuildTime = System.currentTimeMillis();
+    this.structuralBuildTimes = new SimpleLookupTable(3);
+  }
+
+  void copyFrom(State lastState) {
+    try {
+      this.knownPackageNames = null;
+      this.buildNumber = lastState.buildNumber + 1;
+      this.lastStructuralBuildTime = lastState.lastStructuralBuildTime;
+      this.references = (SimpleLookupTable) lastState.references.clone();
+      this.typeLocators = (SimpleLookupTable) lastState.typeLocators.clone();
+    } catch (CloneNotSupportedException e) {
+      this.references = new SimpleLookupTable(lastState.references.elementSize);
+      Object[] keyTable = lastState.references.keyTable;
+      Object[] valueTable = lastState.references.valueTable;
+      for (int i = 0, l = keyTable.length; i < l; i++)
+        if (keyTable[i] != null)
+          this.references.put(keyTable[i], valueTable[i]);
+
+      this.typeLocators = new SimpleLookupTable(lastState.typeLocators.elementSize);
+      keyTable = lastState.typeLocators.keyTable;
+      valueTable = lastState.typeLocators.valueTable;
+      for (int i = 0, l = keyTable.length; i < l; i++)
+        if (keyTable[i] != null)
+          this.typeLocators.put(keyTable[i], valueTable[i]);
+    }
+  }
+
+  char[][] getDefinedTypeNamesFor(String typeLocator) {
+    Object c = references.get(typeLocator);
+    if (c instanceof AdditionalTypeCollection)
+      return ((AdditionalTypeCollection) c).definedTypeNames;
+    return null; // means only one type is defined with the same name as the file... saves space
+  }
+
+  boolean isDuplicateLocator(String qualifiedTypeName, String typeLocator) {
+    String existing = (String) typeLocators.get(qualifiedTypeName);
+    return existing != null && !existing.equals(typeLocator);
+  }
+
+  boolean isKnownPackage(String qualifiedPackageName) {
+    if (knownPackageNames == null) {
+      ArrayList names = new ArrayList(typeLocators.elementSize);
+      Object[] keyTable = typeLocators.keyTable;
+      for (int i = 0, l = keyTable.length; i < l; i++) {
+        if (keyTable[i] != null) {
+          String packageName = (String) keyTable[i]; // is a type name of the form p1/p2/A
+          int last = packageName.lastIndexOf('/');
+          packageName = last == -1 ? null : packageName.substring(0, last);
+          while (packageName != null && !names.contains(packageName)) {
+            names.add(packageName);
+            last = packageName.lastIndexOf('/');
+            packageName = last == -1 ? null : packageName.substring(0, last);
+          }
+        }
+      }
+      knownPackageNames = new String[names.size()];
+      names.toArray(knownPackageNames);
+    }
+    for (int i = 0, l = knownPackageNames.length; i < l; i++)
+      if (knownPackageNames[i].equals(qualifiedPackageName))
+        return true;
+    return false;
+  }
+
+  void record(String typeLocator, char[][][] qualifiedRefs, char[][] simpleRefs, char[] mainTypeName, ArrayList typeNames) {
+    if (typeNames.size() == 1 && CharOperation.equals(mainTypeName, (char[]) typeNames.get(0))) {
+      references.put(typeLocator, new ReferenceCollection(qualifiedRefs, simpleRefs));
+    } else {
+      char[][] definedTypeNames = new char[typeNames.size()][]; // can be empty when no types are defined
+      typeNames.toArray(definedTypeNames);
+      references.put(typeLocator, new AdditionalTypeCollection(definedTypeNames, qualifiedRefs, simpleRefs));
+    }
+  }
+
+  void recordLocatorForType(String qualifiedTypeName, String typeLocator) {
+    this.knownPackageNames = null;
+    typeLocators.put(qualifiedTypeName, typeLocator);
+  }
+
+  void recordStructuralDependency(IProject prereqProject, State prereqState) {
+    if (prereqState != null)
+      structuralBuildTimes.put(prereqProject.getName(), new Long(prereqState.lastStructuralBuildTime));
+  }
+
+  void removeLocator(String typeLocatorToRemove) {
+    this.knownPackageNames = null;
+    references.removeKey(typeLocatorToRemove);
+    typeLocators.removeValue(typeLocatorToRemove);
+  }
+
+  void removePackage(IResourceDelta sourceDelta) {
+    IResource resource = sourceDelta.getResource();
+    switch (resource.getType()) {
+      case IResource.FOLDER :
+        IResourceDelta[] children = sourceDelta.getAffectedChildren();
+        for (int i = 0, l = children.length; i < l; i++)
+          removePackage(children[i]);
+        return;
+      case IResource.FILE :
+        IPath typeLocatorPath = resource.getProjectRelativePath();
+        if (Util.isJavaFileName(typeLocatorPath.lastSegment()))
+          removeLocator(typeLocatorPath.toString());
+    }
+  }
+
+  void removeQualifiedTypeName(String qualifiedTypeNameToRemove) {
+    this.knownPackageNames = null;
+    typeLocators.removeKey(qualifiedTypeNameToRemove);
+  }
+
+  static State read(IProject project, DataInputStream in) throws IOException {
+    if (PHPBuilder.DEBUG)
+      System.out.println("About to read state..."); //$NON-NLS-1$
+    if (VERSION != in.readByte()) {
+      if (PHPBuilder.DEBUG)
+        System.out.println("Found non-compatible state version... answered null"); //$NON-NLS-1$
+      return null;
+    }
+
+    State newState = new State();
+    newState.javaProjectName = in.readUTF();
+    if (!project.getName().equals(newState.javaProjectName)) {
+      if (PHPBuilder.DEBUG)
+        System.out.println("Project's name does not match... answered null"); //$NON-NLS-1$
+      return null;
+    }
+    newState.buildNumber = in.readInt();
+    newState.lastStructuralBuildTime = in.readLong();
+
+    int length = in.readInt();
+    newState.sourceLocations = new ClasspathMultiDirectory[0];
+    // newState.sourceLocations = new ClasspathMultiDirectory[length];
+    // for (int i = 0; i < length; i++) {
+    //         IContainer sourceFolder = project, outputFolder = project;
+    //         String folderName;
+    //         if ((folderName = in.readUTF()).length() > 0) sourceFolder = project.getFolder(folderName);
+    //         if ((folderName = in.readUTF()).length() > 0) outputFolder = project.getFolder(folderName);
+    //         ClasspathMultiDirectory md =
+    //                 (ClasspathMultiDirectory) ClasspathLocation.forSourceFolder(sourceFolder, outputFolder, readNames(in));
+    //         if (in.readBoolean())
+    //                 md.hasIndependentOutputFolder = true;
+    //         newState.sourceLocations[i] = md;
+    // }
+
+    length = in.readInt();
+    // newState.binaryLocations = new ClasspathLocation[length];
+    // IWorkspaceRoot root = project.getWorkspace().getRoot();
+    // for (int i = 0; i < length; i++) {
+    //         switch (in.readByte()) {
+    //                 case SOURCE_FOLDER :
+    //                         newState.binaryLocations[i] = newState.sourceLocations[in.readInt()];
+    //                         break;
+    //                 case BINARY_FOLDER :
+    //                         IPath path = new Path(in.readUTF());
+    //                         IContainer outputFolder = path.segmentCount() == 1
+    //                                 ? (IContainer) root.getProject(path.toString())
+    //                                 : (IContainer) root.getFolder(path);
+    //                         newState.binaryLocations[i] = ClasspathLocation.forBinaryFolder(outputFolder, in.readBoolean());
+    //                         break;
+    //                 case EXTERNAL_JAR :
+    //                         newState.binaryLocations[i] = ClasspathLocation.forLibrary(in.readUTF());
+    //                         break;
+    //                 case INTERNAL_JAR :
+    //                         newState.binaryLocations[i] = ClasspathLocation.forLibrary(root.getFile(new Path(in.readUTF())));
+    //         }
+    // }
+
+    newState.structuralBuildTimes = new SimpleLookupTable(length = in.readInt());
+    for (int i = 0; i < length; i++)
+      newState.structuralBuildTimes.put(in.readUTF(), new Long(in.readLong()));
+
+    String[] internedTypeLocators = new String[length = in.readInt()];
+    for (int i = 0; i < length; i++)
+      internedTypeLocators[i] = in.readUTF();
+
+    newState.typeLocators = new SimpleLookupTable(length = in.readInt());
+    for (int i = 0; i < length; i++)
+      newState.typeLocators.put(in.readUTF(), internedTypeLocators[in.readInt()]);
+
+    char[][] internedSimpleNames = ReferenceCollection.internSimpleNames(readNames(in), false);
+    char[][][] internedQualifiedNames = new char[length = in.readInt()][][];
+    for (int i = 0; i < length; i++) {
+      int qLength = in.readInt();
+      char[][] qName = new char[qLength][];
+      for (int j = 0; j < qLength; j++)
+        qName[j] = internedSimpleNames[in.readInt()];
+      internedQualifiedNames[i] = qName;
+    }
+    internedQualifiedNames = ReferenceCollection.internQualifiedNames(internedQualifiedNames);
+
+    newState.references = new SimpleLookupTable(length = in.readInt());
+    for (int i = 0; i < length; i++) {
+      String typeLocator = internedTypeLocators[in.readInt()];
+      ReferenceCollection collection = null;
+      switch (in.readByte()) {
+        case 1 :
+          char[][] additionalTypeNames = readNames(in);
+          char[][][] qualifiedNames = new char[in.readInt()][][];
+          for (int j = 0, m = qualifiedNames.length; j < m; j++)
+            qualifiedNames[j] = internedQualifiedNames[in.readInt()];
+          char[][] simpleNames = new char[in.readInt()][];
+          for (int j = 0, m = simpleNames.length; j < m; j++)
+            simpleNames[j] = internedSimpleNames[in.readInt()];
+          collection = new AdditionalTypeCollection(additionalTypeNames, qualifiedNames, simpleNames);
+          break;
+        case 2 :
+          char[][][] qNames = new char[in.readInt()][][];
+          for (int j = 0, m = qNames.length; j < m; j++)
+            qNames[j] = internedQualifiedNames[in.readInt()];
+          char[][] sNames = new char[in.readInt()][];
+          for (int j = 0, m = sNames.length; j < m; j++)
+            sNames[j] = internedSimpleNames[in.readInt()];
+          collection = new ReferenceCollection(qNames, sNames);
+      }
+      newState.references.put(typeLocator, collection);
+    }
+    if (PHPBuilder.DEBUG)
+      System.out.println("Successfully read state for " + newState.javaProjectName); //$NON-NLS-1$
+    return newState;
+  }
+
+  private static char[][] readNames(DataInputStream in) throws IOException {
+    int length = in.readInt();
+    char[][] names = new char[length][];
+    for (int i = 0; i < length; i++) {
+      int nLength = in.readInt();
+      char[] name = new char[nLength];
+      for (int j = 0; j < nLength; j++)
+        name[j] = in.readChar();
+      names[i] = name;
+    }
+    return names;
+  }
+
+  void tagAsNoopBuild() {
+    this.buildNumber = -1; // tag the project since it has no source folders and can be skipped
+  }
+
+  boolean wasNoopBuild() {
+    return buildNumber == -1;
+  }
+
+  void tagAsStructurallyChanged() {
+    this.lastStructuralBuildTime = System.currentTimeMillis();
+  }
+
+  boolean wasStructurallyChanged(IProject prereqProject, State prereqState) {
+    if (prereqState != null) {
+      Object o = structuralBuildTimes.get(prereqProject.getName());
+      long previous = o == null ? 0 : ((Long) o).longValue();
+      if (previous == prereqState.lastStructuralBuildTime)
+        return false;
+    }
+    return true;
+  }
+
+  void write(DataOutputStream out) throws IOException {
+    int length;
+    Object[] keyTable;
+    Object[] valueTable;
+
+    /*
+        * byte VERSION String project name int build number int last structural build number
+        */
+    out.writeByte(VERSION);
+    out.writeUTF(javaProjectName);
+    out.writeInt(buildNumber);
+    out.writeLong(lastStructuralBuildTime);
+
+    /*
+        * ClasspathMultiDirectory[] int id String path(s)
+        */
+    out.writeInt(length = sourceLocations.length);
+    for (int i = 0; i < length; i++) {
+      ClasspathMultiDirectory md = sourceLocations[i];
+      out.writeUTF(md.sourceFolder.getProjectRelativePath().toString());
+      out.writeUTF(md.binaryFolder.getProjectRelativePath().toString());
+      writeNames(md.exclusionPatterns, out);
+      out.writeBoolean(md.hasIndependentOutputFolder);
+    }
+
+    /*
+        * ClasspathLocation[] int id String path(s)
+        */
+    // out.writeInt(length = binaryLocations.length);
+    // next : for (int i = 0; i < length; i++) {
+    //         ClasspathLocation c = binaryLocations[i];
+    //         if (c instanceof ClasspathMultiDirectory) {
+    //                 out.writeByte(SOURCE_FOLDER);
+    //                 for (int j = 0, m = sourceLocations.length; j < m; j++) {
+    //                         if (sourceLocations[j] == c) {
+    //                                 out.writeInt(j);
+    //                                 continue next;
+    //                         }
+    //                 }
+    //         } else if (c instanceof ClasspathDirectory) {
+    //                 out.writeByte(BINARY_FOLDER);
+    //                 ClasspathDirectory cd = (ClasspathDirectory) c;
+    //                 out.writeUTF(cd.binaryFolder.getFullPath().toString());
+    //                 out.writeBoolean(cd.isOutputFolder);
+    //         } else {
+    //                 ClasspathJar jar = (ClasspathJar) c;
+    //                 if (jar.resource == null) {
+    //                         out.writeByte(EXTERNAL_JAR);
+    //                         out.writeUTF(jar.zipFilename);
+    //                 } else {
+    //                         out.writeByte(INTERNAL_JAR);
+    //                         out.writeUTF(jar.resource.getFullPath().toString());
+    //                 }
+    //         }
+    // }
+
+    /*
+        * Structural build numbers table String prereq project name int last structural build number
+        */
+    out.writeInt(length = structuralBuildTimes.elementSize);
+    if (length > 0) {
+      keyTable = structuralBuildTimes.keyTable;
+      valueTable = structuralBuildTimes.valueTable;
+      for (int i = 0, l = keyTable.length; i < l; i++) {
+        if (keyTable[i] != null) {
+          length--;
+          out.writeUTF((String) keyTable[i]);
+          out.writeLong(((Long) valueTable[i]).longValue());
+        }
+      }
+      if (PHPBuilder.DEBUG && length != 0)
+        System.out.println("structuralBuildNumbers table is inconsistent"); //$NON-NLS-1$
+    }
+
+    /*
+        * String[] Interned type locators
+        */
+    out.writeInt(length = references.elementSize);
+    ArrayList internedTypeLocators = new ArrayList(length);
+    if (length > 0) {
+      keyTable = references.keyTable;
+      for (int i = 0, l = keyTable.length; i < l; i++) {
+        if (keyTable[i] != null) {
+          length--;
+          String key = (String) keyTable[i];
+          out.writeUTF(key);
+          internedTypeLocators.add(key);
+        }
+      }
+      if (PHPBuilder.DEBUG && length != 0)
+        System.out.println("references table is inconsistent"); //$NON-NLS-1$
+    }
+
+    /*
+        * Type locators table String type name int interned locator id
+        */
+    out.writeInt(length = typeLocators.elementSize);
+    if (length > 0) {
+      keyTable = typeLocators.keyTable;
+      valueTable = typeLocators.valueTable;
+      for (int i = 0, l = keyTable.length; i < l; i++) {
+        if (keyTable[i] != null) {
+          length--;
+          out.writeUTF((String) keyTable[i]);
+          out.writeInt(internedTypeLocators.indexOf((String) valueTable[i]));
+        }
+      }
+      if (PHPBuilder.DEBUG && length != 0)
+        System.out.println("typeLocators table is inconsistent"); //$NON-NLS-1$
+    }
+
+    /*
+        * char[][][] Interned qualified names char[][] Interned simple names
+        */
+    ArrayList internedQualifiedNames = new ArrayList(31);
+    ArrayList internedSimpleNames = new ArrayList(31);
+    valueTable = references.valueTable;
+    for (int i = 0, l = valueTable.length; i < l; i++) {
+      if (valueTable[i] != null) {
+        ReferenceCollection collection = (ReferenceCollection) valueTable[i];
+        char[][][] qNames = collection.qualifiedNameReferences;
+        for (int j = 0, m = qNames.length; j < m; j++) {
+          char[][] qName = qNames[j];
+          if (!internedQualifiedNames.contains(qName)) { // remember the names have been interned
+            internedQualifiedNames.add(qName);
+            for (int k = 0, n = qName.length; k < n; k++) {
+              char[] sName = qName[k];
+              if (!internedSimpleNames.contains(sName)) // remember the names have been interned
+                internedSimpleNames.add(sName);
+            }
+          }
+        }
+        char[][] sNames = collection.simpleNameReferences;
+        for (int j = 0, m = sNames.length; j < m; j++) {
+          char[] sName = sNames[j];
+          if (!internedSimpleNames.contains(sName)) // remember the names have been interned
+            internedSimpleNames.add(sName);
+        }
+      }
+    }
+    char[][] internedArray = new char[internedSimpleNames.size()][];
+    internedSimpleNames.toArray(internedArray);
+    writeNames(internedArray, out);
+    // now write the interned qualified names as arrays of interned simple names
+    out.writeInt(length = internedQualifiedNames.size());
+    for (int i = 0; i < length; i++) {
+      char[][] qName = (char[][]) internedQualifiedNames.get(i);
+      int qLength = qName.length;
+      out.writeInt(qLength);
+      for (int j = 0; j < qLength; j++)
+        out.writeInt(internedSimpleNames.indexOf(qName[j]));
+    }
+
+    /*
+        * References table int interned locator id ReferenceCollection
+        */
+    out.writeInt(length = references.elementSize);
+    if (length > 0) {
+      keyTable = references.keyTable;
+      for (int i = 0, l = keyTable.length; i < l; i++) {
+        if (keyTable[i] != null) {
+          length--;
+          out.writeInt(internedTypeLocators.indexOf((String) keyTable[i]));
+          ReferenceCollection collection = (ReferenceCollection) valueTable[i];
+          if (collection instanceof AdditionalTypeCollection) {
+            out.writeByte(1);
+            AdditionalTypeCollection atc = (AdditionalTypeCollection) collection;
+            writeNames(atc.definedTypeNames, out);
+          } else {
+            out.writeByte(2);
+          }
+          char[][][] qNames = collection.qualifiedNameReferences;
+          int qLength = qNames.length;
+          out.writeInt(qLength);
+          for (int j = 0; j < qLength; j++)
+            out.writeInt(internedQualifiedNames.indexOf(qNames[j]));
+          char[][] sNames = collection.simpleNameReferences;
+          int sLength = sNames.length;
+          out.writeInt(sLength);
+          for (int j = 0; j < sLength; j++)
+            out.writeInt(internedSimpleNames.indexOf(sNames[j]));
+        }
+      }
+      if (PHPBuilder.DEBUG && length != 0)
+        System.out.println("references table is inconsistent"); //$NON-NLS-1$
+    }
+  }
+
+  private void writeNames(char[][] names, DataOutputStream out) throws IOException {
+    int length = names == null ? 0 : names.length;
+    out.writeInt(length);
+    for (int i = 0; i < length; i++) {
+      char[] name = names[i];
+      int nLength = name.length;
+      out.writeInt(nLength);
+      for (int j = 0; j < nLength; j++)
+        out.writeChar(name[j]);
+    }
+  }
+
+  /**
+   * Returns a string representation of the receiver.
+   */
+  public String toString() {
+    return "State for " + javaProjectName //$NON-NLS-1$
+    +" (#" + buildNumber //$NON-NLS-1$
+    +" @ " + new Date(lastStructuralBuildTime) //$NON-NLS-1$
+    +")"; //$NON-NLS-1$
+  }
+
+  /*
+   * Debug helper void dump() { System.out.println("State for " + javaProjectName + " (" + buildNumber + " @ " + new
+   * Date(lastStructuralBuildTime) + ")"); System.out.println("\tClass path source locations:"); for (int i = 0, l =
+   * sourceLocations.length; i < l; i++) System.out.println("\t\t" + sourceLocations[i]); System.out.println("\tClass path binary
+   * locations:"); for (int i = 0, l = binaryLocations.length; i < l; i++) System.out.println("\t\t" + binaryLocations[i]);
+   * 
+   * System.out.print("\tStructural build numbers table:"); if (structuralBuildTimes.elementSize == 0) { System.out.print(" <empty>
+   * "); } else { Object[] keyTable = structuralBuildTimes.keyTable; Object[] valueTable = structuralBuildTimes.valueTable; for
+   * (int i = 0, l = keyTable.length; i < l; i++) if (keyTable[i] != null) System.out.print("\n\t\t" + keyTable[i].toString() + " -> " +
+   * valueTable[i].toString()); }
+   * 
+   * System.out.print("\tType locators table:"); if (typeLocators.elementSize == 0) { System.out.print(" <empty> "); } else {
+   * Object[] keyTable = typeLocators.keyTable; Object[] valueTable = typeLocators.valueTable; for (int i = 0, l = keyTable.length;
+   * i < l; i++) if (keyTable[i] != null) System.out.print("\n\t\t" + keyTable[i].toString() + " -> " + valueTable[i].toString()); }
+   * 
+   * System.out.print("\n\tReferences table:"); if (references.elementSize == 0) { System.out.print(" <empty> "); } else { Object[]
+   * keyTable = references.keyTable; Object[] valueTable = references.valueTable; for (int i = 0, l = keyTable.length; i
+   * < l; i++) { if (keyTable[i] != null) { System.out.print("\n\t\t" + keyTable[i].toString()); ReferenceCollection c =
+   * (ReferenceCollection) valueTable[i]; char[][][] qRefs = c.qualifiedNameReferences; System.out.print("\n\t\t\tqualified:"); if
+   * (qRefs.length == 0) System.out.print(" <empty> "); else for (int j = 0, m = qRefs.length; j < m; j++) System.out.print(" '" +
+   * CharOperation.toString(qRefs[j]) + "'"); char[][] sRefs = c.simpleNameReferences; System.out.print("\n\t\t\tsimple:"); if
+   * (sRefs.length == 0) System.out.print(" <empty> "); else for (int j = 0, m = sRefs.length; j < m; j++) System.out.print(" " +
+   * new String(sRefs[j])); if (c instanceof AdditionalTypeCollection) { char[][] names = ((AdditionalTypeCollection)
+   * c).definedTypeNames; System.out.print("\n\t\t\tadditional type names:"); for (int j = 0, m = names.length; j < m; j++)
+   * System.out.print(" " + new String(names[j])); } } } } System.out.print("\n\n"); }
+   */
 }