package org.rubypeople.rdt.internal.ti;
import java.util.LinkedList;
import java.util.List;
import org.jruby.ast.ArgumentNode;
import org.jruby.ast.BlockNode;
import org.jruby.ast.ClassNode;
import org.jruby.ast.Colon2Node;
import org.jruby.ast.ConstNode;
import org.jruby.ast.DVarNode;
import org.jruby.ast.DefnNode;
import org.jruby.ast.DefsNode;
import org.jruby.ast.GlobalAsgnNode;
import org.jruby.ast.GlobalVarNode;
import org.jruby.ast.InstAsgnNode;
import org.jruby.ast.InstVarNode;
import org.jruby.ast.LocalAsgnNode;
import org.jruby.ast.LocalVarNode;
import org.jruby.ast.Node;
import org.jruby.ast.types.INameNode;
import org.jruby.common.NullWarnings;
import org.jruby.lexer.yacc.IDESourcePosition;
import org.jruby.lexer.yacc.ISourcePosition;
import org.rubypeople.rdt.internal.core.parser.RdtWarnings;
import org.rubypeople.rdt.internal.core.parser.RubyParser;
import org.rubypeople.rdt.internal.ti.util.FirstPrecursorNodeLocator;
import org.rubypeople.rdt.internal.ti.util.INodeAcceptor;
import org.rubypeople.rdt.internal.ti.util.OffsetNodeLocator;
import org.rubypeople.rdt.internal.ti.util.ScopedNodeLocator;
public class DefaultReferenceFinder implements IReferenceFinder {
private static final boolean VERBOSE = false;
public List<ISourcePosition> findReferences(String source, int offset) {
// References to return
List<ISourcePosition> references = new LinkedList<ISourcePosition>();
// Parse the source
RubyParser parser = new RubyParser(new NullWarnings());
Node root = parser.parse(source).getAST();
// Find origiating node
Node orig = OffsetNodeLocator.Instance().getNodeAtOffset(root, offset);
log("Origin: " + orig.getClass().getName());
if ( isLocalVarRef(orig) ) {
pushLocalVarRefs( root, orig, references );
}
if ( isInstanceVarRef(orig) ) {
pushInstVarRefs( root, orig, references );
}
if ( isGlobalVarRef(orig) ) {
pushGlobalVarRefs( root, orig, references );
}
// if ( isMethodRefNode(orig)) {
// pushMethodRefs( root, orig, references );
// }
if ( orig instanceof ConstNode )
{
pushConstRefs( root, orig, references );
}
return references;
}
private ISourcePosition getPositionOfName(Node node, Node scope)
{
ISourcePosition pos = node.getPosition();
//todo: refactor the getting-of-name
String name = null;
if ( isLocalVarRef(node) ) { name = getLocalVarRefName(node, scope); }
if ( isInstanceVarRef(node) ) { name = getInstVarRefName(node, scope); }
if ( isGlobalVarRef(node) ) { name = getGlobalVarRefName(node); }
if ( node instanceof ConstNode ) { name = ((ConstNode)node).getName(); }
if ( name == null )
{
System.err.println("Couldn't get the name for: " + node.toString() + " in " + scope.toString() );
name = "";
}
return new IDESourcePosition(pos.getFile(), pos.getStartLine(), pos.getEndLine(), pos.getStartOffset(), pos.getStartOffset() + name.length() );
}
/**
* Returns the name of a local var ref (LocalAsgnNode, ArgumentNode, LocalVarNode)
* @param node Node to get the name of
* @param scope Enclosing scope (to scrape args, etc.)
* @return
*/
private String getLocalVarRefName( Node node, Node scope ) {
if (node instanceof INameNode) {
return ((INameNode)node).getName();
}
return null;
}
private String getClassNodeName( ClassNode classNode ) {
if (classNode.getCPath() instanceof Colon2Node) {
Colon2Node c2node = (Colon2Node) classNode.getCPath();
return c2node.getName();
}
System.err.println("ClassNode.getCPath() returned other than Colon2Node: " + classNode.toString() );
return null;
}
private String getInstVarRefName( Node node, Node scope ) {
if ( node instanceof InstAsgnNode ) {
return ((InstAsgnNode)node).getName();
}
if ( node instanceof ArgumentNode ) {
return ((ArgumentNode)node).getName();
}
if ( node instanceof InstVarNode ) {
return ((InstVarNode)node).getName();
}
if ( node instanceof DVarNode ) {
return ((DVarNode)node).getName();
}
// System.err.println("Encountered unhandled node type for getInstVarRefName: " + node.toString() + " in " + scope.toString());
return null;
}
private String getGlobalVarRefName( Node node ) {
if ( node instanceof GlobalVarNode )
{
return ((GlobalVarNode)node).getName();
}
if ( node instanceof GlobalAsgnNode ) {
return ((GlobalAsgnNode)node).getName();
}
return null;
}
private boolean isLocalVarRef( Node node ) {
return ( ( node instanceof LocalAsgnNode ) || ( node instanceof ArgumentNode ) || ( node instanceof LocalVarNode ) );
}
private boolean isInstanceVarRef( Node node ) {
return ( ( node instanceof InstAsgnNode ) || ( node instanceof InstVarNode ) ) ;
}
private boolean isGlobalVarRef( Node node ) {
return ( ( node instanceof GlobalAsgnNode ) || ( node instanceof GlobalVarNode ) );
}
private void pushLocalVarRefs( Node root, Node orig, List<ISourcePosition> references ) {
log("Finding references for a local variable " + orig.toString());
// Find the search space
Node searchSpace = FirstPrecursorNodeLocator.Instance().findFirstPrecursor(root, orig.getPosition().getStartOffset(), new INodeAcceptor() {
public boolean doesAccept(Node node) {
return ( ( node instanceof DefnNode ) || ( node instanceof DefsNode ) /*TODO: Block Body? */ );
}
});
// If no enclosing node found, search the entire space
if ( searchSpace == null ) {
searchSpace = root;
}
// Finalize searchSpace because Java's scoping rules are the awesome
final Node finalSearchSpace = searchSpace;
// Get name of local variable reference
final String origName = getLocalVarRefName(orig,searchSpace);
// Find all pertinent nodes
List<Node> searchResults = ScopedNodeLocator.Instance().findNodesInScope(searchSpace, new INodeAcceptor() {
public boolean doesAccept(Node node) {
String name = getLocalVarRefName(node, finalSearchSpace);
return ( name != null && name.equals(origName));
}
});
// Scrape position from pertinent nodes
for ( Node searchResult : searchResults ) {
references.add(getPositionOfName(searchResult, searchSpace));
}
}
private void log(String string) {
if (VERBOSE) System.out.println(string);
}
private void pushInstVarRefs( Node root, Node orig, List<ISourcePosition> references ) {
log("Finding references for an instance variable " + orig.toString() );
Node searchSpace;
// Find the name of the enclosing class
ClassNode enclosingClass = (ClassNode)FirstPrecursorNodeLocator.Instance().findFirstPrecursor(root, orig.getPosition().getStartOffset(), new INodeAcceptor() {
public boolean doesAccept(Node node) {
return ( node instanceof ClassNode );
}
});
// If no enclosing class is identified, search root.
if ( enclosingClass == null ) {
searchSpace = root;
}
// Find the search space - all ClassNodes for that name within root scope
else {
final String className = getClassNodeName(enclosingClass);
List<Node> classNodes = ScopedNodeLocator.Instance().findNodesInScope(root, new INodeAcceptor() {
public boolean doesAccept(Node node) {
if ( node instanceof ClassNode )
{
return getClassNodeName((ClassNode)node).equals(className);
}
return false;
}
});
BlockNode blockNode = new BlockNode(new IDESourcePosition());
for ( Node classNode : classNodes )
{
blockNode.add( classNode );
}
searchSpace = blockNode;
}
// Finalize searchSpace because Java's scoping rules are the awesome
final Node finalSearchSpace = searchSpace;
// Get name of local variable reference
final String origName = getInstVarRefName(orig,searchSpace);
// Find all pertinent nodes
List<Node> searchResults = ScopedNodeLocator.Instance().findNodesInScope(searchSpace, new INodeAcceptor() {
public boolean doesAccept(Node node) {
if ( isInstanceVarRef(node) )
{
String name = getInstVarRefName(node, finalSearchSpace);
return ( name != null && name.equals(origName));
}
return false;
}
});
// Scrape position from pertinent nodes
for ( Node searchResult : searchResults ) {
references.add(getPositionOfName(searchResult, searchSpace));
}
}
private void pushGlobalVarRefs( Node root, Node orig, List<ISourcePosition> references ) {
final Node searchSpace = root;
final String origName = getGlobalVarRefName(orig);
// Find all pertinent nodes
List<Node> searchResults = ScopedNodeLocator.Instance().findNodesInScope(searchSpace, new INodeAcceptor() {
public boolean doesAccept(Node node) {
return isGlobalVarRef(node) && getGlobalVarRefName(node).equals(origName);
}
});
// Scrape position from pertinent nodes
for ( Node searchResult : searchResults ) {
references.add(getPositionOfName(searchResult, searchSpace));
}
}
//todo: complete
// private void pushMethodRefs( Node root, Node orig, List<ISourcePosition> references) {
//
// // DefnNode DefsNode CallNode VCallNode
//
// System.out.println("Finding references for method reference node " + orig.toString() );
//
// final Node searchSpace = root;
// String origName = getMethodRefName(orig);
//
// // If orig is a method definition, find all references to that selector for the orig's enclosing type
// if ( orig instanceof DefnNode || orig instanceof DefsNode )
// {
// ((DefnNode)orig).g
// }
//
// Node receiver = getMethodReceiver(orig);
// }
private void pushConstRefs( Node root, Node orig, List<ISourcePosition> references) {
if ( !( orig instanceof ConstNode) )
{
return;
}
final String matchName = ((ConstNode)orig).getName();
List <Node> searchResults = ScopedNodeLocator.Instance().findNodesInScope(root, new INodeAcceptor() {
public boolean doesAccept(Node node) {
if ( node instanceof ConstNode )
{
return ((ConstNode)node).getName().equals(matchName);
}
return false;
}
});
for ( Node searchResult : searchResults ) {
references.add(getPositionOfName(searchResult, root ) );
}
}
}