/*******************************************************************************
* 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
*******************************************************************************/
package org.eclipse.che.ide.ext.java.jdt.core.dom;
import org.eclipse.che.ide.ext.java.jdt.core.ISourceRange;
/**
* 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;
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;
}
}