001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.corext.dom;
011:
012: import org.eclipse.jdt.core.IBuffer;
013: import org.eclipse.jdt.core.ISourceRange;
014: import org.eclipse.jdt.core.ITypeRoot;
015: import org.eclipse.jdt.core.JavaModelException;
016: import org.eclipse.jdt.core.ToolFactory;
017: import org.eclipse.jdt.core.compiler.IScanner;
018: import org.eclipse.jdt.core.compiler.ITerminalSymbols;
019: import org.eclipse.jdt.core.compiler.InvalidInputException;
020: import org.eclipse.jdt.core.dom.ASTNode;
021:
022: /**
023: * For a give range finds the node covered and the node covering.
024: *
025: * @since 2.1
026: */
027: public class NodeFinder extends GenericVisitor {
028:
029: /**
030: * A visitor that maps a selection to a given ASTNode. The result node is
031: * determined as follows:
032: * <ul>
033: * <li>first the visitor tries to find a node with the exact start and length</li>
034: * <li>if no such node exists than the node that encloses the range defined by
035: * start and end is returned.</li>
036: * <li>if the length is zero than also nodes are considered where the node's
037: * start or end position matches <code>start</code>.</li>
038: * <li>otherwise <code>null</code> is returned.</li>
039: * </ul>
040: *
041: * @param root the root node from which the search starts
042: * @param start the start offset
043: * @param length the length
044: *
045: * @return the result node
046: *
047: * @since 2.1
048: */
049: public static ASTNode perform(ASTNode root, int start, int length) {
050: NodeFinder finder = new NodeFinder(start, length);
051: root.accept(finder);
052: ASTNode result = finder.getCoveredNode();
053: if (result == null || result.getStartPosition() != start
054: || result.getLength() != length) {
055: return finder.getCoveringNode();
056: }
057: return result;
058: }
059:
060: public static ASTNode perform(ASTNode root, ISourceRange range) {
061: return perform(root, range.getOffset(), range.getLength());
062: }
063:
064: /**
065: * A visitor that maps a selection to a given ASTNode. The result node is
066: * determined as follows:
067: * <ul>
068: * <li>first the visitor tries to find a node that is covered by <code>start</code> and
069: * <code>length</code> where either <code>start</code> and <code>length</code> exactly
070: * matches the node or where the text covered before and after the node only consists
071: * of white spaces or comments.</li>
072: * <li>if no such node exists than the node that encloses the range defined by
073: * start and end is returned.</li>
074: * <li>if the length is zero than also nodes are considered where the node's
075: * start or end position matches <code>start</code>.</li>
076: * <li>otherwise <code>null</code> is returned.</li>
077: * </ul>
078: *
079: * @param root the root node from which the search starts
080: * @param start the start offset
081: * @param length the length
082: * @param source the source of the compilation unit
083: *
084: * @return the result node
085: * @throws JavaModelException if an error occurs in the Java model
086: *
087: * @since 3.0
088: */
089: public static ASTNode perform(ASTNode root, int start, int length,
090: ITypeRoot source) throws JavaModelException {
091: NodeFinder finder = new NodeFinder(start, length);
092: root.accept(finder);
093: ASTNode result = finder.getCoveredNode();
094: if (result == null)
095: return null;
096: Selection selection = Selection.createFromStartLength(start,
097: length);
098: if (selection.covers(result)) {
099: IBuffer buffer = source.getBuffer();
100: if (buffer != null) {
101: IScanner scanner = ToolFactory.createScanner(false,
102: false, false, false);
103: scanner.setSource(buffer.getText(start, length)
104: .toCharArray());
105: try {
106: int token = scanner.getNextToken();
107: if (token != ITerminalSymbols.TokenNameEOF) {
108: int tStart = scanner
109: .getCurrentTokenStartPosition();
110: if (tStart == result.getStartPosition() - start) {
111: scanner.resetTo(
112: tStart + result.getLength(),
113: length - 1);
114: token = scanner.getNextToken();
115: if (token == ITerminalSymbols.TokenNameEOF)
116: return result;
117: }
118: }
119: } catch (InvalidInputException e) {
120: }
121: }
122: }
123: return finder.getCoveringNode();
124: }
125:
126: private int fStart;
127: private int fEnd;
128:
129: private ASTNode fCoveringNode;
130: private ASTNode fCoveredNode;
131:
132: public NodeFinder(int offset, int length) {
133: super (true); // include Javadoc tags
134: fStart = offset;
135: fEnd = offset + length;
136: }
137:
138: protected boolean visitNode(ASTNode node) {
139: int nodeStart = node.getStartPosition();
140: int nodeEnd = nodeStart + node.getLength();
141: if (nodeEnd < fStart || fEnd < nodeStart) {
142: return false;
143: }
144: if (nodeStart <= fStart && fEnd <= nodeEnd) {
145: fCoveringNode = node;
146: }
147: if (fStart <= nodeStart && nodeEnd <= fEnd) {
148: if (fCoveringNode == node) { // nodeStart == fStart && nodeEnd == fEnd
149: fCoveredNode = node;
150: return true; // look further for node with same length as parent
151: } else if (fCoveredNode == null) { // no better found
152: fCoveredNode = node;
153: }
154: return false;
155: }
156: return true;
157: }
158:
159: /**
160: * Returns the covered node. If more than one nodes are covered by the selection, the
161: * returned node is first covered node found in a top-down traversal of the AST
162: * @return ASTNode
163: */
164: public ASTNode getCoveredNode() {
165: return fCoveredNode;
166: }
167:
168: /**
169: * Returns the covering node. If more than one nodes are covering the selection, the
170: * returned node is last covering node found in a top-down traversal of the AST
171: * @return ASTNode
172: */
173: public ASTNode getCoveringNode() {
174: return fCoveringNode;
175: }
176:
177: }
|