Skip to content

Changing Method Invocations

Jon Schneider edited this page Nov 28, 2016 · 2 revisions

Change method name

Starting with a couple sample classes:

public interface A {
	void foo();
	void bar();
}
public class B {
	public void b(A a) {
		a.foo();
	}
}

Changing a method name looks like this:

Tr.CompilationUnit cu = parser.parse(asList(a, b)).get(1);
String diff = cu.refactor(tx -> {
	cu.findMethodCalls("A foo()").forEach(m ->
		tx.changeMethodName(m, "bar")
	);
}).diff();

Equivalently, and more succinctly:

Tr.CompilationUnit cu = parser.parse(asList(a, b)).get(1);
String diff = cu.refactor()
	.changeMethodName(cu.findMethodCalls("A foo()"), "bar")
	.diff();

The resulting diff is:

diff --git a/B.java b/B.java
index ea7ba37..a9e0b0b 100644
--- a/B.java
+++ b/B.java
@@ -1,5 +1,5 @@
 public class B {
 	public void b(A a) {
-		a.foo();
+		a.bar();
 	}
 }
\ No newline at end of file

Change method target

The method target is the variable (or type in the case of static method invocations) that a method is executed against. Rewrite can change variable targets to other variable targets, variable targets to statics, etc.

public interface A1 { void foo(); }

public class A2 { 
	public void foo() {}
	public static void staticFoo() {}
}
public class B {
	A1 a1;
	A2 a2;
	
	public void b() {
		a1.foo();
	}
}

Below is an example of changing the target of a method invocation on A1 foo() to a field on the class, if such a field exists. This example is contrived, of course, as such a rule in practice would generally ensure that a field exists by creating it if it doesn't exist.

Tr.CompilationUnit cu = parser.parse(asList(a, b)).get(1);
String diff = cu.refactor()
	.changeMethodTarget(cu.findMethodCalls("A1 foo()"), "a2")
	.diff();

We can be more general about this rule by locating a field of A2 to use, regardless of its name:

Tr.CompilationUnit cu = parser.parse(asList(a, b)).get(1);
String diff = cu.refactor(tx -> {
  cu.getClasses().forEach(clazz -> {
    clazz.findFields(A2.class).stream()
      .findAny()
      .ifPresent(field -> {
        Tr.VariableDecls.NamedVar a2 = field.getVars().get(0);
        tx.changeMethodTarget(
	        cu.findMethodCalls("A1 foo()"), a2);
      });
  });
}).diff();

Changing to a static invocation on A2 is simpler:

Tr.CompilationUnit cu = parser.parse(asList(a, b)).get(1);
String diff = cu.refactor(tx -> {
  cu.findMethodCalls("A1 foo()").forEach(m -> tx.changeTarget(m, A2.class));
}).diff();

Insert, delete, and reorder arguments

public interface A { 
	void foo(int m);
	void foo(int m, int n);
}
public class B {
	public void b(A a) {
		a.foo(1);     // 1
		a.foo(1, 2);  // 2
	}
}

The following insert argument refactor changes a.foo(1) to a.foo(2, 1). Notice that you insert an argument at any position. Because we have specified A foo(int) in findMethodCalls, the change is only applied to statement 1.

Tr.CompilationUnit cu = parser.parse(asList(a, b)).get(1);
String diff = cu.refactor()
	.insertArgument(cu.findMethodCalls("A foo(int)"), 0, 2))
	.diff();

To reverse the operation by deleting the first argument, changing statement 2 from a.foo(1, 2) to a.foo(2):

Tr.CompilationUnit cu = parser.parse(asList(a, b)).get(1);
String diff = cu.refactor()
	.deleteArgument(cu.findMethodCalls("A foo(int, int)"), 0))
	.diff();

Lastly, we may reorder the arguments:

Tr.CompilationUnit cu = parser.parse(asList(a, b)).get(1);
List<Tr.MethodInvocation> foos = cu.findMethodCalls("A foo(int, int)");
String diff = cu.refactor()
	.reorderArguments(foos, "n", "m")
		.setOriginalParamNames("m", "n")
	.diff();

The call to setOriginalParamNames is optional if the source code for foo is available on the classpath or as one of the source files that is being parsed.

Clone this wiki locally