View | Details | Raw Unified | Return to bug 323392 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/jst/jsp/core/internal/JSPCoreMessages.java (+3 lines)
Lines 61-66 Link Here
61
	public static String Initializing;
61
	public static String Initializing;
62
	public static String Persisting_JSP_Translations;
62
	public static String Persisting_JSP_Translations;
63
	
63
	
64
	public static String JSPCorePlugin_Initializing_JSP_Tools;
65
	public static String JSPIndexManager;
66
	
64
	/**
67
	/**
65
	 * @deprecated
68
	 * @deprecated
66
	 */
69
	 */
(-)src/org/eclipse/jst/jsp/core/internal/JSPCorePlugin.java (-47 / +83 lines)
Lines 10-18 Link Here
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.jst.jsp.core.internal;
11
package org.eclipse.jst.jsp.core.internal;
12
12
13
import org.eclipse.core.resources.IResourceChangeEvent;
14
import org.eclipse.core.resources.IResourceChangeListener;
13
import org.eclipse.core.resources.ISaveContext;
15
import org.eclipse.core.resources.ISaveContext;
14
import org.eclipse.core.resources.ISaveParticipant;
16
import org.eclipse.core.resources.ISaveParticipant;
15
import org.eclipse.core.resources.ISavedState;
17
import org.eclipse.core.resources.ISavedState;
18
import org.eclipse.core.resources.IWorkspace;
19
import org.eclipse.core.resources.IWorkspaceRunnable;
16
import org.eclipse.core.resources.ResourcesPlugin;
20
import org.eclipse.core.resources.ResourcesPlugin;
17
import org.eclipse.core.runtime.CoreException;
21
import org.eclipse.core.runtime.CoreException;
18
import org.eclipse.core.runtime.IProgressMonitor;
22
import org.eclipse.core.runtime.IProgressMonitor;
Lines 24-30 Link Here
24
import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController;
28
import org.eclipse.jst.jsp.core.internal.contentmodel.TaglibController;
25
import org.eclipse.jst.jsp.core.internal.contentproperties.JSPFContentPropertiesManager;
29
import org.eclipse.jst.jsp.core.internal.contentproperties.JSPFContentPropertiesManager;
26
import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache;
30
import org.eclipse.jst.jsp.core.internal.contenttype.DeploymentDescriptorPropertyCache;
27
import org.eclipse.jst.jsp.core.internal.java.JSPTranslatorPersister;
28
import org.eclipse.jst.jsp.core.internal.java.search.JSPIndexManager;
31
import org.eclipse.jst.jsp.core.internal.java.search.JSPIndexManager;
29
import org.eclipse.jst.jsp.core.internal.taglib.TaglibHelperManager;
32
import org.eclipse.jst.jsp.core.internal.taglib.TaglibHelperManager;
30
import org.eclipse.jst.jsp.core.taglib.TaglibIndex;
33
import org.eclipse.jst.jsp.core.taglib.TaglibIndex;
Lines 34-52 Link Here
34
 * The main plugin class to be used in the desktop.
37
 * The main plugin class to be used in the desktop.
35
 */
38
 */
36
public class JSPCorePlugin extends Plugin {
39
public class JSPCorePlugin extends Plugin {
37
	// The shared instance.
40
	/** singleton instance of the plugin */
38
	private static JSPCorePlugin plugin;
41
	private static JSPCorePlugin plugin;
39
	
40
	/** Save participant for this plugin */
41
	ISaveParticipant fSaveParticipant;
42
42
43
	/**
43
	/**
44
	 * <p>Job used to finish tasks needed to start up the plugin but that did not have
45
	 * to block the plugin start up process.</p>
46
	 */
47
	private Job fPluginInitializerJob;
48
	
49
	/**
44
	 * The constructor.
50
	 * The constructor.
45
	 */
51
	 */
46
	public JSPCorePlugin() {
52
	public JSPCorePlugin() {
47
		super();
53
		super();
48
		plugin = this;
54
		plugin = this;
49
		fSaveParticipant = new SaveParticipant();
55
		this.fPluginInitializerJob = new PluginInitializerJob();
50
	}
56
	}
51
57
52
	/**
58
	/**
Lines 65-71 Link Here
65
		super.start(context);
71
		super.start(context);
66
72
67
		/*
73
		/*
68
		 * JSPIndexManager depends on TaglibController, so TaglibController
74
		 * JSPIndexManager_old depends on TaglibController, so TaglibController
69
		 * should be started first
75
		 * should be started first
70
		 */
76
		 */
71
		TaglibIndex.startup();
77
		TaglibIndex.startup();
Lines 73-109 Link Here
73
79
74
		// listen for classpath changes
80
		// listen for classpath changes
75
		JavaCore.addElementChangedListener(TaglibHelperManager.getInstance());
81
		JavaCore.addElementChangedListener(TaglibHelperManager.getInstance());
76
77
		//restore save state and process any events that happened before plugin loaded
78
		if (JSPTranslatorPersister.ACTIVATED) {
79
			Job persister = new Job(JSPCoreMessages.Initializing) {
80
				protected IStatus run(IProgressMonitor monitor) {
81
					ISavedState savedState = null;
82
					try {
83
						savedState = ResourcesPlugin.getWorkspace().addSaveParticipant(plugin.getBundle().getSymbolicName(), fSaveParticipant);
84
					}
85
					catch (CoreException e) {
86
						Logger.logException("Could not load previous save state", e);
87
					}
88
					if (savedState != null) {
89
						try {
90
							Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
91
						}
92
						finally {
93
							savedState.processResourceChangeEvents(JSPTranslatorPersister.getDefault());
94
						}
95
					}
96
					return Status.OK_STATUS;
97
				}
98
			};
99
			persister.setUser(false);
100
			persister.schedule(2000);
101
			// set up persister to listen to resource change events
102
			ResourcesPlugin.getWorkspace().addResourceChangeListener(JSPTranslatorPersister.getDefault());
103
		}
104
		
82
		
105
		//init the JSP index
83
		//schedule delayed initialization
106
		JSPIndexManager.getInstance().initialize();
84
		this.fPluginInitializerJob.schedule(2000);
107
85
108
		// listen for resource changes to update content properties keys
86
		// listen for resource changes to update content properties keys
109
		JSPFContentPropertiesManager.startup();
87
		JSPFContentPropertiesManager.startup();
Lines 119-140 Link Here
119
	public void stop(BundleContext context) throws Exception {
97
	public void stop(BundleContext context) throws Exception {
120
		DeploymentDescriptorPropertyCache.stop();
98
		DeploymentDescriptorPropertyCache.stop();
121
99
122
		/*
100
		// stop listening for resource changes to update content properties keys
123
		 * stop listening for resource changes to update content properties
124
		 * keys
125
		 */
126
		JSPFContentPropertiesManager.shutdown();
101
		JSPFContentPropertiesManager.shutdown();
127
102
128
		//remove the plugin save participant
103
		//remove the plugin save participant
129
		ResourcesPlugin.getWorkspace().removeSaveParticipant(plugin.getBundle().getSymbolicName());
104
		ResourcesPlugin.getWorkspace().removeSaveParticipant(plugin.getBundle().getSymbolicName());
130
		
105
		
131
		//remove the translator persister
132
		if(JSPTranslatorPersister.ACTIVATED) {
133
			ResourcesPlugin.getWorkspace().removeResourceChangeListener(JSPTranslatorPersister.getDefault());
134
		}
135
		
136
		// stop any indexing
106
		// stop any indexing
137
		JSPIndexManager.getInstance().shutdown();
107
		JSPIndexManager.getDefault().stop();
138
108
139
		// stop listening for classpath changes
109
		// stop listening for classpath changes
140
		JavaCore.removeElementChangedListener(TaglibHelperManager.getInstance());
110
		JavaCore.removeElementChangedListener(TaglibHelperManager.getInstance());
Lines 147-152 Link Here
147
	}
117
	}
148
	
118
	
149
	/**
119
	/**
120
	 * <p>A {@link Job} used to perform delayed initialization for the plugin</p>
121
	 */
122
	private static class PluginInitializerJob extends Job {
123
		/**
124
		 * <p>Default constructor to set up this {@link Job} as a
125
		 * long running system {@link Job}</p>
126
		 */
127
		protected PluginInitializerJob() {
128
			super(JSPCoreMessages.JSPCorePlugin_Initializing_JSP_Tools);
129
			
130
			this.setUser(false);
131
			this.setSystem(true);
132
			this.setPriority(Job.LONG);
133
		}
134
		
135
		/**
136
		 * <p>Perform delayed initialization for the plugin</p>
137
		 * 
138
		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
139
		 */
140
		protected IStatus run(IProgressMonitor monitor) {
141
			IStatus status = Status.OK_STATUS;
142
			final IWorkspace workspace = ResourcesPlugin.getWorkspace();
143
			try {
144
				workspace.run(new IWorkspaceRunnable() {
145
					public void run(final IProgressMonitor worspaceMonitor) throws CoreException {
146
						ISavedState savedState = null;
147
						
148
						try {
149
							//add the save participant for this bundle
150
							savedState = ResourcesPlugin.getWorkspace().addSaveParticipant(
151
									JSPCorePlugin.plugin.getBundle().getSymbolicName(), new SaveParticipant());
152
						} catch (CoreException e) {
153
							Logger.logException("JSP Core Plugin failed at loading previously saved state." +
154
									" All componenets dependent on this state will start as if first workspace load.", e);
155
						}
156
						
157
						//if there is a saved state start up using that, else start up cold
158
						if(savedState != null) {
159
							try {
160
								Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
161
							} finally {
162
								savedState.processResourceChangeEvents(new IResourceChangeListener() {
163
									/**
164
									 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
165
									 */
166
									public void resourceChanged(IResourceChangeEvent event) {
167
										JSPIndexManager.getDefault().start(event.getDelta(), worspaceMonitor);
168
									}
169
								});
170
							}
171
						} else {
172
							JSPIndexManager.getDefault().start(null, worspaceMonitor);
173
						}
174
					}
175
				}, monitor);
176
			} catch(CoreException e) {
177
				status = e.getStatus();
178
			}
179
			
180
			return status;
181
		}
182
		
183
	}
184
	
185
	/**
150
	 * Used so that all of the IResourceChangeEvents that occurred before
186
	 * Used so that all of the IResourceChangeEvents that occurred before
151
	 * this plugin loaded can be processed.
187
	 * this plugin loaded can be processed.
152
	 */
188
	 */
(-)src/org/eclipse/jst/jsp/core/internal/JSPCorePluginResources.properties (-1 / +4 lines)
Lines 47-50 Link Here
47
TLDValidator_MissingVariable=The variable class '{0}' was not found on the Java Build Path
47
TLDValidator_MissingVariable=The variable class '{0}' was not found on the Java Build Path
48
TLDValidator_MissingListener=The listener class '{0}' was not found on the Java Build Path
48
TLDValidator_MissingListener=The listener class '{0}' was not found on the Java Build Path
49
Initializing=Processing JSP changes since last activation
49
Initializing=Processing JSP changes since last activation
50
Persisting_JSP_Translations=Persisting JSP Translations
50
Persisting_JSP_Translations=Persisting JSP Translations
51
52
JSPCorePlugin_Initializing_JSP_Tools=Initializing JSP Tools
53
JSPIndexManager=JSP Index Manager
(-)src/org/eclipse/jst/jsp/core/internal/java/JSPTranslation.java (-5 / +18 lines)
Lines 16-22 Link Here
16
import java.util.Iterator;
16
import java.util.Iterator;
17
import java.util.List;
17
import java.util.List;
18
18
19
import org.eclipse.core.resources.IFile;
20
import org.eclipse.core.resources.WorkspaceJob;
19
import org.eclipse.core.resources.WorkspaceJob;
21
import org.eclipse.core.runtime.CoreException;
20
import org.eclipse.core.runtime.CoreException;
22
import org.eclipse.core.runtime.IProgressMonitor;
21
import org.eclipse.core.runtime.IProgressMonitor;
Lines 29-35 Link Here
29
import org.eclipse.jdt.core.ICompilationUnit;
28
import org.eclipse.jdt.core.ICompilationUnit;
30
import org.eclipse.jdt.core.IJavaElement;
29
import org.eclipse.jdt.core.IJavaElement;
31
import org.eclipse.jdt.core.IJavaProject;
30
import org.eclipse.jdt.core.IJavaProject;
32
import org.eclipse.jdt.core.JavaCore;
31
import org.eclipse.jdt.core.IPackageFragment;
32
import org.eclipse.jdt.core.IPackageFragmentRoot;
33
import org.eclipse.jdt.core.JavaModelException;
33
import org.eclipse.jdt.core.JavaModelException;
34
import org.eclipse.jdt.core.WorkingCopyOwner;
34
import org.eclipse.jdt.core.WorkingCopyOwner;
35
import org.eclipse.jface.text.Position;
35
import org.eclipse.jface.text.Position;
Lines 436-444 Link Here
436
			return null;
436
			return null;
437
		
437
		
438
		final String name = getClassname() + ".java";
438
		final String name = getClassname() + ".java";
439
		IFile fakeFile = je.getProject().getFile(name);
439
		IPackageFragmentRoot packageFragmentRoot = null;
440
		ICompilationUnit fakeUnit = JavaCore.createCompilationUnitFrom(fakeFile);
440
		IPackageFragmentRoot[] packageFragmentRoots = je.getPackageFragmentRoots();
441
		ICompilationUnit cu = fakeUnit.getWorkingCopy(getWorkingCopyOwner(), getProgressMonitor());
441
		for (int i = 0; i < packageFragmentRoots.length; i++) {
442
			if (!packageFragmentRoots[i].isArchive() && !packageFragmentRoots[i].isExternal()) {
443
				packageFragmentRoot = packageFragmentRoots[i];
444
				break;
445
			}
446
		}
447
		if (packageFragmentRoot == null) {
448
			if(DEBUG) {
449
				System.out.println("** Abort create working copy: cannot create working copy: JSP is not in a Java project with source package fragment root"); //$NON-NLS-1$
450
			}
451
			return null;
452
		}
453
		final IPackageFragment fragment = packageFragmentRoot.getPackageFragment(IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH);
454
		ICompilationUnit cu = fragment.getCompilationUnit(name).getWorkingCopy(getWorkingCopyOwner(), getProgressMonitor());
442
		setContents(cu);
455
		setContents(cu);
443
456
444
		if(DEBUG) {
457
		if(DEBUG) {
(-)src/org/eclipse/jst/jsp/core/internal/java/JSPTranslationAdapter.java (-1 / +2 lines)
Lines 115-121 Link Here
115
		fDocumentIsDirty = true;
115
		fDocumentIsDirty = true;
116
	}
116
	}
117
117
118
	public void release() {
118
	public synchronized void release() {
119
119
120
		if (fJspDocument != null)
120
		if (fJspDocument != null)
121
			fJspDocument.removeDocumentListener(this);
121
			fJspDocument.removeDocumentListener(this);
Lines 129-134 Link Here
129
				System.out.println("JSPTranslationAdapter releasing:" + fJSPTranslation); //$NON-NLS-1$
129
				System.out.println("JSPTranslationAdapter releasing:" + fJSPTranslation); //$NON-NLS-1$
130
130
131
			fJSPTranslation.release();
131
			fJSPTranslation.release();
132
			fJSPTranslation = null;
132
		}
133
		}
133
	}
134
	}
134
135
(-)src/org/eclipse/jst/jsp/core/internal/java/JSPTranslatorPersister.java (-365 / +118 lines)
Lines 17-47 Link Here
17
import java.io.InvalidClassException;
17
import java.io.InvalidClassException;
18
import java.io.ObjectInputStream;
18
import java.io.ObjectInputStream;
19
import java.io.ObjectOutputStream;
19
import java.io.ObjectOutputStream;
20
import java.util.LinkedList;
21
import java.util.zip.CRC32;
20
import java.util.zip.CRC32;
22
21
23
import org.eclipse.core.resources.IFile;
22
import org.eclipse.core.resources.IFile;
24
import org.eclipse.core.resources.IResource;
23
import org.eclipse.core.resources.IResource;
25
import org.eclipse.core.resources.IResourceChangeEvent;
26
import org.eclipse.core.resources.IResourceChangeListener;
27
import org.eclipse.core.resources.IResourceDelta;
28
import org.eclipse.core.resources.IResourceDeltaVisitor;
29
import org.eclipse.core.resources.ResourcesPlugin;
24
import org.eclipse.core.resources.ResourcesPlugin;
30
import org.eclipse.core.runtime.CoreException;
25
import org.eclipse.core.runtime.CoreException;
31
import org.eclipse.core.runtime.IPath;
26
import org.eclipse.core.runtime.IPath;
32
import org.eclipse.core.runtime.IProgressMonitor;
33
import org.eclipse.core.runtime.ISafeRunnable;
34
import org.eclipse.core.runtime.IStatus;
35
import org.eclipse.core.runtime.Platform;
36
import org.eclipse.core.runtime.SafeRunner;
37
import org.eclipse.core.runtime.Status;
38
import org.eclipse.core.runtime.content.IContentType;
39
import org.eclipse.core.runtime.jobs.Job;
40
import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
41
import org.eclipse.jst.jsp.core.internal.JSPCorePlugin;
27
import org.eclipse.jst.jsp.core.internal.JSPCorePlugin;
42
import org.eclipse.jst.jsp.core.internal.Logger;
28
import org.eclipse.jst.jsp.core.internal.Logger;
29
import org.eclipse.jst.jsp.core.internal.java.search.JSPIndexManager;
43
import org.eclipse.jst.jsp.core.internal.modelhandler.ModelHandlerForJSP;
30
import org.eclipse.jst.jsp.core.internal.modelhandler.ModelHandlerForJSP;
44
import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
45
import org.eclipse.wst.sse.core.StructuredModelManager;
31
import org.eclipse.wst.sse.core.StructuredModelManager;
46
import org.eclipse.wst.sse.core.internal.FileBufferModelManager;
32
import org.eclipse.wst.sse.core.internal.FileBufferModelManager;
47
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
33
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
Lines 49-80 Link Here
49
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
35
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
50
36
51
/**
37
/**
52
 * <p>This {@link IResourceChangeListener} is used to keep the {@link JSPTranslator}s for JSP
38
 * <p>This is a static class used to persist JSP translations and retrieve the persisted
53
 * resources persisted to disk.  It can also be used to get persisted translators</p>
39
 * translations.</p>
54
 * <p>This class should be registered as an {@link IResourceChangeListener} on the Workspace
40
 * 
55
 * as well as processing resource change events from a saved state, for example use see below.<p>
41
 * <p>It is not actually in charge of finding files to persist, rather it provides API
56
 * <p><b>Plugin Activation:</b>
42
 * for some other mechanism that tracks JSP files to call into to persist the translations.</p>
57
 * <pre>
58
 * try {
59
 *   ISavedState savedState = ResourcesPlugin.getWorkspace().addSaveParticipant(
60
 *     plugin.getBundle().getSymbolicName(), this.fSaveParticipant);
61
 *   if (savedState != null) {
62
 *     savedState.processResourceChangeEvents(JSPTranslatorPersistor.getDefault());
63
 *   }
64
 * } catch(CoreException e) {}
65
 * ResourcesPlugin.getWorkspace().addResourceChangeListener(JSPTranslatorPersistor.getDefault());
66
 * </pre>
67
 * <b>Plugin Deactivation:</b>
68
 * <pre>
69
 * ResourcesPlugin.getWorkspace().removeSaveParticipant(plugin.getBundle().getSymbolicName());
70
 * ResourcesPlugin.getWorkspace().removeResourceChangeListener(JSPTranslatorPersistor.getDefault());
71
 * </pre></p>
72
 * 
43
 * 
73
 * <p>This class can be deactivated through the <code>persistJSPTranslations</code> system property,
44
 * <p>This class can be deactivated through the <code>persistJSPTranslations</code> system property,
74
 * a value of <code>true</code> means the persister is activated (which is the default), value of
45
 * a value of <code>true</code> means the persister is activated (which is the default), value of
75
 * <code>false</code> means the persister is not activated.</p>
46
 * <code>false</code> means the persister is not activated.</p>
47
 * 
48
 * @see JSPIndexManager
76
 */
49
 */
77
public class JSPTranslatorPersister implements IResourceChangeListener {
50
public class JSPTranslatorPersister{
78
	/**
51
	/**
79
	 * <code>true</code> if the persister is activated, <code>false</code>
52
	 * <code>true</code> if the persister is activated, <code>false</code>
80
	 * otherwise.  This is determined by checking the system property
53
	 * otherwise.  This is determined by checking the system property
Lines 90-152 Link Here
90
	/** used to calculate persisted translator file names */
63
	/** used to calculate persisted translator file names */
91
	private static final CRC32 CHECKSUM_CALC = new CRC32();
64
	private static final CRC32 CHECKSUM_CALC = new CRC32();
92
	
65
	
93
	/** singleton instance of the {@link JSPTranslatorPersister} */
94
	private static final JSPTranslatorPersister INSTANCE = new JSPTranslatorPersister();
95
	
96
	/**
97
	 * Used to handle resource change events
98
	 * @see #resourceChanged(IResourceChangeEvent)
99
	 */
100
	private IResourceDeltaVisitor fResourceDeltaVisitor;
101
	
102
	/** {@link Job} that actually does all the persisting */
103
	protected PersisterJob fPersisterJob;
104
	
105
	/**
66
	/**
106
	 * <p>Private singleton default constructor</p>
67
	 * <p>Private constructor to prevent creating an instance of this class</p>
107
	 */
68
	 */
108
	private JSPTranslatorPersister() {
69
	private JSPTranslatorPersister() {
109
		this.fResourceDeltaVisitor = new JSPResourceVisitor();
110
		this.fPersisterJob = new PersisterJob();
111
	}
112
	
113
	/**
114
	 * <p><b>NOTE: </b><i>This can possible return <code>null</code></i></p>
115
	 * 
116
	 * @return Singleton instance of the {@link JSPTranslatorPersister} if
117
	 * {@link #ACTIVATED} is <code>true</code>, <code>null</code> otherwise.
118
	 */
119
	public static JSPTranslatorPersister getDefault() {
120
		return ACTIVATED ? INSTANCE : null;
121
	}
122
	
123
	/**
124
	 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
125
	 */
126
	public void resourceChanged(IResourceChangeEvent event) {
127
		switch(event.getType()) {
128
			case IResourceChangeEvent.PRE_CLOSE:
129
			case IResourceChangeEvent.PRE_DELETE:
130
				//pre-close or pre-delete stop the persister job so it does not interfere
131
				this.fPersisterJob.stop();
132
				break;
133
			case IResourceChangeEvent.POST_CHANGE:
134
				//post change start up the persister job and process the delta
135
				this.fPersisterJob.start();
136
				
137
				// only analyze the full (starting at root) delta hierarchy
138
				IResourceDelta delta = event.getDelta();
139
				if (delta != null && delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$
140
					try {
141
						//use visitor to visit all children
142
						delta.accept(this.fResourceDeltaVisitor, false);
143
					} catch (CoreException e) {
144
						Logger.logException("Processing resource change event delta failed, " +
145
								"persisted JSPTranslators may not have been updated.", e);
146
					}
147
				}
148
				break;
149
		}
150
	}
70
	}
151
	
71
	
152
	/**
72
	/**
Lines 210-215 Link Here
210
	}
130
	}
211
	
131
	
212
	/**
132
	/**
133
	 * @param resource JSP resource who's translation should be persisted
134
	 */
135
	public static void persistTranslation(IResource resource) {
136
		if(ACTIVATED) {
137
			IPath path = resource.getFullPath();
138
			String filePath = getPersistedTranslatorFilePath(path.toPortableString());
139
			IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
140
	
141
			JSPTranslator translator = getJSPTranslator(file);
142
			if(translator != null) {
143
				persistTranslator(translator, filePath);
144
			}
145
		}
146
	}
147
	
148
	/**
149
	 * @param resource JSP resource who's translation should no longer be persisted
150
	 */
151
	public static void removePersistedTranslation(IResource resource) {
152
		if(ACTIVATED) {
153
			File file = getPersistedFile(resource.getFullPath());
154
			deletePersistedTranslator(file);
155
		}
156
	}
157
	
158
	/**
159
	 * @param resource JSP resource that has moved and thus its persisted translation should be updated
160
	 * @param fromPath Path the JSP resource moved from
161
	 */
162
	public static void movePersistedTranslation(IResource resource, IPath fromPath) {
163
		if(ACTIVATED) {
164
			File from = getPersistedFile(fromPath);
165
			File to = getPersistedFile(resource.getFullPath());
166
	
167
			renamePersistedTranslator(from, to);
168
		}
169
	}
170
	
171
	/**
213
	 * <p>Given the path to a JSP file determines the path to its persisted {@link JSPTranslator}</p>
172
	 * <p>Given the path to a JSP file determines the path to its persisted {@link JSPTranslator}</p>
214
	 * 
173
	 * 
215
	 * @param jspFilePath {@link IPath} to JSP file for which the path to its persisted {@link JSPTranslator}
174
	 * @param jspFilePath {@link IPath} to JSP file for which the path to its persisted {@link JSPTranslator}
Lines 218-224 Link Here
218
	 * @return OS file path to the persisted {@link JSPTranslator} associated with the JSP file at
177
	 * @return OS file path to the persisted {@link JSPTranslator} associated with the JSP file at
219
	 * <code>jspFilePath</code>
178
	 * <code>jspFilePath</code>
220
	 */
179
	 */
221
	protected static String getPersistedTranslatorFilePath(String jspFilePath) {
180
	private static String getPersistedTranslatorFilePath(String jspFilePath) {
222
		CHECKSUM_CALC.reset();
181
		CHECKSUM_CALC.reset();
223
		CHECKSUM_CALC.update(jspFilePath.getBytes());
182
		CHECKSUM_CALC.update(jspFilePath.getBytes());
224
		String persistedTranslatorFileName = Long.toString(CHECKSUM_CALC.getValue()) + ".translator"; //$NON-NLS-1$
183
		String persistedTranslatorFileName = Long.toString(CHECKSUM_CALC.getValue()) + ".translator"; //$NON-NLS-1$
Lines 240-529 Link Here
240
	}
199
	}
241
	
200
	
242
	/**
201
	/**
243
	 * @see JSPResourceVisitor#visit(IResourceDelta)
202
	 * <p>Gets the associated {@link JSPTranslator} for a specific JSP file.</p>
203
	 * <p><b>NOTE: </b><i>This does not get the persisted translator but rather the
204
	 * associated translator in memory</i></p>
205
	 * 
206
	 * @param jspFile {@link IFile} to the JSP file that the associated {@link JSPTranslator}
207
	 * is needed for
208
	 * @return {@link JSPTranslator} associated with the given <code>jspFilePath</code>, or
209
	 * <code>null</code> if none can be found.
244
	 */
210
	 */
245
	private class JSPResourceVisitor implements IResourceDeltaVisitor {
211
	private static JSPTranslator getJSPTranslator(IFile jspFile) {
246
		/**
212
		IStructuredModel model = null;
247
		 * <p>Default constructor</p>
213
		JSPTranslator translator = null;
248
		 */
214
		try {
249
		protected JSPResourceVisitor() {
215
			model = StructuredModelManager.getModelManager().getModelForRead(jspFile);
250
		}
216
			if(model instanceof IDOMModel) {
251
		
217
				IDOMDocument doc = ((IDOMModel)model).getDocument();
252
		/**
218
				ModelHandlerForJSP.ensureTranslationAdapterFactory(model);
253
		 * <p>For each {@link IResourceDelta} determine if its a JSP resource and if it is
219
				JSPTranslationAdapter adapter = (JSPTranslationAdapter)doc.getAdapterFor(IJSPTranslation.class);
254
		 * add the appropriate action to the {@link PersisterJob} so as not to hold up
220
				
255
		 * the {@link IResourceDelta}</p>
221
				//don't want to persist a translator that has not already been requested
256
		 * 
222
				if(adapter != null && adapter.hasTranslation()) {
257
		 * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
223
					translator = adapter.getJSPTranslation().getTranslator();
258
		 */
259
		public boolean visit(IResourceDelta delta) throws CoreException {
260
			if(isJSPResource(delta.getResource())) {
261
				switch (delta.getKind()) {
262
					case IResourceDelta.CHANGED :
263
					case IResourceDelta.ADDED : {
264
						/* if a move, then move the persisted translation
265
						 * else create a new persisted translation, if its a change then
266
						 *   the old persisted translation will be overwritten */
267
						if((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
268
							final File from = getPersistedFile(delta.getMovedFromPath());
269
							final File to = getPersistedFile(delta.getFullPath());
270
							//add the move action to the persister job
271
							JSPTranslatorPersister.this.fPersisterJob.addAction(new ISafeRunnable() {
272
								public void run() throws Exception {
273
									renamePersistedTranslator(from, to);
274
								}
275
276
								public void handleException(Throwable exception) {}
277
							});
278
						} else {
279
							final String filePath = getPersistedTranslatorFilePath(delta.getFullPath().toPortableString());
280
							final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(delta.getFullPath());
281
							//add the add action to the persister job
282
							JSPTranslatorPersister.this.fPersisterJob.addAction(new ISafeRunnable() {
283
								public void run() throws Exception {
284
									JSPTranslator translator = getJSPTranslator(file);
285
									if(translator != null) {
286
										persistTranslator(translator, filePath);
287
									}
288
								}
289
290
								public void handleException(Throwable exception) {}
291
							});
292
						}
293
						
294
						break;
295
					}
296
					case IResourceDelta.REMOVED : {
297
						/* only remove if its not a move,
298
						 * if it is a move the added file delta event will move translation */
299
						if((delta.getFlags() & IResourceDelta.MOVED_TO) == 0) {
300
							final File file = getPersistedFile(delta.getFullPath());
301
							//add the delete action to the persister job
302
							JSPTranslatorPersister.this.fPersisterJob.addAction(new ISafeRunnable() {
303
								public void run() throws Exception {
304
									deletePersistedTranslator(file);
305
								}
306
307
								public void handleException(Throwable exception) {}
308
							});
309
						}
310
						break;
311
					}
312
				}
313
			}
314
			
315
			//visit children deltas
316
			return true;
317
		}
318
		
319
		/**
320
		 * <p>Determines if an {@link IResource} is a JSP resource</p>
321
		 * 
322
		 * @param resource determine if this {@link IResource} is a JSP resource
323
		 * @return <code>true</code> if <code>resource</code> is a JSP resource,
324
		 * <code>false</code> otherwise.
325
		 */
326
		private boolean isJSPResource(IResource resource) {
327
			boolean isJSP = false;
328
			
329
			//general rule for getting files in the workspace
330
			if(resource.getFullPath().segmentCount() >= 2) {
331
				IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(resource.getFullPath());
332
				if(file.getType() == IResource.FILE) {
333
					//get JSP content type each time because there is a possibility it could change
334
					IContentType contentTypeJSP = Platform.getContentTypeManager().getContentType(
335
							ContentTypeIdForJSP.ContentTypeID_JSP);
336
					
337
					isJSP = contentTypeJSP.isAssociatedWith(file.getName());
338
				}
339
			}
340
			
341
			return isJSP;
342
		}
343
		
344
		/**
345
		 * <p>Gets the associated {@link JSPTranslator} for a specific JSP file.</p>
346
		 * <p><b>NOTE: </b><i>This does not get the persisted translator but rather the
347
		 * associated translator in memory</i></p>
348
		 * 
349
		 * @param jspFile {@link IFile} to the JSP file that the associated {@link JSPTranslator}
350
		 * is needed for
351
		 * @return {@link JSPTranslator} associated with the given <code>jspFilePath</code>, or
352
		 * <code>null</code> if none can be found.
353
		 */
354
		protected JSPTranslator getJSPTranslator(IFile jspFile) {
355
			IStructuredModel model = null;
356
			JSPTranslator translator = null;
357
			try {
358
				model = StructuredModelManager.getModelManager().getModelForRead(jspFile);
359
				if(model instanceof IDOMModel) {
360
					IDOMDocument doc = ((IDOMModel)model).getDocument();
361
					ModelHandlerForJSP.ensureTranslationAdapterFactory(model);
362
					JSPTranslationAdapter adapter = (JSPTranslationAdapter)doc.getAdapterFor(IJSPTranslation.class);
363
					
364
					//don't want to persist a translator that has not already been requested
365
					if(adapter != null && adapter.hasTranslation()) {
366
						translator = adapter.getJSPTranslation().getTranslator();
367
					}
368
				}
369
			} catch (IOException e) {
370
				Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$
371
						" because could not read model for same.", e); //$NON-NLS-1$
372
			} catch (CoreException e) {
373
				Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$
374
						" because could not read model for same.", e); //$NON-NLS-1$
375
			} finally {
376
				if(model != null) {
377
					model.releaseFromRead();
378
				}
224
				}
379
			}
225
			}
380
			
226
		} catch (IOException e) {
381
			return translator;
227
			Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$
382
		}
228
					" because could not read model for same.", e); //$NON-NLS-1$
383
		
229
		} catch (CoreException e) {
384
		/**
230
			Logger.logException("Could not get translator for " + jspFile.getName() + //$NON-NLS-1$
385
		 * <p>Persists a {@link JSPTranslator} to disk for a specific JSP file</p>
231
					" because could not read model for same.", e); //$NON-NLS-1$
386
		 * 
232
		} finally {
387
		 * @param translator {@link JSPTranslator} to persist to disk
233
			if(model != null) {
388
		 * @param jspFilePath {@link IPath} to the JSP file the given <code>translator</code> is for
234
				model.releaseFromRead();
389
		 */
390
		protected void persistTranslator(JSPTranslator translator, String filePath) {
391
			try {
392
				FileOutputStream fos = new FileOutputStream(filePath);
393
				ObjectOutputStream out = new ObjectOutputStream(fos);
394
				out.writeObject(translator);
395
				out.close();
396
			} catch (IOException e) {
397
				Logger.logException("Was unable to externalize JSPTranslator " + translator + //$NON-NLS-1$
398
						" to " + filePath, e); //$NON-NLS-1$
399
			}
235
			}
400
		}
236
		}
401
		
237
		
402
		/**
238
		return translator;
403
		 * <p>Deletes a persisted translation for a JSP file that has been deleted</p>
404
		 * 
405
		 * @param jspFilePath {@link IPath} to the JSP file that has been deleted
406
		 */
407
		protected void deletePersistedTranslator(File file) {
408
			file.delete();
409
		}
410
		
411
		/**
412
		 * <p>Renames a persisted translation for a JSP file that has moved</p>
413
		 * 
414
		 * @param jspPrevFilePath {@link IPath} to the previous location of JSP file</p>
415
		 * @param jspNewFilePath {@link IPath} to new location of JSP file</p>
416
		 */
417
		protected void renamePersistedTranslator(File from, File to) {
418
			//do the move
419
			from.renameTo(to);
420
		}
421
422
		private File getPersistedFile(IPath path) {
423
			return new File(getPersistedTranslatorFilePath(path.toPortableString()));
424
		}
425
	}
239
	}
426
	
240
	
427
	/**
241
	/**
428
	 * <p>{@link Job} responsible for reacting to {@link IResourceDelta} visited
242
	 * <p>Persists a {@link JSPTranslator} to disk for a specific JSP file</p>
429
	 * by {@link JSPResourceVisitor}.  This way the actions that need to be taken
243
	 * 
430
	 * in reaction to the delta to not hold up the {@link IResourceChangeListener}
244
	 * @param translator {@link JSPTranslator} to persist to disk
431
	 * or the {@link IResourceDelta}.</p>
245
	 * @param jspFilePath {@link IPath} to the JSP file the given <code>translator</code> is for
432
	 *
246
	 */
433
	 */
247
	private static void persistTranslator(JSPTranslator translator, String filePath) {
434
	private class PersisterJob extends Job {
248
		try {
435
		/** Length to delay when scheduling job */
249
			FileOutputStream fos = new FileOutputStream(filePath);
436
		private static final int DELAY = 500;
250
			ObjectOutputStream out = new ObjectOutputStream(fos);
437
		
251
			out.writeObject(translator);
438
		/** 
252
			out.close();
439
		 *  <code>{@link LinkedList}&lt{@link ISafeRunnable}&gt</code>
253
		} catch (IOException e) {
440
		 * <p>The persister actions that have been queued up by the {@link JSPResourceVisitor}</p>
254
			Logger.logException("Was unable to externalize JSPTranslator " + translator + //$NON-NLS-1$
441
		 */
255
					" to " + filePath, e); //$NON-NLS-1$
442
		private LinkedList fActions;
443
		
444
		/** Whether this job has been stopped or not */
445
		private boolean fIsStopped;
446
		
447
		/**
448
		 * <p>Sets job up as a system job</p>
449
		 */
450
		protected PersisterJob() {
451
			super(JSPCoreMessages.Persisting_JSP_Translations);
452
			this.setUser(false);
453
			this.setSystem(true);
454
			this.setPriority(Job.LONG);
455
			
456
			this.fActions = new LinkedList();
457
			this.fIsStopped = false;
458
		}
459
		
460
		/**
461
		 * <p>Starts this job.  This has no effect if the job is already started.</p>
462
		 * <p>This should be used in place of {@link Job#schedule()} to reset state
463
		 * caused by calling {@link #stop()}</p>
464
		 * 
465
		 * @see #stop()
466
		 */
467
		protected synchronized void start() {
468
			this.fIsStopped = false;
469
			
470
			//get the job running again depending on its current state
471
			if(this.getState() == Job.SLEEPING) {
472
				this.wakeUp(DELAY);
473
			} else {
474
				this.schedule(DELAY);
475
			}
476
		}
477
		
478
		/**
479
		 * <p>Stops this job, even if it is running</p>
480
		 * <p>This should be used in place of {@link Job#sleep()} because {@link Job#sleep()}
481
		 * will not stop a job that is already running but calling this will stop this job
482
		 * even if it is running. {@link #start()} must be used to start this job again</p>
483
		 * 
484
		 * @see #start()
485
		 */
486
		protected synchronized void stop() {
487
			//sleep the job if it is waiting to run
488
			this.sleep();
489
			
490
			//if job is already running will force it to stop
491
			this.fIsStopped = true;
492
		}
493
		
494
		/**
495
		 * @param action {@link ISafeRunnable} containing a persister action to take
496
		 * based on an {@link IResourceDelta} processed by {@link JSPResourceVisitor}
497
		 */
498
		protected void addAction(ISafeRunnable action) {
499
			//add the action
500
			synchronized (this.fActions) {
501
				this.fActions.addLast(action);
502
			}
503
			
504
			//if job has not been manually stopped, then start it
505
			if(!this.fIsStopped) {
506
				this.start();
507
			}
508
		}
256
		}
257
	}
258
	
259
	/**
260
	 * <p>Deletes a persisted translation for a JSP file that has been deleted</p>
261
	 * 
262
	 * @param jspFilePath {@link IPath} to the JSP file that has been deleted
263
	 */
264
	private static void deletePersistedTranslator(File file) {
265
		file.delete();
266
	}
267
	
268
	/**
269
	 * <p>Renames a persisted translation for a JSP file that has moved</p>
270
	 * 
271
	 * @param jspPrevFilePath {@link IPath} to the previous location of JSP file</p>
272
	 * @param jspNewFilePath {@link IPath} to new location of JSP file</p>
273
	 */
274
	private static void renamePersistedTranslator(File from, File to) {
275
		//do the move
276
		from.renameTo(to);
277
	}
509
278
510
		/**
279
	private static File getPersistedFile(IPath path) {
511
		 * <p>Process the actions until there are none left</p>
280
		return new File(getPersistedTranslatorFilePath(path.toPortableString()));
512
		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
513
		 */
514
		protected IStatus run(IProgressMonitor monitor) {
515
			/*  run so long as job has not been stopped,
516
			 *monitor canceled, and have actions to process */
517
			while(!this.fIsStopped && !monitor.isCanceled() && !this.fActions.isEmpty()) {
518
				ISafeRunnable action;
519
				synchronized (this.fActions) {
520
					action = (ISafeRunnable)this.fActions.removeFirst();
521
				}
522
				
523
				SafeRunner.run(action);
524
			}
525
			
526
			return Status.OK_STATUS;
527
		}
528
	}
281
	}
529
}
282
}
(-)src/org/eclipse/jst/jsp/core/internal/java/search/IndexWorkspaceJob.java (-163 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004, 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jst.jsp.core.internal.java.search;
12
13
import java.util.ArrayList;
14
import java.util.List;
15
16
import org.eclipse.core.resources.IFile;
17
import org.eclipse.core.resources.IResource;
18
import org.eclipse.core.resources.IResourceProxy;
19
import org.eclipse.core.resources.IResourceProxyVisitor;
20
import org.eclipse.core.resources.ResourcesPlugin;
21
import org.eclipse.core.runtime.CoreException;
22
import org.eclipse.core.runtime.IProgressMonitor;
23
import org.eclipse.core.runtime.IStatus;
24
import org.eclipse.core.runtime.Platform;
25
import org.eclipse.core.runtime.Status;
26
import org.eclipse.core.runtime.content.IContentType;
27
import org.eclipse.core.runtime.jobs.Job;
28
import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
29
import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
30
31
/**
32
 * Re-indexes the entire workspace.
33
 * Ensures the JSP Index is in a stable state before performing a search.
34
 * (like after a crash or if previous indexing was canceled)
35
 * 
36
 * @author pavery
37
 */
38
public class IndexWorkspaceJob extends Job {
39
40
	// for debugging
41
	static final boolean DEBUG;
42
	static {
43
		String value= Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/jspindexmanager"); //$NON-NLS-1$
44
		DEBUG= value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$
45
	}
46
	
47
	/**
48
	 * Visitor that retrieves jsp project paths for all jsp files in the workspace,
49
	 * and adds the files to be indexed as they are encountered
50
	 */
51
	private class JSPFileVisitor implements IResourceProxyVisitor {
52
	    private List files = new ArrayList(); 
53
		
54
		// monitor from the Job
55
		IProgressMonitor fInnerMonitor = null;
56
		public JSPFileVisitor(IProgressMonitor monitor) {
57
			this.fInnerMonitor = monitor;
58
		}
59
		
60
		public boolean visit(IResourceProxy proxy) throws CoreException {
61
			
62
			// check job canceled
63
			if (this.fInnerMonitor != null && this.fInnerMonitor.isCanceled()) {
64
				setCanceledState();
65
				return false;
66
			}
67
			
68
			// check search support canceled
69
			if(JSPSearchSupport.getInstance().isCanceled()) {
70
				setCanceledState();
71
				return false;
72
			}
73
			
74
			if (proxy.getType() == IResource.FILE) {
75
				
76
				// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3553
77
				// check this before description
78
				// check name before actually getting the file (less work)
79
				if(getJspContentType().isAssociatedWith(proxy.getName())) {
80
					IFile file = (IFile) proxy.requestResource();
81
					if(file.exists()) {
82
						
83
						if(DEBUG)
84
							System.out.println("(+) IndexWorkspaceJob adding file: " + file.getName()); //$NON-NLS-1$
85
						// this call will check the ContentTypeDescription, so don't need to do it here.
86
						//JSPSearchSupport.getInstance().addJspFile(file);
87
						this.files.add(file);
88
						this.fInnerMonitor.subTask(proxy.getName());
89
						
90
						// don't search deeper for files
91
						return false;
92
					}
93
				}
94
			}
95
			return true;
96
		}
97
		
98
		public final IFile[] getFiles() {
99
		    return (IFile[])this.files.toArray(new IFile[this.files.size()]);
100
		}
101
	}
102
	
103
	private IContentType fContentTypeJSP = null;
104
	
105
	public IndexWorkspaceJob() {
106
		// pa_TODO may want to say something like "Rebuilding JSP Index" to be more
107
		// descriptive instead of "Updating JSP Index" since they are 2 different things
108
		super(JSPCoreMessages.JSPIndexManager_0);
109
		setPriority(Job.LONG);
110
		setSystem(true);
111
	}
112
113
	IContentType getJspContentType() {
114
		if(this.fContentTypeJSP == null)
115
			this.fContentTypeJSP = Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP);
116
		return this.fContentTypeJSP;
117
	}
118
	
119
	/**
120
	 * @see org eclipse.core.internal.jobs.InternalJob#run(org.eclipse.core.runtime.IProgressMonitor) 
121
	 * for similar method
122
	 */
123
	protected IStatus run(IProgressMonitor monitor) {
124
		
125
		IStatus status = Status.OK_STATUS;
126
		Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
127
		
128
		if(monitor.isCanceled()) {
129
			setCanceledState();
130
			return Status.CANCEL_STATUS;
131
		}
132
		
133
		if(DEBUG)
134
			System.out.println(" ^ IndexWorkspaceJob started: "); //$NON-NLS-1$
135
		
136
		long start = System.currentTimeMillis();
137
		
138
		try {
139
		    JSPFileVisitor visitor = new JSPFileVisitor(monitor);
140
		    // collect all jsp files
141
			ResourcesPlugin.getWorkspace().getRoot().accept(visitor, IResource.DEPTH_INFINITE);
142
			// request indexing
143
			// this is pretty much like faking an entire workspace resource delta
144
			JSPIndexManager.getInstance().indexFiles(visitor.getFiles());
145
		}
146
		catch (CoreException e) {
147
			if(DEBUG)
148
				e.printStackTrace();
149
		}
150
		finally {
151
			monitor.done();
152
		}
153
		long finish = System.currentTimeMillis();
154
		if(DEBUG)
155
			System.out.println(" ^ IndexWorkspaceJob finished\n   total time running: " + (finish - start)); //$NON-NLS-1$
156
		
157
		return status;
158
	}
159
	
160
	void setCanceledState() {
161
		JSPIndexManager.getInstance().setIndexState(JSPIndexManager.S_CANCELED);
162
	}
163
}
(-)src/org/eclipse/jst/jsp/core/internal/java/search/JSPIndexManager.java (-674 / +95 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2004, 2010 IBM Corporation and others.
2
 * Copyright (c) 2010 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 7-731 Link Here
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
9
 *     IBM Corporation - initial API and implementation
10
 *     
10
 *******************************************************************************/
11
 *******************************************************************************/
11
package org.eclipse.jst.jsp.core.internal.java.search;
12
package org.eclipse.jst.jsp.core.internal.java.search;
12
13
13
import java.io.File;
14
import java.io.File;
14
import java.util.ArrayList;
15
import java.util.HashMap;
16
import java.util.List;
17
15
18
import org.eclipse.core.resources.IFile;
16
import org.eclipse.core.resources.IFile;
19
import org.eclipse.core.resources.IProject;
17
import org.eclipse.core.resources.IProject;
20
import org.eclipse.core.resources.IResource;
18
import org.eclipse.core.resources.IResource;
21
import org.eclipse.core.resources.IResourceChangeEvent;
22
import org.eclipse.core.resources.IResourceChangeListener;
23
import org.eclipse.core.resources.IResourceDelta;
24
import org.eclipse.core.resources.IResourceDeltaVisitor;
25
import org.eclipse.core.resources.ResourcesPlugin;
26
import org.eclipse.core.runtime.CoreException;
27
import org.eclipse.core.runtime.IPath;
19
import org.eclipse.core.runtime.IPath;
28
import org.eclipse.core.runtime.IProgressMonitor;
29
import org.eclipse.core.runtime.IStatus;
30
import org.eclipse.core.runtime.Platform;
20
import org.eclipse.core.runtime.Platform;
31
import org.eclipse.core.runtime.Plugin;
32
import org.eclipse.core.runtime.Status;
33
import org.eclipse.core.runtime.content.IContentType;
21
import org.eclipse.core.runtime.content.IContentType;
34
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
35
import org.eclipse.core.runtime.jobs.Job;
36
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
37
import org.eclipse.jdt.core.IJavaProject;
22
import org.eclipse.jdt.core.IJavaProject;
38
import org.eclipse.jdt.core.JavaCore;
23
import org.eclipse.jdt.core.JavaCore;
39
import org.eclipse.jdt.internal.core.JavaModelManager;
40
import org.eclipse.jdt.internal.core.index.Index;
41
import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
42
import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
24
import org.eclipse.jst.jsp.core.internal.JSPCoreMessages;
43
import org.eclipse.jst.jsp.core.internal.JSPCorePlugin;
25
import org.eclipse.jst.jsp.core.internal.JSPCorePlugin;
44
import org.eclipse.jst.jsp.core.internal.Logger;
26
import org.eclipse.jst.jsp.core.internal.Logger;
27
import org.eclipse.jst.jsp.core.internal.java.JSPTranslatorPersister;
45
import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
28
import org.eclipse.jst.jsp.core.internal.provisional.contenttype.ContentTypeIdForJSP;
46
import org.eclipse.osgi.util.NLS;
29
import org.eclipse.wst.sse.core.indexing.AbstractIndexManager;
47
import org.osgi.framework.Bundle;
48
30
49
/**
31
/**
50
 * Responsible for keeping the JSP index up to date.
32
 * <p>Index manger used to update the JDT index with the Java translations
33
 * of JSPs.</p>
51
 * 
34
 * 
52
 * @author pavery
35
 * <p>Also keeps JSP persistence up to date</p>
36
 * 
37
 * <p>Any action that needs the JDT index to have all of the latest JSP changes processed
38
 * should wait for this manger to report that it is consistent,
39
 * {@link #waitForConsistant(org.eclipse.core.runtime.IProgressMonitor)}.  Such actions
40
 * include but are not limited to searching and refactoring JSPs.</p>
53
 */
41
 */
54
public class JSPIndexManager {
42
public class JSPIndexManager extends AbstractIndexManager {
55
43
	/** the singleton instance of the {@link JSPIndexManager} */
56
	// for debugging
44
	private static JSPIndexManager INSTANCE;
57
	// TODO move this to Logger, as we have in SSE
45
	
58
	static final boolean DEBUG;
46
	/** the JSP {@link IContentType} */
59
	static {
47
	private static final IContentType JSP_CONTENT_TYPE =
60
		String value = Platform.getDebugOption("org.eclipse.jst.jsp.core/debug/jspindexmanager"); //$NON-NLS-1$
48
		Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP);
61
		DEBUG = value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$
49
	
62
	}
50
	/** the location to store state */
63
51
	private IPath fWorkingLocation;
64
	private static final String PKEY_INDEX_STATE = "jspIndexState"; //$NON-NLS-1$
52
	
65
66
	private IndexWorkspaceJob indexingJob = new IndexWorkspaceJob();
67
68
69
70
	// TODO: consider enumeration for these int constants
71
	// set to S_UPDATING once a resource change comes in
72
	// set to S_STABLE if:
73
	// - we know we aren't interested in the resource change
74
	// - or the ProcessFilesJob completes
75
	// set to S_CANCELED if an indexing job is canceled
76
	// set to S_REBUILDING if re-indexing the entire workspace
77
78
	// the int '0' is reserved for the default value if a preference is not
79
	// there
80
	/** index is reliable to use */
81
	public static final int S_STABLE = 1;
82
	/** index is being updated (from a resource delta) */
83
	public static final int S_UPDATING = 2;
84
	/** entire index is being rebuilt */
85
	public static final int S_REBUILDING = 3;
86
	/**
87
	 * indexing job was canceled in the middle of it, index needs to be
88
	 * rebuilt
89
	 */
90
	public static final int S_CANCELED = 4;
91
92
	/** symbolic name for OSGI framework */
93
	private final String OSGI_FRAMEWORK_ID = "org.eclipse.osgi"; //$NON-NLS-1$
94
95
	/**
96
	 * Collects JSP files from a resource delta.
97
	 */
98
	private class JSPResourceVisitor implements IResourceDeltaVisitor {
99
		// using hash map ensures only one of each file
100
		// must be reset before every use
101
		private HashMap jspFiles = null;
102
103
		public JSPResourceVisitor() {
104
			this.jspFiles = new HashMap();
105
		}
106
107
		public boolean visit(IResourceDelta delta) throws CoreException {
108
109
			// in case JSP search was canceled (eg. when closing the editor)
110
			if (JSPSearchSupport.getInstance().isCanceled() || frameworkIsShuttingDown()) {
111
				setCanceledState();
112
				return false;
113
			}
114
115
			try {
116
				if (!isHiddenResource(delta.getFullPath())) {
117
118
					int kind = delta.getKind();
119
					boolean added = (kind & IResourceDelta.ADDED) == IResourceDelta.ADDED;
120
					boolean isInterestingChange = false;
121
					if ((kind & IResourceDelta.CHANGED) == IResourceDelta.CHANGED) {
122
						int flags = delta.getFlags();
123
						// ignore things like marker changes
124
						isInterestingChange = (flags & IResourceDelta.CONTENT) == IResourceDelta.CONTENT || (flags & IResourceDelta.REPLACED) == IResourceDelta.REPLACED;
125
					}
126
					boolean removed = (kind & IResourceDelta.REMOVED) == IResourceDelta.REMOVED;
127
					if (added || isInterestingChange) {
128
129
						visitAdded(delta);
130
					}
131
					else if (removed) {
132
						visitRemoved(delta);
133
					}
134
				}
135
			}
136
			catch (Exception e) {
137
				// need to set state here somehow, and reindex
138
				// otherwise index will be unreliable
139
				if (DEBUG)
140
					Logger.logException("Delta analysis may not be complete", e); //$NON-NLS-1$
141
			}
142
			// if the delta has children, continue to add/remove files
143
			return true;
144
		}
145
146
		private void visitRemoved(IResourceDelta delta) {
147
			// handle cleanup
148
			if (delta.getResource() != null) {
149
				IResource r = delta.getResource();
150
				if (r.getType() == IResource.FOLDER && r.exists()) {
151
					deleteIndex((IFile) r);
152
				}
153
			}
154
		}
155
156
		private void visitAdded(IResourceDelta delta) {
157
			// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3553
158
			// quick check if it's even JSP related to improve
159
			// performance
160
			// checking name from the delta before getting
161
			// resource because it's lighter
162
			String filename = delta.getFullPath().lastSegment();
163
			if (filename != null && getJspContentType().isAssociatedWith(filename)) {
164
				IResource r = delta.getResource();
165
				if (r != null && r.exists() && r.getType() == IResource.FILE) {
166
					this.jspFiles.put(r.getFullPath(), r);
167
				}
168
			}
169
		}
170
171
		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=93463
172
		private boolean isHiddenResource(IPath p) {
173
			String[] segments = p.segments();
174
			for (int i = 0; i < segments.length; i++) {
175
				if (segments[i].startsWith(".")) //$NON-NLS-1$
176
					return true;
177
			}
178
			return false;
179
		}
180
181
		private void deleteIndex(IFile folder) {
182
			// cleanup index
183
			IndexManager im = JavaModelManager.getIndexManager();
184
			IPath folderPath = folder.getFullPath();
185
			IPath indexLocation = JSPSearchSupport.getInstance().computeIndexLocation(folderPath);
186
			im.removeIndex(indexLocation);
187
			// im.indexLocations.removeKey(folderPath);
188
			// im.indexLocations.removeValue(indexLocation);
189
			File f = indexLocation.toFile();
190
			f.delete();
191
		}
192
193
		public IFile[] getFiles() {
194
			return (IFile[]) this.jspFiles.values().toArray(new IFile[this.jspFiles.size()]);
195
		}
196
197
		public void reset() {
198
			this.jspFiles.clear();
199
		}
200
	}
201
202
	// end class JSPResourceVisitor
203
204
	/**
53
	/**
205
	 * schedules JSP files for indexing by Java core
54
	 * <p>Private singleton constructor</p>
206
	 */
55
	 */
207
	private class ProcessFilesJob extends Job {
208
		List fileList = null;
209
		// keep track of how many files we've indexed
210
		int lastFileCursor = 0;
211
212
		ProcessFilesJob(String taskName) {
213
			super(taskName);
214
			fileList = new ArrayList();
215
		}
216
217
		synchronized void process(IFile[] files) {
218
			for (int i = 0; i < files.length; i++) {
219
				fileList.add(files[i]);
220
			}
221
			if (DEBUG) {
222
				System.out.println("JSPIndexManager queuing " + files.length + " files"); //$NON-NLS-2$ //$NON-NLS-1$
223
			}
224
		}
225
226
		synchronized IFile[] getFiles() {
227
			return (IFile[]) fileList.toArray(new IFile[fileList.size()]);
228
		}
229
230
		synchronized void clearFiles() {
231
			fileList.clear();
232
			lastFileCursor = 0;
233
			//System.out.println("cleared files");
234
		}
235
236
		protected IStatus run(IProgressMonitor monitor) {
237
			// System.out.println("indexer monitor" + monitor);
238
			if (isCanceled(monitor) || frameworkIsShuttingDown()) {
239
				setCanceledState();
240
				return Status.CANCEL_STATUS;
241
			}
242
243
			Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
244
			long start = System.currentTimeMillis();
245
246
			try {
247
				IFile[] filesToBeProcessed = getFiles();
248
249
				if (DEBUG) {
250
					System.out.println("JSPIndexManager indexing " + filesToBeProcessed.length + " files"); //$NON-NLS-2$ //$NON-NLS-1$
251
				}
252
				// API indicates that monitor is never null
253
				monitor.beginTask("", filesToBeProcessed.length); //$NON-NLS-1$
254
				JSPSearchSupport ss = JSPSearchSupport.getInstance();
255
				String processingNFiles = ""; //$NON-NLS-1$
256
257
258
				for (;lastFileCursor < filesToBeProcessed.length; lastFileCursor++) {
259
260
					if (isCanceled(monitor) || frameworkIsShuttingDown()) {
261
						setCanceledState();
262
						return Status.CANCEL_STATUS;
263
					}
264
					IFile file = filesToBeProcessed[lastFileCursor];
265
					try {
266
						IProject project = file.getProject();
267
						if (project != null) {
268
							IJavaProject jproject = JavaCore.create(project);
269
							if (jproject.exists()) {
270
								ss.addJspFile(file);
271
								if (DEBUG) {
272
									System.out.println("JSPIndexManager Job added file: " + file.getName()); //$NON-NLS-1$
273
								}
274
							}
275
							// JSP Indexer processing n files
276
							processingNFiles = NLS.bind(JSPCoreMessages.JSPIndexManager_2, new String[]{Integer.toString((filesToBeProcessed.length - lastFileCursor))});
277
							monitor.subTask(processingNFiles + " - " + file.getName()); //$NON-NLS-1$
278
							monitor.worked(1);
279
						}
280
					}
281
					catch (Exception e) {
282
						// RATLC00284776
283
						// ISSUE: we probably shouldn't be catching EVERY
284
						// exception, but
285
						// the framework only allows to return IStatus in
286
						// order to communicate
287
						// that something went wrong, which means the loop
288
						// won't complete, and we would hit the same problem
289
						// the next time.
290
						// 
291
						// a possible solution is to keep track of the
292
						// exceptions logged
293
						// and only log a certain amt of the same one,
294
						// otherwise skip it.
295
						if (!frameworkIsShuttingDown()) {
296
							String filename = file != null ? file.getFullPath().toString() : ""; //$NON-NLS-1$
297
							Logger.logException("JSPIndexer problem indexing:" + filename, e); //$NON-NLS-1$
298
						}
299
					}
300
				} // end for
301
			}
302
			finally {
303
				// just in case something didn't follow API (monitor is null)
304
				if (monitor != null)
305
					monitor.done();
306
			}
307
308
			// successfully finished, clear files list
309
			clearFiles();
310
			
311
			long finish = System.currentTimeMillis();
312
			long diff = finish - start;
313
			if (DEBUG) {
314
				fTotalTime += diff;
315
				System.out.println("============================================================================"); //$NON-NLS-1$
316
				System.out.println("this time: " + diff + " cumulative time for resource changed: " + fTotalTime); //$NON-NLS-1$ //$NON-NLS-2$
317
				System.out.println("============================================================================"); //$NON-NLS-1$
318
			}
319
			return Status.OK_STATUS;
320
		}
321
322
		private boolean isCanceled(IProgressMonitor runMonitor) {
323
324
			boolean canceled = false;
325
			// check specific monitor passed into run method (the progress
326
			// group in this case)
327
			// check main search support canceled
328
			if (runMonitor != null && runMonitor.isCanceled())
329
				canceled = true;
330
			else if (JSPSearchSupport.getInstance().isCanceled()) {
331
				canceled = true;
332
				if (runMonitor != null) {
333
					runMonitor.setCanceled(true);
334
				}
335
			}
336
			return canceled;
337
		}
338
		
339
	}
340
341
	// end class ProcessFilesJob
342
343
	private static JSPIndexManager fSingleton = null;
344
	private boolean initialized;
345
	private boolean initializing = true;
346
347
	private IndexJobCoordinator indexJobCoordinator;
348
	private IResourceChangeListener jspResourceChangeListener;
349
350
	private JSPResourceVisitor fVisitor = null;
351
	private IContentType fContentTypeJSP = null;
352
353
	static long fTotalTime = 0;
354
355
	// Job for processing resource delta
356
	private ProcessFilesJob processFilesJob = null;
357
358
	private JSPIndexManager() {
56
	private JSPIndexManager() {
359
		processFilesJob = new ProcessFilesJob(JSPCoreMessages.JSPIndexManager_0);
57
		super(JSPCoreMessages.JSPIndexManager);
360
		// only show in verbose mode
361
		processFilesJob.setSystem(true);
362
		processFilesJob.setPriority(Job.LONG);
363
		processFilesJob.addJobChangeListener(new JobChangeAdapter() {
364
			public void done(IJobChangeEvent event) {
365
				super.done(event);
366
				setStableState();
367
			}
368
		});
369
	}
370
371
	public synchronized static JSPIndexManager getInstance() {
372
373
		if (fSingleton == null)
374
			fSingleton = new JSPIndexManager();
375
		return fSingleton;
376
	}
377
378
	public void initialize() {
379
380
		JSPIndexManager singleInstance = getInstance();
381
382
383
		if (!singleInstance.initialized) {
384
			singleInstance.initialized = true;
385
			singleInstance.initializing = true;
386
387
			singleInstance.indexJobCoordinator = new IndexJobCoordinator();
388
			singleInstance.jspResourceChangeListener = new JSPResourceChangeListener();
389
390
			// added as JobChange listener so JSPIndexManager can be smarter
391
			// about when it runs
392
			Platform.getJobManager().addJobChangeListener(singleInstance.indexJobCoordinator);
393
394
			// add JSPIndexManager to keep JSP Index up to date
395
			// listening for IResourceChangeEvent.PRE_DELETE and
396
			// IResourceChangeEvent.POST_CHANGE
397
			ResourcesPlugin.getWorkspace().addResourceChangeListener(jspResourceChangeListener, IResourceChangeEvent.POST_CHANGE);
398
399
			// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5091
400
			// makes sure IndexManager is aware of our indexes
401
			saveIndexes();
402
			singleInstance.initializing = false;
403
		}
404
405
	}
58
	}
406
	
59
	
407
	synchronized void setIndexState(int state) {
408
		if (DEBUG) {
409
			System.out.println("JSPIndexManager setting index state to: " + state2String(state)); //$NON-NLS-1$
410
		}
411
		Plugin jspModelPlugin = JSPCorePlugin.getDefault();
412
		jspModelPlugin.getPluginPreferences().setValue(PKEY_INDEX_STATE, state);
413
		jspModelPlugin.savePluginPreferences();
414
415
	}
416
417
	private String state2String(int state) {
418
		String s = "UNKNOWN"; //$NON-NLS-1$
419
		switch (state) {
420
			case (S_STABLE) :
421
				s = "S_STABLE"; //$NON-NLS-1$
422
				break;
423
			case (S_UPDATING) :
424
				s = "S_UPDATING"; //$NON-NLS-1$
425
				break;
426
			case (S_CANCELED) :
427
				s = "S_CANCELED"; //$NON-NLS-1$
428
				break;
429
			case (S_REBUILDING) :
430
				s = "S_REBUILDING"; //$NON-NLS-1$
431
				break;
432
		}
433
		return s;
434
	}
435
436
	int getIndexState() {
437
		return JSPCorePlugin.getDefault().getPluginPreferences().getInt(PKEY_INDEX_STATE);
438
	}
439
440
	void setUpdatingState() {
441
		//if (getIndexState() != S_CANCELED)
442
		setIndexState(S_UPDATING);
443
	}
444
445
	void setCanceledState() {
446
		setIndexState(JSPIndexManager.S_CANCELED);
447
	}
448
449
	void setStableState() {
450
		//if (getIndexState() != S_CANCELED)
451
		setIndexState(S_STABLE);
452
	}
453
454
	void setRebuildingState() {
455
		setIndexState(S_REBUILDING);
456
	}
457
458
	synchronized void rebuildIndexIfNeeded() {
459
		if (getIndexState() != S_STABLE) {
460
			rebuildIndex();
461
		}
462
	}
463
464
	void rebuildIndex() {
465
466
		if (DEBUG)
467
			System.out.println("*** JSP Index unstable, requesting re-indexing"); //$NON-NLS-1$
468
469
		getIndexingJob().addJobChangeListener(new JobChangeAdapter() {
470
			public void aboutToRun(IJobChangeEvent event) {
471
				super.aboutToRun(event);
472
				setRebuildingState();
473
			}
474
475
			public void done(IJobChangeEvent event) {
476
				super.done(event);
477
				setStableState();
478
				getIndexingJob().removeJobChangeListener(this);
479
			}
480
		});
481
		// we're about to reindex everything anyway
482
		getProcessFilesJob().clearFiles();
483
		getIndexingJob().schedule();
484
485
	}
486
487
	/**
60
	/**
488
	 * Creates and schedules a Job to process collected files. All JSP
61
	 * @return the singleton instance of the {@link JSPIndexManager}
489
	 * indexing should be done through this method or processFiles(IFile file)
490
	 * 
491
	 * @param files
492
	 */
62
	 */
493
	final void indexFiles(IFile[] files) {
63
	public static JSPIndexManager getDefault() {
494
		// don't use this rule
64
		return INSTANCE != null ? INSTANCE : (INSTANCE = new JSPIndexManager());
495
		// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4931
496
		// processFiles.setRule(new IndexFileRule());
497
		processFilesJob.process(files);
498
	}
65
	}
499
66
500
501
	/**
67
	/**
502
	 * Package protected for access by inner Job class in resourceChanged(...)
68
	 * @see indexer.internal.indexing.AbstractIndexManager#isResourceToIndex(int, java.lang.String)
503
	 * 
504
	 * @return
505
	 */
69
	 */
506
	JSPResourceVisitor getVisitor() {
70
	protected boolean isResourceToIndex(int type, String name) {
507
71
		return type == IResource.FILE && JSP_CONTENT_TYPE.isAssociatedWith(name);
508
		if (this.fVisitor == null) {
509
			this.fVisitor = new JSPResourceVisitor();
510
		}
511
		return this.fVisitor;
512
	}
513
514
	// https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5091
515
	// makes sure IndexManager is aware of our indexes
516
	void saveIndexes() {
517
		IndexManager indexManager = JavaModelManager.getIndexManager();
518
		IPath jspModelWorkingLocation = JSPSearchSupport.getInstance().getModelJspPluginWorkingLocation();
519
520
		File folder = new File(jspModelWorkingLocation.toOSString());
521
		String[] files = folder.list();
522
		String locay = ""; //$NON-NLS-1$
523
		Index index = null;
524
		try {
525
			for (int i = 0; i < files.length; i++) {
526
				if (files[i].toLowerCase().endsWith(".index")) { //$NON-NLS-1$
527
					locay = jspModelWorkingLocation.toString() + "/" + files[i]; //$NON-NLS-1$
528
					// reuse index file
529
					index = new Index(locay, "Index for " + locay, true); //$NON-NLS-1$
530
					indexManager.saveIndex(index);
531
				}
532
			}
533
		}
534
		catch (Exception e) {
535
			// we should be shutting down, want to shut down quietly
536
			if (DEBUG)
537
				e.printStackTrace();
538
		}
539
	}
540
541
	IContentType getJspContentType() {
542
		if (this.fContentTypeJSP == null)
543
			this.fContentTypeJSP = Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP);
544
		return this.fContentTypeJSP;
545
	}
72
	}
546
73
547
	/**
74
	/**
548
	 * A check to see if the OSGI framework is shutting down.
75
	 * @see indexer.internal.indexing.AbstractIndexManager#getWorkingLocation()
549
	 * 
550
	 * @return true if the System Bundle is stopped (ie. the framework is
551
	 *         shutting down)
552
	 */
76
	 */
553
	boolean frameworkIsShuttingDown() {
77
	protected IPath getWorkingLocation() {
554
		// in the Framework class there's a note:
78
		if(this.fWorkingLocation == null) {
555
		// set the state of the System Bundle to STOPPING.
79
			//create path to working area
556
		// this must be done first according to section 4.19.2 from the OSGi
80
    		IPath workignLocation =
557
		// R3 spec.
81
    			JSPCorePlugin.getDefault().getStateLocation().append("jspsearch"); //$NON-NLS-1$
558
		boolean shuttingDown = Platform.getBundle(OSGI_FRAMEWORK_ID).getState() == Bundle.STOPPING;
559
		if (DEBUG && shuttingDown) {
560
			System.out.println("JSPIndexManager: system is shutting down!"); //$NON-NLS-1$
561
		}
562
		return shuttingDown;
563
	}
564
565
82
566
	public void shutdown() {
83
            // ensure that it exists on disk
567
84
            File folder = new File(workignLocation.toOSString());
568
		// stop listening
85
    		if (!folder.isDirectory()) {
569
		ResourcesPlugin.getWorkspace().removeResourceChangeListener(jspResourceChangeListener);
86
    			try {
570
87
    				folder.mkdir();
571
88
    			}
572
		// stop any searching
89
    			catch (SecurityException e) {
573
		JSPSearchSupport.getInstance().setCanceled(true);
90
    				Logger.logException(this.fName +
574
91
    						": Error while creating state location: " + folder +
575
		// stop listening to jobs
92
    						" This renders the index manager irrevocably broken for this workspace session",
576
		Platform.getJobManager().removeJobChangeListener(indexJobCoordinator);
93
    						e);
577
94
    			}
578
95
    		}
579
		int maxwait = 5000;
96
    		
580
		if (processFilesJob != null) {
97
    		this.fWorkingLocation = workignLocation;
581
			processFilesJob.cancel();
98
    	}
582
		}
99
    	
583
		// attempt to make sure this indexing job is litterally
100
        return this.fWorkingLocation;
584
		// done before continuing, since we are shutting down
585
		waitTillNotRunning(maxwait, processFilesJob);
586
587
		if (indexingJob != null) {
588
			indexingJob.cancel();
589
		}
590
		waitTillNotRunning(maxwait, processFilesJob);
591
	}
592
593
	private void waitTillNotRunning(int maxSeconds, Job job) {
594
		int pauseTime = 10;
595
		int maxtries = maxSeconds / pauseTime;
596
		int count = 0;
597
		while (count++ < maxtries && job.getState() == Job.RUNNING) {
598
			try {
599
				Thread.sleep(pauseTime);
600
				// System.out.println("count: " + count + " max: " +
601
				// maxtries);
602
			}
603
			catch (InterruptedException e) {
604
				Logger.logException(e);
605
			}
606
		}
607
	}
101
	}
608
102
609
	private class IndexJobCoordinator extends JobChangeAdapter {
103
	/**
104
	 * @see indexer.internal.indexing.AbstractIndexManager#performAction(byte, byte, org.eclipse.core.resources.IResource, org.eclipse.core.runtime.IPath)
105
	 */
106
	protected void performAction(byte source, byte action, IResource resource,
107
			IPath movePath) {
610
		
108
		
611
		public void aboutToRun(IJobChangeEvent event) {
109
		//inform the persister of the action unless it come from a full workspace scan
612
			Job jobToCoordinate = event.getJob();
110
		if(JSPTranslatorPersister.ACTIVATED && source != AbstractIndexManager.SOURCE_WORKSPACE_SCAN) {
613
			if (isJobToAvoid(jobToCoordinate)) {
111
			switch(action) {
614
				// job will be rescheduled when the job we
112
				case AbstractIndexManager.ACTION_ADD: {
615
				// are avoiding (eg. build) is done
113
					JSPTranslatorPersister.persistTranslation(resource);
616
				getProcessFilesJob().cancel();
114
					break;
617
				//System.out.println("cancel:" + jobToCoordinate.getName());
115
				}
618
			}
116
				case AbstractIndexManager.ACTION_REMOVE: {
619
		}
117
					JSPTranslatorPersister.removePersistedTranslation(resource);
620
118
					break;
621
		public void done(IJobChangeEvent event) {
119
				}
622
120
				case AbstractIndexManager.ACTION_ADD_MOVE_FROM: {
623
			Job jobToCoordinate = event.getJob();
121
					JSPTranslatorPersister.movePersistedTranslation(resource, movePath);
624
			if (isJobToAvoid(jobToCoordinate)) {
122
					break;
625
				if (getProcessFilesJob().getFiles().length > 0) {
123
				}
626
					getProcessFilesJob().schedule(500);
124
				case AbstractIndexManager.ACTION_REMOVE_MOVE_TO: {
627
					//System.out.println("schedule:" + jobToCoordinate.getName());
125
					//do nothing, taken care of by AbstractIndexManager.ACTION_ADD_MOVE_FROM
126
					break;
628
				}
127
				}
629
					
630
631
			}
632
		}
633
634
		private boolean isJobToAvoid(Job jobToCoordinate) {
635
			boolean result = false;
636
			if (jobToCoordinate.belongsTo(ResourcesPlugin.FAMILY_AUTO_BUILD) || jobToCoordinate.belongsTo(ResourcesPlugin.FAMILY_MANUAL_BUILD) || jobToCoordinate.belongsTo(ResourcesPlugin.FAMILY_AUTO_REFRESH)) {
637
				result = true;
638
			}
128
			}
639
			return result;
640
641
		}
129
		}
642
130
		
643
	}
131
		//add any new JSP files to the JDT index using the JSPSearchSupport
644
132
		if(action == AbstractIndexManager.ACTION_ADD ||
645
	private class JSPResourceChangeListener implements IResourceChangeListener {
133
				action == AbstractIndexManager.ACTION_ADD_MOVE_FROM) {
646
134
		
647
135
			IFile file = (IFile)resource; //this assumption can be made because of #isResourceToIndex
648
		/**
136
			JSPSearchSupport ss = JSPSearchSupport.getInstance();
649
		 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
137
			try {
650
		 */
138
				IProject project = file.getProject();
651
		public void resourceChanged(IResourceChangeEvent event) {
139
				if (project != null) {
652
140
					IJavaProject jproject = JavaCore.create(project);
653
			if (isInitializing())
141
					if (jproject.exists()) {
654
				return;
142
						ss.addJspFile(file);
655
656
			// ignore resource changes if already rebuilding
657
			if (getIndexState() == S_REBUILDING)
658
				return;
659
			// previously canceled, needs entire index rebuild
660
			if (getIndexState() == S_CANCELED) {
661
				// rebuildIndex();
662
				// just resume indexing
663
				getProcessFilesJob().schedule(500);
664
				//System.out.println("schedule: resource changed, previously canceled");
665
				return;
666
			}
667
668
			IResourceDelta delta = event.getDelta();
669
			if (delta != null) {
670
				// only care about adds or changes right now...
671
				int kind = delta.getKind();
672
				boolean added = (kind & IResourceDelta.ADDED) == IResourceDelta.ADDED;
673
				boolean changed = (kind & IResourceDelta.CHANGED) == IResourceDelta.CHANGED;
674
				if (added || changed) {
675
676
					// only analyze the full (starting at root) delta
677
					// hierarchy
678
					if (delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$
679
						try {
680
							JSPResourceVisitor v = getVisitor();
681
							// clear from last run
682
							v.reset();
683
							// count files, possibly do this in a job too...
684
							// don't include PHANTOM resources
685
							delta.accept(v, false);
686
687
							// process files from this delta
688
							IFile[] files = v.getFiles();
689
							if (files.length > 0) {
690
								/*
691
								 * Job change listener should set back to
692
								 * stable when finished
693
								 */
694
								setUpdatingState();
695
								// processFiles(files);
696
								indexFiles(files);
697
							}
698
						}
699
						catch (CoreException e) {
700
							// need to set state here somehow, and reindex
701
							// otherwise index will be unreliable
702
							if (DEBUG)
703
								Logger.logException(e);
704
						}
705
						catch (Exception e) {
706
							// need to set state here somehow, and reindex
707
							// otherwise index will be unreliable
708
							if (DEBUG)
709
								Logger.logException(e);
710
						}
711
					}
143
					}
712
				}
144
				}
713
145
			}
146
			catch (Exception e) {
147
				String filename = file != null ? file.getFullPath().toString() : ""; //$NON-NLS-1$
148
				Logger.logException("JPSIndexManger: problem indexing:" + filename, e); //$NON-NLS-1$
714
			}
149
			}
715
		}
150
		}
716
717
	}
718
719
	IndexWorkspaceJob getIndexingJob() {
720
		return indexingJob;
721
	}
722
723
	ProcessFilesJob getProcessFilesJob() {
724
		return processFilesJob;
725
	}
151
	}
726
727
	boolean isInitializing() {
728
		return initializing;
729
	}
730
731
}
152
}
(-)src/org/eclipse/jst/jsp/core/internal/java/search/JSPSearchSupport.java (-7 / +68 lines)
Lines 315-324 Link Here
315
     * @param requestor
315
     * @param requestor
316
     *            passed in to accept search matches (and do "something" with
316
     *            passed in to accept search matches (and do "something" with
317
     *            them)
317
     *            them)
318
     * 
319
     * @deprecated use {@link #search(String, IJavaSearchScope, int, int, int, boolean, SearchRequestor, IProgressMonitor)}
318
     */
320
     */
319
    public void search(String searchText, IJavaSearchScope scope, int searchFor, int limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor) {
321
    public void search(String searchText, IJavaSearchScope scope, int searchFor, int limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor) {
322
    	this.search(searchText, scope, searchFor, limitTo, matchMode, isCaseSensitive,
323
    			requestor, new NullProgressMonitor());
324
    }
320
325
321
        JSPIndexManager.getInstance().rebuildIndexIfNeeded();
326
    /**
327
     * Search for an IJavaElement, constrained by the given parameters. Runs in
328
     * a background Job (results may still come in after this method call)
329
     * 
330
     * @param element
331
     * @param scope
332
     * @param requestor
333
     * 
334
     * @deprecated use {@link #search(IJavaElement, IJavaSearchScope, SearchRequestor, IProgressMonitor)}
335
     */
336
    public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) {
337
    	this.search(element, scope, requestor, new NullProgressMonitor());
338
    }
339
340
    /**
341
     * Search for an IJavaElement, constrained by the given parameters. Runs in
342
     * an IWorkspace runnable (results will be reported by the end of this
343
     * method)
344
     * 
345
     * @param element
346
     * @param scope
347
     * @param requestor
348
     */
349
    public void searchRunnable(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) {
350
    	this.searchRunnable(element, scope, requestor, new NullProgressMonitor());
351
    }
352
    
353
    /**
354
     * Perform a java search w/ the given parameters. Runs in a background Job
355
     * (results may still come in after this method call)
356
     * 
357
     * @param searchText
358
     *            the string of text to search on
359
     * @param searchFor
360
     *            IJavaSearchConstants.TYPE, METHOD, FIELD, PACKAGE, etc...
361
     * @param limitTo
362
     *            IJavaSearchConstants.DECLARATIONS,
363
     *            IJavaSearchConstants.REFERENCES,
364
     *            IJavaSearchConstants.IMPLEMENTORS, or
365
     *            IJavaSearchConstants.ALL_OCCURRENCES
366
     * @param matchMode
367
     *            allow * wildcards or not
368
     * @param isCaseSensitive
369
     * @param requestor
370
     *            passed in to accept search matches (and do "something" with
371
     *            them)
372
     */
373
    public void search(String searchText, IJavaSearchScope scope, int searchFor, int
374
    		limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor,
375
    		IProgressMonitor monitor) {
376
377
    	//wait for the index
378
		JSPIndexManager.getDefault().waitForConsistant(monitor);
322
379
323
        SearchJob job = new SearchJob(searchText, scope, searchFor, limitTo, matchMode, isCaseSensitive, requestor);
380
        SearchJob job = new SearchJob(searchText, scope, searchFor, limitTo, matchMode, isCaseSensitive, requestor);
324
        setCanceled(false);
381
        setCanceled(false);
Lines 338-347 Link Here
338
     * @param scope
395
     * @param scope
339
     * @param requestor
396
     * @param requestor
340
     */
397
     */
341
    public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) {
398
    public void search(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor,
342
399
    		IProgressMonitor monitor) {
343
        JSPIndexManager.getInstance().rebuildIndexIfNeeded();
344
400
401
    	//wait for the index
402
		JSPIndexManager.getDefault().waitForConsistant(monitor);
403
    	
345
        SearchJob job = new SearchJob(element, scope, requestor);
404
        SearchJob job = new SearchJob(element, scope, requestor);
346
        setCanceled(false);
405
        setCanceled(false);
347
        job.setUser(true);
406
        job.setUser(true);
Lines 359-367 Link Here
359
     * @param scope
418
     * @param scope
360
     * @param requestor
419
     * @param requestor
361
     */
420
     */
362
    public void searchRunnable(IJavaElement element, IJavaSearchScope scope, SearchRequestor requestor) {
421
    public void searchRunnable(IJavaElement element, IJavaSearchScope scope,
363
422
    		SearchRequestor requestor, IProgressMonitor monitor) {
364
        JSPIndexManager.getInstance().rebuildIndexIfNeeded();
423
    	
424
    	//wait for the index
425
		JSPIndexManager.getDefault().waitForConsistant(monitor);
365
426
366
        SearchRunnable searchRunnable = new SearchRunnable(element, scope, requestor);
427
        SearchRunnable searchRunnable = new SearchRunnable(element, scope, requestor);
367
        try {
428
        try {
(-)src/org/eclipse/jst/jsp/ui/internal/java/search/ui/JSPQueryParticipant.java (-2 / +3 lines)
Lines 59-65 Link Here
59
				SearchRequestor jspRequestor = new JSPSearchRequestor(requestor);
59
				SearchRequestor jspRequestor = new JSPSearchRequestor(requestor);
60
				
60
				
61
				// pa_TODO need to adapt JavaSearchScope to a JSPSearchScope
61
				// pa_TODO need to adapt JavaSearchScope to a JSPSearchScope
62
				JSPSearchSupport.getInstance().search(element, new JSPSearchScope(), jspRequestor);
62
				JSPSearchSupport.getInstance().search(element, new JSPSearchScope(), jspRequestor, monitor);
63
				
63
				
64
			}
64
			}
65
			else if(querySpecification instanceof PatternQuerySpecification) {
65
			else if(querySpecification instanceof PatternQuerySpecification) {
Lines 79-85 Link Here
79
														patternQuery.getLimitTo(), 
79
														patternQuery.getLimitTo(), 
80
														SearchPattern.R_PATTERN_MATCH, 
80
														SearchPattern.R_PATTERN_MATCH, 
81
														false, 
81
														false, 
82
														jspRequestor);
82
														jspRequestor,
83
														monitor);
83
			}
84
			}
84
		}
85
		}
85
	}
86
	}
(-)META-INF/MANIFEST.MF (+1 lines)
Lines 7-12 Link Here
7
Bundle-Vendor: %providerName
7
Bundle-Vendor: %providerName
8
Bundle-Localization: plugin
8
Bundle-Localization: plugin
9
Export-Package: org.eclipse.wst.sse.core,
9
Export-Package: org.eclipse.wst.sse.core,
10
 org.eclipse.wst.sse.core.indexing,
10
 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",
11
 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",
11
 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",
12
 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",
12
 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",
13
 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",
(-)src/org/eclipse/wst/sse/core/indexing/AbstractIndexManager.java (+1665 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2010 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *     
11
 *******************************************************************************/
12
package org.eclipse.wst.sse.core.indexing;
13
14
import java.io.BufferedInputStream;
15
import java.io.BufferedOutputStream;
16
import java.io.DataInputStream;
17
import java.io.DataOutputStream;
18
import java.io.File;
19
import java.io.FileInputStream;
20
import java.io.FileNotFoundException;
21
import java.io.FileOutputStream;
22
import java.io.IOException;
23
import java.util.Iterator;
24
import java.util.LinkedHashMap;
25
import java.util.Map;
26
27
import org.eclipse.core.resources.IFile;
28
import org.eclipse.core.resources.IResource;
29
import org.eclipse.core.resources.IResourceChangeEvent;
30
import org.eclipse.core.resources.IResourceChangeListener;
31
import org.eclipse.core.resources.IResourceDelta;
32
import org.eclipse.core.resources.IResourceDeltaVisitor;
33
import org.eclipse.core.resources.IResourceProxy;
34
import org.eclipse.core.resources.IResourceProxyVisitor;
35
import org.eclipse.core.resources.ISaveParticipant;
36
import org.eclipse.core.resources.ResourcesPlugin;
37
import org.eclipse.core.runtime.CoreException;
38
import org.eclipse.core.runtime.IPath;
39
import org.eclipse.core.runtime.IProgressMonitor;
40
import org.eclipse.core.runtime.ISafeRunnable;
41
import org.eclipse.core.runtime.IStatus;
42
import org.eclipse.core.runtime.Path;
43
import org.eclipse.core.runtime.SafeRunner;
44
import org.eclipse.core.runtime.Status;
45
import org.eclipse.core.runtime.SubMonitor;
46
import org.eclipse.core.runtime.jobs.Job;
47
import org.eclipse.osgi.util.NLS;
48
import org.eclipse.wst.sse.core.internal.Logger;
49
import org.eclipse.wst.sse.core.internal.SSECoreMessages;
50
51
/**
52
 * <p>Provides a generic for writing a resource index manager.  It is important
53
 * to note that this only provides the framework for managing an index, not actually
54
 * indexing.  The subtle difference is that the manager is in charge of paying attention
55
 * to all of the resource actions that take place in the workspace and filtering those
56
 * actions down to simple actions that need to be performed on whatever index this manger
57
 * is managing.</p>
58
 * 
59
 * <p>The manger does its very best to make sure the index is always consistent, even if
60
 * resource events take place when the manager is not running.  In the event that the
61
 * manager determines it has missed, loosed, or corrupted any resource change events
62
 * that have occurred before, during, or after its activation or de-activation then the
63
 * manager will inspect the entire workspace to insure the index it is managing is
64
 * Consistent.</p>
65
 *  
66
 */
67
public abstract class AbstractIndexManager {
68
	
69
	/** Default time to wait for other tasks to finish */
70
	private static final int WAIT_TIME = 300;
71
	
72
	/**
73
	 * <p>Used to report progress on jobs where the total work to complete is unknown.
74
	 * The created effect is a progress bar that moves but that will never complete.</p>
75
	 */
76
	private static final int UNKNOWN_WORK = 100;
77
	
78
	/** The amount of events to batch up before sending them off to the processing job*/
79
	private static final int BATCH_UP_AMONT = 100;
80
	
81
	/** If this file exists then a full workspace re-processing is needed */ 
82
	private static final String RE_PROCESS_FILE_NAME = ".re-process"; //$NON-NLS-1$
83
	
84
	/** Common error message to log */
85
	private static final String LOG_ERROR_INDEX_INVALID =
86
		"Index may become invalid, incomplete, or enter some other inconsistent state."; //$NON-NLS-1$
87
	
88
	/** State: manager is stopped */
89
	private static final byte STATE_DISABLED = 0;
90
	
91
	/** State: manager is running */
92
	private static final byte STATE_ENABLED = 1;
93
	
94
	/** Action: add to index */
95
	protected static final byte ACTION_ADD = 0;
96
	
97
	/** Action: remove from index */
98
	protected static final byte ACTION_REMOVE = 1;
99
	
100
	/** Action: add to index caused by move operation */
101
	protected static final byte ACTION_ADD_MOVE_FROM = 2;
102
	
103
	/** Action: remove from index caused by move operation */
104
	protected static final byte ACTION_REMOVE_MOVE_TO = 3;
105
	
106
	/** Source: action originated from resource change event */
107
	protected static final byte SOURCE_RESROUCE_CHANGE = 0;
108
	
109
	/** Source: action originated from workspace scan */
110
	protected static final byte SOURCE_WORKSPACE_SCAN = 1;
111
	
112
	/** Source: action originated from saved state */
113
	protected static final byte SOURCE_SAVED_STATE = 2;
114
	
115
	/** Source: preserved resources to index */
116
	protected static final byte SOURCE_PRESERVED_RESOURCES_TO_INDEX = 3;
117
	
118
	/** the name of this index manager */
119
	protected String fName;
120
	
121
	/** {@link IResourceChangeListener} to listen for file changes */
122
	private ResourceChangeListener fResourceChangeListener;
123
	
124
	/** The {@link Job} that does all of the indexing */
125
	private ResourceEventProcessingJob fResourceEventProcessingJob;
126
	
127
	/** A {@link Job} to search the workspace for all files */
128
	private Job fWorkspaceVisitorJob;
129
	
130
	/**
131
	 * <p>Current state of the manager</p>
132
	 * 
133
	 * @see #STATE_DISABLED
134
	 * @see #STATE_ENABLED
135
	 */
136
	private volatile byte fState;
137
	
138
	/** used to prevent manager from starting and stopping at the same time */
139
	private Object fStartStopLock = new Object();
140
	
141
	/** <code>true</code> if the manger is currently starting, <code>false</code> otherwise */
142
	private boolean fStarting;
143
144
	/**
145
	 * <p>Creates the manager with a given name.</p>
146
	 * 
147
	 * @param name This will be pre-pended to progress reporting messages and thus should
148
	 * be translated
149
	 */
150
	protected AbstractIndexManager(String name) {
151
		this.fName = name;
152
		this.fState = STATE_DISABLED;
153
		this.fResourceChangeListener = new ResourceChangeListener();
154
		this.fResourceEventProcessingJob = new ResourceEventProcessingJob();
155
		this.fStarting = false;
156
	}
157
	
158
	/**
159
	 * <p>Starts up the {@link AbstractIndexManager}.  If a {@link IResourceDelta}
160
	 * is provided then it is assumed that all other files in the workspace
161
	 * have already been index and thus only those in the provided
162
	 * {@link IResourceDelta} will be processed.  Else if the provided
163
	 * {@link IResourceDelta} is <code>null</code> it is assumed no files
164
	 * have been indexed yet so the entire workspace will be searched for
165
	 * files to be indexed.</p>
166
	 * 
167
	 * <p>If {@link IResourceDelta} is provided this will block until that delta
168
	 * has finished processing.  If no {@link IResourceDelta} provided then a
169
	 * separate job will be created to process the entire workspace and this method
170
	 * will return without waiting for that job to complete</p>
171
	 * 
172
	 * <p>Will block until {@link #stop()} has finished running if it is
173
	 * currently running</p>
174
	 * 
175
	 * @param savedStateDelta the delta from a saved state, if <code>null</code>
176
	 * then the entire workspace will be searched for files to index, else
177
	 * only files in this {@link IResourceDelta} will be indexed
178
	 * @param monitor This action can not be canceled but this monitor will be used
179
	 * to report progress
180
	 */
181
	public final void start(IResourceDelta savedStateDelta, IProgressMonitor monitor) {
182
		SubMonitor progress = SubMonitor.convert(monitor);
183
		synchronized (this.fStartStopLock) {
184
			this.fStarting = true;
185
			
186
			if(this.fState == STATE_DISABLED) {
187
				//report status
188
				progress.beginTask(this.fName + ": " + SSECoreMessages.IndexManager_starting, //$NON-NLS-1$
189
						2);
190
				
191
				//start listening for resource change events
192
				this.fResourceChangeListener.start();
193
				
194
				//check to see if a full re-index is required
195
				boolean forcedFullReIndexNeeded = this.isForcedFullReIndexNeeded();
196
				
197
				/* start the indexing job only loading preserved state if not doing full index
198
				 * if failed loading preserved state then force full re-index
199
				 */
200
				forcedFullReIndexNeeded = !this.fResourceEventProcessingJob.start(!forcedFullReIndexNeeded,
201
						progress.newChild(1));
202
				progress.setWorkRemaining(1);
203
				
204
				//don't bother processing saved delta if forced full re-index is needed
205
				boolean stillNeedFullReIndex = forcedFullReIndexNeeded;
206
				if(!forcedFullReIndexNeeded) {
207
					//if there is a delta attempt to process it
208
					if(savedStateDelta != null) {
209
						stillNeedFullReIndex = false;
210
						try {
211
							//deal with reporting progress
212
							SubMonitor savedStateProgress = progress.newChild(1, SubMonitor.SUPPRESS_NONE);
213
							savedStateProgress.setTaskName(
214
									this.fName + ": " + SSECoreMessages.IndexManager_starting + ": " + //$NON-NLS-1$ //$NON-NLS-2$
215
										SSECoreMessages.IndexManager_processing_deferred_resource_changes);
216
							
217
							//process delta
218
							ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(savedStateProgress,
219
									AbstractIndexManager.SOURCE_SAVED_STATE);
220
							savedStateDelta.accept(visitor);
221
							
222
							//process any remaining batched up resources to index
223
							visitor.processBatchedResourceEvents();
224
						} catch (CoreException e) {
225
							stillNeedFullReIndex = true;
226
							Logger.logException(this.fName + ": Could not process saved state. " + //$NON-NLS-1$
227
									"Forced to do a full workspace re-index.", e); //$NON-NLS-1$
228
						}
229
					}
230
				}
231
				progress.worked(1);
232
				
233
				//if need to process the entire workspace do so in another job
234
				if(stillNeedFullReIndex){
235
					this.fWorkspaceVisitorJob = new WorkspaceVisitorJob();
236
					this.fWorkspaceVisitorJob.schedule();
237
				}
238
				
239
				//update state
240
				this.fState = STATE_ENABLED;
241
			}
242
			this.fStarting = false;
243
		}
244
	}
245
	
246
	/**
247
	 * <p>Safely shuts down the manager.</p>
248
	 * 
249
	 * <p>This will block until the {@link #start(IResourceDelta, IProgressMonitor)} has
250
	 * finished (if running).  Also until the current resource event has finished being
251
	 * processed.  Finally it will block until the events still to be processed by
252
	 * the processing job have been preserved to be processed on the next call to
253
	 * {@link #start(IResourceDelta, IProgressMonitor)}.</p>
254
	 * 
255
	 * <p>If at any point during this shut down processes something goes wrong the
256
	 * manager will be sure that on the next call to {@link #start(IResourceDelta, IProgressMonitor)}
257
	 * the entire workspace will be re-processed.</p>
258
	 * 
259
	 * @throws InterruptedException
260
	 */
261
	public final void stop() throws InterruptedException {
262
		synchronized (this.fStartStopLock) {
263
			if(this.fState != STATE_DISABLED) {
264
				
265
				//stop listening for events, and wait for the current event to finish
266
				this.fResourceChangeListener.stop();
267
				
268
				// if currently visiting entire workspace, give up and try again next load
269
				boolean forceFullReIndexNextStart = false;
270
				if(this.fWorkspaceVisitorJob != null) {
271
					if (this.fWorkspaceVisitorJob.getState() != Job.NONE) {
272
						this.fWorkspaceVisitorJob.cancel();
273
						
274
						this.forceFullReIndexNextStart();
275
						forceFullReIndexNextStart = true;
276
					}
277
				}
278
				
279
				//stop the indexing job, only preserve if not already forcing a re-index
280
				forceFullReIndexNextStart = !this.fResourceEventProcessingJob.stop(!forceFullReIndexNextStart);
281
				
282
				//if preserving failed, then force re-index
283
				if(forceFullReIndexNextStart) {
284
					this.forceFullReIndexNextStart();
285
				}
286
				
287
				//update status
288
				this.fState = STATE_DISABLED;
289
			}
290
		}
291
	}
292
	
293
	/**
294
	 * <p>Should be called by an client of the index this manger manages before the index
295
	 * is accessed, assuming the client wants an index consistent with the latest
296
	 * resource changes.</p>
297
	 * 
298
	 * <p>The supplied monitor will be used to supply user readable progress as the manager
299
	 * insures the index has been given all the latest resource events.  This monitor
300
	 * maybe canceled, but if it is the state of the index is not guaranteed to be
301
	 * consistent with the latest resource change events.</p>
302
	 * 
303
	 * @param monitor Used to report user readable progress as the manager insures the
304
	 * index is consistent with the latest resource events.  This monitor can be canceled
305
	 * to stop waiting for consistency but then no guaranty is made about the consistency
306
	 * of the index in relation to un-processed resource changes
307
	 * 
308
	 * @return <code>true</code> if the wait finished successfully and the manger is consistent,
309
	 * <code>false</code> otherwise, either an error occurred while waiting for the manager
310
	 * or the monitor was canceled
311
	 * 
312
	 * @throws InterruptedException This can happen when waiting for other jobs
313
	 */
314
	public final boolean waitForConsistant(IProgressMonitor monitor) {
315
		boolean success = true;
316
		boolean interupted = false;
317
		SubMonitor progress = SubMonitor.convert(monitor);
318
		
319
		//set up the progress of waiting
320
		int remainingWork = 4;
321
		progress.beginTask(NLS.bind(SSECoreMessages.IndexManager_Waiting_for_0, this.fName),
322
				remainingWork);
323
		
324
		//wait for start up
325
		if(this.fStarting && !monitor.isCanceled()) {
326
			SubMonitor startingProgress = progress.newChild(1);
327
			startingProgress.subTask(SSECoreMessages.IndexManager_starting);
328
			while(this.fStarting && !monitor.isCanceled()) {
329
				//this creates a never ending progress that still moves forward
330
				startingProgress.setWorkRemaining(UNKNOWN_WORK);
331
				startingProgress.newChild(1).worked(1);
332
				try {
333
					Thread.sleep(WAIT_TIME);
334
				} catch (InterruptedException e) {
335
					interupted = true;
336
				}
337
			}
338
		}
339
		progress.setWorkRemaining(--remainingWork);
340
		
341
		//wait for workspace visiting job
342
		if(this.fWorkspaceVisitorJob != null && this.fWorkspaceVisitorJob.getState() != Job.NONE && !monitor.isCanceled()) {
343
			SubMonitor workspaceVisitorProgress = progress.newChild(1);
344
			workspaceVisitorProgress.subTask(SSECoreMessages.IndexManager_Processing_entire_workspace_for_the_first_time);
345
			while(this.fWorkspaceVisitorJob.getState() != Job.NONE && !monitor.isCanceled()) {
346
				//this creates a never ending progress that still moves forward
347
				workspaceVisitorProgress.setWorkRemaining(UNKNOWN_WORK);
348
				workspaceVisitorProgress.newChild(1).worked(1);
349
				try {
350
					Thread.sleep(WAIT_TIME);
351
				} catch (InterruptedException e) {
352
					interupted = true;
353
				}
354
			}
355
		}
356
		progress.setWorkRemaining(--remainingWork);
357
		
358
		//wait for the current resource event
359
		if(this.fResourceChangeListener.isProcessingEvents() && !monitor.isCanceled()) {
360
			SubMonitor workspaceVisitorProgress = progress.newChild(1);
361
			workspaceVisitorProgress.subTask(SSECoreMessages.IndexManager_processing_recent_resource_changes);
362
			while(this.fResourceChangeListener.isProcessingEvents() && !monitor.isCanceled()) {
363
				workspaceVisitorProgress.setWorkRemaining(UNKNOWN_WORK);
364
				workspaceVisitorProgress.newChild(1).worked(1);
365
				try {
366
					this.fResourceChangeListener.waitForCurrentEvent(WAIT_TIME);
367
				} catch (InterruptedException e) {
368
					interupted = true;
369
				}
370
			}
371
		}
372
		progress.setWorkRemaining(--remainingWork);
373
		
374
		//wait for all files to be indexed
375
		if(this.fResourceEventProcessingJob.getNumResourceEventsToProcess() != 0 && !monitor.isCanceled()) {
376
			SubMonitor indexingProgress = progress.newChild(1);
377
			int prevNumResrouces;
378
			int numResources = this.fResourceEventProcessingJob.getNumResourceEventsToProcess();
379
			while(numResources != 0 && !monitor.isCanceled()) {
380
				//update the progress indicator
381
				indexingProgress.subTask(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$
382
						NLS.bind(SSECoreMessages.IndexManager_Indexing_0_Files,
383
								"" + numResources)); //$NON-NLS-1$
384
				indexingProgress.setWorkRemaining(numResources);
385
				prevNumResrouces = numResources;
386
				numResources = this.fResourceEventProcessingJob.getNumResourceEventsToProcess();
387
				int numProcessed = prevNumResrouces - numResources;
388
				indexingProgress.worked(numProcessed > 0 ? numProcessed : 0);
389
				
390
				//give the index some time to do some indexing
391
				try {
392
					this.fResourceEventProcessingJob.waitForConsistant(WAIT_TIME);
393
				} catch (InterruptedException e) {
394
					interupted = true;
395
				}
396
			}
397
		}
398
		progress.setWorkRemaining(--remainingWork);
399
		
400
		if(monitor.isCanceled()) {
401
			success = false;
402
		}
403
		
404
		//reset the interrupted flag if we were interrupted
405
		if(interupted) {
406
			Thread.currentThread().interrupt();
407
		}
408
		
409
		return success;
410
	}
411
	
412
	/**
413
	 * <p>Used to determine if an {@link IResource} with the given
414
	 * type and name should be processed by this index manager</p>
415
	 * 
416
	 * @param type see {@link IResource#getType()}
417
	 * @param name the name of the resource
418
	 * 
419
	 * @return <code>true</code> if this index manager processes resources
420
	 * of the given <code>type</code> with the given <code>name</code>,
421
	 * <code>false</code> otherwise
422
	 */
423
	protected abstract boolean isResourceToIndex(int type, String name);
424
	
425
	/**
426
	 * <p>Called for each {@link ResourceEvent} gathered by the various sources and processed
427
	 * by the {@link ResourceEventProcessingJob}.  The implementation of this method
428
	 * should use the given information to update the index this manger is managing.</p>
429
	 * 
430
	 * @param source The source that reported this resource event
431
	 * @param action The action to be taken on the given <code>resource</code>
432
	 * @param resource The index should perform the given <code>action</code> on this
433
	 * resource
434
	 * @param movePath If the given <code>action</code> is {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM}
435
	 * or {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this field will not be
436
	 * null and reports the path the given <code>resource</code> was either moved from or
437
	 * moved to respectively.
438
	 * 
439
	 * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE
440
	 * @see AbstractIndexManager#SOURCE_SAVED_STATE
441
	 * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN
442
	 * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX
443
	 * 
444
	 * @see AbstractIndexManager#ACTION_ADD
445
	 * @see AbstractIndexManager#ACTION_REMOVE
446
	 * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
447
	 * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
448
	 */
449
	protected abstract void performAction(byte source, byte action, IResource resource,
450
			IPath movePath);
451
	
452
	/**
453
	 * <p>Gets the working location of the manager. This is where any relevant
454
	 * state can be persisted.</p>
455
	 * 
456
	 * @return the working location of the manager
457
	 */
458
    protected abstract IPath getWorkingLocation();
459
	
460
	/**
461
	 * <p>Determines if a resource should be visited or not while
462
	 * looking for files to index.  If a resource should not be
463
	 * visited it is assumed its children should not be visited either.</p>
464
	 * 
465
	 * <p>Implementers may override. Default is to ignore resources
466
	 * starting with a period.</p>
467
	 * 
468
	 * @param resourceName name of the resource to determine if it
469
	 * should be visited or not
470
	 * 
471
	 * @return <code>true</code> if the resource should be visited,
472
	 * <code>false</code> otherwise
473
	 */
474
	protected boolean shouldVisit(String resourceName) {
475
		return !resourceName.startsWith(".");//$NON-NLS-1$
476
	}
477
	
478
	/**
479
	 * <p>Next time the manager starts up force a full workspace index</p>
480
	 */
481
	private void forceFullReIndexNextStart() {
482
		IPath reIndexPath =
483
			AbstractIndexManager.this.getWorkingLocation().append(RE_PROCESS_FILE_NAME);
484
		File file = new File(reIndexPath.toOSString());
485
		try {
486
			file.createNewFile();
487
		} catch (IOException e) {
488
			Logger.logException(this.fName + ": Could not create file to tell manager to" + //$NON-NLS-1$
489
					" do a full re-index on next load. " + //$NON-NLS-1$
490
					AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e);
491
		}
492
	}
493
	
494
	/**
495
	 * @return <code>true</code> if a full workspace index is needed as dictated by
496
	 * a previous call to {@link #forceFullReIndexNextStart()}, <code>false</code>
497
	 * otherwise
498
	 */
499
	private boolean isForcedFullReIndexNeeded() {
500
		boolean forcedFullReIndexNeeded = false;
501
		IPath reIndexPath =
502
			AbstractIndexManager.this.getWorkingLocation().append(RE_PROCESS_FILE_NAME);
503
		File file = new File(reIndexPath.toOSString());
504
		if(file.exists()) {
505
			file.delete();
506
			forcedFullReIndexNeeded = true;
507
		}
508
		
509
		return forcedFullReIndexNeeded;
510
	}
511
	
512
	/**
513
	 * <p>A system {@link Job} used to visit all of the files in the workspace
514
	 * looking for files to index.</p>
515
	 * 
516
	 * <p>This should only have to be done once per workspace on the first load,
517
	 * but if it fails or a SavedState can not be retrieved on a subsequent
518
	 * workspace load then this will have to be done again.</p>
519
	 */
520
	private class WorkspaceVisitorJob extends Job {
521
		/**
522
		 * <p>Default constructor that sets up this job as a system job</p>
523
		 */
524
		protected WorkspaceVisitorJob() {
525
			super(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$
526
					SSECoreMessages.IndexManager_Processing_entire_workspace_for_the_first_time);
527
			
528
			this.setUser(false);
529
			this.setSystem(true);
530
			this.setPriority(Job.LONG);
531
		}
532
		
533
		/**
534
		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
535
		 */
536
		protected IStatus run(IProgressMonitor monitor) {
537
			try {
538
				//update status
539
				monitor.beginTask(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$
540
						SSECoreMessages.IndexManager_Processing_entire_workspace_for_the_first_time,
541
						IProgressMonitor.UNKNOWN);
542
				
543
				//visit the workspace
544
				WorkspaceVisitor visitor = new WorkspaceVisitor(monitor);
545
				ResourcesPlugin.getWorkspace().getRoot().accept(visitor, IResource.NONE);
546
				
547
				//process any remaining batched up resources to index
548
				visitor.processBatchedResourceEvents();
549
			} catch(CoreException e) {
550
				Logger.logException(AbstractIndexManager.this.fName +
551
						": Failed visiting entire workspace for initial index. " + //$NON-NLS-1$
552
						AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e);
553
			}
554
			
555
			IStatus status;
556
			if(monitor.isCanceled()) {
557
				status = Status.CANCEL_STATUS;
558
			} else {
559
				status = Status.OK_STATUS;
560
			}
561
			
562
			return status;
563
		}
564
		
565
		/**
566
		 * <p>An {@link IResourceProxyVisitor} used to visit all of the files in the
567
		 * workspace looking for files to add to the index.</p>
568
		 * 
569
		 * <p><b>NOTE: </b>After this visitor is used {@link WorkspaceVisitor#processBatchedResourceEvents()
570
		 * must be called to flush out the last of the {@link ResourceEvent}s produced
571
		 * by this visitor.</p>
572
		 */
573
		private class WorkspaceVisitor implements IResourceProxyVisitor {
574
			/** {@link IProgressMonitor} used to report status and check for cancellation */
575
			private SubMonitor fProgress;
576
			
577
			/**
578
			 * {@link Map}&lt{@link IResource}, {@link ResourceEvent}&gt
579
			 * <p>Map of resources events created and batched up by this visitor.
580
			 * These events are periodical be sent off to the
581
			 * {@link ResourceEventProcessingJob} but need to be sent off
582
			 * one final time after this visitor finishes it work.</p>
583
			 * 
584
			 * @see #processBatchedResourceEvents()
585
			 */
586
			private Map fBatchedResourceEvents;
587
			
588
			/**
589
			 * <p>Default constructor</p>
590
			 * @param monitor used to report status and allow this visitor to be canceled
591
			 */
592
			protected WorkspaceVisitor(IProgressMonitor monitor) {
593
				this.fProgress = SubMonitor.convert(monitor);
594
				this.fBatchedResourceEvents = new LinkedHashMap(BATCH_UP_AMONT);
595
			}
596
597
			/**
598
			 * <p>As long as the monitor is not canceled visit each file in the workspace
599
			 * that should be visited.</p>
600
			 * 
601
			 * @see org.eclipse.core.resources.IResourceProxyVisitor#visit(org.eclipse.core.resources.IResourceProxy)
602
			 * @see AbstractIndexManager#shouldVisit(String)
603
			 */
604
			public boolean visit(IResourceProxy proxy) throws CoreException {
605
				this.fProgress.subTask(proxy.getName());
606
				
607
				boolean visitChildren = false;
608
				
609
				/* if not canceled or a hidden resource then process file
610
				 * else don't visit children
611
				 */
612
				if(!this.fProgress.isCanceled() && shouldVisit(proxy.getName())) {
613
					if(isResourceToIndex(proxy.getType(), proxy.getName())) {
614
						
615
						//add the file to be indexed
616
						IFile file = (IFile) proxy.requestResource();
617
						if(file.exists()) {
618
							this.fBatchedResourceEvents.put(file, new ResourceEvent(
619
									AbstractIndexManager.SOURCE_WORKSPACE_SCAN,
620
									AbstractIndexManager.ACTION_ADD,
621
									null));
622
						}
623
					}
624
					
625
					visitChildren = true;
626
				} else {
627
					visitChildren = false;
628
				}
629
				
630
				//batch up resource changes before sending them out
631
				if(this.fBatchedResourceEvents.size() >= BATCH_UP_AMONT) {
632
					this.processBatchedResourceEvents();
633
				}
634
				
635
				return visitChildren;
636
			}
637
			
638
			/**
639
			 * <p>Sends any batched up resource events created by this visitor to the
640
			 * {@link ResourceEventProcessingJob}.<p>
641
			 * 
642
			 * <p><b>NOTE:</b> This will be called every so often as the visitor is
643
			 * visiting resources but needs to be called a final time by the user of
644
			 * this visitor to be sure the final events are sent off</p>
645
			 */
646
			protected void processBatchedResourceEvents() {
647
				AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents(
648
						this.fBatchedResourceEvents);
649
				this.fBatchedResourceEvents.clear();
650
			}
651
		}
652
	}
653
	
654
	/**
655
	 * <p>Used to listen to resource change events in the workspace.  These events
656
	 * are batched up and then passed onto the {@link ResourceEventProcessingJob}.</p>
657
	 */
658
	private class ResourceChangeListener implements IResourceChangeListener {
659
		/**
660
		 * <p>The number of events currently being processed by this listener.</p>
661
		 * <p>Use the {@link #fEventsBeingProcessedLock} when reading or writing this field</p>
662
		 * 
663
		 * @see #fEventsBeingProcessedLock
664
		 */
665
		private volatile int fEventsBeingProcessed;
666
		
667
		/**
668
		 * Lock to use when reading or writing {@link #fEventsBeingProcessed}
669
		 * 
670
		 * @see #fEventsBeingProcessed
671
		 */
672
		private final Object fEventsBeingProcessedLock = new Object();
673
		
674
		/**
675
		 * <p>Current state of this listener</p>
676
		 * 
677
		 * @see AbstractIndexManager#STATE_DISABLED
678
		 * @see AbstractIndexManager#STATE_ENABLED
679
		 */
680
		private volatile byte fState;
681
		
682
		/**
683
		 * <p>Default constructor</p>
684
		 */
685
		protected ResourceChangeListener() {
686
			this.fState = STATE_DISABLED;
687
			this.fEventsBeingProcessed = 0;
688
		}
689
		
690
		/**
691
		 * <p>Start listening for resource change events</p>
692
		 */
693
		protected void start() {
694
			this.fState = STATE_ENABLED;
695
			ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
696
		}
697
		
698
		/**
699
		 * <p>Stop listening for resource change events and if already processing
700
		 * an event then wait for that processing to finish</p>
701
		 * 
702
		 * @throws InterruptedException waiting for a current event to finish processing
703
		 * could be interrupted
704
		 */
705
		protected void stop() throws InterruptedException {
706
			ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
707
			
708
			//wait indefinitely for current event to finish processing
709
			this.waitForCurrentEvent(0);
710
			
711
			this.fState = STATE_DISABLED;
712
		}
713
		
714
		/**
715
		 * <p>Blocks until either the current resource event has been processed or
716
		 * until the given timeout has passed.</p>
717
		 * 
718
		 * @param timeout block until either this timeout elapses (0 means never to timeout)
719
		 * or the current resource change event finishes being processed
720
		 * 
721
		 * @throws InterruptedException This can happen when waiting for a lock
722
		 */
723
		protected void waitForCurrentEvent(int timeout) throws InterruptedException {
724
			synchronized (this.fEventsBeingProcessedLock) {
725
				if(this.fEventsBeingProcessed != 0) {
726
					this.fEventsBeingProcessedLock.wait(timeout);
727
				}
728
			}
729
		}
730
		
731
		/**
732
		 * @return <code>true</code> if this listener is currently processing any
733
		 * events, <code>false</code> otherwise.
734
		 */
735
		protected boolean isProcessingEvents() {
736
			return this.fEventsBeingProcessed != 0;
737
		}
738
		
739
		/**
740
		 * <p>Process a resource change event.  If it is a pre-close or pre-delete then
741
		 * the {@link ResourceEventProcessingJob} is paused so it does not try to
742
		 * process resources that are about to be deleted.  The {@link ResourceDeltaVisitor}
743
		 * is used to actually process the event.</p>
744
		 * 
745
		 * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
746
		 * @see ResourceDeltaVisitor
747
		 */
748
		public void resourceChanged(IResourceChangeEvent event) {
749
			try {
750
				//update the number of events being processed
751
				synchronized (this.fEventsBeingProcessedLock) {
752
					++this.fEventsBeingProcessed;
753
				}
754
				
755
				if(this.fState == STATE_ENABLED) {
756
					switch(event.getType()) {
757
						case IResourceChangeEvent.PRE_CLOSE:
758
						case IResourceChangeEvent.PRE_DELETE:{
759
							//pre-close or pre-delete pause the persister job so it does not interfere
760
							AbstractIndexManager.this.fResourceEventProcessingJob.pause();
761
							break;
762
						}
763
						case IResourceChangeEvent.POST_BUILD:
764
						case IResourceChangeEvent.POST_CHANGE: {
765
							//post change start up the indexer job and process the delta
766
							AbstractIndexManager.this.fResourceEventProcessingJob.unPause();
767
							
768
							// only analyze the full (starting at root) delta hierarchy
769
							IResourceDelta delta = event.getDelta();
770
							if (delta != null && delta.getFullPath().toString().equals("/")) { //$NON-NLS-1$
771
								try {
772
									//use visitor to visit all children
773
									ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(
774
											AbstractIndexManager.SOURCE_RESROUCE_CHANGE);
775
									delta.accept(visitor, false);
776
									
777
									//process any remaining batched up resources to index
778
									visitor.processBatchedResourceEvents();
779
								} catch (CoreException e) {
780
									Logger.logException(AbstractIndexManager.this.fName +
781
											": Failed visiting resrouce change delta. " + //$NON-NLS-1$
782
											AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e);
783
								}
784
							}
785
							break;
786
						}
787
					}
788
				} else {
789
					Logger.log(Logger.ERROR, "A resource change event came in after " +
790
							AbstractIndexManager.this.fName + " shut down. This should never " +
791
							"ever happen, but if it does the index may now be inconsistant.");
792
				}
793
			} finally {
794
				//no matter how we exit be sure to update the number of events being processed
795
				synchronized (this.fEventsBeingProcessedLock) {
796
					--this.fEventsBeingProcessed;
797
					
798
					//if currently not events being processed, then notify
799
					if(this.fEventsBeingProcessed == 0) {
800
						this.fEventsBeingProcessedLock.notifyAll();
801
					}
802
				}
803
			}
804
		}
805
	}
806
	
807
	/**
808
	 * <p>Used to visit {@link IResourceDelta}s from both the {@link IResourceChangeListener}
809
	 * and from a {@link ISaveParticipant} given to {@link AbstractIndexManager#start(IResourceDelta, IProgressMonitor)}.
810
	 * The resource events are batched into groups of {@link AbstractIndexManager#BATCH_UP_AMONT}
811
	 * before being passed onto the {@link ResourceEventProcessingJob}.</p>
812
	 * 
813
	 * <p><b>NOTE 1: </b> This class is intended for one time use, thus a new instance should
814
	 * be instantiated each time this visitor is needed to process a new {@link IResourceDelta}.</p>
815
	 * 
816
	 * <p><b>NOTE 2: </b> Be sure to call {@link ResourceDeltaVisitor#processBatchedResourceEvents()}
817
	 * after using this visitor to be sure any remaining events get passed onto the
818
	 * {@link ResourceEventProcessingJob}.</p>
819
	 * 
820
	 * @see ResourceDeltaVisitor#processBatchedResourceEvents()
821
	 */
822
	private class ResourceDeltaVisitor implements IResourceDeltaVisitor {
823
		/** {@link IProgressMonitor} used to report status */
824
		private SubMonitor fProgress;
825
		
826
		/**
827
		 * <p>The source that should be used when sending resource events to the
828
		 * {@link ResourceEventProcessingJob}.</p>
829
		 * 
830
		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
831
		 * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE
832
		 */
833
		private byte fSource;
834
		
835
		/**
836
		 * <p>Due to the nature of a visitor it has no way of knowing the total amount
837
		 * of work it has to do but it can start to predict it based on the number of
838
		 * children of each event it processes and whether it plans on visiting those
839
		 * children</p>
840
		 */
841
		private int fPredictedWorkRemaining;
842
		
843
		/**
844
		 * {@link Map}&lt{@link IResource}, {@link ResourceEvent}&gt
845
		 * <p>Map of resources events created and batched up by this visitor.
846
		 * These events are periodical be sent off to the
847
		 * {@link ResourceEventProcessingJob} but need to be sent off
848
		 * one final time after this visitor finishes it work.</p>
849
		 * 
850
		 * @see #processBatchedResourceEvents()
851
		 */
852
		private Map fBatchedResourceEvents;
853
		
854
		/**
855
		 * <p>Creates a visitor that will create resource events based on the resources
856
		 * it visits and using the given source as the source of the events.</p>
857
		 * 
858
		 * @param source The source of the events that should be used when creating
859
		 * resource events from visited resources
860
		 * 
861
		 * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE
862
		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
863
		 */
864
		protected ResourceDeltaVisitor(byte source) {
865
			this(SubMonitor.convert(null), source);
866
		}
867
		
868
		/**
869
		 * <p>Creates a visitor that will create resource events based on the resources
870
		 * it visits and using the given source as the source of the events and
871
		 * report its status to the given progress as best it can as it visits
872
		 * resources.</p>
873
		 * 
874
		 * <p><b>NOTE:</b> While the {@link SubMonitor} is provided to report status the
875
		 * visitor will not honor any cancellation requests.</p>
876
		 * 
877
		 * @param progress Used to report status. This visitor can <b>not</b> be
878
		 * canceled
879
		 * @param source The source of the events that should be used when creating
880
		 * resource events from visited resources
881
		 * 
882
		 * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE
883
		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
884
		 */
885
		protected ResourceDeltaVisitor(SubMonitor progress, byte source) {
886
			this.fProgress = progress;
887
			this.fSource = source;
888
			this.fBatchedResourceEvents = new LinkedHashMap(BATCH_UP_AMONT);
889
			this.fPredictedWorkRemaining = 1;
890
		}
891
		
892
		/**
893
		 * <p>Transforms each {@link IResourceDelta} into a {@link ResourceEvent}.
894
		 * Batches up these {@link ResourceEvent}s and then passes them onto the
895
		 * {@link ResourceEventProcessingJob}.</p>
896
		 * 
897
		 * <p><b>NOTE 2: </b> Be sure to call {@link ResourceDeltaVisitor#processBatchedResourceEvents()}
898
		 * after using this visitor to be sure any remaining events get passed onto the
899
		 * {@link ResourceEventProcessingJob}.</p>
900
		 * 
901
		 * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
902
		 * @see #processBatchedResourceEvents()
903
		 */
904
		public boolean visit(IResourceDelta delta) throws CoreException {
905
			//report status
906
			this.fProgress.subTask(
907
					NLS.bind(SSECoreMessages.IndexManager_0_resources_to_go, "" + fPredictedWorkRemaining) + //$NON-NLS-1$
908
					": " + delta.getFullPath().toString()); //$NON-NLS-1$
909
			
910
			//process delta if resource not hidden
911
			boolean visitChildren = false;
912
			IResource resource = delta.getResource();
913
			if(shouldVisit(resource.getName())) {
914
				//check if should index resource
915
				if(isResourceToIndex(resource.getType(), resource.getName())) {
916
				
917
					switch (delta.getKind()) {
918
						case IResourceDelta.CHANGED : {
919
							/* ignore any change that is not a CONTENT, REPLACED, TYPE,
920
							 * or MOVE_FROM change
921
							 */
922
							if( !((delta.getFlags() & IResourceDelta.CONTENT) != 0) &&
923
									!((delta.getFlags() & IResourceDelta.REPLACED) != 0) &&
924
									!((delta.getFlags() & IResourceDelta.TYPE) != 0) &&
925
									!(((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0))) {
926
								
927
								break;
928
							}
929
						}
930
						//$FALL-THROUGH$ it is intended that sometimes a change will fall through to add
931
						case IResourceDelta.ADDED : {
932
							if((delta.getFlags() & IResourceDelta.MOVED_FROM) != 0) {
933
								//create add move from action
934
								this.fBatchedResourceEvents.put(resource, new ResourceEvent(
935
										this.fSource,
936
										AbstractIndexManager.ACTION_ADD_MOVE_FROM,
937
										delta.getMovedFromPath()));
938
939
							} else {
940
								//create add action
941
								this.fBatchedResourceEvents.put(resource, new ResourceEvent(
942
										this.fSource,
943
										AbstractIndexManager.ACTION_ADD,
944
										null));
945
							}
946
							
947
							break;
948
						}
949
						case IResourceDelta.REMOVED : {
950
							if((delta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
951
								//create remove move to action
952
								this.fBatchedResourceEvents.put(resource, new ResourceEvent(
953
										this.fSource,
954
										AbstractIndexManager.ACTION_REMOVE_MOVE_TO,
955
										delta.getMovedToPath()));
956
							} else {
957
								//create remove action
958
								this.fBatchedResourceEvents.put(resource, new ResourceEvent(
959
										this.fSource,
960
										AbstractIndexManager.ACTION_REMOVE,
961
										null));
962
							}
963
							break;
964
						}
965
					}
966
				}
967
					
968
				visitChildren = true;
969
			} else {
970
				visitChildren = false;
971
			}
972
			
973
			//deal with trying to report progress
974
			if(visitChildren) {
975
				this.fPredictedWorkRemaining += delta.getAffectedChildren().length;
976
			}
977
			this.fProgress.setWorkRemaining(this.fPredictedWorkRemaining);
978
			this.fProgress.worked(1);
979
			--this.fPredictedWorkRemaining;
980
			
981
			//batch up resource changes before sending them out
982
			if(this.fBatchedResourceEvents.size() >= BATCH_UP_AMONT) {
983
				this.processBatchedResourceEvents();
984
			}
985
			
986
			return visitChildren;
987
		}
988
		
989
		/**
990
		 * <p>Sends any batched up resource events created by this visitor to the
991
		 * {@link ResourceEventProcessingJob}.<p>
992
		 * 
993
		 * <p><b>NOTE:</b> This will be called every so often as the visitor is
994
		 * visiting resources but needs to be called a final time by the user of
995
		 * this visitor to be sure the final events are sent off</p>
996
		 */
997
		protected void processBatchedResourceEvents() {
998
			AbstractIndexManager.this.fResourceEventProcessingJob.addResourceEvents(
999
					this.fBatchedResourceEvents);
1000
			this.fBatchedResourceEvents.clear();
1001
		}
1002
	}
1003
	
1004
	/**
1005
	 * <p>Collects {@link ResourceEvent}s from the different sources and then processes
1006
	 * each one by calling {@link AbstractIndexManager#performAction(byte, byte, IResource, IPath)}
1007
	 * for each {@link ResourceEvent}.</p>
1008
	 * 
1009
	 * @see AbstractIndexManager#performAction(byte, byte, IResource, IPath)
1010
	 */
1011
	private class ResourceEventProcessingJob extends Job {
1012
		/** Length to delay when scheduling job */
1013
		private static final int DELAY = 500;
1014
		
1015
		/**
1016
		 * <p>Name of the file where resource events still to index
1017
		 * will be preserved for the next start up.</p>
1018
		 */
1019
		private static final String PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME = ".preservedResourceEvents";  //$NON-NLS-1$
1020
		
1021
		/**
1022
		 * <p>This needs to be updated if {@link #preserveRecievedResourceEvents()} ever
1023
		 * changes how it persists resource events so that {@link #loadPreservedRecievedResourceEvents(SubMonitor)}
1024
		 * knows when opening a file that the format is of the current version and if not
1025
		 * knows it does not know how to read the older version.</p>
1026
		 * 
1027
		 * @see #preserveRecievedResourceEvents()
1028
		 * @see #loadPreservedRecievedResourceEvents(SubMonitor)
1029
		 */
1030
		private static final long serialVersionUID = 1L;
1031
		
1032
		/** Whether this job has been paused or not */
1033
		private volatile boolean fIsPaused;
1034
		
1035
		/**
1036
		 * {@link Map}&lt{@link IResource}, {@link ResourceEvent}&gt
1037
		 * <p>The list of resources events to be processed</p>
1038
		 */
1039
		private Map fResourceEvents;
1040
		
1041
		/** Lock used when accessing {@link #fBatchedResourceEvents} */
1042
		private final Object fResourceEventsLock = new Object();
1043
		
1044
		/**
1045
		 * Locked used for allowing other jobs to wait on this job.  This job
1046
		 * will notify those waiting on this lock whenever it is done processing
1047
		 * all resource events it currently knows about.
1048
		 * 
1049
		 * @see #waitForConsistant(int)
1050
		 */
1051
		private final Object fToNotifyLock = new Object();
1052
		
1053
		/**
1054
		 * <p>Sets up this job as a long running system job</p>
1055
		 */
1056
		protected ResourceEventProcessingJob() {
1057
			super(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$
1058
					SSECoreMessages.IndexManager_Processing_resource_events);
1059
			
1060
			//set this up as a long running system job
1061
			this.setUser(false);
1062
			this.setSystem(true);
1063
			this.setPriority(Job.LONG);
1064
			
1065
			this.fIsPaused = false;
1066
			this.fResourceEvents = new LinkedHashMap();
1067
		}
1068
		
1069
		/**
1070
		 * <p>Loads any preserved {@link ResourceEvent}s from the last time {@link #stop(boolean)}
1071
		 * was invoked and schedules the job to be run</p>
1072
		 * 
1073
		 * <p><b>NOTE: </b>Should be used instead of of calling {@link Job#schedule()}
1074
		 * because this method also takes care of loading preserved state.</p>
1075
		 * 
1076
		 * @param loadPreservedResourceEvents <code>true</code> if should load any
1077
		 * preserved {@link ResourceEvent}s from the last time {@link #stop(boolean)}
1078
		 * was invoked
1079
		 * 
1080
		 * @return <code>true</code> if either <code>loadPreservedResourceEvents</code>
1081
		 * was false or there was success in loading the preserved {@link ResourceEvent}s.
1082
		 * If <code>false</code> then some {@link ResourceEvent}s may have been loosed
1083
		 * and to insure index consistency with the workspace a full workspace  re-index
1084
		 * is needed.
1085
		 * 
1086
		 * @see #stop(boolean)
1087
		 */
1088
		protected synchronized boolean start(boolean loadPreservedResourceEvents, 
1089
				SubMonitor progress) {
1090
			
1091
			boolean successLoadingPreserved = true;
1092
			
1093
			//attempt to load preserved resource events if requested
1094
			if(!loadPreservedResourceEvents) {
1095
				File preservedResourceEventsFile = this.getPreservedResourceEventsFile();
1096
				preservedResourceEventsFile.delete();
1097
			} else {
1098
				successLoadingPreserved = this.loadPreservedRecievedResourceEvents(progress);
1099
			}
1100
			
1101
			//start up the job
1102
			this.schedule();
1103
			
1104
			return successLoadingPreserved;
1105
		}
1106
		
1107
		/**
1108
		 * <p>Immediately stops the job and preserves any {@link ResourceEvent}s in the queue
1109
		 * to be processed by not yet processed if requested</p>
1110
		 * 
1111
		 * @param preserveResourceEvents <code>true</code> to preserve any {@link ResourceEvent}s
1112
		 * in the queue yet to be processed, <code>false</code> otherwise
1113
		 * 
1114
		 * @return <code>true</code> if either <code>preserveResourceEvents</code> is 
1115
		 * <code>false</code> or if there was success in preserving the {@link ResourceEvent}s
1116
		 * yet to be processed.  If <code>false</code> then the preserving failed and a
1117
		 * full workspace re-processing is needed the next time the manger is started
1118
		 * 
1119
		 * @throws InterruptedException This could happen when trying to cancel or join 
1120
		 * the job in progress, but it really shouldn't
1121
		 * 
1122
		 * @see #start(boolean, SubMonitor)
1123
		 */
1124
		protected synchronized boolean stop(boolean preserveResourceEvents) throws InterruptedException {
1125
			//this will not block indefinitely because it is known this job can be canceled
1126
			this.cancel();
1127
			this.join();
1128
			
1129
			//preserve if requested, else be sure no preserve file is left over for next start
1130
			boolean success = true;
1131
			if(preserveResourceEvents && this.hasResourceEventsToProcess()) {
1132
				success = this.preserveRecievedResourceEvents();
1133
			} else {
1134
				this.getPreservedResourceEventsFile().delete();
1135
			}
1136
			
1137
			return success;
1138
		}
1139
		
1140
		/**
1141
		 * @return <code>true</code> if job is currently running or paused
1142
		 * 
1143
		 * @see #pause()
1144
		 * @see #unPause()
1145
		 */
1146
		protected synchronized boolean isProcessing() {
1147
			return this.getState() != Job.NONE || this.fIsPaused;
1148
		}
1149
		
1150
		/**
1151
		 * <p>Un-pauses this job.  This has no effect if the job is already running.</p>
1152
		 * <p>This should be used in place of {@link Job#schedule()} to reset state
1153
		 * caused by calling {@link #pause()}</p>
1154
		 * 
1155
		 * @see #pause()
1156
		 */
1157
		protected synchronized void unPause() {
1158
			this.fIsPaused = false;
1159
			
1160
			//get the job running again depending on its current state
1161
			if(this.getState() == Job.SLEEPING) {
1162
				this.wakeUp(DELAY);
1163
			} else {
1164
				this.schedule(DELAY);
1165
			}
1166
		}
1167
		
1168
		/**
1169
		 * <p>Pauses this job, even if it is running</p>
1170
		 * <p>This should be used in place of {@link Job#sleep()} because {@link Job#sleep()}
1171
		 * will not pause a job that is already running but calling this will pause this job
1172
		 * even if it is running. {@link #unPause()} must be used to start this job again</p>
1173
		 * 
1174
		 * @see #unPause()
1175
		 */
1176
		protected synchronized void pause() {
1177
			//if job is already running this will force it to pause
1178
			this.fIsPaused = true;
1179
			
1180
			//this only works if the job is not running
1181
			this.sleep();
1182
		}
1183
		
1184
		/**
1185
		 * <p>Adds a batch of {@link ResourceEvent}s to the queue of events to be processed.
1186
		 * Will also un-pause the job if it is not already running</p>
1187
		 * 
1188
		 * @param resourceEvents {@link Map}&lt{@link IResource}, {@link ResourceEvent}&gt
1189
		 * A batch of {@link ResourceEvent}s to be processed
1190
		 * 
1191
		 * @see #addResourceEvent(ResourceEvent)
1192
		 * @see #unPause()
1193
		 */
1194
		protected void addResourceEvents(Map resourceEvents) {
1195
			Iterator iter = resourceEvents.keySet().iterator();
1196
			while(iter.hasNext()) {
1197
				IResource resource = (IResource)iter.next();
1198
				ResourceEvent resourceEvent = (ResourceEvent)resourceEvents.get(resource);
1199
				addResourceEvent(resource, resourceEvent);
1200
			}
1201
			
1202
			//un-pause the processor if it is not already running
1203
			if(!isProcessing()) {
1204
				this.unPause();
1205
			}
1206
		}
1207
		
1208
		/**
1209
		 * <p>Gets the number of {@link ResourceEvent}s left to process by this job. This
1210
		 * count is only valid for the exact moment it is returned because events are
1211
		 * constantly being added and removed from the queue of events to process</p>
1212
		 * 
1213
		 * @return the number of {@link ResourceEvent}s left to process
1214
		 */
1215
		protected int getNumResourceEventsToProcess() {
1216
			return this.fResourceEvents.size();
1217
		}
1218
		
1219
		/**
1220
		 * <p>Blocks until either the given timeout elapses (0 means never to timeout), or
1221
		 * there are currently no {@link ResourceEvent}s to process or being processed
1222
		 * by this job</p>
1223
		 * 
1224
		 * @param timeout block until either this timeout elapses (0 means never to timeout)
1225
		 * or there are currently no {@link ResourceEvent}s to process or being processed
1226
		 * by this job
1227
		 * 
1228
		 * @throws InterruptedException This can happen when waiting for a lock
1229
		 */
1230
		protected void waitForConsistant(int timeout) throws InterruptedException {
1231
			if(hasResourceEventsToProcess() || isProcessing()) {
1232
				synchronized (this.fToNotifyLock) {
1233
					this.fToNotifyLock.wait(timeout);
1234
				}
1235
			}
1236
		}
1237
		
1238
		/**
1239
		 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
1240
		 */
1241
		protected IStatus run(IProgressMonitor monitor) {
1242
			try {
1243
				//report status
1244
				SubMonitor progress = SubMonitor.convert(monitor);
1245
				
1246
				while(!this.fIsPaused && !monitor.isCanceled() && this.hasResourceEventsToProcess()) {
1247
					//report status
1248
					progress.setTaskName(AbstractIndexManager.this.fName + ": " + //$NON-NLS-1$
1249
							NLS.bind(SSECoreMessages.IndexManager_Indexing_0_Files,
1250
									"" + getNumResourceEventsToProcess())); //$NON-NLS-1$
1251
					progress.setWorkRemaining(getNumResourceEventsToProcess());
1252
					
1253
					//get the next event to process
1254
					ResourceEvent resourceEvent = null;
1255
					IResource resource = null;
1256
					synchronized (this.fResourceEventsLock) {
1257
						resource = (IResource) this.fResourceEvents.keySet().iterator().next();
1258
						resourceEvent = (ResourceEvent)this.fResourceEvents.remove(resource);
1259
					}
1260
					
1261
					//report status
1262
					monitor.subTask(resource.getName());
1263
					
1264
					//perform action safely
1265
					final byte source = resourceEvent.fSource;
1266
					final byte action = resourceEvent.fAction;
1267
					final IResource finResource = resource;
1268
					final IPath movePath = resourceEvent.fMovePath;
1269
					SafeRunner.run(new ISafeRunnable() {
1270
						public void run() throws Exception {
1271
							AbstractIndexManager.this.performAction(source, action,
1272
									finResource, movePath);
1273
						}
1274
	
1275
						public void handleException(Throwable e) {
1276
							Logger.logException("Error while performing an update to the index. " + //$NON-NLS-1$
1277
									AbstractIndexManager.LOG_ERROR_INDEX_INVALID, e);
1278
						}
1279
					});
1280
					
1281
					//report progress
1282
					progress.worked(1);
1283
					
1284
					//avoid dead locks
1285
					Job.getJobManager().currentJob().yieldRule(monitor);
1286
				}
1287
				
1288
				//done work
1289
				monitor.done();
1290
			} finally {
1291
				//want to be sure we notify no matter how we exit
1292
				this.notifyIfConsistant();
1293
			}
1294
			
1295
			/* if canceled then return CANCEL,
1296
			 * else if done or paused return OK
1297
			 */
1298
			IStatus exitStatus;
1299
			if(monitor.isCanceled()) {
1300
				exitStatus = Status.CANCEL_STATUS;
1301
			} else {
1302
				exitStatus = Status.OK_STATUS;
1303
			}
1304
			
1305
			return exitStatus;
1306
		}
1307
		
1308
		/**
1309
		 * <p>If resource not already scheduled to be processed, schedule it
1310
		 * else if resource already scheduled to be processed, update the action only if
1311
		 * the new action comes from a resource change event.</p>
1312
		 * 
1313
		 * <p>Ignore other sources for updating existing resource events because all other
1314
		 * sources are "start-up" type sources and thus only {@link ResourceEvent} with a
1315
		 * source of a resource change event trump existing events.</p>
1316
		 * 
1317
		 * @param resourceEvent {@link ResourceEvent} to be processed by this job
1318
		 */
1319
		private void addResourceEvent(IResource resource, ResourceEvent resourceEvent) {
1320
			
1321
			synchronized (this.fResourceEventsLock) {
1322
				/* if resource not already scheduled to be processed, schedule it
1323
				 * else if resource already scheduled to be processed, update the action only if
1324
				 * 		the new action comes from a resource change event
1325
				 */
1326
				if(!this.fResourceEvents.containsKey(resource)) {
1327
					this.fResourceEvents.put(resource, resourceEvent);
1328
				} else if(resourceEvent.fSource == AbstractIndexManager.SOURCE_RESROUCE_CHANGE) {
1329
					((ResourceEvent)this.fResourceEvents.get(resource)).fAction = resourceEvent.fAction;
1330
				} else {
1331
					//Purposely ignoring all other resource events
1332
				}
1333
			}
1334
		}
1335
		
1336
		/**
1337
		 * @return <code>true</code> if there are any resources to process,
1338
		 * <code>false</code> otherwise
1339
		 */
1340
		private boolean hasResourceEventsToProcess() {
1341
			return !this.fResourceEvents.isEmpty();
1342
		}
1343
		
1344
		/**
1345
		 * <p>Preserves all of the resource events that have been received by this
1346
		 * manager but not yet processed</p>
1347
		 * 
1348
		 * <p>If this operation was successful then the next time the manager starts
1349
		 * it can load these events and process them.  If it was not successful then a
1350
		 * full re-processing of the entire workspace will need to take place to be
1351
		 * sure the index is consistent.</p>
1352
		 * 
1353
		 * <p><b>NOTE:</b> If this method changes how it preserves these events then
1354
		 * {@link #serialVersionUID} will need to be incremented so that the manger
1355
		 * does not attempt to load an old version of the file that may exist in a users
1356
		 * workspace.  Also {@link #loadPreservedRecievedResourceEvents(SubMonitor)} will
1357
		 * have to be updated to load the new file structure</p>
1358
		 * 
1359
		 * @return <code>true</code> if successfully preserved the resources the resource
1360
		 * events that have been received by not yet processed, <code>false</code> otherwise
1361
		 * 
1362
		 * @see #serialVersionUID
1363
		 * @see #loadPreservedRecievedResourceEvents(SubMonitor)
1364
		 */
1365
		private boolean preserveRecievedResourceEvents() {
1366
			File preservedResourceEventsFile = this.getPreservedResourceEventsFile();
1367
			boolean success = true;
1368
			
1369
			synchronized (this.fResourceEventsLock) {
1370
				DataOutputStream dos = null;
1371
				try {
1372
					//if file already exists delete it
1373
					if(preservedResourceEventsFile.exists()) {
1374
						preservedResourceEventsFile.delete();
1375
						preservedResourceEventsFile.createNewFile();
1376
					}
1377
					
1378
					//create output objects
1379
					FileOutputStream fos = new FileOutputStream(preservedResourceEventsFile);
1380
					BufferedOutputStream bos = new BufferedOutputStream(fos);
1381
					dos = new DataOutputStream(bos);
1382
					
1383
					//write serial version
1384
					dos.writeLong(serialVersionUID);
1385
					
1386
					//write size
1387
					dos.writeInt(this.getNumResourceEventsToProcess());
1388
					
1389
					//write out all the information needed to restore the resource events to process
1390
					Iterator iter = this.fResourceEvents.keySet().iterator();
1391
					while(iter.hasNext()) {
1392
						IResource resource = (IResource)iter.next();
1393
						ResourceEvent resourceEvent = (ResourceEvent)this.fResourceEvents.get(resource);
1394
						
1395
						if(resourceEvent.fSource != AbstractIndexManager.SOURCE_WORKSPACE_SCAN) {
1396
							//write out information
1397
							dos.writeByte(resourceEvent.fAction);
1398
							dos.writeByte(resource.getType());
1399
							dos.writeBytes(resource.getLocation().toPortableString());
1400
							dos.writeByte('\0');
1401
							if(resourceEvent.fMovePath != null) {
1402
								dos.writeBytes(resourceEvent.fMovePath.toPortableString());
1403
							}
1404
							dos.writeByte('\0');
1405
						}
1406
					}
1407
					
1408
					this.fResourceEvents.clear();
1409
					
1410
					dos.flush();
1411
				} catch (FileNotFoundException e) {
1412
					Logger.logException(AbstractIndexManager.this.fName +
1413
							": Exception while opening file to preserve resrouces to index.", //$NON-NLS-1$
1414
							e);
1415
					success = false;
1416
				} catch (IOException e) {
1417
					Logger.logException(AbstractIndexManager.this.fName +
1418
							": Exception while writing to file to preserve resrouces to index.", //$NON-NLS-1$
1419
							e);
1420
					success = false;
1421
				} finally {
1422
					//be sure to close output
1423
					if(dos != null) {
1424
						try {
1425
							dos.close();
1426
						} catch (IOException e) {
1427
							Logger.logException(AbstractIndexManager.this.fName +
1428
									": Exception while closing file with preserved resources to index.", //$NON-NLS-1$
1429
									e);
1430
							success = false;
1431
						}
1432
					}
1433
				}
1434
				
1435
				//if failed, for consistency must do a full re-process next workspace load
1436
				if(!success) {
1437
					preservedResourceEventsFile.delete();
1438
				}
1439
			}
1440
			
1441
			return success;
1442
		}
1443
		
1444
		/**
1445
		 * <p>Loads the received resource events that were preserved during the manger's
1446
		 * last shut down so they can be processed now</p>
1447
		 * 
1448
		 * <p>If this operation is not successful then a full re-processing of the
1449
		 * entire workspace is needed to be sure the index is consistent.</p>
1450
		 * 
1451
		 * @param progress used to report status of loading the preserved received resource events
1452
		 * @return <code>true</code> if the loading of the preserved received resource events
1453
		 * was successful, <code>false</code> otherwise.
1454
		 * 
1455
		 * @see #serialVersionUID
1456
		 * @see #preserveRecievedResourceEvents()
1457
		 */
1458
		private boolean loadPreservedRecievedResourceEvents(SubMonitor progress) {
1459
			progress.subTask(SSECoreMessages.IndexManager_processing_deferred_resource_changes);
1460
			
1461
			boolean success = true;
1462
			File preservedResourceEventsFile = this.getPreservedResourceEventsFile();
1463
			
1464
			if(preservedResourceEventsFile.exists()) {
1465
				Map preservedResrouceEvents = null;
1466
				
1467
				DataInputStream dis = null;
1468
				try {
1469
					FileInputStream fis = new FileInputStream(preservedResourceEventsFile);
1470
					BufferedInputStream bis = new BufferedInputStream(fis);
1471
					dis = new DataInputStream(bis);
1472
					
1473
					//check serial version first
1474
					long preservedSerialVersionUID = dis.readLong();
1475
					if(preservedSerialVersionUID == serialVersionUID) {
1476
						
1477
						//read each record
1478
						int numberOfRecords = dis.readInt();
1479
						preservedResrouceEvents = new LinkedHashMap(numberOfRecords);
1480
						progress.setWorkRemaining(numberOfRecords);
1481
						for(int i = 0; i < numberOfRecords; ++i) {
1482
							//action is first byte
1483
							byte action = dis.readByte();
1484
							
1485
							//file type is the next byte 
1486
							byte fileType = dis.readByte();
1487
							
1488
							//resource location are the next bytes
1489
							StringBuffer resourceLocationBuffer = new StringBuffer();
1490
							byte b = dis.readByte();
1491
							while(b != '\0') {
1492
								resourceLocationBuffer.append(b);
1493
							}
1494
							
1495
							//move path are the next bytes
1496
							StringBuffer movePathBuffer = new StringBuffer();
1497
							b = dis.readByte();
1498
							while(b != '\0') {
1499
								movePathBuffer.append(b);
1500
							}
1501
							
1502
							//get the resource
1503
							IResource resource = null;
1504
							IPath resourcePath = new Path(resourceLocationBuffer.toString());
1505
							if(fileType == IResource.FILE) {
1506
								resource =
1507
									ResourcesPlugin.getWorkspace().getRoot().getFile(resourcePath);
1508
							} else {
1509
								resource =
1510
									ResourcesPlugin.getWorkspace().getRoot().getFolder(resourcePath);
1511
							}
1512
							
1513
							//get the move path
1514
							IPath movePath = null;
1515
							if(movePathBuffer.length() != 0) {
1516
								movePath = new Path(movePathBuffer.toString());
1517
							}
1518
							
1519
							//add the object to the list of of preserved resources
1520
							preservedResrouceEvents.put(resource, new ResourceEvent(
1521
									AbstractIndexManager.SOURCE_PRESERVED_RESOURCES_TO_INDEX,
1522
									action, movePath));
1523
							
1524
							progress.worked(1);
1525
						}	
1526
					} else {
1527
						success = false;
1528
					}
1529
				} catch (FileNotFoundException e) {
1530
					Logger.logException(AbstractIndexManager.this.fName +
1531
							": Exception while opening file to read preserved resrouces to index.", //$NON-NLS-1$
1532
							e);
1533
					success = false;
1534
				} catch (IOException e) {
1535
					Logger.logException(AbstractIndexManager.this.fName +
1536
							": Exception while reading from file of preserved resrouces to index.", //$NON-NLS-1$
1537
							e);
1538
					success = false;
1539
				} finally {
1540
					if(dis != null) {
1541
						try {
1542
							dis.close();
1543
						} catch (IOException e) {
1544
							Logger.logException(AbstractIndexManager.this.fName +
1545
									": Exception while closing file of preserved resources" + //$NON-NLS-1$
1546
									" to index that was just read.  This should have no" + //$NON-NLS-1$
1547
									" effect on the consistency of the index.", //$NON-NLS-1$
1548
									e);
1549
						}
1550
					}
1551
				}
1552
				
1553
				//if success loading preserved then add to master list
1554
				if(success) {
1555
					synchronized (this.fResourceEventsLock) {
1556
						Iterator iter = preservedResrouceEvents.keySet().iterator();
1557
						while(iter.hasNext()) {
1558
							IResource resource = (IResource)iter.next();
1559
							ResourceEvent event = (ResourceEvent)preservedResrouceEvents.get(resource);
1560
							this.fResourceEvents.put(resource, event);
1561
						}
1562
					}
1563
				} else {
1564
					//failed reading file, so delete it
1565
					preservedResourceEventsFile.delete();
1566
				}
1567
			}
1568
			
1569
			return success;
1570
		}
1571
		
1572
		/**
1573
		 * @return {@link File} that contains any resource events received but not processed
1574
		 * by this manager the last time it shutdown. This file may or may not actually
1575
		 * exist.
1576
		 * 
1577
		 * @see #preserveRecievedResourceEvents()
1578
		 * @see #loadPreservedRecievedResourceEvents(SubMonitor)
1579
		 */
1580
		private File getPreservedResourceEventsFile() {
1581
			IPath preservedResroucesToIndexPath =
1582
				AbstractIndexManager.this.getWorkingLocation().append(
1583
						PRESERVED_RESOURCE_EVENTS_TO_PROCESS_FILE_NAME);
1584
			return new File(preservedResroucesToIndexPath.toOSString());
1585
		}
1586
		
1587
		/**
1588
		 * <p>If all resource events have been processed 
1589
		 */
1590
		private void notifyIfConsistant() {
1591
			if(!this.hasResourceEventsToProcess()) {
1592
				synchronized (this.fToNotifyLock) {
1593
					this.fToNotifyLock.notifyAll();
1594
				}
1595
			}
1596
		}
1597
	}
1598
	
1599
	/**
1600
	 * <p>Represents a resource that was discovered by this manager.  Contains
1601
	 * all the information this manager and the index needs to know about this
1602
	 * resource.  Such has how the manager was notified about this resource and
1603
	 * the type of action occurring on the resource.</p>
1604
	 */
1605
	private static class ResourceEvent {
1606
		/**
1607
		 * <p>The source of this resource event</p>
1608
		 * 
1609
		 * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE
1610
		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
1611
		 * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN
1612
		 * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX
1613
		 */
1614
		protected byte fSource;
1615
		
1616
		/**
1617
		 * <p>The action that the index should take with this resource</p>
1618
		 *
1619
		 * @see AbstractIndexManager#ACTION_ADD
1620
		 * @see AbstractIndexManager#ACTION_REMOVE
1621
		 * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
1622
		 * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
1623
		 */
1624
		protected byte fAction;
1625
		
1626
		/**
1627
		 * 
1628
		 * <p>If the {@link #fAction} is {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} or
1629
		 * {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this field will have the
1630
		 * path the resource was moved from or moved to. Else this field will be <code>null</code></p>
1631
		 * 
1632
		 * <p><b>NOTE: </b>Maybe <code>null</code>.</p>
1633
		 * 
1634
		 * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
1635
		 * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
1636
		 */
1637
		protected IPath fMovePath;
1638
		
1639
		/**
1640
		 * <p>Creates a resource event that the index needs to react to in some way</p>
1641
		 * 
1642
		 * @param source source that the manger used to learn of this resource
1643
		 * @param action action the index should take on this resource
1644
		 * @param resource resource that the index should know about
1645
		 * @param movePath if action is {@link AbstractIndexManager#ACTION_ADD_MOVE_FROM} or
1646
		 * {@link AbstractIndexManager#ACTION_REMOVE_MOVE_TO} then this should be the path the
1647
		 * resource was moved from or moved to respectively, else should be <code>null</code>
1648
		 * 
1649
		 * @see AbstractIndexManager#SOURCE_RESROUCE_CHANGE
1650
		 * @see AbstractIndexManager#SOURCE_SAVED_STATE
1651
		 * @see AbstractIndexManager#SOURCE_WORKSPACE_SCAN
1652
		 * @see AbstractIndexManager#SOURCE_PRESERVED_RESOURCES_TO_INDEX
1653
		 * 
1654
		 * @see AbstractIndexManager#ACTION_ADD
1655
		 * @see AbstractIndexManager#ACTION_REMOVE
1656
		 * @see AbstractIndexManager#ACTION_ADD_MOVE_FROM
1657
		 * @see AbstractIndexManager#ACTION_REMOVE_MOVE_TO
1658
		 */
1659
		protected ResourceEvent(byte source, byte action, IPath movePath) {
1660
			this.fSource = source;
1661
			this.fAction = action;
1662
			this.fMovePath = movePath;
1663
		}
1664
	}
1665
}
(-)src/org/eclipse/wst/sse/core/internal/SSECoreMessages.java (-1 / +10 lines)
Lines 1-5 Link Here
1
/**********************************************************************
1
/**********************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others. All rights reserved.   This
2
 * Copyright (c) 2005, 2010 IBM Corporation and others. All rights reserved.   This
3
 * program and the accompanying materials are made available under the terms of
3
 * program and the accompanying materials are made available under the terms of
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
4
 * the Eclipse Public License v1.0 which accompanies this distribution, and is
5
 * available at http://www.eclipse.org/legal/epl-v10.html
5
 * available at http://www.eclipse.org/legal/epl-v10.html
Lines 36-39 Link Here
36
	public static String TaskScanningJob_0;
36
	public static String TaskScanningJob_0;
37
	public static String TaskScanningJob_1;
37
	public static String TaskScanningJob_1;
38
	public static String Migrate_Charset;
38
	public static String Migrate_Charset;
39
	
40
	public static String IndexManager_starting;
41
	public static String IndexManager_Indexing_0_Files;	
42
	public static String IndexManager_processing_deferred_resource_changes;
43
	public static String IndexManager_Processing_entire_workspace_for_the_first_time;
44
	public static String IndexManager_processing_recent_resource_changes;
45
	public static String IndexManager_0_resources_to_go;
46
	public static String IndexManager_Waiting_for_0;
47
	public static String IndexManager_Processing_resource_events;
39
}
48
}
(-)src/org/eclipse/wst/sse/core/internal/SSECorePluginResources.properties (-1 / +10 lines)
Lines 1-5 Link Here
1
###############################################################################
1
###############################################################################
2
# Copyright (c) 2001, 2005 IBM Corporation and others.
2
# Copyright (c) 2001, 2010 IBM Corporation and others.
3
# All rights reserved. This program and the accompanying materials
3
# All rights reserved. This program and the accompanying materials
4
# are made available under the terms of the Eclipse Public License v1.0
4
# are made available under the terms of the Eclipse Public License v1.0
5
# which accompanies this distribution, and is available at
5
# which accompanies this distribution, and is available at
Lines 19-21 Link Here
19
TaskScanningJob_1=Errors while detecting Tasks
19
TaskScanningJob_1=Errors while detecting Tasks
20
###############################################################################
20
###############################################################################
21
Migrate_Charset=Migrate Charset
21
Migrate_Charset=Migrate Charset
22
23
IndexManager_starting=Starting
24
IndexManager_Indexing_0_Files=Indexing {0} Files
25
IndexManager_processing_deferred_resource_changes=Processing deferred resource changes
26
IndexManager_Processing_entire_workspace_for_the_first_time=Processing entire workspace for the first time
27
IndexManager_processing_recent_resource_changes=Processing recent resource changes
28
IndexManager_0_resources_to_go={0} resources to go
29
IndexManager_Waiting_for_0=Waiting for {0}
30
IndexManager_Processing_resource_events=Processing Resource Events

Return to bug 323392