/******************************************************************************* | |
* Copyright (c) 2011 Tasktop Technologies. | |
* 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: | |
* Tasktop Technologies - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.mylyn.internal.sandbox.search.ui.provider; | |
import java.io.BufferedInputStream; | |
import java.io.File; | |
import java.io.IOException; | |
import java.io.InputStream; | |
import java.io.InputStreamReader; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Stack; | |
import java.util.regex.Pattern; | |
import org.eclipse.core.filesystem.EFS; | |
import org.eclipse.core.filesystem.IFileInfo; | |
import org.eclipse.core.filesystem.IFileStore; | |
import org.eclipse.core.runtime.CoreException; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.OperationCanceledException; | |
import org.eclipse.core.runtime.SubMonitor; | |
import org.eclipse.mylyn.sandbox.search.ui.SearchCallback; | |
import org.eclipse.mylyn.sandbox.search.ui.SearchCriteria; | |
import org.eclipse.mylyn.sandbox.search.ui.SearchProvider; | |
import org.eclipse.mylyn.sandbox.search.ui.SearchResult; | |
import org.eclipse.osgi.util.NLS; | |
/** | |
* A search provider that operates over java.io | |
* | |
* @author David Green | |
*/ | |
public class BasicSearchProvider extends SearchProvider { | |
private abstract class FileMatcher { | |
public abstract boolean matches(IFileStore file, IProgressMonitor monitor); | |
} | |
private class CompositeFileMatcher extends FileMatcher { | |
private final List<FileMatcher> delegates; | |
private final boolean allMatch; | |
/** | |
* @param allMatch | |
* indicate if one delegate must match (false) or if all delegates must match (true) | |
*/ | |
public CompositeFileMatcher(boolean allMatch) { | |
this.allMatch = allMatch; | |
delegates = new ArrayList<FileMatcher>(); | |
} | |
public CompositeFileMatcher(List<FileMatcher> delegates, boolean allMatch) { | |
this.delegates = delegates; | |
this.allMatch = allMatch; | |
} | |
public void add(FileMatcher matcher) { | |
delegates.add(matcher); | |
} | |
@Override | |
public boolean matches(IFileStore file, IProgressMonitor monitor) { | |
for (FileMatcher matcher : delegates) { | |
if (monitor.isCanceled()) { | |
return false; | |
} | |
if (!matcher.matches(file, monitor)) { | |
if (allMatch) { | |
return false; | |
} | |
} else if (!allMatch) { | |
return true; | |
} | |
} | |
return allMatch ? true : false; | |
} | |
} | |
private class FileNameMatcher extends FileMatcher { | |
private final Pattern pattern; | |
public FileNameMatcher(String matchPattern) { | |
String regex = ".*?"; //$NON-NLS-1$ | |
regex += patternToRegex(matchPattern); | |
regex += ".*"; //$NON-NLS-1$ | |
pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); | |
} | |
@Override | |
public boolean matches(IFileStore file, IProgressMonitor monitor) { | |
String name = file.getName(); | |
return pattern.matcher(name).matches(); | |
} | |
} | |
private class FileContentMatcher extends FileMatcher { | |
private final Pattern pattern; | |
private final int maxMatchingCharacters = 1024 * 16; | |
public FileContentMatcher(SearchCriteria searchSpecification) { | |
pattern = Pattern.compile(patternToRegex(searchSpecification.getText().trim()), Pattern.CASE_INSENSITIVE); | |
} | |
@Override | |
public boolean matches(IFileStore file, IProgressMonitor monitor) { | |
monitor.subTask(file.toString()); | |
try { | |
InputStream inputStream = file.openInputStream(EFS.NONE, monitor); | |
try { | |
InputStreamReader reader = new InputStreamReader(new BufferedInputStream(inputStream)); | |
try { | |
ReaderCharSequence charSequence = new ReaderCharSequence(maxMatchingCharacters, reader, monitor); | |
return pattern.matcher(charSequence).find(); | |
} finally { | |
reader.close(); | |
} | |
} finally { | |
inputStream.close(); | |
} | |
} catch (IOException e) { | |
// ignore | |
} catch (CoreException e) { | |
// ignore | |
} | |
return false; | |
} | |
} | |
@Override | |
public void performSearch(SearchCriteria searchSpecification, SearchCallback callback, IProgressMonitor m) | |
throws CoreException { | |
SubMonitor monitor = SubMonitor.convert(m); | |
monitor.beginTask(NLS.bind(Messages.BasicSearchProvider_0, searchSpecification.getText()), | |
searchSpecification.getMaximumResults() > 0 | |
? searchSpecification.getMaximumResults() | |
: IProgressMonitor.UNKNOWN); | |
try { | |
FileMatcher matcher = computeMatcher(searchSpecification); | |
File[] roots = File.listRoots(); | |
if (roots != null) { | |
int matchCount = 0; | |
Stack<IFileStore> state = new Stack<IFileStore>(); | |
// reverse-order iteration so that the first one is the last pushed on the stack | |
for (int x = roots.length - 1; x >= 0; --x) { | |
if (monitor.isCanceled()) { | |
break; | |
} | |
File root = roots[x]; | |
IFileStore fileStore = EFS.getLocalFileSystem().fromLocalFile(root); | |
state.push(fileStore); | |
} | |
try { | |
while (!state.isEmpty() && !monitor.isCanceled()) { | |
IFileStore fileStore = state.pop(); | |
IFileInfo fileInfo = fileStore.fetchInfo(); | |
if (isDefaultIgnore(fileStore, fileInfo)) { | |
// ignore | |
} else if (fileInfo.isDirectory()) { | |
monitor.subTask(fileStore.toString()); | |
IFileStore[] childStores = fileStore.childStores(EFS.NONE, monitor.newChild(0)); | |
for (IFileStore child : childStores) { | |
state.push(child); | |
} | |
} else { | |
if (matcher.matches(fileStore, monitor.newChild(0))) { | |
monitor.worked(1); | |
callback.searchResult(new SearchResult(fileStore.toLocalFile(EFS.NONE, | |
monitor.newChild(0)))); | |
if (++matchCount >= searchSpecification.getMaximumResults()) { | |
break; | |
} | |
} | |
} | |
} | |
} catch (OperationCanceledException oce) { | |
// ignore | |
} catch (CoreException e) { | |
if (e.getStatus().getSeverity() != IStatus.CANCEL) { | |
throw e; | |
} | |
} | |
} | |
} finally { | |
monitor.done(); | |
} | |
} | |
private boolean isDefaultIgnore(IFileStore fileStore, IFileInfo fileInfo) { | |
if (fileInfo.getAttribute(EFS.ATTRIBUTE_SYMLINK) || fileInfo.getAttribute(EFS.ATTRIBUTE_HIDDEN)) { | |
// ignore, we don't follow symbolic links or hidden files | |
return true; | |
} else { | |
String name = fileStore.getName(); | |
if (fileInfo.isDirectory()) { | |
if ((name.equals("Windows") || name.equals("$Recycle.Bin")) && fileStore.getParent() != null && fileStore.getParent().getParent() == null) { //$NON-NLS-1$ //$NON-NLS-2$ | |
return true; | |
} else if (name.startsWith(".")) { //$NON-NLS-1$ | |
return true; | |
} | |
} else { | |
if (name.endsWith(".dll") || name.endsWith(".exe") || name.endsWith(".sys") || name.endsWith(".zip") || name.endsWith(".jar") || name.endsWith(".bin")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
private FileMatcher computeMatcher(SearchCriteria searchSpecification) { | |
List<FileMatcher> filenameMatchers = new ArrayList<FileMatcher>(); | |
for (String filenamePattern : searchSpecification.getFilenamePatterns()) { | |
if (filenamePattern.length() > 0) { | |
if (filenamePattern.equals("*") || filenamePattern.equals("*.*")) { //$NON-NLS-1$//$NON-NLS-2$ | |
// every file matches | |
filenameMatchers.clear(); | |
break; | |
} else { | |
filenameMatchers.add(new FileNameMatcher(filenamePattern)); | |
} | |
} | |
} | |
CompositeFileMatcher fileMatcher = new CompositeFileMatcher(true); | |
if (!filenameMatchers.isEmpty()) { | |
fileMatcher.add(filenameMatchers.size() == 1 ? filenameMatchers.get(0) : new CompositeFileMatcher( | |
filenameMatchers, false)); | |
} | |
if (searchSpecification.getText() != null && searchSpecification.getText().trim().length() > 0) { | |
fileMatcher.add(new FileContentMatcher(searchSpecification)); | |
} | |
return fileMatcher; | |
} | |
private String patternToRegex(String matchPattern) { | |
String regex = ""; //$NON-NLS-1$ | |
for (char c : matchPattern.toCharArray()) { | |
if (Character.isLetterOrDigit(c)) { | |
regex += c; | |
} else { | |
if (c == '*') { | |
regex += ".*"; //$NON-NLS-1$ | |
} else if (c == '?') { | |
regex += "."; //$NON-NLS-1$ | |
} else { | |
regex += "\\"; //$NON-NLS-1$ | |
regex += c; | |
} | |
} | |
} | |
return regex; | |
} | |
} |