/******************************************************************************* * Copyright (c) 2000, 2008 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.search.internal.core.text; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.filebuffers.ITextFileBufferManager; import org.eclipse.core.filebuffers.LocationKind; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentDescription; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.text.IDocument; import org.eclipse.search.core.text.TextSearchMatchAccess; import org.eclipse.search.core.text.TextSearchRequestor; import org.eclipse.search.core.text.TextSearchScope; import org.eclipse.search.internal.core.text.FileCharSequenceProvider.FileCharSequenceException; import org.eclipse.search.internal.ui.NewSearchUI; import org.eclipse.search.internal.ui.SearchMessages; import org.eclipse.search.internal.ui.SearchPlugin; import java.io.CharConversionException; import java.io.IOException; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.Collections; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * The visitor that does the actual work. */ public class TextSearchVisitor { public static class ReusableMatchAccess extends TextSearchMatchAccess { private int fOffset; private int fLength; private IFile fFile; private CharSequence fContent; public void initialize(IFile file, int offset, int length, CharSequence content) { fFile= file; fOffset= offset; fLength= length; fContent= content; } public IFile getFile() { return fFile; } public int getMatchOffset() { return fOffset; } public int getMatchLength() { return fLength; } public int getFileContentLength() { return fContent.length(); } public char getFileContentChar(int offset) { return fContent.charAt(offset); } public String getFileContent(int offset, int length) { return fContent.subSequence(offset, offset + length).toString(); // must pass a copy! } } private final TextSearchRequestor fCollector; private final Matcher fMatcher; private IProgressMonitor fProgressMonitor; private int fNumberOfScannedFiles; private int fNumberOfFilesToScan; private IFile fCurrentFile; private final MultiStatus fStatus; private final FileCharSequenceProvider fFileCharSequenceProvider; private final ReusableMatchAccess fMatchAccess; public TextSearchVisitor(TextSearchRequestor collector, Pattern searchPattern) { fCollector = collector; fStatus = new MultiStatus(NewSearchUI.PLUGIN_ID, IStatus.OK, SearchMessages.TextSearchEngine_statusMessage, null); fMatcher = searchPattern.pattern().length() == 0 ? null : searchPattern.matcher(new String()); fFileCharSequenceProvider = new FileCharSequenceProvider(); fMatchAccess = new ReusableMatchAccess(); } public IStatus search(IFile[] files, IProgressMonitor monitor) { fProgressMonitor = monitor == null ? new NullProgressMonitor() : monitor; fNumberOfScannedFiles = 0; fNumberOfFilesToScan = files.length; fCurrentFile = null; Job monitorUpdateJob = new Job(SearchMessages.TextSearchVisitor_progress_updating_job) { private int fLastNumberOfScannedFiles = 0; public IStatus run(IProgressMonitor inner) { while (!inner.isCanceled()) { IFile file = fCurrentFile; if (file != null) { String fileName = file.getName(); Object[] args = {fileName, new Integer(fNumberOfScannedFiles), new Integer(fNumberOfFilesToScan)}; fProgressMonitor.subTask(Messages.format(SearchMessages.TextSearchVisitor_scanning, args)); int steps = fNumberOfScannedFiles - fLastNumberOfScannedFiles; fProgressMonitor.worked(steps); fLastNumberOfScannedFiles += steps; } try { Thread.sleep(100); } catch (InterruptedException e) { return Status.OK_STATUS; } } return Status.OK_STATUS; } }; try { String taskName = fMatcher == null ? SearchMessages.TextSearchVisitor_filesearch_task_label : Messages .format(SearchMessages.TextSearchVisitor_textsearch_task_label, fMatcher.pattern().pattern()); fProgressMonitor.beginTask(taskName, fNumberOfFilesToScan); monitorUpdateJob.setSystem(true); monitorUpdateJob.schedule(); try { fCollector.beginReporting(); processFiles(files); return fStatus; } finally { monitorUpdateJob.cancel(); } } finally { fProgressMonitor.done(); fCollector.endReporting(); } } public IStatus search(TextSearchScope scope, IProgressMonitor monitor) { return search(scope.evaluateFilesInScope(fStatus), monitor); } private void processFiles(IFile[] files) { final Map documentsInEditors; // if (PlatformUI.isWorkbenchRunning()) // documentsInEditors = evalNonFileBufferDocuments(); // else documentsInEditors = Collections.EMPTY_MAP; for (int i = 0; i < files.length; i++) { fCurrentFile = files[i]; boolean res = processFile(fCurrentFile, documentsInEditors); if (!res) break; } } /** // * @return returns a map from IFile to IDocument for all open, dirty editors // */ // private Map evalNonFileBufferDocuments() { // Map result = new HashMap(); // IWorkbench workbench = SearchPlugin.getDefault().getWorkbench(); // IWorkbenchWindow[] windows = workbench.getWorkbenchWindows(); // for (int i = 0; i < windows.length; i++) { // IWorkbenchPage[] pages = windows[i].getPages(); // for (int x = 0; x < pages.length; x++) { // IEditorReference[] editorRefs = pages[x].getEditorReferences(); // for (int z = 0; z < editorRefs.length; z++) { // IEditorPart ep = editorRefs[z].getEditor(false); // if (ep instanceof ITextEditor && ep.isDirty()) { // only dirty editors // evaluateTextEditor(result, ep); // } // } // } // } // return result; // } // private void evaluateTextEditor(Map result, IEditorPart ep) { // IEditorInput input= ep.getEditorInput(); // if (input instanceof IFileEditorInput) { // IFile file= ((IFileEditorInput) input).getFile(); // if (!result.containsKey(file)) { // take the first editor found // ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager(); // ITextFileBuffer textFileBuffer= bufferManager.getTextFileBuffer(file.getFullPath(), LocationKind.IFILE); // if (textFileBuffer != null) { // // file buffer has precedence // result.put(file, textFileBuffer.getDocument()); // } else { // // use document provider // IDocument document= ((ITextEditor) ep).getDocumentProvider().getDocument(input); // if (document != null) { // result.put(file, document); // } // } // } // } // } public boolean processFile(IFile file, Map documentsInEditors) { try { if (!fCollector.acceptFile(file) || fMatcher == null) { return true; } IDocument document= getOpenDocument(file, documentsInEditors); if (document != null) { DocumentCharSequence documentCharSequence= new DocumentCharSequence(document); // assume all documents are non-binary locateMatches(file, documentCharSequence); } else { CharSequence seq= null; try { seq= fFileCharSequenceProvider.newCharSequence(file); if (hasBinaryContent(seq, file) && !fCollector.reportBinaryFile(file)) { return true; } locateMatches(file, seq); } catch (FileCharSequenceProvider.FileCharSequenceException e) { e.throwWrappedException(); } finally { if (seq != null) { try { fFileCharSequenceProvider.releaseCharSequence(seq); } catch (IOException e) { SearchPlugin.log(e); } } } } } catch (UnsupportedCharsetException e) { String[] args= { getCharSetName(file), file.getFullPath().makeRelative().toString()}; String message= Messages.format(SearchMessages.TextSearchVisitor_unsupportedcharset, args); fStatus.add(new Status(IStatus.ERROR, NewSearchUI.PLUGIN_ID, IStatus.ERROR, message, e)); } catch (IllegalCharsetNameException e) { String[] args= { getCharSetName(file), file.getFullPath().makeRelative().toString()}; String message= Messages.format(SearchMessages.TextSearchVisitor_illegalcharset, args); fStatus.add(new Status(IStatus.ERROR, NewSearchUI.PLUGIN_ID, IStatus.ERROR, message, e)); } catch (IOException e) { String[] args= { getExceptionMessage(e), file.getFullPath().makeRelative().toString()}; String message= Messages.format(SearchMessages.TextSearchVisitor_error, args); fStatus.add(new Status(IStatus.ERROR, NewSearchUI.PLUGIN_ID, IStatus.ERROR, message, e)); } catch (CoreException e) { String[] args= { getExceptionMessage(e), file.getFullPath().makeRelative().toString()}; String message= Messages.format(SearchMessages.TextSearchVisitor_error, args); fStatus.add(new Status(IStatus.ERROR, NewSearchUI.PLUGIN_ID, IStatus.ERROR, message, e)); } catch (StackOverflowError e) { String message= SearchMessages.TextSearchVisitor_patterntoocomplex0; fStatus.add(new Status(IStatus.ERROR, NewSearchUI.PLUGIN_ID, IStatus.ERROR, message, e)); return false; } finally { fNumberOfScannedFiles++; } if (fProgressMonitor.isCanceled()) throw new OperationCanceledException(SearchMessages.TextSearchVisitor_canceled); return true; } private boolean hasBinaryContent(CharSequence seq, IFile file) throws CoreException { IContentDescription desc= file.getContentDescription(); if (desc != null) { IContentType contentType= desc.getContentType(); if (contentType != null && contentType.isKindOf(Platform.getContentTypeManager().getContentType(IContentTypeManager.CT_TEXT))) { return false; } } // avoid calling seq.length() at it runs through the complete file, // thus it would do so for all binary files. try { int limit= FileCharSequenceProvider.BUFFER_SIZE; for (int i= 0; i < limit; i++) { if (seq.charAt(i) == '\0') { return true; } } } catch (IndexOutOfBoundsException e) { } catch (FileCharSequenceException ex) { if (ex.getCause() instanceof CharConversionException) return true; throw ex; } return false; } private void locateMatches(IFile file, CharSequence searchInput) throws CoreException { try { fMatcher.reset(searchInput); int k= 0; while (fMatcher.find()) { int start= fMatcher.start(); int end= fMatcher.end(); if (end != start) { // don't report 0-length matches fMatchAccess.initialize(file, start, end - start, searchInput); boolean res= fCollector.acceptPatternMatch(fMatchAccess); if (!res) { return; // no further reporting requested } } if (k++ == 20) { if (fProgressMonitor.isCanceled()) { throw new OperationCanceledException(SearchMessages.TextSearchVisitor_canceled); } k= 0; } } } finally { fMatchAccess.initialize(null, 0, 0, new String()); // clear references } } private String getExceptionMessage(Exception e) { String message= e.getLocalizedMessage(); if (message == null) { return e.getClass().getName(); } return message; } private IDocument getOpenDocument(IFile file, Map documentsInEditors) { IDocument document= (IDocument)documentsInEditors.get(file); if (document == null) { ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager(); ITextFileBuffer textFileBuffer= bufferManager.getTextFileBuffer(file.getFullPath(), LocationKind.IFILE); if (textFileBuffer != null) { document= textFileBuffer.getDocument(); } } return document; } private String getCharSetName(IFile file) { try { return file.getCharset(); } catch (CoreException e) { return "unknown"; //$NON-NLS-1$ } } }