1bd843cb5cd767a72c0eabf82d89eef42c5e859d
[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   /**
91    * Hook allowing to initialize some static state before a complete build iteration.
92    * This hook is invoked during PRE_AUTO_BUILD notification
93    */
94   public static void buildStarting() {
95         // do nothing
96         // TODO (philippe) is it still needed?
97   }
98
99   /**
100    * Hook allowing to reset some static state after a complete build iteration.
101    * This hook is invoked during POST_AUTO_BUILD notification
102    */
103   public static void buildFinished() {
104         BuildNotifier.resetProblemCounters();
105   }
106   public static void removeProblemsFor(IResource resource) {
107     try {
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
113   }
114   public static void removeTasksFor(IResource resource) {
115     try {
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
121   }
122   public static void removeProblemsAndTasksFor(IResource resource) {
123     try {
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);
129       }
130     } catch (CoreException e) {
131     } // assume there were no problems
132   }
133   public static State readState(IProject project, DataInputStream in)
134       throws IOException {
135     return State.read(project, in);
136   }
137   public static void writeState(Object state, DataOutputStream out)
138       throws IOException {
139     ((State) state).write(out);
140   }
141   public PHPBuilder() {
142   }
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];
148     if (DEBUG)
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);
152     notifier.begin();
153     boolean ok = false;
154     try {
155       notifier.checkCancel();
156       initializeBuilder();
157       if (isWorthBuilding()) {
158         if (kind == FULL_BUILD) {
159           processFullPHPIndex(currentProject, monitor);
160           buildAll();
161         } else {
162           if ((this.lastState = getLastState(currentProject)) == null) {
163             if (DEBUG)
164               System.out
165                   .println("Performing full build since last saved state was not found"); //$NON-NLS-1$
166             processFullPHPIndex(currentProject, monitor);
167             buildAll();
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
172 //            buildAll();
173           } else if (nameEnvironment.sourceLocations.length > 0) {
174             // if there is no source to compile & no classpath changes then we
175             // are done
176             SimpleLookupTable deltas = findDeltas();
177             if (deltas == null)
178               buildAll();
179             else if (deltas.elementSize > 0)
180               buildDeltas(deltas);
181             else if (DEBUG)
182               System.out.println("Nothing to build since deltas were empty"); //$NON-NLS-1$
183           } else {
184             if (hasStructuralDelta()) { // double check that a jar file didn't
185               // get replaced in a binary project
186               processFullPHPIndex(currentProject, monitor);
187               buildAll();
188             } else {
189               if (DEBUG)
190                 System.out
191                     .println("Nothing to build since there are no source folders and no deltas"); //$NON-NLS-1$
192               lastState.tagAsNoopBuild();
193             }
194           }
195         }
196         ok = true;
197       }
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
216       if (DEBUG)
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
227       if (DEBUG)
228         System.out.println(Util.bind("build.missingSourceFile",
229             e.missingSourceFile)); //$NON-NLS-1$
230       removeProblemsAndTasksFor(currentProject); // make this the only problem
231       // for this project
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) {
238       e.printStackTrace();
239     } finally {
240       if (!ok)
241         // If the build failed, clear the previously built state, forcing a
242         // full build next time.
243         clearLastState();
244       notifier.done();
245       cleanup();
246     }
247     IProject[] requiredProjects = getRequiredProjects(true);
248     if (DEBUG)
249       System.out.println("Finished build of " + currentProject.getName() //$NON-NLS-1$
250           + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
251     return requiredProjects;
252   }
253   /**
254    * Performs a <code>FULL_BUILD</code> by visiting all nodes in the resource
255    * tree under the specified project.
256    * 
257    * @param iProject
258    */
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();
269           }
270           if ((resource.getFileExtension() != null)
271               && PHPFileUtil.isPHPFile((IFile) resource)) {
272             monitor.worked(1);
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);
278           }
279         }
280         return true;
281       }
282     };
283     // Process the project using the visitor just created
284     try {
285       //      if (iProject.hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) {
286       //        thePHPProject = new PHPProject();
287       //        thePHPProject.setProject(iProject);
288       //      }
289       indexManager.initialize();
290       iProject.accept(myVisitor);
291       indexManager.writeFile();
292     } catch (CoreException e) {
293       e.printStackTrace();
294     }
295   }
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$
301     clearLastState();
302     BatchImageBuilder imageBuilder = new BatchImageBuilder(this);
303     imageBuilder.build();
304     recordNewState(imageBuilder.newState);
305   }
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);
316     else {
317       processFullPHPIndex(currentProject, notifier.monitor);
318       buildAll();
319     }
320   }
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;
328   }
329   private void clearLastState() {
330     JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject,
331         null);
332   }
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))
338           return true;
339     }
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)
345         count--;
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)))
350               return true;
351     }
352     return false;
353   }
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);
358     if (delta != null) {
359       if (delta.getKind() != IResourceDelta.NO_CHANGE) {
360         if (DEBUG)
361           System.out.println("Found source delta for: "
362               + currentProject.getName()); //$NON-NLS-1$
363         deltas.put(currentProject, delta);
364       }
365     } else {
366       if (DEBUG)
367         System.out.println("Missing delta for: " + currentProject.getName()); //$NON-NLS-1$
368       notifier.subTask(""); //$NON-NLS-1$
369       return null;
370     }
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
378           // its delta
379           if (s.wasNoopBuild())
380             continue nextProject; // project has no source folders and can be
381           // skipped
382           //                            ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[])
383           // valueTable[i];
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
389           //                                    else
390           //                                            canSkip = false;
391           //                            }
392           if (canSkip)
393             continue nextProject; // project has no structural changes in its
394           // output folders
395         }
396         notifier.subTask(Util.bind("build.readingDelta", p.getName())); //$NON-NLS-1$
397         delta = getDelta(p);
398         if (delta != null) {
399           if (delta.getKind() != IResourceDelta.NO_CHANGE) {
400             if (DEBUG)
401               System.out.println("Found binary delta for: " + p.getName()); //$NON-NLS-1$
402             deltas.put(p, delta);
403           }
404         } else {
405           if (DEBUG)
406             System.out.println("Missing delta for: " + p.getName()); //$NON-NLS-1$
407           notifier.subTask(""); //$NON-NLS-1$
408           return null;
409         }
410       }
411     }
412     notifier.subTask(""); //$NON-NLS-1$
413     return deltas;
414   }
415   private State getLastState(IProject project) {
416     return (State) JavaModelManager.getJavaModelManager().getLastBuiltState(
417         project, notifier.monitor);
418   }
419   /*
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
425    * the workspace.
426    */
427   private IProject[] getRequiredProjects(boolean includeBinaryPrerequisites) {
428     if (javaProject == null || workspaceRoot == null)
429       return new IProject[0];
430     ArrayList projects = new ArrayList();
431     try {
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();
436         IProject p = null;
437         switch (entry.getEntryKind()) {
438           case IClasspathEntry.CPE_PROJECT :
439             p = workspaceRoot.getProject(path.lastSegment()); // missing
440             // projects are
441             // considered too
442             break;
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;
450             }
451         }
452         if (p != null && !projects.contains(p))
453           projects.add(p);
454       }
455     } catch (JavaModelException e) {
456       return new IProject[0];
457     }
458     IProject[] result = new IProject[projects.size()];
459     projects.toArray(result);
460     return result;
461   }
462 //  private boolean hasClasspathChanged() {
463 //    ClasspathMultiDirectory[] newSourceLocations = nameEnvironment.sourceLocations;
464 //    ClasspathMultiDirectory[] oldSourceLocations = lastState.sourceLocations;
465 //    int newLength = newSourceLocations.length;
466 //    int oldLength = oldSourceLocations.length;
467 //    int n, o;
468 //    for (n = o = 0; n < newLength && o < oldLength; n++, o++) {
469 //      if (newSourceLocations[n].equals(oldSourceLocations[o]))
470 //        continue; // checks source & output folders
471 //      try {
472 //        if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
473 //          // new
474 //          // empty
475 //          // source
476 //          // folder
477 //          o--;
478 //          continue;
479 //        }
480 //      } catch (CoreException ignore) {
481 //      }
482 //      if (DEBUG)
483 //        System.out.println(newSourceLocations[n] + " != "
484 //            + oldSourceLocations[o]); //$NON-NLS-1$
485 //      return true;
486 //    }
487 //    while (n < newLength) {
488 //      try {
489 //        if (newSourceLocations[n].sourceFolder.members().length == 0) { // added
490 //          // new
491 //          // empty
492 //          // source
493 //          // folder
494 //          n++;
495 //          continue;
496 //        }
497 //      } catch (CoreException ignore) {
498 //      }
499 //      if (DEBUG)
500 //        System.out.println("Added non-empty source folder"); //$NON-NLS-1$
501 //      return true;
502 //    }
503 //    if (o < oldLength) {
504 //      if (DEBUG)
505 //        System.out.println("Removed source folder"); //$NON-NLS-1$
506 //      return true;
507 //    }
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;
515 //    //                if (DEBUG)
516 //    //                        System.out.println(newBinaryLocations[n] + " != " +
517 //    // oldBinaryLocations[o]); //$NON-NLS-1$
518 //    //                return true;
519 //    //        }
520 //    //        if (n < newLength || o < oldLength) {
521 //    //                if (DEBUG)
522 //    //                        System.out.println("Number of binary folders/jar files has changed");
523 //    // //$NON-NLS-1$
524 //    //                return true;
525 //    //        }
526 //    return false;
527 //  }
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();
541       //                                        if (p != null) {
542       //                                                IResourceDelta binaryDelta = delta.findMember(p);
543       //                                                if (binaryDelta != null && binaryDelta.getKind() !=
544       // IResourceDelta.NO_CHANGE)
545       //                                                        return true;
546       //                                        }
547       //                                }
548       //                        }
549       //                }
550     }
551     return false;
552   }
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
557     // build cycle
558     String projectName = currentProject.getName();
559     if (builtProjects == null || builtProjects.contains(projectName)) {
560       JavaModel.flushExternalFileCache();
561       builtProjects = new ArrayList();
562     }
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())
571         : null;
572     if (filters == null) {
573       this.extraResourceFileFilters = null;
574       this.extraResourceFolderFilters = null;
575     } else {
576       int fileCount = 0, folderCount = 0;
577       for (int i = 0, l = filters.length; i < l; i++) {
578         char[] f = filters[i];
579         if (f.length == 0)
580           continue;
581         if (f[f.length - 1] == '/')
582           folderCount++;
583         else
584           fileCount++;
585       }
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];
590         if (f.length == 0)
591           continue;
592         if (f[f.length - 1] == '/')
593           extraResourceFolderFilters[--folderCount] = new String(CharOperation
594               .subarray(f, 0, f.length - 1));
595         else
596           extraResourceFileFilters[--fileCount] = f;
597       }
598     }
599   }
600   private boolean isClasspathBroken(IClasspathEntry[] classpath, IProject p)
601       throws CoreException {
602     if (classpath == JavaProject.INVALID_CLASSPATH) // the .classpath file
603       // could not be read
604       return true;
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)
609         return true;
610     return false;
611   }
612   private boolean isWorthBuilding() throws CoreException {
613     boolean abortBuilds = JavaCore.ABORT.equals(javaProject.getOption(
614         JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, true));
615     if (!abortBuilds)
616       return true;
617     // Abort build only if there are classpath errors
618     //    if (isClasspathBroken(javaProject.getRawClasspath(), currentProject)) {
619     //      if (DEBUG)
620     //        System.out.println("Aborted build because project has classpath errors
621     // (incomplete or involved in cycle)"); //$NON-NLS-1$
622     //
623     //      JavaModelManager.getJavaModelManager().deltaProcessor.addForRefresh(javaProject);
624     //
625     //      removeProblemsAndTasksFor(currentProject); // remove all compilation
626     // problems
627     //
628     //      IMarker marker =
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);
633     //      return false;
634     //    }
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)))
648           continue;
649         if (DEBUG)
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);
662         return false;
663       }
664     }
665     return true;
666   }
667   /*
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.
670    */
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
681             .segment(0));
682         if (hasBeenBuilt(project)) {
683           if (DEBUG)
684             System.out
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$
688           needRebuild();
689           return;
690         }
691       }
692     }
693   }
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));
701     }
702     if (DEBUG)
703       System.out.println("Recording new state : " + state); //$NON-NLS-1$
704     // state.dump();
705     JavaModelManager.getJavaModelManager().setLastBuiltState(currentProject,
706         state);
707   }
708   /** 
709    * String representation for debugging purposes 
710    */ 
711   public String toString() { 
712     return currentProject == null ? "JavaBuilder for unknown project" //$NON-NLS-1$
713         : "JavaBuilder for " + currentProject.getName(); //$NON-NLS-1$
714   }
715 }