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