/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo;
import org.eclipse.jdt.internal.core.util.Util;
/**
* Keep the global states used during Java element delta processing.
*/
public class DeltaProcessingState implements IResourceChangeListener {
/*
* Collection of listeners for Java element deltas
*/
public IElementChangedListener[] elementChangedListeners= new IElementChangedListener[5];
public int[] elementChangedListenerMasks= new int[5];
public int elementChangedListenerCount= 0;
/*
* Collection of pre Java resource change listeners
*/
public IResourceChangeListener[] preResourceChangeListeners= new IResourceChangeListener[1];
public int[] preResourceChangeEventMasks= new int[1];
public int preResourceChangeListenerCount= 0;
/*
* The delta processor for the current thread.
*/
private ThreadLocal deltaProcessors= new ThreadLocal();
public void doNotUse() {
// reset the delta processor of the current thread to avoid to keep it in memory
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=269476
this.deltaProcessors.set(null);
}
/* A table from IPath (from a classpath entry) to DeltaProcessor.RootInfo */
public HashMap roots= new HashMap();
/* A table from IPath (from a classpath entry) to ArrayList of DeltaProcessor.RootInfo
* Used when an IPath corresponds to more than one root */
public HashMap otherRoots= new HashMap();
/* A table from IPath (from a classpath entry) to DeltaProcessor.RootInfo
* from the last time the delta processor was invoked. */
public HashMap oldRoots= new HashMap();
/* A table from IPath (from a classpath entry) to ArrayList of DeltaProcessor.RootInfo
* from the last time the delta processor was invoked.
* Used when an IPath corresponds to more than one root */
public HashMap oldOtherRoots= new HashMap();
/* A table from IPath (a source attachment path from a classpath entry) to IPath (a root path) */
public HashMap sourceAttachments= new HashMap();
/* A table from IJavaProject to IJavaProject[] (the list of direct dependent of the key) */
public HashMap projectDependencies= new HashMap();
/* Whether the roots tables should be recomputed */
public boolean rootsAreStale= true;
/* Threads that are currently running initializeRoots() */
private Set initializingThreads= Collections.synchronizedSet(new HashSet());
/* A table from file system absoulte path (String) to timestamp (Long) */
public Hashtable externalTimeStamps;
/*
* Map from IProject to ClasspathChange
* Note these changes need to be kept on the delta processing state to ensure we don't loose them
* (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=271102 Java model corrupt after switching target platform)
*/
private HashMap classpathChanges= new HashMap();
/* A table from JavaProject to ClasspathValidation */
private HashMap classpathValidations= new HashMap();
/* A table from JavaProject to ProjectReferenceChange */
private HashMap projectReferenceChanges= new HashMap();
/* A table from JavaProject to ExternalFolderChange */
private HashMap externalFolderChanges= new HashMap();
/**
* Workaround for bug 15168 circular errors not reported This is a cache of the projects before
* any project addition/deletion has started.
*/
private HashSet javaProjectNamesCache;
/*
* A list of IJavaElement used as a scope for external archives refresh during POST_CHANGE.
* This is null if no refresh is needed.
*/
private HashSet externalElementsToRefresh;
/*
* Need to clone defensively the listener information, in case some listener is reacting to some notification iteration by adding/changing/removing
* any of the other (for example, if it deregisters itself).
*/
public synchronized void addElementChangedListener(IElementChangedListener listener, int eventMask) {
for (int i= 0; i < this.elementChangedListenerCount; i++) {
if (this.elementChangedListeners[i] == listener) {
// only clone the masks, since we could be in the middle of notifications and one listener decide to change
// any event mask of another listeners (yet not notified).
int cloneLength= this.elementChangedListenerMasks.length;
System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks= new int[cloneLength], 0, cloneLength);
this.elementChangedListenerMasks[i]|= eventMask; // could be different
return;
}
}
// may need to grow, no need to clone, since iterators will have cached original arrays and max boundary and we only add to the end.
int length;
if ((length= this.elementChangedListeners.length) == this.elementChangedListenerCount) {
System.arraycopy(this.elementChangedListeners, 0, this.elementChangedListeners= new IElementChangedListener[length * 2], 0, length);
System.arraycopy(this.elementChangedListenerMasks, 0, this.elementChangedListenerMasks= new int[length * 2], 0, length);
}
this.elementChangedListeners[this.elementChangedListenerCount]= listener;
this.elementChangedListenerMasks[this.elementChangedListenerCount]= eventMask;
this.elementChangedListenerCount++;
}
/*
* Adds the given element to the list of elements used as a scope for external jars refresh.
*/
public synchronized void addForRefresh(IJavaElement externalElement) {
if (this.externalElementsToRefresh == null) {
this.externalElementsToRefresh= new HashSet();
}
this.externalElementsToRefresh.add(externalElement);
}
public synchronized void addPreResourceChangedListener(IResourceChangeListener listener, int eventMask) {
for (int i= 0; i < this.preResourceChangeListenerCount; i++) {
if (this.preResourceChangeListeners[i] == listener) {
this.preResourceChangeEventMasks[i]|= eventMask;
return;
}
}
// may need to grow, no need to clone, since iterators will have cached original arrays and max boundary and we only add to the end.
int length;
if ((length= this.preResourceChangeListeners.length) == this.preResourceChangeListenerCount) {
System.arraycopy(this.preResourceChangeListeners, 0, this.preResourceChangeListeners= new IResourceChangeListener[length * 2], 0, length);
System.arraycopy(this.preResourceChangeEventMasks, 0, this.preResourceChangeEventMasks= new int[length * 2], 0, length);
}
this.preResourceChangeListeners[this.preResourceChangeListenerCount]= listener;
this.preResourceChangeEventMasks[this.preResourceChangeListenerCount]= eventMask;
this.preResourceChangeListenerCount++;
}
public DeltaProcessor getDeltaProcessor() {
DeltaProcessor deltaProcessor= (DeltaProcessor)this.deltaProcessors.get();
if (deltaProcessor != null)
return deltaProcessor;
deltaProcessor= new DeltaProcessor(this, JavaModelManager.getJavaModelManager());
this.deltaProcessors.set(deltaProcessor);
return deltaProcessor;
}
public synchronized ClasspathChange addClasspathChange(IProject project, IClasspathEntry[] oldRawClasspath, IPath oldOutputLocation, IClasspathEntry[] oldResolvedClasspath) {
ClasspathChange change= (ClasspathChange)this.classpathChanges.get(project);
if (change == null) {
change= new ClasspathChange((JavaProject)JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(project), oldRawClasspath, oldOutputLocation, oldResolvedClasspath);
this.classpathChanges.put(project, change);
} else {
if (change.oldRawClasspath == null)
change.oldRawClasspath= oldRawClasspath;
if (change.oldOutputLocation == null)
change.oldOutputLocation= oldOutputLocation;
if (change.oldResolvedClasspath == null)
change.oldResolvedClasspath= oldResolvedClasspath;
}
return change;
}
public synchronized ClasspathChange getClasspathChange(IProject project) {
return (ClasspathChange)this.classpathChanges.get(project);
}
public synchronized HashMap removeAllClasspathChanges() {
HashMap result= this.classpathChanges;
this.classpathChanges= new HashMap(result.size());
return result;
}
public synchronized ClasspathValidation addClasspathValidation(JavaProject project) {
ClasspathValidation validation= (ClasspathValidation)this.classpathValidations.get(project);
if (validation == null) {
validation= new ClasspathValidation(project);
this.classpathValidations.put(project, validation);
}
return validation;
}
public synchronized void addExternalFolderChange(JavaProject project, IClasspathEntry[] oldResolvedClasspath) {
ExternalFolderChange change= (ExternalFolderChange)this.externalFolderChanges.get(project);
if (change == null) {
change= new ExternalFolderChange(project, oldResolvedClasspath);
this.externalFolderChanges.put(project, change);
}
}
public synchronized void addProjectReferenceChange(JavaProject project, IClasspathEntry[] oldResolvedClasspath) {
ProjectReferenceChange change= (ProjectReferenceChange)this.projectReferenceChanges.get(project);
if (change == null) {
change= new ProjectReferenceChange(project, oldResolvedClasspath);
this.projectReferenceChanges.put(project, change);
}
}
public void initializeRoots(boolean initAfterLoad) {
// recompute root infos only if necessary
HashMap[] rootInfos= null;
if (this.rootsAreStale) {
Thread currentThread= Thread.currentThread();
boolean addedCurrentThread= false;
try {
// if reentering initialization (through a container initializer for example) no need to compute roots again
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=47213
if (!this.initializingThreads.add(currentThread))
return;
addedCurrentThread= true;
// all classpaths in the workspace are going to be resolved
// ensure that containers are initialized in one batch
JavaModelManager.getJavaModelManager().forceBatchInitializations(initAfterLoad);
rootInfos= getRootInfos(false/*don't use previous session values*/);
} finally {
if (addedCurrentThread) {
this.initializingThreads.remove(currentThread);
}
}
}
synchronized (this) {
this.oldRoots= this.roots;
this.oldOtherRoots= this.otherRoots;
if (this.rootsAreStale && rootInfos != null) { // double check again
this.roots= rootInfos[0];
this.otherRoots= rootInfos[1];
this.sourceAttachments= rootInfos[2];
this.projectDependencies= rootInfos[3];
this.rootsAreStale= false;
}
}
}
synchronized void initializeRootsWithPreviousSession() {
HashMap[] rootInfos= getRootInfos(true/*use previous session values*/);
if (rootInfos != null) {
this.roots= rootInfos[0];
this.otherRoots= rootInfos[1];
this.sourceAttachments= rootInfos[2];
this.projectDependencies= rootInfos[3];
this.rootsAreStale= false;
}
}
private HashMap[] getRootInfos(boolean usePreviousSession) {
HashMap newRoots= new HashMap();
HashMap newOtherRoots= new HashMap();
HashMap newSourceAttachments= new HashMap();
HashMap newProjectDependencies= new HashMap();
IJavaModel model= JavaModelManager.getJavaModelManager().getJavaModel();
IJavaProject[] projects;
try {
projects= model.getJavaProjects();
} catch (JavaModelException e) {
// nothing can be done
return null;
}
for (int i= 0, length= projects.length; i < length; i++) {
JavaProject project= (JavaProject)projects[i];
IClasspathEntry[] classpath;
try {
if (usePreviousSession) {
PerProjectInfo perProjectInfo= project.getPerProjectInfo();
project.resolveClasspath(perProjectInfo, true/*use previous session values*/, false/*don't add classpath change*/);
classpath= perProjectInfo.resolvedClasspath;
} else {
classpath= project.getResolvedClasspath();
}
} 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) {
IJavaProject key= model.getJavaProject(entry.getPath().segment(0)); // TODO (jerome) reuse handle
IJavaProject[] dependents= (IJavaProject[])newProjectDependencies.get(key);
if (dependents == null) {
dependents= new IJavaProject[] { project };
} else {
int dependentsLength= dependents.length;
System.arraycopy(dependents, 0, dependents= new IJavaProject[dependentsLength + 1], 0, dependentsLength);
dependents[dependentsLength]= project;
}
newProjectDependencies.put(key, dependents);
continue;
}
// root path
IPath path= entry.getPath();
if (newRoots.get(path) == null) {
newRoots.put(path,
new DeltaProcessor.RootInfo(project, path, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), entry.getEntryKind()));
} else {
ArrayList rootList= (ArrayList)newOtherRoots.get(path);
if (rootList == null) {
rootList= new ArrayList();
newOtherRoots.put(path, rootList);
}
rootList.add(new DeltaProcessor.RootInfo(project, path, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), entry
.getEntryKind()));
}
// source attachment path
if (entry.getEntryKind() != IClasspathEntry.CPE_LIBRARY)
continue;
String propertyString= null;
try {
propertyString= Util.getSourceAttachmentProperty(path);
} catch (JavaModelException e) {
e.printStackTrace();
}
IPath sourceAttachmentPath;
if (propertyString != null) {
int index= propertyString.lastIndexOf(PackageFragmentRoot.ATTACHMENT_PROPERTY_DELIMITER);
sourceAttachmentPath= (index < 0) ? new Path(propertyString) : new Path(propertyString.substring(0, index));
} else {
sourceAttachmentPath= entry.getSourceAttachmentPath();
}
if (sourceAttachmentPath != null) {
newSourceAttachments.put(sourceAttachmentPath, path);
}
}
}
return new HashMap[] { newRoots, newOtherRoots, newSourceAttachments, newProjectDependencies };
}
public synchronized ClasspathValidation[] removeClasspathValidations() {
int length= this.classpathValidations.size();
if (length == 0)
return null;
ClasspathValidation[] validations= new ClasspathValidation[length];
this.classpathValidations.values().toArray(validations);
this.classpathValidations.clear();
return validations;
}
public synchronized ExternalFolderChange[] removeExternalFolderChanges() {
int length= this.externalFolderChanges.size();
if (length == 0)
return null;
ExternalFolderChange[] updates= new ExternalFolderChange[length];
this.externalFolderChanges.values().toArray(updates);
this.externalFolderChanges.clear();
return updates;
}
public synchronized ProjectReferenceChange[] removeProjectReferenceChanges() {
int length= this.projectReferenceChanges.size();
if (length == 0)
return null;
ProjectReferenceChange[] updates= new ProjectReferenceChange[length];
this.projectReferenceChanges.values().toArray(updates);
this.projectReferenceChanges.clear();
return updates;
}
public synchronized HashSet removeExternalElementsToRefresh() {
HashSet result= this.externalElementsToRefresh;
this.externalElementsToRefresh= null;
return result;
}
public synchronized void removeElementChangedListener(IElementChangedListener listener) {
for (int i= 0; i < this.elementChangedListenerCount; i++) {
if (this.elementChangedListeners[i] == listener) {
// need to clone defensively since we might be in the middle of listener notifications (#fire)
int length= this.elementChangedListeners.length;
IElementChangedListener[] newListeners= new IElementChangedListener[length];
System.arraycopy(this.elementChangedListeners, 0, newListeners, 0, i);
int[] newMasks= new int[length];
System.arraycopy(this.elementChangedListenerMasks, 0, newMasks, 0, i);
// copy trailing listeners
int trailingLength= this.elementChangedListenerCount - i - 1;
if (trailingLength > 0) {
System.arraycopy(this.elementChangedListeners, i + 1, newListeners, i, trailingLength);
System.arraycopy(this.elementChangedListenerMasks, i + 1, newMasks, i, trailingLength);
}
// update manager listener state (#fire need to iterate over original listeners through a local variable to hold onto
// the original ones)
this.elementChangedListeners= newListeners;
this.elementChangedListenerMasks= newMasks;
this.elementChangedListenerCount--;
return;
}
}
}
public synchronized void removePreResourceChangedListener(IResourceChangeListener listener) {
for (int i= 0; i < this.preResourceChangeListenerCount; i++) {
if (this.preResourceChangeListeners[i] == listener) {
// need to clone defensively since we might be in the middle of listener notifications (#fire)
int length= this.preResourceChangeListeners.length;
IResourceChangeListener[] newListeners= new IResourceChangeListener[length];
int[] newEventMasks= new int[length];
System.arraycopy(this.preResourceChangeListeners, 0, newListeners, 0, i);
System.arraycopy(this.preResourceChangeEventMasks, 0, newEventMasks, 0, i);
// copy trailing listeners
int trailingLength= this.preResourceChangeListenerCount - i - 1;
if (trailingLength > 0) {
System.arraycopy(this.preResourceChangeListeners, i + 1, newListeners, i, trailingLength);
System.arraycopy(this.preResourceChangeEventMasks, i + 1, newEventMasks, i, trailingLength);
}
// update manager listener state (#fire need to iterate over original listeners through a local variable to hold onto
// the original ones)
this.preResourceChangeListeners= newListeners;
this.preResourceChangeEventMasks= newEventMasks;
this.preResourceChangeListenerCount--;
return;
}
}
}
public void resourceChanged(final IResourceChangeEvent event) {
for (int i= 0; i < this.preResourceChangeListenerCount; i++) {
// wrap callbacks with Safe runnable for subsequent listeners to be called when some are causing grief
final IResourceChangeListener listener= this.preResourceChangeListeners[i];
if ((this.preResourceChangeEventMasks[i] & event.getType()) != 0)
SafeRunner.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
Util.log(exception, "Exception occurred in listener of pre Java resource change notification"); //$NON-NLS-1$
}
public void run() throws Exception {
listener.resourceChanged(event);
}
});
}
try {
getDeltaProcessor().resourceChanged(event);
} finally {
// TODO (jerome) see 47631, may want to get rid of following so as to reuse delta processor ?
if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
this.deltaProcessors.set(null);
} else {
// If we are going to reuse the delta processor of this thread, don't hang on to state
// that isn't meant to be reused. https://bugs.eclipse.org/bugs/show_bug.cgi?id=273385
getDeltaProcessor().overridenEventType= -1;
}
}
}
public Hashtable getExternalLibTimeStamps() {
if (this.externalTimeStamps == null) {
Hashtable timeStamps= new Hashtable();
File timestampsFile= getTimeStampsFile();
DataInputStream in= null;
try {
in= new DataInputStream(new BufferedInputStream(new FileInputStream(timestampsFile)));
int size= in.readInt();
while (size-- > 0) {
String key= in.readUTF();
long timestamp= in.readLong();
timeStamps.put(Path.fromPortableString(key), new Long(timestamp));
}
} catch (IOException e) {
if (timestampsFile.exists())
Util.log(e, "Unable to read external time stamps"); //$NON-NLS-1$
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// nothing we can do: ignore
}
}
}
this.externalTimeStamps= timeStamps;
}
return this.externalTimeStamps;
}
public IJavaProject findJavaProject(String name) {
if (getOldJavaProjecNames().contains(name))
return JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(name);
return null;
}
/*
* Workaround for bug 15168 circular errors not reported
* Returns the list of java projects before resource delta processing
* has started.
*/
public synchronized HashSet getOldJavaProjecNames() {
if (this.javaProjectNamesCache == null) {
HashSet result= new HashSet();
IJavaProject[] projects;
try {
projects= JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
} catch (JavaModelException e) {
return this.javaProjectNamesCache;
}
for (int i= 0, length= projects.length; i < length; i++) {
IJavaProject project= projects[i];
result.add(project.getElementName());
}
return this.javaProjectNamesCache= result;
}
return this.javaProjectNamesCache;
}
public synchronized void resetOldJavaProjectNames() {
this.javaProjectNamesCache= null;
}
private File getTimeStampsFile() {
return JavaCore.getPlugin().getStateLocation().append("externalLibsTimeStamps").toFile(); //$NON-NLS-1$
}
public void saveExternalLibTimeStamps() throws CoreException {
if (this.externalTimeStamps == null)
return;
// cleanup to avoid any leak ( https://bugs.eclipse.org/bugs/show_bug.cgi?id=244849 )
HashSet toRemove= new HashSet();
if (this.roots != null) {
Enumeration keys= this.externalTimeStamps.keys();
while (keys.hasMoreElements()) {
Object key= keys.nextElement();
if (this.roots.get(key) == null) {
toRemove.add(key);
}
}
}
File timestamps= getTimeStampsFile();
DataOutputStream out= null;
try {
out= new DataOutputStream(new BufferedOutputStream(new FileOutputStream(timestamps)));
out.writeInt(this.externalTimeStamps.size() - toRemove.size());
Iterator entries= this.externalTimeStamps.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry= (Map.Entry)entries.next();
IPath key= (IPath)entry.getKey();
if (!toRemove.contains(key)) {
out.writeUTF(key.toPortableString());
Long timestamp= (Long)entry.getValue();
out.writeLong(timestamp.longValue());
}
}
} catch (IOException e) {
IStatus status= new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.ERROR, "Problems while saving timestamps", e); //$NON-NLS-1$
throw new CoreException(status);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
// nothing we can do: ignore
}
}
}
}
/*
* Update the roots that are affected by the addition or the removal of the given container resource.
*/
public synchronized void updateRoots(IPath containerPath, IResourceDelta containerDelta, DeltaProcessor deltaProcessor) {
Map updatedRoots;
Map otherUpdatedRoots;
if (containerDelta.getKind() == IResourceDelta.REMOVED) {
updatedRoots= this.oldRoots;
otherUpdatedRoots= this.oldOtherRoots;
} else {
updatedRoots= this.roots;
otherUpdatedRoots= this.otherRoots;
}
int containerSegmentCount= containerPath.segmentCount();
boolean containerIsProject= containerSegmentCount == 1;
Iterator iterator= updatedRoots.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry= (Map.Entry)iterator.next();
IPath path= (IPath)entry.getKey();
if (containerPath.isPrefixOf(path) && !containerPath.equals(path)) {
IResourceDelta rootDelta= containerDelta.findMember(path.removeFirstSegments(containerSegmentCount));
if (rootDelta == null)
continue;
DeltaProcessor.RootInfo rootInfo= (DeltaProcessor.RootInfo)entry.getValue();
if (!containerIsProject
|| !rootInfo.project.getPath().isPrefixOf(path)) { // only consider folder roots that are not included in the container
deltaProcessor.updateCurrentDeltaAndIndex(rootDelta, IJavaElement.PACKAGE_FRAGMENT_ROOT, rootInfo);
}
ArrayList rootList= (ArrayList)otherUpdatedRoots.get(path);
if (rootList != null) {
Iterator otherProjects= rootList.iterator();
while (otherProjects.hasNext()) {
rootInfo= (DeltaProcessor.RootInfo)otherProjects.next();
if (!containerIsProject
|| !rootInfo.project.getPath().isPrefixOf(path)) { // only consider folder roots that are not included in the container
deltaProcessor.updateCurrentDeltaAndIndex(rootDelta, IJavaElement.PACKAGE_FRAGMENT_ROOT, rootInfo);
}
}
}
}
}
}
}