blob: b24c1c26947a1113ad334ccb38af095badc5a824 [file] [log] [blame]
package org.eclipse.jdt.internal.core;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IJavaProject;
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.core.search.indexing.IndexManager;
/**
* This class is used by <code>JavaModelManager</code> to convert
* <code>IResourceDelta</code>s into <code>IJavaElementDelta</code>s.
* It also does some processing on the <code>JavaElement</code>s involved
* (e.g. closing them or updating classpaths).
*/
public class DeltaProcessor {
final static int IGNORE = 0;
final static int SOURCE = 1;
final static int BINARY = 2;
/**
* The <code>JavaElementDelta</code> corresponding to the <code>IResourceDelta</code> being translated.
*/
protected JavaElementDelta fCurrentDelta;
protected IndexManager indexManager = new IndexManager();
/* A table from IPath (from a classpath entry) to IJavaProject */
Map roots;
/* A table from IPath (from a classpath entry) to HashSet of IJavaProject
* Used when an IPath corresponds to more than one root */
Map otherRoots;
/* The java element that was last created (see createElement(IResource).
* This is used as a stack of java elements (using getParent() to pop it, and
* using the various get*(...) to push it. */
Openable currentElement;
/*
* The type of the current event being processed (see ChangedElementEvent)
*/
int currentEventType;
HashSet projectsToUpdate = new HashSet();
static final IJavaElementDelta[] NO_DELTA = new IJavaElementDelta[0];
public static boolean VERBOSE = false;
/*
* Adds the dependents of the given project to the list of the projects
* to update.
*/
void addDependentsToProjectsToUpdate(IPath projectPath) {
try {
IJavaProject[] projects = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
for (int i = 0, length = projects.length; i < length; i++) {
IJavaProject project = projects[i];
IClasspathEntry[] classpath = project.getResolvedClasspath(true);
for (int j = 0, length2 = classpath.length; j < length2; j++) {
IClasspathEntry entry = classpath[j];
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT
&& entry.getPath().equals(projectPath)) {
this.projectsToUpdate.add(project);
}
}
}
} catch (JavaModelException e) {
}
}
/*
* Adds the given project and its dependents to the list of the projects
* to update.
*/
void addToProjectsToUpdateWithDependents(IProject project) {
this.projectsToUpdate.add(JavaCore.create(project));
this.addDependentsToProjectsToUpdate(project.getFullPath());
}
/**
* Adds the given child handle to its parent's cache of children.
*/
protected void addToParentInfo(Openable child) {
Openable parent = (Openable) child.getParent();
if (parent != null && parent.isOpen()) {
try {
JavaElementInfo info = parent.getElementInfo();
info.addChild(child);
} catch (JavaModelException e) {
// do nothing - we already checked if open
}
}
}
/**
* Check whether the updated file is affecting some of the properties of a given project (like
* its classpath persisted as a file).
* Also force classpath problems to be refresh if not running in autobuild mode.
* NOTE: It can induce resource changes, and cannot be called during POST_CHANGE notification.
*
*/
public void performPreBuildCheck(
IResourceDelta delta,
IJavaElement parent) {
try {
if (!ResourcesPlugin.getWorkspace().isAutoBuilding()) {
Iterator iterator = this.projectsToUpdate.iterator();
while (iterator.hasNext()) {
try {
JavaProject project = (JavaProject)iterator.next();
// force classpath marker refresh
project.getResolvedClasspath(
true, // ignoreUnresolvedEntry
true); // generateMarkerOnError
} catch (JavaModelException e) {
}
}
if (!this.projectsToUpdate.isEmpty()){
try {
// update all cycle markers
JavaProject.updateAllCycleMarkers();
} catch (JavaModelException e) {
}
}
}
} finally {
this.projectsToUpdate = new HashSet();
}
IResource resource = delta.getResource();
IJavaElement element = JavaCore.create(resource);
boolean processChildren = false;
switch (resource.getType()) {
case IResource.ROOT :
case IResource.PROJECT :
if (delta.getKind() == IResourceDelta.CHANGED) {
processChildren = true;
}
break;
case IResource.FILE :
if (parent.getElementType() == IJavaElement.JAVA_PROJECT) {
IFile file = (IFile) resource;
JavaProject project = (JavaProject) parent;
/* check classpath property file change */
QualifiedName classpathProp;
if (file.getName().equals(
project.computeSharedPropertyFileName(
classpathProp = project.getClasspathPropertyName()))) {
switch (delta.getKind()) {
case IResourceDelta.REMOVED : // recreate one based on in-memory path
try {
project.saveClasspath(project.getRawClasspath(), project.getOutputLocation());
} catch (JavaModelException e) {
}
break;
case IResourceDelta.CHANGED :
if ((delta.getFlags() & IResourceDelta.CONTENT) == 0)
break; // only consider content change
case IResourceDelta.ADDED :
// check if any actual difference
try {
// force to (re)read the property file
String fileClasspathString = project.getSharedProperty(classpathProp);
if (fileClasspathString == null)
break; // did not find the file
IClasspathEntry[] fileEntries = project.readPaths(fileClasspathString);
if (fileEntries == null)
break; // could not read, ignore
if (project.isClasspathEqualsTo(project.getRawClasspath(), project.getOutputLocation(), fileEntries))
break;
// will force an update of the classpath/output location based on the file information
// extract out the output location
IPath outputLocation = null;
if (fileEntries != null && fileEntries.length > 0) {
IClasspathEntry entry = fileEntries[fileEntries.length - 1];
if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
outputLocation = entry.getPath();
IClasspathEntry[] copy = new IClasspathEntry[fileEntries.length - 1];
System.arraycopy(fileEntries, 0, copy, 0, copy.length);
fileEntries = copy;
}
}
// restore output location
if (outputLocation == null) {
outputLocation = SetClasspathOperation.ReuseOutputLocation;
}
try {
project.setRawClasspath(
fileEntries,
outputLocation,
null, // monitor
true, // canChangeResource
false, // forceSave
project.getResolvedClasspath(true), // ignoreUnresolvedVariable
true); // needCycleCheck
} catch (JavaModelException e) {
}
} catch (IOException e) {
break;
} catch (RuntimeException e) {
break;
} catch (CoreException e) {
break;
}
}
}
}
break;
}
if (processChildren) {
IResourceDelta[] children = delta.getAffectedChildren();
for (int i = 0; i < children.length; i++) {
performPreBuildCheck(children[i], element);
}
}
}
/**
* Closes the given element, which removes it from the cache of open elements.
*/
protected static void close(Openable element) {
try {
element.close();
} catch (JavaModelException e) {
// do nothing
}
}
private void cloneCurrentDelta(IJavaProject project, IPackageFragmentRoot root) {
JavaElementDelta delta = (JavaElementDelta)fCurrentDelta.find(root);
if (delta == null) return;
JavaElementDelta clone = (JavaElementDelta)delta.clone(project);
fCurrentDelta.insertDeltaTree(clone.getElement(), clone);
switch (clone.getKind()) {
case IJavaElementDelta.ADDED:
this.addToParentInfo((Openable)clone.getElement());
break;
case IJavaElementDelta.REMOVED:
Openable element = (Openable)clone.getElement();
if (element.isOpen()) {
try {
element.close();
} catch (JavaModelException e) {
}
}
this.removeFromParentInfo(element);
break;
}
}
/**
* Generic processing for elements with changed contents:<ul>
* <li>The element is closed such that any subsequent accesses will re-open
* the element reflecting its new structure.
* <li>An entry is made in the delta reporting a content change (K_CHANGE with F_CONTENT flag set).
* </ul>
*/
protected void contentChanged(Openable element, IResourceDelta delta) {
close(element);
fCurrentDelta.changed(element, IJavaElementDelta.F_CONTENT);
}
/**
* Creates the openables corresponding to this resource.
* Returns null if none was found.
*/
protected Openable createElement(IResource resource, int elementType, IJavaProject project) {
if (resource == null) return null;
IPath path = resource.getFullPath();
IJavaElement element = null;
switch (elementType) {
case IJavaElement.JAVA_PROJECT:
this.popUntilPrefixOf(path);
if (this.currentElement != null) return this.currentElement;
IProject proj = (IProject)resource;
boolean isOpened = proj.isOpen();
if (isOpened && this.hasJavaNature(proj)) {
element = project == null ? JavaCore.create(proj) : project;
} else if (!isOpened) {
if (project == null) {
project = JavaCore.create(proj);
}
if (project.isOpen()) {
element = project; // java project is being closed or removed
}
} // else not a java-project
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
element = project == null ? JavaCore.create(resource) : project.getPackageFragmentRoot(resource);
break;
case IJavaElement.PACKAGE_FRAGMENT:
// find the element that encloses the resource
this.popUntilPrefixOf(path);
if (this.currentElement == null) {
element = JavaModelManager.getJavaModelManager().create(resource, project);
} else {
// find the root
IPackageFragmentRoot root = this.currentElement.getPackageFragmentRoot();
if (root == null) {
element = JavaModelManager.getJavaModelManager().create(resource, project);
} else if (!JavaModelManager.conflictsWithOutputLocation(path, (JavaProject)root.getJavaProject())) {
// create package handle
IPath pkgPath = path.removeFirstSegments(root.getPath().segmentCount());
String pkg = Util.packageName(pkgPath);
if (pkg == null) return null;
element = root.getPackageFragment(pkg);
}
}
break;
case IJavaElement.COMPILATION_UNIT:
case IJavaElement.CLASS_FILE:
// find the element that encloses the resource
this.popUntilPrefixOf(path);
if (this.currentElement == null) {
element = element = JavaModelManager.getJavaModelManager().create(resource, project);
} else {
// find the package
IPackageFragment pkgFragment = null;
switch (this.currentElement.getElementType()) {
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
IPackageFragmentRoot root = (IPackageFragmentRoot)this.currentElement;
IPath rootPath = root.getPath();
IPath pkgPath = path.removeLastSegments(1);
String pkgName = Util.packageName(pkgPath.removeFirstSegments(rootPath.segmentCount()));
if (pkgName != null) {
pkgFragment = root.getPackageFragment(pkgName);
}
break;
case IJavaElement.PACKAGE_FRAGMENT:
Openable pkg = (Openable)this.currentElement;
if (pkg.getPath().equals(path.removeLastSegments(1))) {
pkgFragment = (IPackageFragment)pkg;
} // else case of package x which is a prefix of x.y
break;
case IJavaElement.COMPILATION_UNIT:
case IJavaElement.CLASS_FILE:
pkgFragment = (IPackageFragment)this.currentElement.getParent();
break;
}
if (pkgFragment == null) {
element = JavaModelManager.getJavaModelManager().create(resource, project);
} else {
if (elementType == IJavaElement.COMPILATION_UNIT) {
// create compilation unit handle
// fileName validation has been done in elementType(IResourceDelta, int, boolean)
String fileName = path.lastSegment();
element = pkgFragment.getCompilationUnit(fileName);
} else {
// create class file handle
// fileName validation has been done in elementType(IResourceDelta, int, boolean)
String fileName = path.lastSegment();
element = pkgFragment.getClassFile(fileName);
}
}
}
break;
}
if (element == null) {
return null;
} else {
this.currentElement = (Openable)element;
return this.currentElement;
}
}
/**
* Processing for an element that has been added:<ul>
* <li>If the element is a project, do nothing, and do not process
* children, as when a project is created it does not yet have any
* natures - specifically a java nature.
* <li>If the elemet is not a project, process it as added (see
* <code>basicElementAdded</code>.
* </ul>
*/
protected void elementAdded(Openable element, IResourceDelta delta) {
int elementType = element.getElementType();
if (elementType == IJavaElement.JAVA_PROJECT) {
// project add is handled by JavaProject.configure() because
// when a project is created, it does not yet have a java nature
if (hasJavaNature((IProject)delta.getResource())) {
addToParentInfo(element);
if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
Openable movedFromElement = (Openable)element.getJavaModel().getJavaProject(delta.getMovedFromPath().lastSegment());
fCurrentDelta.movedTo(element, movedFromElement);
} else {
fCurrentDelta.added(element);
}
this.projectsToUpdate.add(element);
}
} else {
addToParentInfo(element);
// Force the element to be closed as it might have been opened
// before the resource modification came in and it might have a new child
// For example, in an IWorkspaceRunnable:
// 1. create a package fragment p using a java model operation
// 2. open package p
// 3. add file X.java in folder p
// When the resource delta comes in, only the addition of p is notified,
// but the package p is already opened, thus its children are not recomputed
// and it appears empty.
close(element);
if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
IPath movedFromPath = delta.getMovedFromPath();
IResource res = delta.getResource();
IResource movedFromRes;
if (res instanceof IFile) {
movedFromRes = res.getWorkspace().getRoot().getFile(movedFromPath);
} else {
movedFromRes = res.getWorkspace().getRoot().getFolder(movedFromPath);
}
// find the element type of the moved from element
IJavaProject projectOfRoot = (IJavaProject)this.roots.get(movedFromPath);
boolean isPkgFragmentRoot =
projectOfRoot != null
&& (projectOfRoot.getProject().getFullPath().isPrefixOf(movedFromPath));
int movedFromType =
this.elementType(
movedFromRes,
delta.getKind(),
delta.getFlags(),
element.getParent().getElementType(),
isPkgFragmentRoot);
// create the moved from element
Openable movedFromElement =
elementType != IJavaElement.JAVA_PROJECT && movedFromType == IJavaElement.JAVA_PROJECT ?
null : // outside classpath
this.createElement(movedFromRes, movedFromType, null); // pass null for the project in case the element is moving to another project
if (movedFromElement == null) {
// moved from outside classpath
fCurrentDelta.added(element);
} else {
fCurrentDelta.movedTo(element, movedFromElement);
}
} else {
fCurrentDelta.added(element);
}
switch (elementType) {
case IJavaElement.PACKAGE_FRAGMENT_ROOT :
// when a root is added, and is on the classpath, the project must be updated
this.projectsToUpdate.add(element.getJavaProject());
break;
case IJavaElement.PACKAGE_FRAGMENT :
// get rid of namelookup since it holds onto obsolete cached info
JavaProject project = (JavaProject) element.getJavaProject();
try {
project.getJavaProjectElementInfo().setNameLookup(null);
} catch (JavaModelException e) {
}
// add subpackages
PackageFragmentRoot root = element.getPackageFragmentRoot();
String name = element.getElementName();
IResourceDelta[] children = delta.getAffectedChildren();
for (int i = 0, length = children.length; i < length; i++) {
IResourceDelta child = children[i];
IResource resource = child.getResource();
if (resource instanceof IFolder) {
String subpkgName =
name.length() == 0 ?
resource.getName() :
name + "." + resource.getName(); //$NON-NLS-1$
Openable subpkg = (Openable)root.getPackageFragment(subpkgName);
this.updateIndex(subpkg, child);
this.elementAdded(subpkg, child);
}
}
break;
}
}
}
/**
* Generic processing for a removed element:<ul>
* <li>Close the element, removing its structure from the cache
* <li>Remove the element from its parent's cache of children
* <li>Add a REMOVED entry in the delta
* </ul>
*/
protected void elementRemoved(Openable element, IResourceDelta delta) {
if (element.isOpen()) {
close(element);
}
removeFromParentInfo(element);
int elementType = element.getElementType();
if ((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
IPath movedToPath = delta.getMovedToPath();
IResource res = delta.getResource();
IResource movedToRes;
switch (res.getType()) {
case IResource.PROJECT:
movedToRes = res.getWorkspace().getRoot().getProject(movedToPath.lastSegment());
break;
case IResource.FOLDER:
movedToRes = res.getWorkspace().getRoot().getFolder(movedToPath);
break;
case IResource.FILE:
movedToRes = res.getWorkspace().getRoot().getFile(movedToPath);
break;
default:
return;
}
// find the element type of the moved from element
IJavaProject projectOfRoot = (IJavaProject)this.roots.get(movedToPath);
boolean isPkgFragmentRoot =
projectOfRoot != null
&& (projectOfRoot.getProject().getFullPath().isPrefixOf(movedToPath));
int movedToType =
this.elementType(
movedToRes,
delta.getKind(),
delta.getFlags(),
element.getParent().getElementType(),
isPkgFragmentRoot);
// create the moved To element
Openable movedToElement =
elementType != IJavaElement.JAVA_PROJECT && movedToType == IJavaElement.JAVA_PROJECT ?
null : // outside classpath
this.createElement(movedToRes, movedToType, null); // pass null for the project in case the element is moving to another project
if (movedToElement == null) {
// moved outside classpath
fCurrentDelta.removed(element);
} else {
fCurrentDelta.movedFrom(element, movedToElement);
}
} else {
fCurrentDelta.removed(element);
}
switch (elementType) {
case IJavaElement.JAVA_MODEL :
element.getJavaModelManager().getIndexManager().reset();
break;
case IJavaElement.JAVA_PROJECT :
JavaModelManager.getJavaModelManager().removePerProjectInfo(
(JavaProject) element);
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT :
this.projectsToUpdate.add(element.getJavaProject());
break;
case IJavaElement.PACKAGE_FRAGMENT :
//1G1TW2T - get rid of namelookup since it holds onto obsolete cached info
JavaProject project = (JavaProject) element.getJavaProject();
try {
project.getJavaProjectElementInfo().setNameLookup(null);
} catch (JavaModelException e) {
}
// remove subpackages
PackageFragmentRoot root = element.getPackageFragmentRoot();
String name = element.getElementName();
IResourceDelta[] children = delta.getAffectedChildren();
for (int i = 0, length = children.length; i < length; i++) {
IResourceDelta child = children[i];
IResource resource = child.getResource();
if (resource instanceof IFolder) {
String subpkgName =
name.length() == 0 ?
resource.getName() :
name + "." + resource.getName(); //$NON-NLS-1$
Openable subpkg = (Openable)root.getPackageFragment(subpkgName);
this.updateIndex(subpkg, child);
this.elementRemoved(subpkg, child);
}
}
break;
}
}
/**
* Filters the generated <code>JavaElementDelta</code>s to remove those
* which should not be fired (because they don't represent a real change
* in the Java Model).
*/
protected IJavaElementDelta[] filterRealDeltas(IJavaElementDelta[] deltas) {
int length = deltas.length;
IJavaElementDelta[] realDeltas = null;
int index = 0;
for (int i = 0; i < length; i++) {
JavaElementDelta delta = (JavaElementDelta)deltas[i];
if (delta == null) {
continue;
}
if (delta.getAffectedChildren().length > 0
|| delta.getKind() == IJavaElementDelta.ADDED
|| delta.getKind() == IJavaElementDelta.REMOVED
|| (delta.getFlags() & IJavaElementDelta.F_CLOSED) != 0
|| (delta.getFlags() & IJavaElementDelta.F_OPENED) != 0
|| delta.resourceDeltasCounter > 0) {
if (realDeltas == null) {
realDeltas = new IJavaElementDelta[length];
}
realDeltas[index++] = delta;
}
}
if (index > 0) {
IJavaElementDelta[] result = new IJavaElementDelta[index];
System.arraycopy(realDeltas, 0, result, 0, index);
return result;
} else {
return NO_DELTA;
}
}
/**
* Returns true if the given resource is contained in an open project
* with a java nature, otherwise false.
*/
protected boolean hasJavaNature(IResource resource) {
// ensure the project has a java nature (if open)
IProject project = resource.getProject();
if (project.isOpen()) {
try {
return project.hasNature(JavaCore.NATURE_ID);
} catch (CoreException e) {
// do nothing
}
}
return false;
}
private JavaModelException newInvalidElementType() {
return new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES));
}
/**
* Generic processing for elements with changed contents:<ul>
* <li>The element is closed such that any subsequent accesses will re-open
* the element reflecting its new structure.
* <li>An entry is made in the delta reporting a content change (K_CHANGE with F_CONTENT flag set).
* </ul>
*/
protected void nonJavaResourcesChanged(Openable element, IResourceDelta delta)
throws JavaModelException {
// reset non-java resources if element was open
if (element.isOpen()) {
JavaElementInfo info = element.getElementInfo();
switch (element.getElementType()) {
case IJavaElement.JAVA_PROJECT :
((JavaProjectElementInfo) info).setNonJavaResources(null);
// if a package fragment root is the project, clear it too
PackageFragmentRoot projectRoot =
(PackageFragmentRoot) ((JavaProject) element).getPackageFragmentRoot(
new Path(IPackageFragment.DEFAULT_PACKAGE_NAME));
if (projectRoot.isOpen()) {
((PackageFragmentRootInfo) projectRoot.getElementInfo()).setNonJavaResources(
null);
}
break;
case IJavaElement.PACKAGE_FRAGMENT :
((PackageFragmentInfo) info).setNonJavaResources(null);
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT :
((PackageFragmentRootInfo) info).setNonJavaResources(null);
}
}
JavaElementDelta elementDelta = fCurrentDelta.find(element);
if (elementDelta == null) {
fCurrentDelta.changed(element, IJavaElementDelta.F_CONTENT);
elementDelta = fCurrentDelta.find(element);
}
elementDelta.addResourceDelta(delta);
}
private void popUntilPrefixOf(IPath path) {
while (this.currentElement != null) {
IPath currentElementPath = null;
if (this.currentElement instanceof IPackageFragmentRoot) {
currentElementPath = ((IPackageFragmentRoot)this.currentElement).getPath();
} else {
IResource currentElementResource = null;
try {
currentElementResource = this.currentElement.getUnderlyingResource();
} catch (JavaModelException e) {
}
if (currentElementResource != null) {
currentElementPath = currentElementResource.getFullPath();
}
}
if (currentElementPath != null) {
if (this.currentElement instanceof IPackageFragment
&& this.currentElement.getElementName().length() == 0
&& currentElementPath.segmentCount() != path.segmentCount()-1) {
// default package and path is not a direct child
this.currentElement = (Openable)this.currentElement.getParent();
}
if (currentElementPath.isPrefixOf(path)) {
return;
}
}
this.currentElement = (Openable)this.currentElement.getParent();
}
}
/**
* Converts a <code>IResourceDelta</code> rooted in a <code>Workspace</code> into
* the corresponding set of <code>IJavaElementDelta</code>, rooted in the
* relevant <code>JavaModel</code>s.
*/
public IJavaElementDelta[] processResourceDelta(IResourceDelta changes, int eventType) {
try {
this.currentEventType = eventType;
IJavaModel model = JavaModelManager.getJavaModelManager().getJavaModel();
if (!model.isOpen()) {
// force opening of java model so that java element delta are reported
try {
model.open(null);
} catch (JavaModelException e) {
if (VERBOSE) {
e.printStackTrace();
}
return NO_DELTA;
}
}
this.initializeRoots(model);
this.currentElement = null;
// get the workspace delta, and start processing there.
IResourceDelta[] deltas = changes.getAffectedChildren();
IJavaElementDelta[] translatedDeltas = new JavaElementDelta[deltas.length];
for (int i = 0; i < deltas.length; i++) {
IResourceDelta delta = deltas[i];
IResource res = delta.getResource();
fCurrentDelta = new JavaElementDelta(model);
// find out whether the delta is a package fragment root
IJavaProject projectOfRoot = (IJavaProject)this.roots.get(res.getFullPath());
boolean isPkgFragmentRoot = projectOfRoot != null;
int elementType =
this.elementType(
res,
delta.getKind(),
delta.getFlags(),
IJavaElement.JAVA_MODEL,
isPkgFragmentRoot);
this.traverseDelta(delta, elementType, projectOfRoot, null, IGNORE); // traverse delta
translatedDeltas[i] = fCurrentDelta;
}
// update package fragment roots of projects that were affected
Iterator iterator = this.projectsToUpdate.iterator();
while (iterator.hasNext()) {
JavaProject project = (JavaProject)iterator.next();
project.updatePackageFragmentRoots();
}
return filterRealDeltas(translatedDeltas);
} finally {
this.projectsToUpdate = new HashSet();
}
}
/*
* Update the current delta (ie. add/remove/change the given element) and update the correponding index.
* Returns whether the children of the given delta must be processed.
* @throws a JavaModelException if the delta doesn't correspond to a java element of the given type.
*/
private boolean updateCurrentDeltaAndIndex(IResourceDelta delta, int elementType, IJavaProject project) throws JavaModelException {
Openable element;
switch (delta.getKind()) {
case IResourceDelta.ADDED :
element = this.createElement(delta.getResource(), elementType, project);
if (element == null) throw newInvalidElementType();
this.updateIndex(element, delta);
this.elementAdded(element, delta);
return false;
case IResourceDelta.REMOVED :
element = this.createElement(delta.getResource(), elementType, project);
if (element == null) throw newInvalidElementType();
this.updateIndex(element, delta);
this.elementRemoved(element, delta);
return false;
case IResourceDelta.CHANGED :
int flags = delta.getFlags();
if ((flags & IResourceDelta.CONTENT) != 0) {
// content has changed
element = this.createElement(delta.getResource(), elementType, project);
if (element == null) throw newInvalidElementType();
this.updateIndex(element, delta);
this.contentChanged(element, delta);
} else if (elementType == IJavaElement.JAVA_PROJECT) {
if ((flags & IResourceDelta.OPEN) != 0) {
// project has been opened or closed
IProject res = (IProject)delta.getResource();
element = this.createElement(res, elementType, project);
if (element == null) throw newInvalidElementType();
if (res.isOpen()) {
if (this.hasJavaNature(res)) {
this.elementAdded(element, delta);
this.indexManager.indexAll(res);
}
} else {
JavaModel javaModel = JavaModelManager.getJavaModelManager().getJavaModel();
boolean wasJavaProject = javaModel.findJavaProject(res) != null;
if (wasJavaProject) {
this.elementRemoved(element, delta);
this.indexManager.removeIndex(res.getFullPath());
}
}
return false; // when a project is open/closed don't process children
}
if ((flags & IResourceDelta.DESCRIPTION) != 0) {
IProject res = (IProject)delta.getResource();
JavaModel javaModel = JavaModelManager.getJavaModelManager().getJavaModel();
boolean wasJavaProject = javaModel.findJavaProject(res) != null;
boolean isJavaProject = this.hasJavaNature(res);
if (wasJavaProject != isJavaProject) {
// project's nature has been added or removed
element = this.createElement(res, elementType, project);
if (element == null) throw newInvalidElementType();
if (isJavaProject) {
this.elementAdded(element, delta);
this.indexManager.indexAll(res);
} else {
this.elementRemoved(element, delta);
this.indexManager.removeIndex(res.getFullPath());
}
return false; // when a project's nature is added/removed don't process children
}
}
}
return true;
}
return true;
}
/**
* Removes the given element from its parents cache of children. If the
* element does not have a parent, or the parent is not currently open,
* this has no effect.
*/
protected void removeFromParentInfo(Openable child) {
Openable parent = (Openable) child.getParent();
if (parent != null && parent.isOpen()) {
try {
JavaElementInfo info = parent.getElementInfo();
info.removeChild(child);
} catch (JavaModelException e) {
// do nothing - we already checked if open
}
}
}
/**
* Converts an <code>IResourceDelta</code> and its children into
* the corresponding <code>IJavaElementDelta</code>s.
* Return whether the delta corresponds to a resource on the classpath.
* If it is not a resource on the classpath, it will be added as a non-java
* resource by the sender of this method.
*/
protected boolean traverseDelta(
IResourceDelta delta,
int elementType,
IJavaProject currentProject,
IPath currentOutput,
int outputTraverseMode) {
IResource res = delta.getResource();
// process current delta
boolean processChildren = true;
if (currentProject != null || res instanceof IProject) {
if (this.currentElement == null || !this.currentElement.getJavaProject().equals(currentProject)) {
// force the currentProject to be used
this.currentElement = (Openable)currentProject;
}
try {
processChildren = this.updateCurrentDeltaAndIndex(delta, elementType, currentProject);
} catch (JavaModelException e) {
// non java resource or invalid project
return false;
}
} else {
// not yet inside a package fragment root
processChildren = true;
}
// get the project's output location
if (currentOutput == null) {
try {
IJavaProject proj =
currentProject == null ?
(IJavaProject)this.createElement(res.getProject(), IJavaElement.JAVA_PROJECT, null) :
currentProject;
if (proj != null) {
currentOutput = proj.getOutputLocation();
if (proj.getProject().getFullPath().equals(currentOutput)){ // case of proj==bin==src
outputTraverseMode = SOURCE;
} else {
// check case of src==bin
IClasspathEntry[] classpath = proj.getResolvedClasspath(true);
for (int i = 0, length = classpath.length; i < length; i++) {
IClasspathEntry entry = classpath[i];
if (entry.getPath().equals(currentOutput)) {
outputTraverseMode = (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) ? SOURCE : BINARY;
break;
}
}
}
}
} catch (JavaModelException e) {
}
}
// process children if needed
if (processChildren) {
IResourceDelta[] children = delta.getAffectedChildren();
boolean oneChildOnClasspath = false;
int length = children.length;
IResourceDelta[] orphanChildren = new IResourceDelta[length];
Openable parent = null;
boolean isValidParent = true;
for (int i = 0; i < length; i++) {
IResourceDelta child = children[i];
IResource childRes = child.getResource();
IPath childPath = childRes.getFullPath();
// find out whether the child is a package fragment root of the current project
IJavaProject projectOfRoot = (IJavaProject)this.roots.get(childPath);
boolean isPkgFragmentRoot =
projectOfRoot != null
&& (projectOfRoot.getProject().getFullPath().isPrefixOf(childPath));
int childType =
this.elementType(
childRes,
child.getKind(),
child.getFlags(),
elementType,
isPkgFragmentRoot);
// filter out changes in output location
if (currentOutput != null && currentOutput.isPrefixOf(childPath)) {
if (outputTraverseMode != IGNORE) {
// case of bin=src
if (outputTraverseMode == SOURCE && childType == IJavaElement.CLASS_FILE) {
continue;
}
// case of .class file under project and no source folder
// proj=bin
if (childType == IJavaElement.JAVA_PROJECT
&& childRes instanceof IFile
&& Util.isValidClassFileName(childRes.getName())) {
continue;
}
} else {
continue;
}
}
// traverse delta for child in the same project
if (childType == -1
|| !this.traverseDelta(child, childType, (currentProject == null && isPkgFragmentRoot) ? projectOfRoot : currentProject, currentOutput, outputTraverseMode)) {
try {
if (currentProject != null) {
if (!isValidParent) continue;
if (parent == null) {
if (this.currentElement == null || !this.currentElement.getJavaProject().equals(currentProject)) {
// force the currentProject to be used
this.currentElement = (Openable)currentProject;
}
if (elementType == IJavaElement.JAVA_PROJECT
|| (elementType == IJavaElement.PACKAGE_FRAGMENT_ROOT && res instanceof IProject)) {
// NB: attach non-java resource to project (not to its package fragment root)
parent = (Openable)currentProject;
} else {
parent = this.createElement(res, elementType, currentProject);
}
if (parent == null) {
isValidParent = false;
continue;
}
}
// add child as non java resource
nonJavaResourcesChanged(parent, child);
} else {
orphanChildren[i] = child;
}
} catch (JavaModelException e) {
}
} else {
oneChildOnClasspath = true;
}
// if child is a package fragment root of another project, traverse delta too
if (projectOfRoot != null && !isPkgFragmentRoot) {
this.traverseDelta(child, IJavaElement.PACKAGE_FRAGMENT_ROOT, projectOfRoot, null, IGNORE); // binary output of projectOfRoot cannot be this root
// NB: No need to check the return value as the child can only be on the classpath
}
// if the child is a package fragment root of one or several other projects
HashSet set;
if ((set = (HashSet)this.otherRoots.get(childPath)) != null) {
IPackageFragmentRoot currentRoot =
(currentProject == null ?
projectOfRoot :
currentProject).getPackageFragmentRoot(childRes);
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
IJavaProject project = (IJavaProject) iterator.next();
this.cloneCurrentDelta(project, currentRoot);
}
}
}
if (oneChildOnClasspath || res instanceof IProject) {
// add orphan children (case of non java resources under project)
JavaProject adoptiveProject = (JavaProject)JavaCore.getJavaCore().create(res.getProject());
if (adoptiveProject != null) {
for (int i = 0; i < length; i++) {
if (orphanChildren[i] != null) {
try {
nonJavaResourcesChanged(adoptiveProject, orphanChildren[i]);
} catch (JavaModelException e) {
}
}
}
}
} // else resource delta will be added by parent
return isValidParent && (currentProject != null || oneChildOnClasspath);
} else {
// if not on classpath or if the element type is -1,
// it's a non-java resource
return currentProject != null && elementType != -1;
}
}
/*
* Returns the type of the java element the given delta matches to.
* Returns -1 if unknown (e.g. a non-java resource.)
*/
private int elementType(IResource res, int kind, int flags, int parentType, boolean isPkgFragmentRoot) {
switch (parentType) {
case IJavaElement.JAVA_MODEL:
if (kind != IResourceDelta.CHANGED
|| (flags & IResourceDelta.OPEN) != 0
|| (flags & IResourceDelta.DESCRIPTION) != 0) {
// project is added, removed, opened or closed, or its nature is changed
return IJavaElement.JAVA_PROJECT;
} // else see below
case IJavaElement.JAVA_PROJECT:
if (isPkgFragmentRoot) {
return IJavaElement.PACKAGE_FRAGMENT_ROOT;
} else {
return IJavaElement.JAVA_PROJECT; // not yet in a package fragment root
}
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
case IJavaElement.PACKAGE_FRAGMENT:
if (res instanceof IFolder) {
if (Util.isValidFolderNameForPackage(res.getName())) {
return IJavaElement.PACKAGE_FRAGMENT;
} else {
return -1;
}
} else {
String fileName = res.getName();
if (Util.isValidCompilationUnitName(fileName)) {
return IJavaElement.COMPILATION_UNIT;
} else if (Util.isValidClassFileName(fileName)) {
return IJavaElement.CLASS_FILE;
} else if (this.roots.get(res.getFullPath()) != null) {
// case of proj=src=bin and resource is a jar file on the classpath
return IJavaElement.PACKAGE_FRAGMENT_ROOT;
} else {
return -1;
}
}
default:
return -1;
}
}
private void initializeRoots(IJavaModel model) {
this.roots = new HashMap();
this.otherRoots = new HashMap();
IJavaProject[] projects;
try {
projects = model.getJavaProjects();
} catch (JavaModelException e) {
// nothing can be done
return;
}
for (int i = 0, length = projects.length; i < length; i++) {
IJavaProject project = projects[i];
IClasspathEntry[] classpath;
try {
classpath = project.getResolvedClasspath(true);
} catch (JavaModelException e) {
// continue with next project
continue;
}
for (int j= 0, classpathLength = classpath.length; j < classpathLength; j++) {
IClasspathEntry entry = classpath[j];
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) continue;
IPath path = entry.getPath();
if (this.roots.get(path) == null) {
this.roots.put(path, project);
} else {
HashSet set = (HashSet)this.otherRoots.get(path);
if (set == null) {
set = new HashSet();
this.otherRoots.put(path, set);
}
set.add(project);
}
}
}
}
private boolean isOnClasspath(IPath path) {
return this.roots.get(path) != null;
}
protected void updateIndex(Openable element, IResourceDelta delta) {
if (indexManager == null)
return;
switch (element.getElementType()) {
case IJavaElement.JAVA_PROJECT :
switch (delta.getKind()) {
case IResourceDelta.ADDED :
indexManager.indexAll(element.getJavaProject().getProject());
break;
case IResourceDelta.REMOVED :
indexManager.removeIndex(element.getJavaProject().getProject().getFullPath());
break;
// NB: Update of index if project is opened, closed, or its java nature is added or removed
// is done in updateCurrentDeltaAndIndex
}
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT :
if (element instanceof JarPackageFragmentRoot) {
JarPackageFragmentRoot root = (JarPackageFragmentRoot)element;
// index jar file only once (if the root is in its declaring project)
IPath jarPath = root.getPath();
switch (delta.getKind()) {
case IResourceDelta.ADDED:
// index the new jar
indexManager.indexLibrary(jarPath, root.getJavaProject().getProject());
break;
case IResourceDelta.CHANGED:
// first remove the index so that it is forced to be re-indexed
indexManager.removeIndex(jarPath);
// then index the jar
indexManager.indexLibrary(jarPath, root.getJavaProject().getProject());
break;
case IResourceDelta.REMOVED:
// the jar was physically removed: remove the index
indexManager.removeIndex(jarPath);
break;
}
break;
} else {
int kind = delta.getKind();
if (kind == IResourceDelta.ADDED || kind == IResourceDelta.REMOVED) {
IPackageFragmentRoot root = (IPackageFragmentRoot)element;
this.updateRootIndex(root, root.getPackageFragment(""), delta); //$NON-NLS-1$
break;
}
}
// don't break as packages of the package fragment root can be indexed below
case IJavaElement.PACKAGE_FRAGMENT :
switch (delta.getKind()) {
case IResourceDelta.ADDED:
case IResourceDelta.REMOVED:
IPackageFragment pkg = null;
if (element instanceof IPackageFragmentRoot) {
IPackageFragmentRoot root = (IPackageFragmentRoot)element;
pkg = root.getPackageFragment(""); //$NON-NLS-1$
} else {
pkg = (IPackageFragment)element;
}
IResourceDelta[] children = delta.getAffectedChildren();
for (int i = 0, length = children.length; i < length; i++) {
IResourceDelta child = children[i];
IResource resource = child.getResource();
if (resource instanceof IFile) {
String extension = resource.getFileExtension();
if ("java".equalsIgnoreCase(extension)) { //$NON-NLS-1$
Openable cu = (Openable)pkg.getCompilationUnit(resource.getName());
this.updateIndex(cu, child);
} else if ("class".equalsIgnoreCase(extension)) { //$NON-NLS-1$
Openable classFile = (Openable)pkg.getClassFile(resource.getName());
this.updateIndex(classFile, child);
}
}
}
break;
}
break;
case IJavaElement.CLASS_FILE :
IFile file = (IFile) delta.getResource();
IJavaProject project = element.getJavaProject();
IPath binaryFolderPath = element.getPackageFragmentRoot().getPath();
// if the class file is part of the binary output, it has been created by
// the java builder -> ignore
try {
if (binaryFolderPath.equals(project.getOutputLocation())) {
break;
}
} catch (JavaModelException e) {
}
switch (delta.getKind()) {
case IResourceDelta.CHANGED :
// no need to index if the content has not changed
if ((delta.getFlags() & IResourceDelta.CONTENT) == 0)
break;
case IResourceDelta.ADDED :
indexManager.addBinary(file, binaryFolderPath);
break;
case IResourceDelta.REMOVED :
indexManager.remove(file.getFullPath().toString(), binaryFolderPath);
break;
}
break;
case IJavaElement.COMPILATION_UNIT :
file = (IFile) delta.getResource();
switch (delta.getKind()) {
case IResourceDelta.CHANGED :
// no need to index if the content has not changed
if ((delta.getFlags() & IResourceDelta.CONTENT) == 0)
break;
case IResourceDelta.ADDED :
indexManager.addSource(file, file.getProject().getProject().getFullPath());
break;
case IResourceDelta.REMOVED :
indexManager.remove(file.getFullPath().toString(), file.getProject().getProject().getFullPath());
break;
}
}
}
/**
* Upadtes the index of the given root (assuming it's an addition or a removal).
* This is done recusively, pkg being the current package.
*/
private void updateRootIndex(IPackageFragmentRoot root, IPackageFragment pkg, IResourceDelta delta) {
this.updateIndex((Openable)pkg, delta);
IResourceDelta[] children = delta.getAffectedChildren();
String name = pkg.getElementName();
for (int i = 0, length = children.length; i < length; i++) {
IResourceDelta child = children[i];
IResource resource = child.getResource();
if (resource instanceof IFolder) {
String subpkgName =
name.length() == 0 ?
resource.getName() :
name + "." + resource.getName(); //$NON-NLS-1$
IPackageFragment subpkg = root.getPackageFragment(subpkgName);
this.updateRootIndex(root, subpkg, child);
}
}
}
}