package org.rubypeople.rdt.internal.ti.util;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.jruby.ast.CallNode;
import org.jruby.ast.ClassNode;
import org.jruby.ast.FCallNode;
import org.jruby.ast.ModuleNode;
import org.jruby.ast.Node;
import org.rubypeople.rdt.internal.ti.ITypeGuess;
import org.rubypeople.rdt.internal.ti.ITypeInferrer;
import org.rubypeople.rdt.internal.ti.TypeInferenceHelper;
/**
* Visitor to find all method invocations for the specified type and method names
* @author Jason Morrison
*/
public class MethodInvocationLocator extends NodeLocator {
//Singleton pattern
private MethodInvocationLocator() {}
private static MethodInvocationLocator staticInstance = new MethodInvocationLocator();
public static MethodInvocationLocator Instance()
{
return staticInstance;
}
/** Inference helper */
private TypeInferenceHelper helper = TypeInferenceHelper.Instance();
/** Type of receiver to look for */
private String typeName;
/** Name of method to search for invocations of */
private String methodName;
/** Running total of results */
private List<Node> locatedNodes;
/** Type inferrer to use when resolving receiver-types */
private ITypeInferrer inferrer;
/** Source to search within */
private String source;
/**
* Finds all method invocation node within rootNode whose receiver is of type typeName and method is named methodName
* @param rootNode Node to search within
* @param source Source to search within
* @param typeName Name of type of method-send-expr receiver
* @param methodName Name of method to find
* @param inferrer Inferrer to use for resolving receiver-types
* @return
*/
public List<Node> findMethodInvocations( Node rootNode, String typeName, String methodName, ITypeInferrer inferrer ) {
if ( rootNode == null ) { return null; }
this.locatedNodes = new LinkedList<Node>();
this.typeNameStack = new LinkedList<String>();
this.typeName = typeName;
this.methodName = methodName;
this.inferrer = inferrer;
typeNameStack.add("Kernel");
// Traverse to find all matches
rootNode.accept(this);
// Return the matches
return locatedNodes;
}
public Object handleNode(Node iVisited) {
// Check for invocations on self
if ( iVisited instanceof FCallNode ) {
if ( ((FCallNode)iVisited).getName().equals(methodName)) {
if ( peekType().equals(typeName)) {
locatedNodes.add(iVisited);
}
}
}
// Look for CallNodes where receiver matches typeName and methodName matches method invoked
if ( iVisited instanceof CallNode ) {
if ( helper.getCallNodeMethodName(iVisited).equals(methodName)) {
// TI the receiver
Node receiverNode = ((CallNode)iVisited).getReceiverNode();
Collection<ITypeGuess> receiverTypeInferences = inferrer.infer( source, receiverNode.getPosition().getStartOffset());
// If the receiver matches desired typeName, add a match!
for ( ITypeGuess inference : receiverTypeInferences ) {
if ( inference.getType().equals( typeName ) ) {
locatedNodes.add( iVisited );
break;
}
}
}
}
// if ( iVisited instanceof VCallNode ) {
//TODO: VCallNode does not have getReceiverNode().
// We don't particularly care for the purpose of finding send-exprs that flow params into args of method-defns, since VCallNodes
// don't send w/ args. But, to make this visitor general-purpose, it would have to support VCallNodes. Consider just renaming the
// }
return super.handleNode(iVisited);
}
public Object visitClassNode(ClassNode iVisited) {
pushType( helper.getTypeNodeName( iVisited ) );
super.visitClassNode( iVisited );
popType();
return null;
}
public Object visitModuleNode(ModuleNode iVisited) {
pushType( helper.getTypeNodeName( iVisited ) );
super.visitModuleNode( iVisited );
popType();
return null;
}
}