KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > core > builder > IncrementalImageBuilder


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-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.*;
17 import org.eclipse.jdt.core.compiler.*;
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.SimpleLookupTable;
22 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
23 import org.eclipse.jdt.internal.core.util.Messages;
24 import org.eclipse.jdt.internal.core.util.Util;
25
26 import java.io.*;
27 import java.net.URI JavaDoc;
28 import java.util.*;
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 StringSet qualifiedStrings;
38 protected StringSet simpleStrings;
39 protected SimpleLookupTable secondaryTypesToRemove;
40 protected boolean hasStructuralChanges;
41 protected int compileLoop;
42 protected boolean makeOutputFolderConsistent;
43
44 public static int MaxCompileLoop = 5; // perform a full build if it takes more than ? incremental compile loops
45

46 protected IncrementalImageBuilder(JavaBuilder javaBuilder, State buildState) {
47     super(javaBuilder, true, buildState);
48     this.nameEnvironment.isIncrementalBuild = true;
49     this.makeOutputFolderConsistent = JavaCore.ENABLED.equals(
50         javaBuilder.javaProject.getOption(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER, true));
51 }
52
53 protected IncrementalImageBuilder(JavaBuilder javaBuilder) {
54     this(javaBuilder, null);
55     this.newState.copyFrom(javaBuilder.lastState);
56 }
57
58 protected IncrementalImageBuilder(BatchImageBuilder batchBuilder) {
59     this(batchBuilder.javaBuilder, batchBuilder.newState);
60     resetCollections();
61 }
62
63 public boolean build(SimpleLookupTable deltas) {
64     // initialize builder
65
// walk this project's deltas, find changed source files
66
// walk prereq projects' deltas, find changed class files & add affected source files
67
// use the build state # to skip the deltas for certain prereq projects
68
// ignore changed zip/jar files since they caused a full build
69
// compile the source files & acceptResult()
70
// compare the produced class files against the existing ones on disk
71
// recompile all dependent source files of any type with structural changes or new/removed secondary type
72
// keep a loop counter to abort & perform a full build
73

74     if (JavaBuilder.DEBUG)
75         System.out.println("INCREMENTAL build"); //$NON-NLS-1$
76

77     try {
78         resetCollections();
79
80         notifier.subTask(Messages.build_analyzingDeltas);
81         if (javaBuilder.hasBuildpathErrors()) {
82             // if a mssing class file was detected in the last build, a build state was saved since its no longer fatal
83
// but we need to rebuild every source file since problems were not recorded
84
// AND to avoid the infinite build scenario if this project is involved in a cycle, see bug 160550
85
// we need to avoid unnecessary deltas caused by doing a full build in this case
86
javaBuilder.currentProject.deleteMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
87             addAllSourceFiles(sourceFiles);
88             notifier.updateProgressDelta(0.25f);
89         } else {
90             IResourceDelta sourceDelta = (IResourceDelta) deltas.get(javaBuilder.currentProject);
91             if (sourceDelta != null)
92                 if (!findSourceFiles(sourceDelta)) return false;
93             notifier.updateProgressDelta(0.10f);
94
95             Object JavaDoc[] keyTable = deltas.keyTable;
96             Object JavaDoc[] valueTable = deltas.valueTable;
97             for (int i = 0, l = valueTable.length; i < l; i++) {
98                 IResourceDelta delta = (IResourceDelta) valueTable[i];
99                 if (delta != null) {
100                     IProject p = (IProject) keyTable[i];
101                     ClasspathLocation[] classFoldersAndJars = (ClasspathLocation[]) javaBuilder.binaryLocationsPerProject.get(p);
102                     if (classFoldersAndJars != null)
103                         if (!findAffectedSourceFiles(delta, classFoldersAndJars, p)) return false;
104                 }
105             }
106             notifier.updateProgressDelta(0.10f);
107
108             notifier.subTask(Messages.build_analyzingSources);
109             addAffectedSourceFiles();
110             notifier.updateProgressDelta(0.05f);
111         }
112
113         this.compileLoop = 0;
114         float increment = 0.40f;
115         while (sourceFiles.size() > 0) { // added to in acceptResult
116
if (++this.compileLoop > MaxCompileLoop) {
117                 if (JavaBuilder.DEBUG)
118                     System.out.println("ABORTING incremental build... exceeded loop count"); //$NON-NLS-1$
119
return false;
120             }
121             notifier.checkCancel();
122
123             SourceFile[] allSourceFiles = new SourceFile[sourceFiles.size()];
124             sourceFiles.toArray(allSourceFiles);
125             resetCollections();
126
127             workQueue.addAll(allSourceFiles);
128             notifier.setProgressPerCompilationUnit(increment / allSourceFiles.length);
129             increment = increment / 2;
130             compile(allSourceFiles);
131             removeSecondaryTypes();
132             addAffectedSourceFiles();
133         }
134         if (this.hasStructuralChanges && javaBuilder.javaProject.hasCycleMarker())
135             javaBuilder.mustPropagateStructuralChanges();
136     } catch (AbortIncrementalBuildException e) {
137         // abort the incremental build and let the batch builder handle the problem
138
if (JavaBuilder.DEBUG)
139             System.out.println("ABORTING incremental build... problem with " + e.qualifiedTypeName + //$NON-NLS-1$
140
". Likely renamed inside its existing source file."); //$NON-NLS-1$
141
return false;
142     } catch (CoreException e) {
143         throw internalException(e);
144     } finally {
145         cleanUp();
146     }
147     return true;
148 }
149
150 protected void buildAfterBatchBuild() {
151     // called from a batch builder once all source files have been compiled AND some changes
152
// need to be propagated incrementally (annotations, missing secondary types)
153

154     if (JavaBuilder.DEBUG)
155         System.out.println("INCREMENTAL build after batch build @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$
156

157     // this is a copy of the incremental build loop
158
try {
159         addAffectedSourceFiles();
160         while (this.sourceFiles.size() > 0) {
161             notifier.checkCancel();
162             SourceFile[] allSourceFiles = new SourceFile[this.sourceFiles.size()];
163             this.sourceFiles.toArray(allSourceFiles);
164             resetCollections();
165             notifier.setProgressPerCompilationUnit(0.08f / allSourceFiles.length);
166             this.workQueue.addAll(allSourceFiles);
167             compile(allSourceFiles);
168             removeSecondaryTypes();
169             addAffectedSourceFiles();
170         }
171     } catch (CoreException e) {
172         throw internalException(e);
173     } finally {
174         cleanUp();
175     }
176 }
177
178 protected void addAffectedSourceFiles() {
179     if (qualifiedStrings.elementSize == 0 && simpleStrings.elementSize == 0) return;
180
181     addAffectedSourceFiles(qualifiedStrings, simpleStrings, null);
182 }
183
184 protected void addAffectedSourceFiles(StringSet qualifiedSet, StringSet simpleSet, StringSet affectedTypes) {
185     // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
186
char[][][] internedQualifiedNames = ReferenceCollection.internQualifiedNames(qualifiedSet);
187     // if a well known qualified name was found then we can skip over these
188
if (internedQualifiedNames.length < qualifiedSet.elementSize)
189         internedQualifiedNames = null;
190     char[][] internedSimpleNames = ReferenceCollection.internSimpleNames(simpleSet);
191     // if a well known name was found then we can skip over these
192
if (internedSimpleNames.length < simpleSet.elementSize)
193         internedSimpleNames = null;
194
195     Object JavaDoc[] keyTable = newState.references.keyTable;
196     Object JavaDoc[] valueTable = newState.references.valueTable;
197     next : for (int i = 0, l = valueTable.length; i < l; i++) {
198         String JavaDoc typeLocator = (String JavaDoc) keyTable[i];
199         if (typeLocator != null) {
200             if (affectedTypes != null && !affectedTypes.includes(typeLocator)) continue next;
201             ReferenceCollection refs = (ReferenceCollection) valueTable[i];
202             if (refs.includes(internedQualifiedNames, internedSimpleNames)) {
203                 IFile file = javaBuilder.currentProject.getFile(typeLocator);
204                 SourceFile sourceFile = findSourceFile(file, true);
205                 if (sourceFile == null) continue next;
206                 if (sourceFiles.contains(sourceFile)) continue next;
207                 if (compiledAllAtOnce && previousSourceFiles != null && previousSourceFiles.contains(sourceFile))
208                     continue next; // can skip previously compiled files since already saw hierarchy related problems
209

210                 if (JavaBuilder.DEBUG)
211                     System.out.println(" adding affected source file " + typeLocator); //$NON-NLS-1$
212
sourceFiles.add(sourceFile);
213             }
214         }
215     }
216 }
217
218 protected void addDependentsOf(IPath path, boolean isStructuralChange) {
219     if (isStructuralChange && !this.hasStructuralChanges) {
220         newState.tagAsStructurallyChanged();
221         this.hasStructuralChanges = true;
222     }
223     // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
224
path = path.setDevice(null);
225     String JavaDoc packageName = path.removeLastSegments(1).toString();
226     qualifiedStrings.add(packageName);
227     String JavaDoc typeName = path.lastSegment();
228     int memberIndex = typeName.indexOf('$');
229     if (memberIndex > 0)
230         typeName = typeName.substring(0, memberIndex);
231     if (simpleStrings.add(typeName) && JavaBuilder.DEBUG)
232         System.out.println(" will look for dependents of " //$NON-NLS-1$
233
+ typeName + " in " + packageName); //$NON-NLS-1$
234
}
235
236 protected boolean checkForClassFileChanges(IResourceDelta binaryDelta, ClasspathMultiDirectory md, int segmentCount) throws CoreException {
237     IResource resource = binaryDelta.getResource();
238     // remember that if inclusion & exclusion patterns change then a full build is done
239
boolean isExcluded = (md.exclusionPatterns != null || md.inclusionPatterns != null)
240         && Util.isExcluded(resource, md.inclusionPatterns, md.exclusionPatterns);
241     switch(resource.getType()) {
242         case IResource.FOLDER :
243             if (isExcluded && md.inclusionPatterns == null)
244                 return true; // no need to go further with this delta since its children cannot be included
245

246             IResourceDelta[] children = binaryDelta.getAffectedChildren();
247             for (int i = 0, l = children.length; i < l; i++)
248                 if (!checkForClassFileChanges(children[i], md, segmentCount))
249                     return false;
250             return true;
251         case IResource.FILE :
252             if (!isExcluded && org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(resource.getName())) {
253                 // perform full build if a managed class file has been changed
254
IPath typePath = resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension();
255                 if (newState.isKnownType(typePath.toString())) {
256                     if (JavaBuilder.DEBUG)
257                         System.out.println("MUST DO FULL BUILD. Found change to class file " + typePath); //$NON-NLS-1$
258
return false;
259                 }
260                 return true;
261             }
262     }
263     return true;
264 }
265
266 protected void cleanUp() {
267     super.cleanUp();
268
269     this.sourceFiles = null;
270     this.previousSourceFiles = null;
271     this.qualifiedStrings = null;
272     this.simpleStrings = null;
273     this.secondaryTypesToRemove = null;
274     this.hasStructuralChanges = false;
275     this.compileLoop = 0;
276 }
277
278 protected void compile(SourceFile[] units, SourceFile[] additionalUnits, boolean compilingFirstGroup) {
279     if (compilingFirstGroup && additionalUnits != null) {
280         // add any source file from additionalUnits to units if it defines secondary types
281
// otherwise its possible during testing with MAX_AT_ONCE == 1 that a secondary type
282
// can cause an infinite loop as it alternates between not found and defined, see bug 146324
283
ArrayList extras = null;
284         for (int i = 0, l = additionalUnits.length; i < l; i++) {
285             SourceFile unit = additionalUnits[i];
286             if (unit != null && newState.getDefinedTypeNamesFor(unit.typeLocator()) != null) {
287                 if (JavaBuilder.DEBUG)
288                     System.out.println("About to compile file with secondary types "+ unit.typeLocator()); //$NON-NLS-1$
289
if (extras == null)
290                     extras = new ArrayList(3);
291                 extras.add(unit);
292             }
293         }
294         if (extras != null) {
295             int oldLength = units.length;
296             int toAdd = extras.size();
297             System.arraycopy(units, 0, units = new SourceFile[oldLength + toAdd], 0, oldLength);
298             for (int i = 0; i < toAdd; i++)
299                 units[oldLength++] = (SourceFile) extras.get(i);
300         }
301     }
302     super.compile(units, additionalUnits, compilingFirstGroup);
303 }
304
305 protected void deleteGeneratedFiles(IFile[] deletedGeneratedFiles) {
306     // delete generated files and recompile any affected source files
307
try {
308         for (int j = deletedGeneratedFiles.length; --j >= 0;) {
309             IFile deletedFile = deletedGeneratedFiles[j];
310             if (deletedFile.exists()) continue; // only delete .class files for source files that were actually deleted
311

312             SourceFile sourceFile = findSourceFile(deletedFile, false);
313             String JavaDoc typeLocator = sourceFile.typeLocator();
314             int mdSegmentCount = sourceFile.sourceLocation.sourceFolder.getFullPath().segmentCount();
315             IPath typePath = sourceFile.resource.getFullPath().removeFirstSegments(mdSegmentCount).removeFileExtension();
316             addDependentsOf(typePath, true); // add dependents of the source file since its now deleted
317
previousSourceFiles = null; // existing source files did not see it as deleted since they were compiled before it was
318
char[][] definedTypeNames = newState.getDefinedTypeNamesFor(typeLocator);
319             if (definedTypeNames == null) { // defined a single type matching typePath
320
removeClassFile(typePath, sourceFile.sourceLocation.binaryFolder);
321             } else {
322                 if (definedTypeNames.length > 0) { // skip it if it failed to successfully define a type
323
IPath packagePath = typePath.removeLastSegments(1);
324                     for (int d = 0, l = definedTypeNames.length; d < l; d++)
325                         removeClassFile(packagePath.append(new String JavaDoc(definedTypeNames[d])), sourceFile.sourceLocation.binaryFolder);
326                 }
327             }
328             this.newState.removeLocator(typeLocator);
329         }
330     } catch (CoreException e) {
331         // must continue with compile loop so just log the CoreException
332
e.printStackTrace();
333     }
334 }
335
336 protected boolean findAffectedSourceFiles(IResourceDelta delta, ClasspathLocation[] classFoldersAndJars, IProject prereqProject) {
337     for (int i = 0, l = classFoldersAndJars.length; i < l; i++) {
338         ClasspathLocation bLocation = classFoldersAndJars[i];
339         // either a .class file folder or a zip/jar file
340
if (bLocation != null) { // skip unchanged output folder
341
IPath p = bLocation.getProjectRelativePath();
342             if (p != null) {
343                 IResourceDelta binaryDelta = delta.findMember(p);
344                 if (binaryDelta != null) {
345                     if (bLocation instanceof ClasspathJar) {
346                         if (JavaBuilder.DEBUG)
347                             System.out.println("ABORTING incremental build... found delta to jar/zip file"); //$NON-NLS-1$
348
return false; // do full build since jar file was changed (added/removed were caught as classpath change)
349
}
350                     if (binaryDelta.getKind() == IResourceDelta.ADDED || binaryDelta.getKind() == IResourceDelta.REMOVED) {
351                         if (JavaBuilder.DEBUG)
352                             System.out.println("ABORTING incremental build... found added/removed binary folder"); //$NON-NLS-1$
353
return false; // added/removed binary folder should not make it here (classpath change), but handle anyways
354
}
355                     int segmentCount = binaryDelta.getFullPath().segmentCount();
356                     IResourceDelta[] children = binaryDelta.getAffectedChildren(); // .class files from class folder
357
StringSet structurallyChangedTypes = null;
358                     if (bLocation.isOutputFolder())
359                         structurallyChangedTypes = this.newState.getStructurallyChangedTypes(javaBuilder.getLastState(prereqProject));
360                     for (int j = 0, m = children.length; j < m; j++)
361                         findAffectedSourceFiles(children[j], segmentCount, structurallyChangedTypes);
362                     notifier.checkCancel();
363                 }
364             }
365         }
366     }
367     return true;
368 }
369
370 protected void findAffectedSourceFiles(IResourceDelta binaryDelta, int segmentCount, StringSet structurallyChangedTypes) {
371     // When a package becomes a type or vice versa, expect 2 deltas,
372
// one on the folder & one on the class file
373
IResource resource = binaryDelta.getResource();
374     switch(resource.getType()) {
375         case IResource.FOLDER :
376             switch (binaryDelta.getKind()) {
377                 case IResourceDelta.ADDED :
378                 case IResourceDelta.REMOVED :
379                     IPath packagePath = resource.getFullPath().removeFirstSegments(segmentCount);
380                     String JavaDoc packageName = packagePath.toString();
381                     if (binaryDelta.getKind() == IResourceDelta.ADDED) {
382                         // see if any known source file is from the same package... classpath already includes new package
383
if (!newState.isKnownPackage(packageName)) {
384                             if (JavaBuilder.DEBUG)
385                                 System.out.println("Found added package " + packageName); //$NON-NLS-1$
386
addDependentsOf(packagePath, false);
387                             return;
388                         }
389                         if (JavaBuilder.DEBUG)
390                             System.out.println("Skipped dependents of added package " + packageName); //$NON-NLS-1$
391
} else {
392                         // see if the package still exists on the classpath
393
if (!nameEnvironment.isPackage(packageName)) {
394                             if (JavaBuilder.DEBUG)
395                                 System.out.println("Found removed package " + packageName); //$NON-NLS-1$
396
addDependentsOf(packagePath, false);
397                             return;
398                         }
399                         if (JavaBuilder.DEBUG)
400                             System.out.println("Skipped dependents of removed package " + packageName); //$NON-NLS-1$
401
}
402                     // fall thru & traverse the sub-packages and .class files
403
case IResourceDelta.CHANGED :
404                     IResourceDelta[] children = binaryDelta.getAffectedChildren();
405                     for (int i = 0, l = children.length; i < l; i++)
406                         findAffectedSourceFiles(children[i], segmentCount, structurallyChangedTypes);
407             }
408             return;
409         case IResource.FILE :
410             if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(resource.getName())) {
411                 IPath typePath = resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension();
412                 switch (binaryDelta.getKind()) {
413                     case IResourceDelta.ADDED :
414                     case IResourceDelta.REMOVED :
415                         if (JavaBuilder.DEBUG)
416                             System.out.println("Found added/removed class file " + typePath); //$NON-NLS-1$
417
addDependentsOf(typePath, false);
418                         return;
419                     case IResourceDelta.CHANGED :
420                         if ((binaryDelta.getFlags() & IResourceDelta.CONTENT) == 0)
421                             return; // skip it since it really isn't changed
422
if (structurallyChangedTypes != null && !structurallyChangedTypes.includes(typePath.toString()))
423                             return; // skip since it wasn't a structural change
424
if (JavaBuilder.DEBUG)
425                             System.out.println("Found changed class file " + typePath); //$NON-NLS-1$
426
addDependentsOf(typePath, false);
427                 }
428                 return;
429             }
430     }
431 }
432
433 protected boolean findSourceFiles(IResourceDelta delta) throws CoreException {
434     ArrayList visited = this.makeOutputFolderConsistent ? new ArrayList(sourceLocations.length) : null;
435     for (int i = 0, l = sourceLocations.length; i < l; i++) {
436         ClasspathMultiDirectory md = sourceLocations[i];
437         if (this.makeOutputFolderConsistent && md.hasIndependentOutputFolder && !visited.contains(md.binaryFolder)) {
438             // even a project which acts as its own source folder can have an independent/nested output folder
439
visited.add(md.binaryFolder);
440             IResourceDelta binaryDelta = delta.findMember(md.binaryFolder.getProjectRelativePath());
441             if (binaryDelta != null) {
442                 int segmentCount = binaryDelta.getFullPath().segmentCount();
443                 IResourceDelta[] children = binaryDelta.getAffectedChildren();
444                 for (int j = 0, m = children.length; j < m; j++)
445                     if (!checkForClassFileChanges(children[j], md, segmentCount))
446                         return false;
447             }
448         }
449         if (md.sourceFolder.equals(javaBuilder.currentProject)) {
450             // skip nested source & output folders when the project is a source folder
451
int segmentCount = delta.getFullPath().segmentCount();
452             IResourceDelta[] children = delta.getAffectedChildren();
453             for (int j = 0, m = children.length; j < m; j++)
454                 if (!isExcludedFromProject(children[j].getFullPath()))
455                     if (!findSourceFiles(children[j], md, segmentCount))
456                         return false;
457         } else {
458             IResourceDelta sourceDelta = delta.findMember(md.sourceFolder.getProjectRelativePath());
459             if (sourceDelta != null) {
460                 if (sourceDelta.getKind() == IResourceDelta.REMOVED) {
461                     if (JavaBuilder.DEBUG)
462                         System.out.println("ABORTING incremental build... found removed source folder"); //$NON-NLS-1$
463
return false; // removed source folder should not make it here, but handle anyways (ADDED is supported)
464
}
465                 int segmentCount = sourceDelta.getFullPath().segmentCount();
466                 IResourceDelta[] children = sourceDelta.getAffectedChildren();
467                 try {
468                     for (int j = 0, m = children.length; j < m; j++)
469                         if (!findSourceFiles(children[j], md, segmentCount))
470                             return false;
471                 } catch (CoreException e) {
472                     // catch the case that a package has been renamed and collides on disk with an as-yet-to-be-deleted package
473
if (e.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) {
474                         if (JavaBuilder.DEBUG)
475                             System.out.println("ABORTING incremental build... found renamed package"); //$NON-NLS-1$
476
return false;
477                     }
478                     throw e; // rethrow
479
}
480             }
481         }
482         notifier.checkCancel();
483     }
484     return true;
485 }
486
487 protected boolean findSourceFiles(IResourceDelta sourceDelta, ClasspathMultiDirectory md, int segmentCount) throws CoreException {
488     // When a package becomes a type or vice versa, expect 2 deltas,
489
// one on the folder & one on the source file
490
IResource resource = sourceDelta.getResource();
491     // remember that if inclusion & exclusion patterns change then a full build is done
492
boolean isExcluded = (md.exclusionPatterns != null || md.inclusionPatterns != null)
493         && Util.isExcluded(resource, md.inclusionPatterns, md.exclusionPatterns);
494     switch(resource.getType()) {
495         case IResource.FOLDER :
496             if (isExcluded && md.inclusionPatterns == null)
497                 return true; // no need to go further with this delta since its children cannot be included
498

499             switch (sourceDelta.getKind()) {
500                 case IResourceDelta.ADDED :
501                     if (!isExcluded) {
502                         IPath addedPackagePath = resource.getFullPath().removeFirstSegments(segmentCount);
503                         createFolder(addedPackagePath, md.binaryFolder); // ensure package exists in the output folder
504
// add dependents even when the package thinks it exists to be on the safe side
505
if (JavaBuilder.DEBUG)
506                             System.out.println("Found added package " + addedPackagePath); //$NON-NLS-1$
507
addDependentsOf(addedPackagePath, true);
508                     }
509                     // fall thru & collect all the source files
510
case IResourceDelta.CHANGED :
511                     IResourceDelta[] children = sourceDelta.getAffectedChildren();
512                     for (int i = 0, l = children.length; i < l; i++)
513                         if (!findSourceFiles(children[i], md, segmentCount))
514                             return false;
515                     return true;
516                 case IResourceDelta.REMOVED :
517                     if (isExcluded) {
518                         // since this folder is excluded then there is nothing to delete (from this md), but must walk any included subfolders
519
children = sourceDelta.getAffectedChildren();
520                         for (int i = 0, l = children.length; i < l; i++)
521                             if (!findSourceFiles(children[i], md, segmentCount))
522                                 return false;
523                         return true;
524                     }
525                     IPath removedPackagePath = resource.getFullPath().removeFirstSegments(segmentCount);
526                     if (sourceLocations.length > 1) {
527                         for (int i = 0, l = sourceLocations.length; i < l; i++) {
528                             if (sourceLocations[i].sourceFolder.getFolder(removedPackagePath).exists()) {
529                                 // only a package fragment was removed, same as removing multiple source files
530
createFolder(removedPackagePath, md.binaryFolder); // ensure package exists in the output folder
531
IResourceDelta[] removedChildren = sourceDelta.getAffectedChildren();
532                                 for (int j = 0, m = removedChildren.length; j < m; j++)
533                                     if (!findSourceFiles(removedChildren[j], md, segmentCount))
534                                         return false;
535                                 return true;
536                             }
537                         }
538                     }
539                     IFolder removedPackageFolder = md.binaryFolder.getFolder(removedPackagePath);
540                     if (removedPackageFolder.exists())
541                         removedPackageFolder.delete(IResource.FORCE, null);
542                     // add dependents even when the package thinks it does not exist to be on the safe side
543
if (JavaBuilder.DEBUG)
544                         System.out.println("Found removed package " + removedPackagePath); //$NON-NLS-1$
545
addDependentsOf(removedPackagePath, true);
546                     newState.removePackage(sourceDelta);
547             }
548             return true;
549         case IResource.FILE :
550             if (isExcluded) return true;
551
552             String JavaDoc resourceName = resource.getName();
553             if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resourceName)) {
554                 IPath typePath = resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension();
555                 String JavaDoc typeLocator = resource.getProjectRelativePath().toString();
556                 switch (sourceDelta.getKind()) {
557                     case IResourceDelta.ADDED :
558                         if (JavaBuilder.DEBUG)
559                             System.out.println("Compile this added source file " + typeLocator); //$NON-NLS-1$
560
sourceFiles.add(new SourceFile((IFile) resource, md, true));
561                         String JavaDoc typeName = typePath.toString();
562                         if (!newState.isDuplicateLocator(typeName, typeLocator)) { // adding dependents results in 2 duplicate errors
563
if (JavaBuilder.DEBUG)
564                                 System.out.println("Found added source file " + typeName); //$NON-NLS-1$
565
addDependentsOf(typePath, true);
566                         }
567                         return true;
568                     case IResourceDelta.REMOVED :
569                         char[][] definedTypeNames = newState.getDefinedTypeNamesFor(typeLocator);
570                         if (definedTypeNames == null) { // defined a single type matching typePath
571
removeClassFile(typePath, md.binaryFolder);
572                             if ((sourceDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
573                                 // remove problems and tasks for a compilation unit that is being moved (to another package or renamed)
574
// if the target file is a compilation unit, the new cu will be recompiled
575
// if the target file is a non-java resource, then markers are removed
576
// see bug 2857
577
IResource movedFile = javaBuilder.workspaceRoot.getFile(sourceDelta.getMovedToPath());
578                                 JavaBuilder.removeProblemsAndTasksFor(movedFile);
579                             }
580                         } else {
581                             if (JavaBuilder.DEBUG)
582                                 System.out.println("Found removed source file " + typePath.toString()); //$NON-NLS-1$
583
addDependentsOf(typePath, true); // add dependents of the source file since it may be involved in a name collision
584
if (definedTypeNames.length > 0) { // skip it if it failed to successfully define a type
585
IPath packagePath = typePath.removeLastSegments(1);
586                                 for (int i = 0, l = definedTypeNames.length; i < l; i++)
587                                     removeClassFile(packagePath.append(new String JavaDoc(definedTypeNames[i])), md.binaryFolder);
588                             }
589                         }
590                         newState.removeLocator(typeLocator);
591                         return true;
592                     case IResourceDelta.CHANGED :
593                         if ((sourceDelta.getFlags() & IResourceDelta.CONTENT) == 0
594                                 && (sourceDelta.getFlags() & IResourceDelta.ENCODING) == 0)
595                             return true; // skip it since it really isn't changed
596
if (JavaBuilder.DEBUG)
597                             System.out.println("Compile this changed source file " + typeLocator); //$NON-NLS-1$
598
sourceFiles.add(new SourceFile((IFile) resource, md, true));
599                 }
600                 return true;
601             } else if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(resourceName)) {
602                 // perform full build if a managed class file has been changed
603
if (this.makeOutputFolderConsistent) {
604                     IPath typePath = resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension();
605                     if (newState.isKnownType(typePath.toString())) {
606                         if (JavaBuilder.DEBUG)
607                             System.out.println("MUST DO FULL BUILD. Found change to class file " + typePath); //$NON-NLS-1$
608
return false;
609                     }
610                 }
611                 return true;
612             } else if (md.hasIndependentOutputFolder) {
613                 if (javaBuilder.filterExtraResource(resource)) return true;
614
615                 // copy all other resource deltas to the output folder
616
IPath resourcePath = resource.getFullPath().removeFirstSegments(segmentCount);
617                 IResource outputFile = md.binaryFolder.getFile(resourcePath);
618                 switch (sourceDelta.getKind()) {
619                     case IResourceDelta.ADDED :
620                         if (outputFile.exists()) {
621                             if (JavaBuilder.DEBUG)
622                                 System.out.println("Deleting existing file " + resourcePath); //$NON-NLS-1$
623
outputFile.delete(IResource.FORCE, null);
624                         }
625                         if (JavaBuilder.DEBUG)
626                             System.out.println("Copying added file " + resourcePath); //$NON-NLS-1$
627
createFolder(resourcePath.removeLastSegments(1), md.binaryFolder); // ensure package exists in the output folder
628
resource.copy(outputFile.getFullPath(), IResource.FORCE | IResource.DERIVED, null);
629                         Util.setReadOnly(outputFile, false); // just in case the original was read only
630
return true;
631                     case IResourceDelta.REMOVED :
632                         if (outputFile.exists()) {
633                             if (JavaBuilder.DEBUG)
634                                 System.out.println("Deleting removed file " + resourcePath); //$NON-NLS-1$
635
outputFile.delete(IResource.FORCE, null);
636                         }
637                         return true;
638                     case IResourceDelta.CHANGED :
639                         if ((sourceDelta.getFlags() & IResourceDelta.CONTENT) == 0
640                                 && (sourceDelta.getFlags() & IResourceDelta.ENCODING) == 0)
641                             return true; // skip it since it really isn't changed
642
if (outputFile.exists()) {
643                             if (JavaBuilder.DEBUG)
644                                 System.out.println("Deleting existing file " + resourcePath); //$NON-NLS-1$
645
outputFile.delete(IResource.FORCE, null);
646                         }
647                         if (JavaBuilder.DEBUG)
648                             System.out.println("Copying changed file " + resourcePath); //$NON-NLS-1$
649
createFolder(resourcePath.removeLastSegments(1), md.binaryFolder); // ensure package exists in the output folder
650
resource.copy(outputFile.getFullPath(), IResource.FORCE | IResource.DERIVED, null);
651                         Util.setReadOnly(outputFile, false); // just in case the original was read only
652
}
653                 return true;
654             }
655     }
656     return true;
657 }
658
659 protected void finishedWith(String JavaDoc sourceLocator, CompilationResult result, char[] mainTypeName, ArrayList definedTypeNames, ArrayList duplicateTypeNames) {
660     char[][] previousTypeNames = newState.getDefinedTypeNamesFor(sourceLocator);
661     if (previousTypeNames == null)
662         previousTypeNames = new char[][] {mainTypeName};
663     IPath packagePath = null;
664     next : for (int i = 0, l = previousTypeNames.length; i < l; i++) {
665         char[] previous = previousTypeNames[i];
666         for (int j = 0, m = definedTypeNames.size(); j < m; j++)
667             if (CharOperation.equals(previous, (char[]) definedTypeNames.get(j)))
668                 continue next;
669
670         SourceFile sourceFile = (SourceFile) result.getCompilationUnit();
671         if (packagePath == null) {
672             int count = sourceFile.sourceLocation.sourceFolder.getFullPath().segmentCount();
673             packagePath = sourceFile.resource.getFullPath().removeFirstSegments(count).removeLastSegments(1);
674         }
675         if (secondaryTypesToRemove == null)
676             this.secondaryTypesToRemove = new SimpleLookupTable();
677         ArrayList types = (ArrayList) secondaryTypesToRemove.get(sourceFile.sourceLocation.binaryFolder);
678         if (types == null)
679             types = new ArrayList(definedTypeNames.size());
680         types.add(packagePath.append(new String JavaDoc(previous)));
681         secondaryTypesToRemove.put(sourceFile.sourceLocation.binaryFolder, types);
682     }
683     super.finishedWith(sourceLocator, result, mainTypeName, definedTypeNames, duplicateTypeNames);
684 }
685
686 protected void processAnnotationResults(CompilationParticipantResult[] results) {
687     for (int i = results.length; --i >= 0;) {
688         CompilationParticipantResult result = results[i];
689         if (result == null) continue;
690
691         IFile[] deletedGeneratedFiles = result.deletedFiles;
692         if (deletedGeneratedFiles != null)
693             deleteGeneratedFiles(deletedGeneratedFiles);
694
695         IFile[] addedGeneratedFiles = result.addedFiles;
696         if (addedGeneratedFiles != null) {
697             for (int j = addedGeneratedFiles.length; --j >= 0;) {
698                 SourceFile sourceFile = findSourceFile(addedGeneratedFiles[j], true);
699                 if (sourceFile != null && !sourceFiles.contains(sourceFile))
700                     this.sourceFiles.add(sourceFile);
701             }
702         }
703
704         recordParticipantResult(result);
705     }
706 }
707
708 protected void removeClassFile(IPath typePath, IContainer outputFolder) throws CoreException {
709     if (typePath.lastSegment().indexOf('$') == -1) { // is not a nested type
710
newState.removeQualifiedTypeName(typePath.toString());
711         // add dependents even when the type thinks it does not exist to be on the safe side
712
if (JavaBuilder.DEBUG)
713             System.out.println("Found removed type " + typePath); //$NON-NLS-1$
714
addDependentsOf(typePath, true); // when member types are removed, their enclosing type is structurally changed
715
}
716     IFile classFile = outputFolder.getFile(typePath.addFileExtension(SuffixConstants.EXTENSION_class));
717     if (classFile.exists()) {
718         if (JavaBuilder.DEBUG)
719             System.out.println("Deleting class file of removed type " + typePath); //$NON-NLS-1$
720
classFile.delete(IResource.FORCE, null);
721     }
722 }
723
724 protected void removeSecondaryTypes() throws CoreException {
725     if (secondaryTypesToRemove != null) { // delayed deleting secondary types until the end of the compile loop
726
Object JavaDoc[] keyTable = secondaryTypesToRemove.keyTable;
727         Object JavaDoc[] valueTable = secondaryTypesToRemove.valueTable;
728         for (int i = 0, l = keyTable.length; i < l; i++) {
729             IContainer outputFolder = (IContainer) keyTable[i];
730             if (outputFolder != null) {
731                 ArrayList paths = (ArrayList) valueTable[i];
732                 for (int j = 0, m = paths.size(); j < m; j++)
733                     removeClassFile((IPath) paths.get(j), outputFolder);
734             }
735         }
736         this.secondaryTypesToRemove = null;
737         if (previousSourceFiles != null)
738             this.previousSourceFiles = null; // cannot optimize recompile case when a secondary type is deleted, see 181269
739
}
740 }
741
742 protected void resetCollections() {
743     if (this.sourceFiles == null) {
744         this.sourceFiles = new ArrayList(33);
745         this.previousSourceFiles = null;
746         this.qualifiedStrings = new StringSet(3);
747         this.simpleStrings = new StringSet(3);
748         this.hasStructuralChanges = false;
749         this.compileLoop = 0;
750     } else {
751         this.previousSourceFiles = this.sourceFiles.isEmpty() ? null : (ArrayList) this.sourceFiles.clone();
752
753         this.sourceFiles.clear();
754         this.qualifiedStrings.clear();
755         this.simpleStrings.clear();
756         this.workQueue.clear();
757     }
758 }
759
760 protected void updateProblemsFor(SourceFile sourceFile, CompilationResult result) throws CoreException {
761     IMarker[] markers = JavaBuilder.getProblemsFor(sourceFile.resource);
762     CategorizedProblem[] problems = result.getProblems();
763     if (problems == null && markers.length == 0) return;
764
765     notifier.updateProblemCounts(markers, problems);
766     JavaBuilder.removeProblemsFor(sourceFile.resource);
767     storeProblemsFor(sourceFile, problems);
768 }
769
770 protected void updateTasksFor(SourceFile sourceFile, CompilationResult result) throws CoreException {
771     IMarker[] markers = JavaBuilder.getTasksFor(sourceFile.resource);
772     CategorizedProblem[] tasks = result.getTasks();
773     if (tasks == null && markers.length == 0) return;
774
775     JavaBuilder.removeTasksFor(sourceFile.resource);
776     storeTasksFor(sourceFile, tasks);
777 }
778
779 protected void writeClassFileBytes(byte[] bytes, IFile file, String JavaDoc qualifiedFileName, boolean isTopLevelType, SourceFile compilationUnit) throws CoreException {
780     // Before writing out the class file, compare it to the previous file
781
// If structural changes occurred then add dependent source files
782
if (file.exists()) {
783         if (writeClassFileCheck(file, qualifiedFileName, bytes) || compilationUnit.updateClassFile) { // see 46093
784
if (JavaBuilder.DEBUG)
785                 System.out.println("Writing changed class file " + file.getName());//$NON-NLS-1$
786
if (!file.isDerived())
787                 file.setDerived(true);
788             file.setContents(new ByteArrayInputStream(bytes), true, false, null);
789         } else if (JavaBuilder.DEBUG) {
790             System.out.println("Skipped over unchanged class file " + file.getName());//$NON-NLS-1$
791
}
792     } else {
793         if (isTopLevelType)
794             addDependentsOf(new Path(qualifiedFileName), true); // new type
795
if (JavaBuilder.DEBUG)
796             System.out.println("Writing new class file " + file.getName());//$NON-NLS-1$
797
try {
798             file.create(new ByteArrayInputStream(bytes), IResource.FORCE | IResource.DERIVED, null);
799         } catch (CoreException e) {
800             if (e.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) {
801                 IStatus status = e.getStatus();
802                 if (status instanceof IResourceStatus) {
803                     IPath oldFilePath = ((IResourceStatus) status).getPath();
804                     char[] oldTypeName = oldFilePath.removeFileExtension().lastSegment().toCharArray();
805                     char[][] previousTypeNames = newState.getDefinedTypeNamesFor(compilationUnit.typeLocator());
806                     boolean fromSameFile = false;
807                     if (previousTypeNames == null) {
808                         fromSameFile = CharOperation.equals(compilationUnit.getMainTypeName(), oldTypeName);
809                     } else {
810                         for (int i = 0, l = previousTypeNames.length; i < l; i++) {
811                             if (CharOperation.equals(previousTypeNames[i], oldTypeName)) {
812                                 fromSameFile = true;
813                                 break;
814                             }
815                         }
816                     }
817                     if (fromSameFile) {
818                         // file is defined by the same compilationUnit, but won't be deleted until later so do it now
819
IFile collision = file.getParent().getFile(new Path(oldFilePath.lastSegment()));
820                         collision.delete(true, false, null);
821                         boolean success = false;
822                         try {
823                             file.create(new ByteArrayInputStream(bytes), IResource.FORCE | IResource.DERIVED, null);
824                             success = true;
825                         } catch (CoreException ignored) {
826                             // ignore the second exception
827
}
828                         if (success) return;
829                     }
830                 }
831                 // catch the case that a type has been renamed and collides on disk with an as-yet-to-be-deleted type
832
throw new AbortCompilation(true, new AbortIncrementalBuildException(qualifiedFileName));
833             }
834             throw e; // rethrow
835
}
836     }
837 }
838
839 protected boolean writeClassFileCheck(IFile file, String JavaDoc fileName, byte[] newBytes) throws CoreException {
840     try {
841         byte[] oldBytes = Util.getResourceContentsAsByteArray(file);
842         notEqual : if (newBytes.length == oldBytes.length) {
843             for (int i = newBytes.length; --i >= 0;)
844                 if (newBytes[i] != oldBytes[i]) break notEqual;
845             return false; // bytes are identical so skip them
846
}
847         URI JavaDoc location = file.getLocationURI();
848         if (location == null) return false; // unable to determine location of this class file
849
String JavaDoc filePath = location.getSchemeSpecificPart();
850         ClassFileReader reader = new ClassFileReader(oldBytes, filePath.toCharArray());
851         // ignore local types since they're only visible inside a single method
852
if (!(reader.isLocal() || reader.isAnonymous()) && reader.hasStructuralChanges(newBytes)) {
853             if (JavaBuilder.DEBUG)
854                 System.out.println("Type has structural changes " + fileName); //$NON-NLS-1$
855
addDependentsOf(new Path(fileName), true);
856             this.newState.wasStructurallyChanged(fileName);
857         }
858     } catch (ClassFormatException e) {
859         addDependentsOf(new Path(fileName), true);
860         this.newState.wasStructurallyChanged(fileName);
861     }
862     return true;
863 }
864
865 public String JavaDoc toString() {
866     return "incremental image builder for:\n\tnew state: " + newState; //$NON-NLS-1$
867
}
868
869
870 /* Debug helper
871
872 static void dump(IResourceDelta delta) {
873     StringBuffer buffer = new StringBuffer();
874     IPath path = delta.getFullPath();
875     for (int i = path.segmentCount(); --i > 0;)
876         buffer.append(" ");
877     switch (delta.getKind()) {
878         case IResourceDelta.ADDED:
879             buffer.append('+');
880             break;
881         case IResourceDelta.REMOVED:
882             buffer.append('-');
883             break;
884         case IResourceDelta.CHANGED:
885             buffer.append('*');
886             break;
887         case IResourceDelta.NO_CHANGE:
888             buffer.append('=');
889             break;
890         default:
891             buffer.append('?');
892             break;
893     }
894     buffer.append(path);
895     System.out.println(buffer.toString());
896     IResourceDelta[] children = delta.getAffectedChildren();
897     for (int i = 0, l = children.length; i < l; i++)
898         dump(children[i]);
899 }
900 */

901 }
902
Popular Tags