1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2003 IBM Corporation and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v1.0
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v10.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  *******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.core.builder;
 
  12 import java.io.DataInputStream;
 
  13 import java.io.DataOutputStream;
 
  14 import java.io.IOException;
 
  15 import java.util.ArrayList;
 
  16 import java.util.Date;
 
  17 import java.util.HashSet;
 
  18 import java.util.Iterator;
 
  21 import net.sourceforge.phpdt.core.IClasspathEntry;
 
  22 import net.sourceforge.phpdt.core.IJavaModelMarker;
 
  23 import net.sourceforge.phpdt.core.JavaCore;
 
  24 import net.sourceforge.phpdt.core.JavaModelException;
 
  25 import net.sourceforge.phpdt.core.compiler.CharOperation;
 
  26 import net.sourceforge.phpdt.internal.core.JavaModel;
 
  27 import net.sourceforge.phpdt.internal.core.JavaModelManager;
 
  28 import net.sourceforge.phpdt.internal.core.JavaProject;
 
  29 import net.sourceforge.phpdt.internal.core.util.SimpleLookupTable;
 
  30 import net.sourceforge.phpdt.internal.core.util.Util;
 
  31 import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
 
  32 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
 
  33 import net.sourceforge.phpeclipse.builder.IdentifierIndexManager;
 
  35 import org.eclipse.core.resources.IFile;
 
  36 import org.eclipse.core.resources.IMarker;
 
  37 import org.eclipse.core.resources.IProject;
 
  38 import org.eclipse.core.resources.IResource;
 
  39 import org.eclipse.core.resources.IResourceChangeEvent;
 
  40 import org.eclipse.core.resources.IResourceDelta;
 
  41 import org.eclipse.core.resources.IResourceVisitor;
 
  42 import org.eclipse.core.resources.IWorkspaceRoot;
 
  43 import org.eclipse.core.resources.IncrementalProjectBuilder;
 
  44 import org.eclipse.core.runtime.CoreException;
 
  45 import org.eclipse.core.runtime.IPath;
 
  46 import org.eclipse.core.runtime.IProgressMonitor;
 
  47 import org.eclipse.core.runtime.OperationCanceledException;
 
  49 public class PHPBuilder extends IncrementalProjectBuilder {
 
  50   IProject currentProject;
 
  51   JavaProject javaProject;
 
  52   IWorkspaceRoot workspaceRoot;
 
  53   NameEnvironment nameEnvironment; 
 
  54   SimpleLookupTable binaryLocationsPerProject; // maps a project to its binary
 
  55   // resources (output folders,
 
  56   // class folders, zip/jar files)
 
  58   BuildNotifier notifier;
 
  59   char[][] extraResourceFileFilters;
 
  60   String[] extraResourceFolderFilters;
 
  61   public static final String CLASS_EXTENSION = "class"; //$NON-NLS-1$
 
  62   public static boolean DEBUG = false;
 
  64    * A list of project names that have been built. This list is used to reset
 
  65    * the JavaModel.existingExternalFiles cache when a build cycle begins so
 
  66    * that deleted external jars are discovered.
 
  68   static ArrayList builtProjects = null;
 
  69   public static IMarker[] getProblemsFor(IResource resource) {
 
  71       if (resource != null && resource.exists())
 
  72         return resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
 
  73             false, IResource.DEPTH_INFINITE);
 
  74     } catch (CoreException e) {
 
  75     } // assume there are no problems
 
  76     return new IMarker[0];
 
  78   public static IMarker[] getTasksFor(IResource resource) {
 
  80       if (resource != null && resource.exists())
 
  81         return resource.findMarkers(IJavaModelMarker.TASK_MARKER, false,
 
  82             IResource.DEPTH_INFINITE);
 
  83     } catch (CoreException e) {
 
  84     } // assume there are no tasks
 
  85     return new IMarker[0];
 
  87   public static void finishedBuilding(IResourceChangeEvent event) {
 
  88     BuildNotifier.resetProblemCounters();
 
  91    * Hook allowing to initialize some static state before a complete build iteration.
 
  92    * This hook is invoked during PRE_AUTO_BUILD notification
 
  94   public static void buildStarting() {
 
  96         // TODO (philippe) is it still needed?
 
 100    * Hook allowing to reset some static state after a complete build iteration.
 
 101    * This hook is invoked during POST_AUTO_BUILD notification
 
 103   public static void buildFinished() {
 
 104         BuildNotifier.resetProblemCounters();
 
 106   public static void removeProblemsFor(IResource resource) {
 
 108       if (resource != null && resource.exists())
 
 109         resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
 
 110             false, IResource.DEPTH_INFINITE);
 
 111     } catch (CoreException e) {
 
 112     } // assume there were no problems
 
 114   public static void removeTasksFor(IResource resource) {
 
 116       if (resource != null && resource.exists())
 
 117         resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false,
 
 118             IResource.DEPTH_INFINITE);
 
 119     } catch (CoreException e) {
 
 120     } // assume there were no problems
 
 122   public static void removeProblemsAndTasksFor(IResource resource) {
 
 124       if (resource != null && resource.exists()) {
 
 125         resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
 
 126             false, IResource.DEPTH_INFINITE);
 
 127         resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false,
 
 128             IResource.DEPTH_INFINITE);
 
 130     } catch (CoreException e) {
 
 131     } // assume there were no problems
 
 133   public static State readState(IProject project, DataInputStream in)
 
 135     return State.read(project, in);
 
 137   public static void writeState(Object state, DataOutputStream out)
 
 139     ((State) state).write(out);
 
 141   public PHPBuilder() {
 
 143   protected IProject[] build(int kind, Map ignored, IProgressMonitor monitor)
 
 144       throws CoreException {
 
 145     this.currentProject = getProject();
 
 146     if (currentProject == null || !currentProject.isAccessible())
 
 147       return new IProject[0];
 
 149       System.out.println("\nStarting build of " + currentProject.getName() //$NON-NLS-1$
 
 150           + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
 
 151     this.notifier = new BuildNotifier(monitor, currentProject);
 
 155       notifier.checkCancel();
 
 157       if (isWorthBuilding()) {
 
 158         if (kind == FULL_BUILD) {
 
 159           processFullPHPIndex(currentProject, monitor);
 
 162           if ((this.lastState = getLastState(currentProject)) == null) {
 
 165                   .println("Performing full build since last saved state was not found"); //$NON-NLS-1$
 
 166             processFullPHPIndex(currentProject, monitor);
 
 168 //          } else if (hasClasspathChanged()) {
 
 169 //            // if the output location changes, do not delete the binary files
 
 170 //            // from old location
 
 171 //            // the user may be trying something
 
 173           } else if (nameEnvironment.sourceLocations.length > 0) {
 
 174             // if there is no source to compile & no classpath changes then we
 
 176             SimpleLookupTable deltas = findDeltas();
 
 179             else if (deltas.elementSize > 0)
 
 182               System.out.println("Nothing to build since deltas were empty"); //$NON-NLS-1$
 
 184             if (hasStructuralDelta()) { // double check that a jar file didn't
 
 185               // get replaced in a binary project
 
 186               processFullPHPIndex(currentProject, monitor);
 
 191                     .println("Nothing to build since there are no source folders and no deltas"); //$NON-NLS-1$
 
 192               lastState.tagAsNoopBuild();
 
 198     } catch (CoreException e) {
 
 199       Util.log(e, "JavaBuilder handling CoreException"); //$NON-NLS-1$
 
 200       IMarker marker = currentProject
 
 201           .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
 
 202       marker.setAttribute(IMarker.MESSAGE, Util.bind(
 
 203           "build.inconsistentProject", e.getLocalizedMessage())); //$NON-NLS-1$
 
 204       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
 
 205     } catch (ImageBuilderInternalException e) {
 
 206       Util.log(e.getThrowable(),
 
 207           "JavaBuilder handling ImageBuilderInternalException"); //$NON-NLS-1$
 
 208       IMarker marker = currentProject
 
 209           .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
 
 210       marker.setAttribute(IMarker.MESSAGE, Util.bind(
 
 211           "build.inconsistentProject", e.coreException.getLocalizedMessage())); //$NON-NLS-1$
 
 212       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
 
 213     } catch (MissingClassFileException e) {
 
 214       // do not log this exception since its thrown to handle aborted compiles
 
 215       // because of missing class files
 
 217         System.out.println(Util.bind("build.incompleteClassPath",
 
 218             e.missingClassFile)); //$NON-NLS-1$
 
 219       IMarker marker = currentProject
 
 220           .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
 
 221       marker.setAttribute(IMarker.MESSAGE, Util.bind(
 
 222           "build.incompleteClassPath", e.missingClassFile)); //$NON-NLS-1$
 
 223       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
 
 224     } catch (MissingSourceFileException e) {
 
 225       // do not log this exception since its thrown to handle aborted compiles
 
 226       // because of missing source files
 
 228         System.out.println(Util.bind("build.missingSourceFile",
 
 229             e.missingSourceFile)); //$NON-NLS-1$
 
 230       removeProblemsAndTasksFor(currentProject); // make this the only problem
 
 232       IMarker marker = currentProject
 
 233           .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
 
 234       marker.setAttribute(IMarker.MESSAGE, Util.bind("build.missingSourceFile",
 
 235           e.missingSourceFile)); //$NON-NLS-1$
 
 236       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
 
 237     } catch (Exception e) {
 
 241         // If the build failed, clear the previously built state, forcing a
 
 242         // full build next time.
 
 247     IProject[] requiredProjects = getRequiredProjects(true);
 
 249       System.out.println("Finished build of " + currentProject.getName() //$NON-NLS-1$
 
 250           + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
 
 251     return requiredProjects;
 
 254    * Performs a <code>FULL_BUILD</code> by visiting all nodes in the resource
 
 255    * tree under the specified project.
 
 259   private void processFullPHPIndex(final IProject iProject,
 
 260       final IProgressMonitor monitor) {
 
 261     final IdentifierIndexManager indexManager = PHPeclipsePlugin.getDefault()
 
 262         .getIndexManager(iProject);
 
 263     // Create resource visitor logic
 
 264     IResourceVisitor myVisitor = new IResourceVisitor() {
 
 265       public boolean visit(IResource resource) throws CoreException {
 
 266         if (resource.getType() == IResource.FILE) {
 
 267           if (monitor.isCanceled()) {
 
 268             throw new OperationCanceledException();
 
 270           if ((resource.getFileExtension() != null)
 
 271               && PHPFileUtil.isPHPFile((IFile) resource)) {
 
 273             monitor.subTask("Parsing: " + resource.getFullPath());
 
 274             // update indexfile for the project:
 
 275 //            PHPProject nature = (PHPProject) iProject
 
 276 //                .getNature(PHPeclipsePlugin.PHP_NATURE_ID);
 
 277             indexManager.addFile((IFile) resource);
 
 283     // Process the project using the visitor just created
 
 285       //      if (iProject.hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) {
 
 286       //        thePHPProject = new PHPProject();
 
 287       //        thePHPProject.setProject(iProject);
 
 289       indexManager.initialize();
 
 290       iProject.accept(myVisitor);
 
 291       indexManager.writeFile();
 
 292     } catch (CoreException e) {
 
 296   private void buildAll() {
 
 297     notifier.checkCancel();
 
 298     notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
 
 299     if (DEBUG && lastState != null)
 
 300       System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
 
 302     BatchImageBuilder imageBuilder = new BatchImageBuilder(this);
 
 303     imageBuilder.build();
 
 304     recordNewState(imageBuilder.newState);
 
 306   private void buildDeltas(SimpleLookupTable deltas) {
 
 307     notifier.checkCancel();
 
 308     notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
 
 309     if (DEBUG && lastState != null)
 
 310       System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
 
 311     clearLastState(); // clear the previously built state so if the build
 
 312     // fails, a full build will occur next time
 
 313     IncrementalImageBuilder imageBuilder = new IncrementalImageBuilder(this);
 
 314     if (imageBuilder.build(deltas))
 
 315       recordNewState(imageBuilder.newState);
 
 317       processFullPHPIndex(currentProject, notifier.monitor);
 
 321   private void cleanup() {
 
 322     this.nameEnvironment = null;
 
 323     this.binaryLocationsPerProject = null;
 
 324     this.lastState = null;
 
 325     this.notifier = null;
 
 326     this.extraResourceFileFilters = null;
 
 327     this.extraResourceFolderFilters = null;
 
 329   private void clearLastState() {
 
 330     JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject,
 
 333   boolean filterExtraResource(IResource resource) {
 
 334     if (extraResourceFileFilters != null) {
 
 335       char[] name = resource.getName().toCharArray();
 
 336       for (int i = 0, l = extraResourceFileFilters.length; i < l; i++)
 
 337         if (CharOperation.match(extraResourceFileFilters[i], name, true))
 
 340     if (extraResourceFolderFilters != null) {
 
 341       IPath path = resource.getProjectRelativePath();
 
 342       String pathName = path.toString();
 
 343       int count = path.segmentCount();
 
 344       if (resource.getType() == IResource.FILE)
 
 346       for (int i = 0, l = extraResourceFolderFilters.length; i < l; i++)
 
 347         if (pathName.indexOf(extraResourceFolderFilters[i]) != -1)
 
 348           for (int j = 0; j < count; j++)
 
 349             if (extraResourceFolderFilters[i].equals(path.segment(j)))
 
 354   private SimpleLookupTable findDeltas() {
 
 355     notifier.subTask(Util.bind("build.readingDelta", currentProject.getName())); //$NON-NLS-1$
 
 356     IResourceDelta delta = getDelta(currentProject);
 
 357     SimpleLookupTable deltas = new SimpleLookupTable(3);
 
 359       if (delta.getKind() != IResourceDelta.NO_CHANGE) {
 
 361           System.out.println("Found source delta for: "
 
 362               + currentProject.getName()); //$NON-NLS-1$
 
 363         deltas.put(currentProject, delta);
 
 367         System.out.println("Missing delta for: " + currentProject.getName()); //$NON-NLS-1$
 
 368       notifier.subTask(""); //$NON-NLS-1$
 
 371     Object[] keyTable = binaryLocationsPerProject.keyTable;
 
 372     Object[] valueTable = binaryLocationsPerProject.valueTable;
 
 373     nextProject : for (int i = 0, l = keyTable.length; i < l; i++) {
 
 374       IProject p = (IProject) keyTable[i];
 
 375       if (p != null && p != currentProject) {
 
 376         State s = getLastState(p);
 
 377         if (!lastState.wasStructurallyChanged(p, s)) { // see if we can skip
 
 379           if (s.wasNoopBuild())
 
 380             continue nextProject; // project has no source folders and can be
 
 382           //                            ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[])
 
 384           boolean canSkip = true;
 
 385           //                            for (int j = 0, m = classFoldersAndJars.length; j < m; j++) {
 
 386           //                                    if (classFoldersAndJars[j].isOutputFolder())
 
 387           //                                            classFoldersAndJars[j] = null; // can ignore output folder since
 
 388           // project was not structurally changed
 
 393             continue nextProject; // project has no structural changes in its
 
 396         notifier.subTask(Util.bind("build.readingDelta", p.getName())); //$NON-NLS-1$
 
 399           if (delta.getKind() != IResourceDelta.NO_CHANGE) {
 
 401               System.out.println("Found binary delta for: " + p.getName()); //$NON-NLS-1$
 
 402             deltas.put(p, delta);
 
 406             System.out.println("Missing delta for: " + p.getName()); //$NON-NLS-1$
 
 407           notifier.subTask(""); //$NON-NLS-1$
 
 412     notifier.subTask(""); //$NON-NLS-1$
 
 415   private State getLastState(IProject project) {
 
 416     return (State) JavaModelManager.getJavaModelManager().getLastBuiltState(
 
 417         project, notifier.monitor);
 
 420    * Return the list of projects for which it requires a resource delta. This
 
 421    * builder's project is implicitly included and need not be specified.
 
 422    * Builders must re-specify the list of interesting projects every time they
 
 423    * are run as this is not carried forward beyond the next build. Missing
 
 424    * projects should be specified but will be ignored until they are added to
 
 427   private IProject[] getRequiredProjects(boolean includeBinaryPrerequisites) {
 
 428     if (javaProject == null || workspaceRoot == null)
 
 429       return new IProject[0];
 
 430     ArrayList projects = new ArrayList();
 
 432       IClasspathEntry[] entries = javaProject.getExpandedClasspath(true);
 
 433       for (int i = 0, l = entries.length; i < l; i++) {
 
 434         IClasspathEntry entry = entries[i];
 
 435         IPath path = entry.getPath();
 
 437         switch (entry.getEntryKind()) {
 
 438           case IClasspathEntry.CPE_PROJECT :
 
 439             p = workspaceRoot.getProject(path.lastSegment()); // missing
 
 443           case IClasspathEntry.CPE_LIBRARY :
 
 444             if (includeBinaryPrerequisites && path.segmentCount() > 1) {
 
 445               // some binary resources on the class path can come from projects
 
 446               // that are not included in the project references
 
 447               IResource resource = workspaceRoot.findMember(path.segment(0));
 
 448               if (resource instanceof IProject)
 
 449                 p = (IProject) resource;
 
 452         if (p != null && !projects.contains(p))
 
 455     } catch (JavaModelException e) {
 
 456       return new IProject[0];
 
 458     IProject[] result = new IProject[projects.size()];
 
 459     projects.toArray(result);
 
 462 //  private boolean hasClasspathChanged() {
 
 463 //    ClasspathMultiDirectory[] newSourceLocations = nameEnvironment.sourceLocations;
 
 464 //    ClasspathMultiDirectory[] oldSourceLocations = lastState.sourceLocations;
 
 465 //    int newLength = newSourceLocations.length;
 
 466 //    int oldLength = oldSourceLocations.length;
 
 468 //    for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
 
 469 //      if (newSourceLocations[n].equals(oldSourceLocations[o]))
 
 470 //        continue; // checks source & output folders
 
 472 //        if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
 
 480 //      } catch (CoreException ignore) {
 
 483 //        System.out.println(newSourceLocations[n] + " != "
 
 484 //            + oldSourceLocations[o]); //$NON-NLS-1$
 
 487 //    while (n < newLength) {
 
 489 //        if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
 
 497 //      } catch (CoreException ignore) {
 
 500 //        System.out.println("Added non-empty source folder"); //$NON-NLS-1$
 
 503 //    if (o < oldLength) {
 
 505 //        System.out.println("Removed source folder"); //$NON-NLS-1$
 
 508 //    //        ClasspathLocation[] newBinaryLocations =
 
 509 //    // nameEnvironment.binaryLocations;
 
 510 //    //        ClasspathLocation[] oldBinaryLocations = lastState.binaryLocations;
 
 511 //    //        newLength = newBinaryLocations.length;
 
 512 //    //        oldLength = oldBinaryLocations.length;
 
 513 //    //        for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
 
 514 //    //                if (newBinaryLocations[n].equals(oldBinaryLocations[o])) continue;
 
 516 //    //                        System.out.println(newBinaryLocations[n] + " != " +
 
 517 //    // oldBinaryLocations[o]); //$NON-NLS-1$
 
 520 //    //        if (n < newLength || o < oldLength) {
 
 522 //    //                        System.out.println("Number of binary folders/jar files has changed");
 
 528   private boolean hasStructuralDelta() {
 
 529     // handle case when currentProject has only .class file folders and/or jar
 
 530     // files... no source/output folders
 
 531     IResourceDelta delta = getDelta(currentProject);
 
 532     if (delta != null && delta.getKind() != IResourceDelta.NO_CHANGE) {
 
 533       //                ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[])
 
 534       // binaryLocationsPerProject.get(currentProject);
 
 535       //                if (classFoldersAndJars != null) {
 
 536       //                        for (int i = 0, l = classFoldersAndJars.length; i < l; i++) {
 
 537       //                                ClasspathLocation classFolderOrJar = classFoldersAndJars[i]; // either
 
 538       // a .class file folder or a zip/jar file
 
 539       //                                if (classFolderOrJar != null) {
 
 540       //                                        IPath p = classFolderOrJar.getProjectRelativePath();
 
 542       //                                                IResourceDelta binaryDelta = delta.findMember(p);
 
 543       //                                                if (binaryDelta != null && binaryDelta.getKind() !=
 
 544       // IResourceDelta.NO_CHANGE)
 
 553   private void initializeBuilder() throws CoreException {
 
 554     this.javaProject = (JavaProject) JavaCore.create(currentProject);
 
 555     this.workspaceRoot = currentProject.getWorkspace().getRoot();
 
 556     // Flush the existing external files cache if this is the beginning of a
 
 558     String projectName = currentProject.getName();
 
 559     if (builtProjects == null || builtProjects.contains(projectName)) {
 
 560       JavaModel.flushExternalFileCache();
 
 561       builtProjects = new ArrayList();
 
 563     builtProjects.add(projectName);
 
 564     this.binaryLocationsPerProject = new SimpleLookupTable(3);
 
 565     this.nameEnvironment = new NameEnvironment(workspaceRoot, javaProject,
 
 566         binaryLocationsPerProject);
 
 567     String filterSequence = javaProject.getOption(
 
 568         JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, true);
 
 569     char[][] filters = filterSequence != null && filterSequence.length() > 0
 
 570         ? CharOperation.splitAndTrimOn(',', filterSequence.toCharArray())
 
 572     if (filters == null) {
 
 573       this.extraResourceFileFilters = null;
 
 574       this.extraResourceFolderFilters = null;
 
 576       int fileCount = 0, folderCount = 0;
 
 577       for (int i = 0, l = filters.length; i < l; i++) {
 
 578         char[] f = filters[i];
 
 581         if (f[f.length - 1] == '/')
 
 586       this.extraResourceFileFilters = new char[fileCount][];
 
 587       this.extraResourceFolderFilters = new String[folderCount];
 
 588       for (int i = 0, l = filters.length; i < l; i++) {
 
 589         char[] f = filters[i];
 
 592         if (f[f.length - 1] == '/')
 
 593           extraResourceFolderFilters[--folderCount] = new String(CharOperation
 
 594               .subarray(f, 0, f.length - 1));
 
 596           extraResourceFileFilters[--fileCount] = f;
 
 600   private boolean isClasspathBroken(IClasspathEntry[] classpath, IProject p)
 
 601       throws CoreException {
 
 602     if (classpath == JavaProject.INVALID_CLASSPATH) // the .classpath file
 
 605     IMarker[] markers = p.findMarkers(
 
 606         IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
 
 607     for (int i = 0, l = markers.length; i < l; i++)
 
 608       if (((Integer) markers[i].getAttribute(IMarker.SEVERITY)).intValue() == IMarker.SEVERITY_ERROR)
 
 612   private boolean isWorthBuilding() throws CoreException {
 
 613     boolean abortBuilds = JavaCore.ABORT.equals(javaProject.getOption(
 
 614         JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true));
 
 617     // Abort build only if there are classpath errors
 
 618     //    if (isClasspathBroken(javaProject.getRawClasspath(), currentProject)) {
 
 620     //        System.out.println("Aborted build because project has classpath errors
 
 621     // (incomplete or involved in cycle)"); //$NON-NLS-1$
 
 623     //      JavaModelManager.getJavaModelManager().deltaProcessor.addForRefresh(javaProject);
 
 625     //      removeProblemsAndTasksFor(currentProject); // remove all compilation
 
 629     // currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
 
 630     //      marker.setAttribute(IMarker.MESSAGE,
 
 631     // Util.bind("build.abortDueToClasspathProblems")); //$NON-NLS-1$
 
 632     //      marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
 
 635     // make sure all prereq projects have valid build states... only when
 
 636     // aborting builds since projects in cycles do not have build states
 
 637     // except for projects involved in a 'warning' cycle (see below)
 
 638     IProject[] requiredProjects = getRequiredProjects(false);
 
 639     next : for (int i = 0, l = requiredProjects.length; i < l; i++) {
 
 640       IProject p = requiredProjects[i];
 
 641       if (getLastState(p) == null) {
 
 642         // The prereq project has no build state: if this prereq project has a
 
 643         // 'warning' cycle marker then allow build (see bug id 23357)
 
 644         JavaProject prereq = (JavaProject) JavaCore.create(p);
 
 645         if (prereq.hasCycleMarker()
 
 646             && JavaCore.WARNING.equals(javaProject.getOption(
 
 647                 JavaCore.CORE_CIRCULAR_CLASSPATH, true)))
 
 650           System.out.println("Aborted build because prereq project "
 
 651               + p.getName() //$NON-NLS-1$
 
 652               + " was not built"); //$NON-NLS-1$
 
 653         removeProblemsAndTasksFor(currentProject); // make this the only
 
 654         // problem for this project
 
 655         IMarker marker = currentProject
 
 656             .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
 
 657         marker.setAttribute(IMarker.MESSAGE, isClasspathBroken(prereq
 
 658             .getRawClasspath(), p) ? Util.bind(
 
 659             "build.prereqProjectHasClasspathProblems", p.getName()) //$NON-NLS-1$
 
 660             : Util.bind("build.prereqProjectMustBeRebuilt", p.getName())); //$NON-NLS-1$
 
 661         marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
 
 668    * Instruct the build manager that this project is involved in a cycle and
 
 669    * needs to propagate structural changes to the other projects in the cycle.
 
 671   void mustPropagateStructuralChanges() {
 
 672     HashSet cycleParticipants = new HashSet(3);
 
 673     javaProject.updateCycleParticipants(null, new ArrayList(),
 
 674         cycleParticipants, workspaceRoot, new HashSet(3));
 
 675     IPath currentPath = javaProject.getPath();
 
 676     Iterator i = cycleParticipants.iterator();
 
 677     while (i.hasNext()) {
 
 678       IPath participantPath = (IPath) i.next();
 
 679       if (participantPath != currentPath) {
 
 680         IProject project = this.workspaceRoot.getProject(participantPath
 
 682         if (hasBeenBuilt(project)) {
 
 685                 .println("Requesting another build iteration since cycle participant "
 
 686                     + project.getName() //$NON-NLS-1$
 
 687                     + " has not yet seen some structural changes"); //$NON-NLS-1$
 
 694   private void recordNewState(State state) {
 
 695     Object[] keyTable = binaryLocationsPerProject.keyTable;
 
 696     for (int i = 0, l = keyTable.length; i < l; i++) {
 
 697       IProject prereqProject = (IProject) keyTable[i];
 
 698       if (prereqProject != null && prereqProject != currentProject)
 
 699         state.recordStructuralDependency(prereqProject,
 
 700             getLastState(prereqProject));
 
 703       System.out.println("Recording new state : " + state); //$NON-NLS-1$
 
 705     JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject,
 
 709    * String representation for debugging purposes 
 
 711   public String toString() { 
 
 712     return currentProject == null ? "JavaBuilder for unknown project" //$NON-NLS-1$
 
 713         : "JavaBuilder for " + currentProject.getName(); //$NON-NLS-1$