Skip to main content
summaryrefslogtreecommitdiffstats
blob: da2462f3d994bf1b3d4950c53a090006b67c5d67 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/*******************************************************************************
 * Copyright (c) 2000, 2010 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;
	}

	@Override
	public IResource[] getRoots() {
		return fRootElements;
	}

	@Override
	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<String> 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= 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<IResource> res= new ArrayList<>();
		for (int i= 0; i < elements.length; i++) {
			IResource curr= elements[i];
			addToList(res, curr, includeDerived);
		}
		return res.toArray(new IResource[res.size()]);
	}

	private static IResource[] convertToResources(IWorkingSet[] workingSets, boolean includeDerived) {
		ArrayList<IResource> 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= elements[k].getAdapter(IResource.class);
				if (curr != null) {
					addToList(res, curr, includeDerived);
				}
			}
		}
		return res.toArray(new IResource[res.size()]);
	}

	private static void addToList(ArrayList<IResource> res, IResource curr, boolean includeDerived) {
		if (!includeDerived && curr.isDerived(IResource.CHECK_ANCESTORS)) {
			return;
		}
		IPath currPath= curr.getFullPath();
		for (int k= res.size() - 1; k >= 0 ; k--) {
			IResource other= res.get(k);
			IPath otherPath= other.getFullPath();
			if (otherPath.isPrefixOf(currPath)) {
				return;
			}
			if (currPath.isPrefixOf(otherPath)) {
				res.remove(k);
			}
		}
		res.add(curr);
	}
}

Back to the top