11

I have to deal with some old legacy code, that unfortunately had the coding style of declaring all the variables at the beginning of a method.

The code looks something like this as an example:

public String doSomething(boolean showStars) {
    StringBuilder builder = null;
    String result = "";
    String prefix = null;
    int i;

    try {
        prefix = "result: ";
        if (showStars) {
            builder = new StringBuilder();
            builder.append(prefix);
            for (i = 0; i < 10 ; i++) {
                builder.append('*');
            }
            result += builder.toString();
        } else {
            builder = new StringBuilder();
            builder.append(prefix);
            for (i = 0; i < 20; i++) {
                builder.append('.');
            }
            result += builder.toString();
        }

    } catch (SomeException ignored) {
    }
    return result;
}

Now I want to apply the following refactoring on my codebase (http://refactoring.com/catalog/reduceScopeOfVariable.html), so that my code will look like something like this, so I can refactor further with Extract Method.

public String doSomething(boolean showStars) {
    String result = "";

    try {
        String prefix = "result: ";
        if (showStars) {
            StringBuilder builder = new StringBuilder();
            builder.append(prefix);
            for (int i = 0; i < 10 ; i++) {
                builder.append('*');
            }
            result += builder.toString();
        } else {
            StringBuilder builder = new StringBuilder();
            builder.append(prefix);
            for (int i = 0; i < 20; i++) {
                builder.append('.');
            }
            result += builder.toString();
        }

    } catch (SomeException ignored) {
    }
    return result;
}

To my surprise I did not find any useful tool for helping me with this kind of refactoring, neither freely available nor any commercial product.

Does anybody know of any tool that supports this kind of refactoring? I looked into NetBeans/Eclipse/IntelliJ IDEA and none of that seem to support this kind of Refactoring out of the box. Because I have about 3000 legacy methods that unfortunately are structured like that, and some are up to 1500 LoC per method, so doing this by hand would be very very tedious.

  • The C# version of IntelliJ, Resharper, supports "Local variable can be declared in inner scope" fixes. I would assume that IntelliJ does too. – Bobson Jun 25 '14 at 17:01
  • A code quality tool might help you track these issues like SonarQube. I still don't think those changes can be fixed automatically by a software :(. Good Luck! – dimzak Jun 25 '14 at 18:18
  • 5
    While OP did not express his requirements as a bulleted list, what OP wants to do seems perfectly clear to me: move all data declarations to the latest and innermost scope that leaves the program function unchanged. Some can be moved. Some will move truly to an innermost scope. Some will move to a mid-level scope, because they are needed in that scope and in sub-scopes. Now all he needs is a tool that will do that. (There are such tools, call "program transformation systems"). I think the "hold" is not reasonable, including by people that don't think this can be done by software. – Ira Baxter Jun 26 '14 at 20:53
  • 2
    Use Eclipse. Move the cursor over a variable, press Ctrl + 1 and choose "Inline local variable" to reduce its scope. After that, highlighted code can be used to extract a method with Alt + Shift + M. – ATG Jun 26 '14 at 22:07
  • @ATG: Could you please post this as an answer? Thanks! – Nicolas Raoul Jun 27 '14 at 6:06
2

You are not likely to find such a tool out of the box.

OP needs a tool that can parse Java, build ASTs, determine scopes, and accurately identify every declaration of an identifier and where that declaration is used in the code.

With that information, one can build a tool to do what he wants. Sketch of a solution:

 For all scopes, outer to inner:
    For each identifier I in a scope S
        If all uses/assignments of I are in a nested scope T,
            Move I to the nested scope T.
        else if I is first used/assigned in statement F in this scope S,
             and last used in statement L in this scope S,
           Introduce a scope X starting at statement F
             and going to/including statement L;
             insert I into this scope.

The details of this are complicated by the variety of scopes in Java (file, class, enum, field, constructor/destructor, method, parameters, locals, anonymous classes, ...) and the type of identifier. Having an AST with a symbol table should make it technically straightforward but practically a bit messy to implement this by inspecting the symbol table and modifying the AST.

Even with good tool foundations, expect this to be a significant amount of work. It might or might not be worth it for 3000 methods; at 4.5 million lines (3000 methods times 1500 lines) this is surely far better than any manual process. At some scale point, this will be a win. (I suspect your management won't really put up with doing this, because you are trying to "fix something that ain't broke").

You might try this with the the Eclipse Java parser. I'm not sure how well it would hold up under the massive set of tree changes this process is likely to cause. In particular, I'm not sure it enables you to inspect the changed AST, which is necessary to carry out the successive insertion of nested scopes and identifiers.

Our DMS Software Reengineering Toolkit has such a Java parser, symbol table, identifier lists, and tools for navigating the ASTs, pattern matching and source-to-source transformations with which to match patterns and make code changes, and metaprogramming glue to tie all this together. We haven't used it to do specifically this, but we have used DMS to do similarly complicated symbol scope shuffling tasks on C++ code which is even more complex. (See Case Study: Re-engineering C++ Component Models Via Automatic Program Transformation, Information & Software Technology 49(3):275-291 2007 for C++ version details). I'm the author/vendor, so you can take this with a grain of salt.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.