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