package org.eclipse.jdt.internal.core.builder.impl; | |
/* | |
* (c) Copyright IBM Corp. 2000, 2001. | |
* All Rights Reserved. | |
*/ | |
import org.eclipse.core.resources.*; | |
import org.eclipse.core.runtime.IPath; | |
import org.eclipse.jdt.internal.compiler.*; | |
import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; | |
import org.eclipse.jdt.internal.compiler.env.IBinaryType; | |
import org.eclipse.jdt.internal.compiler.env.IConstants; | |
import org.eclipse.jdt.core.ElementChangedEvent; | |
import org.eclipse.jdt.core.IClassFile; | |
import org.eclipse.jdt.core.IClasspathEntry; | |
import org.eclipse.jdt.core.IJavaProject; | |
import org.eclipse.jdt.core.ICompilationUnit; | |
import org.eclipse.jdt.core.IJavaElement; | |
import org.eclipse.jdt.core.IPackageFragmentRoot; | |
import org.eclipse.jdt.core.JavaCore; | |
import org.eclipse.jdt.core.JavaModelException; | |
import org.eclipse.jdt.internal.core.builder.*; | |
import org.eclipse.jdt.internal.core.builder.IType; | |
import org.eclipse.jdt.internal.core.builder.NotPresentException; | |
import org.eclipse.jdt.internal.core.lookup.ReferenceInfo; | |
import org.eclipse.jdt.internal.core.Util; | |
import org.eclipse.jdt.internal.compiler.util.*; | |
import org.eclipse.jdt.internal.core.*; | |
import java.util.*; | |
/** | |
* The incremental image builder | |
*/ | |
public class IncrementalImageBuilder extends AbstractImageBuilder { | |
protected IProject fNewProject; | |
protected IImageContext fImageContext; | |
/** | |
* The source deltas between old and new workspaces | |
* Maps from IProject to IResourceDelta for the project | |
*/ | |
protected Hashtable fSourceDeltas; | |
/** | |
* The image delta between old and new states | |
*/ | |
protected IDelta fImageDelta = null; | |
/** | |
* The image context of the last call to getImageDelta() | |
*/ | |
protected IImageContext fContextOfLastDelta = null; | |
/** | |
* Vector of IPath of source packages or zip files | |
* that have been added since old state. | |
* The path is relative to new state. | |
*/ | |
protected Vector fAddedPkgOrZips; | |
/** | |
* Vector of IPath of source packages or zip files | |
* that have been removed since old state. | |
* The path is relative of old state. | |
*/ | |
protected Vector fRemovedPkgOrZips; | |
/** | |
* Vector of ResourceDeltas representing changed source packages or zip files | |
* since old state. | |
*/ | |
protected Vector fChangedPkgOrZips; | |
/** | |
* Vector of IPackage handles of builder packages | |
* that have been added since old state. | |
*/ | |
protected Vector fAddedPackageHandles; | |
/** | |
* Vector of IPackage handles of builder packages | |
* that have been removed since old state. | |
*/ | |
protected Vector fRemovedPackageHandles; | |
/** | |
* Vector of IPackage handles of builder packages | |
* that have changes since old state. | |
*/ | |
protected Vector fChangedPackageHandles; | |
/** | |
* Set of affected IPackage: added, removed, directly changed or indirectly changed. | |
*/ | |
protected Hashtable fAffectedPackages; | |
/** | |
* Vector of SourceEntry representing added classes since old state. | |
*/ | |
protected Vector fAddedClasses; | |
/** | |
* Vector of SourceEntry representing removed classes since old state. | |
*/ | |
protected Vector fRemovedClasses; | |
/** | |
* Vector of SourceEntry representing changed classes since old state. | |
*/ | |
protected Vector fChangedClasses; | |
/** | |
* Vector of IPaths representing changed zips since old state. | |
*/ | |
protected Vector fChangedZips; | |
/** | |
* Table of IType -> BuilderType for types that are being compiled, | |
* so that old structure can be compared to new structure during | |
* indictment processing | |
*/ | |
protected Hashtable fBuilderTypeTable; | |
/** | |
* Creates a new incremental image builder on the given new workspace. | |
* The builder will build all classes that have changed since the old state, | |
* within the given image context. | |
*/ | |
protected IncrementalImageBuilder(StateImpl oldState, IProject newProject, IImageContext context) { | |
fDC = (JavaDevelopmentContextImpl) oldState.getDevelopmentContext(); | |
fOldState = oldState; | |
fNewProject = newProject; | |
fImageContext = context; | |
fBuilderTypeTable = new Hashtable(11); | |
} | |
/** | |
* Given an element delta for a changed package, add the package elements | |
* for changing elements to the table, keyed by package handle and file name. | |
* Added and removed elements are ignored. | |
*/ | |
protected void addChangedFileNamesFromChangedPackage(IResourceDelta pkgDelta, Hashtable table) { | |
IPackage[] pkgs = fNewState.getPathMap().packageHandlesFromPath(pkgDelta.getFullPath()); | |
for (int p = 0; p < pkgs.length; ++p) { | |
IPackage pkg = pkgs[p]; | |
Hashtable pkgTable = (Hashtable) table.get(pkg); | |
if (pkgTable == null) { | |
pkgTable = new Hashtable(11); | |
table.put(pkg, pkgTable); | |
} | |
IResourceDelta[] elementDeltas = pkgDelta.getAffectedChildren(IResourceDelta.CHANGED); | |
for (int i = 0; i < elementDeltas.length; ++i) { | |
IResourceDelta elementDelta = elementDeltas[i]; | |
// Only add if the contents are changing. | |
if ((elementDelta.getFlags() & IResourceDelta.CONTENT) != 0) { | |
IPath path = elementDelta.getFullPath(); | |
String extension = path.getFileExtension(); | |
if (extension != null) { | |
if (extension.equalsIgnoreCase("java"/*nonNLS*/) || extension.equalsIgnoreCase("class"/*nonNLS*/)) { | |
SourceEntry entry = new SourceEntry(path, null, null); | |
PackageElement element = new PackageElement(pkg, entry); | |
pkgTable.put(entry.getFileName(), element); | |
} | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Adds the new classes for this build to the state. | |
*/ | |
protected void addNewClasses() { | |
for (Enumeration e = fAddedClasses.elements(); e.hasMoreElements();) { | |
addSourceElement((SourceEntry) e.nextElement()); | |
} | |
} | |
/** | |
* Adds the given source element to the new state's tables | |
* and dependency graph. | |
*/ | |
protected void addSourceElement(SourceEntry newEntry) { | |
if (newEntry.isSource()) { | |
PackageElement element = fNewState.packageElementFromSourceEntry(newEntry); | |
IPackage pkg = element.getPackage(); | |
/* the addition must be in a package which is in the class path */ | |
Assert.isTrue(fNewState.getPackageMap().containsPackage(pkg)); | |
DependencyGraph graph = fNewState.getInternalDependencyGraph(); | |
graph.add(element); | |
fWorkQueue.add(element); | |
} | |
} | |
/** | |
* Applies the deltas to the old state to build the new state. | |
* The old state and new state have been set. | |
* The new state knows its workspace and build context. | |
* This is the method that actually does the incremental build | |
*/ | |
public void applySourceDelta(Hashtable deltas) { | |
fNotifier = new BuildNotifier(fDC, false); | |
fNotifier.begin(); | |
fNotifier.subTask(Util.bind("build.preparingBuild"/*nonNLS*/)); | |
fSourceDeltas = deltas; | |
fNewState = fOldState.copy(fNewProject, fImageContext); | |
// options might have changed since last builder run, thus refresh them | |
fCompilerOptions = JavaModelManager.convertConfigurableOptions(JavaCore.getOptions()); | |
fNewState.setCompilerOptions(fCompilerOptions); | |
try { | |
/* find out what has changed at the package level */ | |
fNotifier.subTask(Util.bind("build.analyzingPackages"/*nonNLS*/)); | |
computeAllPackages(); | |
checkCancel(); | |
/* update the package map */ | |
updatePackageMap(); | |
fNewState.canonicalizeBuildContext(); | |
fNotifier.updateProgressDelta(0.05f); | |
checkCancel(); | |
/* Update the source element table and namespace table for the removed and changed packages. | |
* The tables are simply deleted. They will be rebuilt lazily for changed packages. */ | |
for (Enumeration e = fRemovedPackageHandles.elements(); e.hasMoreElements();) { | |
IPackage pkgHandle = (IPackage) e.nextElement(); | |
fNewState.getSourceElementTable().removePackage(pkgHandle); | |
} | |
recomputeSourceEntriesForChangedPackages(); | |
checkCancel(); | |
fWorkQueue = new WorkQueue(); | |
/* rebuild the namespaces and issue indictments for changes */ | |
computeNamespaceChanges(); | |
// 1G220B5 - force compilation of all their dependents as well - only one level deeper | |
for (Enumeration e = fWorkQueue.getElementsToCompile().elements(); e.hasMoreElements();) { | |
markDependentsAsNeedingCompile(e.nextElement()); | |
} | |
/* find out what has changed at the package element level */ | |
fNotifier.subTask(Util.bind("build.analyzingSources"/*nonNLS*/)); | |
computeAllClasses(); | |
checkCancel(); | |
/* All dependents of changed zips will need compiling */ | |
markDependentsOfChangedZips(); | |
/* remove old classes and get affected JCUs */ | |
removeOldClasses(); | |
checkCancel(); | |
/* flag changed classes and get compilation units to compile */ | |
updateChangedClasses(); | |
checkCancel(); | |
/* adding new classes might hide (equivalent to delete) old classes */ | |
addNewClasses(); | |
checkCancel(); | |
float amountPerIteration = 0.60f; // Approximation of n + (n/4) + (n/16) + ... = 0.85 | |
/* keep compiling until there is nothing left to compile */ | |
Vector vToCompile = fWorkQueue.getElementsToCompile(); | |
while (vToCompile.size() != 0) { | |
fNotifier.setProgressPerCompilationUnit(amountPerIteration / vToCompile.size()); | |
compile(vToCompile); | |
vToCompile = fWorkQueue.getElementsToCompile(); | |
amountPerIteration *= 0.25f; | |
} | |
// not using PrincipalStructureByPackageTable | |
// propagatePrincipalStructureByPackageTable(); | |
// Force all in build context | |
/* | |
Don't force -- we're not doing lazy builds. | |
if (fAddedPackageHandles.size() > 0 || fChangedPackageHandles.size() > 0) { | |
for (int i = 0; i < fAddedPackageHandles.size(); ++i) { | |
IPackage pkg = (IPackage) fAddedPackageHandles.elementAt(i); | |
maybeForce(pkg); | |
} | |
for (int i = 0; i < fChangedPackageHandles.size(); ++i) { | |
IPackage pkg = (IPackage) fChangedPackageHandles.elementAt(i); | |
maybeForce(pkg); | |
} | |
} | |
*/ | |
/* Update resources in binary output */ | |
IResourceDelta projectDelta = (IResourceDelta) deltas.get(fNewProject); | |
if (projectDelta != null) { | |
ProjectResourceCopier copier = new ProjectResourceCopier(fNewState.getJavaProject(), fDC, fNotifier, 0.10f); | |
copier.updateAffectedResources(projectDelta); | |
} | |
/* Removals and recompilations can leave unused namespace nodes in the | |
* dependency graph. Clean them up. */ | |
cleanupUnusedNamespaceNodes(); | |
checkCancel(); | |
/* Copy resource to binary output */ | |
//copyResources(projectDelta, 0.05f); | |
fNotifier.done(); | |
} finally { | |
cleanUp(); | |
} | |
} | |
/** | |
* Applies the delta to the old state | |
* to build the new state. The old state and new state have been set. | |
* The new state knows its workspace and build context. | |
* This is the method that actually does the incremental build | |
*/ | |
public void applySourceDelta(IResourceDelta projectDelta) { | |
Hashtable deltas = new Hashtable(11); | |
deltas.put(fNewProject, projectDelta); | |
applySourceDelta(deltas); | |
} | |
/** | |
* The given source element has changed and will be compiled. | |
* Flag the node in the dependency graph, and store all compilation | |
* units that will need compiling as a result of the change. | |
*/ | |
protected void changedSourceElement(SourceEntry newEntry) { | |
PackageElement element = fNewState.packageElementFromSourceEntry(newEntry); | |
if (element.isSource()) { | |
fWorkQueue.add(element); | |
} | |
/* remove problems for this source entry */ | |
SourceEntry oldEntry = fOldState.getSourceEntry(element); | |
fNewState.getProblemReporter().removeProblems(oldEntry); | |
} | |
/** | |
* Since the image builder is given as a result, let go of | |
* any unneeded structures. | |
*/ | |
protected void cleanUp() { | |
super.cleanUp(); | |
// Don't clear package level information because it's needed to compute the image delta. | |
fSourceDeltas = null; | |
fAddedClasses = fRemovedClasses = fChangedClasses = null; | |
} | |
/** | |
* Removals and recompilations can leave unused namespace nodes in the | |
* dependency graph. Clean them up. | |
*/ | |
protected void cleanupUnusedNamespaceNodes() { | |
if (fRemovedClasses.size() > 0 || fChangedClasses.size() > 0) { | |
PackageMap packageMap = fNewState.getPackageMap(); | |
DependencyGraph graph = fNewState.getInternalDependencyGraph(); | |
Vector unused = graph.getUnusedNamespaceNodes(); | |
for (Enumeration e = unused.elements(); e.hasMoreElements();) { | |
NamespaceNode node = (NamespaceNode) e.nextElement(); | |
IPackage pkg = node.getPackage(); | |
if (!packageMap.containsPackage(pkg)) { | |
graph.removePackage(pkg); | |
} | |
} | |
} | |
} | |
/** | |
* Compare the visibility and gender of the two types. | |
* Returns true if equal, false if not. | |
*/ | |
protected boolean compareVisibilityAndGender(TypeStructureEntry tsEntry, org.eclipse.jdt.core.IType type) throws JavaModelException { | |
try { | |
/* This relies on visibility bits being the same between the image builder and the java model. */ | |
IType oldType = (IType) tsEntry.getType().inState(fNewState); | |
final int visVlags = IConstants.AccPublic | IConstants.AccPrivate | IConstants.AccProtected; | |
int oldVis = oldType.getModifiers() & visVlags; | |
int newVis = type.getFlags() & visVlags; | |
return oldVis == newVis && oldType.isInterface() == type.isInterface(); | |
} | |
catch (NotPresentException e) { | |
// Old state may be missing. See 1FVQGL1: ITPJCORE:WINNT - SEVERE - Error saving java file | |
return false; | |
} | |
} | |
/** | |
* A unit is being (re)compiled. Save any previous type structure. | |
*/ | |
protected void compiling(CompilerCompilationUnit unit) { | |
/* Save old binaries if they exist */ | |
SourceEntry sEntry = unit.getSourceEntry(); | |
PackageElement element = fNewState.packageElementFromSourceEntry(sEntry); | |
if (fOldState.getSourceEntry(element) != null) { | |
saveBinaryTypes(element); | |
} | |
} | |
/** | |
* Computes the added, removed, changed classes for this incremental build. | |
* Vectors contain SourceEntry objects for each source element being added, | |
* removed, or changed. | |
* | |
* Takes into account package fragments, and only yields elements which are visible | |
* | |
* It's important to do this computation using the computed namespaces rather than | |
* directly from the source element tables, since types may be removed during namespace | |
* computation. | |
*/ | |
protected void computeAllClasses() { | |
fAddedClasses = new Vector(); | |
fRemovedClasses = new Vector(); | |
fChangedClasses = new Vector(); | |
fChangedZips = new Vector(1); | |
/* do for each added builder package */ | |
for (Enumeration e = fAddedPackageHandles.elements(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
SourceEntry[] entries = fNewState.getSourceEntries(pkg); | |
if (entries != null) { | |
for (int i = 0; i < entries.length; ++i) { | |
fAddedClasses.addElement(entries[i]); | |
} | |
} | |
} | |
/* do for each removed builder package */ | |
for (Enumeration e = fRemovedPackageHandles.elements(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
// Don't force the package's table. | |
// If the package's table was not forced in the old state, | |
// there should be no work to do for its classes. | |
if (fOldState.getSourceElementTable().containsPackage(pkg)) { | |
SourceEntry[] entries = fOldState.getSourceEntries(pkg); | |
if (entries != null) { | |
for (int i = 0; i < entries.length; ++i) { | |
fRemovedClasses.addElement(entries[i]); | |
} | |
} | |
} | |
} | |
/* build table of changed package elements, keyed by package */ | |
Hashtable changeTable = new Hashtable(fChangedPkgOrZips.size() * 2 + 1); | |
for (Enumeration e = fChangedPkgOrZips.elements(); e.hasMoreElements();) { | |
IResourceDelta changedPkgOrZip = (IResourceDelta) e.nextElement(); | |
IPath path = changedPkgOrZip.getFullPath(); | |
// ask the state if it is a ZIP file only if it is present in this state | |
if (fNewState.isZipElement(path)) { | |
/** | |
* Don't do any finer grained change calculation, | |
* all dependents of the zip and its namespaces | |
* will be recompiled | |
*/ | |
fChangedZips.addElement(path); | |
} else { | |
addChangedFileNamesFromChangedPackage(changedPkgOrZip, changeTable); | |
} | |
} | |
/* do for each changed builder package */ | |
for (Enumeration e = fChangedPackageHandles.elements(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
Hashtable changesForPkg = (Hashtable) changeTable.get(pkg); | |
Hashtable fileNames = new Hashtable(); | |
SourceEntry[] oldEntries = fOldState.getSourceEntries(pkg); | |
if (oldEntries != null) { | |
for (int i = 0; i < oldEntries.length; ++i) { | |
SourceEntry oldEntry = oldEntries[i]; | |
fileNames.put(oldEntry.getFileName(), oldEntry); | |
} | |
} | |
SourceEntry[] newEntries = fNewState.getSourceEntries(pkg); | |
if (newEntries != null) { | |
for (int i = 0; i < newEntries.length; ++i) { | |
SourceEntry newEntry = newEntries[i]; | |
String fileName = newEntry.getFileName(); | |
SourceEntry oldEntry = (SourceEntry) fileNames.remove(fileName); | |
if (oldEntry != null) { | |
// Present in old and new. Has it changed? | |
if (!newEntry.equals(oldEntry)) { | |
// It has changed path, so treat it as a removal and an addition | |
fRemovedClasses.addElement(oldEntry); | |
fAddedClasses.addElement(newEntry); | |
} | |
else if (changesForPkg != null && changesForPkg.containsKey(fileName)) { | |
fChangedClasses.addElement(newEntry); | |
} | |
} else { | |
// Present only in new | |
fAddedClasses.addElement(newEntry); | |
} | |
} | |
} | |
// Remaining ones are removed. | |
for (Enumeration ee = fileNames.elements(); ee.hasMoreElements();) { | |
fRemovedClasses.addElement(ee.nextElement()); | |
} | |
} | |
} | |
/** | |
* Computes the added, removed, changed packages and zips | |
* for this incremental build. | |
* Looks only in the union of the old and the new classpaths. | |
* Folders outside this union are ignored. | |
*/ | |
protected void computeAllPackages() { | |
fAddedPkgOrZips = new Vector(); | |
fRemovedPkgOrZips = new Vector(); | |
fChangedPkgOrZips = new Vector(); | |
IPackageFragmentRoot[] oldRoots = fOldState.getPackageFragmentRootsInClassPath(); | |
fNewState.readClassPath(); // TBD: Only read it if changed. | |
IPackageFragmentRoot[] newRoots = fNewState.getPackageFragmentRootsInClassPath(); | |
for (Enumeration e = fSourceDeltas.elements(); e.hasMoreElements();) { | |
IResourceDelta delta = (IResourceDelta) e.nextElement(); | |
computeAllPackages(delta, oldRoots, newRoots); | |
} | |
} | |
protected void computeAllPackages(IResourceDelta delta, IPackageFragmentRoot[] oldRoots, IPackageFragmentRoot[] newRoots) { | |
int status = delta.getKind(); | |
IPath path = delta.getFullPath(); | |
IResource rootResource = null; | |
switch (delta.getKind()) { | |
case IResourceDelta.ADDED : | |
/* Look for this package only in the new roots */ | |
for (int i = 0; i < newRoots.length; i++) { | |
rootResource = null; | |
try { | |
rootResource = newRoots[i].getUnderlyingResource(); | |
} catch (JavaModelException e) { | |
} | |
if (rootResource != null && rootResource.getFullPath().isPrefixOf(path)) { | |
fAddedPkgOrZips.addElement(path); | |
break; | |
} | |
} | |
break; | |
case IResourceDelta.REMOVED : | |
/* Look for this package only in the old roots */ | |
for (int i = 0; i < oldRoots.length; i++) { | |
rootResource = null; | |
try { | |
rootResource = oldRoots[i].getUnderlyingResource(); | |
} catch (JavaModelException e) { | |
} | |
if (rootResource != null && rootResource.getFullPath().isPrefixOf(path)) { | |
fRemovedPkgOrZips.addElement(path); | |
break; | |
} | |
} | |
break; | |
case IResourceDelta.CHANGED : | |
/* Look for this package in the union of both sets of roots */ | |
boolean found = false; | |
for (int i = 0; i < newRoots.length; i++) { | |
rootResource = null; | |
try { | |
rootResource = newRoots[i].getUnderlyingResource(); | |
} catch (JavaModelException e) { | |
} | |
if (rootResource != null && rootResource.getFullPath().isPrefixOf(path)) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
for (int i = 0; i < oldRoots.length; i++) { | |
rootResource = null; | |
try { | |
oldRoots[i].getUnderlyingResource(); | |
} catch (JavaModelException e) { | |
} | |
if (rootResource != null && rootResource.getFullPath().isPrefixOf(path)) { | |
found = true; | |
break; | |
} | |
} | |
} | |
if (found) { | |
/* Only include changes if it's not an archive, or if it's an archive and the contents really changed */ | |
if (!fNewState.isZipElement(path) | |
|| (delta.getFlags() & IResourceDelta.CONTENT) != 0) { | |
fChangedPkgOrZips.addElement(delta); | |
} | |
} | |
break; | |
} | |
IResourceDelta[] children = delta.getAffectedChildren(); | |
for (int i = 0; i < children.length; ++i) { | |
String extension = children[i].getFullPath().getFileExtension(); | |
if (extension == null | |
|| extension.equalsIgnoreCase("zip"/*nonNLS*/) | |
|| extension.equalsIgnoreCase("jar"/*nonNLS*/)) { | |
// TBD: Currently rely on empty extension indicating folder | |
computeAllPackages(children[i], oldRoots, newRoots); | |
} | |
} | |
} | |
/** | |
* Computes namespace changes for each added, removed and changed class file or JCU. | |
* The appropriate namespace node is informed of the changes, and it | |
* may invalidate its dependents where necessary. JCUs that need compiling | |
* as a result of invalidations are stored by the state. | |
* Must process removed types here, even though in most cases there will already | |
* be an explicit dependency on the removed type, because it is possible for others | |
* to have a namespace dependency but not a type dependency (e.g. in the case of errors). | |
*/ | |
protected void computeNamespaceChanges() { | |
for (Enumeration e = fSourceDeltas.elements(); e.hasMoreElements();) { | |
IResourceDelta delta = (IResourceDelta) e.nextElement(); | |
JavaModelManager.getJavaModelManager().closeAffectedElements(delta); | |
} | |
// Should really only process packages in image context here, | |
// but in general other packages may depend on namespaces being added, | |
// not just those being removed and changed. So for now, process everything. | |
// The computations here must be based on the computed namespaces rather than | |
// directly off of the namespace contributions of affected source elements | |
// since the namespace computation may remove items due to conflicts. | |
int numPackages = fAddedPackageHandles.size() + fRemovedPackageHandles.size() + fChangedPackageHandles.size(); | |
if (numPackages == 0) { | |
fNotifier.updateProgressDelta(0.10f); | |
return; | |
} | |
float progressDelta = 0.10f / numPackages; | |
// Process changes in the set of package prefixes | |
if (fAddedPackageHandles.size() > 0 || fRemovedPackageHandles.size() > 0) { | |
computePackagePrefixChanges(); | |
} | |
// Process added packages | |
for (Enumeration addedPkgs = fAddedPackageHandles.elements(); addedPkgs.hasMoreElements();) { | |
IPackage pkg = (IPackage) addedPkgs.nextElement(); | |
fNotifier.subTask(Util.bind("build.analyzing"/*nonNLS*/, PackageImpl.readableName(pkg))); | |
// Mark all dependents of missing namespace as needing compile. | |
markDependentsAsNeedingCompile(pkg); | |
// If any types currently exist with the same name as this package, | |
// they must be recompiled | |
markOverlappingTypesAsNeedingCompile(pkg); | |
fNotifier.updateProgressDelta(progressDelta); | |
fNotifier.checkCancel(); | |
} | |
// Process removed packages | |
for (Enumeration removedPkgs = fRemovedPackageHandles.elements(); removedPkgs.hasMoreElements();) { | |
IPackage pkg = (IPackage) removedPkgs.nextElement(); | |
fNotifier.subTask(Util.bind("build.analyzing"/*nonNLS*/, PackageImpl.readableName(pkg))); | |
// Mark all dependents of namespace as needing compile. | |
markDependentsAsNeedingCompile(pkg); | |
// If any types currently exist with the same name as this package, | |
// they must be recompiled | |
markOverlappingTypesAsNeedingCompile(pkg); | |
fNotifier.updateProgressDelta(progressDelta); | |
fNotifier.checkCancel(); | |
} | |
// Process changed packages | |
for (Enumeration changedPkgs = fChangedPackageHandles.elements(); changedPkgs.hasMoreElements();) { | |
IPackage pkg = (IPackage) changedPkgs.nextElement(); | |
fNotifier.subTask(Util.bind("build.analyzing"/*nonNLS*/, PackageImpl.readableName(pkg))); | |
computeNamespaceChanges(pkg); | |
fNotifier.updateProgressDelta(progressDelta); | |
fNotifier.checkCancel(); | |
} | |
} | |
/** | |
* Computes the names with namespace changes for the given type. | |
*/ | |
protected void computeNamespaceChanges(Hashtable oldTSEntries, String parentTypeName, org.eclipse.jdt.core.IType type, Vector vTypeNames) throws JavaModelException { | |
String typeName = type.getElementName(); | |
if (parentTypeName != null) { | |
int len = parentTypeName.length() + typeName.length() + 1; | |
typeName = new StringBuffer(len).append(parentTypeName).append("$"/*nonNLS*/).append(typeName).toString(); | |
} | |
/* Remove it so that only non-matching ones remain in the table. */ | |
TypeStructureEntry tsEntry = (TypeStructureEntry) oldTSEntries.remove(typeName); | |
if (tsEntry == null || !compareVisibilityAndGender(tsEntry, type)) { | |
vTypeNames.addElement(typeName); | |
} | |
org.eclipse.jdt.core.IType[] memberTypes = type.getTypes(); | |
for (int i = 0; i < memberTypes.length; ++i) { | |
computeNamespaceChanges(oldTSEntries, typeName, memberTypes[i], vTypeNames); | |
} | |
} | |
/** | |
* Computes the names with namespace changes for the given element. | |
* Handles the cases where the element is added, removed, or changed. | |
*/ | |
protected void computeNamespaceChanges(PackageElement element, Vector vTypeNames) { | |
Hashtable oldTSEntries = new Hashtable(5); | |
SourceEntry oldSourceEntry = fOldState.getSourceEntry(element); | |
if (oldSourceEntry != null) { | |
// Ignore entries from zip files, since zip file changes are handled wholesale elsewhere. | |
if (oldSourceEntry.getZipEntryName() == null) { | |
org.eclipse.jdt.internal.core.builder.IType[] oldTypes = fOldState.getInternalDependencyGraph().getTypes(element); | |
if (oldTypes != null) { | |
for (int i = 0; i < oldTypes.length; ++i) { | |
TypeStructureEntry tsEntry = fOldState.getTypeStructureEntry(oldTypes[i], false); | |
if (tsEntry != null) { | |
oldTSEntries.put(tsEntry.getType().getSimpleName(), tsEntry); | |
} | |
} | |
} | |
} | |
} | |
SourceEntry newSourceEntry = fNewState.getSourceEntry(element); | |
if (newSourceEntry != null) { | |
// Ignore entries from zip files, since zip file changes are handled wholesale elsewhere. | |
if (oldSourceEntry.getZipEntryName() == null) { | |
/* use this if JavaModel is broken | |
String fileName = element.getFileName(); | |
fileName = fileName.substring(0, fileName.indexOf('.')); | |
FakeType[] types = new FakeType[] {new FakeType(fileName)}; | |
try { | |
computeNamespaceChanges(oldTSEntries, null, types[0], vTypeNames); | |
} catch (NotPresentException e) {} // ignore | |
*/ | |
IJavaElement javaElement = fNewState.getJavaElement(newSourceEntry); | |
if (javaElement instanceof ICompilationUnit) { | |
ICompilationUnit unit = (ICompilationUnit) javaElement; | |
try { | |
org.eclipse.jdt.core.IType[] types = unit.getTypes(); | |
for (int i = 0; i < types.length; ++i) { | |
computeNamespaceChanges(oldTSEntries, null, types[i], vTypeNames); | |
} | |
} catch (JavaModelException e) { | |
// TBD: ignore | |
} | |
} else { | |
if (javaElement instanceof IClassFile) { | |
IClassFile classFile = (IClassFile) javaElement; | |
try { | |
computeNamespaceChanges(oldTSEntries, null, classFile.getType(), vTypeNames); | |
} catch (JavaModelException e) { | |
// TBD: ignore | |
} | |
} | |
} | |
} | |
} | |
for (Enumeration e = oldTSEntries.keys(); e.hasMoreElements();) { | |
String name = (String) e.nextElement(); | |
vTypeNames.addElement(name); | |
} | |
} | |
/** | |
* Computes namespace changes for each added, removed and changed class file or JCU | |
* in an affected package. | |
*/ | |
protected void computeNamespaceChanges(IPackage pkg) { | |
/** | |
* Must remove syntax problems for all source entries in this package | |
* in the old state, regardless of whether they contributed to the old | |
* state's namespace. This must be done before computing the new namespace | |
* because the computation may reveal new errors that we don't want to remove. | |
*/ | |
Hashtable oldTable = new Hashtable(21); | |
SourceEntry[] oldEntries = fOldState.getSourceEntries(pkg); | |
if (oldEntries != null) { | |
for (int i = 0; i < oldEntries.length; i++) { | |
SourceEntry oldEntry = oldEntries[i]; | |
fNewState.getProblemReporter().removeSyntaxErrors(oldEntry); | |
oldTable.put(oldEntry.getFileName(), oldEntry); | |
} | |
} | |
Vector vTypeNames = new Vector(); | |
SourceEntry[] newEntries = fNewState.getSourceEntries(pkg); | |
if (newEntries != null) { | |
Dictionary sourceChanges = getSourceChanges(pkg); | |
for (int i = 0; i < newEntries.length; ++i) { | |
SourceEntry newEntry = newEntries[i]; | |
SourceEntry oldEntry = (SourceEntry) oldTable.remove(newEntry.getFileName()); | |
if (oldEntry == null) { | |
/* Added. Issue indictment based only on file name. */ | |
vTypeNames.addElement(newEntry.getName()); | |
} else { | |
if (!oldEntry.equals(newEntry) || sourceChanges.get(newEntry.getPath()) != null) { | |
/* Changed. Issue indictments by comparing source types with previously built types. */ | |
PackageElement element = new PackageElement(pkg, newEntry); | |
computeNamespaceChanges(element, vTypeNames); | |
} | |
} | |
} | |
} | |
/* Only removed source entries should remain in oldTable now. */ | |
for (Enumeration e = oldTable.elements(); e.hasMoreElements();) { | |
SourceEntry oldEntry = (SourceEntry) e.nextElement(); | |
/* Removed. Issue indictment based only on file name. */ | |
vTypeNames.addElement(oldEntry.getName()); | |
} | |
if (vTypeNames.isEmpty()) { | |
return; | |
} | |
IndictmentSet indicts = new IndictmentSet(); | |
Hashtable nestedIndictsTable = null; | |
for (Enumeration e = vTypeNames.elements(); e.hasMoreElements();) { | |
String name = (String) e.nextElement(); | |
int lastDollar = name.lastIndexOf('$'); | |
if (lastDollar == -1) { | |
indicts.add(Indictment.createTypeIndictment(name)); | |
} else { | |
// Nested type. Issue indictments as if containing type was a package. | |
// Dependencies on missing member types look like namespace dependencies on | |
// package with same name as enclosing type. | |
String qualification = name.substring(0, lastDollar); | |
// Convert qualification from $ separated to . separated. | |
// For example if name = "A$B$C", typeName = "C" and qualification = "A.B". | |
qualification = qualification.replace('$', '.'); | |
String typeName = name.substring(lastDollar + 1); | |
// Issue indictments, not relative to current package. | |
// This catches dependencies on missing types in same package (e.g. ref is A.B). | |
IPackage nestedPkg = fDC.getImage().getPackageHandle(qualification, false); | |
nestedPkg = fNewState.canonicalize(nestedPkg); | |
if (nestedIndictsTable == null) { | |
nestedIndictsTable = new Hashtable(11); | |
} | |
IndictmentSet nestedIndicts = (IndictmentSet) nestedIndictsTable.get(nestedPkg); | |
if (nestedIndicts == null) { | |
nestedIndicts = new IndictmentSet(); | |
nestedIndictsTable.put(nestedPkg, nestedIndicts); | |
} | |
nestedIndicts.add(Indictment.createTypeIndictment(typeName)); | |
// Issue indictments, relative to current package (only if not unnamed package). | |
// This catches dependencies on missing types in other packages (e.g. ref is some.other.pkg.A.B). | |
if (!pkg.isUnnamed()) { | |
nestedPkg = fDC.getImage().getPackageHandle(pkg.getName() + '.' + qualification, false); | |
if (nestedIndictsTable == null) { | |
nestedIndictsTable = new Hashtable(11); | |
} | |
nestedIndicts = (IndictmentSet) nestedIndictsTable.get(nestedPkg); | |
if (nestedIndicts == null) { | |
nestedIndicts = new IndictmentSet(); | |
nestedIndictsTable.put(nestedPkg, nestedIndicts); | |
} | |
nestedIndicts.add(Indictment.createTypeIndictment(typeName)); | |
} | |
} | |
} | |
if (!indicts.isEmpty()) { | |
issueIndictments(pkg, indicts, false); | |
} | |
if (nestedIndictsTable != null) { | |
for (Enumeration e = nestedIndictsTable.keys(); e.hasMoreElements();) { | |
IPackage nestedPkg = (IPackage) e.nextElement(); | |
IndictmentSet nestedIndicts = (IndictmentSet) nestedIndictsTable.get(nestedPkg); | |
issueIndictments(pkg, nestedIndicts, false); | |
} | |
} | |
} | |
/** | |
* Compute the added and removed package prefixes (not actual packages) | |
* and issue the appropriate indictments on the namespaces. | |
*/ | |
protected void computePackagePrefixChanges() { | |
Hashtable oldTable = computePackagePrefixes(fOldState.getPackageMap()); | |
Hashtable newTable = computePackagePrefixes(fNewState.getPackageMap()); | |
for (Enumeration e = oldTable.elements(); e.hasMoreElements();) { | |
String name = (String) e.nextElement(); | |
if (newTable.remove(name) == null) { | |
int lastDot = name.lastIndexOf('.'); | |
if (lastDot != -1) { | |
String parentName = name.substring(0, lastDot); | |
String simpleName = name.substring(lastDot + 1); | |
IPackage parentPkg = fDC.getImage().getPackageHandle(parentName, false); | |
IndictmentSet indictments = new IndictmentSet(); | |
indictments.add(Indictment.createTypeIndictment(simpleName)); | |
issueIndictments(parentPkg, indictments, false); | |
} | |
} | |
} | |
for (Enumeration e = newTable.elements(); e.hasMoreElements();) { | |
String name = (String) e.nextElement(); | |
int lastDot = name.lastIndexOf('.'); | |
if (lastDot != -1) { | |
String parentName = name.substring(0, lastDot); | |
String simpleName = name.substring(lastDot + 1); | |
IPackage parentPkg = fDC.getImage().getPackageHandle(parentName, false); | |
IndictmentSet indictments = new IndictmentSet(); | |
indictments.add(Indictment.createTypeIndictment(simpleName)); | |
issueIndictments(parentPkg, indictments, false); | |
} | |
} | |
} | |
/** | |
* Returns a set of strings containing the names of all packages | |
* in the image, and all their prefixes. | |
*/ | |
protected Hashtable computePackagePrefixes(PackageMap packageMap) { | |
Hashtable prefixes = new Hashtable(packageMap.size() * 3 + 1); | |
for (Enumeration e = packageMap.getAllPackages(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
if (!pkg.isUnnamed()) { | |
String name = pkg.getName(); | |
while (!prefixes.containsKey(name)) { | |
prefixes.put(name, name); | |
int i = name.lastIndexOf('.'); | |
if (i == -1) | |
break; | |
name = name.substring(0, i); | |
} | |
} | |
} | |
return prefixes; | |
} | |
/** | |
* For debugging only. | |
*/ | |
static void dump(IResourceDelta delta) { | |
StringBuffer sb = new StringBuffer(); | |
IPath path = delta.getFullPath(); | |
for (int i = path.segmentCount(); --i > 0;) { | |
sb.append(" "/*nonNLS*/); | |
} | |
switch (delta.getKind()) { | |
case IResourceDelta.ADDED: | |
sb.append('+'); | |
break; | |
case IResourceDelta.REMOVED: | |
sb.append('-'); | |
break; | |
case IResourceDelta.CHANGED: | |
sb.append('*'); | |
break; | |
case IResourceDelta.NO_CHANGE: | |
sb.append('='); | |
break; | |
default: | |
sb.append('?'); | |
break; | |
} | |
sb.append(path); | |
System.out.println(sb.toString()); | |
IResourceDelta[] children = delta.getAffectedChildren(); | |
for (int i = 0; i < children.length; ++i) { | |
dump(children[i]); | |
} | |
} | |
/** | |
* Returns an enumeration of the packages that have been affected | |
* by the build. | |
*/ | |
protected Enumeration getAffectedPackages() { | |
return fAffectedPackages.keys(); | |
} | |
/** | |
* Returns a builder type for the given old and new type structure entries. | |
* Either old or new entries may be null (but not both). | |
* This method should only be called if there is no associated builder | |
* type already in the builder type table. | |
*/ | |
protected BuilderType getBuilderType(TypeStructureEntry oldEntry, TypeStructureEntry newEntry) { | |
IType handle = null; | |
BuilderType type = null; | |
if (oldEntry == null) { | |
/* must have been added */ | |
Assert.isNotNull(newEntry); | |
type = new NewBuilderType(this, newEntry); | |
handle = newEntry.getType(); | |
} else { | |
if (newEntry == null) { | |
/* must have been deleted */ | |
Assert.isNotNull(oldEntry); | |
type = new OldBuilderType(this, oldEntry); | |
handle = oldEntry.getType(); | |
} else { | |
if (oldEntry.fSourceEntry.equals(newEntry.fSourceEntry)) { | |
/* unchanged */ | |
type = new UnmodifiedBuilderType(this, oldEntry); | |
handle = oldEntry.getType(); | |
} else { | |
/* modified */ | |
IBinaryType oldBinary = fOldState.getBinaryTypeOrNull(oldEntry); | |
type = new ModifiedBuilderType(this, oldEntry, oldBinary); | |
handle = oldEntry.getType(); | |
} | |
} | |
} | |
fBuilderTypeTable.put(handle, type); | |
return type; | |
} | |
/** | |
* Returns a builder type for the given type. | |
*/ | |
protected BuilderType getBuilderType(IType type) { | |
BuilderType builderType = (BuilderType) fBuilderTypeTable.get(type); | |
if (builderType != null) { | |
return builderType; | |
} | |
return getBuilderType(fOldState.getTypeStructureEntry(type, false), fNewState.getTypeStructureEntry(type, false)); | |
} | |
/** | |
* Returns an object describing the differences between the old | |
* state and the new state, otherwise returns null. The delta is | |
* restricted to the given ImageContext. This image delta will | |
* include entries for all program elements that are present in: | |
* <pre> | |
* (oldState UNION newState) INTERSECT imageContext | |
*</pre> | |
* That is, it will include each program element that is present in one or the other | |
* state and also in the given image context. | |
* Any delta objects navigated to from the result are restricted | |
* to the same ImageContext. | |
* Note that there is no necessary relationship between the image context | |
* supplied and the build contexts of the old and new states. | |
*/ | |
public IDelta getImageDelta(IImageContext imageContext) { | |
if (fImageDelta == null || !Util.equalOrNull(imageContext, fContextOfLastDelta)) { | |
fImageDelta = new DeltaImpl(this, imageContext); | |
fContextOfLastDelta = imageContext; | |
} | |
return fImageDelta; | |
} | |
/** | |
* Returns a set of paths of files which are changing in the given package. | |
*/ | |
protected Dictionary getSourceChanges(IPackage pkgHandle) { | |
Dictionary set = new Hashtable(30); | |
/* do for each fragment of this package */ | |
IPath[] newFrags = fNewState.getPackageMap().getFragments(pkgHandle); | |
for (int i = 0; i < newFrags.length; i++) { | |
IPath fragPath = newFrags[i]; | |
/* find the JCUs for this package */ | |
IResourceDelta[] fileDeltas = null; | |
for (Enumeration e = fChangedPkgOrZips.elements(); e.hasMoreElements();) { | |
IResourceDelta pkgDelta = (IResourceDelta) e.nextElement(); | |
if (pkgDelta.getFullPath().equals(fragPath)) { | |
/* A zip file is changing. Don't bother optimizing this case -- too complex. */ | |
if (!fNewState.isZipElement(fragPath)) { | |
fileDeltas = pkgDelta.getAffectedChildren(IResourceDelta.CHANGED); | |
} | |
break; | |
} | |
} | |
if (fileDeltas != null) { | |
/* do for each changed file in this fragment */ | |
for (int j = 0; j < fileDeltas.length; j++) { | |
IResourceDelta fileDelta = fileDeltas[j]; | |
// See 1FVSC75: ITPJCORE:ALL - SCENARIO B1 - Builder should check F_CONTENT on changed files | |
if (fileDelta.getKind() == IResourceDelta.CHANGED && (fileDelta.getFlags() & IResourceDelta.CONTENT) != 0) { | |
IPath path = fileDelta.getFullPath(); | |
/* skip non-java resources */ | |
String extension = path.getFileExtension(); | |
if (extension != null && (extension.equalsIgnoreCase("java"/*nonNLS*/) || extension.equalsIgnoreCase("class"/*nonNLS*/))) { | |
set.put(path, path); | |
} | |
} | |
} | |
} | |
} | |
return set; | |
} | |
protected boolean hasPackageMapChanges() { | |
if (!Util.equalArraysOrNull(fOldState.getPackageFragmentRootsInClassPath(), fNewState.getPackageFragmentRootsInClassPath())) { | |
return true; | |
} | |
/* Has package or zip been added / removed ? */ | |
if (fRemovedPkgOrZips.size() > 0 || fAddedPkgOrZips.size() > 0) { | |
return true; | |
} | |
/* if there is a changed zip file, assume package map changes */ | |
for (Enumeration e = fChangedPkgOrZips.elements(); e.hasMoreElements();) { | |
IResourceDelta changed = (IResourceDelta) e.nextElement(); | |
String extension = changed.getFullPath().getFileExtension(); | |
if (extension != null) { | |
if (extension.equalsIgnoreCase("zip"/*nonNLS*/) || extension.equalsIgnoreCase("jar"/*nonNLS*/)) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* Returns true if there is a change to the list of source entries in this package, false | |
* otherwise. This is meant to be a fast optimization, to determine | |
* whether the source entries for this package need to be recomputed. This | |
* method works solely against the source workspace delta. When in doubt, | |
* it is always okay to return true here. | |
*/ | |
protected boolean hasSourceEntryChanges(IPackage pkgHandle) { | |
IPath[] oldFrags = fOldState.getPackageMap().getFragments(pkgHandle); | |
IPath[] newFrags = fNewState.getPackageMap().getFragments(pkgHandle); | |
if (!Util.equalArraysOrNull(oldFrags, newFrags)) { | |
/* The set of package fragments has changed. Don't bother optimizing this case -- too complex. */ | |
return true; | |
} | |
/* do for each fragment of this package */ | |
for (int i = 0; i < newFrags.length; i++) { | |
IPath fragPath = newFrags[i]; | |
/* find the JCUs for this package */ | |
IResourceDelta[] fileDeltas = null; | |
for (Enumeration e = fChangedPkgOrZips.elements(); e.hasMoreElements();) { | |
IResourceDelta pkgDelta = (IResourceDelta) e.nextElement(); | |
if (pkgDelta.getFullPath().equals(fragPath)) { | |
/* A zip file is changing. Don't bother optimizing this case -- too complex. */ | |
if (fNewState.isZipElement(fragPath)) { | |
return true; | |
} | |
fileDeltas = pkgDelta.getAffectedChildren(); | |
break; | |
} | |
} | |
if (fileDeltas == null) { | |
/** | |
* It's a more complex interaction between zips, for example | |
* the package may be added in one zip and removed from another. | |
* Don't bother optimizing this case -- too complex. | |
*/ | |
return true; | |
} | |
/* do for each file in this fragment in the new workspace */ | |
for (int j = 0; j < fileDeltas.length; j++) { | |
IPath path = fileDeltas[j].getFullPath(); | |
/* skip java resources */ | |
String extension = path.getFileExtension(); | |
if (extension != null) { | |
if ((extension.equalsIgnoreCase("java"/*nonNLS*/) || extension.equalsIgnoreCase("class"/*nonNLS*/))) { | |
/* if there is an added or removed jcu or binary, the source entries have changed */ | |
int status = fileDeltas[j].getKind(); | |
if (status == IResourceDelta.ADDED || status == IResourceDelta.REMOVED) { | |
return true; | |
} | |
/* it's a change, but it may be changing local status */ | |
if (fileDeltas[j].getResource().isLocal(IResource.DEPTH_ZERO) != fOldState.contains(new SourceEntry(path, null, null))) { | |
return true; | |
} | |
} | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* Issues indictments to the dependents of the specified element. | |
* If transitive is true, they are issued to all dependents transitively, | |
* otherwise they are issued only to immediate dependents. | |
* The trial process is run as the indictments are issued. | |
* Any newly-convicted compilation units are added to the to-be-compiled list. | |
*/ | |
protected void issueIndictments(Object element, IndictmentSet indicts, boolean transitive) { | |
DependencyGraph graph = fNewState.getInternalDependencyGraph(); | |
Object[] dependents = graph.getDependents(element); | |
if (dependents.length > 0) { | |
Hashtable seen = new Hashtable(11); | |
seen.put(element, element); // Don't visit the given element | |
for (int i = 0; i < dependents.length; ++i) { | |
issueIndictments(graph, dependents[i], indicts, transitive, seen); | |
} | |
} | |
} | |
/** | |
* Issues indictments to the specified element. | |
* If transitive is true, they are issued to all dependents transitively, | |
* otherwise they are issued only to this element. | |
* The trial process is run as the indictments are issued. | |
* Any newly-convicted compilation units are added to the to-be-compiled list. | |
*/ | |
protected void issueIndictments(DependencyGraph graph, Object element, IndictmentSet indicts, boolean transitive, Hashtable seen) { | |
// Only issue indictments to compilation units. | |
if (element instanceof PackageElement) { | |
PackageElement pkgElement = (PackageElement) element; | |
if (pkgElement.isSource()) { | |
// Have we already seen this one? | |
if (!seen.containsKey(element)) { | |
seen.put(element, element); | |
// Is it already in the queue? | |
if (!fWorkQueue.contains(pkgElement)) { | |
// If it's not being removed, conduct the trial. | |
if (fNewState.getSourceEntry(pkgElement) != null) { | |
if (tryUnit(pkgElement, indicts)) { | |
fWorkQueue.add(pkgElement); | |
} | |
} | |
} | |
// Recurse if transitive | |
if (transitive) { | |
Object[] dependents = graph.getDependents(element); | |
for (int i = 0; i < dependents.length; ++i) { | |
issueIndictments(graph, dependents[i], indicts, transitive, seen); | |
} | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Mark the immediate dependents of the given element | |
* as needing to be compiled (if not marked so already), | |
* and add any newly marked ones to the list. | |
*/ | |
protected void markDependentsAsNeedingCompile(Object element) { | |
DependencyGraph graph = fNewState.getInternalDependencyGraph(); | |
Object[] deps = graph.getDependents(element); | |
for (int i = 0; i < deps.length; ++i) { | |
if (deps[i] instanceof PackageElement) { | |
PackageElement pkgElement = (PackageElement) deps[i]; | |
if (pkgElement.isSource()) { | |
if (!fWorkQueue.contains(pkgElement)) { | |
// Mark it, if it's not being removed. | |
if (fNewState.getSourceEntry(pkgElement) != null) { | |
fWorkQueue.add(pkgElement); | |
} | |
} | |
} | |
} | |
} | |
} | |
/** | |
* All elements that refer to changed zips, or packages | |
* referred to by changed zips, must be recompiled. | |
* This is conservative, but since the old zip structure | |
* is not available, we can't do much better. | |
*/ | |
protected void markDependentsOfChangedZips() { | |
PathMap oldPathMap = fOldState.getPathMap(); | |
PathMap newPathMap = fNewState.getPathMap(); | |
/* do for each changed zip */ | |
for (Enumeration e = fChangedZips.elements(); e.hasMoreElements();) { | |
IPath zip = (IPath) e.nextElement(); | |
/* mark dependents of packages in old zip */ | |
IPackage[] pkgs = oldPathMap.packageHandlesFromPath(zip); | |
for (int i = 0; i < pkgs.length; i++) { | |
markDependentsAsNeedingCompile(pkgs[i]); | |
} | |
/* mark dependents of packages in new zip */ | |
pkgs = newPathMap.packageHandlesFromPath(zip); | |
for (int i = 0; i < pkgs.length; i++) { | |
markDependentsAsNeedingCompile(pkgs[i]); | |
} | |
/* mark dependents of the zip itself */ | |
markDependentsAsNeedingCompile(zip); | |
} | |
/* also handle zips which were removed from class path */ | |
IPath[] paths = oldPathMap.getPaths(); | |
for (int i = 0; i < paths.length; ++i) { | |
IPath zip = paths[i]; | |
if (fOldState.isZipElement(zip) && !newPathMap.hasPath(zip)) { | |
/* mark dependents of packages in old zip */ | |
IPackage[] pkgs = oldPathMap.packageHandlesFromPath(zip); | |
for (int j = 0; j < pkgs.length; j++) { | |
markDependentsAsNeedingCompile(pkgs[j]); | |
} | |
/* mark dependents of the zip itself */ | |
markDependentsAsNeedingCompile(zip); | |
} | |
} | |
} | |
/** | |
* The given package is being added or removed. Make sure that no types | |
* exist with the same name as this package. If any such types | |
* exist, mark them as needing compilation and add them to the | |
* compile vector. | |
*/ | |
protected void markOverlappingTypesAsNeedingCompile(IPackage pkg) { | |
String pkgName = pkg.getName(); | |
int lastDot; | |
while ((lastDot = pkgName.lastIndexOf('.')) > 0) { | |
/* try to get a compilation unit for this package name */ | |
PackageElement element = fNewState.getCompilationUnitFromName(pkgName, fNewState.defaultPackageForProject()); | |
if (element != null) { | |
fWorkQueue.add(element); | |
} | |
/* strip off a package level */ | |
pkgName = pkgName.substring(0, lastDot); | |
} | |
} | |
/** | |
* Force compilation of everything in the given package (non-state-specific handle), | |
* if indicated by the build context. | |
*/ | |
protected void maybeForce(IPackage pkg) { | |
ImageContextImpl imageContext = (ImageContextImpl) fNewState.getBuildContext(); | |
if (imageContext == null ? pkg.inState(fNewState).isPresent() : imageContext.containsPackage(pkg)) { | |
// fNotifier.subTask("Forcing " + pkg.getName()); | |
fNewState.getAllTypesForPackage(pkg); | |
checkCancel(); | |
} | |
} | |
/** | |
* Propagate the principal structure by package table, | |
* being sure to exclude any affected packages. | |
*/ | |
protected void propagatePrincipalStructureByPackageTable() { | |
Hashtable newTable = (Hashtable) fOldState.getPrincipalStructureByPackageTable().clone(); | |
for (Enumeration e = fAffectedPackages.keys(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
newTable.remove(pkg); | |
} | |
fNewState.setPrincipalStructureByPackageTable(newTable); | |
} | |
/** | |
* Recomputes the source entries for changed packages. As an optimization, | |
* only recompute the source entries for packages that may actually have | |
* changes to their source entries. | |
*/ | |
protected void recomputeSourceEntriesForChangedPackages() { | |
for (Enumeration e = fChangedPackageHandles.elements(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
if (hasSourceEntryChanges(pkg)) { | |
fNewState.getSourceElementTable().removePackage(pkg); | |
// Now force it. | |
fNewState.getSourceEntries(pkg); | |
} | |
} | |
} | |
/** | |
* Remove all types and compilation units from the new state | |
* that do not exist in the new workspace. Keep track of any | |
* types that will need to be compiled as a result of the change. | |
*/ | |
protected void removeOldClasses() { | |
for (Enumeration e = fRemovedClasses.elements(); e.hasMoreElements();) { | |
SourceEntry entry = (SourceEntry) e.nextElement(); | |
removeSourceElement(entry); | |
} | |
} | |
/** | |
* Removes the given source element from the new state's tables | |
* and dependency graph. Marks all JCUs that depend on the removed element. | |
*/ | |
protected void removeSourceElement(SourceEntry entry) { | |
PackageElement element = fOldState.packageElementFromSourceEntry(entry); | |
SourceEntry oldEntry = fOldState.getSourceEntry(element); | |
if (oldEntry == null) { | |
// It didn't exist in the old state (strange). | |
return; | |
} | |
// delete problems for this entry | |
// only delete non-syntax problems, since new syntax problems may have already | |
// been generated during namespace computations | |
fNewState.getProblemReporter().removeNonSyntaxErrors(oldEntry); | |
/* remove type descriptor for types that belong to this source element */ | |
DependencyGraph graph = fNewState.getInternalDependencyGraph(); | |
Hashtable structureTable = fNewState.getPrincipalStructureTable(); | |
IType[] types = graph.getTypes(element); | |
if (types != null) { | |
for (int i = 0; i < types.length; ++i) { | |
IType type = types[i]; | |
structureTable.remove(type); | |
if (!element.isBinary()) { | |
fNewState.getBinaryOutput().deleteBinary(type); | |
} | |
} | |
} | |
markDependentsAsNeedingCompile(element); | |
graph.remove(element); | |
// The element has already been removed from the source element table. | |
// So don't do any source element table or fragment logic here. | |
// Don't delete the namespace for the package here, because it cannot be rebuilt lazily | |
// since the package may be in process of being removed. | |
} | |
/** | |
* Hang onto the binary types for this source, because it will be compiled | |
*/ | |
protected void saveBinaryTypes(PackageElement element) { | |
DependencyGraph graph = fOldState.getInternalDependencyGraph(); | |
IType[] types = graph.getTypes(element); | |
for (int i = 0; i < types.length; i++) { | |
IType type = types[i]; | |
BuilderType builderType = (BuilderType) fBuilderTypeTable.get(type); | |
if (builderType == null || !builderType.isAffected()) { | |
TypeStructureEntry oldEntry = fOldState.getTypeStructureEntry(type, false); | |
Assert.isNotNull(oldEntry); | |
/* create and store the builder type */ | |
// Allow it to be missing. See 1FW1S0Y: ITPJCORE:ALL - Java builder builds when non-Java files affected | |
IBinaryType oldBinary = fOldState.getBinaryTypeOrNull(oldEntry); | |
builderType = new ModifiedBuilderType(this, oldEntry, oldBinary); | |
fBuilderTypeTable.put(type, builderType); | |
/* remove the principal structure entry in the new state */ | |
fNewState.getPrincipalStructureTable().remove(type); | |
/* nuke the binary because it will soon be stale */ | |
fNewState.getBinaryOutput().deleteBinary(type); | |
} | |
} | |
} | |
/** | |
* Sort the compilation units by topological order. | |
*/ | |
protected void sort(PackageElement[] compileArray) { | |
DependencyGraph graph = fNewState.getInternalDependencyGraph(); | |
int len = compileArray.length; | |
int[] sortOrder = new int[len]; | |
for (int i = 0; i < len; ++i) { | |
sortOrder[i] = graph.getOrder(compileArray[i]); | |
} | |
Util.sort(compileArray, sortOrder); | |
} | |
/** | |
* Returns a string describe the builder | |
* @see IImageBuilder | |
*/ | |
public String toString() { | |
return "incremental image builder for:\n"/*nonNLS*/ + "\tnew state: "/*nonNLS*/ + getNewState() + "\n"/*nonNLS*/ + "\told state: "/*nonNLS*/ + getOldState(); | |
} | |
/** | |
* If this type is a subtype is the originator of an abstract method | |
* indictment, it must be compiled | |
*/ | |
protected boolean tryAbstractMethodIndictments(PackageElement unit, IndictmentSet indictments) { | |
final boolean GUILTY = true, INNOCENT = false; | |
IType[] trialTypes = indictments.getAbstractMethodOriginators(); | |
if (trialTypes.length == 0) { | |
return false; | |
} | |
/* if problems were detected, some innerclasses might not have been generated, | |
thus the state would not reflect their presence, and even if guilty could not | |
be convicted (also see 1GA6CV7) */ | |
Vector problemVector = fOldState.getProblemReporter().getProblemVector(fOldState.getSourceEntry(unit)); | |
if (problemVector != null){ | |
Enumeration problems = problemVector.elements(); | |
while (problems.hasMoreElements()){ | |
IProblemDetail problem = (IProblemDetail) problems.nextElement(); | |
if ((problem.getSeverity() & IProblemDetail.S_ERROR) != 0) return GUILTY; | |
} | |
} | |
/* do for each type in this package element */ | |
IType[] types = fOldState.getInternalDependencyGraph().getTypes(unit); | |
for (int i = 0, imax = types.length; i < imax; i++) { | |
IType type = types[i]; | |
TypeStructureEntry tsEntry = fOldState.getTypeStructureEntry(type, false); | |
/* shouldn't happen, but trust nobody! */ | |
if (tsEntry == null) { | |
continue; | |
} | |
IType oldType = (IType) type.inState(fOldState); | |
int flags = oldType.getModifiers(); | |
/* interfaces aren't affected by method additions/removals in super interfaces, | |
other than for compatibility checking, which is handled elsewhere */ | |
if (oldType.isInterface()) { | |
continue; | |
} | |
/* only check superclasses if this class is not abstract, */ | |
/* because abstract classes aren't affected by abstract method additions/removals */ | |
/* on their superclasses, only on their superinterfaces */ | |
boolean checkSuperclasses = (flags & IConstants.AccAbstract) == 0; | |
/* do for each abstract method originator */ | |
BuilderType builderType = getBuilderType(type); | |
for (int j = 0, jmax = trialTypes.length; j < jmax; ++j) { | |
IType trialType = trialTypes[j]; | |
if (checkSuperclasses) { | |
if (builderType.hasSuperclass(trialType)) { | |
return GUILTY; | |
} | |
} | |
if (builderType.hasSuperInterface(trialType)) { | |
return GUILTY; | |
} | |
} | |
} | |
/* no supertypes convicted this package element, so it must be innocent */ | |
return INNOCENT; | |
} | |
/** | |
* If a subtype of the originator of a method indictment redefines | |
* the method for which there is an indictment, it must be recompiled. | |
* This is also true if any superinterfaces of the subtype defines | |
* an indicted method. | |
*/ | |
protected boolean tryMethodDeclarations(BuilderType builderType, IndictmentSet indictments) { | |
final boolean GUILTY = true, INNOCENT = false; | |
/* try the methods of this type */ | |
IBinaryType oldBinary = builderType.getOldBinaryType(); | |
if (oldBinary == null) return GUILTY; | |
IBinaryMethod[] methods = oldBinary.getMethods(); | |
if (methods != null) { | |
for (int k = 0; k < methods.length; k++) { | |
if (indictments.tryMethodDeclaration(methods[k])) { | |
return GUILTY; | |
} | |
} | |
} | |
/* recurse on superinterfaces */ | |
char[][] interfaces = oldBinary.getInterfaceNames(); | |
if (interfaces != null) { | |
for (int i = 0; i < interfaces.length; i++) { | |
BuilderType supr = getBuilderType(BinaryStructure.getType(builderType.getNewState(), builderType.getNewTypeStructureEntry(), interfaces[i])); | |
if (tryMethodDeclarations(supr, indictments)) { | |
return GUILTY; | |
} | |
} | |
} | |
return INNOCENT; | |
} | |
/** | |
* If a subtype of the originator of a method indictment redefines | |
* the method for which there is an indictment, it must be recompiled. | |
*/ | |
protected boolean tryMethodDeclarations(PackageElement unit, IndictmentSet indictments) { | |
final boolean GUILTY = true, INNOCENT = false; | |
IType[] methodIndictmentOwners = indictments.getMethodIndictmentOwners(); | |
if (methodIndictmentOwners.length == 0) { | |
return INNOCENT; | |
} | |
/* if problems were detected, some innerclasses might not have been generated, | |
thus the state would not reflect their presence, and even if guilty could not | |
be convicted (also see 1GA6CV7) */ | |
Vector problemVector = fOldState.getProblemReporter().getProblemVector(fOldState.getSourceEntry(unit)); | |
if (problemVector != null){ | |
Enumeration problems = problemVector.elements(); | |
while (problems.hasMoreElements()){ | |
IProblemDetail problem = (IProblemDetail) problems.nextElement(); | |
if ((problem.getSeverity() & IProblemDetail.S_ERROR) != 0) return GUILTY; | |
} | |
} | |
IType[] types = fOldState.getInternalDependencyGraph().getTypes(unit); | |
for (int i = 0; i < types.length; ++i) { | |
boolean found = false; | |
BuilderType trialType = getBuilderType(types[i]); | |
for (int j = 0, len = methodIndictmentOwners.length; j < len; ++j) { | |
//note this is conservative because owners are not matched to their methods | |
if (trialType.hasSuperType(methodIndictmentOwners[j])) { | |
found = true; | |
break; | |
} | |
} | |
if (found) { | |
if (tryMethodDeclarations(trialType, indictments)){ | |
return GUILTY; | |
} | |
} | |
} | |
return INNOCENT; | |
} | |
/** | |
* Conducts a trial on a single compilation unit. Returns true if the unit | |
* is guilty. The sentence is compilation without possibility of parole. | |
*/ | |
protected boolean tryUnit(PackageElement unit, IndictmentSet indictments) { | |
/* innocent unless proven guilty */ | |
final boolean GUILTY = true, INNOCENT = false; | |
/* quick test to see if there's no indictments */ | |
if (indictments.isEmpty()) { | |
return INNOCENT; | |
} | |
/* automatically guilty if there is an upstream hierarchy change */ | |
if (indictments.hasHierarchyIndictment()) { | |
return GUILTY; | |
} | |
/* gather the evidence */ | |
ReferenceInfo evidence = fNewState.getReferencesForPackageElement(unit); | |
/* Unable to index unit, convict and let compiler try it */ | |
if (evidence == null) { | |
return GUILTY; | |
} | |
if (indictments.tryAllEvidence(evidence)) { | |
return GUILTY; | |
} | |
/** | |
* If a subtype of the originator of a method indictment redefines | |
* the method for which there is an indictment, it must be recompiled. | |
*/ | |
if (tryMethodDeclarations(unit, indictments)) { | |
return GUILTY; | |
} | |
/** | |
* If this type is a subtype of the originator of an abstract method | |
* indictment, it may need to be recompiled. | |
*/ | |
if (tryAbstractMethodIndictments(unit, indictments)) { | |
return GUILTY; | |
} | |
/** | |
* If there have been changes to constructors in the direct superclass, | |
* it must be recompiled. | |
*/ | |
if (tryZeroArgConstructorInSuperclass(unit, indictments)) { | |
return GUILTY; | |
} | |
/* evidence is exhausted and unit has not been convicted */ | |
return INNOCENT; | |
} | |
/** | |
* If there have been changes to the zero-arg constructor in the superclass of any types in the CU, | |
* the CU must be recompiled. This handles refs by default constructors and implicit super invocations in | |
* constructors, which aren't covered by the normal method indictments since they | |
* generate no evidence of refs to the super constructor. | |
*/ | |
protected boolean tryZeroArgConstructorInSuperclass(PackageElement unit, IndictmentSet indictments) { | |
if (!indictments.hasConstructorIndictments()) { | |
return false; | |
} | |
IType[] types = fOldState.getInternalDependencyGraph().getTypes(unit); | |
for (int i = 0; i < types.length; ++i) { | |
IType superclass = getBuilderType(types[i]).getSuperclass(); | |
if (superclass != null) { | |
String key = '<' + superclass.getDeclaredName() + ">/0"/*nonNLS*/; | |
if (indictments.tryMethodEvidence(key.toCharArray())) { | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* Processes changed classes in the state. Stores compilation | |
* units that need to be compiled as a result of the changes. | |
*/ | |
protected void updateChangedClasses() { | |
for (Enumeration e = fChangedClasses.elements(); e.hasMoreElements();) { | |
SourceEntry entry = (SourceEntry) e.nextElement(); | |
changedSourceElement(entry); | |
} | |
} | |
/** | |
* Updates or rebuilds the package map with the affected package fragments. | |
* After, the added/removed/changed builder packages are known. | |
*/ | |
protected void updatePackageMap() { | |
// Simply rebuild if adding or removing packages, rather than trying to do this | |
// incrementally, which is tricky. E.g. a package should not really be added if its | |
// project does not appear in the class path. | |
boolean rebuild = hasPackageMapChanges(); | |
if (rebuild) { | |
fNewState.buildInitialPackageMap(); | |
} | |
/* Set of affected package handles */ | |
Vector affected = new Vector(); | |
/* Process changed package fragments (package map not affected). * | |
* Due to changing class paths, a package which is changing in the source * | |
* may actually be added / removed rather than changed. Figure out which. */ | |
for (Enumeration e = fChangedPkgOrZips.elements(); e.hasMoreElements();) { | |
IResourceDelta delta = (IResourceDelta) e.nextElement(); | |
IPackage[] pkgHandles = null; | |
/* Look in the new state only if the package has not been removed */ | |
if (delta.getKind() != IResourceDelta.REMOVED) { | |
pkgHandles = fNewState.getPathMap().packageHandlesFromPath(delta.getFullPath()); | |
for (int i = 0; i < pkgHandles.length; i++) { | |
if (!affected.contains(pkgHandles[i])) { | |
affected.addElement(pkgHandles[i]); | |
} | |
} | |
} | |
/* Look in the old state only if the package has not been added */ | |
if (delta.getKind() != IResourceDelta.ADDED) { | |
pkgHandles = fOldState.getPathMap().packageHandlesFromPath(delta.getFullPath()); | |
for (int i = 0; i < pkgHandles.length; i++) { | |
if (!affected.contains(pkgHandles[i])) { | |
affected.addElement(pkgHandles[i]); | |
} | |
} | |
} | |
} | |
/* Partition affected packages into added/removed/changed */ | |
fAddedPackageHandles = new Vector(); | |
fRemovedPackageHandles = new Vector(); | |
fChangedPackageHandles = new Vector(); | |
PackageMap oldMap = fOldState.getPackageMap(); | |
PackageMap newMap = fNewState.getPackageMap(); | |
for (Enumeration e = affected.elements(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
if (oldMap.containsPackage(pkg)) { | |
if (newMap.containsPackage(pkg)) { | |
fChangedPackageHandles.addElement(pkg); | |
} else { | |
fRemovedPackageHandles.addElement(pkg); | |
} | |
} else { | |
if (newMap.containsPackage(pkg)) { | |
fAddedPackageHandles.addElement(pkg); | |
} else { | |
// This can occur if there are changes to a package | |
// which does not appear in either the old or new class path. | |
// Ignore it. | |
} | |
} | |
} | |
/* Check for added/removed/changed packages due to added/removed fragments and/or class path changes */ | |
if (rebuild) { | |
for (Enumeration e = oldMap.getAllPackages(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
IPath[] newFragments = newMap.getFragments(pkg); | |
if (newFragments == null) { | |
// package has been removed due to class path change; | |
// may also have been removed due to source change | |
if (!fRemovedPackageHandles.contains(pkg)) { | |
fRemovedPackageHandles.addElement(pkg); | |
} | |
} else | |
if (!Util.equalArraysOrNull(oldMap.getFragments(pkg), newFragments)) { | |
// package has changed package fragments due to class path change; | |
// may also have source change | |
if (!fChangedPackageHandles.contains(pkg)) { | |
fChangedPackageHandles.addElement(pkg); | |
} | |
} | |
} | |
for (Enumeration e = newMap.getAllPackages(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
if (!oldMap.containsPackage(pkg)) { | |
// package has been added due to class path change; | |
// may also have been added due to source change | |
if (!fAddedPackageHandles.contains(pkg)) { | |
fAddedPackageHandles.addElement(pkg); | |
} | |
} | |
} | |
} | |
/* Add all affected packages to fAffectedPackages. */ | |
fAffectedPackages = new Hashtable(11); | |
for (Enumeration e = fAddedPackageHandles.elements(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
fAffectedPackages.put(pkg, pkg); | |
} | |
for (Enumeration e = fRemovedPackageHandles.elements(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
fAffectedPackages.put(pkg, pkg); | |
} | |
for (Enumeration e = fChangedPackageHandles.elements(); e.hasMoreElements();) { | |
IPackage pkg = (IPackage) e.nextElement(); | |
fAffectedPackages.put(pkg, pkg); | |
} | |
} | |
/** | |
* Stores the results of a compilation in the appropriate state tables. | |
* Keeps track of what compilation units need to be compiled as a result | |
* of the changes. | |
*/ | |
protected void updateState(ConvertedCompilationResult[] results) { | |
int n = results.length; | |
PackageElement[] oldUnits = new PackageElement[n]; | |
PackageElement[] newUnits = new PackageElement[n]; | |
IType[][] oldTypeList = new IType[n][]; | |
// Preparation | |
DependencyGraph oldGraph = fOldState.getInternalDependencyGraph(); | |
DependencyGraph newGraph = fNewState.getInternalDependencyGraph(); | |
for (int i = 0; i < n; i++) { | |
PackageElement element = results[i].getPackageElement(); | |
// Be sure the package is in the set of affected packages. | |
// It may not be if this unit was recompiled in a package | |
// other than the ones which have direct changes. | |
IPackage pkg = element.getPackage(); | |
fAffectedPackages.put(pkg, pkg); | |
// Be sure to look up the source entries in the old and new state, | |
// since they may be different. | |
SourceEntry oldSourceEntry = fOldState.getSourceEntry(element); | |
oldUnits[i] = (oldSourceEntry == null ? null : fOldState.packageElementFromSourceEntry(oldSourceEntry)); | |
SourceEntry newSourceEntry = fNewState.getSourceEntry(element); | |
newUnits[i] = fNewState.packageElementFromSourceEntry(newSourceEntry); | |
if (oldUnits[i] != null) { | |
oldTypeList[i] = oldGraph.getTypes(oldUnits[i]); | |
} | |
} | |
// Remove old problems and principal structure from new state before | |
// storing new results. | |
for (int i = 0; i < n; i++) { | |
if (oldUnits[i] != null) { | |
SourceEntry sEntry = fOldState.getSourceEntry(oldUnits[i]); | |
fNewState.getProblemReporter().removeNonSyntaxErrors(sEntry); | |
} | |
if (oldTypeList[i] != null) { | |
IType[] oldTypes = oldTypeList[i]; | |
for (int j = 0; j < oldTypes.length; ++j) { | |
fNewState.getPrincipalStructureTable().remove(oldTypes[j]); | |
} | |
} | |
} | |
super.updateState(results); | |
// now calculate the changes | |
for (int i = 0; i < n; i++) { | |
PackageElement unit = newUnits[i]; | |
if (unit == null) { | |
// Unit isn't visible. Shouldn't have gotten this far. Skip it. | |
continue; | |
} | |
/** | |
* Assumption: namespace changes have been dealt with before | |
* compilation. Compilation units that were removed have already generated | |
* type collaborator indictments. Here, we are only concerned | |
* with changes within each compilation unit. | |
*/ | |
TypeStructureEntry[] newTSEntries = results[i].getTypes(); | |
IndictmentSet indictments = new IndictmentSet(); | |
/* do for each type generated by unit in old state */ | |
IType[] oldTypes = oldTypeList[i]; | |
if (oldTypes != null) { | |
for (int j = 0; j < oldTypes.length; ++j) { | |
IType oldType = oldTypes[j]; | |
boolean found = false; | |
for (int k = 0; k < newTSEntries.length; ++k) { | |
if (newTSEntries[k] != null && newTSEntries[k].getType().equals(oldType)) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
getBuilderType(oldType).computeIndictments(indictments); | |
} | |
} | |
} | |
/* do for each type in result */ | |
for (int j = 0; j < newTSEntries.length; j++) { | |
TypeStructureEntry newTSEntry = newTSEntries[j]; | |
if (newTSEntry != null) { | |
/* compute the indictments for this type */ | |
IType type = newTSEntry.getType(); | |
BuilderType bType = getBuilderType(type); | |
/* the new tsEntry wasn't known at compilation time */ | |
if (bType.getNewTypeStructureEntry() == null) { | |
bType.setNewTypeStructureEntry(newTSEntry); | |
} | |
bType.computeIndictments(indictments); | |
} | |
} | |
if (!indictments.isEmpty()) { | |
issueIndictments(unit, indictments, false); | |
} | |
} | |
} | |
} |