package org.rubypeople.rdt.internal.ti.util;
import org.jruby.ast.Node;
/**
* Visitor to find the first node that precedes a given offset that satisfies a given condition.
* @author Jason Morrison
*/
public class FirstPrecursorNodeLocator extends NodeLocator {
//Singleton pattern
private FirstPrecursorNodeLocator() {}
private static FirstPrecursorNodeLocator staticInstance = new FirstPrecursorNodeLocator();
public static FirstPrecursorNodeLocator Instance()
{
return staticInstance;
}
/** Offset to start searching backwards from. */
private int offset;
/** INodeAcceptor that defines the desired node. */
private INodeAcceptor acceptor;
/** Running best match for closest precursor */
private Node locatedNode;
/**
* Finds the first node preceding the given offset that is accepted by the acceptor.
* @param rootNode Root Node that contains all nodes to search.
* @param offset Offset to search backwards from; returned node must occur strictly before this (i.e. end before offset.)
* @param acceptor INodeAcceptor defining the condition which the desired node fulfills.
* @return First precursor or null.
*/
public Node findFirstPrecursor(Node rootNode, int offset, INodeAcceptor acceptor ) {
locatedNode = null;
this.offset = offset;
this.acceptor = acceptor;
// Traverse to find closest precursor
rootNode.accept(this);
// Return the match
return locatedNode;
}
/**
* Searches via InOrderVisitor for the closest precursor.
*/
public Object handleNode(Node iVisited) {
// TODO This will include nodes that envelop nodeStart, not only those starting strictly before it.
// If this behavior is unwanted, remove the || (iVisited.getPosition().getStartOffset() <= offset)
// in the conditional
if (( iVisited.getPosition().getEndOffset() <= offset) || (iVisited.getPosition().getStartOffset() <= offset )) {
if ( acceptor.doesAccept( iVisited ) ) {
locatedNode = iVisited;
}
}
return super.handleNode(iVisited);
}
}