Introduction to Rescripter

Are you a Java developer? Do you use Eclipse? Ever find yourself making the same mindless change over and over again, wishing you could automate it? ME TOO. So I wrote an Eclipse plugin that let’s you write scripts that refactor your source code.

It does what now?

Some changes are easy to describe, but laborious to do. Perhaps some examples would help:

  • Renaming a method and renaming every call to that method (ok, Eclipse has built-in support for this)
  • Replacing a call to a constructor with a static factory method (ok, IntelliJ has built-in support for this, but Eclipse doesn’t)
  • Moving a method and updating callers to get a reference to the target class
  • Replacing use of one library with a similar one with a different API

Basically anything that involves the same, small change made multiple times across your source code.

How does it work?

After installing the Rescripter Eclipse plugin write some JavaScript to describe your change; when you run this, the script can make changes to your source code using Eclipse’s built-in refactoring and source code modification support.

Why Javascript? Because its a well understood language which, via Rhino, integrates excellently with Java.

An example would help about now

Ok, so imagine I have my own number class.

public class MyNumber {
	private int value;

	public MyNumber(String value) {
		this.value = Integer.parseInt(value);
	}

	public static MyNumber valueOf(String value) {
		return valueOf(value);
	}
}

My source code becomes littered with calls to the constructor:

MyNumber myNumber = new MyNumber("42");

As part of some refactoring, if I want to change how these numbers are created or implemented I may want to replace all calls to the constructor with calls to the static factory method. For example, this would let me introduce a class hierarchy that the factory method knows about, or any number of other changes that cannot be done if client code calls the constructor directly.

Unfortunately, Eclipse doesn’t provide a way to do this refactoring – however, Rescripter let’s you do it. We can describe what we want to do quite simply:

Find all references to the constructor, then for each reference:

  • Add a static import to MyNumber.valueOf
  • Replace the method call (new MyNumber) with valueOf
So what does this look like?
var matches = Search.forReferencesToMethod("com.example.MyNumber(String)");

var edit = new MultiSourceChange();
foreach(filter(matches, Search.onlySourceMatches),
    function(match) {
        edit.changeFile(match.getElement().getCompilationUnit())
            .addImport("static com.example.MyNumber.valueOf")
            .addEdit(Refactor.createReplaceMethodCallEdit(
                 match.getElement().getCompilationUnit(),
                 match.getOffset(), match.getLength(),
                 "valueOf"));
    });
edit.apply();

The first line finds references to the MyNumber(String) constructor.

Line 4 introduces a loop over each reference, filtering to only matches in source files (we can’t change .class files, only .java files).

Line 7 adds our static import.

Lines 8-11 replace the constructor call with the text “valueOf”

Our original call now looks like:

MyNumber myNumber = valueOf("42");

Now having replaced all uses of the constructor I can make it private and make whatever changes I need to the static factory method.

That’s great, but can it…?

Yes, probably. But maybe not yet. Rescripter is very basic at the minute, although you can still run some pretty powerful scripts making broad changes to your source code. If you’re having problems, have a feature suggestion or bug report – either post a comment or drop me a mail.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s