package org.eclipse.jdt.internal.core;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.core.DeltaProcessor.RootInfo;
import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo;
import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
import org.eclipse.jdt.internal.core.util.Util;
@SuppressWarnings({"rawtypes", "unchecked"})
public class ClasspathChange {
public static final int NO_DELTA = 0x00;
public static final int HAS_DELTA = 0x01;
public static final int HAS_PROJECT_CHANGE = 0x02;
public static final int HAS_LIBRARY_CHANGE = 0x04;
JavaProject project;
IClasspathEntry[] oldRawClasspath;
IPath oldOutputLocation;
IClasspathEntry[] oldResolvedClasspath;
public ClasspathChange(JavaProject project, IClasspathEntry[] oldRawClasspath, IPath oldOutputLocation, IClasspathEntry[] oldResolvedClasspath) {
this.project = project;
this.oldRawClasspath = oldRawClasspath;
this.oldOutputLocation = oldOutputLocation;
this.oldResolvedClasspath = oldResolvedClasspath;
}
private void addClasspathDeltas(JavaElementDelta delta, IPackageFragmentRoot[] roots, int flag) {
for (int i = 0; i < roots.length; i++) {
IPackageFragmentRoot root = roots[i];
delta.changed(root, flag);
if ((flag & IJavaElementDelta.F_REMOVED_FROM_CLASSPATH) != 0
|| (flag & IJavaElementDelta.F_SOURCEATTACHED) != 0
|| (flag & IJavaElementDelta.F_SOURCEDETACHED) != 0){
try {
root.close();
} catch (JavaModelException e) {
}
}
}
}
private int classpathContains(IClasspathEntry[] list, IClasspathEntry entry) {
IPath[] exclusionPatterns = entry.getExclusionPatterns();
IPath[] inclusionPatterns = entry.getInclusionPatterns();
int listLen = list == null ? 0 : list.length;
nextEntry: for (int i = 0; i < listLen; i++) {
IClasspathEntry other = list[i];
if (other.getContentKind() == entry.getContentKind()
&& other.getEntryKind() == entry.getEntryKind()
&& other.isExported() == entry.isExported()
&& other.getPath().equals(entry.getPath())) {
IPath entryOutput = entry.getOutputLocation();
IPath otherOutput = other.getOutputLocation();
if (entryOutput == null) {
if (otherOutput != null)
continue;
} else {
if (!entryOutput.equals(otherOutput))
continue;
}
IPath[] otherIncludes = other.getInclusionPatterns();
if (inclusionPatterns != otherIncludes) {
if (inclusionPatterns == null) continue;
int includeLength = inclusionPatterns.length;
if (otherIncludes == null || otherIncludes.length != includeLength)
continue;
for (int j = 0; j < includeLength; j++) {
if (!inclusionPatterns[j].toString().equals(otherIncludes[j].toString()))
continue nextEntry;
}
}
IPath[] otherExcludes = other.getExclusionPatterns();
if (exclusionPatterns != otherExcludes) {
if (exclusionPatterns == null) continue;
int excludeLength = exclusionPatterns.length;
if (otherExcludes == null || otherExcludes.length != excludeLength)
continue;
for (int j = 0; j < excludeLength; j++) {
if (!exclusionPatterns[j].toString().equals(otherExcludes[j].toString()))
continue nextEntry;
}
}
if (JavaCore.ENABLED.equals(this.project.getOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, true))) {
String annotationPath = ClasspathEntry.getRawExternalAnnotationPath(entry);
String otherAnnotationPath = ClasspathEntry.getRawExternalAnnotationPath(other);
if (annotationPath != null && otherAnnotationPath != null) {
if (!annotationPath.equals(otherAnnotationPath))
continue;
} else if (annotationPath != otherAnnotationPath) {
continue;
}
}
if (((ClasspathEntry) entry).isModular() !=
((ClasspathEntry) other).isModular()) {
continue nextEntry;
}
return i;
}
}
return -1;
}
private void collectAllSubfolders(IFolder folder, ArrayList collection) throws JavaModelException {
try {
IResource[] members= folder.members();
for (int i = 0, max = members.length; i < max; i++) {
IResource r= members[i];
if (r.getType() == IResource.FOLDER) {
collection.add(r);
collectAllSubfolders((IFolder)r, collection);
}
}
} catch (CoreException e) {
throw new JavaModelException(e);
}
}
private ArrayList determineAffectedPackageFragments(IPath location) throws JavaModelException {
ArrayList fragments = new ArrayList();
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IResource resource = null;
if (location != null) {
resource = workspace.getRoot().findMember(location);
}
if (resource != null && resource.getType() == IResource.FOLDER) {
IFolder folder = (IFolder) resource;
IClasspathEntry[] classpath = this.project.getExpandedClasspath();
for (int i = 0; i < classpath.length; i++) {
IClasspathEntry entry = classpath[i];
IPath path = classpath[i].getPath();
if (entry.getEntryKind() != IClasspathEntry.CPE_PROJECT && path.isPrefixOf(location) && !path.equals(location)) {
IPackageFragmentRoot[] roots = this.project.computePackageFragmentRoots(classpath[i]);
PackageFragmentRoot root = (PackageFragmentRoot) roots[0];
ArrayList folders = new ArrayList();
folders.add(folder);
collectAllSubfolders(folder, folders);
Iterator elements = folders.iterator();
int segments = path.segmentCount();
while (elements.hasNext()) {
IFolder f = (IFolder) elements.next();
IPath relativePath = f.getFullPath().removeFirstSegments(segments);
String[] pkgName = relativePath.segments();
IPackageFragment pkg = root.getPackageFragment(pkgName);
if (!Util.isExcluded(pkg))
fragments.add(pkg);
}
}
}
}
return fragments;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ClasspathChange))
return false;
return this.project.equals(((ClasspathChange) obj).project);
}
public int generateDelta(JavaElementDelta delta, boolean addClasspathChange) {
JavaModelManager manager = JavaModelManager.getJavaModelManager();
DeltaProcessingState state = manager.deltaState;
if (state.findJavaProject(this.project.getElementName()) == null)
return NO_DELTA;
DeltaProcessor deltaProcessor = state.getDeltaProcessor();
IClasspathEntry[] newResolvedClasspath = null;
IPath newOutputLocation = null;
int result = NO_DELTA;
try {
PerProjectInfo perProjectInfo = this.project.getPerProjectInfo();
this.project.resolveClasspath(perProjectInfo, false, addClasspathChange);
IClasspathEntry[] newRawClasspath;
synchronized (perProjectInfo) {
newRawClasspath = perProjectInfo.rawClasspath;
newResolvedClasspath = perProjectInfo.getResolvedClasspath();
newOutputLocation = perProjectInfo.outputLocation;
}
if (newResolvedClasspath == null) {
PerProjectInfo temporaryInfo = this.project.newTemporaryInfo();
this.project.resolveClasspath(temporaryInfo, false, addClasspathChange);
newRawClasspath = temporaryInfo.rawClasspath;
newResolvedClasspath = temporaryInfo.getResolvedClasspath();
newOutputLocation = temporaryInfo.outputLocation;
}
if (this.oldRawClasspath != null && !JavaProject.areClasspathsEqual(this.oldRawClasspath, newRawClasspath, this.oldOutputLocation, newOutputLocation)) {
delta.changed(this.project, IJavaElementDelta.F_CLASSPATH_CHANGED);
result |= HAS_DELTA;
for (int i = 0, length = this.oldRawClasspath.length; i < length; i++) {
IClasspathEntry entry = this.oldRawClasspath[i];
if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
if (classpathContains(newRawClasspath, entry) == -1)
manager.containerPut(this.project, entry.getPath(), null);
}
}
}
if (this.oldResolvedClasspath != null && JavaProject.areClasspathsEqual(this.oldResolvedClasspath, newResolvedClasspath, this.oldOutputLocation, newOutputLocation))
return result;
this.project.close();
deltaProcessor.projectCachesToReset.add(this.project);
} catch (JavaModelException e) {
if (DeltaProcessor.VERBOSE) {
e.printStackTrace();
}
return result;
}
if (this.oldResolvedClasspath == null)
return result;
delta.changed(this.project, IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED);
result |= HAS_DELTA;
state.addForRefresh(this.project);
Map removedRoots = null;
IPackageFragmentRoot[] roots = null;
Map allOldRoots ;
if ((allOldRoots = deltaProcessor.oldRoots) != null) {
roots = (IPackageFragmentRoot[]) allOldRoots.get(this.project);
}
if (roots != null) {
removedRoots = new HashMap();
for (int i = 0; i < roots.length; i++) {
IPackageFragmentRoot root = roots[i];
removedRoots.put(root.getPath(), root);
}
}
int newLength = newResolvedClasspath.length;
int oldLength = this.oldResolvedClasspath.length;
for (int i = 0; i < oldLength; i++) {
int index = classpathContains(newResolvedClasspath, this.oldResolvedClasspath[i]);
if (index == -1) {
int entryKind = this.oldResolvedClasspath[i].getEntryKind();
if (entryKind == IClasspathEntry.CPE_PROJECT) {
result |= HAS_PROJECT_CHANGE;
continue;
}
if (entryKind == IClasspathEntry.CPE_LIBRARY) {
result |= HAS_LIBRARY_CHANGE;
}
IPackageFragmentRoot[] pkgFragmentRoots = null;
if (removedRoots != null) {
PackageFragmentRoot oldRoot = (PackageFragmentRoot) removedRoots.get(this.oldResolvedClasspath[i].getPath());
if (oldRoot != null) {
pkgFragmentRoots = new PackageFragmentRoot[] { oldRoot };
}
}
if (pkgFragmentRoots == null) {
try {
ObjectVector accumulatedRoots = new ObjectVector();
HashSet rootIDs = new HashSet(5);
rootIDs.add(this.project.rootID());
JrtPackageFragmentRoot.workingOnOldClasspath.set(Boolean.TRUE);
this.project.computePackageFragmentRoots(
this.oldResolvedClasspath[i],
accumulatedRoots,
rootIDs,
null,
false,
true,
null);
RootInfo rootInfo = state.oldRoots.get(this.oldResolvedClasspath[i].getPath());
if (rootInfo != null && rootInfo.cache != null) {
IPackageFragmentRoot oldRoot = rootInfo.cache;
boolean found = false;
for (int j = 0; j < accumulatedRoots.size(); j++) {
IPackageFragmentRoot root = (IPackageFragmentRoot) accumulatedRoots.elementAt(j);
if (root.getPath().equals(oldRoot.getPath())) {
found = true;
break;
}
}
if (!found)
accumulatedRoots.add(oldRoot);
}
pkgFragmentRoots = new PackageFragmentRoot[accumulatedRoots.size()];
accumulatedRoots.copyInto(pkgFragmentRoots);
} catch (JavaModelException e) {
pkgFragmentRoots = new PackageFragmentRoot[] {};
} finally {
JrtPackageFragmentRoot.workingOnOldClasspath.set(null);
}
}
addClasspathDeltas(delta, pkgFragmentRoots, IJavaElementDelta.F_REMOVED_FROM_CLASSPATH);
} else {
if (this.oldResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT) {
result |= HAS_PROJECT_CHANGE;
continue;
}
if (index != i) {
addClasspathDeltas(delta, this.project.computePackageFragmentRoots(this.oldResolvedClasspath[i]), IJavaElementDelta.F_REORDER);
}
IPath newSourcePath = newResolvedClasspath[index].getSourceAttachmentPath();
int sourceAttachmentFlags = getSourceAttachmentDeltaFlag(this.oldResolvedClasspath[i].getSourceAttachmentPath(), newSourcePath);
IPath oldRootPath = this.oldResolvedClasspath[i].getSourceAttachmentRootPath();
IPath newRootPath = newResolvedClasspath[index].getSourceAttachmentRootPath();
int sourceAttachmentRootFlags = getSourceAttachmentDeltaFlag(oldRootPath, newRootPath);
int flags = sourceAttachmentFlags | sourceAttachmentRootFlags;
if (flags != 0) {
addClasspathDeltas(delta, this.project.computePackageFragmentRoots(this.oldResolvedClasspath[i]), flags);
} else {
if (oldRootPath == null && newRootPath == null) {
IPackageFragmentRoot[] computedRoots = this.project.computePackageFragmentRoots(this.oldResolvedClasspath[i]);
for (int j = 0; j < computedRoots.length; j++) {
IPackageFragmentRoot root = computedRoots[j];
try {
root.close();
} catch (JavaModelException e) {
}
}
}
}
}
}
for (int i = 0; i < newLength; i++) {
int index = classpathContains(this.oldResolvedClasspath, newResolvedClasspath[i]);
if (index == -1) {
int entryKind = newResolvedClasspath[i].getEntryKind();
if (entryKind == IClasspathEntry.CPE_PROJECT) {
result |= HAS_PROJECT_CHANGE;
continue;
}
if (entryKind == IClasspathEntry.CPE_LIBRARY) {
result |= HAS_LIBRARY_CHANGE;
}
addClasspathDeltas(delta, this.project.computePackageFragmentRoots(newResolvedClasspath[i]), IJavaElementDelta.F_ADDED_TO_CLASSPATH);
}
}
if ((newOutputLocation == null && this.oldOutputLocation != null)
|| (newOutputLocation != null && !newOutputLocation.equals(this.oldOutputLocation))) {
try {
ArrayList added = determineAffectedPackageFragments(this.oldOutputLocation);
Iterator iter = added.iterator();
while (iter.hasNext()){
IPackageFragment frag= (IPackageFragment)iter.next();
((IPackageFragmentRoot)frag.getParent()).close();
delta.added(frag);
}
ArrayList removed = determineAffectedPackageFragments(newOutputLocation);
iter = removed.iterator();
while (iter.hasNext()) {
IPackageFragment frag= (IPackageFragment)iter.next();
((IPackageFragmentRoot)frag.getParent()).close();
delta.removed(frag);
}
} catch (JavaModelException e) {
if (DeltaProcessor.VERBOSE)
e.printStackTrace();
}
}
return result;
}
private int getSourceAttachmentDeltaFlag(IPath oldPath, IPath newPath) {
if (oldPath == null) {
if (newPath != null) {
return IJavaElementDelta.F_SOURCEATTACHED;
} else {
return 0;
}
} else if (newPath == null) {
return IJavaElementDelta.F_SOURCEDETACHED;
} else if (!oldPath.equals(newPath)) {
return IJavaElementDelta.F_SOURCEATTACHED | IJavaElementDelta.F_SOURCEDETACHED;
} else {
return 0;
}
}
@Override
public int hashCode() {
return this.project.hashCode();
}
public void requestIndexing() {
IClasspathEntry[] newResolvedClasspath = null;
try {
newResolvedClasspath = this.project.getResolvedClasspath();
} catch (JavaModelException e) {
return;
}
JavaModelManager manager = JavaModelManager.getJavaModelManager();
IndexManager indexManager = manager.indexManager;
if (indexManager == null)
return;
DeltaProcessingState state = manager.deltaState;
int newLength = newResolvedClasspath.length;
int oldLength = this.oldResolvedClasspath == null ? 0 : this.oldResolvedClasspath.length;
for (int i = 0; i < oldLength; i++) {
int index = classpathContains(newResolvedClasspath, this.oldResolvedClasspath[i]);
if (index == -1) {
if (this.oldResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT){
continue;
}
IClasspathEntry oldEntry = this.oldResolvedClasspath[i];
final IPath path = oldEntry.getPath();
int changeKind = this.oldResolvedClasspath[i].getEntryKind();
switch (changeKind) {
case IClasspathEntry.CPE_SOURCE:
char[][] inclusionPatterns = ((ClasspathEntry)oldEntry).fullInclusionPatternChars();
char[][] exclusionPatterns = ((ClasspathEntry)oldEntry).fullExclusionPatternChars();
indexManager.removeSourceFolderFromIndex(this.project, path, inclusionPatterns, exclusionPatterns);
break;
case IClasspathEntry.CPE_LIBRARY:
if (state.otherRoots.get(path) == null) {
indexManager.discardJobs(path.toString());
indexManager.removeIndex(path);
}
break;
}
}
}
for (int i = 0; i < newLength; i++) {
int index = classpathContains(this.oldResolvedClasspath, newResolvedClasspath[i]);
if (index == -1 || newResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
if (newResolvedClasspath[i].getEntryKind() == IClasspathEntry.CPE_PROJECT){
continue;
}
int entryKind = newResolvedClasspath[i].getEntryKind();
URL newurl = ((ClasspathEntry)newResolvedClasspath[i]).getLibraryIndexLocation();
switch (entryKind) {
case IClasspathEntry.CPE_LIBRARY:
boolean pathHasChanged = true;
IPath newPath = newResolvedClasspath[i].getPath();
for (int j = 0; j < oldLength; j++) {
IClasspathEntry oldEntry = this.oldResolvedClasspath[j];
if (oldEntry.getPath().equals(newPath)) {
URL oldurl = ((ClasspathEntry)oldEntry).getLibraryIndexLocation();
if (oldurl == null && newurl == null) {
pathHasChanged = false;
} else if (oldurl != null && newurl != null) {
pathHasChanged = !(newurl.equals(oldurl));
} else if (oldurl != null) {
indexManager.removeIndex(newPath);
}
break;
}
}
if (pathHasChanged) {
indexManager.indexLibrary(newPath, this.project.getProject(), newurl);
}
break;
case IClasspathEntry.CPE_SOURCE:
IClasspathEntry entry = newResolvedClasspath[i];
IPath path = entry.getPath();
char[][] inclusionPatterns = ((ClasspathEntry)entry).fullInclusionPatternChars();
char[][] exclusionPatterns = ((ClasspathEntry)entry).fullExclusionPatternChars();
indexManager.indexSourceFolder(this.project, path, inclusionPatterns, exclusionPatterns);
break;
}
}
}
}
@Override
public String toString() {
return "ClasspathChange: " + this.project.getElementName();
}
}