EclipseJDT Source Viewer

Home|eclipse_jdt/src/org/eclipse/jdt/core/dom/NodeFinder.java
1/*******************************************************************************
2 * Copyright (c) 2000, 2013 IBM Corporation and others.
3 *
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
8 *
9 * SPDX-License-Identifier: EPL-2.0
10 *
11 * Contributors:
12 *     IBM Corporation - initial API and implementation
13 *******************************************************************************/
14package org.eclipse.jdt.core.dom;
15
16import org.eclipse.jdt.core.IBuffer;
17import org.eclipse.jdt.core.ISourceRange;
18import org.eclipse.jdt.core.ITypeRoot;
19import org.eclipse.jdt.core.JavaModelException;
20import org.eclipse.jdt.core.ToolFactory;
21import org.eclipse.jdt.core.compiler.IScanner;
22import org.eclipse.jdt.core.compiler.ITerminalSymbols;
23import org.eclipse.jdt.core.compiler.InvalidInputException;
24
25/**
26 * For a given selection range, finds the covered node and the covering node.
27 *
28 * @since 3.5
29 */
30public final class NodeFinder {
31    /**
32     * This class defines the actual visitor that finds the node.
33     */
34    private static class NodeFinderVisitor extends ASTVisitor {
35        private int fStart;
36        private int fEnd;
37        private ASTNode fCoveringNode;
38        private ASTNode fCoveredNode;
39
40        NodeFinderVisitor(int offsetint length) {
41            super(true); // include Javadoc tags
42            this.fStartoffset;
43            this.fEndoffset + length;
44        }
45
46        @Override
47        public boolean preVisit2(ASTNode node) {
48            int nodeStartnode.getStartPosition();
49            int nodeEndnodeStart + node.getLength();
50            if (nodeEnd < this.fStart || this.fEnd < nodeStart) {
51                return false;
52            }
53            if (nodeStart <= this.fStart && this.fEnd <= nodeEnd) {
54                this.fCoveringNodenode;
55            }
56            if (this.fStart <= nodeStart && nodeEnd <= this.fEnd) {
57                if (this.fCoveringNode == node) { // nodeStart == fStart && nodeEnd == fEnd
58                    this.fCoveredNodenode;
59                    return true// look further for node with same length as parent
60                } else if (this.fCoveredNode == null) { // no better found
61                    this.fCoveredNodenode;
62                }
63                return false;
64            }
65            return true;
66        }
67        /**
68         * Returns the covered node. If more than one nodes are covered by the selection, the
69         * returned node is first covered node found in a top-down traversal of the AST
70         * @return ASTNode
71         */
72        public ASTNode getCoveredNode() {
73            return this.fCoveredNode;
74        }
75
76        /**
77         * Returns the covering node. If more than one nodes are covering the selection, the
78         * returned node is last covering node found in a top-down traversal of the AST
79         * @return ASTNode
80         */
81        public ASTNode getCoveringNode() {
82            return this.fCoveringNode;
83        }
84    }
85    /**
86     * Maps a selection to an ASTNode, where the selection is defined using a start and a length.
87     * The result node is determined as follows:
88     * <ul>
89     *   <li>First, tries to find a node whose range is the exactly the given selection.
90     *       If multiple matching nodes are found, the innermost is returned.</li>
91     *   <li>If no such node exists, then the last node in a preorder traversal of the AST is returned, where
92     *       the node range fully contains the selection.
93     *       If the length is zero, then ties between adjacent nodes are broken by choosing the right side.</li>
94     * </ul>
95     *
96     * @param root the root node from which the search starts
97     * @param start the start of the selection
98     * @param length the length of the selection
99     *
100     * @return the innermost node that exactly matches the selection, or the first node that contains the selection
101     */
102    public static ASTNode perform(ASTNode rootint startint length) {
103        NodeFinder finder = new NodeFinder(rootstartlength);
104        ASTNode resultfinder.getCoveredNode();
105        if (result == null || result.getStartPosition() != start || result.getLength() != length) {
106            return finder.getCoveringNode();
107        }
108        return result;
109    }
110
111    /**
112     * Maps a selection to an ASTNode, where the selection is defined using a source range.
113     * Calls <code>perform(root, range.getOffset(), range.getLength())</code>.
114     *
115     * @param root the root node from which the search starts
116     * @param range the selection range
117     * @return the innermost node that exactly matches the selection, or the first node that contains the selection
118     * @see #perform(ASTNode, int, int)
119     */
120    public static ASTNode perform(ASTNode rootISourceRange range) {
121        return perform(rootrange.getOffset(), range.getLength());
122    }
123
124    /**
125     * Maps a selection to an ASTNode, where the selection is given by a start and a length.
126     * The result node is determined as follows:
127     * <ul>
128     *   <li>If {@link #getCoveredNode()} doesn't find a node, returns <code>null</code>.</li>
129     *   <li>Otherwise, iff the selection only contains the covered node and optionally some whitespace or comments
130     *       on either side of the node, returns the node.</li>
131     *   <li>Otherwise, returns the {@link #getCoveringNode() covering} node.</li>
132     * </ul>
133     *
134     * @param root the root node from which the search starts
135     * @param start the start of the selection
136     * @param length the length of the selection
137     * @param source the source of the compilation unit
138     *
139     * @return the result node
140     * @throws JavaModelException if an error occurs in the Java model
141     */
142    public static ASTNode perform(ASTNode rootint startint lengthITypeRoot source) throws JavaModelException {
143        NodeFinder finder = new NodeFinder(rootstartlength);
144        ASTNode resultfinder.getCoveredNode();
145        if (result == null)
146            return null;
147        int nodeStartresult.getStartPosition();
148        if (start <= nodeStart && ((nodeStart + result.getLength()) <= (start + length))) {
149            IBuffer buffersource.getBuffer();
150            if (buffer != null) {
151                IScanner scannerToolFactory.createScanner(falsefalsefalsefalse);
152                try {
153                    scanner.setSource(buffer.getText(startlength).toCharArray());
154                    int tokenscanner.getNextToken();
155                    if (token != ITerminalSymbols.TokenNameEOF) {
156                        int tStartscanner.getCurrentTokenStartPosition();
157                        if (tStart == result.getStartPosition() - start) {
158                            scanner.resetTo(tStart + result.getLength(), length - 1);
159                            tokenscanner.getNextToken();
160                            if (token == ITerminalSymbols.TokenNameEOF)
161                                return result;
162                        }
163                    }
164                } catch (InvalidInputException e) {
165                    // ignore
166                } catch (IndexOutOfBoundsException e) {
167                    // https://bugs.eclipse.org/bugs/show_bug.cgi?id=305001
168                    return null;
169                }
170            }
171        }
172        return finder.getCoveringNode();
173    }
174    private ASTNode fCoveringNode;
175    private ASTNode fCoveredNode;
176
177    /**
178     * Instantiate a new node finder using the given root node, the given start and the given length.
179     *
180     * @param root the given root node
181     * @param start the given start
182     * @param length the given length
183     */
184    public NodeFinder(ASTNode rootint startint length) {
185        NodeFinderVisitor nodeFinderVisitor = new NodeFinderVisitor(startlength);
186        root.accept(nodeFinderVisitor);
187        this.fCoveredNode = nodeFinderVisitor.getCoveredNode();
188        this.fCoveringNode = nodeFinderVisitor.getCoveringNode();
189    }
190    /**
191     * If the AST contains nodes whose range is equal to the selection, returns the innermost of those nodes.
192     * Otherwise, returns the first node in a preorder traversal of the AST, where the complete node range is covered by the selection.
193     * <p>
194     * Example: For a {@link SimpleType} whose name is a {@link SimpleName} and a selection that equals both nodes' range,
195     * the covered node is the <code>SimpleName</code>.
196     * But if the selection is expanded to include a whitespace before or after the <code>SimpleType</code>,
197     * then the covered node is the <code>SimpleType</code>.
198     * </p>
199     *
200     * @return the covered node, or <code>null</code> if the selection is empty or too short to cover an entire node
201     */
202    public ASTNode getCoveredNode() {
203        return this.fCoveredNode;
204    }
205
206    /**
207     * Returns the innermost node that fully contains the selection. A node also contains the zero-length selection on either end.
208     * <p>
209     * If more than one node covers the selection, the returned node is the last covering node found in a preorder traversal of the AST.
210     * This implies that for a zero-length selection between two adjacent sibling nodes, the node on the right is returned.
211     * </p>
212     *
213     * @return the covering node
214     */
215    public ASTNode getCoveringNode() {
216        return this.fCoveringNode;
217    }
218}
219
MembersX
NodeFinder:NodeFinderVisitor:preVisit2
NodeFinder:perform:Block:finder
NodeFinder:fCoveringNode
NodeFinder:perform
NodeFinder:perform:Block:Block:Block:Block:token
NodeFinder:getCoveredNode
NodeFinder:NodeFinder
NodeFinder:NodeFinderVisitor:NodeFinderVisitor
NodeFinder:NodeFinderVisitor:preVisit2:Block:nodeStart
NodeFinder:fCoveredNode
NodeFinder:perform:Block:Block:Block:Block:Block:tStart
NodeFinder:NodeFinderVisitor:fCoveredNode
NodeFinder:NodeFinderVisitor:getCoveredNode
NodeFinder:NodeFinder:Block:nodeFinderVisitor
NodeFinder:NodeFinderVisitor:fCoveringNode
NodeFinder:NodeFinderVisitor:getCoveringNode
NodeFinder:perform:Block:Block:Block:scanner
NodeFinder:perform:Block:nodeStart
NodeFinder:getCoveringNode
NodeFinder:NodeFinderVisitor:preVisit2:Block:nodeEnd
NodeFinder:perform:Block:Block:buffer
NodeFinder:perform:Block:result
NodeFinder:NodeFinderVisitor:fStart
NodeFinder:NodeFinderVisitor:fEnd
Members
X