1
/*******************************************************************************
2
* Copyright (c) 2007, 2009 Google, Inc and others.
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
* Sergey Prigogin (Google) - initial API and implementation
10
*******************************************************************************/
11
package org.eclipse.cdt.internal.ui.search;
13
import java.util.ArrayList;
14
import java.util.Arrays;
15
import java.util.List;
17
import org.eclipse.jface.text.IRegion;
18
import org.eclipse.jface.text.Region;
20
import org.eclipse.cdt.core.dom.ast.DOMException;
21
import org.eclipse.cdt.core.dom.ast.IASTComment;
22
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
23
import org.eclipse.cdt.core.dom.ast.IASTName;
24
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElseStatement;
25
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorEndifStatement;
26
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfStatement;
27
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfdefStatement;
28
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfndefStatement;
29
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
30
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement;
31
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorUndefStatement;
32
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
33
import org.eclipse.cdt.core.dom.ast.IBinding;
34
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
35
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId;
36
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
37
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
38
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
40
import org.eclipse.cdt.internal.core.dom.parser.ASTTranslationUnit;
43
* Finds locations of linked names. Used by Rename in File.
45
public class LinkedNamesFinder {
46
private static final IRegion[] EMPTY_LOCATIONS_ARRAY = new IRegion[0];
48
private LinkedNamesFinder() {
52
public static IRegion[] findByName(IASTTranslationUnit root, IASTName name) {
53
IBinding target = name.resolveBinding();
55
return EMPTY_LOCATIONS_ARRAY;
57
BindingFinder bindingFinder = new BindingFinder(root);
58
bindingFinder.find(target);
59
return bindingFinder.getLocations();
62
private static class BindingFinder {
63
private final IASTTranslationUnit root;
64
private final List<IRegion> locations;
66
public BindingFinder(IASTTranslationUnit root) {
68
locations = new ArrayList<IRegion>();
71
public void find(IBinding target) {
72
if (target instanceof IMacroBinding) {
73
findMacro((IMacroBinding) target);
78
if (target instanceof ICPPConstructor ||
79
target instanceof ICPPMethod && ((ICPPMethod) target).isDestructor()) {
80
target = ((ICPPMethod) target).getClassOwner();
82
} catch (DOMException e1) {
86
if (target instanceof ICPPClassType) {
88
ICPPConstructor[] constructors = ((ICPPClassType) target).getConstructors();
89
for (ICPPConstructor ctor : constructors) {
90
if (!ctor.isImplicit()) {
94
ICPPMethod[] methods = ((ICPPClassType) target).getDeclaredMethods();
95
for (ICPPMethod method : methods) {
96
if (method.isDestructor()) {
100
} catch (DOMException e) {
105
public IRegion[] getLocations() {
106
if (locations.isEmpty()) {
107
return EMPTY_LOCATIONS_ARRAY;
109
return locations.toArray(new IRegion[locations.size()]);
112
private void findBinding(IBinding target) {
113
IASTName[] names= root.getDeclarationsInAST(target);
114
for (int i= 0; i < names.length; i++) {
115
IASTName candidate= names[i];
116
if (candidate.isPartOfTranslationUnitFile()) {
117
addLocation(candidate);
120
names= root.getReferences(target);
121
for (int i= 0; i < names.length; i++) {
122
IASTName candidate= names[i];
123
if (candidate.isPartOfTranslationUnitFile()) {
124
addLocation(candidate);
129
private void addLocation(IASTName name) {
130
IBinding binding = name.resolveBinding();
131
if (binding != null) {
132
if (name instanceof ICPPASTTemplateId) {
133
name= ((ICPPASTTemplateId) name).getTemplateName();
135
IASTFileLocation fileLocation= name.getImageLocation();
136
if (fileLocation == null || !root.getFilePath().equals(fileLocation.getFileName())) {
137
fileLocation= name.getFileLocation();
139
if (fileLocation != null) {
140
int offset= fileLocation.getNodeOffset();
141
int length= fileLocation.getNodeLength();
142
if (binding instanceof ICPPMethod && ((ICPPMethod) binding).isDestructor()) {
147
if (offset >= 0 && length > 0) {
148
locations.add(new Region(offset, length));
155
* Adds all occurrences of a macro name to the list of locations. Macro occurrences
156
* may belong to multiple macro bindings with the same name. Macro names are also
157
* looked for in the comments of #else and #endif statements.
158
* Comments of #else and #endif statements related to #ifdef or #ifndef are searched
159
* for the macro name referenced by the #if[n]def.
160
* @param target a binding representing a macro.
162
private void findMacro(IMacroBinding target) {
164
char[] nameChars = target.getNameCharArray();
165
List<IASTName> ifdefNameStack = new ArrayList<IASTName>();
166
IASTPreprocessorStatement[] statements = root.getAllPreprocessorStatements();
167
for (IASTPreprocessorStatement statement : statements) {
168
if (!statement.isPartOfTranslationUnitFile()) {
171
IASTName macroName = null;
172
boolean ifStatement = false;
173
if (statement instanceof IASTPreprocessorIfdefStatement) {
174
macroName = ((IASTPreprocessorIfdefStatement) statement).getMacroReference();
176
} else if (statement instanceof IASTPreprocessorIfndefStatement) {
177
macroName = ((IASTPreprocessorIfndefStatement) statement).getMacroReference();
179
} else if (statement instanceof IASTPreprocessorMacroDefinition) {
180
macroName = ((IASTPreprocessorMacroDefinition) statement).getName();
181
} else if (statement instanceof IASTPreprocessorUndefStatement) {
182
macroName = ((IASTPreprocessorUndefStatement) statement).getMacroName();
183
} else if (statement instanceof IASTPreprocessorIfStatement) {
185
} else if (statement instanceof IASTPreprocessorEndifStatement) {
186
if (!ifdefNameStack.isEmpty())
187
if (ifdefNameStack.remove(ifdefNameStack.size() - 1) != null) {
188
findInStatementComment(nameChars, statement);
190
} else if (statement instanceof IASTPreprocessorElseStatement) {
191
if (!ifdefNameStack.isEmpty())
192
if (ifdefNameStack.get(ifdefNameStack.size() - 1) != null) {
193
findInStatementComment(nameChars, statement);
196
if (macroName != null) {
197
if (Arrays.equals(nameChars, macroName.getSimpleID())) {
198
IBinding binding = macroName.resolveBinding();
199
if (!target.equals(binding)) {
200
findBinding(binding);
207
ifdefNameStack.add(macroName);
213
* Finds locations of a given name in the comment of a preprocessor statement.
215
private void findInStatementComment(char[] nameChars, IASTPreprocessorStatement statement) {
216
IASTFileLocation location = statement.getFileLocation();
217
IASTComment comment = findComment(location.getNodeOffset() + location.getNodeLength());
218
if (comment != null &&
219
comment.getFileLocation().getStartingLineNumber() == location.getStartingLineNumber()) {
220
findInComment(nameChars, comment);
225
* Returns the first comment after the given offset.
226
* @param startOffset a file offset.
227
* @return a comment or <code>null</code>, if there are no comments after the offset.
229
private IASTComment findComment(int startOffset) {
230
IASTComment[] comments = ((ASTTranslationUnit) root).getComments();
232
int high = comments.length;
234
int mid = (low + high) / 2;
235
int offset = comments[mid].getFileLocation().getNodeOffset();
236
if (offset < startOffset) {
240
if (offset == startOffset) {
245
return high < comments.length ? comments[high] : null;
249
* Adds all occurrences of a name in a comment to the list of locations.
251
private void findInComment(char[] name, IASTComment comment) {
252
char[] text = comment.getComment();
254
// First two characters are either /* or //
255
for (int i = 2; i <= text.length - name.length + j; i++) {
257
if (!Character.isJavaIdentifierPart(c)) {
259
} else if (j >= 0 && j < name.length && name[j] == c) {
261
if (j == name.length &&
262
(i + 1 == text.length || !Character.isJavaIdentifierPart(text[i + 1]))) {
263
int offset = comment.getFileLocation().getNodeOffset() + i + 1 - name.length;
264
locations.add(new Region(offset, name.length));