### Eclipse Workspace Patch 1.0 #P org.eclipse.jst.jsp.core Index: src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java,v retrieving revision 1.27 diff -u -r1.27 JSPCoreMessages.java --- src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java 19 Mar 2010 20:18:09 -0000 1.27 +++ src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java 1 Sep 2010 12:23:07 -0000 @@ -61,6 +61,9 @@ public static String Initializing; public static String Persisting_JSP_Translations; + public static String JSPCorePlugin_Initializing_JSP_Tools; + public static String JSPIndexManager; + /** * @deprecated */ Index: src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java,v retrieving revision 1.19.2.1 diff -u -r1.19.2.1 JSPCorePlugin.java --- src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java 23 Aug 2010 20:43:11 -0000 1.19.2.1 +++ src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java 1 Sep 2010 12:23:07 -0000 @@ -10,9 +10,13 @@ *******************************************************************************/ package org.eclipse.jst.jsp.core.internal; +import org.eclipse.core.resources.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.ISaveContext; import org.eclipse.core.resources.ISaveParticipant; import org.eclipse.core.resources.ISavedState; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -24,7 +28,6 @@ import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController; import org.eclipse.jst.jsp.core.internal.contentproperties.JSPFContentPropertiesManager; import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache; -import org.eclipse.jst.jsp.core.internal.java.JSPTranslatorPersister; import org.eclipse.jst.jsp.core.internal.java.search.JSPIndexManager; import org.eclipse.jst.jsp.core.internal.taglib.TaglibHelperManager; import org.eclipse.jst.jsp.core.taglib.TaglibIndex; @@ -34,19 +37,22 @@ * The main plugin class to be used in the desktop. */ public class JSPCorePlugin extends Plugin { - // The shared instance. + /** singleton instance of the plugin */ private static JSPCorePlugin plugin; - - /** Save participant for this plugin */ - ISaveParticipant fSaveParticipant; /** + *

Job used to finish tasks needed to start up the plugin but that did not have + * to block the plugin start up process.

+ */ + private Job fPluginInitializerJob; + + /** * The constructor. */ public JSPCorePlugin() { super(); plugin = this; - fSaveParticipant = new SaveParticipant(); + this.fPluginInitializerJob = new PluginInitializerJob(); } /** @@ -65,7 +71,7 @@ super.start(context); /* - * JSPIndexManager depends on TaglibController, so TaglibController + * JSPIndexManager_old depends on TaglibController, so TaglibController * should be started first */ TaglibIndex.startup(); @@ -73,37 +79,9 @@ // listen for classpath changes JavaCore.addElementChangedListener(TaglibHelperManager.getInstance()); - - //restore save state and process any events that happened before plugin loaded - if (JSPTranslatorPersister.ACTIVATED) { - Job persister = new Job(JSPCoreMessages.Initializing) { - protected IStatus run(IProgressMonitor monitor) { - ISavedState savedState = null; - try { - savedState = ResourcesPlugin.getWorkspace().addSaveParticipant(plugin.getBundle().getSymbolicName(), fSaveParticipant); - } - catch (CoreException e) { - Logger.logException("Could not load previous save state", e); - } - if (savedState != null) { - try { - Thread.currentThread().setPriority(Thread.MIN_PRIORITY); - } - finally { - savedState.processResourceChangeEvents(JSPTranslatorPersister.getDefault()); - } - } - return Status.OK_STATUS; - } - }; - persister.setUser(false); - persister.schedule(2000); - // set up persister to listen to resource change events - ResourcesPlugin.getWorkspace().addResourceChangeListener(JSPTranslatorPersister.getDefault()); - } - //init the JSP index - JSPIndexManager.getInstance().initialize(); + //schedule delayed initialization + this.fPluginInitializerJob.schedule(2000); // listen for resource changes to update content properties keys JSPFContentPropertiesManager.startup(); @@ -119,22 +97,14 @@ public void stop(BundleContext context) throws Exception { DeploymentDescriptorPropertyCache.stop(); - /* - * stop listening for resource changes to update content properties - * keys - */ + // stop listening for resource changes to update content properties keys JSPFContentPropertiesManager.shutdown(); //remove the plugin save participant ResourcesPlugin.getWorkspace().removeSaveParticipant(plugin.getBundle().getSymbolicName()); - //remove the translator persister - if(JSPTranslatorPersister.ACTIVATED) { - ResourcesPlugin.getWorkspace().removeResourceChangeListener(JSPTranslatorPersister.getDefault()); - } - // stop any indexing - JSPIndexManager.getInstance().shutdown(); + JSPIndexManager.getDefault().stop(); // stop listening for classpath changes JavaCore.removeElementChangedListener(TaglibHelperManager.getInstance()); @@ -147,6 +117,72 @@ } /** + *

A {@link Job} used to perform delayed initialization for the plugin

+ */ + private static class PluginInitializerJob extends Job { + /** + *

Default constructor to set up this {@link Job} as a + * long running system {@link Job}

+ */ + protected PluginInitializerJob() { + super(JSPCoreMessages.JSPCorePlugin_Initializing_JSP_Tools); + + this.setUser(false); + this.setSystem(true); + this.setPriority(Job.LONG); + } + + /** + *

Perform delayed initialization for the plugin

+ * + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + IStatus status = Status.OK_STATUS; + final IWorkspace workspace = ResourcesPlugin.getWorkspace(); + try { + workspace.run(new IWorkspaceRunnable() { + public void run(final IProgressMonitor worspaceMonitor) throws CoreException { + ISavedState savedState = null; + + try { + //add the save participant for this bundle + savedState = ResourcesPlugin.getWorkspace().addSaveParticipant( + JSPCorePlugin.plugin.getBundle().getSymbolicName(), new SaveParticipant()); + } catch (CoreException e) { + Logger.logException("JSP Core Plugin failed at loading previously saved state." + + " All componenets dependent on this state will start as if first workspace load.", e); + } + + //if there is a saved state start up using that, else start up cold + if(savedState != null) { + try { + Thread.currentThread().setPriority(Thread.MIN_PRIORITY); + } finally { + savedState.processResourceChangeEvents(new IResourceChangeListener() { + /** + * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) + */ + public void resourceChanged(IResourceChangeEvent event) { + JSPIndexManager.getDefault().start(event.getDelta(), worspaceMonitor); + } + }); + } + } else { + JSPIndexManager.getDefault().start(null, worspaceMonitor); + } + } + }, monitor); + } catch(CoreException e) { + status = e.getStatus(); + } + + return status; + } + + } + + /** * Used so that all of the IResourceChangeEvents that occurred before * this plugin loaded can be processed. */ Index: src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties,v retrieving revision 1.33 diff -u -r1.33 JSPCorePluginResources.properties --- src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties 19 Mar 2010 20:18:09 -0000 1.33 +++ src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties 1 Sep 2010 12:23:07 -0000 @@ -47,4 +47,7 @@ TLDValidator_MissingVariable=The variable class '{0}' was not found on the Java Build Path TLDValidator_MissingListener=The listener class '{0}' was not found on the Java Build Path Initializing=Processing JSP changes since last activation -Persisting_JSP_Translations=Persisting JSP Translations \ No newline at end of file +Persisting_JSP_Translations=Persisting JSP Translations + +JSPCorePlugin_Initializing_JSP_Tools=Initializing JSP Tools +JSPIndexManager=JSP Index Manager \ No newline at end of file Index: src/org/eclipse/jst/jsp/core/internal/java/JSPTranslation.java =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslation.java,v retrieving revision 1.18 diff -u -r1.18 JSPTranslation.java --- src/org/eclipse/jst/jsp/core/internal/java/JSPTranslation.java 15 Apr 2010 02:14:10 -0000 1.18 +++ src/org/eclipse/jst/jsp/core/internal/java/JSPTranslation.java 1 Sep 2010 12:23:07 -0000 @@ -16,7 +16,6 @@ import java.util.Iterator; import java.util.List; -import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -29,7 +28,8 @@ import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jface.text.Position; @@ -436,9 +436,22 @@ return null; final String name = getClassname() + ".java"; - IFile fakeFile = je.getProject().getFile(name); - ICompilationUnit fakeUnit = JavaCore.createCompilationUnitFrom(fakeFile); - ICompilationUnit cu = fakeUnit.getWorkingCopy(getWorkingCopyOwner(), getProgressMonitor()); + IPackageFragmentRoot packageFragmentRoot = null; + IPackageFragmentRoot[] packageFragmentRoots = je.getPackageFragmentRoots(); + for (int i = 0; i < packageFragmentRoots.length; i++) { + if (!packageFragmentRoots[i].isArchive() && !packageFragmentRoots[i].isExternal()) { + packageFragmentRoot = packageFragmentRoots[i]; + break; + } + } + if (packageFragmentRoot == null) { + if(DEBUG) { + System.out.println("** Abort create working copy: cannot create working copy: JSP is not in a Java project with source package fragment root"); //$NON-NLS-1$ + } + return null; + } + final IPackageFragment fragment = packageFragmentRoot.getPackageFragment(IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH); + ICompilationUnit cu = fragment.getCompilationUnit(name).getWorkingCopy(getWorkingCopyOwner(), getProgressMonitor()); setContents(cu); if(DEBUG) { Index: src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java,v retrieving revision 1.17 diff -u -r1.17 JSPTranslationAdapter.java --- src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java 28 Jan 2010 19:59:44 -0000 1.17 +++ src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java 1 Sep 2010 12:23:07 -0000 @@ -115,7 +115,7 @@ fDocumentIsDirty = true; } - public void release() { + public synchronized void release() { if (fJspDocument != null) fJspDocument.removeDocumentListener(this); @@ -129,6 +129,7 @@ System.out.println("JSPTranslationAdapter releasing:" + fJSPTranslation); //$NON-NLS-1$ fJSPTranslation.release(); + fJSPTranslation = null; } } Index: src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java,v retrieving revision 1.3 diff -u -r1.3 JSPTranslatorPersister.java --- src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java 19 Mar 2010 20:18:09 -0000 1.3 +++ src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java 1 Sep 2010 12:23:07 -0000 @@ -17,31 +17,17 @@ import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.util.LinkedList; import java.util.zip.CRC32; import org.eclipse.core.resources.IFile; 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.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.ISafeRunnable; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Platform; -import org.eclipse.core.runtime.SafeRunner; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.content.IContentType; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jst.jsp.core.internal.JSPCoreMessages; import org.eclipse.jst.jsp.core.internal.JSPCorePlugin; import org.eclipse.jst.jsp.core.internal.Logger; +import org.eclipse.jst.jsp.core.internal.java.search.JSPIndexManager; import org.eclipse.jst.jsp.core.internal.modelhandler.ModelHandlerForJSP; -import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.FileBufferModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; @@ -49,32 +35,19 @@ import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel; /** - *

This {@link IResourceChangeListener} is used to keep the {@link JSPTranslator}s for JSP - * resources persisted to disk. It can also be used to get persisted translators

- *

This class should be registered as an {@link IResourceChangeListener} on the Workspace - * as well as processing resource change events from a saved state, for example use see below.

- *

Plugin Activation: - *

- * try {
- *   ISavedState savedState = ResourcesPlugin.getWorkspace().addSaveParticipant(
- *     plugin.getBundle().getSymbolicName(), this.fSaveParticipant);
- *   if (savedState != null) {
- *     savedState.processResourceChangeEvents(JSPTranslatorPersistor.getDefault());
- *   }
- * } catch(CoreException e) {}
- * ResourcesPlugin.getWorkspace().addResourceChangeListener(JSPTranslatorPersistor.getDefault());
- * 
- * Plugin Deactivation: - *
- * ResourcesPlugin.getWorkspace().removeSaveParticipant(plugin.getBundle().getSymbolicName());
- * ResourcesPlugin.getWorkspace().removeResourceChangeListener(JSPTranslatorPersistor.getDefault());
- * 

+ *

This is a static class used to persist JSP translations and retrieve the persisted + * translations.

+ * + *

It is not actually in charge of finding files to persist, rather it provides API + * for some other mechanism that tracks JSP files to call into to persist the translations.

* *

This class can be deactivated through the persistJSPTranslations system property, * a value of true means the persister is activated (which is the default), value of * false means the persister is not activated.

+ * + * @see JSPIndexManager */ -public class JSPTranslatorPersister implements IResourceChangeListener { +public class JSPTranslatorPersister{ /** * true if the persister is activated, false * otherwise. This is determined by checking the system property @@ -90,63 +63,10 @@ /** used to calculate persisted translator file names */ private static final CRC32 CHECKSUM_CALC = new CRC32(); - /** singleton instance of the {@link JSPTranslatorPersister} */ - private static final JSPTranslatorPersister INSTANCE = new JSPTranslatorPersister(); - - /** - * Used to handle resource change events - * @see #resourceChanged(IResourceChangeEvent) - */ - private IResourceDeltaVisitor fResourceDeltaVisitor; - - /** {@link Job} that actually does all the persisting */ - protected PersisterJob fPersisterJob; - /** - *

Private singleton default constructor

+ *

Private constructor to prevent creating an instance of this class

*/ private JSPTranslatorPersister() { - this.fResourceDeltaVisitor = new JSPResourceVisitor(); - this.fPersisterJob = new PersisterJob(); - } - - /** - *

NOTE: This can possible return null

- * - * @return Singleton instance of the {@link JSPTranslatorPersister} if - * {@link #ACTIVATED} is true, null otherwise. - */ - public static JSPTranslatorPersister getDefault() { - return ACTIVATED ? INSTANCE : null; - } - - /** - * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) - */ - public void resourceChanged(IResourceChangeEvent event) { - switch(event.getType()) { - case IResourceChangeEvent.PRE_CLOSE: - case IResourceChangeEvent.PRE_DELETE: - //pre-close or pre-delete stop the persister job so it does not interfere - this.fPersisterJob.stop(); - break; - case IResourceChangeEvent.POST_CHANGE: - //post change start up the persister job and process the delta - this.fPersisterJob.start(); - - // only analyze the full (starting at root) delta hierarchy - IResourceDelta delta = event.getDelta(); - if (delta != null && delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$ - try { - //use visitor to visit all children - delta.accept(this.fResourceDeltaVisitor, false); - } catch (CoreException e) { - Logger.logException("Processing resource change event delta failed, " + - "persisted JSPTranslators may not have been updated.", e); - } - } - break; - } } /** @@ -210,6 +130,45 @@ } /** + * @param resource JSP resource who's translation should be persisted + */ + public static void persistTranslation(IResource resource) { + if(ACTIVATED) { + IPath path = resource.getFullPath(); + String filePath = getPersistedTranslatorFilePath(path.toPortableString()); + IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path); + + JSPTranslator translator = getJSPTranslator(file); + if(translator != null) { + persistTranslator(translator, filePath); + } + } + } + + /** + * @param resource JSP resource who's translation should no longer be persisted + */ + public static void removePersistedTranslation(IResource resource) { + if(ACTIVATED) { + File file = getPersistedFile(resource.getFullPath()); + deletePersistedTranslator(file); + } + } + + /** + * @param resource JSP resource that has moved and thus its persisted translation should be updated + * @param fromPath Path the JSP resource moved from + */ + public static void movePersistedTranslation(IResource resource, IPath fromPath) { + if(ACTIVATED) { + File from = getPersistedFile(fromPath); + File to = getPersistedFile(resource.getFullPath()); + + renamePersistedTranslator(from, to); + } + } + + /** *

Given the path to a JSP file determines the path to its persisted {@link JSPTranslator}

* * @param jspFilePath {@link IPath} to JSP file for which the path to its persisted {@link JSPTranslator} @@ -218,7 +177,7 @@ * @return OS file path to the persisted {@link JSPTranslator} associated with the JSP file at * jspFilePath */ - protected static String getPersistedTranslatorFilePath(String jspFilePath) { + private static String getPersistedTranslatorFilePath(String jspFilePath) { CHECKSUM_CALC.reset(); CHECKSUM_CALC.update(jspFilePath.getBytes()); String persistedTranslatorFileName = Long.toString(CHECKSUM_CALC.getValue()) + ".translator"; //$NON-NLS-1$ @@ -240,290 +199,84 @@ } /** - * @see JSPResourceVisitor#visit(IResourceDelta) + *

Gets the associated {@link JSPTranslator} for a specific JSP file.

+ *

NOTE: This does not get the persisted translator but rather the + * associated translator in memory

+ * + * @param jspFile {@link IFile} to the JSP file that the associated {@link JSPTranslator} + * is needed for + * @return {@link JSPTranslator} associated with the given jspFilePath, or + * null if none can be found. */ - private class JSPResourceVisitor implements IResourceDeltaVisitor { - /** - *

Default constructor

- */ - protected JSPResourceVisitor() { - } - - /** - *

For each {@link IResourceDelta} determine if its a JSP resource and if it is - * add the appropriate action to the {@link PersisterJob} so as not to hold up - * the {@link IResourceDelta}

- * - * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) - */ - public boolean visit(IResourceDelta delta) throws CoreException { - if(isJSPResource(delta.getResource())) { - switch (delta.getKind()) { - case IResourceDelta.CHANGED : - case IResourceDelta.ADDED : { - /* if a move, then move the persisted translation - * else create a new persisted translation, if its a change then - * the old persisted translation will be overwritten */ - if((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) { - final File from = getPersistedFile(delta.getMovedFromPath()); - final File to = getPersistedFile(delta.getFullPath()); - //add the move action to the persister job - JSPTranslatorPersister.this.fPersisterJob.addAction(new ISafeRunnable() { - public void run() throws Exception { - renamePersistedTranslator(from, to); - } - - public void handleException(Throwable exception) {} - }); - } else { - final String filePath = getPersistedTranslatorFilePath(delta.getFullPath().toPortableString()); - final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(delta.getFullPath()); - //add the add action to the persister job - JSPTranslatorPersister.this.fPersisterJob.addAction(new ISafeRunnable() { - public void run() throws Exception { - JSPTranslator translator = getJSPTranslator(file); - if(translator != null) { - persistTranslator(translator, filePath); - } - } - - public void handleException(Throwable exception) {} - }); - } - - break; - } - case IResourceDelta.REMOVED : { - /* only remove if its not a move, - * if it is a move the added file delta event will move translation */ - if((delta.getFlags() & IResourceDelta.MOVED_TO) == 0) { - final File file = getPersistedFile(delta.getFullPath()); - //add the delete action to the persister job - JSPTranslatorPersister.this.fPersisterJob.addAction(new ISafeRunnable() { - public void run() throws Exception { - deletePersistedTranslator(file); - } - - public void handleException(Throwable exception) {} - }); - } - break; - } - } - } - - //visit children deltas - return true; - } - - /** - *

Determines if an {@link IResource} is a JSP resource

- * - * @param resource determine if this {@link IResource} is a JSP resource - * @return true if resource is a JSP resource, - * false otherwise. - */ - private boolean isJSPResource(IResource resource) { - boolean isJSP = false; - - //general rule for getting files in the workspace - if(resource.getFullPath().segmentCount() >= 2) { - IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(resource.getFullPath()); - if(file.getType() == IResource.FILE) { - //get JSP content type each time because there is a possibility it could change - IContentType contentTypeJSP = Platform.getContentTypeManager().getContentType( - ContentTypeIdForJSP.ContentTypeID_JSP); - - isJSP = contentTypeJSP.isAssociatedWith(file.getName()); - } - } - - return isJSP; - } - - /** - *

Gets the associated {@link JSPTranslator} for a specific JSP file.

- *

NOTE: This does not get the persisted translator but rather the - * associated translator in memory

- * - * @param jspFile {@link IFile} to the JSP file that the associated {@link JSPTranslator} - * is needed for - * @return {@link JSPTranslator} associated with the given jspFilePath, or - * null if none can be found. - */ - protected JSPTranslator getJSPTranslator(IFile jspFile) { - IStructuredModel model = null; - JSPTranslator translator = null; - try { - model = StructuredModelManager.getModelManager().getModelForRead(jspFile); - if(model instanceof IDOMModel) { - IDOMDocument doc = ((IDOMModel)model).getDocument(); - ModelHandlerForJSP.ensureTranslationAdapterFactory(model); - JSPTranslationAdapter adapter = (JSPTranslationAdapter)doc.getAdapterFor(IJSPTranslation.class); - - //don't want to persist a translator that has not already been requested - if(adapter != null && adapter.hasTranslation()) { - translator = adapter.getJSPTranslation().getTranslator(); - } - } - } catch (IOException e) { - Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$ - " because could not read model for same.", e); //$NON-NLS-1$ - } catch (CoreException e) { - Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$ - " because could not read model for same.", e); //$NON-NLS-1$ - } finally { - if(model != null) { - model.releaseFromRead(); + private static JSPTranslator getJSPTranslator(IFile jspFile) { + IStructuredModel model = null; + JSPTranslator translator = null; + try { + model = StructuredModelManager.getModelManager().getModelForRead(jspFile); + if(model instanceof IDOMModel) { + IDOMDocument doc = ((IDOMModel)model).getDocument(); + ModelHandlerForJSP.ensureTranslationAdapterFactory(model); + JSPTranslationAdapter adapter = (JSPTranslationAdapter)doc.getAdapterFor(IJSPTranslation.class); + + //don't want to persist a translator that has not already been requested + if(adapter != null && adapter.hasTranslation()) { + translator = adapter.getJSPTranslation().getTranslator(); } } - - return translator; - } - - /** - *

Persists a {@link JSPTranslator} to disk for a specific JSP file

- * - * @param translator {@link JSPTranslator} to persist to disk - * @param jspFilePath {@link IPath} to the JSP file the given translator is for - */ - protected void persistTranslator(JSPTranslator translator, String filePath) { - try { - FileOutputStream fos = new FileOutputStream(filePath); - ObjectOutputStream out = new ObjectOutputStream(fos); - out.writeObject(translator); - out.close(); - } catch (IOException e) { - Logger.logException("Was unable to externalize JSPTranslator " + translator + //$NON-NLS-1$ - " to " + filePath, e); //$NON-NLS-1$ + } catch (IOException e) { + Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$ + " because could not read model for same.", e); //$NON-NLS-1$ + } catch (CoreException e) { + Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$ + " because could not read model for same.", e); //$NON-NLS-1$ + } finally { + if(model != null) { + model.releaseFromRead(); } } - /** - *

Deletes a persisted translation for a JSP file that has been deleted

- * - * @param jspFilePath {@link IPath} to the JSP file that has been deleted - */ - protected void deletePersistedTranslator(File file) { - file.delete(); - } - - /** - *

Renames a persisted translation for a JSP file that has moved

- * - * @param jspPrevFilePath {@link IPath} to the previous location of JSP file

- * @param jspNewFilePath {@link IPath} to new location of JSP file

- */ - protected void renamePersistedTranslator(File from, File to) { - //do the move - from.renameTo(to); - } - - private File getPersistedFile(IPath path) { - return new File(getPersistedTranslatorFilePath(path.toPortableString())); - } + return translator; } /** - *

{@link Job} responsible for reacting to {@link IResourceDelta} visited - * by {@link JSPResourceVisitor}. This way the actions that need to be taken - * in reaction to the delta to not hold up the {@link IResourceChangeListener} - * or the {@link IResourceDelta}.

- * - */ - private class PersisterJob extends Job { - /** Length to delay when scheduling job */ - private static final int DELAY = 500; - - /** - * {@link LinkedList}<{@link ISafeRunnable}> - *

The persister actions that have been queued up by the {@link JSPResourceVisitor}

- */ - private LinkedList fActions; - - /** Whether this job has been stopped or not */ - private boolean fIsStopped; - - /** - *

Sets job up as a system job

- */ - protected PersisterJob() { - super(JSPCoreMessages.Persisting_JSP_Translations); - this.setUser(false); - this.setSystem(true); - this.setPriority(Job.LONG); - - this.fActions = new LinkedList(); - this.fIsStopped = false; - } - - /** - *

Starts this job. This has no effect if the job is already started.

- *

This should be used in place of {@link Job#schedule()} to reset state - * caused by calling {@link #stop()}

- * - * @see #stop() - */ - protected synchronized void start() { - this.fIsStopped = false; - - //get the job running again depending on its current state - if(this.getState() == Job.SLEEPING) { - this.wakeUp(DELAY); - } else { - this.schedule(DELAY); - } - } - - /** - *

Stops this job, even if it is running

- *

This should be used in place of {@link Job#sleep()} because {@link Job#sleep()} - * will not stop a job that is already running but calling this will stop this job - * even if it is running. {@link #start()} must be used to start this job again

- * - * @see #start() - */ - protected synchronized void stop() { - //sleep the job if it is waiting to run - this.sleep(); - - //if job is already running will force it to stop - this.fIsStopped = true; - } - - /** - * @param action {@link ISafeRunnable} containing a persister action to take - * based on an {@link IResourceDelta} processed by {@link JSPResourceVisitor} - */ - protected void addAction(ISafeRunnable action) { - //add the action - synchronized (this.fActions) { - this.fActions.addLast(action); - } - - //if job has not been manually stopped, then start it - if(!this.fIsStopped) { - this.start(); - } + *

Persists a {@link JSPTranslator} to disk for a specific JSP file

+ * + * @param translator {@link JSPTranslator} to persist to disk + * @param jspFilePath {@link IPath} to the JSP file the given translator is for + */ + private static void persistTranslator(JSPTranslator translator, String filePath) { + try { + FileOutputStream fos = new FileOutputStream(filePath); + ObjectOutputStream out = new ObjectOutputStream(fos); + out.writeObject(translator); + out.close(); + } catch (IOException e) { + Logger.logException("Was unable to externalize JSPTranslator " + translator + //$NON-NLS-1$ + " to " + filePath, e); //$NON-NLS-1$ } + } + + /** + *

Deletes a persisted translation for a JSP file that has been deleted

+ * + * @param jspFilePath {@link IPath} to the JSP file that has been deleted + */ + private static void deletePersistedTranslator(File file) { + file.delete(); + } + + /** + *

Renames a persisted translation for a JSP file that has moved

+ * + * @param jspPrevFilePath {@link IPath} to the previous location of JSP file

+ * @param jspNewFilePath {@link IPath} to new location of JSP file

+ */ + private static void renamePersistedTranslator(File from, File to) { + //do the move + from.renameTo(to); + } - /** - *

Process the actions until there are none left

- * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) - */ - protected IStatus run(IProgressMonitor monitor) { - /* run so long as job has not been stopped, - *monitor canceled, and have actions to process */ - while(!this.fIsStopped && !monitor.isCanceled() && !this.fActions.isEmpty()) { - ISafeRunnable action; - synchronized (this.fActions) { - action = (ISafeRunnable)this.fActions.removeFirst(); - } - - SafeRunner.run(action); - } - - return Status.OK_STATUS; - } + private static File getPersistedFile(IPath path) { + return new File(getPersistedTranslatorFilePath(path.toPortableString())); } } \ No newline at end of file Index: src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java =================================================================== RCS file: src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java diff -N src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java --- src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java 11 Dec 2008 01:36:27 -0000 1.10 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,163 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004, 2005 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.jst.jsp.core.internal.java.search; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceProxy; -import org.eclipse.core.resources.IResourceProxyVisitor; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Platform; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.content.IContentType; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.jst.jsp.core.internal.JSPCoreMessages; -import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP; - -/** - * Re-indexes the entire workspace. - * Ensures the JSP Index is in a stable state before performing a search. - * (like after a crash or if previous indexing was canceled) - * - * @author pavery - */ -public class IndexWorkspaceJob extends Job { - - // for debugging - static final boolean DEBUG; - static { - String value= Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/jspindexmanager"); //$NON-NLS-1$ - DEBUG= value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$ - } - - /** - * Visitor that retrieves jsp project paths for all jsp files in the workspace, - * and adds the files to be indexed as they are encountered - */ - private class JSPFileVisitor implements IResourceProxyVisitor { - private List files = new ArrayList(); - - // monitor from the Job - IProgressMonitor fInnerMonitor = null; - public JSPFileVisitor(IProgressMonitor monitor) { - this.fInnerMonitor = monitor; - } - - public boolean visit(IResourceProxy proxy) throws CoreException { - - // check job canceled - if (this.fInnerMonitor != null && this.fInnerMonitor.isCanceled()) { - setCanceledState(); - return false; - } - - // check search support canceled - if(JSPSearchSupport.getInstance().isCanceled()) { - setCanceledState(); - return false; - } - - if (proxy.getType() == IResource.FILE) { - - // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3553 - // check this before description - // check name before actually getting the file (less work) - if(getJspContentType().isAssociatedWith(proxy.getName())) { - IFile file = (IFile) proxy.requestResource(); - if(file.exists()) { - - if(DEBUG) - System.out.println("(+) IndexWorkspaceJob adding file: " + file.getName()); //$NON-NLS-1$ - // this call will check the ContentTypeDescription, so don't need to do it here. - //JSPSearchSupport.getInstance().addJspFile(file); - this.files.add(file); - this.fInnerMonitor.subTask(proxy.getName()); - - // don't search deeper for files - return false; - } - } - } - return true; - } - - public final IFile[] getFiles() { - return (IFile[])this.files.toArray(new IFile[this.files.size()]); - } - } - - private IContentType fContentTypeJSP = null; - - public IndexWorkspaceJob() { - // pa_TODO may want to say something like "Rebuilding JSP Index" to be more - // descriptive instead of "Updating JSP Index" since they are 2 different things - super(JSPCoreMessages.JSPIndexManager_0); - setPriority(Job.LONG); - setSystem(true); - } - - IContentType getJspContentType() { - if(this.fContentTypeJSP == null) - this.fContentTypeJSP = Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP); - return this.fContentTypeJSP; - } - - /** - * @see org eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor) - * for similar method - */ - protected IStatus run(IProgressMonitor monitor) { - - IStatus status = Status.OK_STATUS; - Thread.currentThread().setPriority(Thread.MIN_PRIORITY); - - if(monitor.isCanceled()) { - setCanceledState(); - return Status.CANCEL_STATUS; - } - - if(DEBUG) - System.out.println(" ^ IndexWorkspaceJob started: "); //$NON-NLS-1$ - - long start = System.currentTimeMillis(); - - try { - JSPFileVisitor visitor = new JSPFileVisitor(monitor); - // collect all jsp files - ResourcesPlugin.getWorkspace().getRoot().accept(visitor, IResource.DEPTH_INFINITE); - // request indexing - // this is pretty much like faking an entire workspace resource delta - JSPIndexManager.getInstance().indexFiles(visitor.getFiles()); - } - catch (CoreException e) { - if(DEBUG) - e.printStackTrace(); - } - finally { - monitor.done(); - } - long finish = System.currentTimeMillis(); - if(DEBUG) - System.out.println(" ^ IndexWorkspaceJob finished\n total time running: " + (finish - start)); //$NON-NLS-1$ - - return status; - } - - void setCanceledState() { - JSPIndexManager.getInstance().setIndexState(JSPIndexManager.S_CANCELED); - } -} Index: src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java,v retrieving revision 1.27 diff -u -r1.27 JSPIndexManager.java --- src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java 5 Apr 2010 19:13:27 -0000 1.27 +++ src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java 1 Sep 2010 12:23:07 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2004, 2010 IBM Corporation and others. + * Copyright (c) 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,725 +7,146 @@ * * Contributors: * IBM Corporation - initial API and implementation + * *******************************************************************************/ package org.eclipse.jst.jsp.core.internal.java.search; import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; 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.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; -import org.eclipse.core.runtime.Plugin; -import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentType; -import org.eclipse.core.runtime.jobs.IJobChangeEvent; -import org.eclipse.core.runtime.jobs.Job; -import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.internal.core.JavaModelManager; -import org.eclipse.jdt.internal.core.index.Index; -import org.eclipse.jdt.internal.core.search.indexing.IndexManager; import org.eclipse.jst.jsp.core.internal.JSPCoreMessages; import org.eclipse.jst.jsp.core.internal.JSPCorePlugin; import org.eclipse.jst.jsp.core.internal.Logger; +import org.eclipse.jst.jsp.core.internal.java.JSPTranslatorPersister; import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP; -import org.eclipse.osgi.util.NLS; -import org.osgi.framework.Bundle; +import org.eclipse.wst.sse.core.indexing.AbstractIndexManager; /** - * Responsible for keeping the JSP index up to date. + *

Index manger used to update the JDT index with the Java translations + * of JSPs.

* - * @author pavery + *

Also keeps JSP persistence up to date

+ * + *

Any action that needs the JDT index to have all of the latest JSP changes processed + * should wait for this manger to report that it is consistent, + * {@link #waitForConsistant(org.eclipse.core.runtime.IProgressMonitor)}. Such actions + * include but are not limited to searching and refactoring JSPs.

*/ -public class JSPIndexManager { - - // for debugging - // TODO move this to Logger, as we have in SSE - static final boolean DEBUG; - static { - String value = Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/jspindexmanager"); //$NON-NLS-1$ - DEBUG = value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$ - } - - private static final String PKEY_INDEX_STATE = "jspIndexState"; //$NON-NLS-1$ - - private IndexWorkspaceJob indexingJob = new IndexWorkspaceJob(); - - - - // TODO: consider enumeration for these int constants - // set to S_UPDATING once a resource change comes in - // set to S_STABLE if: - // - we know we aren't interested in the resource change - // - or the ProcessFilesJob completes - // set to S_CANCELED if an indexing job is canceled - // set to S_REBUILDING if re-indexing the entire workspace - - // the int '0' is reserved for the default value if a preference is not - // there - /** index is reliable to use */ - public static final int S_STABLE = 1; - /** index is being updated (from a resource delta) */ - public static final int S_UPDATING = 2; - /** entire index is being rebuilt */ - public static final int S_REBUILDING = 3; - /** - * indexing job was canceled in the middle of it, index needs to be - * rebuilt - */ - public static final int S_CANCELED = 4; - - /** symbolic name for OSGI framework */ - private final String OSGI_FRAMEWORK_ID = "org.eclipse.osgi"; //$NON-NLS-1$ - - /** - * Collects JSP files from a resource delta. - */ - private class JSPResourceVisitor implements IResourceDeltaVisitor { - // using hash map ensures only one of each file - // must be reset before every use - private HashMap jspFiles = null; - - public JSPResourceVisitor() { - this.jspFiles = new HashMap(); - } - - public boolean visit(IResourceDelta delta) throws CoreException { - - // in case JSP search was canceled (eg. when closing the editor) - if (JSPSearchSupport.getInstance().isCanceled() || frameworkIsShuttingDown()) { - setCanceledState(); - return false; - } - - try { - if (!isHiddenResource(delta.getFullPath())) { - - int kind = delta.getKind(); - boolean added = (kind & IResourceDelta.ADDED) == IResourceDelta.ADDED; - boolean isInterestingChange = false; - if ((kind & IResourceDelta.CHANGED) == IResourceDelta.CHANGED) { - int flags = delta.getFlags(); - // ignore things like marker changes - isInterestingChange = (flags & IResourceDelta.CONTENT) == IResourceDelta.CONTENT || (flags & IResourceDelta.REPLACED) == IResourceDelta.REPLACED; - } - boolean removed = (kind & IResourceDelta.REMOVED) == IResourceDelta.REMOVED; - if (added || isInterestingChange) { - - visitAdded(delta); - } - else if (removed) { - visitRemoved(delta); - } - } - } - catch (Exception e) { - // need to set state here somehow, and reindex - // otherwise index will be unreliable - if (DEBUG) - Logger.logException("Delta analysis may not be complete", e); //$NON-NLS-1$ - } - // if the delta has children, continue to add/remove files - return true; - } - - private void visitRemoved(IResourceDelta delta) { - // handle cleanup - if (delta.getResource() != null) { - IResource r = delta.getResource(); - if (r.getType() == IResource.FOLDER && r.exists()) { - deleteIndex((IFile) r); - } - } - } - - private void visitAdded(IResourceDelta delta) { - // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3553 - // quick check if it's even JSP related to improve - // performance - // checking name from the delta before getting - // resource because it's lighter - String filename = delta.getFullPath().lastSegment(); - if (filename != null && getJspContentType().isAssociatedWith(filename)) { - IResource r = delta.getResource(); - if (r != null && r.exists() && r.getType() == IResource.FILE) { - this.jspFiles.put(r.getFullPath(), r); - } - } - } - - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=93463 - private boolean isHiddenResource(IPath p) { - String[] segments = p.segments(); - for (int i = 0; i < segments.length; i++) { - if (segments[i].startsWith(".")) //$NON-NLS-1$ - return true; - } - return false; - } - - private void deleteIndex(IFile folder) { - // cleanup index - IndexManager im = JavaModelManager.getIndexManager(); - IPath folderPath = folder.getFullPath(); - IPath indexLocation = JSPSearchSupport.getInstance().computeIndexLocation(folderPath); - im.removeIndex(indexLocation); - // im.indexLocations.removeKey(folderPath); - // im.indexLocations.removeValue(indexLocation); - File f = indexLocation.toFile(); - f.delete(); - } - - public IFile[] getFiles() { - return (IFile[]) this.jspFiles.values().toArray(new IFile[this.jspFiles.size()]); - } - - public void reset() { - this.jspFiles.clear(); - } - } - - // end class JSPResourceVisitor - +public class JSPIndexManager extends AbstractIndexManager { + /** the singleton instance of the {@link JSPIndexManager} */ + private static JSPIndexManager INSTANCE; + + /** the JSP {@link IContentType} */ + private static final IContentType JSP_CONTENT_TYPE = + Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP); + + /** the location to store state */ + private IPath fWorkingLocation; + /** - * schedules JSP files for indexing by Java core + *

Private singleton constructor

*/ - private class ProcessFilesJob extends Job { - List fileList = null; - // keep track of how many files we've indexed - int lastFileCursor = 0; - - ProcessFilesJob(String taskName) { - super(taskName); - fileList = new ArrayList(); - } - - synchronized void process(IFile[] files) { - for (int i = 0; i < files.length; i++) { - fileList.add(files[i]); - } - if (DEBUG) { - System.out.println("JSPIndexManager queuing " + files.length + " files"); //$NON-NLS-2$ //$NON-NLS-1$ - } - } - - synchronized IFile[] getFiles() { - return (IFile[]) fileList.toArray(new IFile[fileList.size()]); - } - - synchronized void clearFiles() { - fileList.clear(); - lastFileCursor = 0; - //System.out.println("cleared files"); - } - - protected IStatus run(IProgressMonitor monitor) { - // System.out.println("indexer monitor" + monitor); - if (isCanceled(monitor) || frameworkIsShuttingDown()) { - setCanceledState(); - return Status.CANCEL_STATUS; - } - - Thread.currentThread().setPriority(Thread.MIN_PRIORITY); - long start = System.currentTimeMillis(); - - try { - IFile[] filesToBeProcessed = getFiles(); - - if (DEBUG) { - System.out.println("JSPIndexManager indexing " + filesToBeProcessed.length + " files"); //$NON-NLS-2$ //$NON-NLS-1$ - } - // API indicates that monitor is never null - monitor.beginTask("", filesToBeProcessed.length); //$NON-NLS-1$ - JSPSearchSupport ss = JSPSearchSupport.getInstance(); - String processingNFiles = ""; //$NON-NLS-1$ - - - for (;lastFileCursor < filesToBeProcessed.length; lastFileCursor++) { - - if (isCanceled(monitor) || frameworkIsShuttingDown()) { - setCanceledState(); - return Status.CANCEL_STATUS; - } - IFile file = filesToBeProcessed[lastFileCursor]; - try { - IProject project = file.getProject(); - if (project != null) { - IJavaProject jproject = JavaCore.create(project); - if (jproject.exists()) { - ss.addJspFile(file); - if (DEBUG) { - System.out.println("JSPIndexManager Job added file: " + file.getName()); //$NON-NLS-1$ - } - } - // JSP Indexer processing n files - processingNFiles = NLS.bind(JSPCoreMessages.JSPIndexManager_2, new String[]{Integer.toString((filesToBeProcessed.length - lastFileCursor))}); - monitor.subTask(processingNFiles + " - " + file.getName()); //$NON-NLS-1$ - monitor.worked(1); - } - } - catch (Exception e) { - // RATLC00284776 - // ISSUE: we probably shouldn't be catching EVERY - // exception, but - // the framework only allows to return IStatus in - // order to communicate - // that something went wrong, which means the loop - // won't complete, and we would hit the same problem - // the next time. - // - // a possible solution is to keep track of the - // exceptions logged - // and only log a certain amt of the same one, - // otherwise skip it. - if (!frameworkIsShuttingDown()) { - String filename = file != null ? file.getFullPath().toString() : ""; //$NON-NLS-1$ - Logger.logException("JSPIndexer problem indexing:" + filename, e); //$NON-NLS-1$ - } - } - } // end for - } - finally { - // just in case something didn't follow API (monitor is null) - if (monitor != null) - monitor.done(); - } - - // successfully finished, clear files list - clearFiles(); - - long finish = System.currentTimeMillis(); - long diff = finish - start; - if (DEBUG) { - fTotalTime += diff; - System.out.println("============================================================================"); //$NON-NLS-1$ - System.out.println("this time: " + diff + " cumulative time for resource changed: " + fTotalTime); //$NON-NLS-1$ //$NON-NLS-2$ - System.out.println("============================================================================"); //$NON-NLS-1$ - } - return Status.OK_STATUS; - } - - private boolean isCanceled(IProgressMonitor runMonitor) { - - boolean canceled = false; - // check specific monitor passed into run method (the progress - // group in this case) - // check main search support canceled - if (runMonitor != null && runMonitor.isCanceled()) - canceled = true; - else if (JSPSearchSupport.getInstance().isCanceled()) { - canceled = true; - if (runMonitor != null) { - runMonitor.setCanceled(true); - } - } - return canceled; - } - - } - - // end class ProcessFilesJob - - private static JSPIndexManager fSingleton = null; - private boolean initialized; - private boolean initializing = true; - - private IndexJobCoordinator indexJobCoordinator; - private IResourceChangeListener jspResourceChangeListener; - - private JSPResourceVisitor fVisitor = null; - private IContentType fContentTypeJSP = null; - - static long fTotalTime = 0; - - // Job for processing resource delta - private ProcessFilesJob processFilesJob = null; - private JSPIndexManager() { - processFilesJob = new ProcessFilesJob(JSPCoreMessages.JSPIndexManager_0); - // only show in verbose mode - processFilesJob.setSystem(true); - processFilesJob.setPriority(Job.LONG); - processFilesJob.addJobChangeListener(new JobChangeAdapter() { - public void done(IJobChangeEvent event) { - super.done(event); - setStableState(); - } - }); - } - - public synchronized static JSPIndexManager getInstance() { - - if (fSingleton == null) - fSingleton = new JSPIndexManager(); - return fSingleton; - } - - public void initialize() { - - JSPIndexManager singleInstance = getInstance(); - - - if (!singleInstance.initialized) { - singleInstance.initialized = true; - singleInstance.initializing = true; - - singleInstance.indexJobCoordinator = new IndexJobCoordinator(); - singleInstance.jspResourceChangeListener = new JSPResourceChangeListener(); - - // added as JobChange listener so JSPIndexManager can be smarter - // about when it runs - Platform.getJobManager().addJobChangeListener(singleInstance.indexJobCoordinator); - - // add JSPIndexManager to keep JSP Index up to date - // listening for IResourceChangeEvent.PRE_DELETE and - // IResourceChangeEvent.POST_CHANGE - ResourcesPlugin.getWorkspace().addResourceChangeListener(jspResourceChangeListener, IResourceChangeEvent.POST_CHANGE); - - // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5091 - // makes sure IndexManager is aware of our indexes - saveIndexes(); - singleInstance.initializing = false; - } - + super(JSPCoreMessages.JSPIndexManager); } - synchronized void setIndexState(int state) { - if (DEBUG) { - System.out.println("JSPIndexManager setting index state to: " + state2String(state)); //$NON-NLS-1$ - } - Plugin jspModelPlugin = JSPCorePlugin.getDefault(); - jspModelPlugin.getPluginPreferences().setValue(PKEY_INDEX_STATE, state); - jspModelPlugin.savePluginPreferences(); - - } - - private String state2String(int state) { - String s = "UNKNOWN"; //$NON-NLS-1$ - switch (state) { - case (S_STABLE) : - s = "S_STABLE"; //$NON-NLS-1$ - break; - case (S_UPDATING) : - s = "S_UPDATING"; //$NON-NLS-1$ - break; - case (S_CANCELED) : - s = "S_CANCELED"; //$NON-NLS-1$ - break; - case (S_REBUILDING) : - s = "S_REBUILDING"; //$NON-NLS-1$ - break; - } - return s; - } - - int getIndexState() { - return JSPCorePlugin.getDefault().getPluginPreferences().getInt(PKEY_INDEX_STATE); - } - - void setUpdatingState() { - //if (getIndexState() != S_CANCELED) - setIndexState(S_UPDATING); - } - - void setCanceledState() { - setIndexState(JSPIndexManager.S_CANCELED); - } - - void setStableState() { - //if (getIndexState() != S_CANCELED) - setIndexState(S_STABLE); - } - - void setRebuildingState() { - setIndexState(S_REBUILDING); - } - - synchronized void rebuildIndexIfNeeded() { - if (getIndexState() != S_STABLE) { - rebuildIndex(); - } - } - - void rebuildIndex() { - - if (DEBUG) - System.out.println("*** JSP Index unstable, requesting re-indexing"); //$NON-NLS-1$ - - getIndexingJob().addJobChangeListener(new JobChangeAdapter() { - public void aboutToRun(IJobChangeEvent event) { - super.aboutToRun(event); - setRebuildingState(); - } - - public void done(IJobChangeEvent event) { - super.done(event); - setStableState(); - getIndexingJob().removeJobChangeListener(this); - } - }); - // we're about to reindex everything anyway - getProcessFilesJob().clearFiles(); - getIndexingJob().schedule(); - - } - /** - * Creates and schedules a Job to process collected files. All JSP - * indexing should be done through this method or processFiles(IFile file) - * - * @param files + * @return the singleton instance of the {@link JSPIndexManager} */ - final void indexFiles(IFile[] files) { - // don't use this rule - // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4931 - // processFiles.setRule(new IndexFileRule()); - processFilesJob.process(files); + public static JSPIndexManager getDefault() { + return INSTANCE != null ? INSTANCE : (INSTANCE = new JSPIndexManager()); } - /** - * Package protected for access by inner Job class in resourceChanged(...) - * - * @return + * @see indexer.internal.indexing.AbstractIndexManager#isResourceToIndex(int, java.lang.String) */ - JSPResourceVisitor getVisitor() { - - if (this.fVisitor == null) { - this.fVisitor = new JSPResourceVisitor(); - } - return this.fVisitor; - } - - // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5091 - // makes sure IndexManager is aware of our indexes - void saveIndexes() { - IndexManager indexManager = JavaModelManager.getIndexManager(); - IPath jspModelWorkingLocation = JSPSearchSupport.getInstance().getModelJspPluginWorkingLocation(); - - File folder = new File(jspModelWorkingLocation.toOSString()); - String[] files = folder.list(); - String locay = ""; //$NON-NLS-1$ - Index index = null; - try { - for (int i = 0; i < files.length; i++) { - if (files[i].toLowerCase().endsWith(".index")) { //$NON-NLS-1$ - locay = jspModelWorkingLocation.toString() + "/" + files[i]; //$NON-NLS-1$ - // reuse index file - index = new Index(locay, "Index for " + locay, true); //$NON-NLS-1$ - indexManager.saveIndex(index); - } - } - } - catch (Exception e) { - // we should be shutting down, want to shut down quietly - if (DEBUG) - e.printStackTrace(); - } - } - - IContentType getJspContentType() { - if (this.fContentTypeJSP == null) - this.fContentTypeJSP = Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP); - return this.fContentTypeJSP; + protected boolean isResourceToIndex(int type, String name) { + return type == IResource.FILE && JSP_CONTENT_TYPE.isAssociatedWith(name); } /** - * A check to see if the OSGI framework is shutting down. - * - * @return true if the System Bundle is stopped (ie. the framework is - * shutting down) + * @see indexer.internal.indexing.AbstractIndexManager#getWorkingLocation() */ - boolean frameworkIsShuttingDown() { - // in the Framework class there's a note: - // set the state of the System Bundle to STOPPING. - // this must be done first according to section 4.19.2 from the OSGi - // R3 spec. - boolean shuttingDown = Platform.getBundle(OSGI_FRAMEWORK_ID).getState() == Bundle.STOPPING; - if (DEBUG && shuttingDown) { - System.out.println("JSPIndexManager: system is shutting down!"); //$NON-NLS-1$ - } - return shuttingDown; - } - + protected IPath getWorkingLocation() { + if(this.fWorkingLocation == null) { + //create path to working area + IPath workignLocation = + JSPCorePlugin.getDefault().getStateLocation().append("jspsearch"); //$NON-NLS-1$ - public void shutdown() { - - // stop listening - ResourcesPlugin.getWorkspace().removeResourceChangeListener(jspResourceChangeListener); - - - // stop any searching - JSPSearchSupport.getInstance().setCanceled(true); - - // stop listening to jobs - Platform.getJobManager().removeJobChangeListener(indexJobCoordinator); - - - int maxwait = 5000; - if (processFilesJob != null) { - processFilesJob.cancel(); - } - // attempt to make sure this indexing job is litterally - // done before continuing, since we are shutting down - waitTillNotRunning(maxwait, processFilesJob); - - if (indexingJob != null) { - indexingJob.cancel(); - } - waitTillNotRunning(maxwait, processFilesJob); - } - - private void waitTillNotRunning(int maxSeconds, Job job) { - int pauseTime = 10; - int maxtries = maxSeconds / pauseTime; - int count = 0; - while (count++ < maxtries && job.getState() == Job.RUNNING) { - try { - Thread.sleep(pauseTime); - // System.out.println("count: " + count + " max: " + - // maxtries); - } - catch (InterruptedException e) { - Logger.logException(e); - } - } + // ensure that it exists on disk + File folder = new File(workignLocation.toOSString()); + if (!folder.isDirectory()) { + try { + folder.mkdir(); + } + catch (SecurityException e) { + Logger.logException(this.fName + + ": Error while creating state location: " + folder + + " This renders the index manager irrevocably broken for this workspace session", + e); + } + } + + this.fWorkingLocation = workignLocation; + } + + return this.fWorkingLocation; } - private class IndexJobCoordinator extends JobChangeAdapter { + /** + * @see indexer.internal.indexing.AbstractIndexManager#performAction(byte, byte, org.eclipse.core.resources.IResource, org.eclipse.core.runtime.IPath) + */ + protected void performAction(byte source, byte action, IResource resource, + IPath movePath) { - public void aboutToRun(IJobChangeEvent event) { - Job jobToCoordinate = event.getJob(); - if (isJobToAvoid(jobToCoordinate)) { - // job will be rescheduled when the job we - // are avoiding (eg. build) is done - getProcessFilesJob().cancel(); - //System.out.println("cancel:" + jobToCoordinate.getName()); - } - } - - public void done(IJobChangeEvent event) { - - Job jobToCoordinate = event.getJob(); - if (isJobToAvoid(jobToCoordinate)) { - if (getProcessFilesJob().getFiles().length > 0) { - getProcessFilesJob().schedule(500); - //System.out.println("schedule:" + jobToCoordinate.getName()); + //inform the persister of the action unless it come from a full workspace scan + if(JSPTranslatorPersister.ACTIVATED && source != AbstractIndexManager.SOURCE_WORKSPACE_SCAN) { + switch(action) { + case AbstractIndexManager.ACTION_ADD: { + JSPTranslatorPersister.persistTranslation(resource); + break; + } + case AbstractIndexManager.ACTION_REMOVE: { + JSPTranslatorPersister.removePersistedTranslation(resource); + break; + } + case AbstractIndexManager.ACTION_ADD_MOVE_FROM: { + JSPTranslatorPersister.movePersistedTranslation(resource, movePath); + break; + } + case AbstractIndexManager.ACTION_REMOVE_MOVE_TO: { + //do nothing, taken care of by AbstractIndexManager.ACTION_ADD_MOVE_FROM + break; } - - - } - } - - private boolean isJobToAvoid(Job jobToCoordinate) { - boolean result = false; - if (jobToCoordinate.belongsTo(ResourcesPlugin.FAMILY_AUTO_BUILD) || jobToCoordinate.belongsTo(ResourcesPlugin.FAMILY_MANUAL_BUILD) || jobToCoordinate.belongsTo(ResourcesPlugin.FAMILY_AUTO_REFRESH)) { - result = true; } - return result; - } - - } - - private class JSPResourceChangeListener implements IResourceChangeListener { - - - /** - * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) - */ - public void resourceChanged(IResourceChangeEvent event) { - - if (isInitializing()) - return; - - // ignore resource changes if already rebuilding - if (getIndexState() == S_REBUILDING) - return; - // previously canceled, needs entire index rebuild - if (getIndexState() == S_CANCELED) { - // rebuildIndex(); - // just resume indexing - getProcessFilesJob().schedule(500); - //System.out.println("schedule: resource changed, previously canceled"); - return; - } - - IResourceDelta delta = event.getDelta(); - if (delta != null) { - // only care about adds or changes right now... - int kind = delta.getKind(); - boolean added = (kind & IResourceDelta.ADDED) == IResourceDelta.ADDED; - boolean changed = (kind & IResourceDelta.CHANGED) == IResourceDelta.CHANGED; - if (added || changed) { - - // only analyze the full (starting at root) delta - // hierarchy - if (delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$ - try { - JSPResourceVisitor v = getVisitor(); - // clear from last run - v.reset(); - // count files, possibly do this in a job too... - // don't include PHANTOM resources - delta.accept(v, false); - - // process files from this delta - IFile[] files = v.getFiles(); - if (files.length > 0) { - /* - * Job change listener should set back to - * stable when finished - */ - setUpdatingState(); - // processFiles(files); - indexFiles(files); - } - } - catch (CoreException e) { - // need to set state here somehow, and reindex - // otherwise index will be unreliable - if (DEBUG) - Logger.logException(e); - } - catch (Exception e) { - // need to set state here somehow, and reindex - // otherwise index will be unreliable - if (DEBUG) - Logger.logException(e); - } + + //add any new JSP files to the JDT index using the JSPSearchSupport + if(action == AbstractIndexManager.ACTION_ADD || + action == AbstractIndexManager.ACTION_ADD_MOVE_FROM) { + + IFile file = (IFile)resource; //this assumption can be made because of #isResourceToIndex + JSPSearchSupport ss = JSPSearchSupport.getInstance(); + try { + IProject project = file.getProject(); + if (project != null) { + IJavaProject jproject = JavaCore.create(project); + if (jproject.exists()) { + ss.addJspFile(file); } } - + } + catch (Exception e) { + String filename = file != null ? file.getFullPath().toString() : ""; //$NON-NLS-1$ + Logger.logException("JPSIndexManger: problem indexing:" + filename, e); //$NON-NLS-1$ } } - - } - - IndexWorkspaceJob getIndexingJob() { - return indexingJob; - } - - ProcessFilesJob getProcessFilesJob() { - return processFilesJob; } - - boolean isInitializing() { - return initializing; - } - } Index: src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java,v retrieving revision 1.22 diff -u -r1.22 JSPSearchSupport.java --- src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java 26 Feb 2009 05:24:21 -0000 1.22 +++ src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java 1 Sep 2010 12:23:07 -0000 @@ -315,10 +315,67 @@ * @param requestor * passed in to accept search matches (and do "something" with * them) + * + * @deprecated use {@link #search(String, IJavaSearchScope, int, int, int, boolean, SearchRequestor, IProgressMonitor)} */ public void search(String searchText, IJavaSearchScope scope, int searchFor, int limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor) { + this.search(searchText, scope, searchFor, limitTo, matchMode, isCaseSensitive, + requestor, new NullProgressMonitor()); + } - JSPIndexManager.getInstance().rebuildIndexIfNeeded(); + /** + * Search for an IJavaElement, constrained by the given parameters. Runs in + * a background Job (results may still come in after this method call) + * + * @param element + * @param scope + * @param requestor + * + * @deprecated use {@link #search(IJavaElement, IJavaSearchScope, SearchRequestor, IProgressMonitor)} + */ + public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) { + this.search(element, scope, requestor, new NullProgressMonitor()); + } + + /** + * Search for an IJavaElement, constrained by the given parameters. Runs in + * an IWorkspace runnable (results will be reported by the end of this + * method) + * + * @param element + * @param scope + * @param requestor + */ + public void searchRunnable(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) { + this.searchRunnable(element, scope, requestor, new NullProgressMonitor()); + } + + /** + * Perform a java search w/ the given parameters. Runs in a background Job + * (results may still come in after this method call) + * + * @param searchText + * the string of text to search on + * @param searchFor + * IJavaSearchConstants.TYPE, METHOD, FIELD, PACKAGE, etc... + * @param limitTo + * IJavaSearchConstants.DECLARATIONS, + * IJavaSearchConstants.REFERENCES, + * IJavaSearchConstants.IMPLEMENTORS, or + * IJavaSearchConstants.ALL_OCCURRENCES + * @param matchMode + * allow * wildcards or not + * @param isCaseSensitive + * @param requestor + * passed in to accept search matches (and do "something" with + * them) + */ + public void search(String searchText, IJavaSearchScope scope, int searchFor, int + limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor, + IProgressMonitor monitor) { + + //wait for the index + JSPIndexManager.getDefault().waitForConsistant(monitor); SearchJob job = new SearchJob(searchText, scope, searchFor, limitTo, matchMode, isCaseSensitive, requestor); setCanceled(false); @@ -338,10 +395,12 @@ * @param scope * @param requestor */ - public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) { - - JSPIndexManager.getInstance().rebuildIndexIfNeeded(); + public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor, + IProgressMonitor monitor) { + //wait for the index + JSPIndexManager.getDefault().waitForConsistant(monitor); + SearchJob job = new SearchJob(element, scope, requestor); setCanceled(false); job.setUser(true); @@ -359,9 +418,11 @@ * @param scope * @param requestor */ - public void searchRunnable(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) { - - JSPIndexManager.getInstance().rebuildIndexIfNeeded(); + public void searchRunnable(IJavaElement element, IJavaSearchScope scope, + SearchRequestor requestor, IProgressMonitor monitor) { + + //wait for the index + JSPIndexManager.getDefault().waitForConsistant(monitor); SearchRunnable searchRunnable = new SearchRunnable(element, scope, requestor); try { #P org.eclipse.jst.jsp.ui Index: src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java,v retrieving revision 1.4 diff -u -r1.4 JSPQueryParticipant.java --- src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java 20 Oct 2009 17:14:55 -0000 1.4 +++ src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java 1 Sep 2010 12:23:08 -0000 @@ -59,7 +59,7 @@ SearchRequestor jspRequestor = new JSPSearchRequestor(requestor); // pa_TODO need to adapt JavaSearchScope to a JSPSearchScope - JSPSearchSupport.getInstance().search(element, new JSPSearchScope(), jspRequestor); + JSPSearchSupport.getInstance().search(element, new JSPSearchScope(), jspRequestor, monitor); } else if(querySpecification instanceof PatternQuerySpecification) { @@ -79,7 +79,8 @@ patternQuery.getLimitTo(), SearchPattern.R_PATTERN_MATCH, false, - jspRequestor); + jspRequestor, + monitor); } } } #P org.eclipse.wst.sse.core Index: META-INF/MANIFEST.MF =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF,v retrieving revision 1.35.2.2 diff -u -r1.35.2.2 MANIFEST.MF --- META-INF/MANIFEST.MF 16 Aug 2010 17:36:29 -0000 1.35.2.2 +++ META-INF/MANIFEST.MF 1 Sep 2010 12:23:09 -0000 @@ -7,6 +7,7 @@ Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: org.eclipse.wst.sse.core, + org.eclipse.wst.sse.core.indexing, org.eclipse.wst.sse.core.internal;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", org.eclipse.wst.sse.core.internal.cleanup;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", org.eclipse.wst.sse.core.internal.document;x-friends:="org.eclipse.wst.dtd.core,org.eclipse.wst.dtd.ui,org.eclipse.wst.sse.ui,org.eclipse.wst.xml.core,org.eclipse.wst.xml.ui,org.eclipse.wst.xsd.core,org.eclipse.wst.xsd.ui", Index: src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java =================================================================== RCS file: src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java diff -N src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,1665 @@ +/******************************************************************************* + * Copyright (c) 2010 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * + *******************************************************************************/ +package org.eclipse.wst.sse.core.indexing; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +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.IResourceProxy; +import org.eclipse.core.resources.IResourceProxyVisitor; +import org.eclipse.core.resources.ISaveParticipant; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.osgi.util.NLS; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.SSECoreMessages; + +/** + *

Provides a generic for writing a resource index manager. It is important + * to note that this only provides the framework for managing an index, not actually + * indexing. The subtle difference is that the manager is in charge of paying attention + * to all of the resource actions that take place in the workspace and filtering those + * actions down to simple actions that need to be performed on whatever index this manger + * is managing.

+ * + *

The manger does its very best to make sure the index is always consistent, even if + * resource events take place when the manager is not running. In the event that the + * manager determines it has missed, loosed, or corrupted any resource change events + * that have occurred before, during, or after its activation or de-activation then the + * manager will inspect the entire workspace to insure the index it is managing is + * Consistent.

+ * + */ +public abstract class AbstractIndexManager { + + /** Default time to wait for other tasks to finish */ + private static final int WAIT_TIME = 300; + + /** + *

Used to report progress on jobs where the total work to complete is unknown. + * The created effect is a progress bar that moves but that will never complete.

+ */ + private static final int UNKNOWN_WORK = 100; + + /** The amount of events to batch up before sending them off to the processing job*/ + private static final int BATCH_UP_AMONT = 100; + + /** If this file exists then a full workspace re-processing is needed */ + private static final String RE_PROCESS_FILE_NAME = ".re-process"; //$NON-NLS-1$ + + /** Common error message to log */ + private static final String LOG_ERROR_INDEX_INVALID = + "Index may become invalid, incomplete, or enter some other inconsistent state."; //$NON-NLS-1$ + + /** State: manager is stopped */ + private static final byte STATE_DISABLED = 0; + + /** State: manager is running */ + private static final byte STATE_ENABLED = 1; + + /** Action: add to index */ + protected static final byte ACTION_ADD = 0; + + /** Action: remove from index */ + protected static final byte ACTION_REMOVE = 1; + + /** Action: add to index caused by move operation */ + protected static final byte ACTION_ADD_MOVE_FROM = 2; + + /** Action: remove from index caused by move operation */ + protected static final byte ACTION_REMOVE_MOVE_TO = 3; + + /** Source: action originated from resource change event */ + protected static final byte SOURCE_RESROUCE_CHANGE = 0; + + /** Source: action originated from workspace scan */ + protected static final byte SOURCE_WORKSPACE_SCAN = 1; + + /** Source: action originated from saved state */ + protected static final byte SOURCE_SAVED_STATE = 2; + + /** Source: preserved resources to index */ + protected static final byte SOURCE_PRESERVED_RESOURCES_TO_INDEX = 3; + + /** the name of this index manager */ + protected String fName; + + /** {@link IResourceChangeListener} to listen for file changes */ + private ResourceChangeListener fResourceChangeListener; + + /** The {@link Job} that does all of the indexing */ + private ResourceEventProcessingJob fResourceEventProcessingJob; + + /** A {@link Job} to search the workspace for all files */ + private Job fWorkspaceVisitorJob; + + /** + *

Current state of the manager

+ * + * @see #STATE_DISABLED + * @see #STATE_ENABLED + */ + private volatile byte fState; + + /** used to prevent manager from starting and stopping at the same time */ + private Object fStartStopLock = new Object(); + + /** true if the manger is currently starting, false otherwise */ + private boolean fStarting; + + /** + *

Creates the manager with a given name.

+ * + * @param name This will be pre-pended to progress reporting messages and thus should + * be translated + */ + protected AbstractIndexManager(String name) { + this.fName = name; + this.fState = STATE_DISABLED; + this.fResourceChangeListener = new ResourceChangeListener(); + this.fResourceEventProcessingJob = new ResourceEventProcessingJob(); + this.fStarting = false; + } + + /** + *

Starts up the {@link AbstractIndexManager}. If a {@link IResourceDelta} + * is provided then it is assumed that all other files in the workspace + * have already been index and thus only those in the provided + * {@link IResourceDelta} will be processed. Else if the provided + * {@link IResourceDelta} is null it is assumed no files + * have been indexed yet so the entire workspace will be searched for + * files to be indexed.

+ * + *

If {@link IResourceDelta} is provided this will block until that delta + * has finished processing. If no {@link IResourceDelta} provided then a + * separate job will be created to process the entire workspace and this method + * will return without waiting for that job to complete

+ * + *

Will block until {@link #stop()} has finished running if it is + * currently running

+ * + * @param savedStateDelta the delta from a saved state, if null + * then the entire workspace will be searched for files to index, else + * only files in this {@link IResourceDelta} will be indexed + * @param monitor This action can not be canceled but this monitor will be used + * to report progress + */ + public final void start(IResourceDelta savedStateDelta, IProgressMonitor monitor) { + SubMonitor progress = SubMonitor.convert(monitor); + synchronized (this.fStartStopLock) { + this.fStarting = true; + + if(this.fState == STATE_DISABLED) { + //report status + progress.beginTask(this.fName + ": " + SSECoreMessages.IndexManager_starting, //$NON-NLS-1$ + 2); + + //start listening for resource change events + this.fResourceChangeListener.start(); + + //check to see if a full re-index is required + boolean forcedFullReIndexNeeded = this.isForcedFullReIndexNeeded(); + + /* start the indexing job only loading preserved state if not doing full index + * if failed loading preserved state then force full re-index + */ + forcedFullReIndexNeeded = !this.fResourceEventProcessingJob.start(!forcedFullReIndexNeeded, + progress.newChild(1)); + progress.setWorkRemaining(1); + + //don't bother processing saved delta if forced full re-index is needed + boolean stillNeedFullReIndex = forcedFullReIndexNeeded; + if(!forcedFullReIndexNeeded) { + //if there is a delta attempt to process it + if(savedStateDelta != null) { + stillNeedFullReIndex = false; + try { + //deal with reporting progress + SubMonitor savedStateProgress = progress.newChild(1, SubMonitor.SUPPRESS_NONE); + savedStateProgress.setTaskName( + this.fName + ": " + SSECoreMessages.IndexManager_starting + ": " + //$NON-NLS-1$ //$NON-NLS-2$ + SSECoreMessages.IndexManager_processing_deferred_resource_changes); + + //process delta + ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(savedStateProgress, + AbstractIndexManager.SOURCE_SAVED_STATE); + savedStateDelta.accept(visitor); + + //process any remaining batched up resources to index + visitor.processBatchedResourceEvents(); + } catch (CoreException e) { + stillNeedFullReIndex = true; + Logger.logException(this.fName + ": Could not process saved state. " + //$NON-NLS-1$ + "Forced to do a full workspace re-index.", e); //$NON-NLS-1$ + } + } + } + progress.worked(1); + + //if need to process the entire workspace do so in another job + if(stillNeedFullReIndex){ + this.fWorkspaceVisitorJob = new WorkspaceVisitorJob(); + this.fWorkspaceVisitorJob.schedule(); + } + + //update state + this.fState = STATE_ENABLED; + } + this.fStarting = false; + } + } + + /** + *

Safely shuts down the manager.

+ * + *

This will block until the {@link #start(IResourceDelta, IProgressMonitor)} has + * finished (if running). Also until the current resource event has finished being + * processed. Finally it will block until the events still to be processed by + * the processing job have been preserved to be processed on the next call to + * {@link #start(IResourceDelta, IProgressMonitor)}.

+ * + *

If at any point during this shut down processes something goes wrong the + * manager will be sure that on the next call to {@link #start(IResourceDelta, IProgressMonitor)} + * the entire workspace will be re-processed.

+ * + * @throws InterruptedException + */ + public final void stop() throws InterruptedException { + synchronized (this.fStartStopLock) { + if(this.fState != STATE_DISABLED) { + + //stop listening for events, and wait for the current event to finish + this.fResourceChangeListener.stop(); + + // if currently visiting entire workspace, give up and try again next load + boolean forceFullReIndexNextStart = false; + if(this.fWorkspaceVisitorJob != null) { + if (this.fWorkspaceVisitorJob.getState() != Job.NONE) { + this.fWorkspaceVisitorJob.cancel(); + + this.forceFullReIndexNextStart(); + forceFullReIndexNextStart = true; + } + } + + //stop the indexing job, only preserve if not already forcing a re-index + forceFullReIndexNextStart = !this.fResourceEventProcessingJob.stop(!forceFullReIndexNextStart); + + //if preserving failed, then force re-index + if(forceFullReIndexNextStart) { + this.forceFullReIndexNextStart(); + } + + //update status + this.fState = STATE_DISABLED; + } + } + } + + /** + *

Should be called by an client of the index this manger manages before the index + * is accessed, assuming the client wants an index consistent with the latest + * resource changes.

+ * + *

The supplied monitor will be used to supply user readable progress as the manager + * insures the index has been given all the latest resource events. This monitor + * maybe canceled, but if it is the state of the index is not guaranteed to be + * consistent with the latest resource change events.

+ * + * @param monitor Used to report user readable progress as the manager insures the + * index is consistent with the latest resource events. This monitor can be canceled + * to stop waiting for consistency but then no guaranty is made about the consistency + * of the index in relation to un-processed resource changes + * + * @return true if the wait finished successfully and the manger is consistent, + * false otherwise, either an error occurred while waiting for the manager + * or the monitor was canceled + * + * @throws InterruptedException This can happen when waiting for other jobs + */ + public final boolean waitForConsistant(IProgressMonitor monitor) { + boolean success = true; + boolean interupted = false; + SubMonitor progress = SubMonitor.convert(monitor); + + //set up the progress of waiting + int remainingWork = 4; + progress.beginTask(NLS.bind(SSECoreMessages.IndexManager_Waiting_for_0, this.fName), + remainingWork); + + //wait for start up + if(this.fStarting && !monitor.isCanceled()) { + SubMonitor startingProgress = progress.newChild(1); + startingProgress.subTask(SSECoreMessages.IndexManager_starting); + while(this.fStarting && !monitor.isCanceled()) { + //this creates a never ending progress that still moves forward + startingProgress.setWorkRemaining(UNKNOWN_WORK); + startingProgress.newChild(1).worked(1); + try { + Thread.sleep(WAIT_TIME); + } catch (InterruptedException e) { + interupted = true; + } + } + } + progress.setWorkRemaining(--remainingWork); + + //wait for workspace visiting job + if(this.fWorkspaceVisitorJob != null && this.fWorkspaceVisitorJob.getState() != Job.NONE && !monitor.isCanceled()) { + SubMonitor workspaceVisitorProgress = progress.newChild(1); + workspaceVisitorProgress.subTask(SSECoreMessages.IndexManager_Processing_entire_workspace_for_the_first_time); + while(this.fWorkspaceVisitorJob.getState() != Job.NONE && !monitor.isCanceled()) { + //this creates a never ending progress that still moves forward + workspaceVisitorProgress.setWorkRemaining(UNKNOWN_WORK); + workspaceVisitorProgress.newChild(1).worked(1); + try { + Thread.sleep(WAIT_TIME); + } catch (InterruptedException e) { + interupted = true; + } + } + } + progress.setWorkRemaining(--remainingWork); + + //wait for the current resource event + if(this.fResourceChangeListener.isProcessingEvents() && !monitor.isCanceled()) { + SubMonitor workspaceVisitorProgress = progress.newChild(1); + workspaceVisitorProgress.subTask(SSECoreMessages.IndexManager_processing_recent_resource_changes); + while(this.fResourceChangeListener.isProcessingEvents() && !monitor.isCanceled()) { + workspaceVisitorProgress.setWorkRemaining(UNKNOWN_WORK); + workspaceVisitorProgress.newChild(1).worked(1); + try { + this.fResourceChangeListener.waitForCurrentEvent(WAIT_TIME); + } catch (InterruptedException e) { + interupted = true; + } + } + } + progress.setWorkRemaining(--remainingWork); + + //wait for all files to be indexed + if(this.fResourceEventProcessingJob.getNumResourceEventsToProcess() != 0 && !monitor.isCanceled()) { + SubMonitor indexingProgress = progress.newChild(1); + int prevNumResrouces; + int numResources = this.fResourceEventProcessingJob.getNumResourceEventsToProcess(); + while(numResources != 0 && !monitor.isCanceled()) { + //update the progress indicator + indexingProgress.subTask(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$ + NLS.bind(SSECoreMessages.IndexManager_Indexing_0_Files, + "" + numResources)); //$NON-NLS-1$ + indexingProgress.setWorkRemaining(numResources); + prevNumResrouces = numResources; + numResources = this.fResourceEventProcessingJob.getNumResourceEventsToProcess(); + int numProcessed = prevNumResrouces - numResources; + indexingProgress.worked(numProcessed > 0 ? numProcessed : 0); + + //give the index some time to do some indexing + try { + this.fResourceEventProcessingJob.waitForConsistant(WAIT_TIME); + } catch (InterruptedException e) { + interupted = true; + } + } + } + progress.setWorkRemaining(--remainingWork); + + if(monitor.isCanceled()) { + success = false; + } + + //reset the interrupted flag if we were interrupted + if(interupted) { + Thread.currentThread().interrupt(); + } + + return success; + } + + /** + *

Used to determine if an {@link IResource} with the given + * type and name should be processed by this index manager

+ * + * @param type see {@link IResource#getType()} + * @param name the name of the resource + * + * @return true if this index manager processes resources + * of the given type with the given name, + * false otherwise + */ + protected abstract boolean isResourceToIndex(int type, String name); + + /** + *

Called for each {@link ResourceEvent} gathered by the various sources and processed + * by the {@link ResourceEventProcessingJob}. The implementation of this method + * should use the given information to update the index this manger is managing.

+ * + * @param source The source that reported this resource event + * @param action The action to be taken on the given resource + * @param resource The index should perform the given action on this + * resource + * @param movePath If the given action is {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} + * or {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this field will not be + * null and reports the path the given resource was either moved from or + * moved to respectively. + * + * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN + * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX + * + * @see AbstractIndexManager#ACTION_ADD + * @see AbstractIndexManager#ACTION_REMOVE + * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM + * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO + */ + protected abstract void performAction(byte source, byte action, IResource resource, + IPath movePath); + + /** + *

Gets the working location of the manager. This is where any relevant + * state can be persisted.

+ * + * @return the working location of the manager + */ + protected abstract IPath getWorkingLocation(); + + /** + *

Determines if a resource should be visited or not while + * looking for files to index. If a resource should not be + * visited it is assumed its children should not be visited either.

+ * + *

Implementers may override. Default is to ignore resources + * starting with a period.

+ * + * @param resourceName name of the resource to determine if it + * should be visited or not + * + * @return true if the resource should be visited, + * false otherwise + */ + protected boolean shouldVisit(String resourceName) { + return !resourceName.startsWith(".");//$NON-NLS-1$ + } + + /** + *

Next time the manager starts up force a full workspace index

+ */ + private void forceFullReIndexNextStart() { + IPath reIndexPath = + AbstractIndexManager.this.getWorkingLocation().append(RE_PROCESS_FILE_NAME); + File file = new File(reIndexPath.toOSString()); + try { + file.createNewFile(); + } catch (IOException e) { + Logger.logException(this.fName + ": Could not create file to tell manager to" + //$NON-NLS-1$ + " do a full re-index on next load. " + //$NON-NLS-1$ + AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e); + } + } + + /** + * @return true if a full workspace index is needed as dictated by + * a previous call to {@link #forceFullReIndexNextStart()}, false + * otherwise + */ + private boolean isForcedFullReIndexNeeded() { + boolean forcedFullReIndexNeeded = false; + IPath reIndexPath = + AbstractIndexManager.this.getWorkingLocation().append(RE_PROCESS_FILE_NAME); + File file = new File(reIndexPath.toOSString()); + if(file.exists()) { + file.delete(); + forcedFullReIndexNeeded = true; + } + + return forcedFullReIndexNeeded; + } + + /** + *

A system {@link Job} used to visit all of the files in the workspace + * looking for files to index.

+ * + *

This should only have to be done once per workspace on the first load, + * but if it fails or a SavedState can not be retrieved on a subsequent + * workspace load then this will have to be done again.

+ */ + private class WorkspaceVisitorJob extends Job { + /** + *

Default constructor that sets up this job as a system job

+ */ + protected WorkspaceVisitorJob() { + super(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$ + SSECoreMessages.IndexManager_Processing_entire_workspace_for_the_first_time); + + this.setUser(false); + this.setSystem(true); + this.setPriority(Job.LONG); + } + + /** + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + try { + //update status + monitor.beginTask(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$ + SSECoreMessages.IndexManager_Processing_entire_workspace_for_the_first_time, + IProgressMonitor.UNKNOWN); + + //visit the workspace + WorkspaceVisitor visitor = new WorkspaceVisitor(monitor); + ResourcesPlugin.getWorkspace().getRoot().accept(visitor, IResource.NONE); + + //process any remaining batched up resources to index + visitor.processBatchedResourceEvents(); + } catch(CoreException e) { + Logger.logException(AbstractIndexManager.this.fName + + ": Failed visiting entire workspace for initial index. " + //$NON-NLS-1$ + AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e); + } + + IStatus status; + if(monitor.isCanceled()) { + status = Status.CANCEL_STATUS; + } else { + status = Status.OK_STATUS; + } + + return status; + } + + /** + *

An {@link IResourceProxyVisitor} used to visit all of the files in the + * workspace looking for files to add to the index.

+ * + *

NOTE: After this visitor is used {@link WorkspaceVisitor#processBatchedResourceEvents() + * must be called to flush out the last of the {@link ResourceEvent}s produced + * by this visitor.

+ */ + private class WorkspaceVisitor implements IResourceProxyVisitor { + /** {@link IProgressMonitor} used to report status and check for cancellation */ + private SubMonitor fProgress; + + /** + * {@link Map}<{@link IResource}, {@link ResourceEvent}> + *

Map of resources events created and batched up by this visitor. + * These events are periodical be sent off to the + * {@link ResourceEventProcessingJob} but need to be sent off + * one final time after this visitor finishes it work.

+ * + * @see #processBatchedResourceEvents() + */ + private Map fBatchedResourceEvents; + + /** + *

Default constructor

+ * @param monitor used to report status and allow this visitor to be canceled + */ + protected WorkspaceVisitor(IProgressMonitor monitor) { + this.fProgress = SubMonitor.convert(monitor); + this.fBatchedResourceEvents = new LinkedHashMap(BATCH_UP_AMONT); + } + + /** + *

As long as the monitor is not canceled visit each file in the workspace + * that should be visited.

+ * + * @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.core.resources.IResourceProxy) + * @see AbstractIndexManager#shouldVisit(String) + */ + public boolean visit(IResourceProxy proxy) throws CoreException { + this.fProgress.subTask(proxy.getName()); + + boolean visitChildren = false; + + /* if not canceled or a hidden resource then process file + * else don't visit children + */ + if(!this.fProgress.isCanceled() && shouldVisit(proxy.getName())) { + if(isResourceToIndex(proxy.getType(), proxy.getName())) { + + //add the file to be indexed + IFile file = (IFile) proxy.requestResource(); + if(file.exists()) { + this.fBatchedResourceEvents.put(file, new ResourceEvent( + AbstractIndexManager.SOURCE_WORKSPACE_SCAN, + AbstractIndexManager.ACTION_ADD, + null)); + } + } + + visitChildren = true; + } else { + visitChildren = false; + } + + //batch up resource changes before sending them out + if(this.fBatchedResourceEvents.size() >= BATCH_UP_AMONT) { + this.processBatchedResourceEvents(); + } + + return visitChildren; + } + + /** + *

Sends any batched up resource events created by this visitor to the + * {@link ResourceEventProcessingJob}.

+ * + *

NOTE: This will be called every so often as the visitor is + * visiting resources but needs to be called a final time by the user of + * this visitor to be sure the final events are sent off

+ */ + protected void processBatchedResourceEvents() { + AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents( + this.fBatchedResourceEvents); + this.fBatchedResourceEvents.clear(); + } + } + } + + /** + *

Used to listen to resource change events in the workspace. These events + * are batched up and then passed onto the {@link ResourceEventProcessingJob}.

+ */ + private class ResourceChangeListener implements IResourceChangeListener { + /** + *

The number of events currently being processed by this listener.

+ *

Use the {@link #fEventsBeingProcessedLock} when reading or writing this field

+ * + * @see #fEventsBeingProcessedLock + */ + private volatile int fEventsBeingProcessed; + + /** + * Lock to use when reading or writing {@link #fEventsBeingProcessed} + * + * @see #fEventsBeingProcessed + */ + private final Object fEventsBeingProcessedLock = new Object(); + + /** + *

Current state of this listener

+ * + * @see AbstractIndexManager#STATE_DISABLED + * @see AbstractIndexManager#STATE_ENABLED + */ + private volatile byte fState; + + /** + *

Default constructor

+ */ + protected ResourceChangeListener() { + this.fState = STATE_DISABLED; + this.fEventsBeingProcessed = 0; + } + + /** + *

Start listening for resource change events

+ */ + protected void start() { + this.fState = STATE_ENABLED; + ResourcesPlugin.getWorkspace().addResourceChangeListener(this); + } + + /** + *

Stop listening for resource change events and if already processing + * an event then wait for that processing to finish

+ * + * @throws InterruptedException waiting for a current event to finish processing + * could be interrupted + */ + protected void stop() throws InterruptedException { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + + //wait indefinitely for current event to finish processing + this.waitForCurrentEvent(0); + + this.fState = STATE_DISABLED; + } + + /** + *

Blocks until either the current resource event has been processed or + * until the given timeout has passed.

+ * + * @param timeout block until either this timeout elapses (0 means never to timeout) + * or the current resource change event finishes being processed + * + * @throws InterruptedException This can happen when waiting for a lock + */ + protected void waitForCurrentEvent(int timeout) throws InterruptedException { + synchronized (this.fEventsBeingProcessedLock) { + if(this.fEventsBeingProcessed != 0) { + this.fEventsBeingProcessedLock.wait(timeout); + } + } + } + + /** + * @return true if this listener is currently processing any + * events, false otherwise. + */ + protected boolean isProcessingEvents() { + return this.fEventsBeingProcessed != 0; + } + + /** + *

Process a resource change event. If it is a pre-close or pre-delete then + * the {@link ResourceEventProcessingJob} is paused so it does not try to + * process resources that are about to be deleted. The {@link ResourceDeltaVisitor} + * is used to actually process the event.

+ * + * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) + * @see ResourceDeltaVisitor + */ + public void resourceChanged(IResourceChangeEvent event) { + try { + //update the number of events being processed + synchronized (this.fEventsBeingProcessedLock) { + ++this.fEventsBeingProcessed; + } + + if(this.fState == STATE_ENABLED) { + switch(event.getType()) { + case IResourceChangeEvent.PRE_CLOSE: + case IResourceChangeEvent.PRE_DELETE:{ + //pre-close or pre-delete pause the persister job so it does not interfere + AbstractIndexManager.this.fResourceEventProcessingJob.pause(); + break; + } + case IResourceChangeEvent.POST_BUILD: + case IResourceChangeEvent.POST_CHANGE: { + //post change start up the indexer job and process the delta + AbstractIndexManager.this.fResourceEventProcessingJob.unPause(); + + // only analyze the full (starting at root) delta hierarchy + IResourceDelta delta = event.getDelta(); + if (delta != null && delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$ + try { + //use visitor to visit all children + ResourceDeltaVisitor visitor = new ResourceDeltaVisitor( + AbstractIndexManager.SOURCE_RESROUCE_CHANGE); + delta.accept(visitor, false); + + //process any remaining batched up resources to index + visitor.processBatchedResourceEvents(); + } catch (CoreException e) { + Logger.logException(AbstractIndexManager.this.fName + + ": Failed visiting resrouce change delta. " + //$NON-NLS-1$ + AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e); + } + } + break; + } + } + } else { + Logger.log(Logger.ERROR, "A resource change event came in after " + + AbstractIndexManager.this.fName + " shut down. This should never " + + "ever happen, but if it does the index may now be inconsistant."); + } + } finally { + //no matter how we exit be sure to update the number of events being processed + synchronized (this.fEventsBeingProcessedLock) { + --this.fEventsBeingProcessed; + + //if currently not events being processed, then notify + if(this.fEventsBeingProcessed == 0) { + this.fEventsBeingProcessedLock.notifyAll(); + } + } + } + } + } + + /** + *

Used to visit {@link IResourceDelta}s from both the {@link IResourceChangeListener} + * and from a {@link ISaveParticipant} given to {@link AbstractIndexManager#start(IResourceDelta, IProgressMonitor)}. + * The resource events are batched into groups of {@link AbstractIndexManager#BATCH_UP_AMONT} + * before being passed onto the {@link ResourceEventProcessingJob}.

+ * + *

NOTE 1: This class is intended for one time use, thus a new instance should + * be instantiated each time this visitor is needed to process a new {@link IResourceDelta}.

+ * + *

NOTE 2: Be sure to call {@link ResourceDeltaVisitor#processBatchedResourceEvents()} + * after using this visitor to be sure any remaining events get passed onto the + * {@link ResourceEventProcessingJob}.

+ * + * @see ResourceDeltaVisitor#processBatchedResourceEvents() + */ + private class ResourceDeltaVisitor implements IResourceDeltaVisitor { + /** {@link IProgressMonitor} used to report status */ + private SubMonitor fProgress; + + /** + *

The source that should be used when sending resource events to the + * {@link ResourceEventProcessingJob}.

+ * + * @see AbstractIndexManager#SOURCE_SAVED_STATE + * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE + */ + private byte fSource; + + /** + *

Due to the nature of a visitor it has no way of knowing the total amount + * of work it has to do but it can start to predict it based on the number of + * children of each event it processes and whether it plans on visiting those + * children

+ */ + private int fPredictedWorkRemaining; + + /** + * {@link Map}<{@link IResource}, {@link ResourceEvent}> + *

Map of resources events created and batched up by this visitor. + * These events are periodical be sent off to the + * {@link ResourceEventProcessingJob} but need to be sent off + * one final time after this visitor finishes it work.

+ * + * @see #processBatchedResourceEvents() + */ + private Map fBatchedResourceEvents; + + /** + *

Creates a visitor that will create resource events based on the resources + * it visits and using the given source as the source of the events.

+ * + * @param source The source of the events that should be used when creating + * resource events from visited resources + * + * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + */ + protected ResourceDeltaVisitor(byte source) { + this(SubMonitor.convert(null), source); + } + + /** + *

Creates a visitor that will create resource events based on the resources + * it visits and using the given source as the source of the events and + * report its status to the given progress as best it can as it visits + * resources.

+ * + *

NOTE: While the {@link SubMonitor} is provided to report status the + * visitor will not honor any cancellation requests.

+ * + * @param progress Used to report status. This visitor can not be + * canceled + * @param source The source of the events that should be used when creating + * resource events from visited resources + * + * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + */ + protected ResourceDeltaVisitor(SubMonitor progress, byte source) { + this.fProgress = progress; + this.fSource = source; + this.fBatchedResourceEvents = new LinkedHashMap(BATCH_UP_AMONT); + this.fPredictedWorkRemaining = 1; + } + + /** + *

Transforms each {@link IResourceDelta} into a {@link ResourceEvent}. + * Batches up these {@link ResourceEvent}s and then passes them onto the + * {@link ResourceEventProcessingJob}.

+ * + *

NOTE 2: Be sure to call {@link ResourceDeltaVisitor#processBatchedResourceEvents()} + * after using this visitor to be sure any remaining events get passed onto the + * {@link ResourceEventProcessingJob}.

+ * + * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) + * @see #processBatchedResourceEvents() + */ + public boolean visit(IResourceDelta delta) throws CoreException { + //report status + this.fProgress.subTask( + NLS.bind(SSECoreMessages.IndexManager_0_resources_to_go, "" + fPredictedWorkRemaining) + //$NON-NLS-1$ + ": " + delta.getFullPath().toString()); //$NON-NLS-1$ + + //process delta if resource not hidden + boolean visitChildren = false; + IResource resource = delta.getResource(); + if(shouldVisit(resource.getName())) { + //check if should index resource + if(isResourceToIndex(resource.getType(), resource.getName())) { + + switch (delta.getKind()) { + case IResourceDelta.CHANGED : { + /* ignore any change that is not a CONTENT, REPLACED, TYPE, + * or MOVE_FROM change + */ + if( !((delta.getFlags() & IResourceDelta.CONTENT) != 0) && + !((delta.getFlags() & IResourceDelta.REPLACED) != 0) && + !((delta.getFlags() & IResourceDelta.TYPE) != 0) && + !(((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0))) { + + break; + } + } + //$FALL-THROUGH$ it is intended that sometimes a change will fall through to add + case IResourceDelta.ADDED : { + if((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) { + //create add move from action + this.fBatchedResourceEvents.put(resource, new ResourceEvent( + this.fSource, + AbstractIndexManager.ACTION_ADD_MOVE_FROM, + delta.getMovedFromPath())); + + } else { + //create add action + this.fBatchedResourceEvents.put(resource, new ResourceEvent( + this.fSource, + AbstractIndexManager.ACTION_ADD, + null)); + } + + break; + } + case IResourceDelta.REMOVED : { + if((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) { + //create remove move to action + this.fBatchedResourceEvents.put(resource, new ResourceEvent( + this.fSource, + AbstractIndexManager.ACTION_REMOVE_MOVE_TO, + delta.getMovedToPath())); + } else { + //create remove action + this.fBatchedResourceEvents.put(resource, new ResourceEvent( + this.fSource, + AbstractIndexManager.ACTION_REMOVE, + null)); + } + break; + } + } + } + + visitChildren = true; + } else { + visitChildren = false; + } + + //deal with trying to report progress + if(visitChildren) { + this.fPredictedWorkRemaining += delta.getAffectedChildren().length; + } + this.fProgress.setWorkRemaining(this.fPredictedWorkRemaining); + this.fProgress.worked(1); + --this.fPredictedWorkRemaining; + + //batch up resource changes before sending them out + if(this.fBatchedResourceEvents.size() >= BATCH_UP_AMONT) { + this.processBatchedResourceEvents(); + } + + return visitChildren; + } + + /** + *

Sends any batched up resource events created by this visitor to the + * {@link ResourceEventProcessingJob}.

+ * + *

NOTE: This will be called every so often as the visitor is + * visiting resources but needs to be called a final time by the user of + * this visitor to be sure the final events are sent off

+ */ + protected void processBatchedResourceEvents() { + AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents( + this.fBatchedResourceEvents); + this.fBatchedResourceEvents.clear(); + } + } + + /** + *

Collects {@link ResourceEvent}s from the different sources and then processes + * each one by calling {@link AbstractIndexManager#performAction(byte, byte, IResource, IPath)} + * for each {@link ResourceEvent}.

+ * + * @see AbstractIndexManager#performAction(byte, byte, IResource, IPath) + */ + private class ResourceEventProcessingJob extends Job { + /** Length to delay when scheduling job */ + private static final int DELAY = 500; + + /** + *

Name of the file where resource events still to index + * will be preserved for the next start up.

+ */ + private static final String PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME = ".preservedResourceEvents"; //$NON-NLS-1$ + + /** + *

This needs to be updated if {@link #preserveRecievedResourceEvents()} ever + * changes how it persists resource events so that {@link #loadPreservedRecievedResourceEvents(SubMonitor)} + * knows when opening a file that the format is of the current version and if not + * knows it does not know how to read the older version.

+ * + * @see #preserveRecievedResourceEvents() + * @see #loadPreservedRecievedResourceEvents(SubMonitor) + */ + private static final long serialVersionUID = 1L; + + /** Whether this job has been paused or not */ + private volatile boolean fIsPaused; + + /** + * {@link Map}<{@link IResource}, {@link ResourceEvent}> + *

The list of resources events to be processed

+ */ + private Map fResourceEvents; + + /** Lock used when accessing {@link #fBatchedResourceEvents} */ + private final Object fResourceEventsLock = new Object(); + + /** + * Locked used for allowing other jobs to wait on this job. This job + * will notify those waiting on this lock whenever it is done processing + * all resource events it currently knows about. + * + * @see #waitForConsistant(int) + */ + private final Object fToNotifyLock = new Object(); + + /** + *

Sets up this job as a long running system job

+ */ + protected ResourceEventProcessingJob() { + super(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$ + SSECoreMessages.IndexManager_Processing_resource_events); + + //set this up as a long running system job + this.setUser(false); + this.setSystem(true); + this.setPriority(Job.LONG); + + this.fIsPaused = false; + this.fResourceEvents = new LinkedHashMap(); + } + + /** + *

Loads any preserved {@link ResourceEvent}s from the last time {@link #stop(boolean)} + * was invoked and schedules the job to be run

+ * + *

NOTE: Should be used instead of of calling {@link Job#schedule()} + * because this method also takes care of loading preserved state.

+ * + * @param loadPreservedResourceEvents true if should load any + * preserved {@link ResourceEvent}s from the last time {@link #stop(boolean)} + * was invoked + * + * @return true if either loadPreservedResourceEvents + * was false or there was success in loading the preserved {@link ResourceEvent}s. + * If false then some {@link ResourceEvent}s may have been loosed + * and to insure index consistency with the workspace a full workspace re-index + * is needed. + * + * @see #stop(boolean) + */ + protected synchronized boolean start(boolean loadPreservedResourceEvents, + SubMonitor progress) { + + boolean successLoadingPreserved = true; + + //attempt to load preserved resource events if requested + if(!loadPreservedResourceEvents) { + File preservedResourceEventsFile = this.getPreservedResourceEventsFile(); + preservedResourceEventsFile.delete(); + } else { + successLoadingPreserved = this.loadPreservedRecievedResourceEvents(progress); + } + + //start up the job + this.schedule(); + + return successLoadingPreserved; + } + + /** + *

Immediately stops the job and preserves any {@link ResourceEvent}s in the queue + * to be processed by not yet processed if requested

+ * + * @param preserveResourceEvents true to preserve any {@link ResourceEvent}s + * in the queue yet to be processed, false otherwise + * + * @return true if either preserveResourceEvents is + * false or if there was success in preserving the {@link ResourceEvent}s + * yet to be processed. If false then the preserving failed and a + * full workspace re-processing is needed the next time the manger is started + * + * @throws InterruptedException This could happen when trying to cancel or join + * the job in progress, but it really shouldn't + * + * @see #start(boolean, SubMonitor) + */ + protected synchronized boolean stop(boolean preserveResourceEvents) throws InterruptedException { + //this will not block indefinitely because it is known this job can be canceled + this.cancel(); + this.join(); + + //preserve if requested, else be sure no preserve file is left over for next start + boolean success = true; + if(preserveResourceEvents && this.hasResourceEventsToProcess()) { + success = this.preserveRecievedResourceEvents(); + } else { + this.getPreservedResourceEventsFile().delete(); + } + + return success; + } + + /** + * @return true if job is currently running or paused + * + * @see #pause() + * @see #unPause() + */ + protected synchronized boolean isProcessing() { + return this.getState() != Job.NONE || this.fIsPaused; + } + + /** + *

Un-pauses this job. This has no effect if the job is already running.

+ *

This should be used in place of {@link Job#schedule()} to reset state + * caused by calling {@link #pause()}

+ * + * @see #pause() + */ + protected synchronized void unPause() { + this.fIsPaused = false; + + //get the job running again depending on its current state + if(this.getState() == Job.SLEEPING) { + this.wakeUp(DELAY); + } else { + this.schedule(DELAY); + } + } + + /** + *

Pauses this job, even if it is running

+ *

This should be used in place of {@link Job#sleep()} because {@link Job#sleep()} + * will not pause a job that is already running but calling this will pause this job + * even if it is running. {@link #unPause()} must be used to start this job again

+ * + * @see #unPause() + */ + protected synchronized void pause() { + //if job is already running this will force it to pause + this.fIsPaused = true; + + //this only works if the job is not running + this.sleep(); + } + + /** + *

Adds a batch of {@link ResourceEvent}s to the queue of events to be processed. + * Will also un-pause the job if it is not already running

+ * + * @param resourceEvents {@link Map}<{@link IResource}, {@link ResourceEvent}> + * A batch of {@link ResourceEvent}s to be processed + * + * @see #addResourceEvent(ResourceEvent) + * @see #unPause() + */ + protected void addResourceEvents(Map resourceEvents) { + Iterator iter = resourceEvents.keySet().iterator(); + while(iter.hasNext()) { + IResource resource = (IResource)iter.next(); + ResourceEvent resourceEvent = (ResourceEvent)resourceEvents.get(resource); + addResourceEvent(resource, resourceEvent); + } + + //un-pause the processor if it is not already running + if(!isProcessing()) { + this.unPause(); + } + } + + /** + *

Gets the number of {@link ResourceEvent}s left to process by this job. This + * count is only valid for the exact moment it is returned because events are + * constantly being added and removed from the queue of events to process

+ * + * @return the number of {@link ResourceEvent}s left to process + */ + protected int getNumResourceEventsToProcess() { + return this.fResourceEvents.size(); + } + + /** + *

Blocks until either the given timeout elapses (0 means never to timeout), or + * there are currently no {@link ResourceEvent}s to process or being processed + * by this job

+ * + * @param timeout block until either this timeout elapses (0 means never to timeout) + * or there are currently no {@link ResourceEvent}s to process or being processed + * by this job + * + * @throws InterruptedException This can happen when waiting for a lock + */ + protected void waitForConsistant(int timeout) throws InterruptedException { + if(hasResourceEventsToProcess() || isProcessing()) { + synchronized (this.fToNotifyLock) { + this.fToNotifyLock.wait(timeout); + } + } + } + + /** + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + try { + //report status + SubMonitor progress = SubMonitor.convert(monitor); + + while(!this.fIsPaused && !monitor.isCanceled() && this.hasResourceEventsToProcess()) { + //report status + progress.setTaskName(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$ + NLS.bind(SSECoreMessages.IndexManager_Indexing_0_Files, + "" + getNumResourceEventsToProcess())); //$NON-NLS-1$ + progress.setWorkRemaining(getNumResourceEventsToProcess()); + + //get the next event to process + ResourceEvent resourceEvent = null; + IResource resource = null; + synchronized (this.fResourceEventsLock) { + resource = (IResource) this.fResourceEvents.keySet().iterator().next(); + resourceEvent = (ResourceEvent)this.fResourceEvents.remove(resource); + } + + //report status + monitor.subTask(resource.getName()); + + //perform action safely + final byte source = resourceEvent.fSource; + final byte action = resourceEvent.fAction; + final IResource finResource = resource; + final IPath movePath = resourceEvent.fMovePath; + SafeRunner.run(new ISafeRunnable() { + public void run() throws Exception { + AbstractIndexManager.this.performAction(source, action, + finResource, movePath); + } + + public void handleException(Throwable e) { + Logger.logException("Error while performing an update to the index. " + //$NON-NLS-1$ + AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e); + } + }); + + //report progress + progress.worked(1); + + //avoid dead locks + Job.getJobManager().currentJob().yieldRule(monitor); + } + + //done work + monitor.done(); + } finally { + //want to be sure we notify no matter how we exit + this.notifyIfConsistant(); + } + + /* if canceled then return CANCEL, + * else if done or paused return OK + */ + IStatus exitStatus; + if(monitor.isCanceled()) { + exitStatus = Status.CANCEL_STATUS; + } else { + exitStatus = Status.OK_STATUS; + } + + return exitStatus; + } + + /** + *

If resource not already scheduled to be processed, schedule it + * else if resource already scheduled to be processed, update the action only if + * the new action comes from a resource change event.

+ * + *

Ignore other sources for updating existing resource events because all other + * sources are "start-up" type sources and thus only {@link ResourceEvent} with a + * source of a resource change event trump existing events.

+ * + * @param resourceEvent {@link ResourceEvent} to be processed by this job + */ + private void addResourceEvent(IResource resource, ResourceEvent resourceEvent) { + + synchronized (this.fResourceEventsLock) { + /* if resource not already scheduled to be processed, schedule it + * else if resource already scheduled to be processed, update the action only if + * the new action comes from a resource change event + */ + if(!this.fResourceEvents.containsKey(resource)) { + this.fResourceEvents.put(resource, resourceEvent); + } else if(resourceEvent.fSource == AbstractIndexManager.SOURCE_RESROUCE_CHANGE) { + ((ResourceEvent)this.fResourceEvents.get(resource)).fAction = resourceEvent.fAction; + } else { + //Purposely ignoring all other resource events + } + } + } + + /** + * @return true if there are any resources to process, + * false otherwise + */ + private boolean hasResourceEventsToProcess() { + return !this.fResourceEvents.isEmpty(); + } + + /** + *

Preserves all of the resource events that have been received by this + * manager but not yet processed

+ * + *

If this operation was successful then the next time the manager starts + * it can load these events and process them. If it was not successful then a + * full re-processing of the entire workspace will need to take place to be + * sure the index is consistent.

+ * + *

NOTE: If this method changes how it preserves these events then + * {@link #serialVersionUID} will need to be incremented so that the manger + * does not attempt to load an old version of the file that may exist in a users + * workspace. Also {@link #loadPreservedRecievedResourceEvents(SubMonitor)} will + * have to be updated to load the new file structure

+ * + * @return true if successfully preserved the resources the resource + * events that have been received by not yet processed, false otherwise + * + * @see #serialVersionUID + * @see #loadPreservedRecievedResourceEvents(SubMonitor) + */ + private boolean preserveRecievedResourceEvents() { + File preservedResourceEventsFile = this.getPreservedResourceEventsFile(); + boolean success = true; + + synchronized (this.fResourceEventsLock) { + DataOutputStream dos = null; + try { + //if file already exists delete it + if(preservedResourceEventsFile.exists()) { + preservedResourceEventsFile.delete(); + preservedResourceEventsFile.createNewFile(); + } + + //create output objects + FileOutputStream fos = new FileOutputStream(preservedResourceEventsFile); + BufferedOutputStream bos = new BufferedOutputStream(fos); + dos = new DataOutputStream(bos); + + //write serial version + dos.writeLong(serialVersionUID); + + //write size + dos.writeInt(this.getNumResourceEventsToProcess()); + + //write out all the information needed to restore the resource events to process + Iterator iter = this.fResourceEvents.keySet().iterator(); + while(iter.hasNext()) { + IResource resource = (IResource)iter.next(); + ResourceEvent resourceEvent = (ResourceEvent)this.fResourceEvents.get(resource); + + if(resourceEvent.fSource != AbstractIndexManager.SOURCE_WORKSPACE_SCAN) { + //write out information + dos.writeByte(resourceEvent.fAction); + dos.writeByte(resource.getType()); + dos.writeBytes(resource.getLocation().toPortableString()); + dos.writeByte('\0'); + if(resourceEvent.fMovePath != null) { + dos.writeBytes(resourceEvent.fMovePath.toPortableString()); + } + dos.writeByte('\0'); + } + } + + this.fResourceEvents.clear(); + + dos.flush(); + } catch (FileNotFoundException e) { + Logger.logException(AbstractIndexManager.this.fName + + ": Exception while opening file to preserve resrouces to index.", //$NON-NLS-1$ + e); + success = false; + } catch (IOException e) { + Logger.logException(AbstractIndexManager.this.fName + + ": Exception while writing to file to preserve resrouces to index.", //$NON-NLS-1$ + e); + success = false; + } finally { + //be sure to close output + if(dos != null) { + try { + dos.close(); + } catch (IOException e) { + Logger.logException(AbstractIndexManager.this.fName + + ": Exception while closing file with preserved resources to index.", //$NON-NLS-1$ + e); + success = false; + } + } + } + + //if failed, for consistency must do a full re-process next workspace load + if(!success) { + preservedResourceEventsFile.delete(); + } + } + + return success; + } + + /** + *

Loads the received resource events that were preserved during the manger's + * last shut down so they can be processed now

+ * + *

If this operation is not successful then a full re-processing of the + * entire workspace is needed to be sure the index is consistent.

+ * + * @param progress used to report status of loading the preserved received resource events + * @return true if the loading of the preserved received resource events + * was successful, false otherwise. + * + * @see #serialVersionUID + * @see #preserveRecievedResourceEvents() + */ + private boolean loadPreservedRecievedResourceEvents(SubMonitor progress) { + progress.subTask(SSECoreMessages.IndexManager_processing_deferred_resource_changes); + + boolean success = true; + File preservedResourceEventsFile = this.getPreservedResourceEventsFile(); + + if(preservedResourceEventsFile.exists()) { + Map preservedResrouceEvents = null; + + DataInputStream dis = null; + try { + FileInputStream fis = new FileInputStream(preservedResourceEventsFile); + BufferedInputStream bis = new BufferedInputStream(fis); + dis = new DataInputStream(bis); + + //check serial version first + long preservedSerialVersionUID = dis.readLong(); + if(preservedSerialVersionUID == serialVersionUID) { + + //read each record + int numberOfRecords = dis.readInt(); + preservedResrouceEvents = new LinkedHashMap(numberOfRecords); + progress.setWorkRemaining(numberOfRecords); + for(int i = 0; i < numberOfRecords; ++i) { + //action is first byte + byte action = dis.readByte(); + + //file type is the next byte + byte fileType = dis.readByte(); + + //resource location are the next bytes + StringBuffer resourceLocationBuffer = new StringBuffer(); + byte b = dis.readByte(); + while(b != '\0') { + resourceLocationBuffer.append(b); + } + + //move path are the next bytes + StringBuffer movePathBuffer = new StringBuffer(); + b = dis.readByte(); + while(b != '\0') { + movePathBuffer.append(b); + } + + //get the resource + IResource resource = null; + IPath resourcePath = new Path(resourceLocationBuffer.toString()); + if(fileType == IResource.FILE) { + resource = + ResourcesPlugin.getWorkspace().getRoot().getFile(resourcePath); + } else { + resource = + ResourcesPlugin.getWorkspace().getRoot().getFolder(resourcePath); + } + + //get the move path + IPath movePath = null; + if(movePathBuffer.length() != 0) { + movePath = new Path(movePathBuffer.toString()); + } + + //add the object to the list of of preserved resources + preservedResrouceEvents.put(resource, new ResourceEvent( + AbstractIndexManager.SOURCE_PRESERVED_RESOURCES_TO_INDEX, + action, movePath)); + + progress.worked(1); + } + } else { + success = false; + } + } catch (FileNotFoundException e) { + Logger.logException(AbstractIndexManager.this.fName + + ": Exception while opening file to read preserved resrouces to index.", //$NON-NLS-1$ + e); + success = false; + } catch (IOException e) { + Logger.logException(AbstractIndexManager.this.fName + + ": Exception while reading from file of preserved resrouces to index.", //$NON-NLS-1$ + e); + success = false; + } finally { + if(dis != null) { + try { + dis.close(); + } catch (IOException e) { + Logger.logException(AbstractIndexManager.this.fName + + ": Exception while closing file of preserved resources" + //$NON-NLS-1$ + " to index that was just read. This should have no" + //$NON-NLS-1$ + " effect on the consistency of the index.", //$NON-NLS-1$ + e); + } + } + } + + //if success loading preserved then add to master list + if(success) { + synchronized (this.fResourceEventsLock) { + Iterator iter = preservedResrouceEvents.keySet().iterator(); + while(iter.hasNext()) { + IResource resource = (IResource)iter.next(); + ResourceEvent event = (ResourceEvent)preservedResrouceEvents.get(resource); + this.fResourceEvents.put(resource, event); + } + } + } else { + //failed reading file, so delete it + preservedResourceEventsFile.delete(); + } + } + + return success; + } + + /** + * @return {@link File} that contains any resource events received but not processed + * by this manager the last time it shutdown. This file may or may not actually + * exist. + * + * @see #preserveRecievedResourceEvents() + * @see #loadPreservedRecievedResourceEvents(SubMonitor) + */ + private File getPreservedResourceEventsFile() { + IPath preservedResroucesToIndexPath = + AbstractIndexManager.this.getWorkingLocation().append( + PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME); + return new File(preservedResroucesToIndexPath.toOSString()); + } + + /** + *

If all resource events have been processed + */ + private void notifyIfConsistant() { + if(!this.hasResourceEventsToProcess()) { + synchronized (this.fToNotifyLock) { + this.fToNotifyLock.notifyAll(); + } + } + } + } + + /** + *

Represents a resource that was discovered by this manager. Contains + * all the information this manager and the index needs to know about this + * resource. Such has how the manager was notified about this resource and + * the type of action occurring on the resource.

+ */ + private static class ResourceEvent { + /** + *

The source of this resource event

+ * + * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN + * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX + */ + protected byte fSource; + + /** + *

The action that the index should take with this resource

+ * + * @see AbstractIndexManager#ACTION_ADD + * @see AbstractIndexManager#ACTION_REMOVE + * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM + * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO + */ + protected byte fAction; + + /** + * + *

If the {@link #fAction} is {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} or + * {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this field will have the + * path the resource was moved from or moved to. Else this field will be null

+ * + *

NOTE: Maybe null.

+ * + * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM + * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO + */ + protected IPath fMovePath; + + /** + *

Creates a resource event that the index needs to react to in some way

+ * + * @param source source that the manger used to learn of this resource + * @param action action the index should take on this resource + * @param resource resource that the index should know about + * @param movePath if action is {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} or + * {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this should be the path the + * resource was moved from or moved to respectively, else should be null + * + * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE + * @see AbstractIndexManager#SOURCE_SAVED_STATE + * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN + * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX + * + * @see AbstractIndexManager#ACTION_ADD + * @see AbstractIndexManager#ACTION_REMOVE + * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM + * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO + */ + protected ResourceEvent(byte source, byte action, IPath movePath) { + this.fSource = source; + this.fAction = action; + this.fMovePath = movePath; + } + } +} \ No newline at end of file Index: src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java,v retrieving revision 1.4 diff -u -r1.4 SSECoreMessages.java --- src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java 24 Jun 2005 20:13:21 -0000 1.4 +++ src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java 1 Sep 2010 12:23:11 -0000 @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2005 IBM Corporation and others. All rights reserved. This + * Copyright (c) 2005, 2010 IBM Corporation and others. All rights reserved. This * program and the accompanying materials are made available under the terms of * the Eclipse Public License v1.0 which accompanies this distribution, and is * available at http://www.eclipse.org/legal/epl-v10.html @@ -36,4 +36,13 @@ public static String TaskScanningJob_0; public static String TaskScanningJob_1; public static String Migrate_Charset; + + public static String IndexManager_starting; + public static String IndexManager_Indexing_0_Files; + public static String IndexManager_processing_deferred_resource_changes; + public static String IndexManager_Processing_entire_workspace_for_the_first_time; + public static String IndexManager_processing_recent_resource_changes; + public static String IndexManager_0_resources_to_go; + public static String IndexManager_Waiting_for_0; + public static String IndexManager_Processing_resource_events; } Index: src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties =================================================================== RCS file: /cvsroot/webtools/sourceediting/plugins/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties,v retrieving revision 1.5 diff -u -r1.5 SSECorePluginResources.properties --- src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties 10 Apr 2007 20:07:25 -0000 1.5 +++ src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties 1 Sep 2010 12:23:11 -0000 @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2001, 2005 IBM Corporation and others. +# Copyright (c) 2001, 2010 IBM Corporation and others. # All rights reserved. This program and the accompanying materials # are made available under the terms of the Eclipse Public License v1.0 # which accompanies this distribution, and is available at @@ -19,3 +19,12 @@ TaskScanningJob_1=Errors while detecting Tasks ############################################################################### Migrate_Charset=Migrate Charset + +IndexManager_starting=Starting +IndexManager_Indexing_0_Files=Indexing {0} Files +IndexManager_processing_deferred_resource_changes=Processing deferred resource changes +IndexManager_Processing_entire_workspace_for_the_first_time=Processing entire workspace for the first time +IndexManager_processing_recent_resource_changes=Processing recent resource changes +IndexManager_0_resources_to_go={0} resources to go +IndexManager_Waiting_for_0=Waiting for {0} +IndexManager_Processing_resource_events=Processing Resource Events \ No newline at end of file