Skip to content

Finding Method Invocations

Jon Schneider edited this page Nov 22, 2016 · 5 revisions

Rewrite provides an AspectJ-inspired shorthand for performing type-aware method searching in a body of code. The search utility is type-aware on the target type of the invocation (even if it is implicit because of a static import or has an unspecified target of this) as well as the types of any arguments present. It is not necessary to attempt to match on return type, since no two methods can co-exist that have the same target and arguments but vary only in return type.

To understand the search options at your disposal, let's start with a sample class:

package com.a;
import java.util.*;
public class A {
	public void foo() {}
	
	public void foo(int n) {}
	public void foo(int m, int n) {}
	public void foo(int[] ns) {}
	
	public void foo(Float n) {}
	public void foo(List<Integer> ns) {}
	
	public void varargs(int m, int... n) {}

	public static void foo() {}
}

Find methods based on target type

import com.a.A;
import static com.a.A.foo;
public class B {
	A a = new A();
	public void test() {
		new A().foo(); // 1
		a.foo();       // 2
		A.foo();       // 3
		foo();         // 4
	}
}

First, let's parse both classes and grab the Tr.CompilationUnit of class B.

Tr.CompilationUnit cu = parser.parse(Arrays.asList(a, b)).get(1);

We can retrieve a list of the Tr.MethodInvocation instances for all four statements with this:

cu.findMethods("com.a.A foo()");
cu.getClasses().get(0).findMethods("com.a.A foo()");
cu.getFirstClass().findMethods("com.a.A foo()"); // shorthand for getClasses().get(0)

Notice that we can search for the method invocations from either the Tr.CompilationUnit level (the whole source file) or the Tr.ClassDecl level (an individual type inside of that source file). Which one you use depends on whether you require information about the invocation's enclosing type for your work.

We can wildcard portions of the type:

cu.findMethods("**..A foo()");
cu.findMethods("com.*.A foo()");

As well as the name:

cu.findMethods("com.a.A f*()");
cu.findMethods("com.a.A *o()");
cu.findMethods("com.a.A *()");

Find methods based on arguments

import com.a.A;
import static com.a.A.foo;
public class B {
	A a = new A();
	public void test() {
		a.foo(1);                // 1
		a.foo(1, 2);             // 2
		a.foo(1.0f);             // 3
		a.foo(Arrays.asList(1)); // 4
		a.foo(new int[] { 1 });  // 5
		a.varargs(1, 2, 3, 4);   // 6
	}
}

Use primitive names, fully qualified type names, or simple class names for classes in java.lang.* to match on arguments.

Matching on primitives (statements 1 and 2):

cu.findMethods("com.a.A foo(int)");
cu.findMethods("com.a.A foo(int,int)");

Matching on java.lang.* classes (statement 3):

cu.findMethods("com.a.A foo(Float)");
cu.findMethods("com.a.A foo(java.lang.Float)"); // unnecessary

Note that in the following example of matching on fully qualified names, we don't define a syntax for specifying the generic parameter, because it is not possible for two methods to exist whose signatures only vary by generic parameters because of erasure (statement 4):

cu.findMethods("com.a.A foo(java.util.List)");

Use .. to match on any number of arguments, including zero arguments (matches statements 1-5):

cu.findMethods("com.a.A foo(..)");

.. can be used in conjunction with other arguments. To select only the two argument version of foo (matches statement 2):

cu.findMethods("com.a.A foo(int, ..)");
cu.findMethods("com.a.A foo(.., int)");

To match on array arguments (matches statement 5):

cu.findMethods("com.a.A foo(int[])");

Finally, ... with a type matches varargs (matches statement 6):

cu.findMethods("com.a.A varargs(int, int...)");

Clone this wiki locally