package org.rubypeople.rdt.internal.core.parser;
import org.jruby.ast.NewlineNode;
import org.jruby.ast.Node;
import org.jruby.lexer.yacc.ISourcePosition;
import org.rubypeople.rdt.internal.ti.util.INodeAcceptor;
public class ClosestNodeLocator extends InOrderVisitor
{
private int startOffset;
private int endOffset;
private int smallestDiff = Integer.MAX_VALUE;
private Node locatedNode;
private INodeAcceptor acceptor;
public Object handleNode(Node iVisited)
{
ISourcePosition position = iVisited.getPosition();
int diff = Integer.MAX_VALUE;
// if node is before we want to compare the end of the node's position to beginning of given position
if (position.getEndOffset() < startOffset)
{
diff = Math.abs(startOffset - position.getEndOffset());
}
else if (position.getStartOffset() > endOffset)
{
diff = Math.abs(position.getStartOffset() - endOffset);
}
else
{
// wraps the given position. So we compare starts to each other and ends to each other and take smallest
diff = Math.abs(position.getStartOffset() - startOffset);
diff = Math.min(diff, Math.abs(position.getEndOffset() - endOffset));
}
if (diff <= smallestDiff && acceptor.doesAccept(iVisited))
{
locatedNode = iVisited;
smallestDiff = diff;
}
// TODO If both start and end offset are past the start and end offsets we have, then we can probably shortcut
// FIXME Since we're going in order we can probably shortcut big time here once we see the diffs getting
// progressively bigger again
return super.handleNode(iVisited);
}
public Node getClosestNodeAtOffset(Node ast, int startOffset)
{
return getClosestNodeAtOffset(ast, startOffset, new INodeAcceptor()
{
public boolean doesAccept(Node node)
{
return !(node instanceof NewlineNode);
}
});
}
public Node getClosestNodeAtOffset(Node ast, int startOffset, INodeAcceptor nodeAcceptor)
{
return getClosestNode(ast, startOffset, startOffset, nodeAcceptor);
}
public Node getClosestNode(Node ast, ISourcePosition pos)
{
return getClosestNode(ast, pos, new INodeAcceptor()
{
public boolean doesAccept(Node node)
{
return !(node instanceof NewlineNode);
}
});
}
public Node getClosestNode(Node ast, ISourcePosition pos, INodeAcceptor nodeAcceptor)
{
return getClosestNode(ast, pos.getStartOffset(), pos.getEndOffset(), nodeAcceptor);
}
private Node getClosestNode(Node ast, int startOffset, int endOffset, INodeAcceptor nodeAcceptor)
{
this.startOffset = startOffset;
this.endOffset = endOffset;
this.acceptor = nodeAcceptor;
ast.accept(this);
this.acceptor = null;
return locatedNode;
}
}