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