1) Reintroduced finishedBuilding
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / builder / PHPBuilder.java
index d6ce598..b6eebad 100644 (file)
@@ -9,6 +9,7 @@
  *     IBM Corporation - initial API and implementation
  *******************************************************************************/
 package net.sourceforge.phpdt.internal.core.builder;
+
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
@@ -17,21 +18,21 @@ import java.util.Date;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
+
 import net.sourceforge.phpdt.core.IClasspathEntry;
 import net.sourceforge.phpdt.core.IJavaModelMarker;
-import net.sourceforge.phpdt.core.JavaModelException;
 import net.sourceforge.phpdt.core.JavaCore;
+import net.sourceforge.phpdt.core.JavaModelException;
 import net.sourceforge.phpdt.core.compiler.CharOperation;
 import net.sourceforge.phpdt.internal.core.JavaModel;
 import net.sourceforge.phpdt.internal.core.JavaModelManager;
 import net.sourceforge.phpdt.internal.core.JavaProject;
 import net.sourceforge.phpdt.internal.core.util.SimpleLookupTable;
 import net.sourceforge.phpdt.internal.core.util.Util;
-import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
+import net.sourceforge.phpdt.internal.core.util.PHPFileUtil;
 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
 import net.sourceforge.phpeclipse.builder.IdentifierIndexManager;
-import net.sourceforge.phpeclipse.phpeditor.PHPParserAction;
-import net.sourceforge.phpeclipse.resourcesview.PHPProject;
+
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IMarker;
 import org.eclipse.core.resources.IProject;
@@ -45,652 +46,754 @@ import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.OperationCanceledException;
+
 public class PHPBuilder extends IncrementalProjectBuilder {
-  IProject currentProject;
-  JavaProject javaProject;
-  IWorkspaceRoot workspaceRoot;
-  NameEnvironment nameEnvironment;
-  SimpleLookupTable binaryLocationsPerProject; // maps a project to its binary
-  // resources (output folders,
-  // class folders, zip/jar files)
-  State lastState;
-  BuildNotifier notifier;
-  char[][] extraResourceFileFilters;
-  String[] extraResourceFolderFilters;
-  public static final String CLASS_EXTENSION = "class"; //$NON-NLS-1$
-  public static boolean DEBUG = true;
-  /**
-   * A list of project names that have been built. This list is used to reset
-   * the JavaModel.existingExternalFiles cache when a build cycle begins so
-   * that deleted external jars are discovered.
-   */
-  static ArrayList builtProjects = null;
-  public static IMarker[] getProblemsFor(IResource resource) {
-    try {
-      if (resource != null && resource.exists())
-        return resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
-            false, IResource.DEPTH_INFINITE);
-    } catch (CoreException e) {
-    } // assume there are no problems
-    return new IMarker[0];
-  }
-  public static IMarker[] getTasksFor(IResource resource) {
-    try {
-      if (resource != null && resource.exists())
-        return resource.findMarkers(IJavaModelMarker.TASK_MARKER, false,
-            IResource.DEPTH_INFINITE);
-    } catch (CoreException e) {
-    } // assume there are no tasks
-    return new IMarker[0];
-  }
-  public static void finishedBuilding(IResourceChangeEvent event) {
-    BuildNotifier.resetProblemCounters();
-  }
-  public static void removeProblemsFor(IResource resource) {
-    try {
-      if (resource != null && resource.exists())
-        resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
-            false, IResource.DEPTH_INFINITE);
-    } catch (CoreException e) {
-    } // assume there were no problems
-  }
-  public static void removeTasksFor(IResource resource) {
-    try {
-      if (resource != null && resource.exists())
-        resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false,
-            IResource.DEPTH_INFINITE);
-    } catch (CoreException e) {
-    } // assume there were no problems
-  }
-  public static void removeProblemsAndTasksFor(IResource resource) {
-    try {
-      if (resource != null && resource.exists()) {
-        resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
-            false, IResource.DEPTH_INFINITE);
-        resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false,
-            IResource.DEPTH_INFINITE);
-      }
-    } catch (CoreException e) {
-    } // assume there were no problems
-  }
-  public static State readState(IProject project, DataInputStream in)
-      throws IOException {
-    return State.read(project, in);
-  }
-  public static void writeState(Object state, DataOutputStream out)
-      throws IOException {
-    ((State) state).write(out);
-  }
-  public PHPBuilder() {
-  }
-  protected IProject[] build(int kind, Map ignored, IProgressMonitor monitor)
-      throws CoreException {
-    this.currentProject = getProject();
-    if (currentProject == null || !currentProject.isAccessible())
-      return new IProject[0];
-    if (DEBUG)
-      System.out.println("\nStarting build of " + currentProject.getName() //$NON-NLS-1$
-          + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
-    this.notifier = new BuildNotifier(monitor, currentProject);
-    notifier.begin();
-    boolean ok = false;
-    try {
-      notifier.checkCancel();
-      initializeBuilder();
-      if (isWorthBuilding()) {
-        if (kind == FULL_BUILD) {
-          processFullPHPIndex(currentProject, monitor);
-          buildAll();
-        } else {
-          if ((this.lastState = getLastState(currentProject)) == null) {
-            if (DEBUG)
-              System.out
-                  .println("Performing full build since last saved state was not found"); //$NON-NLS-1$
-            processFullPHPIndex(currentProject, monitor);
-            buildAll();
-//          } else if (hasClasspathChanged()) {
-//            // if the output location changes, do not delete the binary files
-//            // from old location
-//            // the user may be trying something
-//            buildAll();
-          } else if (nameEnvironment.sourceLocations.length > 0) {
-            // if there is no source to compile & no classpath changes then we
-            // are done
-            SimpleLookupTable deltas = findDeltas();
-            if (deltas == null)
-              buildAll();
-            else if (deltas.elementSize > 0)
-              buildDeltas(deltas);
-            else if (DEBUG)
-              System.out.println("Nothing to build since deltas were empty"); //$NON-NLS-1$
-          } else {
-            if (hasStructuralDelta()) { // double check that a jar file didn't
-              // get replaced in a binary project
-              processFullPHPIndex(currentProject, monitor);
-              buildAll();
-            } else {
-              if (DEBUG)
-                System.out
-                    .println("Nothing to build since there are no source folders and no deltas"); //$NON-NLS-1$
-              lastState.tagAsNoopBuild();
-            }
-          }
-        }
-        ok = true;
-      }
-    } catch (CoreException e) {
-      Util.log(e, "JavaBuilder handling CoreException"); //$NON-NLS-1$
-      IMarker marker = currentProject
-          .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
-      marker.setAttribute(IMarker.MESSAGE, Util.bind(
-          "build.inconsistentProject", e.getLocalizedMessage())); //$NON-NLS-1$
-      marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
-    } catch (ImageBuilderInternalException e) {
-      Util.log(e.getThrowable(),
-          "JavaBuilder handling ImageBuilderInternalException"); //$NON-NLS-1$
-      IMarker marker = currentProject
-          .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
-      marker.setAttribute(IMarker.MESSAGE, Util.bind(
-          "build.inconsistentProject", e.coreException.getLocalizedMessage())); //$NON-NLS-1$
-      marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
-    } catch (MissingClassFileException e) {
-      // do not log this exception since its thrown to handle aborted compiles
-      // because of missing class files
-      if (DEBUG)
-        System.out.println(Util.bind("build.incompleteClassPath",
-            e.missingClassFile)); //$NON-NLS-1$
-      IMarker marker = currentProject
-          .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
-      marker.setAttribute(IMarker.MESSAGE, Util.bind(
-          "build.incompleteClassPath", e.missingClassFile)); //$NON-NLS-1$
-      marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
-    } catch (MissingSourceFileException e) {
-      // do not log this exception since its thrown to handle aborted compiles
-      // because of missing source files
-      if (DEBUG)
-        System.out.println(Util.bind("build.missingSourceFile",
-            e.missingSourceFile)); //$NON-NLS-1$
-      removeProblemsAndTasksFor(currentProject); // make this the only problem
-      // for this project
-      IMarker marker = currentProject
-          .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
-      marker.setAttribute(IMarker.MESSAGE, Util.bind("build.missingSourceFile",
-          e.missingSourceFile)); //$NON-NLS-1$
-      marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
-    } finally {
-      if (!ok)
-        // If the build failed, clear the previously built state, forcing a
-        // full build next time.
-        clearLastState();
-      notifier.done();
-      cleanup();
-    }
-    IProject[] requiredProjects = getRequiredProjects(true);
-    if (DEBUG)
-      System.out.println("Finished build of " + currentProject.getName() //$NON-NLS-1$
-          + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
-    return requiredProjects;
-  }
-  /**
-   * Performs a <code>FULL_BUILD</code> by visiting all nodes in the resource
-   * tree under the specified project.
-   * 
-   * @param iProject
-   */
-  private void processFullPHPIndex(final IProject iProject,
-      final IProgressMonitor monitor) {
-    final IdentifierIndexManager indexManager = PHPeclipsePlugin.getDefault()
-        .getIndexManager(iProject);
-    // Create resource visitor logic
-    IResourceVisitor myVisitor = new IResourceVisitor() {
-      public boolean visit(IResource resource) throws CoreException {
-        if (resource.getType() == IResource.FILE) {
-          if (monitor.isCanceled()) {
-            throw new OperationCanceledException();
-          }
-          if ((resource.getFileExtension() != null)
-              && PHPFileUtil.isPHPFile((IFile) resource)) {
-            monitor.worked(1);
-            monitor.subTask("Parsing: " + resource.getFullPath());
-            // update indexfile for the project:
-//            PHPProject nature = (PHPProject) iProject
-//                .getNature(PHPeclipsePlugin.PHP_NATURE_ID);
-            indexManager.addFile((IFile) resource);
-          }
-        }
-        return true;
-      }
-    };
-    // Process the project using the visitor just created
-    try {
-      //      if (iProject.hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) {
-      //        thePHPProject = new PHPProject();
-      //        thePHPProject.setProject(iProject);
-      //      }
-      indexManager.initialize();
-      iProject.accept(myVisitor);
-      indexManager.writeFile();
-    } catch (CoreException e) {
-      e.printStackTrace();
-    }
-  }
-  private void buildAll() {
-    notifier.checkCancel();
-    notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
-    if (DEBUG && lastState != null)
-      System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
-    clearLastState();
-    BatchImageBuilder imageBuilder = new BatchImageBuilder(this);
-    imageBuilder.build();
-    recordNewState(imageBuilder.newState);
-  }
-  private void buildDeltas(SimpleLookupTable deltas) {
-    notifier.checkCancel();
-    notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
-    if (DEBUG && lastState != null)
-      System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
-    clearLastState(); // clear the previously built state so if the build
-    // fails, a full build will occur next time
-    IncrementalImageBuilder imageBuilder = new IncrementalImageBuilder(this);
-    if (imageBuilder.build(deltas))
-      recordNewState(imageBuilder.newState);
-    else {
-      processFullPHPIndex(currentProject, notifier.monitor);
-      buildAll();
-    }
-  }
-  private void cleanup() {
-    this.nameEnvironment = null;
-    this.binaryLocationsPerProject = null;
-    this.lastState = null;
-    this.notifier = null;
-    this.extraResourceFileFilters = null;
-    this.extraResourceFolderFilters = null;
-  }
-  private void clearLastState() {
-    JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject,
-        null);
-  }
-  boolean filterExtraResource(IResource resource) {
-    if (extraResourceFileFilters != null) {
-      char[] name = resource.getName().toCharArray();
-      for (int i = 0, l = extraResourceFileFilters.length; i < l; i++)
-        if (CharOperation.match(extraResourceFileFilters[i], name, true))
-          return true;
-    }
-    if (extraResourceFolderFilters != null) {
-      IPath path = resource.getProjectRelativePath();
-      String pathName = path.toString();
-      int count = path.segmentCount();
-      if (resource.getType() == IResource.FILE)
-        count--;
-      for (int i = 0, l = extraResourceFolderFilters.length; i < l; i++)
-        if (pathName.indexOf(extraResourceFolderFilters[i]) != -1)
-          for (int j = 0; j < count; j++)
-            if (extraResourceFolderFilters[i].equals(path.segment(j)))
-              return true;
-    }
-    return false;
-  }
-  private SimpleLookupTable findDeltas() {
-    notifier.subTask(Util.bind("build.readingDelta", currentProject.getName())); //$NON-NLS-1$
-    IResourceDelta delta = getDelta(currentProject);
-    SimpleLookupTable deltas = new SimpleLookupTable(3);
-    if (delta != null) {
-      if (delta.getKind() != IResourceDelta.NO_CHANGE) {
-        if (DEBUG)
-          System.out.println("Found source delta for: "
-              + currentProject.getName()); //$NON-NLS-1$
-        deltas.put(currentProject, delta);
-      }
-    } else {
-      if (DEBUG)
-        System.out.println("Missing delta for: " + currentProject.getName()); //$NON-NLS-1$
-      notifier.subTask(""); //$NON-NLS-1$
-      return null;
-    }
-    Object[] keyTable = binaryLocationsPerProject.keyTable;
-    Object[] valueTable = binaryLocationsPerProject.valueTable;
-    nextProject : for (int i = 0, l = keyTable.length; i < l; i++) {
-      IProject p = (IProject) keyTable[i];
-      if (p != null && p != currentProject) {
-        State s = getLastState(p);
-        if (!lastState.wasStructurallyChanged(p, s)) { // see if we can skip
-          // its delta
-          if (s.wasNoopBuild())
-            continue nextProject; // project has no source folders and can be
-          // skipped
-          //                           ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[])
-          // valueTable[i];
-          boolean canSkip = true;
-          //                           for (int j = 0, m = classFoldersAndJars.length; j < m; j++) {
-          //                                   if (classFoldersAndJars[j].isOutputFolder())
-          //                                           classFoldersAndJars[j] = null; // can ignore output folder since
-          // project was not structurally changed
-          //                                   else
-          //                                           canSkip = false;
-          //                           }
-          if (canSkip)
-            continue nextProject; // project has no structural changes in its
-          // output folders
-        }
-        notifier.subTask(Util.bind("build.readingDelta", p.getName())); //$NON-NLS-1$
-        delta = getDelta(p);
-        if (delta != null) {
-          if (delta.getKind() != IResourceDelta.NO_CHANGE) {
-            if (DEBUG)
-              System.out.println("Found binary delta for: " + p.getName()); //$NON-NLS-1$
-            deltas.put(p, delta);
-          }
-        } else {
-          if (DEBUG)
-            System.out.println("Missing delta for: " + p.getName()); //$NON-NLS-1$
-          notifier.subTask(""); //$NON-NLS-1$
-          return null;
-        }
-      }
-    }
-    notifier.subTask(""); //$NON-NLS-1$
-    return deltas;
-  }
-  private State getLastState(IProject project) {
-    return (State) JavaModelManager.getJavaModelManager().getLastBuiltState(
-        project, notifier.monitor);
-  }
-  /*
-   * Return the list of projects for which it requires a resource delta. This
-   * builder's project is implicitly included and need not be specified.
-   * Builders must re-specify the list of interesting projects every time they
-   * are run as this is not carried forward beyond the next build. Missing
-   * projects should be specified but will be ignored until they are added to
-   * the workspace.
-   */
-  private IProject[] getRequiredProjects(boolean includeBinaryPrerequisites) {
-    if (javaProject == null || workspaceRoot == null)
-      return new IProject[0];
-    ArrayList projects = new ArrayList();
-    try {
-      IClasspathEntry[] entries = javaProject.getExpandedClasspath(true);
-      for (int i = 0, l = entries.length; i < l; i++) {
-        IClasspathEntry entry = entries[i];
-        IPath path = entry.getPath();
-        IProject p = null;
-        switch (entry.getEntryKind()) {
-          case IClasspathEntry.CPE_PROJECT :
-            p = workspaceRoot.getProject(path.lastSegment()); // missing
-            // projects are
-            // considered too
-            break;
-          case IClasspathEntry.CPE_LIBRARY :
-            if (includeBinaryPrerequisites && path.segmentCount() > 1) {
-              // some binary resources on the class path can come from projects
-              // that are not included in the project references
-              IResource resource = workspaceRoot.findMember(path.segment(0));
-              if (resource instanceof IProject)
-                p = (IProject) resource;
-            }
-        }
-        if (p != null && !projects.contains(p))
-          projects.add(p);
-      }
-    } catch (JavaModelException e) {
-      return new IProject[0];
-    }
-    IProject[] result = new IProject[projects.size()];
-    projects.toArray(result);
-    return result;
-  }
-//  private boolean hasClasspathChanged() {
-//    ClasspathMultiDirectory[] newSourceLocations = nameEnvironment.sourceLocations;
-//    ClasspathMultiDirectory[] oldSourceLocations = lastState.sourceLocations;
-//    int newLength = newSourceLocations.length;
-//    int oldLength = oldSourceLocations.length;
-//    int n, o;
-//    for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
-//      if (newSourceLocations[n].equals(oldSourceLocations[o]))
-//        continue; // checks source & output folders
-//      try {
-//        if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
-//          // new
-//          // empty
-//          // source
-//          // folder
-//          o--;
-//          continue;
-//        }
-//      } catch (CoreException ignore) {
-//      }
-//      if (DEBUG)
-//        System.out.println(newSourceLocations[n] + " != "
-//            + oldSourceLocations[o]); //$NON-NLS-1$
-//      return true;
-//    }
-//    while (n < newLength) {
-//      try {
-//        if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
-//          // new
-//          // empty
-//          // source
-//          // folder
-//          n++;
-//          continue;
-//        }
-//      } catch (CoreException ignore) {
-//      }
-//      if (DEBUG)
-//        System.out.println("Added non-empty source folder"); //$NON-NLS-1$
-//      return true;
-//    }
-//    if (o < oldLength) {
-//      if (DEBUG)
-//        System.out.println("Removed source folder"); //$NON-NLS-1$
-//      return true;
-//    }
-//    //       ClasspathLocation[] newBinaryLocations =
-//    // nameEnvironment.binaryLocations;
-//    //       ClasspathLocation[] oldBinaryLocations = lastState.binaryLocations;
-//    //       newLength = newBinaryLocations.length;
-//    //       oldLength = oldBinaryLocations.length;
-//    //       for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
-//    //               if (newBinaryLocations[n].equals(oldBinaryLocations[o])) continue;
-//    //               if (DEBUG)
-//    //                       System.out.println(newBinaryLocations[n] + " != " +
-//    // oldBinaryLocations[o]); //$NON-NLS-1$
-//    //               return true;
-//    //       }
-//    //       if (n < newLength || o < oldLength) {
-//    //               if (DEBUG)
-//    //                       System.out.println("Number of binary folders/jar files has changed");
-//    // //$NON-NLS-1$
-//    //               return true;
-//    //       }
-//    return false;
-//  }
-  private boolean hasStructuralDelta() {
-    // handle case when currentProject has only .class file folders and/or jar
-    // files... no source/output folders
-    IResourceDelta delta = getDelta(currentProject);
-    if (delta != null && delta.getKind() != IResourceDelta.NO_CHANGE) {
-      //               ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[])
-      // binaryLocationsPerProject.get(currentProject);
-      //               if (classFoldersAndJars != null) {
-      //                       for (int i = 0, l = classFoldersAndJars.length; i < l; i++) {
-      //                               ClasspathLocation classFolderOrJar = classFoldersAndJars[i]; // either
-      // a .class file folder or a zip/jar file
-      //                               if (classFolderOrJar != null) {
-      //                                       IPath p = classFolderOrJar.getProjectRelativePath();
-      //                                       if (p != null) {
-      //                                               IResourceDelta binaryDelta = delta.findMember(p);
-      //                                               if (binaryDelta != null && binaryDelta.getKind() !=
-      // IResourceDelta.NO_CHANGE)
-      //                                                       return true;
-      //                                       }
-      //                               }
-      //                       }
-      //               }
-    }
-    return false;
-  }
-  private void initializeBuilder() throws CoreException {
-    this.javaProject = (JavaProject) JavaCore.create(currentProject);
-    this.workspaceRoot = currentProject.getWorkspace().getRoot();
-    // Flush the existing external files cache if this is the beginning of a
-    // build cycle
-    String projectName = currentProject.getName();
-    if (builtProjects == null || builtProjects.contains(projectName)) {
-      JavaModel.flushExternalFileCache();
-      builtProjects = new ArrayList();
-    }
-    builtProjects.add(projectName);
-    this.binaryLocationsPerProject = new SimpleLookupTable(3);
-    this.nameEnvironment = new NameEnvironment(workspaceRoot, javaProject,
-        binaryLocationsPerProject);
-    String filterSequence = javaProject.getOption(
-        JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, true);
-    char[][] filters = filterSequence != null && filterSequence.length() > 0
-        ? CharOperation.splitAndTrimOn(',', filterSequence.toCharArray())
-        : null;
-    if (filters == null) {
-      this.extraResourceFileFilters = null;
-      this.extraResourceFolderFilters = null;
-    } else {
-      int fileCount = 0, folderCount = 0;
-      for (int i = 0, l = filters.length; i < l; i++) {
-        char[] f = filters[i];
-        if (f.length == 0)
-          continue;
-        if (f[f.length - 1] == '/')
-          folderCount++;
-        else
-          fileCount++;
-      }
-      this.extraResourceFileFilters = new char[fileCount][];
-      this.extraResourceFolderFilters = new String[folderCount];
-      for (int i = 0, l = filters.length; i < l; i++) {
-        char[] f = filters[i];
-        if (f.length == 0)
-          continue;
-        if (f[f.length - 1] == '/')
-          extraResourceFolderFilters[--folderCount] = new String(CharOperation
-              .subarray(f, 0, f.length - 1));
-        else
-          extraResourceFileFilters[--fileCount] = f;
-      }
-    }
-  }
-  private boolean isClasspathBroken(IClasspathEntry[] classpath, IProject p)
-      throws CoreException {
-    if (classpath == JavaProject.INVALID_CLASSPATH) // the .classpath file
-      // could not be read
-      return true;
-    IMarker[] markers = p.findMarkers(
-        IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
-    for (int i = 0, l = markers.length; i < l; i++)
-      if (((Integer) markers[i].getAttribute(IMarker.SEVERITY)).intValue() == IMarker.SEVERITY_ERROR)
-        return true;
-    return false;
-  }
-  private boolean isWorthBuilding() throws CoreException {
-    boolean abortBuilds = JavaCore.ABORT.equals(javaProject.getOption(
-        JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true));
-    if (!abortBuilds)
-      return true;
-    // Abort build only if there are classpath errors
-    //    if (isClasspathBroken(javaProject.getRawClasspath(), currentProject)) {
-    //      if (DEBUG)
-    //        System.out.println("Aborted build because project has classpath errors
-    // (incomplete or involved in cycle)"); //$NON-NLS-1$
-    //
-    //      JavaModelManager.getJavaModelManager().deltaProcessor.addForRefresh(javaProject);
-    //
-    //      removeProblemsAndTasksFor(currentProject); // remove all compilation
-    // problems
-    //
-    //      IMarker marker =
-    // currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
-    //      marker.setAttribute(IMarker.MESSAGE,
-    // Util.bind("build.abortDueToClasspathProblems")); //$NON-NLS-1$
-    //      marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
-    //      return false;
-    //    }
-    // make sure all prereq projects have valid build states... only when
-    // aborting builds since projects in cycles do not have build states
-    // except for projects involved in a 'warning' cycle (see below)
-    IProject[] requiredProjects = getRequiredProjects(false);
-    next : for (int i = 0, l = requiredProjects.length; i < l; i++) {
-      IProject p = requiredProjects[i];
-      if (getLastState(p) == null) {
-        // The prereq project has no build state: if this prereq project has a
-        // 'warning' cycle marker then allow build (see bug id 23357)
-        JavaProject prereq = (JavaProject) JavaCore.create(p);
-        if (prereq.hasCycleMarker()
-            && JavaCore.WARNING.equals(javaProject.getOption(
-                JavaCore.CORE_CIRCULAR_CLASSPATH, true)))
-          continue;
-        if (DEBUG)
-          System.out.println("Aborted build because prereq project "
-              + p.getName() //$NON-NLS-1$
-              + " was not built"); //$NON-NLS-1$
-        removeProblemsAndTasksFor(currentProject); // make this the only
-        // problem for this project
-        IMarker marker = currentProject
-            .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
-        marker.setAttribute(IMarker.MESSAGE, isClasspathBroken(prereq
-            .getRawClasspath(), p) ? Util.bind(
-            "build.prereqProjectHasClasspathProblems", p.getName()) //$NON-NLS-1$
-            : Util.bind("build.prereqProjectMustBeRebuilt", p.getName())); //$NON-NLS-1$
-        marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
-        return false;
-      }
-    }
-    return true;
-  }
-  /*
-   * Instruct the build manager that this project is involved in a cycle and
-   * needs to propagate structural changes to the other projects in the cycle.
-   */
-  void mustPropagateStructuralChanges() {
-    HashSet cycleParticipants = new HashSet(3);
-    javaProject.updateCycleParticipants(null, new ArrayList(),
-        cycleParticipants, workspaceRoot, new HashSet(3));
-    IPath currentPath = javaProject.getPath();
-    Iterator i = cycleParticipants.iterator();
-    while (i.hasNext()) {
-      IPath participantPath = (IPath) i.next();
-      if (participantPath != currentPath) {
-        IProject project = this.workspaceRoot.getProject(participantPath
-            .segment(0));
-        if (hasBeenBuilt(project)) {
-          if (DEBUG)
-            System.out
-                .println("Requesting another build iteration since cycle participant "
-                    + project.getName() //$NON-NLS-1$
-                    + " has not yet seen some structural changes"); //$NON-NLS-1$
-          needRebuild();
-          return;
-        }
-      }
-    }
-  }
-  private void recordNewState(State state) {
-    Object[] keyTable = binaryLocationsPerProject.keyTable;
-    for (int i = 0, l = keyTable.length; i < l; i++) {
-      IProject prereqProject = (IProject) keyTable[i];
-      if (prereqProject != null && prereqProject != currentProject)
-        state.recordStructuralDependency(prereqProject,
-            getLastState(prereqProject));
-    }
-    if (DEBUG)
-      System.out.println("Recording new state : " + state); //$NON-NLS-1$
-    // state.dump();
-    JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject,
-        state);
-  }
-  /**
-   * String representation for debugging purposes
-   */
-  public String toString() {
-    return currentProject == null ? "JavaBuilder for unknown project" //$NON-NLS-1$
-        : "JavaBuilder for " + currentProject.getName(); //$NON-NLS-1$
-  }
+       IProject currentProject;
+
+       JavaProject javaProject;
+
+       IWorkspaceRoot workspaceRoot;
+
+       NameEnvironment nameEnvironment;
+
+       SimpleLookupTable binaryLocationsPerProject; // maps a project to its
+                                                                                                       // binary
+
+       // resources (output folders,
+       // class folders, zip/jar files)
+       State lastState;
+
+       BuildNotifier notifier;
+
+       char[][] extraResourceFileFilters;
+
+       String[] extraResourceFolderFilters;
+
+       public static final String CLASS_EXTENSION = "class"; //$NON-NLS-1$
+
+       public static boolean DEBUG = false;
+
+       /**
+        * A list of project names that have been built. This list is used to reset
+        * the JavaModel.existingExternalFiles cache when a build cycle begins so
+        * that deleted external jars are discovered.
+        */
+       static ArrayList builtProjects = null;
+
+       public static IMarker[] getProblemsFor(IResource resource) {
+               try {
+                       if (resource != null && resource.exists())
+                               return resource.findMarkers(
+                                               IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false,
+                                               IResource.DEPTH_INFINITE);
+               } catch (CoreException e) {
+               } // assume there are no problems
+               return new IMarker[0];
+       }
+
+       public static IMarker[] getTasksFor(IResource resource) {
+               try {
+                       if (resource != null && resource.exists())
+                               return resource.findMarkers(IJavaModelMarker.TASK_MARKER,
+                                               false, IResource.DEPTH_INFINITE);
+               } catch (CoreException e) {
+               } // assume there are no tasks
+               return new IMarker[0];
+       }
+
+       public static void finishedBuilding(IResourceChangeEvent event) {
+               BuildNotifier.resetProblemCounters();
+       }
+
+       /**
+        * Hook allowing to initialize some static state before a complete build
+        * iteration. This hook is invoked during PRE_AUTO_BUILD notification
+        */
+       public static void buildStarting() {
+               // do nothing
+               // TODO (philippe) is it still needed?
+       }
+
+       /**
+        * Hook allowing to reset some static state after a complete build
+        * iteration. This hook is invoked during POST_AUTO_BUILD notification
+        */
+       public static void buildFinished() {
+               BuildNotifier.resetProblemCounters();
+       }
+
+       public static void removeProblemsFor(IResource resource) {
+               try {
+                       if (resource != null && resource.exists())
+                               resource.deleteMarkers(
+                                               IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false,
+                                               IResource.DEPTH_INFINITE);
+               } catch (CoreException e) {
+               } // assume there were no problems
+       }
+
+       public static void removeTasksFor(IResource resource) {
+               try {
+                       if (resource != null && resource.exists())
+                               resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false,
+                                               IResource.DEPTH_INFINITE);
+               } catch (CoreException e) {
+               } // assume there were no problems
+       }
+
+       public static void removeProblemsAndTasksFor(IResource resource) {
+               try {
+                       if (resource != null && resource.exists()) {
+                               resource.deleteMarkers(
+                                               IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false,
+                                               IResource.DEPTH_INFINITE);
+                               resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false,
+                                               IResource.DEPTH_INFINITE);
+                       }
+               } catch (CoreException e) {
+               } // assume there were no problems
+       }
+
+       public static State readState(IProject project, DataInputStream in)
+                       throws IOException {
+               return State.read(project, in);
+       }
+
+       public static void writeState(Object state, DataOutputStream out)
+                       throws IOException {
+               ((State) state).write(out);
+       }
+
+       public PHPBuilder() {
+       }
+
+       protected IProject[] build(int kind, Map ignored, IProgressMonitor monitor)
+                       throws CoreException {
+               this.currentProject = getProject();
+               if (currentProject == null || !currentProject.isAccessible())
+                       return new IProject[0];
+               if (DEBUG)
+                       System.out
+                                       .println("\nStarting build of " + currentProject.getName() //$NON-NLS-1$
+                                                       + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
+               this.notifier = new BuildNotifier(monitor, currentProject);
+               notifier.begin();
+               boolean ok = false;
+               try {
+                       notifier.checkCancel();
+                       initializeBuilder();
+                       if (isWorthBuilding()) {
+                               if (kind == FULL_BUILD) {
+                                       processFullPHPIndex(currentProject, monitor);
+                                       buildAll();
+                               } else {
+                                       if ((this.lastState = getLastState(currentProject)) == null) {
+                                               if (DEBUG)
+                                                       System.out
+                                                                       .println("Performing full build since last saved state was not found"); //$NON-NLS-1$
+                                               processFullPHPIndex(currentProject, monitor);
+                                               buildAll();
+                                               // } else if (hasClasspathChanged()) {
+                                               // // if the output location changes, do not delete the
+                                               // binary files
+                                               // // from old location
+                                               // // the user may be trying something
+                                               // buildAll();
+                                       } else if (nameEnvironment.sourceLocations.length > 0) {
+                                               // if there is no source to compile & no classpath
+                                               // changes then we
+                                               // are done
+                                               SimpleLookupTable deltas = findDeltas();
+                                               if (deltas == null)
+                                                       buildAll();
+                                               else if (deltas.elementSize > 0)
+                                                       buildDeltas(deltas);
+                                               else if (DEBUG)
+                                                       System.out
+                                                                       .println("Nothing to build since deltas were empty"); //$NON-NLS-1$
+                                       } else {
+                                               if (hasStructuralDelta()) { // double check that a jar
+                                                                                                       // file didn't
+                                                       // get replaced in a binary project
+                                                       processFullPHPIndex(currentProject, monitor);
+                                                       buildAll();
+                                               } else {
+                                                       if (DEBUG)
+                                                               System.out
+                                                                               .println("Nothing to build since there are no source folders and no deltas"); //$NON-NLS-1$
+                                                       lastState.tagAsNoopBuild();
+                                               }
+                                       }
+                               }
+                               ok = true;
+                       }
+               } catch (CoreException e) {
+                       Util.log(e, "JavaBuilder handling CoreException"); //$NON-NLS-1$
+                       IMarker marker = currentProject
+                                       .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
+                       marker.setAttribute(IMarker.MESSAGE, Util.bind(
+                                       "build.inconsistentProject", e.getLocalizedMessage())); //$NON-NLS-1$
+                       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
+               } catch (ImageBuilderInternalException e) {
+                       Util.log(e.getThrowable(),
+                                       "JavaBuilder handling ImageBuilderInternalException"); //$NON-NLS-1$
+                       IMarker marker = currentProject
+                                       .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
+                       marker
+                                       .setAttribute(
+                                                       IMarker.MESSAGE,
+                                                       Util
+                                                                       .bind(
+                                                                                       "build.inconsistentProject", e.coreException.getLocalizedMessage())); //$NON-NLS-1$
+                       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
+               } catch (MissingClassFileException e) {
+                       // do not log this exception since its thrown to handle aborted
+                       // compiles
+                       // because of missing class files
+                       if (DEBUG)
+                               System.out.println(Util.bind("build.incompleteClassPath",
+                                               e.missingClassFile)); //$NON-NLS-1$
+                       IMarker marker = currentProject
+                                       .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
+                       marker.setAttribute(IMarker.MESSAGE, Util.bind(
+                                       "build.incompleteClassPath", e.missingClassFile)); //$NON-NLS-1$
+                       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
+               } catch (MissingSourceFileException e) {
+                       // do not log this exception since its thrown to handle aborted
+                       // compiles
+                       // because of missing source files
+                       if (DEBUG)
+                               System.out.println(Util.bind("build.missingSourceFile",
+                                               e.missingSourceFile)); //$NON-NLS-1$
+                       removeProblemsAndTasksFor(currentProject); // make this the only
+                                                                                                               // problem
+                       // for this project
+                       IMarker marker = currentProject
+                                       .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
+                       marker.setAttribute(IMarker.MESSAGE, Util.bind(
+                                       "build.missingSourceFile", e.missingSourceFile)); //$NON-NLS-1$
+                       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
+               } catch (Exception e) {
+                       e.printStackTrace();
+               } finally {
+                       if (!ok)
+                               // If the build failed, clear the previously built state,
+                               // forcing a
+                               // full build next time.
+                               clearLastState();
+                       notifier.done();
+                       cleanup();
+               }
+               IProject[] requiredProjects = getRequiredProjects(true);
+               if (DEBUG)
+                       System.out.println("Finished build of " + currentProject.getName() //$NON-NLS-1$
+                                       + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
+               return requiredProjects;
+       }
+
+       /**
+        * Performs a <code>FULL_BUILD</code> by visiting all nodes in the
+        * resource tree under the specified project.
+        * 
+        * @param iProject
+        */
+       private void processFullPHPIndex(final IProject iProject,
+                       final IProgressMonitor monitor) {
+               final IdentifierIndexManager indexManager = PHPeclipsePlugin
+                               .getDefault().getIndexManager(iProject);
+               // Create resource visitor logic
+               IResourceVisitor myVisitor = new IResourceVisitor() {
+                       public boolean visit(IResource resource) throws CoreException {
+                               if (resource.getType() == IResource.FILE) {
+                                       if (monitor.isCanceled()) {
+                                               throw new OperationCanceledException();
+                                       }
+                                       if ((resource.getFileExtension() != null)
+                                                       && PHPFileUtil.isPHPFile((IFile) resource)) {
+                                               monitor.worked(1);
+                                               monitor.subTask("Parsing: " + resource.getFullPath());
+                                               // update indexfile for the project:
+                                               // PHPProject nature = (PHPProject) iProject
+                                               // .getNature(PHPeclipsePlugin.PHP_NATURE_ID);
+                                               indexManager.addFile((IFile) resource);
+                                       }
+                               }
+                               return true;
+                       }
+               };
+               // Process the project using the visitor just created
+               try {
+                       // if (iProject.hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) {
+                       // thePHPProject = new PHPProject();
+                       // thePHPProject.setProject(iProject);
+                       // }
+                       indexManager.initialize();
+                       iProject.accept(myVisitor);
+                       indexManager.writeFile();
+               } catch (CoreException e) {
+                       e.printStackTrace();
+               }
+       }
+
+       private void buildAll() {
+               notifier.checkCancel();
+               notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
+               if (DEBUG && lastState != null)
+                       System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
+               clearLastState();
+               BatchImageBuilder imageBuilder = new BatchImageBuilder(this);
+               imageBuilder.build();
+               recordNewState(imageBuilder.newState);
+       }
+
+       private void buildDeltas(SimpleLookupTable deltas) {
+               notifier.checkCancel();
+               notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
+               if (DEBUG && lastState != null)
+                       System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
+               clearLastState(); // clear the previously built state so if the build
+               // fails, a full build will occur next time
+               IncrementalImageBuilder imageBuilder = new IncrementalImageBuilder(this);
+               if (imageBuilder.build(deltas))
+                       recordNewState(imageBuilder.newState);
+               else {
+                       processFullPHPIndex(currentProject, notifier.monitor);
+                       buildAll();
+               }
+       }
+
+       private void cleanup() {
+               this.nameEnvironment = null;
+               this.binaryLocationsPerProject = null;
+               this.lastState = null;
+               this.notifier = null;
+               this.extraResourceFileFilters = null;
+               this.extraResourceFolderFilters = null;
+       }
+
+       private void clearLastState() {
+               JavaModelManager.getJavaModelManager().setLastBuiltState(
+                               currentProject, null);
+       }
+
+       boolean filterExtraResource(IResource resource) {
+               if (extraResourceFileFilters != null) {
+                       char[] name = resource.getName().toCharArray();
+                       for (int i = 0, l = extraResourceFileFilters.length; i < l; i++)
+                               if (CharOperation
+                                               .match(extraResourceFileFilters[i], name, true))
+                                       return true;
+               }
+               if (extraResourceFolderFilters != null) {
+                       IPath path = resource.getProjectRelativePath();
+                       String pathName = path.toString();
+                       int count = path.segmentCount();
+                       if (resource.getType() == IResource.FILE)
+                               count--;
+                       for (int i = 0, l = extraResourceFolderFilters.length; i < l; i++)
+                               if (pathName.indexOf(extraResourceFolderFilters[i]) != -1)
+                                       for (int j = 0; j < count; j++)
+                                               if (extraResourceFolderFilters[i].equals(path
+                                                               .segment(j)))
+                                                       return true;
+               }
+               return false;
+       }
+
+       private SimpleLookupTable findDeltas() {
+               notifier.subTask(Util.bind(
+                               "build.readingDelta", currentProject.getName())); //$NON-NLS-1$
+               IResourceDelta delta = getDelta(currentProject);
+               SimpleLookupTable deltas = new SimpleLookupTable(3);
+               if (delta != null) {
+                       if (delta.getKind() != IResourceDelta.NO_CHANGE) {
+                               if (DEBUG)
+                                       System.out.println("Found source delta for: "
+                                                       + currentProject.getName()); //$NON-NLS-1$
+                               deltas.put(currentProject, delta);
+                       }
+               } else {
+                       if (DEBUG)
+                               System.out
+                                               .println("Missing delta for: " + currentProject.getName()); //$NON-NLS-1$
+                       notifier.subTask(""); //$NON-NLS-1$
+                       return null;
+               }
+               Object[] keyTable = binaryLocationsPerProject.keyTable;
+               //Object[] valueTable = binaryLocationsPerProject.valueTable;
+               nextProject: for (int i = 0, l = keyTable.length; i < l; i++) {
+                       IProject p = (IProject) keyTable[i];
+                       if (p != null && p != currentProject) {
+                               State s = getLastState(p);
+                               if (!lastState.wasStructurallyChanged(p, s)) { // see if we can
+                                                                                                                               // skip
+                                       // its delta
+                                       if (s.wasNoopBuild())
+                                               continue nextProject; // project has no source folders
+                                                                                               // and can be
+                                       // skipped
+                                       // ClasspathLocation[] classFoldersAndJars =
+                                       // (ClasspathLocation[])
+                                       // valueTable[i];
+                                       boolean canSkip = true;
+                                       // for (int j = 0, m = classFoldersAndJars.length; j < m;
+                                       // j++) {
+                                       // if (classFoldersAndJars[j].isOutputFolder())
+                                       // classFoldersAndJars[j] = null; // can ignore output
+                                       // folder since
+                                       // project was not structurally changed
+                                       // else
+                                       // canSkip = false;
+                                       // }
+                                       if (canSkip)
+                                               continue nextProject; // project has no structural
+                                                                                               // changes in its
+                                       // output folders
+                               }
+                               notifier.subTask(Util.bind("build.readingDelta", p.getName())); //$NON-NLS-1$
+                               delta = getDelta(p);
+                               if (delta != null) {
+                                       if (delta.getKind() != IResourceDelta.NO_CHANGE) {
+                                               if (DEBUG)
+                                                       System.out
+                                                                       .println("Found binary delta for: " + p.getName()); //$NON-NLS-1$
+                                               deltas.put(p, delta);
+                                       }
+                               } else {
+                                       if (DEBUG)
+                                               System.out.println("Missing delta for: " + p.getName()); //$NON-NLS-1$
+                                       notifier.subTask(""); //$NON-NLS-1$
+                                       return null;
+                               }
+                       }
+               }
+               notifier.subTask(""); //$NON-NLS-1$
+               return deltas;
+       }
+
+       private State getLastState(IProject project) {
+               return (State) JavaModelManager.getJavaModelManager()
+                               .getLastBuiltState(project, notifier.monitor);
+       }
+
+       /*
+        * Return the list of projects for which it requires a resource delta. This
+        * builder's project is implicitly included and need not be specified.
+        * Builders must re-specify the list of interesting projects every time they
+        * are run as this is not carried forward beyond the next build. Missing
+        * projects should be specified but will be ignored until they are added to
+        * the workspace.
+        */
+       private IProject[] getRequiredProjects(boolean includeBinaryPrerequisites) {
+               if (javaProject == null || workspaceRoot == null)
+                       return new IProject[0];
+               ArrayList projects = new ArrayList();
+               try {
+                       IClasspathEntry[] entries = javaProject.getExpandedClasspath(true);
+                       for (int i = 0, l = entries.length; i < l; i++) {
+                               IClasspathEntry entry = entries[i];
+                               IPath path = entry.getPath();
+                               IProject p = null;
+                               switch (entry.getEntryKind()) {
+                               case IClasspathEntry.CPE_PROJECT:
+                                       p = workspaceRoot.getProject(path.lastSegment()); // missing
+                                       // projects are
+                                       // considered too
+                                       break;
+                               case IClasspathEntry.CPE_LIBRARY:
+                                       if (includeBinaryPrerequisites && path.segmentCount() > 1) {
+                                               // some binary resources on the class path can come from
+                                               // projects
+                                               // that are not included in the project references
+                                               IResource resource = workspaceRoot.findMember(path
+                                                               .segment(0));
+                                               if (resource instanceof IProject)
+                                                       p = (IProject) resource;
+                                       }
+                               }
+                               if (p != null && !projects.contains(p))
+                                       projects.add(p);
+                       }
+               } catch (JavaModelException e) {
+                       return new IProject[0];
+               }
+               IProject[] result = new IProject[projects.size()];
+               projects.toArray(result);
+               return result;
+       }
+
+       // private boolean hasClasspathChanged() {
+       // ClasspathMultiDirectory[] newSourceLocations =
+       // nameEnvironment.sourceLocations;
+       // ClasspathMultiDirectory[] oldSourceLocations = lastState.sourceLocations;
+       // int newLength = newSourceLocations.length;
+       // int oldLength = oldSourceLocations.length;
+       // int n, o;
+       // for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
+       // if (newSourceLocations[n].equals(oldSourceLocations[o]))
+       // continue; // checks source & output folders
+       // try {
+       // if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
+       // // new
+       // // empty
+       // // source
+       // // folder
+       // o--;
+       // continue;
+       // }
+       // } catch (CoreException ignore) {
+       // }
+       // if (DEBUG)
+       // System.out.println(newSourceLocations[n] + " != "
+       // + oldSourceLocations[o]); //$NON-NLS-1$
+       // return true;
+       // }
+       // while (n < newLength) {
+       // try {
+       // if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
+       // // new
+       // // empty
+       // // source
+       // // folder
+       // n++;
+       // continue;
+       // }
+       // } catch (CoreException ignore) {
+       // }
+       // if (DEBUG)
+       // System.out.println("Added non-empty source folder"); //$NON-NLS-1$
+       // return true;
+       // }
+       // if (o < oldLength) {
+       // if (DEBUG)
+       // System.out.println("Removed source folder"); //$NON-NLS-1$
+       // return true;
+       // }
+       // // ClasspathLocation[] newBinaryLocations =
+       // // nameEnvironment.binaryLocations;
+       // // ClasspathLocation[] oldBinaryLocations = lastState.binaryLocations;
+       // // newLength = newBinaryLocations.length;
+       // // oldLength = oldBinaryLocations.length;
+       // // for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
+       // // if (newBinaryLocations[n].equals(oldBinaryLocations[o])) continue;
+       // // if (DEBUG)
+       // // System.out.println(newBinaryLocations[n] + " != " +
+       // // oldBinaryLocations[o]); //$NON-NLS-1$
+       // // return true;
+       // // }
+       // // if (n < newLength || o < oldLength) {
+       // // if (DEBUG)
+       // // System.out.println("Number of binary folders/jar files has changed");
+       // // //$NON-NLS-1$
+       // // return true;
+       // // }
+       // return false;
+       // }
+       private boolean hasStructuralDelta() {
+               // handle case when currentProject has only .class file folders and/or
+               // jar
+               // files... no source/output folders
+               IResourceDelta delta = getDelta(currentProject);
+               if (delta != null && delta.getKind() != IResourceDelta.NO_CHANGE) {
+                       // ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[])
+                       // binaryLocationsPerProject.get(currentProject);
+                       // if (classFoldersAndJars != null) {
+                       // for (int i = 0, l = classFoldersAndJars.length; i < l; i++) {
+                       // ClasspathLocation classFolderOrJar = classFoldersAndJars[i]; //
+                       // either
+                       // a .class file folder or a zip/jar file
+                       // if (classFolderOrJar != null) {
+                       // IPath p = classFolderOrJar.getProjectRelativePath();
+                       // if (p != null) {
+                       // IResourceDelta binaryDelta = delta.findMember(p);
+                       // if (binaryDelta != null && binaryDelta.getKind() !=
+                       // IResourceDelta.NO_CHANGE)
+                       // return true;
+                       // }
+                       // }
+                       // }
+                       // }
+               }
+               return false;
+       }
+
+       private void initializeBuilder() throws CoreException {
+               this.javaProject = (JavaProject) JavaCore.create(currentProject);
+               this.workspaceRoot = currentProject.getWorkspace().getRoot();
+               // Flush the existing external files cache if this is the beginning of a
+               // build cycle
+               String projectName = currentProject.getName();
+               if (builtProjects == null || builtProjects.contains(projectName)) {
+                       JavaModel.flushExternalFileCache();
+                       builtProjects = new ArrayList();
+               }
+               builtProjects.add(projectName);
+               this.binaryLocationsPerProject = new SimpleLookupTable(3);
+               this.nameEnvironment = new NameEnvironment(workspaceRoot, javaProject,
+                               binaryLocationsPerProject);
+               String filterSequence = javaProject.getOption(
+                               JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, true);
+               char[][] filters = filterSequence != null
+                               && filterSequence.length() > 0 ? CharOperation.splitAndTrimOn(
+                               ',', filterSequence.toCharArray()) : null;
+               if (filters == null) {
+                       this.extraResourceFileFilters = null;
+                       this.extraResourceFolderFilters = null;
+               } else {
+                       int fileCount = 0, folderCount = 0;
+                       for (int i = 0, l = filters.length; i < l; i++) {
+                               char[] f = filters[i];
+                               if (f.length == 0)
+                                       continue;
+                               if (f[f.length - 1] == '/')
+                                       folderCount++;
+                               else
+                                       fileCount++;
+                       }
+                       this.extraResourceFileFilters = new char[fileCount][];
+                       this.extraResourceFolderFilters = new String[folderCount];
+                       for (int i = 0, l = filters.length; i < l; i++) {
+                               char[] f = filters[i];
+                               if (f.length == 0)
+                                       continue;
+                               if (f[f.length - 1] == '/')
+                                       extraResourceFolderFilters[--folderCount] = new String(
+                                                       CharOperation.subarray(f, 0, f.length - 1));
+                               else
+                                       extraResourceFileFilters[--fileCount] = f;
+                       }
+               }
+       }
+
+       private boolean isClasspathBroken(IClasspathEntry[] classpath, IProject p)
+                       throws CoreException {
+               if (classpath == JavaProject.INVALID_CLASSPATH) // the .classpath file
+                       // could not be read
+                       return true;
+               IMarker[] markers = p.findMarkers(
+                               IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false,
+                               IResource.DEPTH_ZERO);
+               for (int i = 0, l = markers.length; i < l; i++)
+                       if (((Integer) markers[i].getAttribute(IMarker.SEVERITY))
+                                       .intValue() == IMarker.SEVERITY_ERROR)
+                               return true;
+               return false;
+       }
+
+       private boolean isWorthBuilding() throws CoreException {
+               boolean abortBuilds = JavaCore.ABORT.equals(javaProject.getOption(
+                               JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true));
+               if (!abortBuilds)
+                       return true;
+               // Abort build only if there are classpath errors
+               // if (isClasspathBroken(javaProject.getRawClasspath(), currentProject))
+               // {
+               // if (DEBUG)
+               // System.out.println("Aborted build because project has classpath
+               // errors
+               // (incomplete or involved in cycle)"); //$NON-NLS-1$
+               //
+               // JavaModelManager.getJavaModelManager().deltaProcessor.addForRefresh(javaProject);
+               //
+               // removeProblemsAndTasksFor(currentProject); // remove all compilation
+               // problems
+               //
+               // IMarker marker =
+               // currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
+               // marker.setAttribute(IMarker.MESSAGE,
+               // ProjectPrefUtil.bind("build.abortDueToClasspathProblems"));
+               // //$NON-NLS-1$
+               // marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
+               // return false;
+               // }
+               // make sure all prereq projects have valid build states... only when
+               // aborting builds since projects in cycles do not have build states
+               // except for projects involved in a 'warning' cycle (see below)
+               IProject[] requiredProjects = getRequiredProjects(false);
+               next: for (int i = 0, l = requiredProjects.length; i < l; i++) {
+                       IProject p = requiredProjects[i];
+                       if (getLastState(p) == null) {
+                               // The prereq project has no build state: if this prereq project
+                               // has a
+                               // 'warning' cycle marker then allow build (see bug id 23357)
+                               JavaProject prereq = (JavaProject) JavaCore.create(p);
+                               if (prereq.hasCycleMarker()
+                                               && JavaCore.WARNING.equals(javaProject.getOption(
+                                                               JavaCore.CORE_CIRCULAR_CLASSPATH, true)))
+                                       continue;
+                               if (DEBUG)
+                                       System.out.println("Aborted build because prereq project "
+                                                       + p.getName() //$NON-NLS-1$
+                                                       + " was not built"); //$NON-NLS-1$
+                               removeProblemsAndTasksFor(currentProject); // make this the
+                                                                                                                       // only
+                               // problem for this project
+                               IMarker marker = currentProject
+                                               .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
+                               marker
+                                               .setAttribute(
+                                                               IMarker.MESSAGE,
+                                                               isClasspathBroken(prereq.getRawClasspath(), p) ? Util
+                                                                               .bind(
+                                                                                               "build.prereqProjectHasClasspathProblems", p.getName()) //$NON-NLS-1$
+                                                                               : Util
+                                                                                               .bind(
+                                                                                                               "build.prereqProjectMustBeRebuilt", p.getName())); //$NON-NLS-1$
+                               marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
+                               return false;
+                       }
+               }
+               return true;
+       }
+
+       /*
+        * Instruct the build manager that this project is involved in a cycle and
+        * needs to propagate structural changes to the other projects in the cycle.
+        */
+       void mustPropagateStructuralChanges() {
+               HashSet cycleParticipants = new HashSet(3);
+               javaProject.updateCycleParticipants(null, new ArrayList(),
+                               cycleParticipants, workspaceRoot, new HashSet(3));
+               IPath currentPath = javaProject.getPath();
+               Iterator i = cycleParticipants.iterator();
+               while (i.hasNext()) {
+                       IPath participantPath = (IPath) i.next();
+                       if (participantPath != currentPath) {
+                               IProject project = this.workspaceRoot
+                                               .getProject(participantPath.segment(0));
+                               if (hasBeenBuilt(project)) {
+                                       if (DEBUG)
+                                               System.out
+                                                               .println("Requesting another build iteration since cycle participant "
+                                                                               + project.getName() //$NON-NLS-1$
+                                                                               + " has not yet seen some structural changes"); //$NON-NLS-1$
+                                       needRebuild();
+                                       return;
+                               }
+                       }
+               }
+       }
+
+       private void recordNewState(State state) {
+               Object[] keyTable = binaryLocationsPerProject.keyTable;
+               for (int i = 0, l = keyTable.length; i < l; i++) {
+                       IProject prereqProject = (IProject) keyTable[i];
+                       if (prereqProject != null && prereqProject != currentProject)
+                               state.recordStructuralDependency(prereqProject,
+                                               getLastState(prereqProject));
+               }
+               if (DEBUG)
+                       System.out.println("Recording new state : " + state); //$NON-NLS-1$
+               // state.dump();
+               JavaModelManager.getJavaModelManager().setLastBuiltState(
+                               currentProject, state);
+       }
+
+       /**
+        * String representation for debugging purposes
+        */
+       public String toString() {
+               return currentProject == null ? "JavaBuilder for unknown project" //$NON-NLS-1$
+                               : "JavaBuilder for " + currentProject.getName(); //$NON-NLS-1$
+       }
 }