package org.rubypeople.rdt.internal.ti.util;
import org.jruby.ast.Node;
/**
* Visitor to find the closest node that spans a given offset that satisfies a given condition.
*
* @author Jason Morrison
*/
public class ClosestSpanningNodeLocator extends NodeLocator
{
// Singleton pattern
private ClosestSpanningNodeLocator()
{
}
private static ClosestSpanningNodeLocator staticInstance = new ClosestSpanningNodeLocator();
public static ClosestSpanningNodeLocator Instance()
{
return staticInstance;
}
/** Offset to span. */
private int offset;
/** INodeAcceptor that defines the desired node. */
private INodeAcceptor acceptor;
/** Running best match for closest spanner */
private Node locatedNode;
/**
* Finds the closest spanning node given offset that is accepted by the acceptor.
*
* @param rootNode
* Root Node that contains all nodes to search.
* @param offset
* Offset to search for
* @param acceptor
* INodeAcceptor defining the condition which the desired node fulfills.
* @return First precursor or null.
*/
public Node findClosestSpanner(Node rootNode, int offset, INodeAcceptor acceptor)
{
if (rootNode == null)
return null;
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 spanning node.
*/
public Object handleNode(Node iVisited)
{
boolean nodeSpansOffset = nodeSpansOffset(iVisited, offset);
boolean nodeSpansMoreCloselyThanCurrent = (locatedNode == null)
|| (calculateSpanLength(iVisited) <= calculateSpanLength(locatedNode));
if (nodeSpansOffset && nodeSpansMoreCloselyThanCurrent && acceptor.doesAccept(iVisited))
{
locatedNode = iVisited;
}
return super.handleNode(iVisited);
}
/**
* Determine whether the node's position spans an offset
*
* @param node
* Node to check
* @param offset
* Offset to check
* @return Whether it spans the offset
*/
public static boolean nodeSpansOffset(Node node, int offset)
{
return (node.getPosition().getStartOffset() <= offset) && (node.getPosition().getEndOffset() > offset);
}
/**
* Gets the span length of the node (endOffset - startOffset)
*
* @param node
* Node to check
* @return Span length
*/
private int calculateSpanLength(Node node)
{
if (node == null)
{
return 0;
}
if (node.getPosition() == null)
{
return 0;
}
return node.getPosition().getEndOffset() - node.getPosition().getStartOffset();
}
}