Added
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 2000, 2008 IBM Corporation 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 |
7 |
* |
8 |
* Contributors: |
9 |
* IBM Corporation - initial API and implementation |
10 |
*******************************************************************************/ |
11 |
package org.eclipse.jdt.core.dom; |
12 |
|
13 |
import org.eclipse.jdt.core.IBuffer; |
14 |
import org.eclipse.jdt.core.ISourceRange; |
15 |
import org.eclipse.jdt.core.ITypeRoot; |
16 |
import org.eclipse.jdt.core.JavaModelException; |
17 |
import org.eclipse.jdt.core.ToolFactory; |
18 |
import org.eclipse.jdt.core.compiler.IScanner; |
19 |
import org.eclipse.jdt.core.compiler.ITerminalSymbols; |
20 |
import org.eclipse.jdt.core.compiler.InvalidInputException; |
21 |
|
22 |
/** |
23 |
* For a given range, finds the covered node and the covering node. |
24 |
* This API is still under discussion, see https://bugs.eclipse.org/53024 . |
25 |
* |
26 |
* @since 3.5 |
27 |
*/ |
28 |
public final class NodeFinder { |
29 |
/** |
30 |
* This class defines the actual visitor that finds the node. |
31 |
*/ |
32 |
private static class NodeFinderVisitor extends ASTVisitor { |
33 |
private int fStart; |
34 |
private int fEnd; |
35 |
private ASTNode fCoveringNode; |
36 |
private ASTNode fCoveredNode; |
37 |
|
38 |
NodeFinderVisitor(int offset, int length) { |
39 |
super(true); // include Javadoc tags |
40 |
this.fStart= offset; |
41 |
this.fEnd= offset + length; |
42 |
} |
43 |
|
44 |
public boolean preVisit2(ASTNode node) { |
45 |
int nodeStart= node.getStartPosition(); |
46 |
int nodeEnd= nodeStart + node.getLength(); |
47 |
if (nodeEnd < this.fStart || this.fEnd < nodeStart) { |
48 |
return false; |
49 |
} |
50 |
if (nodeStart <= this.fStart && this.fEnd <= nodeEnd) { |
51 |
this.fCoveringNode= node; |
52 |
} |
53 |
if (this.fStart <= nodeStart && nodeEnd <= this.fEnd) { |
54 |
if (this.fCoveringNode == node) { // nodeStart == fStart && nodeEnd == fEnd |
55 |
this.fCoveredNode= node; |
56 |
return true; // look further for node with same length as parent |
57 |
} else if (this.fCoveredNode == null) { // no better found |
58 |
this.fCoveredNode= node; |
59 |
} |
60 |
return false; |
61 |
} |
62 |
return true; |
63 |
} |
64 |
/** |
65 |
* Returns the covered node. If more than one nodes are covered by the selection, the |
66 |
* returned node is first covered node found in a top-down traversal of the AST |
67 |
* @return ASTNode |
68 |
*/ |
69 |
public ASTNode getCoveredNode() { |
70 |
return this.fCoveredNode; |
71 |
} |
72 |
|
73 |
/** |
74 |
* Returns the covering node. If more than one nodes are covering the selection, the |
75 |
* returned node is last covering node found in a top-down traversal of the AST |
76 |
* @return ASTNode |
77 |
*/ |
78 |
public ASTNode getCoveringNode() { |
79 |
return this.fCoveringNode; |
80 |
} |
81 |
} |
82 |
/** |
83 |
* Maps a selection to a given ASTNode, where the selection is defined using a start and a length. |
84 |
* The result node is determined as follows: |
85 |
* <ul> |
86 |
* <li>first the visitor tries to find a node with the exact <code>start</code> and <code>length</code></li> |
87 |
* <li>if no such node exists than the node that encloses the range defined by |
88 |
* <code>start</code> and <code>length</code> is returned.</li> |
89 |
* <li>if the length is zero than also nodes are considered where the node's |
90 |
* start or end position matches <code>start</code>.</li> |
91 |
* <li>otherwise <code>null</code> is returned.</li> |
92 |
* </ul> |
93 |
* |
94 |
* @param root the root node from which the search starts |
95 |
* @param start the given start |
96 |
* @param length the given length |
97 |
* |
98 |
* @return the found node |
99 |
*/ |
100 |
public static ASTNode perform(ASTNode root, int start, int length) { |
101 |
NodeFinder finder = new NodeFinder(root, start, length); |
102 |
ASTNode result= finder.getCoveredNode(); |
103 |
if (result == null || result.getStartPosition() != start || result.getLength() != length) { |
104 |
return finder.getCoveringNode(); |
105 |
} |
106 |
return result; |
107 |
} |
108 |
|
109 |
/** |
110 |
* Maps a selection to a given ASTNode, where the selection is defined using a source range. |
111 |
* It calls <code>perform(root, range.getOffset(), range.getLength())</code>. |
112 |
* |
113 |
* @return the result node |
114 |
* @see #perform(ASTNode, int, int) |
115 |
*/ |
116 |
public static ASTNode perform(ASTNode root, ISourceRange range) { |
117 |
return perform(root, range.getOffset(), range.getLength()); |
118 |
} |
119 |
|
120 |
/** |
121 |
* Maps a selection to a given ASTNode, where the selection is given by a start and a length. |
122 |
* The result node is determined as follows: |
123 |
* <ul> |
124 |
* <li>first the visitor tries to find a node that is covered by <code>start</code> and |
125 |
* <code>length</code> where either <code>start</code> and <code>length</code> exactly |
126 |
* matches the node or where the text covered before and after the node only consists |
127 |
* of white spaces or comments.</li> |
128 |
* <li>if no such node exists than the node that encloses the range defined by |
129 |
* <code>start</code> and <code>length</code> is returned.</li> |
130 |
* <li>if the length is zero than also nodes are considered where the node's |
131 |
* start or end position matches <code>start</code>.</li> |
132 |
* <li>otherwise <code>null</code> is returned.</li> |
133 |
* </ul> |
134 |
* |
135 |
* @param root the root node from which the search starts |
136 |
* @param start the given start |
137 |
* @param length the given length |
138 |
* @param source the source of the compilation unit |
139 |
* |
140 |
* @return the result node |
141 |
* @throws JavaModelException if an error occurs in the Java model |
142 |
*/ |
143 |
public static ASTNode perform(ASTNode root, int start, int length, ITypeRoot source) throws JavaModelException { |
144 |
NodeFinder finder = new NodeFinder(root, start, length); |
145 |
ASTNode result= finder.getCoveredNode(); |
146 |
if (result == null) |
147 |
return null; |
148 |
int nodeStart= result.getStartPosition(); |
149 |
if (start <= nodeStart && ((nodeStart + result.getLength()) <= (start + length))) { |
150 |
IBuffer buffer= source.getBuffer(); |
151 |
if (buffer != null) { |
152 |
IScanner scanner= ToolFactory.createScanner(false, false, false, false); |
153 |
scanner.setSource(buffer.getText(start, length).toCharArray()); |
154 |
try { |
155 |
int token= scanner.getNextToken(); |
156 |
if (token != ITerminalSymbols.TokenNameEOF) { |
157 |
int tStart= scanner.getCurrentTokenStartPosition(); |
158 |
if (tStart == result.getStartPosition() - start) { |
159 |
scanner.resetTo(tStart + result.getLength(), length - 1); |
160 |
token= scanner.getNextToken(); |
161 |
if (token == ITerminalSymbols.TokenNameEOF) |
162 |
return result; |
163 |
} |
164 |
} |
165 |
} catch (InvalidInputException e) { |
166 |
// ignore |
167 |
} |
168 |
} |
169 |
} |
170 |
return finder.getCoveringNode(); |
171 |
} |
172 |
private ASTNode fCoveringNode; |
173 |
private ASTNode fCoveredNode; |
174 |
|
175 |
/** |
176 |
* Instantiate a new node finder using the given root node, the given start and the given length. |
177 |
* |
178 |
* @param root the given root node |
179 |
* @param start the given start |
180 |
* @param length the given length |
181 |
*/ |
182 |
public NodeFinder(ASTNode root, int start, int length) { |
183 |
NodeFinderVisitor nodeFinderVisitor = new NodeFinderVisitor(start, length); |
184 |
root.accept(nodeFinderVisitor); |
185 |
this.fCoveredNode = nodeFinderVisitor.getCoveredNode(); |
186 |
this.fCoveringNode = nodeFinderVisitor.getCoveringNode(); |
187 |
} |
188 |
/** |
189 |
* Returns the covered node. If more than one nodes are covered by the selection, the |
190 |
* returned node is first covered node found in a top-down traversal of the AST. |
191 |
* |
192 |
* @return the covered node |
193 |
*/ |
194 |
public ASTNode getCoveredNode() { |
195 |
return this.fCoveredNode; |
196 |
} |
197 |
|
198 |
/** |
199 |
* Returns the covering node. If more than one nodes are covering the selection, the |
200 |
* returned node is last covering node found in a top-down traversal of the AST. |
201 |
* |
202 |
* @return the covering node |
203 |
*/ |
204 |
public ASTNode getCoveringNode() { |
205 |
return this.fCoveringNode; |
206 |
} |
207 |
} |