blob: 20aee64f64ae317bb26ed665f4e3c681bad00884 [file] [log] [blame]
/*******************************************************************************
* 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.ui.text;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.search.core.text.TextSearchScope;
import org.eclipse.search.internal.core.text.PatternConstructor;
import org.eclipse.search.internal.ui.Messages;
import org.eclipse.search.internal.ui.SearchMessages;
import org.eclipse.search.internal.ui.WorkingSetComparator;
import org.eclipse.search.internal.ui.text.BasicElementLabels;
import org.eclipse.search.internal.ui.util.FileTypeEditor;
/**
* A text search scope used by the file search dialog. Additionally to roots it allows to define file name
* patterns and exclude all derived resources.
*
* <p>
* Clients should not instantiate or subclass this class.
* </p>
* @since 3.2
*
* @noinstantiate This class is not intended to be instantiated by clients.
*/
public final class FileTextSearchScope extends TextSearchScope {
private static final boolean IS_CASE_SENSITIVE_FILESYSTEM = !new File("Temp").equals(new File("temp")); //$NON-NLS-1$ //$NON-NLS-2$
/**
* Returns a scope for the workspace. The created scope contains all resources in the workspace
* that match the given file name patterns. Depending on <code>includeDerived</code>, derived resources or
* resources inside a derived container are part of the scope or not.
*
* @param fileNamePatterns file name pattern that all files have to match <code>null</code> to include all file names.
* @param includeDerived defines if derived files and files inside derived containers are included in the scope.
* @return a scope containing all files in the workspace that match the given file name patterns.
*/
public static FileTextSearchScope newWorkspaceScope(String[] fileNamePatterns, boolean includeDerived) {
return new FileTextSearchScope(SearchMessages.WorkspaceScope, new IResource[] { ResourcesPlugin.getWorkspace().getRoot() }, null, fileNamePatterns, includeDerived);
}
/**
* Returns a scope for the given root resources. The created scope contains all root resources and their
* children that match the given file name patterns. Depending on <code>includeDerived</code>, derived resources or
* resources inside a derived container are part of the scope or not.
*
* @param roots the roots resources defining the scope.
* @param fileNamePatterns file name pattern that all files have to match <code>null</code> to include all file names.
* @param includeDerived defines if derived files and files inside derived containers are included in the scope.
* @return a scope containing the resources and its children if they match the given file name patterns.
*/
public static FileTextSearchScope newSearchScope(IResource[] roots, String[] fileNamePatterns, boolean includeDerived) {
roots= removeRedundantEntries(roots, includeDerived);
String description;
if (roots.length == 0) {
description= SearchMessages.FileTextSearchScope_scope_empty;
} else if (roots.length == 1) {
String label= SearchMessages.FileTextSearchScope_scope_single;
description= Messages.format(label, roots[0].getName());
} else if (roots.length == 2) {
String label= SearchMessages.FileTextSearchScope_scope_double;
description= Messages.format(label, new String[] { roots[0].getName(), roots[1].getName()});
} else {
String label= SearchMessages.FileTextSearchScope_scope_multiple;
description= Messages.format(label, new String[] { roots[0].getName(), roots[1].getName()});
}
return new FileTextSearchScope(description, roots, null, fileNamePatterns, includeDerived);
}
/**
* Returns a scope for the given working sets. The created scope contains all resources in the
* working sets that match the given file name patterns. Depending on <code>includeDerived</code>, derived resources or
* resources inside a derived container are part of the scope or not.
*
* @param workingSets the working sets defining the scope.
* @param fileNamePatterns file name pattern that all files have to match <code>null</code> to include all file names.
* @param includeDerived defines if derived files and files inside derived containers are included in the scope.
* @return a scope containing the resources in the working set if they match the given file name patterns.
*/
public static FileTextSearchScope newSearchScope(IWorkingSet[] workingSets, String[] fileNamePatterns, boolean includeDerived) {
String description;
Arrays.sort(workingSets, new WorkingSetComparator());
if (workingSets.length == 0) {
description= SearchMessages.FileTextSearchScope_ws_scope_empty;
} else if (workingSets.length == 1) {
String label= SearchMessages.FileTextSearchScope_ws_scope_single;
description= Messages.format(label, workingSets[0].getLabel());
} else if (workingSets.length == 2) {
String label= SearchMessages.FileTextSearchScope_ws_scope_double;
description= Messages.format(label, new String[] { workingSets[0].getLabel(), workingSets[1].getLabel()});
} else {
String label= SearchMessages.FileTextSearchScope_ws_scope_multiple;
description= Messages.format(label, new String[] { workingSets[0].getLabel(), workingSets[1].getLabel()});
}
FileTextSearchScope scope= new FileTextSearchScope(description, convertToResources(workingSets, includeDerived), workingSets, fileNamePatterns, includeDerived);
return scope;
}
private final String fDescription;
private final IResource[] fRootElements;
private final String[] fFileNamePatterns;
private final Matcher fPositiveFileNameMatcher;
private final Matcher fNegativeFileNameMatcher;
private boolean fVisitDerived;
private IWorkingSet[] fWorkingSets;
private FileTextSearchScope(String description, IResource[] resources, IWorkingSet[] workingSets, String[] fileNamePatterns, boolean visitDerived) {
fDescription= description;
fRootElements= resources;
fFileNamePatterns= fileNamePatterns;
fVisitDerived= visitDerived;
fWorkingSets= workingSets;
fPositiveFileNameMatcher= createMatcher(fileNamePatterns, false);
fNegativeFileNameMatcher= createMatcher(fileNamePatterns, true);
}
/**
* Returns the description of the scope
*
* @return the description of the scope
*/
public String getDescription() {
return fDescription;
}
/**
* Returns the file name pattern configured for this scope or <code>null</code> to match
* all file names.
*
* @return the file name pattern starings
*/
public String[] getFileNamePatterns() {
return fFileNamePatterns;
}
/**
* Returns the working-sets that were used to configure this scope or <code>null</code>
* if the scope was not created off working sets.
*
* @return the working-sets the scope is based on.
*/
public IWorkingSet[] getWorkingSets() {
return fWorkingSets;
}
/**
* Returns the content types configured for this scope or <code>null</code> to match
* all content types.
*
* @return the file name pattern starings
*/
public IContentType[] getContentTypes() {
return null; // to be implemented in the future
}
/**
* Returns a description describing the file name patterns and content types.
*
* @return the description of the scope
*/
public String getFilterDescription() {
String[] ext= fFileNamePatterns;
if (ext == null) {
return BasicElementLabels.getFilePattern("*"); //$NON-NLS-1$
}
Arrays.sort(ext);
StringBuffer buf= new StringBuffer();
for (int i= 0; i < ext.length; i++) {
if (i > 0) {
buf.append(", "); //$NON-NLS-1$
}
buf.append(ext[i]);
}
return BasicElementLabels.getFilePattern(buf.toString());
}
/**
* Returns whether derived resources are included in this search scope.
*
* @return whether derived resources are included in this search scope.
*/
public boolean includeDerived() {
return fVisitDerived;
}
/* (non-Javadoc)
* @see org.eclipse.search.core.text.FileSearchScope#getRoots()
*/
public IResource[] getRoots() {
return fRootElements;
}
/* (non-Javadoc)
* @see org.eclipse.search.core.text.FileSearchScope#contains(org.eclipse.core.resources.IResourceProxy)
*/
public boolean contains(IResourceProxy proxy) {
if (!fVisitDerived && proxy.isDerived()) {
return false; // all resources in a derived folder are considered to be derived, see bug 103576
}
if (proxy.getType() == IResource.FILE) {
return matchesFileName(proxy.getName());
}
return true;
}
private boolean matchesFileName(String fileName) {
if (fPositiveFileNameMatcher != null && !fPositiveFileNameMatcher.reset(fileName).matches()) {
return false;
}
if (fNegativeFileNameMatcher != null && fNegativeFileNameMatcher.reset(fileName).matches()) {
return false;
}
return true;
}
private Matcher createMatcher(String[] fileNamePatterns, boolean negativeMatcher) {
if (fileNamePatterns == null || fileNamePatterns.length == 0) {
return null;
}
ArrayList patterns= new ArrayList();
for (int i= 0; i < fileNamePatterns.length; i++) {
String pattern= fFileNamePatterns[i];
if (negativeMatcher == pattern.startsWith(FileTypeEditor.FILE_PATTERN_NEGATOR)) {
if (negativeMatcher) {
pattern= pattern.substring(FileTypeEditor.FILE_PATTERN_NEGATOR.length()).trim();
}
if (pattern.length() > 0) {
patterns.add(pattern);
}
}
}
if (!patterns.isEmpty()) {
String[] patternArray= (String[]) patterns.toArray(new String[patterns.size()]);
Pattern pattern= PatternConstructor.createPattern(patternArray, IS_CASE_SENSITIVE_FILESYSTEM);
return pattern.matcher(""); //$NON-NLS-1$
}
return null;
}
private static IResource[] removeRedundantEntries(IResource[] elements, boolean includeDerived) {
ArrayList res= new ArrayList();
for (int i= 0; i < elements.length; i++) {
IResource curr= elements[i];
addToList(res, curr, includeDerived);
}
return (IResource[])res.toArray(new IResource[res.size()]);
}
private static IResource[] convertToResources(IWorkingSet[] workingSets, boolean includeDerived) {
ArrayList res= new ArrayList();
for (int i= 0; i < workingSets.length; i++) {
IWorkingSet workingSet= workingSets[i];
if (workingSet.isAggregateWorkingSet() && workingSet.isEmpty()) {
return new IResource[] { ResourcesPlugin.getWorkspace().getRoot() };
}
IAdaptable[] elements= workingSet.getElements();
for (int k= 0; k < elements.length; k++) {
IResource curr= (IResource) elements[k].getAdapter(IResource.class);
if (curr != null) {
addToList(res, curr, includeDerived);
}
}
}
return (IResource[]) res.toArray(new IResource[res.size()]);
}
private static void addToList(ArrayList res, IResource curr, boolean includeDerived) {
if (!includeDerived && isDerived(curr)) {
return;
}
IPath currPath= curr.getFullPath();
for (int k= res.size() - 1; k >= 0 ; k--) {
IResource other= (IResource) res.get(k);
IPath otherPath= other.getFullPath();
if (otherPath.isPrefixOf(currPath)) {
return;
}
if (currPath.isPrefixOf(otherPath)) {
res.remove(k);
}
}
res.add(curr);
}
private static boolean isDerived(IResource curr) {
do {
if (curr.isDerived()) {
return true;
}
curr= curr.getParent();
} while (curr != null);
return false;
}
}