| /******************************************************************************* |
| * Copyright (c) 2000, 2010 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Technical University Berlin - extended API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.core.dom; |
| |
| import org.eclipse.jdt.core.IBuffer; |
| import org.eclipse.jdt.core.ISourceRange; |
| import org.eclipse.jdt.core.ITypeRoot; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.ToolFactory; |
| import org.eclipse.jdt.core.compiler.IScanner; |
| import org.eclipse.jdt.core.compiler.ITerminalSymbols; |
| import org.eclipse.jdt.core.compiler.InvalidInputException; |
| |
| /** |
| * For a given range, finds the covered node and the covering node. |
| * |
| * @since 3.5 |
| */ |
| public final class NodeFinder { |
| /** |
| * This class defines the actual visitor that finds the node. |
| */ |
| private static class NodeFinderVisitor extends ASTVisitor { |
| private int fStart; |
| private int fEnd; |
| private ASTNode fCoveringNode; |
| private ASTNode fCoveredNode; |
| |
| //{ObjectTeams: Do not descend into role files: |
| // The topmost type found during traversal |
| private TypeDeclaration topType= null; |
| public boolean visit(TypeDeclaration type) { |
| if (topType == null) |
| topType= type; |
| return super.visit(type); |
| } |
| public boolean visit(RoleTypeDeclaration roleType) { |
| if (this.topType != null && roleType.isRoleFile()) |
| return false; |
| if (topType == null) |
| topType= roleType; |
| return super.visit(roleType); |
| } |
| // SH} |
| |
| NodeFinderVisitor(int offset, int length) { |
| super(true); // include Javadoc tags |
| this.fStart= offset; |
| this.fEnd= offset + length; |
| } |
| |
| public boolean preVisit2(ASTNode node) { |
| int nodeStart= node.getStartPosition(); |
| int nodeEnd= nodeStart + node.getLength(); |
| if (nodeEnd < this.fStart || this.fEnd < nodeStart) { |
| return false; |
| } |
| if (nodeStart <= this.fStart && this.fEnd <= nodeEnd) { |
| this.fCoveringNode= node; |
| } |
| if (this.fStart <= nodeStart && nodeEnd <= this.fEnd) { |
| if (this.fCoveringNode == node) { // nodeStart == fStart && nodeEnd == fEnd |
| this.fCoveredNode= node; |
| return true; // look further for node with same length as parent |
| } else if (this.fCoveredNode == null) { // no better found |
| this.fCoveredNode= node; |
| } |
| return false; |
| } |
| return true; |
| } |
| /** |
| * Returns the covered node. If more than one nodes are covered by the selection, the |
| * returned node is first covered node found in a top-down traversal of the AST |
| * @return ASTNode |
| */ |
| public ASTNode getCoveredNode() { |
| return this.fCoveredNode; |
| } |
| |
| /** |
| * Returns the covering node. If more than one nodes are covering the selection, the |
| * returned node is last covering node found in a top-down traversal of the AST |
| * @return ASTNode |
| */ |
| public ASTNode getCoveringNode() { |
| return this.fCoveringNode; |
| } |
| } |
| /** |
| * Maps a selection to a given ASTNode, where the selection is defined using a start and a length. |
| * The result node is determined as follows: |
| * <ul> |
| * <li>first the visitor tries to find a node with the exact <code>start</code> and <code>length</code></li> |
| * <li>if no such node exists then the node that encloses the range defined by |
| * <code>start</code> and <code>length</code> is returned.</li> |
| * <li>if the length is zero then also nodes are considered where the node's |
| * start or end position matches <code>start</code>.</li> |
| * <li>otherwise <code>null</code> is returned.</li> |
| * </ul> |
| * |
| * @param root the root node from which the search starts |
| * @param start the given start |
| * @param length the given length |
| * |
| * @return the found node |
| */ |
| public static ASTNode perform(ASTNode root, int start, int length) { |
| NodeFinder finder = new NodeFinder(root, start, length); |
| ASTNode result= finder.getCoveredNode(); |
| if (result == null || result.getStartPosition() != start || result.getLength() != length) { |
| return finder.getCoveringNode(); |
| } |
| return result; |
| } |
| |
| /** |
| * Maps a selection to a given ASTNode, where the selection is defined using a source range. |
| * It calls <code>perform(root, range.getOffset(), range.getLength())</code>. |
| * |
| * @return the result node |
| * @see #perform(ASTNode, int, int) |
| */ |
| public static ASTNode perform(ASTNode root, ISourceRange range) { |
| return perform(root, range.getOffset(), range.getLength()); |
| } |
| |
| /** |
| * Maps a selection to a given ASTNode, where the selection is given by a start and a length. |
| * The result node is determined as follows: |
| * <ul> |
| * <li>first the visitor tries to find a node that is covered by <code>start</code> and |
| * <code>length</code> where either <code>start</code> and <code>length</code> exactly |
| * matches the node or where the text covered before and after the node only consists |
| * of white spaces or comments.</li> |
| * <li>if no such node exists then the node that encloses the range defined by |
| * <code>start</code> and <code>length</code> is returned.</li> |
| * <li>if the length is zero then also nodes are considered where the node's |
| * start or end position matches <code>start</code>.</li> |
| * <li>otherwise <code>null</code> is returned.</li> |
| * </ul> |
| * |
| * @param root the root node from which the search starts |
| * @param start the given start |
| * @param length the given length |
| * @param source the source of the compilation unit |
| * |
| * @return the result node |
| * @throws JavaModelException if an error occurs in the Java model |
| */ |
| public static ASTNode perform(ASTNode root, int start, int length, ITypeRoot source) throws JavaModelException { |
| NodeFinder finder = new NodeFinder(root, start, length); |
| ASTNode result= finder.getCoveredNode(); |
| if (result == null) |
| return null; |
| int nodeStart= result.getStartPosition(); |
| if (start <= nodeStart && ((nodeStart + result.getLength()) <= (start + length))) { |
| IBuffer buffer= source.getBuffer(); |
| if (buffer != null) { |
| IScanner scanner= ToolFactory.createScanner(false, false, false, false); |
| try { |
| scanner.setSource(buffer.getText(start, length).toCharArray()); |
| int token= scanner.getNextToken(); |
| if (token != ITerminalSymbols.TokenNameEOF) { |
| int tStart= scanner.getCurrentTokenStartPosition(); |
| if (tStart == result.getStartPosition() - start) { |
| scanner.resetTo(tStart + result.getLength(), length - 1); |
| token= scanner.getNextToken(); |
| if (token == ITerminalSymbols.TokenNameEOF) |
| return result; |
| } |
| } |
| } catch (InvalidInputException e) { |
| // ignore |
| } catch (IndexOutOfBoundsException e) { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=305001 |
| return null; |
| } |
| } |
| } |
| return finder.getCoveringNode(); |
| } |
| private ASTNode fCoveringNode; |
| private ASTNode fCoveredNode; |
| |
| /** |
| * Instantiate a new node finder using the given root node, the given start and the given length. |
| * |
| * @param root the given root node |
| * @param start the given start |
| * @param length the given length |
| */ |
| public NodeFinder(ASTNode root, int start, int length) { |
| NodeFinderVisitor nodeFinderVisitor = new NodeFinderVisitor(start, length); |
| root.accept(nodeFinderVisitor); |
| this.fCoveredNode = nodeFinderVisitor.getCoveredNode(); |
| this.fCoveringNode = nodeFinderVisitor.getCoveringNode(); |
| } |
| /** |
| * Returns the covered node. If more than one nodes are covered by the selection, the |
| * returned node is first covered node found in a top-down traversal of the AST. |
| * |
| * @return the covered node |
| */ |
| public ASTNode getCoveredNode() { |
| return this.fCoveredNode; |
| } |
| |
| /** |
| * Returns the covering node. If more than one nodes are covering the selection, the |
| * returned node is last covering node found in a top-down traversal of the AST. |
| * |
| * @return the covering node |
| */ |
| public ASTNode getCoveringNode() { |
| return this.fCoveringNode; |
| } |
| } |