Wednesday, October 24, 2007

Beyond simple refactorings and IDE portability of projects

I recently did a short presentation at work on some of the java pattern based search and replace features that can be found in IntelliJ (SSR) and Netbeans (jackpot). The nice thing about these two features is that they let you specify a custom refactoring without worrying about whitespace or lines and you can match against more than just the raw text (i.e. - match 'where the base class is xxxx' or 'where the expression evaluates to type String' or even where this statement was preceeded by a statement that created a variable.......etc..').

In relatively short order, I was able to whip up some expressions to do custom refactorings across an entire codebase. Here are some examples:
  • Replace two line declare and return pattern with one-line return
    • Netbeans jackpot: { $stmts$; $type $value = $expr; return $value; } => { $stmts$; return $expr; }
  • Move common trailing statements in if/else blocks outside the if
    • Intellij SSR:
      • Search pattern:
        if($boolean_expression$) {
        $trueStatements$;
        $commonstmts$;
        }
        else {
        $falseStatements$;
        $commonstmts$;
        }
      • Replace pattern:
        if($boolean_expression$) {
        $trueStatements$;
        }
        else {
        $falseStatements$
        }
        $commonstmts$;

  • Improve the expressiveness of code by quickly introducing helper's for certain patterns
    • Netbeans jackpot: {$type $var = $expr; $var.add($valueExpr); $remainingStmts$;} => {$type $var = Arrays.asList($valueExpr); $remainingStmts$;};

The possibilities of what you can search and replace with these pattern/ast based features is really impressive. As I showed some examples, my colleagues were continuously coming up with additional ideas for things we could do with this.

The Intellij SSR is a bit more polished but the jackpot rules language seems like a more flexible idea. Jackpot also lets you access a Java API to do transformations on code, though I think I hold off on learning that until I need to do a refactoring against a code base large enough to justify that time investment.

Intellij and Netbeans are using their AST (Abstract Syntax Tree) to provide this useful
functionality. I can only hope that Eclipse will catch up soon. In the meantime, one of the side-effects of the exercise was that it forced me to exercise some of the new maven integration capabilities of the latest IDE's. I've now got a fairly solid way to open a project in any of the 3 major IDE's (Eclipse, Netbeans, Intellij) without doing any special configuration. Here is the basic idea:
  1. Use a maven pom.xml to define project dependencies.
  2. To Use Eclipse: use the mvn eclipse:eclipse plugin to generate the eclipse project files.
  3. To Use Netbeans: Point Netbeans at the maven pom.
  4. To Use Intellij: Point Intellij at the maven pom.
It's nice to finally have a simple way to open any project in any of the leading IDE's without any manual configuration.

2 comments:

Jason said...

Wow that's awesome ... thinking about all the great refactoring tools you could come up with and I would eve find the ones you made as examples useful.

We're doing the mvn:rad (tweak of mvn:eclipse) FINALLY! It's great man... not only is it great on small applications for generating the .files in a way that will create IDE specific projects, but in large applications with potentially lots of project dependencies you can let mvn pull those as jar's into your local repo and only have .java files of the stuff you actually want to work with.

Matt said...

Good point.. I've gotten so used to having maven in the equation that I think I've forgotten what it's like to have all those jars embedded directly in the project directory structure. While it's not perfect, maven definately enables some productive tweaks to a development project.