| /******************************************************************************* |
| * Copyright (c) 2004, 2009 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.web.core.javascript.search; |
| |
| import java.io.File; |
| import java.util.zip.CRC32; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.wst.jsdt.core.IJavaScriptElement; |
| import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchConstants; |
| import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchScope; |
| import org.eclipse.wst.jsdt.core.search.SearchDocument; |
| import org.eclipse.wst.jsdt.core.search.SearchEngine; |
| import org.eclipse.wst.jsdt.core.search.SearchPattern; |
| import org.eclipse.wst.jsdt.core.search.SearchRequestor; |
| import org.eclipse.wst.jsdt.internal.core.JavaModelManager; |
| import org.eclipse.wst.jsdt.web.core.internal.JsCoreMessages; |
| import org.eclipse.wst.jsdt.web.core.internal.JsCorePlugin; |
| import org.eclipse.wst.jsdt.web.core.internal.Logger; |
| import org.eclipse.wst.jsdt.web.core.internal.validation.Util; |
| import org.eclipse.wst.jsdt.web.core.javascript.JsNameManglerUtil; |
| /** |
| * |
| |
| * Provisional API: This class/interface is part of an interim API that is still under development and expected to |
| * change significantly before reaching stability. It is being made available at this early stage to solicit feedback |
| * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken |
| * (repeatedly) as the API evolves. |
| *(copied from JSP) |
| * Central access to java indexing and search. All contact between JDT indexing |
| * and Searching should be done through here. |
| * |
| * Clients should access the methods of this class via the single instance via |
| * <code>getInstance()</code>. |
| * |
| * @author pavery |
| */ |
| public class JsSearchSupport { |
| |
| // for debugging |
| static final boolean DEBUG; |
| static { |
| String value = Platform.getDebugOption("org.eclipse.wst.jsdt.web.core/debug/jssearch"); //$NON-NLS-1$ |
| DEBUG = value != null && value.equalsIgnoreCase("true"); //$NON-NLS-1$ |
| } |
| |
| private static JsSearchSupport singleton = null; |
| |
| private JsSearchParticipant fParticipant = null; |
| |
| private IPath fJsPluginLocation = null; |
| |
| // pa_TODO may be slow (esp for indexing entire workspace) |
| private final CRC32 fChecksumCalculator = new CRC32(); |
| |
| /** main cancel montior for all search support */ |
| private final IProgressMonitor fMonitor = new NullProgressMonitor(); |
| |
| private JsSearchSupport() { |
| // force use of single instance |
| } |
| |
| /** |
| * This operation ensures that the live resource's search markers show up in |
| * the open editor. It also allows the ability to pass in a ProgressMonitor |
| */ |
| private class SearchJob extends Job implements IJavaScriptSearchConstants { |
| |
| String fSearchText = ""; //$NON-NLS-1$ |
| |
| IJavaScriptSearchScope fScope = null; |
| |
| int fSearchFor = FIELD; |
| |
| int fLimitTo = ALL_OCCURRENCES; |
| |
| int fMatchMode = SearchPattern.R_PATTERN_MATCH; |
| |
| // boolean fIsCaseSensitive = false; |
| |
| SearchRequestor fRequestor = null; |
| |
| IJavaScriptElement fElement = null; |
| |
| // constructor w/ java element |
| public SearchJob(IJavaScriptElement element, IJavaScriptSearchScope scope, SearchRequestor requestor) { |
| |
| super(JsCoreMessages.JSP_Search + element.getElementName()); |
| this.fElement = element; |
| this.fScope = scope; |
| this.fRequestor = requestor; |
| } |
| |
| // constructor w/ search text |
| public SearchJob(String searchText, IJavaScriptSearchScope scope, int searchFor, int limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor) { |
| |
| super(JsCoreMessages.JSP_Search + searchText); |
| this.fSearchText = searchText; |
| this.fScope = scope; |
| this.fSearchFor = searchFor; |
| this.fLimitTo = limitTo; |
| this.fMatchMode = matchMode; |
| // this.fIsCaseSensitive = isCaseSensitive; |
| this.fRequestor = requestor; |
| } |
| |
| public IStatus run(IProgressMonitor jobMonitor) { |
| |
| if ((jobMonitor != null) && jobMonitor.isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| if (JsSearchSupport.getInstance().isCanceled()) { |
| return Status.CANCEL_STATUS; |
| } |
| |
| SearchPattern javaSearchPattern = null; |
| // if an element is available, use that to create search pattern |
| // (eg. LocalVariable) |
| // otherwise use the text and other paramters |
| if (this.fElement != null) { |
| javaSearchPattern = SearchPattern.createPattern(this.fElement, this.fLimitTo); |
| } else { |
| javaSearchPattern = SearchPattern.createPattern(this.fSearchText, this.fSearchFor, this.fLimitTo, this.fMatchMode); |
| } |
| |
| if (javaSearchPattern != null) { |
| JsSearchParticipant[] participants = { getSearchParticipant() }; |
| SearchEngine engine = new SearchEngine(); |
| try { |
| if (jobMonitor != null) { |
| jobMonitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$ |
| } |
| engine.search(javaSearchPattern, participants, this.fScope, this.fRequestor, jobMonitor); |
| } catch (CoreException e) { |
| if (DEBUG) { |
| Logger.logException(e); |
| } |
| } |
| // non-CoreExceptions will permanently stall the Worker thread |
| catch (Exception e) { |
| if (DEBUG) { |
| Logger.logException(e); |
| } |
| } finally { |
| if (jobMonitor != null) { |
| jobMonitor.done(); |
| } |
| } |
| } |
| return Status.OK_STATUS; |
| } |
| } |
| |
| // end SearchJob |
| /** |
| * Runnable forces caller to wait until finished (as opposed to using a Job) |
| */ |
| private class SearchRunnable implements IWorkspaceRunnable, IJavaScriptSearchConstants { |
| |
| String fSearchText = ""; //$NON-NLS-1$ |
| |
| IJavaScriptSearchScope fScope = null; |
| |
| int fSearchFor = FIELD; |
| |
| int fLimitTo = ALL_OCCURRENCES; |
| |
| int fMatchMode = SearchPattern.R_PATTERN_MATCH; |
| |
| //boolean fIsCaseSensitive = false; |
| |
| SearchRequestor fRequestor = null; |
| |
| IJavaScriptElement fElement = null; |
| |
| // constructor w/ java element |
| public SearchRunnable(IJavaScriptElement element, IJavaScriptSearchScope scope, SearchRequestor requestor) { |
| |
| this.fElement = element; |
| this.fScope = scope; |
| this.fRequestor = requestor; |
| } |
| |
| // constructor w/ search text |
| // public SearchRunnable(String searchText, IJavaScriptSearchScope scope, int searchFor, int limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor) { |
| // |
| // this.fSearchText = searchText; |
| // this.fScope = scope; |
| // this.fSearchFor = searchFor; |
| // this.fLimitTo = limitTo; |
| // this.fMatchMode = matchMode; |
| // this.fIsCaseSensitive = isCaseSensitive; |
| // this.fRequestor = requestor; |
| // } |
| |
| public void run(IProgressMonitor monitor) throws CoreException { |
| |
| if ((monitor != null) && monitor.isCanceled()) { |
| return; |
| } |
| if (JsSearchSupport.getInstance().isCanceled()) { |
| return; |
| } |
| |
| SearchPattern javaSearchPattern = null; |
| // if an element is available, use that to create search pattern |
| // (eg. LocalVariable) |
| // otherwise use the text and other paramters |
| if (this.fElement != null) { |
| javaSearchPattern = SearchPattern.createPattern(this.fElement, fLimitTo); |
| } else { |
| javaSearchPattern = SearchPattern.createPattern(fSearchText, fSearchFor, fLimitTo, fMatchMode); |
| } |
| |
| if (javaSearchPattern != null) { |
| JsSearchParticipant[] participants = { getSearchParticipant() }; |
| SearchEngine engine = new SearchEngine(); |
| try { |
| if (monitor != null) { |
| monitor.beginTask("", 0); //$NON-NLS-1$ |
| } |
| engine.search(javaSearchPattern, participants, fScope, fRequestor, monitor); |
| } catch (CoreException e) { |
| Logger.logException(e); |
| //throw e; |
| } |
| // non-CoreExceptions will permanently stall the Worker thread |
| catch (Exception e) { |
| Logger.logException(e); |
| } finally { |
| if (monitor != null) { |
| monitor.done(); |
| } |
| } |
| } |
| } |
| } |
| |
| // end SearchRunnable |
| |
| /** |
| * Clients should access the methods of this class via the single instance |
| * via getInstance() |
| * |
| * @return |
| */ |
| public synchronized static JsSearchSupport getInstance() { |
| |
| if (singleton == null) { |
| singleton = new JsSearchSupport(); |
| } |
| return singleton; |
| } |
| |
| /** |
| * Utility method to check if a file is a jsp file (since this is done |
| * frequently) |
| */ |
| public static boolean isJsp(IFile file) { |
| return Util.isJsType(file.getName()); |
| // (pa) 20051025 removing deep content type check |
| // because this method is called frequently |
| // and IO is expensive |
| // boolean isJsp = false; |
| // |
| // if (file != null && file.exists()) { |
| // |
| // IContentType contentTypeJSP = Platform.getContentTypeManager().getContentType(ContentTypeIdForJSP.ContentTypeID_JSP); |
| // // check this before description, it's less expensive |
| // if (contentTypeJSP.isAssociatedWith(file.getName())) { |
| // isJsp = true; |
| // } |
| // } |
| // |
| // return isJsp; |
| } |
| |
| /** |
| * schedules a search document representing this JSP file for indexing (by |
| * the java indexer) |
| * |
| * @param file |
| * the JSP file |
| * @return true if indexing was successful, false otherwise |
| * @throws CoreException |
| */ |
| public SearchDocument addJspFile(IFile file) { |
| if (JsSearchSupport.getInstance().isCanceled() || !file.isAccessible()) { |
| return null; |
| } |
| |
| if (DEBUG) { |
| System.out.println("adding JSP file:" + file.getFullPath()); //$NON-NLS-1$ |
| } |
| |
| // create |
| SearchDocument delegate = createSearchDocument(file); |
| // null if not a jsp file |
| if (delegate != null) { |
| try { |
| getSearchParticipant().scheduleDocumentIndexing(delegate, computeIndexLocation(file.getParent().getFullPath())); |
| } catch (Exception e) { |
| // ensure that failure here doesn't keep other documents from |
| // being indexed |
| // if peformed in a batch call (like JSPIndexManager) |
| if (DEBUG) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| if (DEBUG) { |
| System.out.println("scheduled" + delegate + "for indexing"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| return delegate; |
| } |
| |
| /** |
| * Perform a java search w/ the given parameters. Runs in a background Job |
| * (results may still come in after this method call) |
| * |
| * @param searchText |
| * the string of text to search on |
| * @param searchFor |
| * IJavaScriptSearchConstants.TYPE, METHOD, FIELD, PACKAGE, etc... |
| * @param limitTo |
| * IJavaScriptSearchConstants.DECLARATIONS, |
| * IJavaScriptSearchConstants.REFERENCES, |
| * IJavaScriptSearchConstants.IMPLEMENTORS, or |
| * IJavaScriptSearchConstants.ALL_OCCURRENCES |
| * @param matchMode |
| * allow * wildcards or not |
| * @param isCaseSensitive |
| * @param requestor |
| * passed in to accept search matches (and do "something" with |
| * them) |
| */ |
| public void search(String searchText, IJavaScriptSearchScope scope, int searchFor, int limitTo, int matchMode, boolean isCaseSensitive, SearchRequestor requestor) { |
| |
| JsIndexManager.getInstance().rebuildIndexIfNeeded(); |
| |
| SearchJob job = new SearchJob(searchText, scope, searchFor, limitTo, matchMode, isCaseSensitive, requestor); |
| setCanceled(false); |
| job.setUser(true); |
| // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5032 |
| // pops up user operation blocked dialog if you perform a long search, |
| // then open a file because it locks the workspace |
| //job.setRule(ResourcesPlugin.getWorkspace().getRoot()); |
| job.schedule(); |
| } |
| |
| /** |
| * Search for an IJavaScriptElement, constrained by the given parameters. Runs in |
| * a background Job (results may still come in after this method call) |
| * |
| * @param element |
| * @param scope |
| * @param requestor |
| */ |
| public void search(IJavaScriptElement element, IJavaScriptSearchScope scope, SearchRequestor requestor) { |
| |
| JsIndexManager.getInstance().rebuildIndexIfNeeded(); |
| |
| SearchJob job = new SearchJob(element, scope, requestor); |
| setCanceled(false); |
| job.setUser(true); |
| // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=5032 |
| //job.setRule(ResourcesPlugin.getWorkspace().getRoot()); |
| job.schedule(); |
| } |
| |
| /** |
| * Search for an IJavaScriptElement, constrained by the given parameters. Runs in |
| * an IWorkspace runnable (results will be reported by the end of this |
| * method) |
| * |
| * @param element |
| * @param scope |
| * @param requestor |
| */ |
| public void searchRunnable(IJavaScriptElement element, IJavaScriptSearchScope scope, SearchRequestor requestor) { |
| |
| JsIndexManager.getInstance().rebuildIndexIfNeeded(); |
| |
| SearchRunnable searchRunnable = new SearchRunnable(element, scope, requestor); |
| try { |
| setCanceled(false); |
| ResourcesPlugin.getWorkspace().run(searchRunnable, JsSearchSupport.getInstance().getProgressMonitor()); |
| } catch (CoreException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| /** |
| * @param jspFile |
| * @return SearchDocument if the file is not null, exists, and is a JSP |
| * file, otherwise null. |
| */ |
| private SearchDocument createSearchDocument(IFile jspFile) { |
| |
| JSDTSearchDocumentDelegate delegate = null; |
| if ((jspFile != null) && jspFile.exists() && isJsp(jspFile)) { |
| |
| delegate = new JSDTSearchDocumentDelegate(new JsSearchDocument(jspFile.getFullPath().toString(), getSearchParticipant())); |
| } |
| return delegate; |
| |
| } |
| |
| /** |
| * Centralized place to access JSPSearchDocuments (used by |
| * JSPSearchParticipant and JSPSearchRequestor) |
| * |
| * @param searchDocPath |
| * @param doc |
| * @return the JSPSearchDocument or null if one is not found |
| */ |
| public SearchDocument getSearchDocument(String searchDocPath) { |
| |
| SearchDocument delegate = null; |
| IFile f = fileForCUPath(searchDocPath); |
| if (f != null) { |
| delegate = createSearchDocument(f); |
| } else { |
| // handle failure case... (file deleted maybe?) |
| } |
| return delegate; |
| } |
| |
| /** |
| * Unmangles the searchDocPath and returns the corresponding JSP file. |
| * |
| * @param searchDocPath |
| */ |
| private IFile fileForCUPath(String searchDocPath) { |
| |
| String[] split = searchDocPath.split("/"); //$NON-NLS-1$ |
| String classname = split[split.length - 1]; |
| |
| // ignore anything but .java matches (like .class binary matches) |
| if(!searchDocPath.endsWith(".js")) { //$NON-NLS-1$ |
| return null; |
| } |
| |
| String filePath = JsNameManglerUtil.unmangle(classname); |
| |
| // try absolute path |
| IFile f = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(new Path(filePath)); |
| // workspace relative then |
| if(f == null) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=86009 |
| // must have a project name as well |
| // which would mean >= 2 path segments |
| IPath path = new Path(filePath); |
| if(path.segmentCount() >= 2) { |
| f = ResourcesPlugin.getWorkspace().getRoot().getFile(path); |
| } |
| } |
| return f; |
| } |
| |
| JsSearchParticipant getSearchParticipant() { |
| |
| if (this.fParticipant == null) { |
| this.fParticipant = new JsSearchParticipant(); |
| } |
| return this.fParticipant; |
| } |
| |
| // This is called from JSPPathIndexer |
| // pa_TODO |
| //how can we make sure participant indexLocations are updated at startup? |
| public final IPath computeIndexLocation(IPath containerPath) { |
| |
| IPath indexLocation = null; |
| // we don't want to inadvertently use a JDT Index |
| // we want to be sure to use the Index from the JSP location |
| //Object obj = indexLocations.get(containerPath); |
| //if (obj != null) { |
| // indexLocation = (String) obj; |
| //} else { |
| // create index entry |
| String pathString = containerPath.toOSString(); |
| this.fChecksumCalculator.reset(); |
| this.fChecksumCalculator.update(pathString.getBytes()); |
| String fileName = Long.toString(this.fChecksumCalculator.getValue()) + ".index"; //$NON-NLS-1$ |
| // this is the only difference from |
| // IndexManager#computeIndexLocation(...) |
| indexLocation = getModelJspPluginWorkingLocation().append(fileName); |
| |
| // pa_TODO need to add to java path too, so JDT search support knows |
| // there should be a non internal way to do this. |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=77564 |
| JavaModelManager.getJavaModelManager().getIndexManager().indexLocations.put(containerPath, indexLocation); |
| //} |
| return indexLocation; |
| } |
| public IPath getModelJspPluginWorkingLocation(IProject project) { |
| if (project == null) { |
| System.out.println("Null project"); //$NON-NLS-1$ |
| } |
| IPath workingLocationFile = project.getWorkingLocation(JsCorePlugin.PLUGIN_ID).append("jssearch"); //$NON-NLS-1$ |
| // ensure that it exists on disk |
| File folder = new File(workingLocationFile.toOSString()); |
| if (!folder.isDirectory()) { |
| try { |
| folder.mkdir(); |
| } catch (SecurityException e) { |
| } |
| } |
| return workingLocationFile; |
| } |
| // copied from JDT IndexManager |
| public IPath getModelJspPluginWorkingLocation() { |
| |
| if (this.fJsPluginLocation != null) { |
| return this.fJsPluginLocation; |
| } |
| |
| // Append the folder name "jssearch" to keep the state location area cleaner |
| IPath stateLocation = JsCorePlugin.getDefault().getStateLocation().addTrailingSeparator().append("jssearch"); //$NON-NLS-1$ |
| |
| // pa_TODO workaround for |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=62267 |
| // copied from IndexManager |
| String device = stateLocation.getDevice(); |
| if ((device != null) && (device.charAt(0) == '/')) { |
| stateLocation = stateLocation.setDevice(device.substring(1)); |
| } |
| |
| // ensure that it exists on disk |
| File folder = new File(stateLocation.toOSString()); |
| if (!folder.isDirectory()) { |
| try { |
| folder.mkdir(); |
| } |
| catch (SecurityException e) { |
| } |
| } |
| |
| return this.fJsPluginLocation = stateLocation; |
| } |
| |
| /** |
| * JSP Indexing and Search jobs check this |
| * |
| * @return |
| */ |
| public synchronized final void setCanceled(boolean cancel) { |
| //System.out.println("search support monitor" + fMonitor); |
| fMonitor.setCanceled(cancel); |
| } |
| |
| /** |
| * JSP Indexing and Search jobs check this |
| * |
| * @return |
| */ |
| public synchronized final boolean isCanceled() { |
| |
| return fMonitor.isCanceled(); |
| } |
| |
| /** |
| * JSP Indexing and Search jobs check this |
| * |
| * @return |
| */ |
| public final IProgressMonitor getProgressMonitor() { |
| |
| return this.fMonitor; |
| } |
| } |