X-Git-Url: http://secure.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/DeltaProcessor.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/DeltaProcessor.java index 45ffede..a0bf965 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/DeltaProcessor.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/DeltaProcessor.java @@ -12,18 +12,24 @@ 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.PHPCore; import net.sourceforge.phpeclipse.PHPeclipsePlugin; import org.eclipse.core.resources.IFile; @@ -37,6 +43,8 @@ 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; @@ -58,8 +66,11 @@ public class DeltaProcessor implements IResourceChangeListener { final static String EXTERNAL_JAR_UNCHANGED = "external jar unchanged"; //$NON-NLS-1$ final static String INTERNAL_JAR_IGNORE = "internal jar ignore"; //$NON-NLS-1$ - final static int NON_JAVA_RESOURCE = -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 JavaElementDelta corresponding to the IResourceDelta being translated. */ @@ -93,14 +104,38 @@ public class DeltaProcessor implements IResourceChangeListener { * 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(); - JavaModelManager manager; + /* + * 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. @@ -112,7 +147,6 @@ public class DeltaProcessor implements IResourceChangeListener { * This is null if no refresh is needed. */ HashSet refreshedElements; - public static boolean VERBOSE = false; class OutputsInfo { IPath[] paths; @@ -190,10 +224,19 @@ public class DeltaProcessor implements IResourceChangeListener { } } - DeltaProcessor(JavaModelManager manager) { +// 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. @@ -229,7 +272,7 @@ public class DeltaProcessor implements IResourceChangeListener { * to update. */ void addToProjectsToUpdateWithDependents(IProject project) { - this.projectsToUpdate.add(PHPCore.create(project)); + this.projectsToUpdate.add(JavaCore.create(project)); // this.addDependentProjects(project.getFullPath(), this.projectsToUpdate); } @@ -672,7 +715,7 @@ public class DeltaProcessor implements IResourceChangeListener { // } else if (((JavaProject)root.getJavaProject()).contains(resource)) { // // create package handle // IPath pkgPath = path.removeFirstSegments(root.getPath().segmentCount()); -// String pkg = Util.packageName(pkgPath); +// String pkg = ProjectPrefUtil.packageName(pkgPath); // if (pkg == null) return null; // element = root.getPackageFragment(pkg); // } @@ -693,7 +736,7 @@ public class DeltaProcessor implements IResourceChangeListener { // IPackageFragmentRoot root = (IPackageFragmentRoot)this.currentElement; // IPath rootPath = root.getPath(); // IPath pkgPath = path.removeLastSegments(1); -// String pkgName = Util.packageName(pkgPath.removeFirstSegments(rootPath.segmentCount())); +// String pkgName = ProjectPrefUtil.packageName(pkgPath.removeFirstSegments(rootPath.segmentCount())); // if (pkgName != null) { // pkgFragment = root.getPackageFragment(pkgName); // } @@ -881,7 +924,7 @@ public class DeltaProcessor implements IResourceChangeListener { // IResource resource = child.getResource(); // if (resource instanceof IFolder) { // String folderName = resource.getName(); -// if (Util.isValidFolderNameForPackage(folderName)) { +// if (ProjectPrefUtil.isValidFolderNameForPackage(folderName)) { // String subpkgName = // name.length() == 0 ? // folderName : @@ -991,7 +1034,7 @@ public class DeltaProcessor implements IResourceChangeListener { // IResource resource = child.getResource(); // if (resource instanceof IFolder) { // String folderName = resource.getName(); -// if (Util.isValidFolderNameForPackage(folderName)) { +// if (ProjectPrefUtil.isValidFolderNameForPackage(folderName)) { // String subpkgName = // name.length() == 0 ? // folderName : @@ -1031,20 +1074,20 @@ public class DeltaProcessor implements IResourceChangeListener { // if (rootInfo == null) { // rootInfo = this.enclosingRootInfo(res.getFullPath(), kind); // } -// if (rootInfo == null || Util.isExcluded(res, rootInfo.exclusionPatterns)) { +// if (rootInfo == null || ProjectPrefUtil.isExcluded(res, rootInfo.exclusionPatterns)) { // return NON_JAVA_RESOURCE; // } // if (res instanceof IFolder) { -// if (Util.isValidFolderNameForPackage(res.getName())) { +// if (ProjectPrefUtil.isValidFolderNameForPackage(res.getName())) { // return IJavaElement.PACKAGE_FRAGMENT; // } else { // return NON_JAVA_RESOURCE; // } // } else { // String fileName = res.getName(); -// if (Util.isValidCompilationUnitName(fileName)) { +// if (ProjectPrefUtil.isValidCompilationUnitName(fileName)) { // return IJavaElement.COMPILATION_UNIT; -// } else if (Util.isValidClassFileName(fileName)) { +// } 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 @@ -1114,7 +1157,7 @@ public class DeltaProcessor implements IResourceChangeListener { // source attachment path if (entry.getEntryKind() != IClasspathEntry.CPE_LIBRARY) continue; - QualifiedName qName = new QualifiedName(PHPCore.PLUGIN_ID, "sourceattachment: " + path.toOSString()); //$NON-NLS-1$; + QualifiedName qName = new QualifiedName(JavaCore.PLUGIN_ID, "sourceattachment: " + path.toOSString()); //$NON-NLS-1$; String propertyString = null; try { propertyString = ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(qName); @@ -1203,96 +1246,48 @@ public class DeltaProcessor implements IResourceChangeListener { } return false; } - - /** - * Generic processing for elements with changed contents: + /* + * Merges all awaiting deltas. */ -// protected 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 elementDelta = currentDelta().find(element); -// if (elementDelta == null) { -// currentDelta().changed(element, IJavaElementDelta.F_CONTENT); -// elementDelta = currentDelta().find(element); -// } -// elementDelta.addResourceDelta(delta); -// } -// private OutputsInfo outputsInfo(RootInfo rootInfo, IResource res) { -// try { -// IJavaProject proj = -// rootInfo == null ? -// (IJavaProject)this.createElement(res.getProject(), IJavaElement.JAVA_PROJECT, null) : -// rootInfo.project; -// if (proj != null) { -// IPath projectOutput = proj.getOutputLocation(); -// int traverseMode = IGNORE; -// if (proj.getProject().getFullPath().equals(projectOutput)){ // case of proj==bin==src -// return new OutputsInfo(new IPath[] {projectOutput}, new int[] {SOURCE}, 1); -// } else { -// IClasspathEntry[] classpath = proj.getResolvedClasspath(true); -// IPath[] outputs = new IPath[classpath.length+1]; -// int[] traverseModes = new int[classpath.length+1]; -// int outputCount = 1; -// outputs[0] = projectOutput; -// traverseModes[0] = traverseMode; -// for (int i = 0, length = classpath.length; i < length; i++) { -// IClasspathEntry entry = classpath[i]; -// IPath entryPath = entry.getPath(); -// IPath output = entry.getOutputLocation(); -// if (output != null) { -// outputs[outputCount] = output; -// // check case of src==bin -// if (entryPath.equals(output)) { -// traverseModes[outputCount++] = (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) ? SOURCE : BINARY; -// } else { -// traverseModes[outputCount++] = IGNORE; -// } -// } -// -// // check case of src==bin -// if (entryPath.equals(projectOutput)) { -// traverseModes[0] = (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) ? SOURCE : BINARY; -// } -// } -// return new OutputsInfo(outputs, traverseModes, outputCount); -// } -// } -// } catch (JavaModelException e) { -// } -// return null; -// } + 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 @@ -1403,75 +1398,6 @@ public class DeltaProcessor implements IResourceChangeListener { * the corresponding set of IJavaElementDelta, rooted in the * relevant JavaModels. */ -// public 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.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 -// if (!this.traverseDelta(delta, elementType, rootInfo, null) -// || (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) { -// } -// } -// -// } -// -// // update package fragment roots of projects that were affected -// Iterator iterator = this.projectsToUpdate.iterator(); -// while (iterator.hasNext()) { -// JavaProject project = (JavaProject)iterator.next(); -// project.updatePackageFragmentRoots(); -// } -// -// updateDependentNamelookups(); -// -// return this.currentDelta; -// } finally { -// this.currentDelta = null; -// this.projectsToUpdate.clear(); -// this.projectsForDependentNamelookupRefresh.clear(); -// } -// } /** * Update the JavaModel according to a .classpath file change. The file can have changed as a result of a previous @@ -1488,7 +1414,7 @@ public class DeltaProcessor implements IResourceChangeListener { // } // } catch (JavaModelException e) { // if (project.getProject().isAccessible()) { -// Util.log(e, "Could not save classpath for "+ project.getPath()); //$NON-NLS-1$ +// ProjectPrefUtil.log(e, "Could not save classpath for "+ project.getPath()); //$NON-NLS-1$ // } // } // break; @@ -1544,7 +1470,7 @@ public class DeltaProcessor implements IResourceChangeListener { // } catch (RuntimeException e) { // // setRawClasspath might fire a delta, and a listener may throw an exception // if (project.getProject().isAccessible()) { -// Util.log(e, "Could not set classpath for "+ project.getPath()); //$NON-NLS-1$ +// ProjectPrefUtil.log(e, "Could not set classpath for "+ project.getPath()); //$NON-NLS-1$ // } // break; // } catch (JavaModelException e) { // CP failed validation @@ -1553,11 +1479,11 @@ public class DeltaProcessor implements IResourceChangeListener { // // happens if the .classpath could not be written to disk // project.createClasspathProblemMarker(new JavaModelStatus( // IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT, -// Util.bind("classpath.couldNotWriteClasspathFile", project.getElementName(), e.getMessage()))); //$NON-NLS-1$ +// ProjectPrefUtil.bind("classpath.couldNotWriteClasspathFile", project.getElementName(), e.getMessage()))); //$NON-NLS-1$ // } else { // project.createClasspathProblemMarker(new JavaModelStatus( // IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT, -// Util.bind("classpath.invalidClasspathInClasspathFile", project.getElementName(), e.getMessage()))); //$NON-NLS-1$ +// ProjectPrefUtil.bind("classpath.invalidClasspathInClasspathFile", project.getElementName(), e.getMessage()))); //$NON-NLS-1$ // } // } // break; @@ -1632,7 +1558,12 @@ public class DeltaProcessor implements IResourceChangeListener { // //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, @@ -1650,6 +1581,7 @@ public class DeltaProcessor implements IResourceChangeListener { } } } + /** * Notification that some resource changes have happened * on the platform, and that the Java Model should update any required @@ -1660,74 +1592,525 @@ public class DeltaProcessor implements IResourceChangeListener { * @see IResource */ public void resourceChanged(IResourceChangeEvent event) { - - if (event.getSource() instanceof IWorkspace) { - IResource resource = event.getResource(); - IResourceDelta delta = event.getDelta(); - - switch(event.getType()){ - case IResourceChangeEvent.PRE_DELETE : + 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 { - if(resource.getType() == IResource.PROJECT - && ((IProject) resource).hasNature(PHPeclipsePlugin.PHP_NATURE_ID)) { - // TODO khartlage temp-del -// this.deleting((IProject)resource); + try { + stopDeltas(); +// checkProjectsBeingAddedOrRemoved(delta); +// if (this.refreshedElements != null) { +// createExternalArchiveDelta(null); +// } + IJavaElementDelta translatedDelta = processResourceDelta(delta); + if (translatedDelta != null) { + registerJavaModelDelta(translatedDelta); + } + } finally { + startDeltas(); } - } catch(CoreException e){ +// 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_AUTO_BUILD : -// TODO khartlage 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(); + } + 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){ // } - // only fire already computed deltas (resource ones will be processed in post change only) - this.manager.fire(null, ElementChangedEvent.PRE_AUTO_BUILD); - break; +// 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()); - case IResourceChangeEvent.POST_AUTO_BUILD : -// TODO khartlage temp-del -// JavaBuilder.finishedBuilding(event); - break; - - case IResourceChangeEvent.POST_CHANGE : -// TODO khartlage temp-del -// if (isAffectedBy(delta)) { -// try { -// if (this.refreshedElements != null) { -// try { -// createExternalArchiveDelta(null); -// } catch (JavaModelException e) { -// e.printStackTrace(); + 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 IResourceDelta rooted in a Workspace into + * the corresponding set of IJavaElementDelta, rooted in the + * relevant JavaModels. + */ + 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 IResourceDelta and its children into + * the corresponding IJavaElementDeltas. + */ +// 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; // } // } -// IJavaElementDelta translatedDelta = this.processResourceDelta(delta); -// if (translatedDelta != null) { -// this.manager.registerJavaModelDelta(translatedDelta); +// // 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 // } -// this.manager.fire(null, ElementChangedEvent.POST_CHANGE); -// } finally { -// // workaround for bug 15168 circular errors not reported -// this.manager.javaProjectsCache = null; -// this.removedRoots = null; // } // } +// } +// } // 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: + */ + 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. @@ -1741,6 +2124,89 @@ public class DeltaProcessor implements IResourceChangeListener { 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 ? "" : 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 ? "" : 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) { @@ -2147,10 +2613,10 @@ public class DeltaProcessor implements IResourceChangeListener { // IResource resource = child.getResource(); // if (resource instanceof IFile) { // String name = resource.getName(); -// if (Util.isJavaFileName(name)) { +// if (ProjectPrefUtil.isJavaFileName(name)) { // Openable cu = (Openable)pkg.getCompilationUnit(name); // this.updateIndex(cu, child); -// } else if (Util.isClassFileName(name)) { +// } else if (ProjectPrefUtil.isClassFileName(name)) { // Openable classFile = (Openable)pkg.getClassFile(name); // this.updateIndex(classFile, child); // }