Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/jdt/internal/core/builder/IncrementalImageBuilder.java


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