added first version of codetemplates (needs to fix some bugs)
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / builder / PHPBuilder.java
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
7  * 
8  * Contributors:
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;
19 import java.util.Map;
20
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;
34
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;
48
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)
57   State lastState;
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;
63   /**
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.
67    */
68   static ArrayList builtProjects = null;
69   public static IMarker[] getProblemsFor(IResource resource) {
70     try {
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];
77   }
78   public static IMarker[] getTasksFor(IResource resource) {
79     try {
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];
86   }
87   public static void finishedBuilding(IResourceChangeEvent event) {
88     BuildNotifier.resetProblemCounters();
89   }
90   public static void removeProblemsFor(IResource resource) {
91     try {
92       if (resource != null && resource.exists())
93         resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
94             false, IResource.DEPTH_INFINITE);
95     } catch (CoreException e) {
96     } // assume there were no problems
97   }
98   public static void removeTasksFor(IResource resource) {
99     try {
100       if (resource != null && resource.exists())
101         resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false,
102             IResource.DEPTH_INFINITE);
103     } catch (CoreException e) {
104     } // assume there were no problems
105   }
106   public static void removeProblemsAndTasksFor(IResource resource) {
107     try {
108       if (resource != null && resource.exists()) {
109         resource.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
110             false, IResource.DEPTH_INFINITE);
111         resource.deleteMarkers(IJavaModelMarker.TASK_MARKER, false,
112             IResource.DEPTH_INFINITE);
113       }
114     } catch (CoreException e) {
115     } // assume there were no problems
116   }
117   public static State readState(IProject project, DataInputStream in)
118       throws IOException {
119     return State.read(project, in);
120   }
121   public static void writeState(Object state, DataOutputStream out)
122       throws IOException {
123     ((State) state).write(out);
124   }
125   public PHPBuilder() {
126   }
127   protected IProject[] build(int kind, Map ignored, IProgressMonitor monitor)
128       throws CoreException {
129     this.currentProject = getProject();
130     if (currentProject == null || !currentProject.isAccessible())
131       return new IProject[0];
132     if (DEBUG)
133       System.out.println("\nStarting build of " + currentProject.getName() //$NON-NLS-1$
134           + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
135     this.notifier = new BuildNotifier(monitor, currentProject);
136     notifier.begin();
137     boolean ok = false;
138     try {
139       notifier.checkCancel();
140       initializeBuilder();
141       if (isWorthBuilding()) {
142         if (kind == FULL_BUILD) {
143           processFullPHPIndex(currentProject, monitor);
144           buildAll();
145         } else {
146           if ((this.lastState = getLastState(currentProject)) == null) {
147             if (DEBUG)
148               System.out
149                   .println("Performing full build since last saved state was not found"); //$NON-NLS-1$
150             processFullPHPIndex(currentProject, monitor);
151             buildAll();
152 //          } else if (hasClasspathChanged()) {
153 //            // if the output location changes, do not delete the binary files
154 //            // from old location
155 //            // the user may be trying something
156 //            buildAll();
157           } else if (nameEnvironment.sourceLocations.length > 0) {
158             // if there is no source to compile & no classpath changes then we
159             // are done
160             SimpleLookupTable deltas = findDeltas();
161             if (deltas == null)
162               buildAll();
163             else if (deltas.elementSize > 0)
164               buildDeltas(deltas);
165             else if (DEBUG)
166               System.out.println("Nothing to build since deltas were empty"); //$NON-NLS-1$
167           } else {
168             if (hasStructuralDelta()) { // double check that a jar file didn't
169               // get replaced in a binary project
170               processFullPHPIndex(currentProject, monitor);
171               buildAll();
172             } else {
173               if (DEBUG)
174                 System.out
175                     .println("Nothing to build since there are no source folders and no deltas"); //$NON-NLS-1$
176               lastState.tagAsNoopBuild();
177             }
178           }
179         }
180         ok = true;
181       }
182     } catch (CoreException e) {
183       Util.log(e, "JavaBuilder handling CoreException"); //$NON-NLS-1$
184       IMarker marker = currentProject
185           .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
186       marker.setAttribute(IMarker.MESSAGE, Util.bind(
187           "build.inconsistentProject", e.getLocalizedMessage())); //$NON-NLS-1$
188       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
189     } catch (ImageBuilderInternalException e) {
190       Util.log(e.getThrowable(),
191           "JavaBuilder handling ImageBuilderInternalException"); //$NON-NLS-1$
192       IMarker marker = currentProject
193           .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
194       marker.setAttribute(IMarker.MESSAGE, Util.bind(
195           "build.inconsistentProject", e.coreException.getLocalizedMessage())); //$NON-NLS-1$
196       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
197     } catch (MissingClassFileException e) {
198       // do not log this exception since its thrown to handle aborted compiles
199       // because of missing class files
200       if (DEBUG)
201         System.out.println(Util.bind("build.incompleteClassPath",
202             e.missingClassFile)); //$NON-NLS-1$
203       IMarker marker = currentProject
204           .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
205       marker.setAttribute(IMarker.MESSAGE, Util.bind(
206           "build.incompleteClassPath", e.missingClassFile)); //$NON-NLS-1$
207       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
208     } catch (MissingSourceFileException e) {
209       // do not log this exception since its thrown to handle aborted compiles
210       // because of missing source files
211       if (DEBUG)
212         System.out.println(Util.bind("build.missingSourceFile",
213             e.missingSourceFile)); //$NON-NLS-1$
214       removeProblemsAndTasksFor(currentProject); // make this the only problem
215       // for this project
216       IMarker marker = currentProject
217           .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
218       marker.setAttribute(IMarker.MESSAGE, Util.bind("build.missingSourceFile",
219           e.missingSourceFile)); //$NON-NLS-1$
220       marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
221     } catch (Exception e) {
222       e.printStackTrace();
223     } finally {
224       if (!ok)
225         // If the build failed, clear the previously built state, forcing a
226         // full build next time.
227         clearLastState();
228       notifier.done();
229       cleanup();
230     }
231     IProject[] requiredProjects = getRequiredProjects(true);
232     if (DEBUG)
233       System.out.println("Finished build of " + currentProject.getName() //$NON-NLS-1$
234           + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
235     return requiredProjects;
236   }
237   /**
238    * Performs a <code>FULL_BUILD</code> by visiting all nodes in the resource
239    * tree under the specified project.
240    * 
241    * @param iProject
242    */
243   private void processFullPHPIndex(final IProject iProject,
244       final IProgressMonitor monitor) {
245     final IdentifierIndexManager indexManager = PHPeclipsePlugin.getDefault()
246         .getIndexManager(iProject);
247     // Create resource visitor logic
248     IResourceVisitor myVisitor = new IResourceVisitor() {
249       public boolean visit(IResource resource) throws CoreException {
250         if (resource.getType() == IResource.FILE) {
251           if (monitor.isCanceled()) {
252             throw new OperationCanceledException();
253           }
254           if ((resource.getFileExtension() != null)
255               && PHPFileUtil.isPHPFile((IFile) resource)) {
256             monitor.worked(1);
257             monitor.subTask("Parsing: " + resource.getFullPath());
258             // update indexfile for the project:
259 //            PHPProject nature = (PHPProject) iProject
260 //                .getNature(PHPeclipsePlugin.PHP_NATURE_ID);
261             indexManager.addFile((IFile) resource);
262           }
263         }
264         return true;
265       }
266     };
267     // Process the project using the visitor just created
268     try {
269       //      if (iProject.hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) {
270       //        thePHPProject = new PHPProject();
271       //        thePHPProject.setProject(iProject);
272       //      }
273       indexManager.initialize();
274       iProject.accept(myVisitor);
275       indexManager.writeFile();
276     } catch (CoreException e) {
277       e.printStackTrace();
278     }
279   }
280   private void buildAll() {
281     notifier.checkCancel();
282     notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
283     if (DEBUG && lastState != null)
284       System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
285     clearLastState();
286     BatchImageBuilder imageBuilder = new BatchImageBuilder(this);
287     imageBuilder.build();
288     recordNewState(imageBuilder.newState);
289   }
290   private void buildDeltas(SimpleLookupTable deltas) {
291     notifier.checkCancel();
292     notifier.subTask(Util.bind("build.preparingBuild")); //$NON-NLS-1$
293     if (DEBUG && lastState != null)
294       System.out.println("Clearing last state : " + lastState); //$NON-NLS-1$
295     clearLastState(); // clear the previously built state so if the build
296     // fails, a full build will occur next time
297     IncrementalImageBuilder imageBuilder = new IncrementalImageBuilder(this);
298     if (imageBuilder.build(deltas))
299       recordNewState(imageBuilder.newState);
300     else {
301       processFullPHPIndex(currentProject, notifier.monitor);
302       buildAll();
303     }
304   }
305   private void cleanup() {
306     this.nameEnvironment = null;
307     this.binaryLocationsPerProject = null;
308     this.lastState = null;
309     this.notifier = null;
310     this.extraResourceFileFilters = null;
311     this.extraResourceFolderFilters = null;
312   }
313   private void clearLastState() {
314     JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject,
315         null);
316   }
317   boolean filterExtraResource(IResource resource) {
318     if (extraResourceFileFilters != null) {
319       char[] name = resource.getName().toCharArray();
320       for (int i = 0, l = extraResourceFileFilters.length; i < l; i++)
321         if (CharOperation.match(extraResourceFileFilters[i], name, true))
322           return true;
323     }
324     if (extraResourceFolderFilters != null) {
325       IPath path = resource.getProjectRelativePath();
326       String pathName = path.toString();
327       int count = path.segmentCount();
328       if (resource.getType() == IResource.FILE)
329         count--;
330       for (int i = 0, l = extraResourceFolderFilters.length; i < l; i++)
331         if (pathName.indexOf(extraResourceFolderFilters[i]) != -1)
332           for (int j = 0; j < count; j++)
333             if (extraResourceFolderFilters[i].equals(path.segment(j)))
334               return true;
335     }
336     return false;
337   }
338   private SimpleLookupTable findDeltas() {
339     notifier.subTask(Util.bind("build.readingDelta", currentProject.getName())); //$NON-NLS-1$
340     IResourceDelta delta = getDelta(currentProject);
341     SimpleLookupTable deltas = new SimpleLookupTable(3);
342     if (delta != null) {
343       if (delta.getKind() != IResourceDelta.NO_CHANGE) {
344         if (DEBUG)
345           System.out.println("Found source delta for: "
346               + currentProject.getName()); //$NON-NLS-1$
347         deltas.put(currentProject, delta);
348       }
349     } else {
350       if (DEBUG)
351         System.out.println("Missing delta for: " + currentProject.getName()); //$NON-NLS-1$
352       notifier.subTask(""); //$NON-NLS-1$
353       return null;
354     }
355     Object[] keyTable = binaryLocationsPerProject.keyTable;
356     Object[] valueTable = binaryLocationsPerProject.valueTable;
357     nextProject : for (int i = 0, l = keyTable.length; i < l; i++) {
358       IProject p = (IProject) keyTable[i];
359       if (p != null && p != currentProject) {
360         State s = getLastState(p);
361         if (!lastState.wasStructurallyChanged(p, s)) { // see if we can skip
362           // its delta
363           if (s.wasNoopBuild())
364             continue nextProject; // project has no source folders and can be
365           // skipped
366           //                            ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[])
367           // valueTable[i];
368           boolean canSkip = true;
369           //                            for (int j = 0, m = classFoldersAndJars.length; j < m; j++) {
370           //                                    if (classFoldersAndJars[j].isOutputFolder())
371           //                                            classFoldersAndJars[j] = null; // can ignore output folder since
372           // project was not structurally changed
373           //                                    else
374           //                                            canSkip = false;
375           //                            }
376           if (canSkip)
377             continue nextProject; // project has no structural changes in its
378           // output folders
379         }
380         notifier.subTask(Util.bind("build.readingDelta", p.getName())); //$NON-NLS-1$
381         delta = getDelta(p);
382         if (delta != null) {
383           if (delta.getKind() != IResourceDelta.NO_CHANGE) {
384             if (DEBUG)
385               System.out.println("Found binary delta for: " + p.getName()); //$NON-NLS-1$
386             deltas.put(p, delta);
387           }
388         } else {
389           if (DEBUG)
390             System.out.println("Missing delta for: " + p.getName()); //$NON-NLS-1$
391           notifier.subTask(""); //$NON-NLS-1$
392           return null;
393         }
394       }
395     }
396     notifier.subTask(""); //$NON-NLS-1$
397     return deltas;
398   }
399   private State getLastState(IProject project) {
400     return (State) JavaModelManager.getJavaModelManager().getLastBuiltState(
401         project, notifier.monitor);
402   }
403   /*
404    * Return the list of projects for which it requires a resource delta. This
405    * builder's project is implicitly included and need not be specified.
406    * Builders must re-specify the list of interesting projects every time they
407    * are run as this is not carried forward beyond the next build. Missing
408    * projects should be specified but will be ignored until they are added to
409    * the workspace.
410    */
411   private IProject[] getRequiredProjects(boolean includeBinaryPrerequisites) {
412     if (javaProject == null || workspaceRoot == null)
413       return new IProject[0];
414     ArrayList projects = new ArrayList();
415     try {
416       IClasspathEntry[] entries = javaProject.getExpandedClasspath(true);
417       for (int i = 0, l = entries.length; i < l; i++) {
418         IClasspathEntry entry = entries[i];
419         IPath path = entry.getPath();
420         IProject p = null;
421         switch (entry.getEntryKind()) {
422           case IClasspathEntry.CPE_PROJECT :
423             p = workspaceRoot.getProject(path.lastSegment()); // missing
424             // projects are
425             // considered too
426             break;
427           case IClasspathEntry.CPE_LIBRARY :
428             if (includeBinaryPrerequisites && path.segmentCount() > 1) {
429               // some binary resources on the class path can come from projects
430               // that are not included in the project references
431               IResource resource = workspaceRoot.findMember(path.segment(0));
432               if (resource instanceof IProject)
433                 p = (IProject) resource;
434             }
435         }
436         if (p != null && !projects.contains(p))
437           projects.add(p);
438       }
439     } catch (JavaModelException e) {
440       return new IProject[0];
441     }
442     IProject[] result = new IProject[projects.size()];
443     projects.toArray(result);
444     return result;
445   }
446 //  private boolean hasClasspathChanged() {
447 //    ClasspathMultiDirectory[] newSourceLocations = nameEnvironment.sourceLocations;
448 //    ClasspathMultiDirectory[] oldSourceLocations = lastState.sourceLocations;
449 //    int newLength = newSourceLocations.length;
450 //    int oldLength = oldSourceLocations.length;
451 //    int n, o;
452 //    for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
453 //      if (newSourceLocations[n].equals(oldSourceLocations[o]))
454 //        continue; // checks source & output folders
455 //      try {
456 //        if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
457 //          // new
458 //          // empty
459 //          // source
460 //          // folder
461 //          o--;
462 //          continue;
463 //        }
464 //      } catch (CoreException ignore) {
465 //      }
466 //      if (DEBUG)
467 //        System.out.println(newSourceLocations[n] + " != "
468 //            + oldSourceLocations[o]); //$NON-NLS-1$
469 //      return true;
470 //    }
471 //    while (n < newLength) {
472 //      try {
473 //        if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
474 //          // new
475 //          // empty
476 //          // source
477 //          // folder
478 //          n++;
479 //          continue;
480 //        }
481 //      } catch (CoreException ignore) {
482 //      }
483 //      if (DEBUG)
484 //        System.out.println("Added non-empty source folder"); //$NON-NLS-1$
485 //      return true;
486 //    }
487 //    if (o < oldLength) {
488 //      if (DEBUG)
489 //        System.out.println("Removed source folder"); //$NON-NLS-1$
490 //      return true;
491 //    }
492 //    //        ClasspathLocation[] newBinaryLocations =
493 //    // nameEnvironment.binaryLocations;
494 //    //        ClasspathLocation[] oldBinaryLocations = lastState.binaryLocations;
495 //    //        newLength = newBinaryLocations.length;
496 //    //        oldLength = oldBinaryLocations.length;
497 //    //        for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
498 //    //                if (newBinaryLocations[n].equals(oldBinaryLocations[o])) continue;
499 //    //                if (DEBUG)
500 //    //                        System.out.println(newBinaryLocations[n] + " != " +
501 //    // oldBinaryLocations[o]); //$NON-NLS-1$
502 //    //                return true;
503 //    //        }
504 //    //        if (n < newLength || o < oldLength) {
505 //    //                if (DEBUG)
506 //    //                        System.out.println("Number of binary folders/jar files has changed");
507 //    // //$NON-NLS-1$
508 //    //                return true;
509 //    //        }
510 //    return false;
511 //  }
512   private boolean hasStructuralDelta() {
513     // handle case when currentProject has only .class file folders and/or jar
514     // files... no source/output folders
515     IResourceDelta delta = getDelta(currentProject);
516     if (delta != null && delta.getKind() != IResourceDelta.NO_CHANGE) {
517       //                ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[])
518       // binaryLocationsPerProject.get(currentProject);
519       //                if (classFoldersAndJars != null) {
520       //                        for (int i = 0, l = classFoldersAndJars.length; i < l; i++) {
521       //                                ClasspathLocation classFolderOrJar = classFoldersAndJars[i]; // either
522       // a .class file folder or a zip/jar file
523       //                                if (classFolderOrJar != null) {
524       //                                        IPath p = classFolderOrJar.getProjectRelativePath();
525       //                                        if (p != null) {
526       //                                                IResourceDelta binaryDelta = delta.findMember(p);
527       //                                                if (binaryDelta != null && binaryDelta.getKind() !=
528       // IResourceDelta.NO_CHANGE)
529       //                                                        return true;
530       //                                        }
531       //                                }
532       //                        }
533       //                }
534     }
535     return false;
536   }
537   private void initializeBuilder() throws CoreException {
538     this.javaProject = (JavaProject) JavaCore.create(currentProject);
539     this.workspaceRoot = currentProject.getWorkspace().getRoot();
540     // Flush the existing external files cache if this is the beginning of a
541     // build cycle
542     String projectName = currentProject.getName();
543     if (builtProjects == null || builtProjects.contains(projectName)) {
544       JavaModel.flushExternalFileCache();
545       builtProjects = new ArrayList();
546     }
547     builtProjects.add(projectName);
548     this.binaryLocationsPerProject = new SimpleLookupTable(3);
549     this.nameEnvironment = new NameEnvironment(workspaceRoot, javaProject,
550         binaryLocationsPerProject);
551     String filterSequence = javaProject.getOption(
552         JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, true);
553     char[][] filters = filterSequence != null && filterSequence.length() > 0
554         ? CharOperation.splitAndTrimOn(',', filterSequence.toCharArray())
555         : null;
556     if (filters == null) {
557       this.extraResourceFileFilters = null;
558       this.extraResourceFolderFilters = null;
559     } else {
560       int fileCount = 0, folderCount = 0;
561       for (int i = 0, l = filters.length; i < l; i++) {
562         char[] f = filters[i];
563         if (f.length == 0)
564           continue;
565         if (f[f.length - 1] == '/')
566           folderCount++;
567         else
568           fileCount++;
569       }
570       this.extraResourceFileFilters = new char[fileCount][];
571       this.extraResourceFolderFilters = new String[folderCount];
572       for (int i = 0, l = filters.length; i < l; i++) {
573         char[] f = filters[i];
574         if (f.length == 0)
575           continue;
576         if (f[f.length - 1] == '/')
577           extraResourceFolderFilters[--folderCount] = new String(CharOperation
578               .subarray(f, 0, f.length - 1));
579         else
580           extraResourceFileFilters[--fileCount] = f;
581       }
582     }
583   }
584   private boolean isClasspathBroken(IClasspathEntry[] classpath, IProject p)
585       throws CoreException {
586     if (classpath == JavaProject.INVALID_CLASSPATH) // the .classpath file
587       // could not be read
588       return true;
589     IMarker[] markers = p.findMarkers(
590         IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
591     for (int i = 0, l = markers.length; i < l; i++)
592       if (((Integer) markers[i].getAttribute(IMarker.SEVERITY)).intValue() == IMarker.SEVERITY_ERROR)
593         return true;
594     return false;
595   }
596   private boolean isWorthBuilding() throws CoreException {
597     boolean abortBuilds = JavaCore.ABORT.equals(javaProject.getOption(
598         JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true));
599     if (!abortBuilds)
600       return true;
601     // Abort build only if there are classpath errors
602     //    if (isClasspathBroken(javaProject.getRawClasspath(), currentProject)) {
603     //      if (DEBUG)
604     //        System.out.println("Aborted build because project has classpath errors
605     // (incomplete or involved in cycle)"); //$NON-NLS-1$
606     //
607     //      JavaModelManager.getJavaModelManager().deltaProcessor.addForRefresh(javaProject);
608     //
609     //      removeProblemsAndTasksFor(currentProject); // remove all compilation
610     // problems
611     //
612     //      IMarker marker =
613     // currentProject.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
614     //      marker.setAttribute(IMarker.MESSAGE,
615     // Util.bind("build.abortDueToClasspathProblems")); //$NON-NLS-1$
616     //      marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
617     //      return false;
618     //    }
619     // make sure all prereq projects have valid build states... only when
620     // aborting builds since projects in cycles do not have build states
621     // except for projects involved in a 'warning' cycle (see below)
622     IProject[] requiredProjects = getRequiredProjects(false);
623     next : for (int i = 0, l = requiredProjects.length; i < l; i++) {
624       IProject p = requiredProjects[i];
625       if (getLastState(p) == null) {
626         // The prereq project has no build state: if this prereq project has a
627         // 'warning' cycle marker then allow build (see bug id 23357)
628         JavaProject prereq = (JavaProject) JavaCore.create(p);
629         if (prereq.hasCycleMarker()
630             && JavaCore.WARNING.equals(javaProject.getOption(
631                 JavaCore.CORE_CIRCULAR_CLASSPATH, true)))
632           continue;
633         if (DEBUG)
634           System.out.println("Aborted build because prereq project "
635               + p.getName() //$NON-NLS-1$
636               + " was not built"); //$NON-NLS-1$
637         removeProblemsAndTasksFor(currentProject); // make this the only
638         // problem for this project
639         IMarker marker = currentProject
640             .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
641         marker.setAttribute(IMarker.MESSAGE, isClasspathBroken(prereq
642             .getRawClasspath(), p) ? Util.bind(
643             "build.prereqProjectHasClasspathProblems", p.getName()) //$NON-NLS-1$
644             : Util.bind("build.prereqProjectMustBeRebuilt", p.getName())); //$NON-NLS-1$
645         marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
646         return false;
647       }
648     }
649     return true;
650   }
651   /*
652    * Instruct the build manager that this project is involved in a cycle and
653    * needs to propagate structural changes to the other projects in the cycle.
654    */
655   void mustPropagateStructuralChanges() {
656     HashSet cycleParticipants = new HashSet(3);
657     javaProject.updateCycleParticipants(null, new ArrayList(),
658         cycleParticipants, workspaceRoot, new HashSet(3));
659     IPath currentPath = javaProject.getPath();
660     Iterator i = cycleParticipants.iterator();
661     while (i.hasNext()) {
662       IPath participantPath = (IPath) i.next();
663       if (participantPath != currentPath) {
664         IProject project = this.workspaceRoot.getProject(participantPath
665             .segment(0));
666         if (hasBeenBuilt(project)) {
667           if (DEBUG)
668             System.out
669                 .println("Requesting another build iteration since cycle participant "
670                     + project.getName() //$NON-NLS-1$
671                     + " has not yet seen some structural changes"); //$NON-NLS-1$
672           needRebuild();
673           return;
674         }
675       }
676     }
677   }
678   private void recordNewState(State state) {
679     Object[] keyTable = binaryLocationsPerProject.keyTable;
680     for (int i = 0, l = keyTable.length; i < l; i++) {
681       IProject prereqProject = (IProject) keyTable[i];
682       if (prereqProject != null && prereqProject != currentProject)
683         state.recordStructuralDependency(prereqProject,
684             getLastState(prereqProject));
685     }
686     if (DEBUG)
687       System.out.println("Recording new state : " + state); //$NON-NLS-1$
688     // state.dump();
689     JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject,
690         state);
691   }
692   /** 
693    * String representation for debugging purposes 
694    */ 
695   public String toString() { 
696     return currentProject == null ? "JavaBuilder for unknown project" //$NON-NLS-1$
697         : "JavaBuilder for " + currentProject.getName(); //$NON-NLS-1$
698   }
699 }