/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package net.sourceforge.phpdt.internal.core;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import net.sourceforge.phpdt.core.ElementChangedEvent;
import net.sourceforge.phpdt.core.IClasspathEntry;
import net.sourceforge.phpdt.core.IElementChangedListener;
import net.sourceforge.phpdt.core.IJavaElement;
import net.sourceforge.phpdt.core.IJavaElementDelta;
import net.sourceforge.phpdt.core.IJavaModel;
import net.sourceforge.phpdt.core.IJavaProject;
import net.sourceforge.phpdt.core.JavaCore;
import net.sourceforge.phpdt.core.JavaModelException;
import net.sourceforge.phpdt.internal.core.builder.PHPBuilder;
import net.sourceforge.phpdt.internal.core.util.Util;
import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
import net.sourceforge.phpeclipse.PHPeclipsePlugin;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
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.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;

/**
 * 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 implements IResourceChangeListener {

	final static int IGNORE = 0;

	final static int SOURCE = 1;

	final static int BINARY = 2;

	final static String EXTERNAL_JAR_ADDED = "external jar added"; //$NON-NLS-1$

	final static String EXTERNAL_JAR_REMOVED = "external jar removed"; //$NON-NLS-1$

	final static String EXTERNAL_JAR_CHANGED = "external jar changed"; //$NON-NLS-1$

	final static String EXTERNAL_JAR_UNCHANGED = "external jar unchanged"; //$NON-NLS-1$

	final static String INTERNAL_JAR_IGNORE = "internal jar ignore"; //$NON-NLS-1$

	private final static int NON_JAVA_RESOURCE = -1;

	public static boolean DEBUG = false;

	public static boolean VERBOSE = false;

	public static final int DEFAULT_CHANGE_EVENT = 0; // must not collide with
														// ElementChangedEvent
														// event masks

	/**
	 * The <code>JavaElementDelta</code> corresponding to the
	 * <code>IResourceDelta</code> being translated.
	 */
	protected JavaElementDelta currentDelta;

	// protected IndexManager indexManager = new IndexManager();

	/* A table from IPath (from a classpath entry) to RootInfo */
	public Map roots;

	/*
	 * A table from IPath (from a classpath entry) to ArrayList of RootInfo Used
	 * when an IPath corresponds to more than one root
	 */
	Map otherRoots;

	/* Whether the roots tables should be recomputed */
	public boolean rootsAreStale = true;

	/*
	 * A table from IPath (from a classpath entry) to RootInfo from the last
	 * time the delta processor was invoked.
	 */
	public Map oldRoots;

	/*
	 * A table from IPath (from a classpath entry) to ArrayList of RootInfo from
	 * the last time the delta processor was invoked. Used when an IPath
	 * corresponds to more than one root
	 */
	Map oldOtherRoots;

	/*
	 * A table from IPath (a source attachment path from a classpath entry) to
	 * IPath (a root path)
	 */
	Map sourceAttachments;

	/*
	 * 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;

	/*
	 * Queue of deltas created explicily by the Java Model that have yet to be
	 * fired.
	 */
	public ArrayList javaModelDeltas = new ArrayList();

	/*
	 * Queue of reconcile deltas on working copies that have yet to be fired.
	 * This is a table form IWorkingCopy to IJavaElementDelta
	 */
	public HashMap reconcileDeltas = new HashMap();

	/*
	 * Turns delta firing on/off. By default it is on.
	 */
	private boolean isFiring = true;

	public HashMap externalTimeStamps = new HashMap();

	public HashSet projectsToUpdate = new HashSet();

	// list of root projects which namelookup caches need to be updated for
	// dependents
	// TODO: (jerome) is it needed? projectsToUpdate might be sufficient
	public HashSet projectsForDependentNamelookupRefresh = new HashSet();

	/*
	 * The global state of delta processing.
	 */
	private DeltaProcessingState state;

	/*
	 * The Java model manager
	 */
	private JavaModelManager manager;

	/*
	 * A table from IJavaProject to an array of IPackageFragmentRoot. This table
	 * contains the pkg fragment roots of the project that are being deleted.
	 */
	Map removedRoots;

	/*
	 * A list of IJavaElement used as a scope for external archives refresh
	 * during POST_CHANGE. This is null if no refresh is needed.
	 */
	HashSet refreshedElements;

	class OutputsInfo {
		IPath[] paths;

		int[] traverseModes;

		int outputCount;

		OutputsInfo(IPath[] paths, int[] traverseModes, int outputCount) {
			this.paths = paths;
			this.traverseModes = traverseModes;
			this.outputCount = outputCount;
		}

		public String toString() {
			if (this.paths == null)
				return "<none>"; //$NON-NLS-1$
			StringBuffer buffer = new StringBuffer();
			for (int i = 0; i < this.outputCount; i++) {
				buffer.append("path="); //$NON-NLS-1$
				buffer.append(this.paths[i].toString());
				buffer.append("\n->traverse="); //$NON-NLS-1$
				switch (this.traverseModes[i]) {
				case BINARY:
					buffer.append("BINARY"); //$NON-NLS-1$
					break;
				case IGNORE:
					buffer.append("IGNORE"); //$NON-NLS-1$
					break;
				case SOURCE:
					buffer.append("SOURCE"); //$NON-NLS-1$
					break;
				default:
					buffer.append("<unknown>"); //$NON-NLS-1$
				}
				if (i + 1 < this.outputCount) {
					buffer.append('\n');
				}
			}
			return buffer.toString();
		}
	}

	class RootInfo {
		IJavaProject project;

		IPath rootPath;

		char[][] exclusionPatterns;

		RootInfo(IJavaProject project, IPath rootPath,
				char[][] exclusionPatterns) {
			this.project = project;
			this.rootPath = rootPath;
			this.exclusionPatterns = exclusionPatterns;
		}

		boolean isRootOfProject(IPath path) {
			return this.rootPath.equals(path)
					&& this.project.getProject().getFullPath().isPrefixOf(path);
		}

		public String toString() {
			StringBuffer buffer = new StringBuffer("project="); //$NON-NLS-1$
			if (this.project == null) {
				buffer.append("null"); //$NON-NLS-1$
			} else {
				buffer.append(this.project.getElementName());
			}
			buffer.append("\npath="); //$NON-NLS-1$
			if (this.rootPath == null) {
				buffer.append("null"); //$NON-NLS-1$
			} else {
				buffer.append(this.rootPath.toString());
			}
			buffer.append("\nexcluding="); //$NON-NLS-1$
			if (this.exclusionPatterns == null) {
				buffer.append("null"); //$NON-NLS-1$
			} else {
				for (int i = 0, length = this.exclusionPatterns.length; i < length; i++) {
					buffer.append(new String(this.exclusionPatterns[i]));
					if (i < length - 1) {
						buffer.append("|"); //$NON-NLS-1$
					}
				}
			}
			return buffer.toString();
		}
	}

	// DeltaProcessor(JavaModelManager manager) {
	// this.manager = manager;
	// }

	/*
	 * Type of event that should be processed no matter what the real event type
	 * is.
	 */
	public int overridenEventType = -1;

	public DeltaProcessor(DeltaProcessingState state, JavaModelManager manager) {
		this.state = state;
		this.manager = manager;
	}

	/*
	 * Adds the dependents of the given project to the list of the projects to
	 * update.
	 */
	// void addDependentProjects(IPath projectPath, HashSet result) {
	// try {
	// IJavaProject[] projects = this.manager.getJavaModel().getJavaProjects();
	// for (int i = 0, length = projects.length; i < length; i++) {
	// IJavaProject project = projects[i];
	// IClasspathEntry[] classpath =
	// ((JavaProject)project).getExpandedClasspath(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)) {
	// result.add(project);
	// }
	// }
	// }
	// } catch (JavaModelException e) {
	// }
	// }
	/*
	 * Adds the given element to the list of elements used as a scope for
	 * external jars refresh.
	 */
	public void addForRefresh(IJavaElement element) {
		if (this.refreshedElements == null) {
			this.refreshedElements = new HashSet();
		}
		this.refreshedElements.add(element);
	}

	/*
	 * 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.addDependentProjects(project.getFullPath(),
		// this.projectsToUpdate);
	}

	/**
	 * 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 = (JavaElementInfo) parent
						.getElementInfo();
				info.addChild(child);
			} catch (JavaModelException e) {
				// do nothing - we already checked if open
			}
		}
	}

	/**
	 * Check all external archive (referenced by given roots, projects or model)
	 * status and issue a corresponding root delta. Also triggers index updates
	 */
	// public void checkExternalArchiveChanges(IJavaElement[] refreshedElements,
	// IProgressMonitor monitor) throws JavaModelException {
	// try {
	// for (int i = 0, length = refreshedElements.length; i < length; i++) {
	// this.addForRefresh(refreshedElements[i]);
	// }
	// boolean hasDelta = this.createExternalArchiveDelta(monitor);
	// if (monitor != null && monitor.isCanceled()) return;
	// if (hasDelta){
	// // force classpath marker refresh of affected projects
	// JavaModel.flushExternalFileCache();
	// IJavaElementDelta[] projectDeltas =
	// this.currentDelta.getAffectedChildren();
	// for (int i = 0, length = projectDeltas.length; i < length; i++) {
	// IJavaElementDelta delta = projectDeltas[i];
	// ((JavaProject)delta.getElement()).getResolvedClasspath(
	// true, // ignoreUnresolvedEntry
	// true); // generateMarkerOnError
	// }
	// if (this.currentDelta != null) { // if delta has not been fired while
	// creating markers
	// this.manager.fire(this.currentDelta,
	// JavaModelManager.DEFAULT_CHANGE_EVENT);
	// }
	// }
	// } finally {
	// this.currentDelta = null;
	// if (monitor != null) monitor.done();
	// }
	// }
	/*
	 * Check if external archives have changed and create the corresponding
	 * deltas. Returns whether at least on delta was created.
	 */
	// public boolean createExternalArchiveDelta(IProgressMonitor monitor)
	// throws JavaModelException {
	//		
	// if (this.refreshedElements == null) return false;
	//			
	// HashMap externalArchivesStatus = new HashMap();
	// boolean hasDelta = false;
	//		
	// // find JARs to refresh
	// HashSet archivePathsToRefresh = new HashSet();
	// try {
	// Iterator iterator = this.refreshedElements.iterator();
	// while (iterator.hasNext()) {
	// IJavaElement element = (IJavaElement)iterator.next();
	// switch(element.getElementType()){
	// case IJavaElement.PACKAGE_FRAGMENT_ROOT :
	// archivePathsToRefresh.add(element.getPath());
	// break;
	// case IJavaElement.JAVA_PROJECT :
	// IJavaProject project = (IJavaProject) element;
	// if (!JavaProject.hasJavaNature(project.getProject())) {
	// // project is not accessible or has lost its Java nature
	// break;
	// }
	// IClasspathEntry[] classpath = project.getResolvedClasspath(true);
	// for (int j = 0, cpLength = classpath.length; j < cpLength; j++){
	// if (classpath[j].getEntryKind() == IClasspathEntry.CPE_LIBRARY){
	// archivePathsToRefresh.add(classpath[j].getPath());
	// }
	// }
	// break;
	// case IJavaElement.JAVA_MODEL :
	// IJavaProject[] projects =
	// manager.getJavaModel().getOldJavaProjectsList();
	// for (int j = 0, projectsLength = projects.length; j < projectsLength;
	// j++){
	// project = projects[j];
	// if (!JavaProject.hasJavaNature(project.getProject())) {
	// // project is not accessible or has lost its Java nature
	// continue;
	// }
	// classpath = project.getResolvedClasspath(true);
	// for (int k = 0, cpLength = classpath.length; k < cpLength; k++){
	// if (classpath[k].getEntryKind() == IClasspathEntry.CPE_LIBRARY){
	// archivePathsToRefresh.add(classpath[k].getPath());
	// }
	// }
	// }
	// break;
	// }
	// }
	// } finally {
	// this.refreshedElements = null;
	// }
	//		
	// // perform refresh
	// IJavaProject[] projects =
	// manager.getJavaModel().getOldJavaProjectsList();
	// IWorkspaceRoot wksRoot = ResourcesPlugin.getWorkspace().getRoot();
	// for (int i = 0, length = projects.length; i < length; i++) {
	//			
	// if (monitor != null && monitor.isCanceled()) break;
	//			
	// IJavaProject project = projects[i];
	// if (!JavaProject.hasJavaNature(project.getProject())) {
	// // project is not accessible or has lost its Java nature
	// continue;
	// }
	// IClasspathEntry[] entries = project.getResolvedClasspath(true);
	// for (int j = 0; j < entries.length; j++){
	// if (entries[j].getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
	//					
	// IPath entryPath = entries[j].getPath();
	//					
	// if (!archivePathsToRefresh.contains(entryPath)) continue; // not supposed
	// to be refreshed
	//					
	// String status = (String)externalArchivesStatus.get(entryPath);
	// if (status == null){
	//						
	// // compute shared status
	// Object targetLibrary = JavaModel.getTarget(wksRoot, entryPath, true);
	//		
	// if (targetLibrary == null){ // missing JAR
	// if (this.externalTimeStamps.containsKey(entryPath)){
	// this.externalTimeStamps.remove(entryPath);
	// externalArchivesStatus.put(entryPath, EXTERNAL_JAR_REMOVED);
	// // the jar was physically removed: remove the index
	// indexManager.removeIndex(entryPath);
	// }
	//		
	// } else if (targetLibrary instanceof File){ // external JAR
	//		
	// File externalFile = (File)targetLibrary;
	//							
	// // check timestamp to figure if JAR has changed in some way
	// Long oldTimestamp =(Long) this.externalTimeStamps.get(entryPath);
	// long newTimeStamp = getTimeStamp(externalFile);
	// if (oldTimestamp != null){
	//		
	// if (newTimeStamp == 0){ // file doesn't exist
	// externalArchivesStatus.put(entryPath, EXTERNAL_JAR_REMOVED);
	// this.externalTimeStamps.remove(entryPath);
	// // remove the index
	// indexManager.removeIndex(entryPath);
	//		
	// } else if (oldTimestamp.longValue() != newTimeStamp){
	// externalArchivesStatus.put(entryPath, EXTERNAL_JAR_CHANGED);
	// this.externalTimeStamps.put(entryPath, new Long(newTimeStamp));
	// // first remove the index so that it is forced to be re-indexed
	// indexManager.removeIndex(entryPath);
	// // then index the jar
	// indexManager.indexLibrary(entryPath, project.getProject());
	// } else {
	// externalArchivesStatus.put(entryPath, EXTERNAL_JAR_UNCHANGED);
	// }
	// } else {
	// if (newTimeStamp == 0){ // jar still doesn't exist
	// externalArchivesStatus.put(entryPath, EXTERNAL_JAR_UNCHANGED);
	// } else {
	// externalArchivesStatus.put(entryPath, EXTERNAL_JAR_ADDED);
	// this.externalTimeStamps.put(entryPath, new Long(newTimeStamp));
	// // index the new jar
	// indexManager.indexLibrary(entryPath, project.getProject());
	// }
	// }
	// } else { // internal JAR
	// externalArchivesStatus.put(entryPath, INTERNAL_JAR_IGNORE);
	// }
	// }
	// // according to computed status, generate a delta
	// status = (String)externalArchivesStatus.get(entryPath);
	// if (status != null){
	// if (status == EXTERNAL_JAR_ADDED){
	// PackageFragmentRoot root =
	// (PackageFragmentRoot)project.getPackageFragmentRoot(entryPath.toString());
	// if (VERBOSE){
	// System.out.println("- External JAR ADDED, affecting root:
	// "+root.getElementName()); //$NON-NLS-1$
	// }
	// elementAdded(root, null, null);
	// hasDelta = true;
	// } else if (status == EXTERNAL_JAR_CHANGED) {
	// PackageFragmentRoot root =
	// (PackageFragmentRoot)project.getPackageFragmentRoot(entryPath.toString());
	// if (VERBOSE){
	// System.out.println("- External JAR CHANGED, affecting root:
	// "+root.getElementName()); //$NON-NLS-1$
	// }
	// // reset the corresponding project built state, since the builder would
	// miss this change
	// this.manager.setLastBuiltState(project.getProject(), null /*no state*/);
	// contentChanged(root, null);
	// hasDelta = true;
	// } else if (status == EXTERNAL_JAR_REMOVED) {
	// PackageFragmentRoot root =
	// (PackageFragmentRoot)project.getPackageFragmentRoot(entryPath.toString());
	// if (VERBOSE){
	// System.out.println("- External JAR REMOVED, affecting root:
	// "+root.getElementName()); //$NON-NLS-1$
	// }
	// elementRemoved(root, null, null);
	// hasDelta = true;
	// }
	// }
	// }
	// }
	// }
	// return hasDelta;
	// }
	JavaElementDelta currentDelta() {
		if (this.currentDelta == null) {
			this.currentDelta = new JavaElementDelta(this.manager
					.getJavaModel());
		}
		return this.currentDelta;
	}

	/*
	 * Process the given delta and look for projects being added, opened, closed
	 * or with a java nature being added or removed. Note that projects being
	 * deleted are checked in deleting(IProject). In all cases, add the
	 * project's dependents to the list of projects to update so that the
	 * classpath related markers can be updated.
	 */
	// public void checkProjectsBeingAddedOrRemoved(IResourceDelta delta) {
	// IResource resource = delta.getResource();
	// switch (resource.getType()) {
	// case IResource.ROOT :
	// // workaround for bug 15168 circular errors not reported
	// if (this.manager.javaProjectsCache == null) {
	// try {
	// this.manager.javaProjectsCache =
	// this.manager.getJavaModel().getJavaProjects();
	// } catch (JavaModelException e) {
	// }
	// }
	//				
	// IResourceDelta[] children = delta.getAffectedChildren();
	// for (int i = 0, length = children.length; i < length; i++) {
	// this.checkProjectsBeingAddedOrRemoved(children[i]);
	// }
	// break;
	// case IResource.PROJECT :
	// // NB: No need to check project's nature as if the project is not a java
	// project:
	// // - if the project is added or changed this is a noop for
	// projectsBeingDeleted
	// // - if the project is closed, it has already lost its java nature
	// int deltaKind = delta.getKind();
	// if (deltaKind == IResourceDelta.ADDED) {
	// // remember project and its dependents
	// IProject project = (IProject)resource;
	// this.addToProjectsToUpdateWithDependents(project);
	//					
	// // workaround for bug 15168 circular errors not reported
	// if (JavaProject.hasJavaNature(project)) {
	// this.addToParentInfo((JavaProject)JavaCore.create(project));
	// }
	//
	// } else if (deltaKind == IResourceDelta.CHANGED) {
	// IProject project = (IProject)resource;
	// if ((delta.getFlags() & IResourceDelta.OPEN) != 0) {
	// // project opened or closed: remember project and its dependents
	// this.addToProjectsToUpdateWithDependents(project);
	//						
	// // workaround for bug 15168 circular errors not reported
	// if (project.isOpen()) {
	// if (JavaProject.hasJavaNature(project)) {
	// this.addToParentInfo((JavaProject)JavaCore.create(project));
	// }
	// } else {
	// JavaProject javaProject =
	// (JavaProject)this.manager.getJavaModel().findJavaProject(project);
	// if (javaProject != null) {
	// try {
	// javaProject.close();
	// } catch (JavaModelException e) {
	// }
	// this.removeFromParentInfo(javaProject);
	// }
	// }
	// } else if ((delta.getFlags() & IResourceDelta.DESCRIPTION) != 0) {
	// boolean wasJavaProject =
	// this.manager.getJavaModel().findJavaProject(project) != null;
	// boolean isJavaProject = JavaProject.hasJavaNature(project);
	// if (wasJavaProject != isJavaProject) {
	// // java nature added or removed: remember project and its dependents
	// this.addToProjectsToUpdateWithDependents(project);
	//
	// // workaround for bug 15168 circular errors not reported
	// if (isJavaProject) {
	// this.addToParentInfo((JavaProject)JavaCore.create(project));
	// } else {
	// JavaProject javaProject = (JavaProject)JavaCore.create(project);
	//								
	// // flush classpath markers
	// javaProject.
	// flushClasspathProblemMarkers(
	// true, // flush cycle markers
	// true //flush classpath format markers
	// );
	//									
	// // remove problems and tasks created by the builder
	// JavaBuilder.removeProblemsAndTasksFor(project);
	//
	// // close project
	// try {
	// javaProject.close();
	// } catch (JavaModelException e) {
	// }
	// this.removeFromParentInfo(javaProject);
	// }
	// } else {
	// // in case the project was removed then added then changed (see bug
	// 19799)
	// if (JavaProject.hasJavaNature(project)) { // need nature check - 18698
	// this.addToParentInfo((JavaProject)JavaCore.create(project));
	// }
	// }
	// } else {
	// // workaround for bug 15168 circular errors not reported
	// // in case the project was removed then added then changed
	// if (JavaProject.hasJavaNature(project)) { // need nature check - 18698
	// this.addToParentInfo((JavaProject)JavaCore.create(project));
	// }
	// }
	// }
	// break;
	// }
	// }
	// private void checkSourceAttachmentChange(IResourceDelta delta, IResource
	// res) {
	// IPath rootPath = (IPath)this.sourceAttachments.get(res.getFullPath());
	// if (rootPath != null) {
	// RootInfo rootInfo = this.rootInfo(rootPath, delta.getKind());
	// if (rootInfo != null) {
	// IJavaProject projectOfRoot = rootInfo.project;
	// IPackageFragmentRoot root = null;
	// try {
	// // close the root so that source attachement cache is flushed
	// root = projectOfRoot.findPackageFragmentRoot(rootPath);
	// if (root != null) {
	// root.close();
	// }
	// } catch (JavaModelException e) {
	// }
	// if (root == null) return;
	// switch (delta.getKind()) {
	// case IResourceDelta.ADDED:
	// currentDelta().sourceAttached(root);
	// break;
	// case IResourceDelta.CHANGED:
	// currentDelta().sourceDetached(root);
	// currentDelta().sourceAttached(root);
	// break;
	// case IResourceDelta.REMOVED:
	// currentDelta().sourceDetached(root);
	// break;
	// }
	// }
	// }
	// }
	/**
	 * 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
	// }
	// }
	/**
	 * 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>
	 * Delta argument could be null if processing an external JAR change
	 */
	// protected void contentChanged(Openable element, IResourceDelta delta) {
	//
	// close(element);
	// int flags = IJavaElementDelta.F_CONTENT;
	// if (element instanceof JarPackageFragmentRoot){
	// flags |= IJavaElementDelta.F_ARCHIVE_CONTENT_CHANGED;
	// }
	// currentDelta().changed(element, flags);
	// }
	//	
	/**
	 * Creates the openables corresponding to this resource. Returns null if
	 * none was found.
	 */
	// protected Openable createElement(IResource resource, int elementType,
	// RootInfo rootInfo) {
	// if (resource == null) return null;
	//		
	// IPath path = resource.getFullPath();
	// IJavaElement element = null;
	// switch (elementType) {
	//			
	// case IJavaElement.JAVA_PROJECT:
	//			
	// // note that non-java resources rooted at the project level will also
	// enter this code with
	// // an elementType JAVA_PROJECT (see #elementType(...)).
	// if (resource instanceof IProject){
	//
	// this.popUntilPrefixOf(path);
	//					
	// if (this.currentElement != null
	// && this.currentElement.getElementType() == IJavaElement.JAVA_PROJECT
	// && ((IJavaProject)this.currentElement).getProject().equals(resource)) {
	// return this.currentElement;
	// }
	// if (rootInfo != null && rootInfo.project.getProject().equals(resource)){
	// element = (Openable)rootInfo.project;
	// break;
	// }
	// IProject proj = (IProject)resource;
	// if (JavaProject.hasJavaNature(proj)) {
	// element = JavaCore.create(proj);
	// } else {
	// // java project may have been been closed or removed (look for
	// // element amongst old java project s list).
	// element = (Openable) manager.getJavaModel().findJavaProject(proj);
	// }
	// }
	// break;
	// case IJavaElement.PACKAGE_FRAGMENT_ROOT:
	// element = rootInfo == null ? JavaCore.create(resource) :
	// rootInfo.project.getPackageFragmentRoot(resource);
	// break;
	// case IJavaElement.PACKAGE_FRAGMENT:
	// // find the element that encloses the resource
	// this.popUntilPrefixOf(path);
	//				
	// if (this.currentElement == null) {
	// element = rootInfo == null ? JavaCore.create(resource) :
	// JavaModelManager.create(resource, rootInfo.project);
	// } else {
	// // find the root
	// IPackageFragmentRoot root = this.currentElement.getPackageFragmentRoot();
	// if (root == null) {
	// element = rootInfo == null ? JavaCore.create(resource) :
	// JavaModelManager.create(resource, rootInfo.project);
	// } else if (((JavaProject)root.getJavaProject()).contains(resource)) {
	// // create package handle
	// IPath pkgPath = path.removeFirstSegments(root.getPath().segmentCount());
	// String pkg = ProjectPrefUtil.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 = rootInfo == null ? JavaCore.create(resource) :
	// JavaModelManager.create(resource, rootInfo.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 =
	// ProjectPrefUtil.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 = rootInfo == null ? JavaCore.create(resource) :
	// JavaModelManager.create(resource, rootInfo.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;
	// }
	// }
	/**
	 * Note that the project is about to be deleted.
	 */
	// public void deleting(IProject project) {
	//		
	// try {
	// // discard indexing jobs that belong to this project so that the project
	// can be
	// // deleted without interferences from the index manager
	// this.indexManager.discardJobs(project.getName());
	//
	// JavaProject javaProject = (JavaProject)JavaCore.create(project);
	//			
	// // remember roots of this project
	// if (this.removedRoots == null) {
	// this.removedRoots = new HashMap();
	// }
	// if (javaProject.isOpen()) {
	// this.removedRoots.put(javaProject,
	// javaProject.getPackageFragmentRoots());
	// } else {
	// // compute roots without opening project
	// this.removedRoots.put(
	// javaProject,
	// javaProject.computePackageFragmentRoots(
	// javaProject.getResolvedClasspath(true),
	// false));
	// }
	//			
	// javaProject.close();
	//
	// // workaround for bug 15168 circular errors not reported
	// if (this.manager.javaProjectsCache == null) {
	// this.manager.javaProjectsCache =
	// this.manager.getJavaModel().getJavaProjects();
	// }
	// this.removeFromParentInfo(javaProject);
	//
	// } catch (JavaModelException e) {
	// }
	//		
	// this.addDependentProjects(project.getFullPath(), this.projectsToUpdate);
	// }

	/**
	 * 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>
	 * Delta argument could be null if processing an external JAR change
	 */
	// protected void elementAdded(Openable element, IResourceDelta delta,
	// RootInfo rootInfo) {
	// 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 (delta != null &&
	// JavaProject.hasJavaNature((IProject)delta.getResource())) {
	// addToParentInfo(element);
	// if ((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
	// Openable movedFromElement =
	// (Openable)element.getJavaModel().getJavaProject(delta.getMovedFromPath().lastSegment());
	// currentDelta().movedTo(element, movedFromElement);
	// } else {
	// currentDelta().added(element);
	// }
	// this.projectsToUpdate.add(element);
	// this.updateRoots(element.getPath(), delta);
	// this.projectsForDependentNamelookupRefresh.add((JavaProject) 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 != null && (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
	// RootInfo movedFromInfo = this.enclosingRootInfo(movedFromPath,
	// IResourceDelta.REMOVED);
	// int movedFromType =
	// this.elementType(
	// movedFromRes,
	// IResourceDelta.REMOVED,
	// element.getParent().getElementType(),
	// movedFromInfo);
	//				
	// // reset current element as it might be inside a nested root
	// (popUntilPrefixOf() may use the outer root)
	// this.currentElement = null;
	//			
	// // create the moved from element
	// Openable movedFromElement =
	// elementType != IJavaElement.JAVA_PROJECT && movedFromType ==
	// IJavaElement.JAVA_PROJECT ?
	// null : // outside classpath
	// this.createElement(movedFromRes, movedFromType, movedFromInfo);
	// if (movedFromElement == null) {
	// // moved from outside classpath
	// currentDelta().added(element);
	// } else {
	// currentDelta().movedTo(element, movedFromElement);
	// }
	// } else {
	// currentDelta().added(element);
	// }
	//			
	// switch (elementType) {
	// case IJavaElement.PACKAGE_FRAGMENT_ROOT :
	// // when a root is added, and is on the classpath, the project must be
	// updated
	// JavaProject project = (JavaProject) element.getJavaProject();
	// this.projectsToUpdate.add(project);
	// this.projectsForDependentNamelookupRefresh.add(project);
	//					
	// break;
	// case IJavaElement.PACKAGE_FRAGMENT :
	// // get rid of namelookup since it holds onto obsolete cached info
	// project = (JavaProject) element.getJavaProject();
	// try {
	// project.getJavaProjectElementInfo().setNameLookup(null);
	// this.projectsForDependentNamelookupRefresh.add(project);
	// } catch (JavaModelException e) {
	// }
	// // add subpackages
	// if (delta != null){
	// 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 folderName = resource.getName();
	// if (ProjectPrefUtil.isValidFolderNameForPackage(folderName)) {
	// String subpkgName =
	// name.length() == 0 ?
	// folderName :
	// name + "." + folderName; //$NON-NLS-1$
	// Openable subpkg = (Openable)root.getPackageFragment(subpkgName);
	// this.updateIndex(subpkg, child);
	// this.elementAdded(subpkg, child, rootInfo);
	// }
	// }
	// }
	// }
	// 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>
	 * Delta argument could be null if processing an external JAR change
	 */
	// protected void elementRemoved(Openable element, IResourceDelta delta,
	// RootInfo rootInfo) {
	//		
	// if (element.isOpen()) {
	// close(element);
	// }
	// removeFromParentInfo(element);
	// int elementType = element.getElementType();
	// if (delta != null && (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
	// RootInfo movedToInfo = this.enclosingRootInfo(movedToPath,
	// IResourceDelta.ADDED);
	// int movedToType =
	// this.elementType(
	// movedToRes,
	// IResourceDelta.ADDED,
	// element.getParent().getElementType(),
	// movedToInfo);
	//
	// // reset current element as it might be inside a nested root
	// (popUntilPrefixOf() may use the outer root)
	// this.currentElement = null;
	//			
	// // create the moved To element
	// Openable movedToElement =
	// elementType != IJavaElement.JAVA_PROJECT && movedToType ==
	// IJavaElement.JAVA_PROJECT ?
	// null : // outside classpath
	// this.createElement(movedToRes, movedToType, movedToInfo);
	// if (movedToElement == null) {
	// // moved outside classpath
	// currentDelta().removed(element);
	// } else {
	// currentDelta().movedFrom(element, movedToElement);
	// }
	// } else {
	// currentDelta().removed(element);
	// }
	//
	// switch (elementType) {
	// case IJavaElement.JAVA_MODEL :
	// this.indexManager.reset();
	// break;
	// case IJavaElement.JAVA_PROJECT :
	// this.manager.removePerProjectInfo(
	// (JavaProject) element);
	// this.updateRoots(element.getPath(), delta);
	// this.projectsForDependentNamelookupRefresh.add((JavaProject) element);
	// break;
	// case IJavaElement.PACKAGE_FRAGMENT_ROOT :
	// JavaProject project = (JavaProject) element.getJavaProject();
	// this.projectsToUpdate.add(project);
	// this.projectsForDependentNamelookupRefresh.add(project);
	// break;
	// case IJavaElement.PACKAGE_FRAGMENT :
	// //1G1TW2T - get rid of namelookup since it holds onto obsolete cached
	// info
	// project = (JavaProject) element.getJavaProject();
	// try {
	// project.getJavaProjectElementInfo().setNameLookup(null);
	// this.projectsForDependentNamelookupRefresh.add(project);
	// } catch (JavaModelException e) {
	// }
	// // remove subpackages
	// if (delta != null){
	// 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 folderName = resource.getName();
	// if (ProjectPrefUtil.isValidFolderNameForPackage(folderName)) {
	// String subpkgName =
	// name.length() == 0 ?
	// folderName :
	// name + "." + folderName; //$NON-NLS-1$
	// Openable subpkg = (Openable)root.getPackageFragment(subpkgName);
	// this.updateIndex(subpkg, child);
	// this.elementRemoved(subpkg, child, rootInfo);
	// }
	// }
	// }
	// }
	// break;
	// }
	// }
	/*
	 * Returns the type of the java element the given delta matches to. Returns
	 * NON_JAVA_RESOURCE if unknown (e.g. a non-java resource or excluded .java
	 * file)
	 */
	// private int elementType(IResource res, int kind, int parentType, RootInfo
	// rootInfo) {
	// switch (parentType) {
	// case IJavaElement.JAVA_MODEL:
	// // case of a movedTo or movedFrom project (other cases are handled in
	// processResourceDelta(...)
	// return IJavaElement.JAVA_PROJECT;
	// case NON_JAVA_RESOURCE:
	// case IJavaElement.JAVA_PROJECT:
	// if (rootInfo == null) {
	// rootInfo = this.enclosingRootInfo(res.getFullPath(), kind);
	// }
	// if (rootInfo != null && rootInfo.isRootOfProject(res.getFullPath())) {
	// return IJavaElement.PACKAGE_FRAGMENT_ROOT;
	// } else {
	// return NON_JAVA_RESOURCE; // not yet in a package fragment root or root
	// of another project
	// }
	// case IJavaElement.PACKAGE_FRAGMENT_ROOT:
	// case IJavaElement.PACKAGE_FRAGMENT:
	// if (rootInfo == null) {
	// rootInfo = this.enclosingRootInfo(res.getFullPath(), kind);
	// }
	// if (rootInfo == null || ProjectPrefUtil.isExcluded(res,
	// rootInfo.exclusionPatterns)) {
	// return NON_JAVA_RESOURCE;
	// }
	// if (res instanceof IFolder) {
	// if (ProjectPrefUtil.isValidFolderNameForPackage(res.getName())) {
	// return IJavaElement.PACKAGE_FRAGMENT;
	// } else {
	// return NON_JAVA_RESOURCE;
	// }
	// } else {
	// String fileName = res.getName();
	// if (ProjectPrefUtil.isValidCompilationUnitName(fileName)) {
	// return IJavaElement.COMPILATION_UNIT;
	// } else if (ProjectPrefUtil.isValidClassFileName(fileName)) {
	// return IJavaElement.CLASS_FILE;
	// } else if (this.rootInfo(res.getFullPath(), kind) != null) {
	// // case of proj=src=bin and resource is a jar file on the classpath
	// return IJavaElement.PACKAGE_FRAGMENT_ROOT;
	// } else {
	// return NON_JAVA_RESOURCE;
	// }
	// }
	// default:
	// return NON_JAVA_RESOURCE;
	// }
	// }
	/**
	 * Answer a combination of the lastModified stamp and the size. Used for
	 * detecting external JAR changes
	 */
	public static long getTimeStamp(File file) {
		return file.lastModified() + file.length();
	}

	public void initializeRoots() {
		// remember roots infos as old roots infos
		this.oldRoots = this.roots == null ? new HashMap() : this.roots;
		this.oldOtherRoots = this.otherRoots == null ? new HashMap()
				: this.otherRoots;

		// recompute root infos only if necessary
		if (!rootsAreStale)
			return;

		this.roots = new HashMap();
		this.otherRoots = new HashMap();
		this.sourceAttachments = new HashMap();

		IJavaModel model = this.manager.getJavaModel();
		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;

				// root path
				IPath path = entry.getPath();
				if (this.roots.get(path) == null) {
					this.roots.put(path, new RootInfo(project, path,
							((ClasspathEntry) entry)
									.fullExclusionPatternChars()));
				} else {
					ArrayList rootList = (ArrayList) this.otherRoots.get(path);
					if (rootList == null) {
						rootList = new ArrayList();
						this.otherRoots.put(path, rootList);
					}
					rootList.add(new RootInfo(project, path,
							((ClasspathEntry) entry)
									.fullExclusionPatternChars()));
				}

				// source attachment path
				if (entry.getEntryKind() != IClasspathEntry.CPE_LIBRARY)
					continue;
				QualifiedName qName = new QualifiedName(JavaCore.PLUGIN_ID,
						"sourceattachment: " + path.toOSString()); //$NON-NLS-1$;
				String propertyString = null;
				try {
					propertyString = ResourcesPlugin.getWorkspace().getRoot()
							.getPersistentProperty(qName);
				} catch (CoreException e) {
					continue;
				}
				IPath sourceAttachmentPath;
				// if (propertyString != null) {
				// int index=
				// propertyString.lastIndexOf(JarPackageFragmentRoot.ATTACHMENT_PROPERTY_DELIMITER);
				// sourceAttachmentPath = (index < 0) ? new Path(propertyString)
				// : new Path(propertyString.substring(0, index));
				// } else {
				sourceAttachmentPath = entry.getSourceAttachmentPath();
				// }
				if (sourceAttachmentPath != null) {
					this.sourceAttachments.put(sourceAttachmentPath, path);
				}
			}
		}
		this.rootsAreStale = false;
	}

	/*
	 * Returns whether a given delta contains some information relevant to the
	 * JavaModel, in particular it will not consider SYNC or MARKER only deltas.
	 */
	public boolean isAffectedBy(IResourceDelta rootDelta) {
		// if (rootDelta == null) System.out.println("NULL DELTA");
		// long start = System.currentTimeMillis();
		if (rootDelta != null) {
			// use local exception to quickly escape from delta traversal
			class FoundRelevantDeltaException extends RuntimeException {
			}
			try {
				rootDelta.accept(new IResourceDeltaVisitor() {
					public boolean visit(IResourceDelta delta)
							throws CoreException {
						switch (delta.getKind()) {
						case IResourceDelta.ADDED:
						case IResourceDelta.REMOVED:
							throw new FoundRelevantDeltaException();
						case IResourceDelta.CHANGED:
							// if any flag is set but SYNC or MARKER, this delta
							// should be considered
							if (delta.getAffectedChildren().length == 0 // only
																		// check
																		// leaf
																		// delta
																		// nodes
									&& (delta.getFlags() & ~(IResourceDelta.SYNC | IResourceDelta.MARKERS)) != 0) {
								throw new FoundRelevantDeltaException();
							}
						}
						return true;
					}
				});
			} catch (FoundRelevantDeltaException e) {
				// System.out.println("RELEVANT DELTA detected in: "+
				// (System.currentTimeMillis() - start));
				return true;
			} catch (CoreException e) { // ignore delta if not able to traverse
			}
		}
		// System.out.println("IGNORE SYNC DELTA took: "+
		// (System.currentTimeMillis() - start));
		return false;
	}

	/*
	 * Returns whether the given resource is in one of the given output folders
	 * and if it is filtered out from this output folder.
	 */
	private boolean isResFilteredFromOutput(OutputsInfo info, IResource res,
			int elementType) {
		if (info != null) {
			IPath resPath = res.getFullPath();
			for (int i = 0; i < info.outputCount; i++) {
				if (info.paths[i].isPrefixOf(resPath)) {
					if (info.traverseModes[i] != IGNORE) {
						// case of bin=src
						if (info.traverseModes[i] == SOURCE
								&& elementType == IJavaElement.CLASS_FILE) {
							return true;
						} else {
							// case of .class file under project and no source
							// folder
							// proj=bin
							if (elementType == IJavaElement.JAVA_PROJECT
									&& res instanceof IFile
									&& PHPFileUtil.isPHPFile((IFile) res)) {
								return true;
							}
						}
					} else {
						return true;
					}
				}
			}
		}
		return false;
	}

	/*
	 * Merges all awaiting deltas.
	 */
	private IJavaElementDelta mergeDeltas(Collection deltas) {
		if (deltas.size() == 0)
			return null;
		if (deltas.size() == 1)
			return (IJavaElementDelta) deltas.iterator().next();

		if (VERBOSE) {
			System.out
					.println("MERGING " + deltas.size() + " DELTAS [" + Thread.currentThread() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}

		Iterator iterator = deltas.iterator();
		JavaElementDelta rootDelta = new JavaElementDelta(
				this.manager.javaModel);
		boolean insertedTree = false;
		while (iterator.hasNext()) {
			JavaElementDelta delta = (JavaElementDelta) iterator.next();
			if (VERBOSE) {
				System.out.println(delta.toString());
			}
			IJavaElement element = delta.getElement();
			if (this.manager.javaModel.equals(element)) {
				IJavaElementDelta[] children = delta.getAffectedChildren();
				for (int j = 0; j < children.length; j++) {
					JavaElementDelta projectDelta = (JavaElementDelta) children[j];
					rootDelta.insertDeltaTree(projectDelta.getElement(),
							projectDelta);
					insertedTree = true;
				}
				IResourceDelta[] resourceDeltas = delta.getResourceDeltas();
				if (resourceDeltas != null) {
					for (int i = 0, length = resourceDeltas.length; i < length; i++) {
						rootDelta.addResourceDelta(resourceDeltas[i]);
						insertedTree = true;
					}
				}
			} else {
				rootDelta.insertDeltaTree(element, delta);
				insertedTree = true;
			}
		}
		if (insertedTree)
			return rootDelta;
		return null;
	}

	/**
	 * 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) {
	//	
	// IResource resource = delta.getResource();
	// IJavaElement element = null;
	// boolean processChildren = false;
	//	
	// switch (resource.getType()) {
	//	
	// case IResource.ROOT :
	// if (delta.getKind() == IResourceDelta.CHANGED) {
	// element = JavaCore.create(resource);
	// processChildren = true;
	// }
	// break;
	// case IResource.PROJECT :
	// int kind = delta.getKind();
	// switch (kind) {
	// case IResourceDelta.CHANGED:
	// // do not visit non-java projects (see bug 16140 Non-java project gets
	// .classpath)
	// IProject project = (IProject)resource;
	// if (JavaProject.hasJavaNature(project)) {
	// element = JavaCore.create(resource);
	// processChildren = true;
	// } else if
	// (JavaModelManager.getJavaModelManager().getJavaModel().findJavaProject(project)
	// != null) {
	// // project had the java nature
	// this.rootsAreStale = true;
	//
	// // remove classpath cache so that initializeRoots() will not consider the
	// project has a classpath
	// this.manager.removePerProjectInfo((JavaProject)JavaCore.create(project));
	// }
	// break;
	// case IResourceDelta.ADDED:
	// this.rootsAreStale = true;
	// break;
	// case IResourceDelta.REMOVED:
	// // remove classpath cache so that initializeRoots() will not consider the
	// project has a classpath
	// this.manager.removePerProjectInfo((JavaProject)JavaCore.create(resource));
	//						
	// this.rootsAreStale = true;
	// break;
	// }
	// break;
	// case IResource.FILE :
	// if (parent.getElementType() == IJavaElement.JAVA_PROJECT) {
	// IFile file = (IFile) resource;
	// JavaProject project = (JavaProject) parent;
	//	
	// /* check classpath file change */
	// if (file.getName().equals(JavaProject.CLASSPATH_FILENAME)) {
	// reconcileClasspathFileUpdate(delta, file, project);
	// this.rootsAreStale = true;
	// break;
	// }
	// // /* check custom preference file change */
	// // if (file.getName().equals(JavaProject.PREF_FILENAME)) {
	// // reconcilePreferenceFileUpdate(delta, file, project);
	// // break;
	// // }
	// }
	// break;
	// }
	// if (processChildren) {
	// IResourceDelta[] children = delta.getAffectedChildren();
	// for (int i = 0; i < children.length; i++) {
	// performPreBuildCheck(children[i], element);
	// }
	// }
	// }
	// 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 = this.currentElement.getResource();
	// 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.
	 */

	/**
	 * Update the JavaModel according to a .classpath file change. The file can
	 * have changed as a result of a previous call to
	 * JavaProject#setRawClasspath or as a result of some user update (through
	 * repository)
	 */
	// void reconcileClasspathFileUpdate(IResourceDelta delta, IFile file,
	// JavaProject project) {
	//			
	// switch (delta.getKind()) {
	// case IResourceDelta.REMOVED : // recreate one based on in-memory
	// classpath
	// try {
	// JavaModelManager.PerProjectInfo info =
	// JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project.getProject());
	// if (info.classpath != null) { // if there is an in-memory classpath
	// project.saveClasspath(info.classpath, info.outputLocation);
	// }
	// } catch (JavaModelException e) {
	// if (project.getProject().isAccessible()) {
	// ProjectPrefUtil.log(e, "Could not save classpath for "+
	// project.getPath()); //$NON-NLS-1$
	// }
	// }
	// break;
	// case IResourceDelta.CHANGED :
	// if ((delta.getFlags() & IResourceDelta.CONTENT) == 0 // only consider
	// content change
	// && (delta.getFlags() & IResourceDelta.MOVED_FROM) == 0) // and also move
	// and overide scenario (see
	// http://dev.eclipse.org/bugs/show_bug.cgi?id=21420)
	// break;
	// case IResourceDelta.ADDED :
	// // check if any actual difference
	// project.flushClasspathProblemMarkers(false, true);
	// boolean wasSuccessful = false; // flag recording if .classpath file
	// change got reflected
	// try {
	// // force to (re)read the property file
	// IClasspathEntry[] fileEntries = project.readClasspathFile(true/*create
	// markers*/, false/*don't log problems*/);
	// if (fileEntries == null)
	// break; // could not read, ignore
	// JavaModelManager.PerProjectInfo info =
	// JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project.getProject());
	// if (info.classpath != null) { // if there is an in-memory classpath
	// if (project.isClasspathEqualsTo(info.classpath, info.outputLocation,
	// fileEntries)) {
	// wasSuccessful = true;
	// 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;
	// // clean mode will also default to reusing current one
	// }
	// project.setRawClasspath(
	// fileEntries,
	// outputLocation,
	// null, // monitor
	// true, // canChangeResource
	// project.getResolvedClasspath(true), // ignoreUnresolvedVariable
	// true, // needValidation
	// false); // no need to save
	//					
	// // if reach that far, the classpath file change got absorbed
	// wasSuccessful = true;
	// } catch (RuntimeException e) {
	// // setRawClasspath might fire a delta, and a listener may throw an
	// exception
	// if (project.getProject().isAccessible()) {
	// ProjectPrefUtil.log(e, "Could not set classpath for "+
	// project.getPath()); //$NON-NLS-1$
	// }
	// break;
	// } catch (JavaModelException e) { // CP failed validation
	// if (project.getProject().isAccessible()) {
	// if (e.getJavaModelStatus().getException() instanceof CoreException) {
	// // happens if the .classpath could not be written to disk
	// project.createClasspathProblemMarker(new JavaModelStatus(
	// IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
	// ProjectPrefUtil.bind("classpath.couldNotWriteClasspathFile",
	// project.getElementName(), e.getMessage()))); //$NON-NLS-1$
	// } else {
	// project.createClasspathProblemMarker(new JavaModelStatus(
	// IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
	// ProjectPrefUtil.bind("classpath.invalidClasspathInClasspathFile",
	// project.getElementName(), e.getMessage()))); //$NON-NLS-1$
	// }
	// }
	// break;
	// } finally {
	// if (!wasSuccessful) {
	// try {
	// project.setRawClasspath0(JavaProject.INVALID_CLASSPATH);
	// project.updatePackageFragmentRoots();
	// } catch (JavaModelException e) {
	// }
	// }
	// }
	// }
	// }
	/**
	 * Update the JavaModel according to a .jprefs file change. The file can
	 * have changed as a result of a previous call to JavaProject#setOptions or
	 * as a result of some user update (through repository) Unused until
	 * preference file get shared (.jpref)
	 */
	// void reconcilePreferenceFileUpdate(IResourceDelta delta, IFile file,
	// JavaProject project) {
	//			
	// switch (delta.getKind()) {
	// case IResourceDelta.REMOVED : // flush project custom settings
	// project.setOptions(null);
	// return;
	// case IResourceDelta.CHANGED :
	// if ((delta.getFlags() & IResourceDelta.CONTENT) == 0 // only consider
	// content change
	// && (delta.getFlags() & IResourceDelta.MOVED_FROM) == 0) // and also move
	// and overide scenario
	// break;
	// identityCheck : { // check if any actual difference
	// // force to (re)read the property file
	// Preferences filePreferences = project.loadPreferences();
	// if (filePreferences == null){
	// project.setOptions(null); // should have got removed delta.
	// return;
	// }
	// Preferences projectPreferences = project.getPreferences();
	// if (projectPreferences == null) return; // not a Java project
	//						
	// // compare preferences set to their default
	// String[] defaultProjectPropertyNames =
	// projectPreferences.defaultPropertyNames();
	// String[] defaultFilePropertyNames =
	// filePreferences.defaultPropertyNames();
	// if (defaultProjectPropertyNames.length ==
	// defaultFilePropertyNames.length) {
	// for (int i = 0; i < defaultProjectPropertyNames.length; i++){
	// String propertyName = defaultProjectPropertyNames[i];
	// if
	// (!projectPreferences.getString(propertyName).trim().equals(filePreferences.getString(propertyName).trim())){
	// break identityCheck;
	// }
	// }
	// } else break identityCheck;
	//
	// // compare custom preferences not set to their default
	// String[] projectPropertyNames = projectPreferences.propertyNames();
	// String[] filePropertyNames = filePreferences.propertyNames();
	// if (projectPropertyNames.length == filePropertyNames.length) {
	// for (int i = 0; i < projectPropertyNames.length; i++){
	// String propertyName = projectPropertyNames[i];
	// if
	// (!projectPreferences.getString(propertyName).trim().equals(filePreferences.getString(propertyName).trim())){
	// break identityCheck;
	// }
	// }
	// } else break identityCheck;
	//					
	// // identical - do nothing
	// return;
	// }
	// case IResourceDelta.ADDED :
	// // not identical, create delta and reset cached preferences
	// project.setPreferences(null);
	// // create delta
	// //fCurrentDelta.changed(project, IJavaElementDelta.F_OPTIONS_CHANGED);
	// }
	// }
	/*
	 * Registers the given delta with this delta processor.
	 */
	public void registerJavaModelDelta(IJavaElementDelta delta) {
		this.javaModelDeltas.add(delta);
	}

	/**
	 * 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 = (JavaElementInfo) parent
						.getElementInfo();
				info.removeChild(child);
			} catch (JavaModelException e) {
				// do nothing - we already checked if open
			}
		}
	}

	/**
	 * Notification that some resource changes have happened on the platform,
	 * and that the Java Model should update any required internal structures
	 * such that its elements remain consistent. Translates
	 * <code>IResourceDeltas</code> into <code>IJavaElementDeltas</code>.
	 * 
	 * @see IResourceDelta
	 * @see IResource
	 */
	public void resourceChanged(IResourceChangeEvent event) {
		if (event.getSource() instanceof IWorkspace) {
			int eventType = this.overridenEventType == -1 ? event.getType()
					: this.overridenEventType;
			IResource resource = event.getResource();
			IResourceDelta delta = event.getDelta();

			switch (eventType) {
			case IResourceChangeEvent.PRE_DELETE:
				try {
					if (resource.getType() == IResource.PROJECT
							&& ((IProject) resource)
									.hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) {

						deleting((IProject) resource);
					}
				} catch (CoreException e) {
					// project doesn't exist or is not open: ignore
				}
				return;

			case IResourceChangeEvent.POST_CHANGE:
				if (isAffectedBy(delta)) { // avoid populating for SYNC or
											// MARKER deltas
					try {
						try {
							stopDeltas();
							// checkProjectsBeingAddedOrRemoved(delta);
							// if (this.refreshedElements != null) {
							// createExternalArchiveDelta(null);
							// }
							IJavaElementDelta translatedDelta = processResourceDelta(delta);
							if (translatedDelta != null) {
								registerJavaModelDelta(translatedDelta);
							}
						} finally {
							startDeltas();
						}
						// notifyTypeHierarchies(this.state.elementChangedListeners,
						// this.state.elementChangedListenerCount);
						fire(null, ElementChangedEvent.POST_CHANGE);
					} finally {
						// workaround for bug 15168 circular errors not reported
						this.state.modelProjectsCache = null;
						this.removedRoots = null;
					}
				}
				return;

			case IResourceChangeEvent.PRE_BUILD:
				DeltaProcessingState.ProjectUpdateInfo[] updates = this.state
						.removeAllProjectUpdates();
				if (updates != null) {
					for (int i = 0, length = updates.length; i < length; i++) {
						try {
							updates[i].updateProjectReferencesIfNecessary();
						} catch (JavaModelException e) {
							// do nothing
						}
					}
				}
				// this.processPostChange = false;
				if (isAffectedBy(delta)) { // avoid populating for SYNC or
											// MARKER deltas
				// updateClasspathMarkers(delta);
					PHPBuilder.buildStarting();
				}
				// does not fire any deltas
				return;

			case IResourceChangeEvent.POST_BUILD:
				PHPBuilder.buildFinished();
				return;
			}
		}
		// // jsurfer TODO compare 3.0 sources
		// if (event.getSource() instanceof IWorkspace) {
		// int eventType = this.overridenEventType == -1 ? event.getType() :
		// this.overridenEventType;
		// IResource resource = event.getResource();
		// IResourceDelta delta = event.getDelta();
		//			
		// switch(eventType){
		// case IResourceChangeEvent.PRE_DELETE :
		// try {
		// if(resource.getType() == IResource.PROJECT
		// && ((IProject) resource).hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) {
		// // TODO jsurfer temp-del
		// // this.deleting((IProject)resource);
		// }
		// } catch(CoreException e){
		// }
		// return;
		//					
		// case IResourceChangeEvent.PRE_BUILD :
		// // TODO jsurfer temp-del
		// // if(isAffectedBy(delta)) { // avoid populating for SYNC or MARKER
		// deltas
		// // this.checkProjectsBeingAddedOrRemoved(delta);
		// //
		// // // update the classpath related markers
		// // this.updateClasspathMarkers();
		// //
		// // // the following will close project if affected by the property
		// file change
		// // try {
		// // // don't fire classpath change deltas right away, but batch them
		// // this.manager.stopDeltas();
		// // this.performPreBuildCheck(delta, null);
		// // } finally {
		// // this.manager.startDeltas();
		// // }
		// // }
		// // only fire already computed deltas (resource ones will be processed
		// in post change only)
		// this.manager.fire(null, ElementChangedEvent.PRE_AUTO_BUILD);
		// break;
		//
		// case IResourceChangeEvent.POST_BUILD :
		// // TODO jsurfer temp-del
		// // JavaBuilder.finishedBuilding(event);
		// break;
		//					
		// case IResourceChangeEvent.POST_CHANGE :
		// // TODO jsurfer temp-del
		// // if (isAffectedBy(delta)) {
		// // try {
		// // if (this.refreshedElements != null) {
		// // try {
		// // createExternalArchiveDelta(null);
		// // } catch (JavaModelException e) {
		// // e.printStackTrace();
		// // }
		// // }
		// // IJavaElementDelta translatedDelta =
		// this.processResourceDelta(delta);
		// // if (translatedDelta != null) {
		// // this.manager.registerJavaModelDelta(translatedDelta);
		// // }
		// // this.manager.fire(null, ElementChangedEvent.POST_CHANGE);
		// // } finally {
		// // // workaround for bug 15168 circular errors not reported
		// // this.manager.javaProjectsCache = null;
		// // this.removedRoots = null;
		// // }
		// // }
		// }
		// }
	}

	/*
	 * Turns the firing mode to on. That is, deltas that are/have been
	 * registered will be fired.
	 */
	private void startDeltas() {
		this.isFiring = true;
	}

	/*
	 * Turns the firing mode to off. That is, deltas that are/have been
	 * registered will not be fired until deltas are started again.
	 */
	private void stopDeltas() {
		this.isFiring = false;
	}

	/*
	 * Note that the project is about to be deleted.
	 */
	private void deleting(IProject project) {

		try {
			// discard indexing jobs that belong to this project so that the
			// project can be
			// deleted without interferences from the index manager
			// this.manager.indexManager.discardJobs(project.getName());

			JavaProject javaProject = (JavaProject) JavaCore.create(project);

			// remember roots of this project
			if (this.removedRoots == null) {
				this.removedRoots = new HashMap();
			}
			if (javaProject.isOpen()) {
				this.removedRoots.put(javaProject, javaProject
						.getPackageFragmentRoots());
			} else {
				// compute roots without opening project
				// this.removedRoots.put(
				// javaProject,
				// javaProject.computePackageFragmentRoots(
				// javaProject.getResolvedClasspath(true/*ignoreUnresolvedEntry*/,
				// false/*don't generateMarkerOnError*/, false/*don't
				// returnResolutionInProgress*/),
				// false));
			}

			javaProject.close();

			// workaround for bug 15168 circular errors not reported
			if (this.state.modelProjectsCache == null) {
				this.state.modelProjectsCache = this.manager.getJavaModel()
						.getJavaProjects();
			}
			this.removeFromParentInfo(javaProject);

		} catch (JavaModelException e) {
			// java project doesn't exist: ignore
		}
	}

	/*
	 * 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.
	 */
	private IJavaElementDelta processResourceDelta(IResourceDelta changes) {

		try {
			IJavaModel model = this.manager.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 null;
				}
			}
			this.state.initializeRoots();
			this.currentElement = null;

			// get the workspace delta, and start processing there.
			IResourceDelta[] deltas = changes.getAffectedChildren();
			for (int i = 0; i < deltas.length; i++) {
				IResourceDelta delta = deltas[i];
				IResource res = delta.getResource();

				// find out the element type
				// RootInfo rootInfo = null;
				int elementType;
				IProject proj = (IProject) res;
				boolean wasJavaProject = this.manager.getJavaModel()
						.findJavaProject(proj) != null;
				boolean isJavaProject = JavaProject.hasJavaNature(proj);
				if (!wasJavaProject && !isJavaProject) {
					elementType = NON_JAVA_RESOURCE;
				} else {
					// rootInfo = this.enclosingRootInfo(res.getFullPath(),
					// delta.getKind());
					// if (rootInfo != null &&
					// rootInfo.isRootOfProject(res.getFullPath())) {
					// elementType = IJavaElement.PACKAGE_FRAGMENT_ROOT;
					// } else {
					elementType = IJavaElement.JAVA_PROJECT;
					// }
				}

				// traverse delta
				// this.traverseDelta(delta, elementType, rootInfo, null);

				if (elementType == NON_JAVA_RESOURCE
						|| (wasJavaProject != isJavaProject && (delta.getKind()) == IResourceDelta.CHANGED)) { // project
																												// has
																												// changed
																												// nature
																												// (description
																												// or
																												// open/closed)
					try {
						// add child as non java resource
						nonJavaResourcesChanged((JavaModel) model, delta);
					} catch (JavaModelException e) {
						// java model could not be opened
					}
				}

			}
			// refreshPackageFragmentRoots();
			// resetProjectCaches();

			return this.currentDelta;
		} finally {
			this.currentDelta = null;
			// this.rootsToRefresh.clear();
			// this.projectCachesToReset.clear();
		}
	}

	/*
	 * Converts an <code>IResourceDelta</code> and its children into the
	 * corresponding <code>IJavaElementDelta</code>s.
	 */
	// private void traverseDelta(
	// IResourceDelta delta,
	// int elementType,
	// RootInfo rootInfo,
	// OutputsInfo outputsInfo) {
	//			
	// IResource res = delta.getResource();
	//	
	// // set stack of elements
	// if (this.currentElement == null && rootInfo != null) {
	// // this.currentElement = rootInfo.project;
	// }
	//		
	// // process current delta
	// boolean processChildren = true;
	// if (res instanceof IProject) {
	// processChildren =
	// this.updateCurrentDeltaAndIndex(
	// delta,
	// elementType == IJavaElement.PACKAGE_FRAGMENT_ROOT ?
	// IJavaElement.JAVA_PROJECT : // case of prj=src
	// elementType,
	// rootInfo);
	// } else if (rootInfo != null) {
	// processChildren = this.updateCurrentDeltaAndIndex(delta, elementType,
	// rootInfo);
	// } else {
	// // not yet inside a package fragment root
	// processChildren = true;
	// }
	//		
	// // get the project's output locations and traverse mode
	// if (outputsInfo == null) outputsInfo = this.outputsInfo(rootInfo, res);
	//	
	// // process children if needed
	// if (processChildren) {
	// IResourceDelta[] children = delta.getAffectedChildren();
	// boolean oneChildOnClasspath = false;
	// int length = children.length;
	// IResourceDelta[] orphanChildren = null;
	// Openable parent = null;
	// boolean isValidParent = true;
	// for (int i = 0; i < length; i++) {
	// IResourceDelta child = children[i];
	// IResource childRes = child.getResource();
	//	
	// // check source attachment change
	// this.checkSourceAttachmentChange(child, childRes);
	//				
	// // find out whether the child is a package fragment root of the current
	// project
	// IPath childPath = childRes.getFullPath();
	// int childKind = child.getKind();
	// RootInfo childRootInfo = this.rootInfo(childPath, childKind);
	// if (childRootInfo != null && !childRootInfo.isRootOfProject(childPath)) {
	// // package fragment root of another project (dealt with later)
	// childRootInfo = null;
	// }
	//				
	// // compute child type
	// int childType =
	// this.elementType(
	// childRes,
	// childKind,
	// elementType,
	// rootInfo == null ? childRootInfo : rootInfo
	// );
	//						
	// // is childRes in the output folder and is it filtered out ?
	// boolean isResFilteredFromOutput =
	// this.isResFilteredFromOutput(outputsInfo, childRes, childType);
	//
	// boolean isNestedRoot = rootInfo != null && childRootInfo != null;
	// if (!isResFilteredFromOutput
	// && !isNestedRoot) { // do not treat as non-java rsc if nested root
	//
	// this.traverseDelta(child, childType, rootInfo == null ? childRootInfo :
	// rootInfo, outputsInfo); // traverse delta for child in the same project
	//
	// if (childType == NON_JAVA_RESOURCE) {
	// if (rootInfo != null) { // if inside a package fragment root
	// if (!isValidParent) continue;
	// if (parent == null) {
	// // find the parent of the non-java resource to attach to
	// if (this.currentElement == null
	// || !rootInfo.project.equals(this.currentElement.getJavaProject())) { //
	// note if currentElement is the IJavaModel, getJavaProject() is null
	// // force the currentProject to be used
	// this.currentElement = rootInfo.project;
	// }
	// 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 = rootInfo.project;
	// } else {
	// parent = this.createElement(res, elementType, rootInfo);
	// }
	// if (parent == null) {
	// isValidParent = false;
	// continue;
	// }
	// }
	// // add child as non java resource
	// try {
	// nonJavaResourcesChanged(parent, child);
	// } catch (JavaModelException e) {
	// // ignore
	// }
	// } else {
	// // the non-java resource (or its parent folder) will be attached to the
	// java project
	// if (orphanChildren == null) orphanChildren = new IResourceDelta[length];
	// orphanChildren[i] = child;
	// }
	// } else {
	// oneChildOnClasspath = true;
	// }
	// } else {
	// oneChildOnClasspath = true; // to avoid reporting child delta as non-java
	// resource delta
	// }
	//								
	// // if child is a nested root
	// // or if it is not a package fragment root of the current project
	// // but it is a package fragment root of another project, traverse delta
	// too
	// if (isNestedRoot
	// || (childRootInfo == null && (childRootInfo = this.rootInfo(childPath,
	// childKind)) != null)) {
	// this.traverseDelta(child, IJavaElement.PACKAGE_FRAGMENT_ROOT,
	// childRootInfo, null); // binary output of childRootInfo.project cannot be
	// this root
	// }
	//	
	// // if the child is a package fragment root of one or several other
	// projects
	// ArrayList rootList;
	// if ((rootList = this.otherRootsInfo(childPath, childKind)) != null) {
	// Iterator iterator = rootList.iterator();
	// while (iterator.hasNext()) {
	// childRootInfo = (RootInfo) iterator.next();
	// this.traverseDelta(child, IJavaElement.PACKAGE_FRAGMENT_ROOT,
	// childRootInfo, null); // binary output of childRootInfo.project cannot be
	// this root
	// }
	// }
	// }
	// if (orphanChildren != null
	// && (oneChildOnClasspath // orphan children are siblings of a package
	// fragment root
	// || res instanceof IProject)) { // non-java resource directly under a
	// project
	//						
	// // attach orphan children
	// IProject rscProject = res.getProject();
	// JavaProject adoptiveProject = (JavaProject)JavaCore.create(rscProject);
	// if (adoptiveProject != null
	// && JavaProject.hasJavaNature(rscProject)) { // delta iff Java project
	// (18698)
	// for (int i = 0; i < length; i++) {
	// if (orphanChildren[i] != null) {
	// try {
	// nonJavaResourcesChanged(adoptiveProject, orphanChildren[i]);
	// } catch (JavaModelException e) {
	// // ignore
	// }
	// }
	// }
	// }
	// } // else resource delta will be added by parent
	// } // else resource delta will be added by parent
	// }
	private void notifyListeners(IJavaElementDelta deltaToNotify,
			int eventType, IElementChangedListener[] listeners,
			int[] listenerMask, int listenerCount) {
		final ElementChangedEvent extraEvent = new ElementChangedEvent(
				deltaToNotify, eventType);
		for (int i = 0; i < listenerCount; i++) {
			if ((listenerMask[i] & eventType) != 0) {
				final IElementChangedListener listener = listeners[i];
				long start = -1;
				if (VERBOSE) {
					System.out
							.print("Listener #" + (i + 1) + "=" + listener.toString());//$NON-NLS-1$//$NON-NLS-2$
					start = System.currentTimeMillis();
				}
				// wrap callbacks with Safe runnable for subsequent listeners to
				// be called when some are causing grief
				Platform.run(new ISafeRunnable() {
					public void handleException(Throwable exception) {
						Util
								.log(exception,
										"Exception occurred in listener of Java element change notification"); //$NON-NLS-1$
					}

					public void run() throws Exception {
						listener.elementChanged(extraEvent);
					}
				});
				if (VERBOSE) {
					System.out
							.println(" -> " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
				}
			}
		}
	}

	// private void notifyTypeHierarchies(IElementChangedListener[] listeners,
	// int listenerCount) {
	// for (int i= 0; i < listenerCount; i++) {
	// final IElementChangedListener listener = listeners[i];
	// if (!(listener instanceof TypeHierarchy)) continue;
	//
	// // wrap callbacks with Safe runnable for subsequent listeners to be
	// called when some are causing grief
	// Platform.run(new ISafeRunnable() {
	// public void handleException(Throwable exception) {
	// ProjectPrefUtil.log(exception, "Exception occurred in listener of Java
	// element change notification"); //$NON-NLS-1$
	// }
	// public void run() throws Exception {
	// TypeHierarchy typeHierarchy = (TypeHierarchy)listener;
	// if (typeHierarchy.hasFineGrainChanges()) {
	// // case of changes in primary working copies
	// typeHierarchy.needsRefresh = true;
	// typeHierarchy.fireChange();
	// }
	// }
	// });
	// }
	// }
	/*
	 * 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>
	 */
	private void nonJavaResourcesChanged(Openable element, IResourceDelta delta)
			throws JavaModelException {

		// reset non-java resources if element was open
		if (element.isOpen()) {
			JavaElementInfo info = (JavaElementInfo) element.getElementInfo();
			switch (element.getElementType()) {
			case IJavaElement.JAVA_MODEL:
				((JavaModelInfo) info).nonJavaResources = null;
				currentDelta().addResourceDelta(delta);
				return;
			case IJavaElement.JAVA_PROJECT:
				((JavaProjectElementInfo) info).setNonJavaResources(null);

				// if a package fragment root is the project, clear it too
				JavaProject project = (JavaProject) element;
				PackageFragmentRoot projectRoot = (PackageFragmentRoot) project
						.getPackageFragmentRoot(project.getProject());
				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 current = currentDelta();
		JavaElementDelta elementDelta = current.find(element);
		if (elementDelta == null) {
			// don't use find after creating the delta as it can be null (see
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=63434)
			elementDelta = current
					.changed(element, IJavaElementDelta.F_CONTENT);
		}
		elementDelta.addResourceDelta(delta);
	}

	/*
	 * Flushes all deltas without firing them.
	 */
	public void flush() {
		this.javaModelDeltas = new ArrayList();
	}

	/*
	 * Finds the root info this path is included in. Returns null if not found.
	 */
	RootInfo enclosingRootInfo(IPath path, int kind) {
		while (path != null && path.segmentCount() > 0) {
			RootInfo rootInfo = this.rootInfo(path, kind);
			if (rootInfo != null)
				return rootInfo;
			path = path.removeLastSegments(1);
		}
		return null;
	}

	/*
	 * Fire Java Model delta, flushing them after the fact after post_change
	 * notification. If the firing mode has been turned off, this has no effect.
	 */
	public void fire(IJavaElementDelta customDelta, int eventType) {
		if (!this.isFiring)
			return;

		if (DEBUG) {
			System.out
					.println("-----------------------------------------------------------------------------------------------------------------------");//$NON-NLS-1$
		}

		IJavaElementDelta deltaToNotify;
		if (customDelta == null) {
			deltaToNotify = this.mergeDeltas(this.javaModelDeltas);
		} else {
			deltaToNotify = customDelta;
		}

		// Refresh internal scopes
		// if (deltaToNotify != null) {
		// Iterator scopes = this.manager.searchScopes.keySet().iterator();
		// while (scopes.hasNext()) {
		// AbstractSearchScope scope = (AbstractSearchScope)scopes.next();
		// scope.processDelta(deltaToNotify);
		// }
		// }

		// Notification

		// Important: if any listener reacts to notification by updating the
		// listeners list or mask, these lists will
		// be duplicated, so it is necessary to remember original lists in a
		// variable (since field values may change under us)
		IElementChangedListener[] listeners = this.state.elementChangedListeners;
		int[] listenerMask = this.state.elementChangedListenerMasks;
		int listenerCount = this.state.elementChangedListenerCount;

		switch (eventType) {
		case DEFAULT_CHANGE_EVENT:
			firePostChangeDelta(deltaToNotify, listeners, listenerMask,
					listenerCount);
			fireReconcileDelta(listeners, listenerMask, listenerCount);
			break;
		case ElementChangedEvent.POST_CHANGE:
			firePostChangeDelta(deltaToNotify, listeners, listenerMask,
					listenerCount);
			fireReconcileDelta(listeners, listenerMask, listenerCount);
			break;
		}
	}

	private void firePostChangeDelta(IJavaElementDelta deltaToNotify,
			IElementChangedListener[] listeners, int[] listenerMask,
			int listenerCount) {

		// post change deltas
		if (DEBUG) {
			System.out
					.println("FIRING POST_CHANGE Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$
			System.out
					.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
		}
		if (deltaToNotify != null) {
			// flush now so as to keep listener reactions to post their own
			// deltas for subsequent iteration
			this.flush();

			notifyListeners(deltaToNotify, ElementChangedEvent.POST_CHANGE,
					listeners, listenerMask, listenerCount);
		}
	}

	private void fireReconcileDelta(IElementChangedListener[] listeners,
			int[] listenerMask, int listenerCount) {

		IJavaElementDelta deltaToNotify = mergeDeltas(this.reconcileDeltas
				.values());
		if (DEBUG) {
			System.out
					.println("FIRING POST_RECONCILE Delta [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$
			System.out
					.println(deltaToNotify == null ? "<NONE>" : deltaToNotify.toString()); //$NON-NLS-1$
		}
		if (deltaToNotify != null) {
			// flush now so as to keep listener reactions to post their own
			// deltas for subsequent iteration
			this.reconcileDeltas = new HashMap();

			notifyListeners(deltaToNotify, ElementChangedEvent.POST_RECONCILE,
					listeners, listenerMask, listenerCount);
		}
	}

	/*
	 * Returns the root info for the given path. Look in the old roots table if
	 * kind is REMOVED.
	 */
	RootInfo rootInfo(IPath path, int kind) {
		if (kind == IResourceDelta.REMOVED) {
			return (RootInfo) this.oldRoots.get(path);
		} else {
			return (RootInfo) this.roots.get(path);
		}
	}

	/*
	 * Returns the other root infos for the given path. Look in the old other
	 * roots table if kind is REMOVED.
	 */
	ArrayList otherRootsInfo(IPath path, int kind) {
		if (kind == IResourceDelta.REMOVED) {
			return (ArrayList) this.oldOtherRoots.get(path);
		} else {
			return (ArrayList) this.otherRoots.get(path);
		}
	}

	/**
	 * Converts an <code>IResourceDelta</code> and its children into the
	 * corresponding <code>IJavaElementDelta</code>s. Return whether the
	 * delta corresponds to a java element. If it is not a java element, it will
	 * be added as a non-java resource by the sender of this method.
	 */
	// protected boolean traverseDelta(
	// IResourceDelta delta,
	// int elementType,
	// RootInfo rootInfo,
	// OutputsInfo outputsInfo) {
	//			
	// IResource res = delta.getResource();
	//	
	// // set stack of elements
	// if (this.currentElement == null && rootInfo != null) {
	// this.currentElement = (Openable)rootInfo.project;
	// }
	//		
	// // process current delta
	// boolean processChildren = true;
	// if (res instanceof IProject) {
	// processChildren =
	// this.updateCurrentDeltaAndIndex(
	// delta,
	// elementType == IJavaElement.PACKAGE_FRAGMENT_ROOT ?
	// IJavaElement.JAVA_PROJECT : // case of prj=src
	// elementType,
	// rootInfo);
	// } else if (rootInfo != null) {
	// processChildren = this.updateCurrentDeltaAndIndex(delta, elementType,
	// rootInfo);
	// } else {
	// // not yet inside a package fragment root
	// processChildren = true;
	// }
	//		
	// // get the project's output locations and traverse mode
	// if (outputsInfo == null) outputsInfo = this.outputsInfo(rootInfo, res);
	//	
	// // process children if needed
	// if (processChildren) {
	// IResourceDelta[] children = delta.getAffectedChildren();
	// boolean oneChildOnClasspath = false;
	// int length = children.length;
	// IResourceDelta[] orphanChildren = null;
	// Openable parent = null;
	// boolean isValidParent = true;
	// for (int i = 0; i < length; i++) {
	// IResourceDelta child = children[i];
	// IResource childRes = child.getResource();
	//	
	// // check source attachment change
	// this.checkSourceAttachmentChange(child, childRes);
	//				
	// // find out whether the child is a package fragment root of the current
	// project
	// IPath childPath = childRes.getFullPath();
	// int childKind = child.getKind();
	// RootInfo childRootInfo = this.rootInfo(childPath, childKind);
	// if (childRootInfo != null && !childRootInfo.isRootOfProject(childPath)) {
	// // package fragment root of another project (dealt with later)
	// childRootInfo = null;
	// }
	//				
	// // compute child type
	// int childType =
	// this.elementType(
	// childRes,
	// childKind,
	// elementType,
	// rootInfo == null ? childRootInfo : rootInfo
	// );
	//						
	// // is childRes in the output folder and is it filtered out ?
	// boolean isResFilteredFromOutput =
	// this.isResFilteredFromOutput(outputsInfo, childRes, childType);
	//
	// boolean isNestedRoot = rootInfo != null && childRootInfo != null;
	// if (!isResFilteredFromOutput
	// && !isNestedRoot) { // do not treat as non-java rsc if nested root
	// if (!this.traverseDelta(child, childType, rootInfo == null ?
	// childRootInfo : rootInfo, outputsInfo)) { // traverse delta for child in
	// the same project
	// // it is a non-java resource
	// try {
	// if (rootInfo != null) { // if inside a package fragment root
	// if (!isValidParent) continue;
	// if (parent == null) {
	// // find the parent of the non-java resource to attach to
	// if (this.currentElement == null
	// || !this.currentElement.getJavaProject().equals(rootInfo.project)) {
	// // force the currentProject to be used
	// this.currentElement = (Openable)rootInfo.project;
	// }
	// 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)rootInfo.project;
	// } else {
	// parent = this.createElement(res, elementType, rootInfo);
	// }
	// if (parent == null) {
	// isValidParent = false;
	// continue;
	// }
	// }
	// // add child as non java resource
	// nonJavaResourcesChanged(parent, child);
	// } else {
	// // the non-java resource (or its parent folder) will be attached to the
	// java project
	// if (orphanChildren == null) orphanChildren = new IResourceDelta[length];
	// orphanChildren[i] = child;
	// }
	// } catch (JavaModelException e) {
	// }
	// } else {
	// oneChildOnClasspath = true;
	// }
	// } else {
	// oneChildOnClasspath = true; // to avoid reporting child delta as non-java
	// resource delta
	// }
	//								
	// // if child is a nested root
	// // or if it is not a package fragment root of the current project
	// // but it is a package fragment root of another project, traverse delta
	// too
	// if (isNestedRoot
	// || (childRootInfo == null && (childRootInfo = this.rootInfo(childPath,
	// childKind)) != null)) {
	// this.traverseDelta(child, IJavaElement.PACKAGE_FRAGMENT_ROOT,
	// childRootInfo, null); // binary output of childRootInfo.project 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
	// ArrayList rootList;
	// if ((rootList = this.otherRootsInfo(childPath, childKind)) != null) {
	// Iterator iterator = rootList.iterator();
	// while (iterator.hasNext()) {
	// childRootInfo = (RootInfo) iterator.next();
	// this.traverseDelta(child, IJavaElement.PACKAGE_FRAGMENT_ROOT,
	// childRootInfo, null); // binary output of childRootInfo.project cannot be
	// this root
	// }
	// }
	// }
	// if (orphanChildren != null
	// && (oneChildOnClasspath // orphan children are siblings of a package
	// fragment root
	// || res instanceof IProject)) { // non-java resource directly under a
	// project
	//						
	// // attach orphan children
	// IProject rscProject = res.getProject();
	// JavaProject adoptiveProject = (JavaProject)JavaCore.create(rscProject);
	// if (adoptiveProject != null
	// && JavaProject.hasJavaNature(rscProject)) { // delta iff Java project
	// (18698)
	// 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 elementType != NON_JAVA_RESOURCE; // TODO: (jerome) do we still
	// need to return? (check could be done by caller)
	// } else {
	// return elementType != NON_JAVA_RESOURCE;
	// }
	// }
	/**
	 * Update the classpath markers and cycle markers for the projects to
	 * update.
	 */
	// void updateClasspathMarkers() {
	// 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();
	// }
	// }
	/*
	 * 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, RootInfo rootInfo) {
	// Openable element;
	// switch (delta.getKind()) {
	// case IResourceDelta.ADDED :
	// IResource deltaRes = delta.getResource();
	// element = this.createElement(deltaRes, elementType, rootInfo);
	// if (element == null) {
	// // resource might be containing shared roots (see bug 19058)
	// this.updateRoots(deltaRes.getFullPath(), delta);
	// return false;
	// }
	// this.updateIndex(element, delta);
	// this.elementAdded(element, delta, rootInfo);
	// return false;
	// case IResourceDelta.REMOVED :
	// deltaRes = delta.getResource();
	// element = this.createElement(deltaRes, elementType, rootInfo);
	// if (element == null) {
	// // resource might be containing shared roots (see bug 19058)
	// this.updateRoots(deltaRes.getFullPath(), delta);
	// return false;
	// }
	// this.updateIndex(element, delta);
	// this.elementRemoved(element, delta, rootInfo);
	//	
	// if (deltaRes.getType() == IResource.PROJECT){
	// // reset the corresponding project built state, since cannot reuse if
	// added back
	// this.manager.setLastBuiltState((IProject)deltaRes, null /*no state*/);
	// }
	// return false;
	// case IResourceDelta.CHANGED :
	// int flags = delta.getFlags();
	// if ((flags & IResourceDelta.CONTENT) != 0) {
	// // content has changed
	// element = this.createElement(delta.getResource(), elementType, rootInfo);
	// if (element == null) return false;
	// 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, rootInfo);
	// if (element == null) {
	// // resource might be containing shared roots (see bug 19058)
	// this.updateRoots(res.getFullPath(), delta);
	// return false;
	// }
	// if (res.isOpen()) {
	// if (JavaProject.hasJavaNature(res)) {
	// this.elementAdded(element, delta, rootInfo);
	// this.indexManager.indexAll(res);
	// }
	// } else {
	// JavaModel javaModel = this.manager.getJavaModel();
	// boolean wasJavaProject = javaModel.findJavaProject(res) != null;
	// if (wasJavaProject) {
	// this.elementRemoved(element, delta, rootInfo);
	// this.indexManager.discardJobs(element.getElementName());
	// this.indexManager.removeIndexFamily(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 = this.manager.getJavaModel();
	// boolean wasJavaProject = javaModel.findJavaProject(res) != null;
	// boolean isJavaProject = JavaProject.hasJavaNature(res);
	// if (wasJavaProject != isJavaProject) {
	// // project's nature has been added or removed
	// element = this.createElement(res, elementType, rootInfo);
	// if (element == null) return false; // note its resources are still
	// visible as roots to other projects
	// if (isJavaProject) {
	// this.elementAdded(element, delta, rootInfo);
	// this.indexManager.indexAll(res);
	// } else {
	// this.elementRemoved(element, delta, rootInfo);
	// this.indexManager.discardJobs(element.getElementName());
	// this.indexManager.removeIndexFamily(res.getFullPath());
	// // reset the corresponding project built state, since cannot reuse if
	// added back
	// this.manager.setLastBuiltState(res, null /*no state*/);
	// }
	// return false; // when a project's nature is added/removed don't process
	// children
	// }
	// }
	// }
	// return true;
	// }
	// return true;
	// }
	/**
	 * Traverse the set of projects which have changed namespace, and refresh
	 * their dependents
	 */
	// public void updateDependentNamelookups() {
	// Iterator iterator;
	// // update namelookup of dependent projects
	// iterator = this.projectsForDependentNamelookupRefresh.iterator();
	// HashSet affectedDependents = new HashSet();
	// while (iterator.hasNext()) {
	// JavaProject project = (JavaProject)iterator.next();
	// addDependentProjects(project.getPath(), affectedDependents);
	// }
	// iterator = affectedDependents.iterator();
	// while (iterator.hasNext()) {
	// JavaProject project = (JavaProject) iterator.next();
	// if (project.isOpen()){
	// try {
	// ((JavaProjectElementInfo)project.getElementInfo()).setNameLookup(null);
	// } catch (JavaModelException e) {
	// }
	// }
	// }
	// }
	// protected void updateIndex(Openable element, IResourceDelta delta) {
	//
	// if (indexManager == null)
	// return;
	//
	// switch (element.getElementType()) {
	// case IJavaElement.JAVA_PROJECT :
	// switch (delta.getKind()) {
	// case IResourceDelta.ADDED :
	// this.indexManager.indexAll(element.getJavaProject().getProject());
	// break;
	// case IResourceDelta.REMOVED :
	// this.indexManager.removeIndexFamily(element.getJavaProject().getProject().getFullPath());
	// // NB: Discarding index jobs belonging to this project was done during
	// PRE_DELETE
	// 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
	// this.indexManager.discardJobs(jarPath.toString());
	// this.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 name = resource.getName();
	// if (ProjectPrefUtil.isJavaFileName(name)) {
	// Openable cu = (Openable)pkg.getCompilationUnit(name);
	// this.updateIndex(cu, child);
	// } else if (ProjectPrefUtil.isClassFileName(name)) {
	// Openable classFile = (Openable)pkg.getClassFile(name);
	// 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);
	// }
	// }
	// }
	/*
	 * Update the roots that are affected by the addition or the removal of the
	 * given container resource.
	 */
	// private void updateRoots(IPath containerPath, IResourceDelta
	// containerDelta) {
	// Map roots;
	// Map otherRoots;
	// if (containerDelta.getKind() == IResourceDelta.REMOVED) {
	// roots = this.oldRoots;
	// otherRoots = this.oldOtherRoots;
	// } else {
	// roots = this.roots;
	// otherRoots = this.otherRoots;
	// }
	// Iterator iterator = roots.keySet().iterator();
	// while (iterator.hasNext()) {
	// IPath path = (IPath)iterator.next();
	// if (containerPath.isPrefixOf(path) && !containerPath.equals(path)) {
	// IResourceDelta rootDelta =
	// containerDelta.findMember(path.removeFirstSegments(1));
	// if (rootDelta == null) continue;
	// RootInfo rootInfo = (RootInfo)roots.get(path);
	//
	// if (!rootInfo.project.getPath().isPrefixOf(path)) { // only consider
	// roots that are not included in the container
	// this.updateCurrentDeltaAndIndex(rootDelta,
	// IJavaElement.PACKAGE_FRAGMENT_ROOT, rootInfo);
	// }
	//			
	// ArrayList rootList = (ArrayList)otherRoots.get(path);
	// if (rootList != null) {
	// Iterator otherProjects = rootList.iterator();
	// while (otherProjects.hasNext()) {
	// rootInfo = (RootInfo)otherProjects.next();
	// if (!rootInfo.project.getPath().isPrefixOf(path)) { // only consider
	// roots that are not included in the container
	// this.updateCurrentDeltaAndIndex(rootDelta,
	// IJavaElement.PACKAGE_FRAGMENT_ROOT, rootInfo);
	// }
	// }
	// }
	// }
	// }
	// }
}
