1
/*******************************************************************************
2
* Copyright (c) 2004, 2008 Wind River Systems, Inc.
3
* All rights reserved. This program and the accompanying materials
4
* are made available under the terms of the Eclipse Public License v1.0
5
* which accompanies this distribution, and is available at
6
* http://www.eclipse.org/legal/epl-v10.html
9
* Markus Schorn - initial API and implementation
10
* Sergey Prigogin (Google)
11
*******************************************************************************/
12
package org.eclipse.cdt.internal.ui.refactoring.rename;
14
import java.io.BufferedReader;
15
import java.io.IOException;
16
import java.io.InputStreamReader;
17
import java.io.Reader;
18
import java.io.UnsupportedEncodingException;
19
import java.util.ArrayList;
20
import java.util.Arrays;
21
import java.util.Collections;
22
import java.util.Comparator;
23
import java.util.HashSet;
24
import java.util.Iterator;
25
import java.util.LinkedList;
26
import java.util.List;
27
import java.util.regex.Matcher;
28
import java.util.regex.Pattern;
30
import org.eclipse.core.resources.IFile;
31
import org.eclipse.core.resources.IProject;
32
import org.eclipse.core.resources.IResource;
33
import org.eclipse.core.resources.IResourceProxy;
34
import org.eclipse.core.runtime.CoreException;
35
import org.eclipse.core.runtime.IAdaptable;
36
import org.eclipse.core.runtime.IPath;
37
import org.eclipse.core.runtime.IProgressMonitor;
38
import org.eclipse.core.runtime.IStatus;
39
import org.eclipse.core.runtime.SubProgressMonitor;
40
import org.eclipse.search.core.text.TextSearchEngine;
41
import org.eclipse.search.core.text.TextSearchMatchAccess;
42
import org.eclipse.search.core.text.TextSearchRequestor;
43
import org.eclipse.search.core.text.TextSearchScope;
44
import org.eclipse.ui.IWorkingSet;
45
import org.eclipse.ui.IWorkingSetManager;
46
import org.eclipse.ui.PlatformUI;
48
import org.eclipse.cdt.core.model.CoreModel;
49
import org.eclipse.cdt.core.model.ICElement;
50
import org.eclipse.cdt.core.model.ITranslationUnit;
51
import org.eclipse.cdt.utils.PathUtil;
53
import org.eclipse.cdt.internal.formatter.scanner.SimpleScanner;
54
import org.eclipse.cdt.internal.formatter.scanner.Token;
57
* Wraps the platform text search and uses a scanner to categorize the text-matches
58
* by location (comments, string-literals, etc.).
60
public class TextSearchWrapper {
61
public final static int SCOPE_FILE = 1;
62
public final static int SCOPE_WORKSPACE = 2;
63
public final static int SCOPE_RELATED_PROJECTS = 3;
64
public final static int SCOPE_SINGLE_PROJECT = 4;
65
public final static int SCOPE_WORKING_SET = 5;
67
private static class SearchScope extends TextSearchScope {
68
public static SearchScope newSearchScope(IWorkingSet ws, IResource[] filter) {
69
IAdaptable[] adaptables= ws.getElements();
70
ArrayList<IResource> resources = new ArrayList<IResource>();
71
for (int i = 0; i < adaptables.length; i++) {
72
IAdaptable adaptable = adaptables[i];
73
IResource r= (IResource) adaptable.getAdapter(IResource.class);
78
return newSearchScope(resources.toArray(new IResource[resources.size()]), filter);
81
public static SearchScope newSearchScope(IResource[] roots, IResource[] filter) {
83
ArrayList<IResource> files = new ArrayList<IResource>(filter.length);
84
for (IResource file : filter) {
85
if (isInForest(file, roots)) {
89
roots = files.toArray(new IResource[files.size()]);
91
return new SearchScope(roots);
95
* Checks is a file belongs to one of the given containers.
97
private static boolean isInForest(IResource file, IResource[] roots) {
98
IPath filePath = file.getFullPath();
99
for (IResource root : roots) {
100
if (PathUtil.isPrefix(root.getFullPath(), filePath)) {
107
private IResource[] fRootResources;
108
private ArrayList<Matcher> fFileMatcher= new ArrayList<Matcher>();
110
private SearchScope(IResource[] roots) {
111
fRootResources= roots;
115
public IResource[] getRoots() {
116
return fRootResources;
120
public boolean contains(IResourceProxy proxy) {
121
if (proxy.isDerived()) {
124
if (proxy.getType() == IResource.FILE) {
125
return containsFile(proxy.getName());
130
private boolean containsFile(String name) {
131
for (Iterator<Matcher> iter = fFileMatcher.iterator(); iter.hasNext();) {
132
Matcher matcher = iter.next();
134
if (matcher.matches()) {
141
public void addFileNamePattern(String filePattern) {
142
Pattern p= Pattern.compile(filePatternToRegex(filePattern));
143
fFileMatcher.add(p.matcher("")); //$NON-NLS-1$
146
private String filePatternToRegex(String filePattern) {
147
StringBuilder result = new StringBuilder();
148
for (int i = 0; i < filePattern.length(); i++) {
149
char c = filePattern.charAt(i);
170
result.append(".*"); //$NON-NLS-1$
177
return result.toString();
181
public TextSearchWrapper() {
184
private TextSearchScope createSearchScope(IFile file, int scope, String workingSetName,
185
IResource[] filter, String[] patterns) {
187
case SCOPE_WORKSPACE:
188
return defineSearchScope(file.getWorkspace().getRoot(), filter, patterns);
189
case SCOPE_SINGLE_PROJECT:
190
return defineSearchScope(file.getProject(), filter, patterns);
192
return defineSearchScope(file, filter, patterns);
193
case SCOPE_WORKING_SET: {
194
TextSearchScope result= defineWorkingSetAsSearchScope(workingSetName, filter, patterns);
195
if (result == null) {
196
result= defineSearchScope(file.getWorkspace().getRoot(), filter, patterns);
201
return defineRelatedProjectsAsSearchScope(file.getProject(), filter, patterns);
204
private TextSearchScope defineRelatedProjectsAsSearchScope(IProject project, IResource[] filter, String[] patterns) {
205
HashSet<IProject> projects= new HashSet<IProject>();
206
LinkedList<IProject> workThrough= new LinkedList<IProject>();
207
workThrough.add(project);
208
while (!workThrough.isEmpty()) {
209
IProject prj= workThrough.removeLast();
210
if (projects.add(prj)) {
212
workThrough.addAll(Arrays.asList(prj.getReferencedProjects()));
213
workThrough.addAll(Arrays.asList(prj.getReferencingProjects()));
214
} catch (CoreException e) {
219
IResource[] roots= projects.toArray(new IResource[projects.size()]);
220
return defineSearchScope(roots, filter, patterns);
223
private TextSearchScope defineWorkingSetAsSearchScope(String workingSetName, IResource[] filter, String[] patterns) {
224
if (workingSetName == null) {
227
IWorkingSetManager wsManager= PlatformUI.getWorkbench().getWorkingSetManager();
228
IWorkingSet ws= wsManager.getWorkingSet(workingSetName);
232
SearchScope result= SearchScope.newSearchScope(ws, filter);
233
applyFilePatterns(result, patterns);
237
private void applyFilePatterns(SearchScope scope, String[] patterns) {
238
for (int i = 0; i < patterns.length; i++) {
239
String pattern = patterns[i];
240
scope.addFileNamePattern(pattern);
244
private TextSearchScope defineSearchScope(IResource root, IResource[] filter, String[] patterns) {
245
SearchScope result= SearchScope.newSearchScope(new IResource[] { root }, filter);
246
applyFilePatterns(result, patterns);
250
private TextSearchScope defineSearchScope(IResource[] roots, IResource[] filter, String[] patterns) {
251
SearchScope result= SearchScope.newSearchScope(roots, filter);
252
applyFilePatterns(result, patterns);
257
* Searches for a given word.
259
* @param scope One of SCOPE_FILE, SCOPE_WORKSPACE, SCOPE_RELATED_PROJECTS, SCOPE_SINGLE_PROJECT,
260
* or SCOPE_WORKING_SET.
261
* @param file The file used as an anchor for the scope.
262
* @param workingSet The name of a working set. Ignored is scope is not SCOPE_WORKING_SET.
263
* @param filter If not null, further limits the scope of the search.
264
* @param patterns File name patterns.
265
* @param word The word to search for.
266
* @param monitor A progress monitor.
267
* @param target The list that gets populated with search results.
269
public IStatus searchWord(int scope, IFile file, String workingSet, IResource[] filter, String[] patterns,
270
String word, IProgressMonitor monitor, final List<CRefactoringMatch> target) {
271
int startPos= target.size();
272
TextSearchEngine engine= TextSearchEngine.create();
273
StringBuilder searchPattern= new StringBuilder(word.length() + 8);
274
searchPattern.append("\\b"); //$NON-NLS-1$
275
searchPattern.append("\\Q"); //$NON-NLS-1$
276
searchPattern.append(word);
277
searchPattern.append("\\E"); //$NON-NLS-1$
278
searchPattern.append("\\b"); //$NON-NLS-1$
280
Pattern pattern= Pattern.compile(searchPattern.toString());
282
TextSearchScope searchscope= createSearchScope(file, scope, workingSet, filter, patterns);
283
TextSearchRequestor requestor= new TextSearchRequestor() {
285
public boolean acceptPatternMatch(TextSearchMatchAccess access) {
286
IFile file= access.getFile();
287
ICElement elem= CoreModel.getDefault().create(file);
288
if (elem instanceof ITranslationUnit) {
289
target.add(new CRefactoringMatch(file,
290
access.getMatchOffset(), access.getMatchLength(), 0));
295
IStatus result= engine.search(searchscope, requestor, pattern,
296
new SubProgressMonitor(monitor, 95));
297
categorizeMatches(target.subList(startPos, target.size()),
298
new SubProgressMonitor(monitor, 5));
303
public void categorizeMatches(List<CRefactoringMatch> matches, IProgressMonitor monitor) {
304
monitor.beginTask(RenameMessages.TextSearch_monitor_categorizeMatches, matches.size());
306
ArrayList<int[]> locations= null;
307
for (Iterator<CRefactoringMatch> iter = matches.iterator(); iter.hasNext();) {
308
CRefactoringMatch match = iter.next();
309
IFile tfile= match.getFile();
310
if (file == null || !file.equals(tfile)) {
312
locations= new ArrayList<int[]>();
313
computeLocations(file, locations);
315
match.setLocation(findLocation(match, locations));
320
final static Comparator<int[]> COMPARE_FIRST_INTEGER= new Comparator<int[]>() {
321
public int compare(int[] o1, int[] o2) {
322
return (o1)[0] - (o2)[0];
326
private int findLocation(CRefactoringMatch match, ArrayList<int[]> states) {
327
int pos = Collections.binarySearch(states, new int[] {match.getOffset()}, COMPARE_FIRST_INTEGER);
334
int endOffset= match.getOffset() + match.getLength();
336
while (pos < states.size()) {
337
int[] info= states.get(pos);
338
if (info[0] >= endOffset) {
347
private void computeLocations(IFile file, ArrayList<int[]> locations) {
349
SimpleScanner scanner= new SimpleScanner();
351
reader = new BufferedReader(new InputStreamReader(file.getContents(), file.getCharset()));
352
} catch (CoreException e) {
354
} catch (UnsupportedEncodingException e) {
358
scanner.initialize(reader, null);
359
scanner.setReuseToken(true);
362
while ((token= scanner.nextToken()) != null) {
363
int state= CRefactory.OPTION_IN_CODE;
364
switch (token.getType()) {
365
case Token.tLINECOMMENT:
366
case Token.tBLOCKCOMMENT:
367
state= CRefactory.OPTION_IN_COMMENT;
372
state= CRefactory.OPTION_IN_STRING_LITERAL;
374
case Token.tPREPROCESSOR:
375
state= CRefactory.OPTION_IN_PREPROCESSOR_DIRECTIVE;
377
case Token.tPREPROCESSOR_DEFINE:
378
state= CRefactory.OPTION_IN_MACRO_DEFINITION;
380
case Token.tPREPROCESSOR_INCLUDE:
381
state= CRefactory.OPTION_IN_INCLUDE_DIRECTIVE;
384
if (state != lastState) {
385
locations.add(new int[] {token.getOffset(), state});
392
} catch (IOException e) {