[329397] [index] Need way to wait for JSP index to be consistent
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java
index 697124d..c3d65d2 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2009 IBM Corporation and others.
+ * 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
@@ -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
*/
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java
index b101b5b..a3c184f 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java
@@ -10,6 +10,8 @@
*******************************************************************************/
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;
@@ -26,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;
@@ -36,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 */
- private ISaveParticipant fSaveParticipant;
/**
+ * <p>Job used to finish tasks needed to start up the plugin but that did not have
+ * to block the plugin start up process.</p>
+ */
+ private Job fPluginInitializerJob;
+
+ /**
* The constructor.
*/
public JSPCorePlugin() {
super();
plugin = this;
- fSaveParticipant = new SaveParticipant();
+ this.fPluginInitializerJob = new PluginInitializerJob();
}
/**
@@ -75,58 +79,9 @@
// listen for classpath changes
JavaCore.addElementChangedListener(TaglibHelperManager.getInstance());
-
- /*
- * Restore save state and process any events that happened before
- * plug-in loaded. Don't do it immediately since adding the save
- * participant requires a lock on the workspace to compute the
- * accumulated deltas, and if the tree is not already locked it
- * becomes a blocking call.
- */
- if (JSPTranslatorPersister.ACTIVATED) {
- Job persister = new Job(JSPCoreMessages.Initializing) {
- protected IStatus run(IProgressMonitor monitor) {
- final IWorkspace workspace = ResourcesPlugin.getWorkspace();
- try {
- workspace.run(new IWorkspaceRunnable() {
- public void run(IProgressMonitor monitor) throws CoreException {
- ISavedState savedState = null;
- try {
- savedState = workspace.addSaveParticipant(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());
- }
- }
- }
- }, monitor);
- }
- catch (CoreException e) {
- return e.getStatus();
- }
- finally {
- /*
- * set up persister to listen to resource change
- * events
- */
- workspace.addResourceChangeListener(JSPTranslatorPersister.getDefault());
- }
- return Status.OK_STATUS;
- }
- };
- persister.setUser(false);
- persister.schedule(2000);
- }
- //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();
@@ -142,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());
@@ -170,6 +117,79 @@
}
/**
+ * <p>A {@link Job} used to perform delayed initialization for the plugin</p>
+ */
+ private static class PluginInitializerJob extends Job {
+ /**
+ * <p>Default constructor to set up this {@link Job} as a
+ * long running system {@link Job}</p>
+ */
+ protected PluginInitializerJob() {
+ super(JSPCoreMessages.JSPCorePlugin_Initializing_JSP_Tools);
+
+ this.setUser(false);
+ this.setSystem(true);
+ this.setPriority(Job.LONG);
+ }
+
+ /**
+ * <p>Perform delayed initialization for the plugin</p>
+ *
+ * @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 {
+ /*
+ * Restore save state and process any events that happened before
+ * plug-in loaded. Don't do it immediately since adding the save
+ * participant requires a lock on the workspace to compute the
+ * accumulated deltas, and if the tree is not already locked it
+ * becomes a blocking call.
+ */
+ 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." + //$NON-NLS-1$
+ " All componenets dependent on this state will start as if first workspace load.", e); //$NON-NLS-1$
+ }
+
+ //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.
*/
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties
index e7e91b5..f322e91 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties
@@ -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
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java
index c7b1154..ac7475e 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java
@@ -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;
}
}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java
index 5cafc54..d41723b 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java
@@ -17,31 +17,18 @@
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
-import java.util.LinkedList;
+import java.io.UnsupportedEncodingException;
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 +36,19 @@
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
/**
- * <p>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</p>
- * <p>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.<p>
- * <p><b>Plugin Activation:</b>
- * <pre>
- * 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());
- * </pre>
- * <b>Plugin Deactivation:</b>
- * <pre>
- * ResourcesPlugin.getWorkspace().removeSaveParticipant(plugin.getBundle().getSymbolicName());
- * ResourcesPlugin.getWorkspace().removeResourceChangeListener(JSPTranslatorPersistor.getDefault());
- * </pre></p>
+ * <p>This is a static class used to persist JSP translations and retrieve the persisted
+ * translations.</p>
+ *
+ * <p>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.</p>
*
* <p>This class can be deactivated through the <code>persistJSPTranslations</code> system property,
* a value of <code>true</code> means the persister is activated (which is the default), value of
* <code>false</code> means the persister is not activated.</p>
+ *
+ * @see JSPIndexManager
*/
-public class JSPTranslatorPersister implements IResourceChangeListener {
+public class JSPTranslatorPersister{
/**
* <code>true</code> if the persister is activated, <code>false</code>
* otherwise. This is determined by checking the system property
@@ -90,63 +64,13 @@
/** 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();
+ /** lock to use while using the checksum */
+ private static final Object CHECKSUM_CALC_LOCK = new Object();
/**
- * Used to handle resource change events
- * @see #resourceChanged(IResourceChangeEvent)
- */
- private IResourceDeltaVisitor fResourceDeltaVisitor;
-
- /** {@link Job} that actually does all the persisting */
- protected PersisterJob fPersisterJob;
-
- /**
- * <p>Private singleton default constructor</p>
+ * <p>Private constructor to prevent creating an instance of this class</p>
*/
private JSPTranslatorPersister() {
- this.fResourceDeltaVisitor = new JSPResourceVisitor();
- this.fPersisterJob = new PersisterJob();
- }
-
- /**
- * <p><b>NOTE: </b><i>This can possible return <code>null</code></i></p>
- *
- * @return Singleton instance of the {@link JSPTranslatorPersister} if
- * {@link #ACTIVATED} is <code>true</code>, <code>null</code> 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 +134,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.toString());
+ 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());
+ file.delete();
+ }
+ }
+
+ /**
+ * @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());
+
+ from.renameTo(to);
+ }
+ }
+
+ /**
* <p>Given the path to a JSP file determines the path to its persisted {@link JSPTranslator}</p>
*
* @param jspFilePath {@link IPath} to JSP file for which the path to its persisted {@link JSPTranslator}
@@ -218,10 +181,18 @@
* @return OS file path to the persisted {@link JSPTranslator} associated with the JSP file at
* <code>jspFilePath</code>
*/
- protected static String getPersistedTranslatorFilePath(String jspFilePath) {
- CHECKSUM_CALC.reset();
- CHECKSUM_CALC.update(jspFilePath.getBytes());
- String persistedTranslatorFileName = Long.toString(CHECKSUM_CALC.getValue()) + ".translator"; //$NON-NLS-1$
+ private static String getPersistedTranslatorFilePath(String jspFilePath) {
+ String persistedTranslatorFileName = "error.translator"; //$NON-NLS-1$
+ synchronized(CHECKSUM_CALC_LOCK){
+ try {
+ CHECKSUM_CALC.reset();
+ CHECKSUM_CALC.update(jspFilePath.getBytes("utf16")); //$NON-NLS-1$
+ persistedTranslatorFileName = Long.toString(CHECKSUM_CALC.getValue()) + ".translator"; //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ Logger.logException("Could not get utf16 encoded bytes to create checksum to store persisted file.", e); //$NON-NLS-1$
+ }
+ }
+
IPath location = PERSIST_LOCATION;
// ensure the folder exists on disk
@@ -240,290 +211,69 @@
}
/**
- * @see JSPResourceVisitor#visit(IResourceDelta)
+ * <p>Gets the associated {@link JSPTranslator} for a specific JSP file.</p>
+ * <p><b>NOTE: </b><i>This does not get the persisted translator but rather the
+ * associated translator in memory</i></p>
+ *
+ * @param jspFile {@link IFile} to the JSP file that the associated {@link JSPTranslator}
+ * is needed for
+ * @return {@link JSPTranslator} associated with the given <code>jspFilePath</code>, or
+ * <code>null</code> if none can be found.
*/
- private class JSPResourceVisitor implements IResourceDeltaVisitor {
- /**
- * <p>Default constructor</p>
- */
- protected JSPResourceVisitor() {
- }
-
- /**
- * <p>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}</p>
- *
- * @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;
- }
+ 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();
}
}
-
- //visit children deltas
- return true;
- }
-
- /**
- * <p>Determines if an {@link IResource} is a JSP resource</p>
- *
- * @param resource determine if this {@link IResource} is a JSP resource
- * @return <code>true</code> if <code>resource</code> is a JSP resource,
- * <code>false</code> 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;
- }
-
- /**
- * <p>Gets the associated {@link JSPTranslator} for a specific JSP file.</p>
- * <p><b>NOTE: </b><i>This does not get the persisted translator but rather the
- * associated translator in memory</i></p>
- *
- * @param jspFile {@link IFile} to the JSP file that the associated {@link JSPTranslator}
- * is needed for
- * @return {@link JSPTranslator} associated with the given <code>jspFilePath</code>, or
- * <code>null</code> 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();
- }
- }
-
- return translator;
- }
-
- /**
- * <p>Persists a {@link JSPTranslator} to disk for a specific JSP file</p>
- *
- * @param translator {@link JSPTranslator} to persist to disk
- * @param jspFilePath {@link IPath} to the JSP file the given <code>translator</code> 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();
}
}
- /**
- * <p>Deletes a persisted translation for a JSP file that has been deleted</p>
- *
- * @param jspFilePath {@link IPath} to the JSP file that has been deleted
- */
- protected void deletePersistedTranslator(File file) {
- file.delete();
- }
-
- /**
- * <p>Renames a persisted translation for a JSP file that has moved</p>
- *
- * @param jspPrevFilePath {@link IPath} to the previous location of JSP file</p>
- * @param jspNewFilePath {@link IPath} to new location of JSP file</p>
- */
- 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;
}
/**
- * <p>{@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}.</p>
- *
+ * <p>Persists a {@link JSPTranslator} to disk for a specific JSP file</p>
+ *
+ * @param translator {@link JSPTranslator} to persist to disk
+ * @param jspFilePath {@link IPath} to the JSP file the given <code>translator</code> is for
*/
- private class PersisterJob extends Job {
- /** Length to delay when scheduling job */
- private static final int DELAY = 500;
-
- /**
- * <code>{@link LinkedList}<{@link ISafeRunnable}></code>
- * <p>The persister actions that have been queued up by the {@link JSPResourceVisitor}</p>
- */
- private LinkedList fActions;
-
- /** Whether this job has been stopped or not */
- private boolean fIsStopped;
-
- /**
- * <p>Sets job up as a system job</p>
- */
- protected PersisterJob() {
- super(JSPCoreMessages.Persisting_JSP_Translations);
- this.setUser(false);
- this.setSystem(true);
- this.setPriority(Job.LONG);
-
- this.fActions = new LinkedList();
- this.fIsStopped = false;
+ 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$
}
-
- /**
- * <p>Starts this job. This has no effect if the job is already started.</p>
- * <p>This should be used in place of {@link Job#schedule()} to reset state
- * caused by calling {@link #stop()}</p>
- *
- * @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);
- }
- }
-
- /**
- * <p>Stops this job, even if it is running</p>
- * <p>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</p>
- *
- * @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();
- }
- }
+ }
- /**
- * <p>Process the actions until there are none left</p>
- * @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;
- }
+ /**
+ * @param path {@link IPath} to the JSP file that the persisted translator is needed for
+ * @return The persisted translator {@link File} for the JSP file at the given path
+ * or <code>null</code> if no persisted translator exists for the JSP file at the given path
+ */
+ private static File getPersistedFile(IPath path) {
+ return new File(getPersistedTranslatorFilePath(path.toString()));
}
}
\ No newline at end of file
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java
deleted file mode 100644
index c4a43ef..0000000
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java
index a6e8d55..ddd050d 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java
@@ -7,725 +7,150 @@
*
* 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.
+ * <p>Index manger used to update the JDT index with the Java translations
+ * of JSPs.</p>
*
- * @author pavery
+ * <p>Also keeps JSP persistence up to date</p>
+ *
+ * <p>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 #waitForConsistent(org.eclipse.core.runtime.IProgressMonitor)}. Such actions
+ * include but are not limited to searching and refactoring JSPs.</p>
*/
-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$
+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;
+
+ /**
+ * <p>Private singleton constructor</p>
+ */
+ private JSPIndexManager() {
+ super(JSPCoreMessages.JSPIndexManager);
+ }
+
+ /**
+ * @return the singleton instance of the {@link JSPIndexManager}
+ */
+ public static JSPIndexManager getDefault() {
+ return INSTANCE != null ? INSTANCE : (INSTANCE = new JSPIndexManager());
}
- 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
+ * @see indexer.internal.indexing.AbstractIndexManager#isResourceToIndex(int, java.lang.String)
*/
- public static final int S_CANCELED = 4;
-
- /** symbolic name for OSGI framework */
- private final String OSGI_FRAMEWORK_ID = "org.eclipse.osgi"; //$NON-NLS-1$
+ protected boolean isResourceToIndex(int type, IPath path) {
+ String name = path.lastSegment();
+ return
+ type == IResource.PROJECT ||
+ (type == IResource.FOLDER && !name.equals("bin") && !name.startsWith(".")) || //$NON-NLS-1$ //$NON-NLS-2$
+ JSP_CONTENT_TYPE.isAssociatedWith(path.lastSegment());
+ }
/**
- * Collects JSP files from a resource delta.
+ * @see indexer.internal.indexing.AbstractIndexManager#getWorkingLocation()
*/
- private class JSPResourceVisitor implements IResourceDeltaVisitor {
- // using hash map ensures only one of each file
- // must be reset before every use
- private HashMap jspFiles = null;
+ protected IPath getWorkingLocation() {
+ if(this.fWorkingLocation == null) {
+ //create path to working area
+ IPath workingLocation =
+ JSPCorePlugin.getDefault().getStateLocation().append("jspsearch"); //$NON-NLS-1$
- public JSPResourceVisitor() {
- this.jspFiles = new HashMap();
- }
+ // ensure that it exists on disk
+ File folder = new File(workingLocation.toOSString());
+ if (!folder.isDirectory()) {
+ try {
+ folder.mkdir();
+ }
+ catch (SecurityException e) {
+ Logger.logException(this.getName() +
+ ": Error while creating state location: " + folder + //$NON-NLS-1$
+ " This renders the index manager irrevocably broken for this workspace session", //$NON-NLS-1$
+ e);
+ }
+ }
+
+ this.fWorkingLocation = workingLocation;
+ }
+
+ return this.fWorkingLocation;
+ }
- 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;
+ /**
+ * @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) {
+
+ //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;
+ }
}
-
+ }
+
+ //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 {
- 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);
+ IProject project = file.getProject();
+ if (project != null) {
+ IJavaProject jproject = JavaCore.create(project);
+ if (jproject.exists()) {
+ ss.addJspFile(file);
}
}
}
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
-
- /**
- * schedules JSP files for indexing by Java core
- */
- 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;
- }
-
- }
-
- 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
- */
- 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);
- }
-
-
- /**
- * Package protected for access by inner Job class in resourceChanged(...)
- *
- * @return
- */
- 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;
- }
-
- /**
- * 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)
- */
- 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;
- }
-
-
- 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);
+ String filename = file != null ? file.getFullPath().toString() : ""; //$NON-NLS-1$
+ Logger.logException("JPSIndexManger: problem indexing:" + filename, e); //$NON-NLS-1$
}
}
}
-
- private class IndexJobCoordinator extends JobChangeAdapter {
-
- 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());
- }
-
-
- }
- }
-
- 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);
- }
- }
- }
-
- }
- }
-
- }
-
- IndexWorkspaceJob getIndexingJob() {
- return indexingJob;
- }
-
- ProcessFilesJob getProcessFilesJob() {
- return processFilesJob;
- }
-
- boolean isInitializing() {
- return initializing;
- }
-
}
diff --git a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java
index ed3ef8b..f17e430 100644
--- a/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java
+++ b/bundles/org.eclipse.jst.jsp.core/src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2007 IBM Corporation and others.
+ * Copyright (c) 2004, 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
@@ -33,7 +33,6 @@
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
-import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
import org.eclipse.jst.jsp.core.internal.JSPCorePlugin;
import org.eclipse.jst.jsp.core.internal.Logger;
@@ -315,10 +314,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().waitForConsistent(monitor);
SearchJob job = new SearchJob(searchText, scope, searchFor, limitTo, matchMode, isCaseSensitive, requestor);
setCanceled(false);
@@ -338,10 +394,12 @@
* @param scope
* @param requestor
*/
- public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) {
+ public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor,
+ IProgressMonitor monitor) {
- JSPIndexManager.getInstance().rebuildIndexIfNeeded();
-
+ //wait for the index
+ JSPIndexManager.getDefault().waitForConsistent(monitor);
+
SearchJob job = new SearchJob(element, scope, requestor);
setCanceled(false);
job.setUser(true);
@@ -359,9 +417,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().waitForConsistent(monitor);
SearchRunnable searchRunnable = new SearchRunnable(element, scope, requestor);
try {
@@ -467,11 +527,6 @@
// this is the only difference from
// IndexManager#computeIndexLocation(...)
indexLocation = getModelJspPluginWorkingLocation().append(fileName);
-
- // pa_TODO need to add to java path too, so JDT search support knows
- // there should be a non internal way to do this.
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=77564
- JavaModelManager.getIndexManager().indexLocations.put(containerPath, indexLocation);
//}
return indexLocation;
}
@@ -483,7 +538,7 @@
return this.fJspPluginLocation;
// Append the folder name "jspsearch" to keep the state location area cleaner
- IPath stateLocation = JSPCorePlugin.getDefault().getStateLocation().append("jspsearch");
+ IPath stateLocation = JSPCorePlugin.getDefault().getStateLocation().append("jspsearch"); //$NON-NLS-1$
// pa_TODO workaround for
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=62267
diff --git a/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java
index 399f27a..1cd9d31 100644
--- a/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java
+++ b/bundles/org.eclipse.jst.jsp.ui/src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * Copyright (c) 2004, 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
@@ -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);
}
}
}
diff --git a/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF b/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF
index 1e7bcf4..7ef5042 100644
--- a/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.wst.sse.core/META-INF/MANIFEST.MF
@@ -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",
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java
new file mode 100644
index 0000000..27424c3
--- /dev/null
+++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java
@@ -0,0 +1,1701 @@
+/*******************************************************************************
+ * 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.ByteArrayOutputStream;
+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;
+
+/**
+ * <p>A generic class for implementing 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 manager
+ * is managing.</p>
+ *
+ * <p>The manager 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, lost, or corrupted any resource change events
+ * that have occurred before, during, or after its activation or deactivation then the
+ * manager will inspect the entire workspace to insure the index it is managing is
+ * consistent.</p>
+ *
+ */
+public abstract class AbstractIndexManager {
+
+ /** Used to encode path bytes in case they contain double byte characters */
+ private static final String ENCODING_UTF16 = "utf16"; //$NON-NLS-1$
+
+ /** Default time to wait for other tasks to finish */
+ private static final int WAIT_TIME = 300;
+
+ /**
+ * <p>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.</p>
+ */
+ 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_AMOUNT = 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_RESOURCE_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 */
+ private 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;
+
+ /**
+ * <p>Current state of the manager</p>
+ *
+ * @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();
+
+ /** <code>true</code> if the manager is currently starting, <code>false</code> otherwise */
+ private boolean fStarting;
+
+ /**
+ * <p>Creates the manager with a given name.</p>
+ *
+ * @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;
+ }
+
+ /**
+ * <p>Creates the manager with a given name.</p>
+ *
+ * @param name This will be pre-pended to progress reporting messages and thus should
+ * be translated
+ *
+ * @param messageRunning ignored
+ * @param messagegInitializing ignored
+ * @param messageProcessingFiles ignored
+ *
+ * @deprecated This constructor ignores the last three parameters.
+ * @see #AbstractIndexManager(String)
+ */
+
+ protected AbstractIndexManager(String name, String messageRunning,
+ String messagegInitializing, String messageProcessingFiles) {
+ this(name);
+ }
+
+ /**
+ * <p>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 <code>null</code> it is assumed no files
+ * have been indexed yet so the entire workspace will be searched for
+ * files to be indexed.</p>
+ *
+ * <p>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</p>
+ *
+ * <p>Will block until {@link #stop()} has finished running if it is
+ * currently running</p>
+ *
+ * @param savedStateDelta the delta from a saved state, if <code>null</code>
+ * 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(NLS.bind(SSECoreMessages.IndexManager_0_starting, this.fName), 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(NLS.bind(SSECoreMessages.IndexManager_0_starting_1,
+ new String[] {this.fName, 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;
+ }
+ }
+
+ /**
+ * <p>Safely shuts down the manager.</p>
+ *
+ * <p>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)}.</p>
+ *
+ * <p>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.</p>
+ *
+ * @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;
+ }
+ }
+ }
+
+ /**
+ * @return the name of this indexer
+ */
+ protected String getName() {
+ return this.fName;
+ }
+
+ /**
+ * <p>Should be called by a client of the index this manager manages before the index
+ * is accessed, assuming the client wants an index consistent with the latest
+ * resource changes.</p>
+ *
+ * <p>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
+ * may be canceled, but if it is the state of the index is not guaranteed to be
+ * consistent with the latest resource change events.</p>
+ *
+ * @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 unprocessed resource changes
+ *
+ * @return <code>true</code> if the wait finished successfully and the manager is consistent,
+ * <code>false</code> 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 waitForConsistent(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(NLS.bind(SSECoreMessages.IndexManager_0_starting, this.fName));
+ 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(NLS.bind(SSECoreMessages.IndexManager_0_Indexing_1_Files,
+ new Object[] {AbstractIndexManager.this.fName, "" + 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;
+ }
+
+ /**
+ * <p>Called for each {@link IResource} given in a resource delta. If the resource
+ * type is a file then used to determine if that file should be processed by the manager,
+ * if the resource type is a project or directory then it is used to determine if the children
+ * of the project or directory should be processed looking for file resources.</p>
+ *
+ * <p><b>NOTE:</b> Even if <code>true</code> is returned for a directory resource that
+ * only means the children of the directory should be inspected for possible files to index.
+ * Directories themselves can not be managed in order to add to an index.</p>
+ *
+ * @param type the {@link IResource#getType()} result of the resource to possibly index
+ * @param path the full {@link IPath} to the resource to possibly index
+ * @return <code>true</code> If the resource with the given <code>type</code> and
+ * <code>path</code> should either itself be indexed, or its children should be indexed,
+ * <code>false</code> if neither the described resource or any children should be indexed
+ */
+ protected abstract boolean isResourceToIndex(int type, IPath path);
+
+ /**
+ * <p>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 manager is managing.</p>
+ *
+ * @param source The source that reported this resource event
+ * @param action The action to be taken on the given <code>resource</code>
+ * @param resource The index should perform the given <code>action</code> on this
+ * resource
+ * @param movePath If the given <code>action</code> 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 <code>resource</code> was either moved from or
+ * moved to respectively.
+ *
+ * @see AbstractIndexManager#SOURCE_RESOURCE_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);
+
+ /**
+ * <p>Gets the working location of the manager. This is where any relevant
+ * state can be persisted.</p>
+ *
+ * @return the working location of the manager
+ */
+ protected abstract IPath getWorkingLocation();
+
+ /**
+ * <p>Next time the manager starts up force a full workspace index</p>
+ */
+ 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 <code>true</code> if a full workspace index is needed as dictated by
+ * a previous call to {@link #forceFullReIndexNextStart()}, <code>false</code>
+ * 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;
+ }
+
+ /**
+ * <p>A system {@link Job} used to visit all of the files in the workspace
+ * looking for files to index.</p>
+ *
+ * <p>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.</p>
+ */
+ private class WorkspaceVisitorJob extends Job {
+ /**
+ * <p>Default constructor that sets up this job as a system job</p>
+ */
+ protected WorkspaceVisitorJob() {
+ super(NLS.bind(SSECoreMessages.IndexManager_0_Processing_entire_workspace_for_the_first_time,
+ AbstractIndexManager.this.fName));
+
+ 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(
+ NLS.bind(SSECoreMessages.IndexManager_0_Processing_entire_workspace_for_the_first_time,
+ AbstractIndexManager.this.fName),
+ 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;
+ }
+
+ /**
+ * <p>An {@link IResourceProxyVisitor} used to visit all of the files in the
+ * workspace looking for files to add to the index.</p>
+ *
+ * <p><b>NOTE: </b>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.</p>
+ */
+ private class WorkspaceVisitor implements IResourceProxyVisitor {
+ /** {@link IProgressMonitor} used to report status and check for cancellation */
+ private SubMonitor fProgress;
+
+ /**
+ * {@link Map}<{@link IResource}, {@link ResourceEvent}>
+ * <p>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.</p>
+ *
+ * @see #processBatchedResourceEvents()
+ */
+ private Map fBatchedResourceEvents;
+
+ /**
+ * <p>Default constructor</p>
+ * @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_AMOUNT);
+ }
+
+ /**
+ * <p>As long as the monitor is not canceled visit each file in the workspace
+ * that should be visited.</p>
+ *
+ * @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()) {
+ /* if root node always visit children
+ * else ask manager implementation if resource and its children should be visited
+ */
+ IPath path = proxy.requestFullPath();
+ if(path.toString().equals("/")) { //$NON-NLS-1$
+ visitChildren = true;
+ } else if(isResourceToIndex(proxy.getType(), path)) {
+ if(proxy.getType() == IResource.FILE) {
+ //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;
+ }
+ visitChildren = false;
+ } else {
+ visitChildren = false;
+ }
+
+ //batch up resource changes before sending them out
+ if(this.fBatchedResourceEvents.size() >= BATCH_UP_AMOUNT) {
+ this.processBatchedResourceEvents();
+ }
+
+ return visitChildren;
+ }
+
+ /**
+ * <p>Sends any batched up resource events created by this visitor to the
+ * {@link ResourceEventProcessingJob}.<p>
+ *
+ * <p><b>NOTE:</b> 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</p>
+ */
+ protected void processBatchedResourceEvents() {
+ AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents(
+ this.fBatchedResourceEvents);
+ this.fBatchedResourceEvents.clear();
+ }
+ }
+ }
+
+ /**
+ * <p>Used to listen to resource change events in the workspace. These events
+ * are batched up and then passed onto the {@link ResourceEventProcessingJob}.</p>
+ */
+ private class ResourceChangeListener implements IResourceChangeListener {
+ /**
+ * <p>The number of events currently being processed by this listener.</p>
+ * <p>Use the {@link #fEventsBeingProcessedLock} when reading or writing this field</p>
+ *
+ * @see #fEventsBeingProcessedLock
+ */
+ private volatile int fEventsBeingProcessed;
+
+ /**
+ * Lock to use when reading or writing {@link #fEventsBeingProcessed}
+ *
+ * @see #fEventsBeingProcessed
+ */
+ private final Object fEventsBeingProcessedLock = new Object();
+
+ /**
+ * <p>Current state of this listener</p>
+ *
+ * @see AbstractIndexManager#STATE_DISABLED
+ * @see AbstractIndexManager#STATE_ENABLED
+ */
+ private volatile byte fState;
+
+ /**
+ * <p>Default constructor</p>
+ */
+ protected ResourceChangeListener() {
+ this.fState = STATE_DISABLED;
+ this.fEventsBeingProcessed = 0;
+ }
+
+ /**
+ * <p>Start listening for resource change events</p>
+ */
+ protected void start() {
+ this.fState = STATE_ENABLED;
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
+ }
+
+ /**
+ * <p>Stop listening for resource change events and if already processing
+ * an event then wait for that processing to finish</p>
+ *
+ * @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;
+ }
+
+ /**
+ * <p>Blocks until either the current resource event has been processed or
+ * until the given timeout has passed.</p>
+ *
+ * @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 <code>true</code> if this listener is currently processing any
+ * events, <code>false</code> otherwise.
+ */
+ protected boolean isProcessingEvents() {
+ return this.fEventsBeingProcessed != 0;
+ }
+
+ /**
+ * <p>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.</p>
+ *
+ * @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_RESOURCE_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 " + //$NON-NLS-1$
+ AbstractIndexManager.this.fName + " shut down. This should never " + //$NON-NLS-1$
+ "ever happen, but if it does the index may now be inconsistant."); //$NON-NLS-1$
+ }
+ } 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();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * <p>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_AMOUNT}
+ * before being passed onto the {@link ResourceEventProcessingJob}.</p>
+ *
+ * <p><b>NOTE 1: </b> 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}.</p>
+ *
+ * <p><b>NOTE 2: </b> Be sure to call {@link ResourceDeltaVisitor#processBatchedResourceEvents()}
+ * after using this visitor to be sure any remaining events get passed onto the
+ * {@link ResourceEventProcessingJob}.</p>
+ *
+ * @see ResourceDeltaVisitor#processBatchedResourceEvents()
+ */
+ private class ResourceDeltaVisitor implements IResourceDeltaVisitor {
+ /** {@link IProgressMonitor} used to report status */
+ private SubMonitor fProgress;
+
+ /**
+ * <p>The source that should be used when sending resource events to the
+ * {@link ResourceEventProcessingJob}.</p>
+ *
+ * @see AbstractIndexManager#SOURCE_SAVED_STATE
+ * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+ */
+ private byte fSource;
+
+ /**
+ * <p>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</p>
+ */
+ private int fPredictedWorkRemaining;
+
+ /**
+ * {@link Map}<{@link IResource}, {@link ResourceEvent}>
+ * <p>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.</p>
+ *
+ * @see #processBatchedResourceEvents()
+ */
+ private Map fBatchedResourceEvents;
+
+ /**
+ * <p>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.</p>
+ *
+ * @param source The source of the events that should be used when creating
+ * resource events from visited resources
+ *
+ * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+ * @see AbstractIndexManager#SOURCE_SAVED_STATE
+ */
+ protected ResourceDeltaVisitor(byte source) {
+ this(SubMonitor.convert(null), source);
+ }
+
+ /**
+ * <p>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.</p>
+ *
+ * <p><b>NOTE:</b> While the {@link SubMonitor} is provided to report status the
+ * visitor will not honor any cancellation requests.</p>
+ *
+ * @param progress Used to report status. This visitor can <b>not</b> be
+ * canceled
+ * @param source The source of the events that should be used when creating
+ * resource events from visited resources
+ *
+ * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+ * @see AbstractIndexManager#SOURCE_SAVED_STATE
+ */
+ protected ResourceDeltaVisitor(SubMonitor progress, byte source) {
+ this.fProgress = progress;
+ this.fSource = source;
+ this.fBatchedResourceEvents = new LinkedHashMap(BATCH_UP_AMOUNT);
+ this.fPredictedWorkRemaining = 1;
+ }
+
+ /**
+ * <p>Transforms each {@link IResourceDelta} into a {@link ResourceEvent}.
+ * Batches up these {@link ResourceEvent}s and then passes them onto the
+ * {@link ResourceEventProcessingJob}.</p>
+ *
+ * <p><b>NOTE 1: </b> Be sure to call {@link ResourceDeltaVisitor#processBatchedResourceEvents()}
+ * after using this visitor to be sure any remaining events get passed onto the
+ * {@link ResourceEventProcessingJob}.</p>
+ *
+ * @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_1,
+ new Object[] {"" + fPredictedWorkRemaining, delta.getFullPath().toString()})); //$NON-NLS-1$
+
+ //process delta if resource not hidden
+ boolean visitChildren = false;
+
+ /* if root node always visit its children
+ * else ask manager implementation if resource and its children should be visited
+ */
+ if(delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$
+ visitChildren = true;
+ } else {
+ IResource resource = delta.getResource();
+
+ //check if should index resource or its children
+ if(isResourceToIndex(resource.getType(), resource.getFullPath())) {
+ if(resource.getType() == IResource.FILE) {
+
+ 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;
+ }
+ }
+ }//end is file
+
+ 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_AMOUNT) {
+ this.processBatchedResourceEvents();
+ }
+ }
+
+ return visitChildren;
+ }
+
+ /**
+ * <p>Sends any batched up resource events created by this visitor to the
+ * {@link ResourceEventProcessingJob}.<p>
+ *
+ * <p><b>NOTE:</b> 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</p>
+ */
+ protected void processBatchedResourceEvents() {
+ AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents(
+ this.fBatchedResourceEvents);
+ this.fBatchedResourceEvents.clear();
+ }
+ }
+
+ /**
+ * <p>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}.</p>
+ *
+ * @see AbstractIndexManager#performAction(byte, byte, IResource, IPath)
+ */
+ private class ResourceEventProcessingJob extends Job {
+ /** Length to delay when scheduling job */
+ private static final int DELAY = 500;
+
+ /**
+ * <p>Name of the file where resource events still to index
+ * will be preserved for the next start up.</p>
+ */
+ private static final String PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME = ".preservedResourceEvents"; //$NON-NLS-1$
+
+ /**
+ * <p>This needs to be updated if {@link #preserveReceivedResourceEvents()} ever
+ * changes how it persists resource events so that {@link #loadPreservedReceivedResourceEvents(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.</p>
+ *
+ * @see #preserveReceivedResourceEvents()
+ * @see #loadPreservedReceivedResourceEvents(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}>
+ * <p>The list of resources events to be processed</p>
+ */
+ 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();
+
+ /**
+ * <p>Sets up this job as a long running system job</p>
+ */
+ protected ResourceEventProcessingJob() {
+ super(NLS.bind(SSECoreMessages.IndexManager_0_Processing_resource_events,
+ AbstractIndexManager.this.fName));
+
+ //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();
+ }
+
+ /**
+ * <p>Loads any preserved {@link ResourceEvent}s from the last time {@link #stop(boolean)}
+ * was invoked and schedules the job to be run</p>
+ *
+ * <p><b>NOTE: </b>Should be used instead of of calling {@link Job#schedule()}
+ * because this method also takes care of loading preserved state.</p>
+ *
+ * @param loadPreservedResourceEvents <code>true</code> if should load any
+ * preserved {@link ResourceEvent}s from the last time {@link #stop(boolean)}
+ * was invoked
+ *
+ * @return <code>true</code> if either <code>loadPreservedResourceEvents</code>
+ * was false or there was success in loading the preserved {@link ResourceEvent}s.
+ * If <code>false</code> 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.loadPreservedReceivedResourceEvents(progress);
+ }
+
+ //start up the job
+ this.schedule();
+
+ return successLoadingPreserved;
+ }
+
+ /**
+ * <p>Immediately stops the job and preserves any {@link ResourceEvent}s in the queue
+ * to be processed by not yet processed if requested</p>
+ *
+ * @param preserveResourceEvents <code>true</code> to preserve any {@link ResourceEvent}s
+ * in the queue yet to be processed, <code>false</code> otherwise
+ *
+ * @return <code>true</code> if either <code>preserveResourceEvents</code> is
+ * <code>false</code> or if there was success in preserving the {@link ResourceEvent}s
+ * yet to be processed. If <code>false</code> then the preserving failed and a
+ * full workspace re-processing is needed the next time the manager 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.preserveReceivedResourceEvents();
+ } else {
+ this.getPreservedResourceEventsFile().delete();
+ }
+
+ return success;
+ }
+
+ /**
+ * @return <code>true</code> if job is currently running or paused
+ *
+ * @see #pause()
+ * @see #unPause()
+ */
+ protected synchronized boolean isProcessing() {
+ return this.getState() != Job.NONE || this.fIsPaused;
+ }
+
+ /**
+ * <p>Un-pauses this job. This has no effect if the job is already running.</p>
+ * <p>This should be used in place of {@link Job#schedule()} to reset state
+ * caused by calling {@link #pause()}</p>
+ *
+ * @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);
+ }
+ }
+
+ /**
+ * <p>Pauses this job, even if it is running</p>
+ * <p>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</p>
+ *
+ * @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();
+ }
+
+ /**
+ * <p>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</p>
+ *
+ * @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();
+ }
+ }
+
+ /**
+ * <p>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</p>
+ *
+ * @return the number of {@link ResourceEvent}s left to process
+ */
+ protected int getNumResourceEventsToProcess() {
+ return this.fResourceEvents.size();
+ }
+
+ /**
+ * <p>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</p>
+ *
+ * @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(NLS.bind(SSECoreMessages.IndexManager_0_Indexing_1_Files,
+ new Object[] {AbstractIndexManager.this.fName, "" + 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;
+ }
+
+ /**
+ * <p>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.</p>
+ *
+ * <p>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.</p>
+ *
+ * @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_RESOURCE_CHANGE) {
+ ((ResourceEvent)this.fResourceEvents.get(resource)).fAction = resourceEvent.fAction;
+ } else {
+ //Purposely ignoring all other resource events
+ }
+ }
+ }
+
+ /**
+ * @return <code>true</code> if there are any resources to process,
+ * <code>false</code> otherwise
+ */
+ private boolean hasResourceEventsToProcess() {
+ return !this.fResourceEvents.isEmpty();
+ }
+
+ /**
+ * <p>Preserves all of the resource events that have been received by this
+ * manager but not yet processed</p>
+ *
+ * <p>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.</p>
+ *
+ * <p><b>NOTE:</b> If this method changes how it preserves these events then
+ * {@link #serialVersionUID} will need to be incremented so that the manager
+ * does not attempt to load an old version of the file that may exist in a users
+ * workspace. Also {@link #loadPreservedReceivedResourceEvents(SubMonitor)} will
+ * have to be updated to load the new file structure.</p>
+ *
+ * @return <code>true</code> if successfully preserved the resource events that
+ * have been received by not yet processed, <code>false</code> otherwise
+ *
+ * @see #serialVersionUID
+ * @see #loadPreservedReceivedResourceEvents(SubMonitor)
+ */
+ private boolean preserveReceivedResourceEvents() {
+ 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());
+ byte[] pathBytes = resource.getFullPath().toString().getBytes(ENCODING_UTF16);
+ dos.write(pathBytes);
+ 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;
+ }
+
+ /**
+ * <p>Loads the received resource events that were preserved during the manager's
+ * last shut down so they can be processed now</p>
+ *
+ * <p>If this operation is not successful then a full re-processing of the
+ * entire workspace is needed to be sure the index is consistent.</p>
+ *
+ * @param progress used to report status of loading the preserved received resource events
+ * @return <code>true</code> if the loading of the preserved received resource events
+ * was successful, <code>false</code> otherwise.
+ *
+ * @see #serialVersionUID
+ * @see #preserveReceivedResourceEvents()
+ */
+ private boolean loadPreservedReceivedResourceEvents(SubMonitor progress) {
+ progress.subTask(SSECoreMessages.IndexManager_processing_deferred_resource_changes);
+
+ boolean success = true;
+ File preservedResourceEventsFile = this.getPreservedResourceEventsFile();
+
+ if(preservedResourceEventsFile.exists()) {
+ Map preservedResourceEvents = 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();
+ preservedResourceEvents = 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
+ ByteArrayOutputStream resourceLocoationBOS = new ByteArrayOutputStream();
+ byte b = dis.readByte();
+ while(b != '\0') {
+ resourceLocoationBOS.write(b);
+ b = dis.readByte();
+ }
+
+ //move path are the next bytes
+ ByteArrayOutputStream movePathBOS = new ByteArrayOutputStream();
+ b = dis.readByte();
+ while(b != '\0') {
+ movePathBOS.write(b);
+ b = dis.readByte();
+ }
+
+ //get the resource
+ IResource resource = null;
+ IPath resourcePath = new Path(new String(resourceLocoationBOS.toByteArray(), ENCODING_UTF16));
+ 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(movePathBOS.size() != 0) {
+ movePath = new Path(new String(movePathBOS.toByteArray(), ENCODING_UTF16));
+ }
+
+ //add the object to the list of of preserved resources
+ preservedResourceEvents.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 resources to index.", //$NON-NLS-1$
+ e);
+ success = false;
+ } catch (IOException e) {
+ Logger.logException(AbstractIndexManager.this.fName +
+ ": Exception while reading from file of preserved resources 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 = preservedResourceEvents.keySet().iterator();
+ while(iter.hasNext()) {
+ IResource resource = (IResource)iter.next();
+ ResourceEvent event = (ResourceEvent)preservedResourceEvents.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 #preserveReceivedResourceEvents()
+ * @see #loadPreservedReceivedResourceEvents(SubMonitor)
+ */
+ private File getPreservedResourceEventsFile() {
+ IPath preservedResroucesToIndexPath =
+ AbstractIndexManager.this.getWorkingLocation().append(
+ PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME);
+ return new File(preservedResroucesToIndexPath.toOSString());
+ }
+
+ /**
+ * <p>If all resource events have been processed
+ */
+ private void notifyIfConsistant() {
+ if(!this.hasResourceEventsToProcess()) {
+ synchronized (this.fToNotifyLock) {
+ this.fToNotifyLock.notifyAll();
+ }
+ }
+ }
+ }
+
+ /**
+ * <p>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.</p>
+ */
+ private static class ResourceEvent {
+ /**
+ * <p>The source of this resource event</p>
+ *
+ * @see AbstractIndexManager#SOURCE_RESOURCE_CHANGE
+ * @see AbstractIndexManager#SOURCE_SAVED_STATE
+ * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN
+ * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX
+ */
+ protected byte fSource;
+
+ /**
+ * <p>The action that the index should take with this resource</p>
+ *
+ * @see AbstractIndexManager#ACTION_ADD
+ * @see AbstractIndexManager#ACTION_REMOVE
+ * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
+ * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
+ */
+ protected byte fAction;
+
+ /**
+ *
+ * <p>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 <code>null</code></p>
+ *
+ * <p><b>NOTE: </b>Maybe <code>null</code>.</p>
+ *
+ * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
+ * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
+ */
+ protected IPath fMovePath;
+
+ /**
+ * <p>Creates a resource event that the index needs to react to in some way</p>
+ *
+ * @param source source that the manager 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 <code>null</code>
+ *
+ * @see AbstractIndexManager#SOURCE_RESOURCE_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
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java
index 15fd1aa..4546839 100644
--- a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java
+++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java
@@ -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,15 @@
public static String TaskScanningJob_0;
public static String TaskScanningJob_1;
public static String Migrate_Charset;
+
+ public static String IndexManager_0_starting;
+ public static String IndexManager_0_starting_1;
+ public static String IndexManager_0_Indexing_1_Files;
+ public static String IndexManager_processing_deferred_resource_changes;
+ public static String IndexManager_Processing_entire_workspace_for_the_first_time;
+ public static String IndexManager_0_Processing_entire_workspace_for_the_first_time;
+ public static String IndexManager_processing_recent_resource_changes;
+ public static String IndexManager_0_resources_to_go_1;
+ public static String IndexManager_Waiting_for_0;
+ public static String IndexManager_0_Processing_resource_events;
}
diff --git a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties
index b6f01f8..971c0f7 100644
--- a/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties
+++ b/bundles/org.eclipse.wst.sse.core/src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties
@@ -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,14 @@
TaskScanningJob_1=Errors while detecting Tasks
###############################################################################
Migrate_Charset=Migrate Charset
+
+IndexManager_0_starting={0}: Starting
+IndexManager_0_starting_1={0}: Starting: {1}
+IndexManager_0_Indexing_1_Files={0}: Indexing {1} 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_0_Processing_entire_workspace_for_the_first_time={0}: Processing entire workspace for the first time
+IndexManager_processing_recent_resource_changes=Processing recent resource changes
+IndexManager_0_resources_to_go_1={0} resources to index: {1}
+IndexManager_Waiting_for_0=Waiting for {0}
+IndexManager_0_Processing_resource_events={0}: Processing Resource Events
\ No newline at end of file