91e2b85484fb5a3f43ce0d11da7d75eb324663ff
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / builder / IncrementalImageBuilder.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.util.ArrayList;
13 import net.sourceforge.phpdt.core.compiler.CharOperation;
14 import net.sourceforge.phpdt.core.compiler.IProblem;
15 import net.sourceforge.phpdt.internal.compiler.CompilationResult;
16 import net.sourceforge.phpdt.internal.core.Util;
17 import net.sourceforge.phpdt.internal.core.util.SimpleLookupTable;
18 import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
19 import net.sourceforge.phpeclipse.PHPeclipsePlugin;
20 import net.sourceforge.phpeclipse.builder.IdentifierIndexManager;
21 import org.eclipse.core.resources.IContainer;
22 import org.eclipse.core.resources.IFile;
23 import org.eclipse.core.resources.IFolder;
24 import org.eclipse.core.resources.IMarker;
25 import org.eclipse.core.resources.IResource;
26 import org.eclipse.core.resources.IResourceDelta;
27 import org.eclipse.core.runtime.CoreException;
28 import org.eclipse.core.runtime.IPath;
29 /**
30  * The incremental image builder
31  */
32 public class IncrementalImageBuilder extends AbstractImageBuilder {
33   protected ArrayList sourceFiles;
34   protected ArrayList previousSourceFiles;
35   protected ArrayList qualifiedStrings;
36   protected ArrayList simpleStrings;
37   protected SimpleLookupTable secondaryTypesToRemove;
38   protected boolean hasStructuralChanges;
39   protected int compileLoop;
40   public static int MaxCompileLoop = 5; // perform a full build if it takes
41   // more than ? incremental compile
42   // loops
43   protected IncrementalImageBuilder(PHPBuilder javaBuilder) {
44     super(javaBuilder);
45     this.nameEnvironment.isIncrementalBuild = true;
46     this.newState.copyFrom(javaBuilder.lastState);
47     this.sourceFiles = new ArrayList(33);
48     this.previousSourceFiles = null;
49     this.qualifiedStrings = new ArrayList(33);
50     this.simpleStrings = new ArrayList(33);
51     this.hasStructuralChanges = false;
52     this.compileLoop = 0;
53   }
54   public boolean build(SimpleLookupTable deltas) {
55     // initialize builder
56     // walk this project's deltas, find changed source files
57     // walk prereq projects' deltas, find changed class files & add affected
58     // source files
59     //   use the build state # to skip the deltas for certain prereq projects
60     //   ignore changed zip/jar files since they caused a full build
61     // compile the source files & acceptResult()
62     // compare the produced class files against the existing ones on disk
63     // recompile all dependent source files of any type with structural changes
64     // or new/removed secondary type
65     // keep a loop counter to abort & perform a full build
66     if (PHPBuilder.DEBUG)
67       System.out.println("INCREMENTAL build"); //$NON-NLS-1$
68     try {
69       resetCollections();
70       notifier.subTask(Util.bind("build.analyzingDeltas")); //$NON-NLS-1$
71       IResourceDelta sourceDelta = (IResourceDelta) deltas
72           .get(javaBuilder.currentProject);
73       if (sourceDelta != null)
74         if (!findSourceFiles(sourceDelta))
75           return false;
76       notifier.updateProgressDelta(0.10f);
77       Object[] keyTable = deltas.keyTable;
78       Object[] valueTable = deltas.valueTable;
79       final IdentifierIndexManager indexManager = PHPeclipsePlugin.getDefault()
80           .getIndexManager(javaBuilder.currentProject);
81       for (int i = 0, l = valueTable.length; i < l; i++) {
82         IResourceDelta delta = (IResourceDelta) valueTable[i];
83         if (delta != null) {
84           IResource resource = delta.getResource();
85           int resourceType = resource.getType();
86           if (resourceType == IResource.FILE) {
87             switch (delta.getKind()) {
88               case IResourceDelta.ADDED :
89                 if ((resource.getFileExtension() != null)
90                     && PHPFileUtil.isPHPFile((IFile) resource)) {
91                   // update indexfile for the project:
92                   indexManager.addFile((IFile) resource);
93                 }
94                 break;
95               case IResourceDelta.CHANGED :
96                 if ((resource.getFileExtension() != null)
97                     && PHPFileUtil.isPHPFile((IFile) resource)) {
98                   // update indexfile for the project:
99                   indexManager.changeFile((IFile) resource);
100                 }
101                 break;
102               case IResourceDelta.REMOVED :
103                 if ((resource.getFileExtension() != null)
104                     && PHPFileUtil.isPHPFile((IFile) resource)) {
105                   // update indexfile for the project:
106                   indexManager.removeFile((IFile) resource);
107                 }
108                 break;
109             }
110           }
111           //                            ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[])
112           // javaBuilder.binaryLocationsPerProject.get(keyTable[i]);
113           //                            if (classFoldersAndJars != null)
114           //                                    if (!findAffectedSourceFiles(delta, classFoldersAndJars)) return
115           // false;
116         }
117       }
118       notifier.updateProgressDelta(0.10f);
119       notifier.subTask(Util.bind("build.analyzingSources")); //$NON-NLS-1$
120       addAffectedSourceFiles();
121       notifier.updateProgressDelta(0.05f);
122       this.compileLoop = 0;
123       float increment = 0.40f;
124       while (sourceFiles.size() > 0) { // added to in acceptResult
125         if (++this.compileLoop > MaxCompileLoop) {
126           if (PHPBuilder.DEBUG)
127             System.out
128                 .println("ABORTING incremental build... exceeded loop count"); //$NON-NLS-1$
129           return false;
130         }
131         notifier.checkCancel();
132         SourceFile[] allSourceFiles = new SourceFile[sourceFiles.size()];
133         sourceFiles.toArray(allSourceFiles);
134         resetCollections();
135         workQueue.addAll(allSourceFiles);
136         notifier.setProgressPerCompilationUnit(increment
137             / allSourceFiles.length);
138         increment = increment / 2;
139         compile(allSourceFiles);
140 //        removeSecondaryTypes();
141         addAffectedSourceFiles();
142       }
143       if (this.hasStructuralChanges && javaBuilder.javaProject.hasCycleMarker())
144         javaBuilder.mustPropagateStructuralChanges();
145     } catch (AbortIncrementalBuildException e) {
146       // abort the incremental build and let the batch builder handle the
147       // problem
148       if (PHPBuilder.DEBUG)
149         System.out.println("ABORTING incremental build... cannot find "
150             + e.qualifiedTypeName + //$NON-NLS-1$
151             ". Could have been renamed inside its existing source file."); //$NON-NLS-1$
152       return false;
153     } catch (CoreException e) {
154       throw internalException(e);
155     } finally {
156       cleanUp();
157     }
158     return true;
159   }
160   protected void addAffectedSourceFiles() {
161     if (qualifiedStrings.isEmpty() && simpleStrings.isEmpty())
162       return;
163     // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are
164     // just 'X'
165 //    char[][][] qualifiedNames = ReferenceCollection
166 //        .internQualifiedNames(qualifiedStrings);
167     // if a well known qualified name was found then we can skip over these
168 //    if (qualifiedNames.length < qualifiedStrings.size())
169 //      qualifiedNames = null;
170 //    char[][] simpleNames = ReferenceCollection.internSimpleNames(simpleStrings);
171     // if a well known name was found then we can skip over these
172 //    if (simpleNames.length < simpleStrings.size())
173 //      simpleNames = null;
174 //    Object[] keyTable = newState.references.keyTable;
175 //    Object[] valueTable = newState.references.valueTable;
176 //    next : for (int i = 0, l = valueTable.length; i < l; i++) {
177 //      ReferenceCollection refs = (ReferenceCollection) valueTable[i];
178 //      if (refs != null && refs.includes(qualifiedNames, simpleNames)) {
179 //        String typeLocator = (String) keyTable[i];
180 //        IFile file = javaBuilder.currentProject.getFile(typeLocator);
181 //        if (file.exists()) {
182 //          ClasspathMultiDirectory md = sourceLocations[0];
183 //          if (sourceLocations.length > 1) {
184 //            IPath sourceFileFullPath = file.getFullPath();
185 //            for (int j = 0, m = sourceLocations.length; j < m; j++) {
186 //              if (sourceLocations[j].sourceFolder.getFullPath().isPrefixOf(
187 //                  sourceFileFullPath)) {
188 //                md = sourceLocations[j];
189 //                if (md.exclusionPatterns == null
190 //                    || !Util.isExcluded(file, md.exclusionPatterns))
191 //                  break;
192 //              }
193 //            }
194 //          }
195 //          SourceFile sourceFile = new SourceFile(file, md, encoding);
196 //          if (sourceFiles.contains(sourceFile))
197 //            continue next;
198 //          if (compiledAllAtOnce && previousSourceFiles != null
199 //              && previousSourceFiles.contains(sourceFile))
200 //            continue next; // can skip previously compiled files since already
201 //          // saw hierarchy related problems
202 //          if (PHPBuilder.DEBUG)
203 //            System.out.println("  adding affected source file " + typeLocator); //$NON-NLS-1$
204 //          //                          // update indexfile for the project:
205 //          sourceFiles.add(sourceFile);
206 //        }
207 //      }
208 //    }
209   }
210   protected void addDependentsOf(IPath path, boolean hasStructuralChanges) {
211     if (hasStructuralChanges) {
212       newState.tagAsStructurallyChanged();
213       this.hasStructuralChanges = true;
214     }
215     // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are
216     // just 'X'
217     path = path.setDevice(null);
218     String packageName = path.removeLastSegments(1).toString();
219     if (!qualifiedStrings.contains(packageName))
220       qualifiedStrings.add(packageName);
221     String typeName = path.lastSegment();
222     int memberIndex = typeName.indexOf('$');
223     if (memberIndex > 0)
224       typeName = typeName.substring(0, memberIndex);
225     if (!simpleStrings.contains(typeName)) {
226       if (PHPBuilder.DEBUG)
227         System.out.println("  will look for dependents of " //$NON-NLS-1$
228             + typeName + " in " + packageName); //$NON-NLS-1$
229       simpleStrings.add(typeName);
230     }
231   }
232   protected void cleanUp() {
233     super.cleanUp();
234     this.sourceFiles = null;
235     this.previousSourceFiles = null;
236     this.qualifiedStrings = null;
237     this.simpleStrings = null;
238     this.secondaryTypesToRemove = null;
239     this.hasStructuralChanges = false;
240     this.compileLoop = 0;
241   }
242   //protected boolean findAffectedSourceFiles(IResourceDelta delta,
243   // ClasspathLocation[] classFoldersAndJars) {
244   //    for (int i = 0, l = classFoldersAndJars.length; i < l; i++) {
245   //            ClasspathLocation bLocation = classFoldersAndJars[i];
246   //            // either a .class file folder or a zip/jar file
247   //            if (bLocation != null) { // skip unchanged output folder
248   //                    IPath p = bLocation.getProjectRelativePath();
249   //                    if (p != null) {
250   //                            IResourceDelta binaryDelta = delta.findMember(p);
251   //                            if (binaryDelta != null) {
252   //                                    if (bLocation instanceof ClasspathJar) {
253   //                                            if (JavaBuilder.DEBUG)
254   //                                                    System.out.println("ABORTING incremental build... found delta to jar/zip
255   // file"); //$NON-NLS-1$
256   //                                            return false; // do full build since jar file was changed (added/removed
257   // were caught as classpath change)
258   //                                    }
259   //                                    if (binaryDelta.getKind() == IResourceDelta.ADDED || binaryDelta.getKind()
260   // == IResourceDelta.REMOVED) {
261   //                                            if (JavaBuilder.DEBUG)
262   //                                                    System.out.println("ABORTING incremental build... found added/removed
263   // binary folder"); //$NON-NLS-1$
264   //                                            return false; // added/removed binary folder should not make it here
265   // (classpath change), but handle anyways
266   //                                    }
267   //                                    int segmentCount = binaryDelta.getFullPath().segmentCount();
268   //                                    IResourceDelta[] children = binaryDelta.getAffectedChildren(); // .class
269   // files from class folder
270   //                                    for (int j = 0, m = children.length; j < m; j++)
271   //                                            findAffectedSourceFiles(children[j], segmentCount);
272   //                                    notifier.checkCancel();
273   //                            }
274   //                    }
275   //            }
276   //    }
277   //    return true;
278   //}
279   protected void findAffectedSourceFiles(IResourceDelta binaryDelta,
280       int segmentCount) {
281     // When a package becomes a type or vice versa, expect 2 deltas,
282     // one on the folder & one on the class file
283     IResource resource = binaryDelta.getResource();
284     switch (resource.getType()) {
285       case IResource.FOLDER :
286         switch (binaryDelta.getKind()) {
287           case IResourceDelta.ADDED :
288           case IResourceDelta.REMOVED :
289             IPath packagePath = resource.getFullPath().removeFirstSegments(
290                 segmentCount);
291             String packageName = packagePath.toString();
292             if (binaryDelta.getKind() == IResourceDelta.ADDED) {
293               // see if any known source file is from the same package...
294               // classpath already includes new package
295 //              if (!newState.isKnownPackage(packageName)) {
296 //                if (PHPBuilder.DEBUG)
297 //                  System.out.println("Found added package " + packageName); //$NON-NLS-1$
298 //                addDependentsOf(packagePath, false);
299 //                return;
300 //              }
301               if (PHPBuilder.DEBUG)
302                 System.out.println("Skipped dependents of added package "
303                     + packageName); //$NON-NLS-1$
304             } else {
305               // see if the package still exists on the classpath
306               //                                                if (!nameEnvironment.isPackage(packageName)) {
307               //                                                        if (JavaBuilder.DEBUG)
308               //                                                                System.out.println("Found removed package " + packageName);
309               // //$NON-NLS-1$
310               //                                                        addDependentsOf(packagePath, false);
311               //                                                        return;
312               //                                                }
313               if (PHPBuilder.DEBUG)
314                 System.out.println("Skipped dependents of removed package "
315                     + packageName); //$NON-NLS-1$
316             }
317           // fall thru & traverse the sub-packages and .class files
318           case IResourceDelta.CHANGED :
319             IResourceDelta[] children = binaryDelta.getAffectedChildren();
320             for (int i = 0, l = children.length; i < l; i++)
321               findAffectedSourceFiles(children[i], segmentCount);
322         }
323         return;
324       case IResource.FILE :
325     //                  if (Util.isClassFileName(resource.getName())) {
326     //                          IPath typePath =
327     // resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension();
328     //                          switch (binaryDelta.getKind()) {
329     //                                  case IResourceDelta.ADDED :
330     //                                  case IResourceDelta.REMOVED :
331     //                                          if (JavaBuilder.DEBUG)
332     //                                                  System.out.println("Found added/removed class file " + typePath);
333     // //$NON-NLS-1$
334     //                                          addDependentsOf(typePath, false);
335     //                                          return;
336     //                                  case IResourceDelta.CHANGED :
337     //                                          if ((binaryDelta.getFlags() & IResourceDelta.CONTENT) == 0)
338     //                                                  return; // skip it since it really isn't changed
339     //                                          if (JavaBuilder.DEBUG)
340     //                                                  System.out.println("Found changed class file " + typePath);
341     // //$NON-NLS-1$
342     //                                          addDependentsOf(typePath, false);
343     //                          }
344     //                          return;
345     //                  }
346     }
347   }
348   protected boolean findSourceFiles(IResourceDelta delta) throws CoreException {
349     for (int i = 0, l = sourceLocations.length; i < l; i++) {
350       ClasspathMultiDirectory md = sourceLocations[i];
351       if (md.sourceFolder.equals(javaBuilder.currentProject)) {
352         // skip nested source & output folders when the project is a source
353         // folder
354         int segmentCount = delta.getFullPath().segmentCount();
355         IResourceDelta[] children = delta.getAffectedChildren();
356         for (int j = 0, m = children.length; j < m; j++)
357           if (!isExcludedFromProject(children[j].getFullPath()))
358             findSourceFiles(children[j], md, segmentCount);
359       } else {
360         IResourceDelta sourceDelta = delta.findMember(md.sourceFolder
361             .getProjectRelativePath());
362         if (sourceDelta != null) {
363           if (sourceDelta.getKind() == IResourceDelta.REMOVED) {
364             if (PHPBuilder.DEBUG)
365               System.out
366                   .println("ABORTING incremental build... found removed source folder"); //$NON-NLS-1$
367             return false; // removed source folder should not make it here, but
368             // handle anyways (ADDED is supported)
369           }
370           int segmentCount = sourceDelta.getFullPath().segmentCount();
371           IResourceDelta[] children = sourceDelta.getAffectedChildren();
372           for (int j = 0, m = children.length; j < m; j++)
373             findSourceFiles(children[j], md, segmentCount);
374         }
375       }
376       notifier.checkCancel();
377     }
378     return true;
379   }
380   protected void findSourceFiles(IResourceDelta sourceDelta,
381       ClasspathMultiDirectory md, int segmentCount) throws CoreException {
382     // When a package becomes a type or vice versa, expect 2 deltas,
383     // one on the folder & one on the source file
384     IResource resource = sourceDelta.getResource();
385     if (md.exclusionPatterns != null
386         && Util.isExcluded(resource, md.exclusionPatterns))
387       return;
388     switch (resource.getType()) {
389       case IResource.FOLDER :
390         switch (sourceDelta.getKind()) {
391           case IResourceDelta.ADDED :
392             IPath addedPackagePath = resource.getFullPath()
393                 .removeFirstSegments(segmentCount);
394             createFolder(addedPackagePath, md.binaryFolder); // ensure package
395             // exists in the
396             // output folder
397             // add dependents even when the package thinks it exists to be on
398             // the safe side
399             if (PHPBuilder.DEBUG)
400               System.out.println("Found added package " + addedPackagePath); //$NON-NLS-1$
401             addDependentsOf(addedPackagePath, true);
402           // fall thru & collect all the source files
403           case IResourceDelta.CHANGED :
404             IResourceDelta[] children = sourceDelta.getAffectedChildren();
405             for (int i = 0, l = children.length; i < l; i++)
406               findSourceFiles(children[i], md, segmentCount);
407             return;
408           case IResourceDelta.REMOVED :
409             IPath removedPackagePath = resource.getFullPath()
410                 .removeFirstSegments(segmentCount);
411             if (sourceLocations.length > 1) {
412               for (int i = 0, l = sourceLocations.length; i < l; i++) {
413                 if (sourceLocations[i].sourceFolder.getFolder(
414                     removedPackagePath).exists()) {
415                   // only a package fragment was removed, same as removing
416                   // multiple source files
417                   createFolder(removedPackagePath, md.binaryFolder); // ensure
418                   // package
419                   // exists
420                   // in the
421                   // output
422                   // folder
423                   IResourceDelta[] removedChildren = sourceDelta
424                       .getAffectedChildren();
425                   for (int j = 0, m = removedChildren.length; j < m; j++)
426                     findSourceFiles(removedChildren[j], md, segmentCount);
427                   return;
428                 }
429               }
430             }
431             IFolder removedPackageFolder = md.binaryFolder
432                 .getFolder(removedPackagePath);
433             if (removedPackageFolder.exists())
434               removedPackageFolder.delete(IResource.FORCE, null);
435             // add dependents even when the package thinks it does not exist to
436             // be on the safe side
437             if (PHPBuilder.DEBUG)
438               System.out.println("Found removed package " + removedPackagePath); //$NON-NLS-1$
439             addDependentsOf(removedPackagePath, true);
440             newState.removePackage(sourceDelta);
441         }
442         return;
443       case IResource.FILE :
444         String resourceName = resource.getName();
445         if (Util.isJavaFileName(resourceName)) {
446           IPath typePath = resource.getFullPath().removeFirstSegments(
447               segmentCount).removeFileExtension();
448           String typeLocator = resource.getProjectRelativePath().toString();
449           switch (sourceDelta.getKind()) {
450             case IResourceDelta.ADDED :
451               if (PHPBuilder.DEBUG)
452                 System.out.println("Compile this added source file "
453                     + typeLocator); //$NON-NLS-1$
454               sourceFiles.add(new SourceFile((IFile) resource, md, encoding));
455               String typeName = typePath.toString();
456 //              if (!newState.isDuplicateLocator(typeName, typeLocator)) { // adding
457 //                // dependents
458 //                // results
459 //                // in
460 //                // 2
461 //                // duplicate
462 //                // errors
463 //                if (PHPBuilder.DEBUG)
464 //                  System.out.println("Found added source file " + typeName); //$NON-NLS-1$
465 //                addDependentsOf(typePath, true);
466 //              }
467               return;
468             case IResourceDelta.REMOVED :
469 //              char[][] definedTypeNames = newState
470 //                  .getDefinedTypeNamesFor(typeLocator);
471 //              if (definedTypeNames == null) { // defined a single type matching
472 //                // typePath
473 //                removeClassFile(typePath, md.binaryFolder);
474 //                if ((sourceDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
475 //                  // remove problems and tasks for a compilation unit that is
476 //                  // being moved (to another package or renamed)
477 //                  // if the target file is a compilation unit, the new cu will
478 //                  // be recompiled
479 //                  // if the target file is a non-java resource, then markers
480 //                  // are removed
481 //                  // see bug 2857
482 //                  IResource movedFile = javaBuilder.workspaceRoot
483 //                      .getFile(sourceDelta.getMovedToPath());
484 //                  PHPBuilder.removeProblemsAndTasksFor(movedFile);
485 //                }
486 //              } else {
487                 if (PHPBuilder.DEBUG)
488                   System.out.println("Found removed source file "
489                       + typePath.toString()); //$NON-NLS-1$
490                 addDependentsOf(typePath, true); // add dependents of the
491                 // source file since it may be
492                 // involved in a name
493                 // collision
494 //                if (definedTypeNames.length > 0) { // skip it if it failed to
495 //                  // successfully define a
496 //                  // type
497 //                  IPath packagePath = typePath.removeLastSegments(1);
498 //                  for (int i = 0, l = definedTypeNames.length; i < l; i++)
499 //                    removeClassFile(packagePath.append(new String(
500 //                        definedTypeNames[i])), md.binaryFolder);
501 //                }
502 //              }
503 //              newState.removeLocator(typeLocator);
504               return;
505             case IResourceDelta.CHANGED :
506               if ((sourceDelta.getFlags() & IResourceDelta.CONTENT) == 0)
507                 return; // skip it since it really isn't changed
508               if (PHPBuilder.DEBUG)
509                 System.out.println("Compile this changed source file "
510                     + typeLocator); //$NON-NLS-1$
511               sourceFiles.add(new SourceFile((IFile) resource, md, encoding));
512           }
513           return;
514           //                    } else if (Util.isClassFileName(resourceName)) {
515           //                            return; // skip class files
516         } else if (md.hasIndependentOutputFolder) {
517           if (javaBuilder.filterExtraResource(resource))
518             return;
519           // copy all other resource deltas to the output folder
520           IPath resourcePath = resource.getFullPath().removeFirstSegments(
521               segmentCount);
522           IResource outputFile = md.binaryFolder.getFile(resourcePath);
523           switch (sourceDelta.getKind()) {
524             case IResourceDelta.ADDED :
525               if (outputFile.exists()) {
526                 if (PHPBuilder.DEBUG)
527                   System.out.println("Deleting existing file " + resourcePath); //$NON-NLS-1$
528                 outputFile.delete(IResource.FORCE, null);
529               }
530               if (PHPBuilder.DEBUG)
531                 System.out.println("Copying added file " + resourcePath); //$NON-NLS-1$
532               createFolder(resourcePath.removeLastSegments(1), md.binaryFolder); // ensure
533               // package
534               // exists
535               // in
536               // the
537               // output
538               // folder
539               resource.copy(outputFile.getFullPath(), IResource.FORCE, null);
540               outputFile.setDerived(true);
541               return;
542             case IResourceDelta.REMOVED :
543               if (outputFile.exists()) {
544                 if (PHPBuilder.DEBUG)
545                   System.out.println("Deleting removed file " + resourcePath); //$NON-NLS-1$
546                 outputFile.delete(IResource.FORCE, null);
547               }
548               return;
549             case IResourceDelta.CHANGED :
550               if ((sourceDelta.getFlags() & IResourceDelta.CONTENT) == 0)
551                 return; // skip it since it really isn't changed
552               if (outputFile.exists()) {
553                 if (PHPBuilder.DEBUG)
554                   System.out.println("Deleting existing file " + resourcePath); //$NON-NLS-1$
555                 outputFile.delete(IResource.FORCE, null);
556               }
557               if (PHPBuilder.DEBUG)
558                 System.out.println("Copying changed file " + resourcePath); //$NON-NLS-1$
559               createFolder(resourcePath.removeLastSegments(1), md.binaryFolder); // ensure
560               // package
561               // exists
562               // in
563               // the
564               // output
565               // folder
566               resource.copy(outputFile.getFullPath(), IResource.FORCE, null);
567               outputFile.setDerived(true);
568           }
569           return;
570         }
571     }
572   }
573   protected void finishedWith(String sourceLocator, CompilationResult result,
574       char[] mainTypeName, ArrayList definedTypeNames,
575       ArrayList duplicateTypeNames) throws CoreException {
576 //    char[][] previousTypeNames = newState.getDefinedTypeNamesFor(sourceLocator);
577 //    if (previousTypeNames == null)
578 //      previousTypeNames = new char[][]{mainTypeName};
579 //    IPath packagePath = null;
580 //    next : for (int i = 0, l = previousTypeNames.length; i < l; i++) {
581 //      char[] previous = previousTypeNames[i];
582 //      for (int j = 0, m = definedTypeNames.size(); j < m; j++)
583 //        if (CharOperation.equals(previous, (char[]) definedTypeNames.get(j)))
584 //          continue next;
585 //      SourceFile sourceFile = (SourceFile) result.getCompilationUnit();
586 //      if (packagePath == null) {
587 //        int count = sourceFile.sourceLocation.sourceFolder.getFullPath()
588 //            .segmentCount();
589 //        packagePath = sourceFile.resource.getFullPath().removeFirstSegments(
590 //            count).removeLastSegments(1);
591 //      }
592 //      if (secondaryTypesToRemove == null)
593 //        this.secondaryTypesToRemove = new SimpleLookupTable();
594 //      ArrayList types = (ArrayList) secondaryTypesToRemove
595 //          .get(sourceFile.sourceLocation.binaryFolder);
596 //      if (types == null)
597 //        types = new ArrayList(definedTypeNames.size());
598 //      types.add(packagePath.append(new String(previous)));
599 //      secondaryTypesToRemove.put(sourceFile.sourceLocation.binaryFolder, types);
600 //    }
601     //  super.finishedWith(sourceLocator, result, mainTypeName,
602     // definedTypeNames, duplicateTypeNames);
603   }
604 //  protected void removeClassFile(IPath typePath, IContainer outputFolder)
605 //      throws CoreException {
606 //    if (typePath.lastSegment().indexOf('$') == -1) { // is not a nested type
607 //      newState.removeQualifiedTypeName(typePath.toString());
608 //      // add dependents even when the type thinks it does not exist to be on
609 //      // the safe side
610 //      if (PHPBuilder.DEBUG)
611 //        System.out.println("Found removed type " + typePath); //$NON-NLS-1$
612 //      addDependentsOf(typePath, true); // when member types are removed, their
613 //      // enclosing type is structurally
614 //      // changed
615 //    }
616 //    IFile classFile = outputFolder.getFile(typePath
617 //        .addFileExtension(PHPBuilder.CLASS_EXTENSION));
618 //    if (classFile.exists()) {
619 //      if (PHPBuilder.DEBUG)
620 //        System.out.println("Deleting class file of removed type " + typePath); //$NON-NLS-1$
621 //      classFile.delete(IResource.FORCE, null);
622 //    }
623 //  }
624 //  protected void removeSecondaryTypes() throws CoreException {
625 //    if (secondaryTypesToRemove != null) { // delayed deleting secondary types
626 //      // until the end of the compile loop
627 //      Object[] keyTable = secondaryTypesToRemove.keyTable;
628 //      Object[] valueTable = secondaryTypesToRemove.valueTable;
629 //      for (int i = 0, l = keyTable.length; i < l; i++) {
630 //        IContainer outputFolder = (IContainer) keyTable[i];
631 //        if (outputFolder != null) {
632 //          ArrayList paths = (ArrayList) valueTable[i];
633 //          for (int j = 0, m = paths.size(); j < m; j++)
634 //            removeClassFile((IPath) paths.get(j), outputFolder);
635 //        }
636 //      }
637 //      this.secondaryTypesToRemove = null;
638 //      if (previousSourceFiles != null && previousSourceFiles.size() > 1)
639 //        this.previousSourceFiles = null; // cannot optimize recompile case when
640 //      // a secondary type is deleted
641 //    }
642 //  }
643   protected void resetCollections() {
644     previousSourceFiles = sourceFiles.isEmpty()
645         ? null
646         : (ArrayList) sourceFiles.clone();
647     sourceFiles.clear();
648     qualifiedStrings.clear();
649     simpleStrings.clear();
650     workQueue.clear();
651   }
652   protected void updateProblemsFor(SourceFile sourceFile,
653       CompilationResult result) throws CoreException {
654     IMarker[] markers = PHPBuilder.getProblemsFor(sourceFile.resource);
655     IProblem[] problems = result.getProblems();
656     if (problems == null && markers.length == 0)
657       return;
658     notifier.updateProblemCounts(markers, problems);
659     PHPBuilder.removeProblemsFor(sourceFile.resource);
660     storeProblemsFor(sourceFile, problems);
661   }
662   protected void updateTasksFor(SourceFile sourceFile, CompilationResult result)
663       throws CoreException {
664     IMarker[] markers = PHPBuilder.getTasksFor(sourceFile.resource);
665     IProblem[] tasks = result.getTasks();
666     if (tasks == null && markers.length == 0)
667       return;
668     PHPBuilder.removeTasksFor(sourceFile.resource);
669     storeTasksFor(sourceFile, tasks);
670   }
671   //protected void writeClassFileBytes(byte[] bytes, IFile file, String
672   // qualifiedFileName, boolean isSecondaryType) throws CoreException {
673   //    // Before writing out the class file, compare it to the previous file
674   //    // If structural changes occured then add dependent source files
675   //    if (file.exists()) {
676   //            if (writeClassFileCheck(file, qualifiedFileName, bytes)) {
677   //                    if (JavaBuilder.DEBUG)
678   //                            System.out.println("Writing changed class file " +
679   // file.getName());//$NON-NLS-1$
680   //                    file.setContents(new ByteArrayInputStream(bytes), true, false, null);
681   //                    if (!file.isDerived())
682   //                            file.setDerived(true);
683   //            } else if (JavaBuilder.DEBUG) {
684   //                    System.out.println("Skipped over unchanged class file " +
685   // file.getName());//$NON-NLS-1$
686   //            }
687   //    } else {
688   //            if (isSecondaryType)
689   //                    addDependentsOf(new Path(qualifiedFileName), true); // new secondary type
690   //            if (JavaBuilder.DEBUG)
691   //                    System.out.println("Writing new class file " +
692   // file.getName());//$NON-NLS-1$
693   //            file.create(new ByteArrayInputStream(bytes), IResource.FORCE, null);
694   //            file.setDerived(true);
695   //    }
696   //}
697   //protected boolean writeClassFileCheck(IFile file, String fileName, byte[]
698   // newBytes) throws CoreException {
699   //    try {
700   //            byte[] oldBytes = Util.getResourceContentsAsByteArray(file);
701   //            if (this.compileLoop > 1) { // only optimize files which were recompiled
702   // during the dependent pass, see 33990
703   //                    notEqual : if (newBytes.length == oldBytes.length) {
704   //                            for (int i = newBytes.length; --i >= 0;)
705   //                                    if (newBytes[i] != oldBytes[i]) break notEqual;
706   //                            return false; // bytes are identical so skip them
707   //                    }
708   //            }
709   //            IPath location = file.getLocation();
710   //            if (location == null) return false; // unable to determine location of
711   // this class file
712   //            ClassFileReader reader = new ClassFileReader(oldBytes,
713   // location.toString().toCharArray());
714   //            // ignore local types since they're only visible inside a single method
715   //            if (!(reader.isLocal() || reader.isAnonymous()) &&
716   // reader.hasStructuralChanges(newBytes)) {
717   //                    if (JavaBuilder.DEBUG)
718   //                            System.out.println("Type has structural changes " + fileName);
719   // //$NON-NLS-1$
720   //                    addDependentsOf(new Path(fileName), true);
721   //            }
722   //    } catch (ClassFormatException e) {
723   //            addDependentsOf(new Path(fileName), true);
724   //    }
725   //    return true;
726   //}
727   public String toString() {
728     return "incremental image builder for:\n\tnew state: " + newState; //$NON-NLS-1$
729   }
730   /*
731    * Debug helper
732    * 
733    * static void dump(IResourceDelta delta) { StringBuffer buffer = new
734    * StringBuffer(); IPath path = delta.getFullPath(); for (int i =
735    * path.segmentCount(); --i > 0;) buffer.append(" "); switch
736    * (delta.getKind()) { case IResourceDelta.ADDED: buffer.append('+'); break;
737    * case IResourceDelta.REMOVED: buffer.append('-'); break; case
738    * IResourceDelta.CHANGED: '); break; case IResourceDelta.NO_CHANGE:
739    * buffer.append('='); break; default: buffer.append('?'); break; }
740    * buffer.append(path); System.out.println(buffer.toString());
741    * IResourceDelta[] children = delta.getAffectedChildren(); for (int i = 0, l =
742    * children.length; i < l; i++) dump(children[i]); }
743    */
744 }