Sergej Werfel
Sergej Werfel

Reputation: 1365

How to extract a method in eclipse that converts a field or constant value to a parameter

Situation: I'm working on legacy code and trying to improve readability. The following example should visualize the intent:

private static final String CONSTANT_1 = "anyValue";
private static final String CONSTANT_2 = "anyValue";
private static final String CONSTANT_3 = "anyValue";
private static final String CONSTANT_4 = "anyValue";
private static final String CONSTANT_5 = "anyValue";

private final SomeType someField = new SomeType();

private void contentOfSomeMethods(){
    someMethod(someField, CONSTANT_1, true);
    someMethod(someField, CONSTANT_2, true);
    someMethod(someField, CONSTANT_3, true);
    someMethod(someField, CONSTANT_4, false);
    someMethod(someField, CONSTANT_5, false);
}

private void someMethod(SomeType type, String value, boolean someFlag) { }

Imagine, there are about 50 calls of someMethod using about 50 constants. I want to do safe automatical refactorings on that code so that the contentOfSomeMethods method changes to

private void contentOfSomeMethods(){
    doItWith(CONSTANT_1);
    doItWith(CONSTANT_2);
    doItWith(CONSTANT_3);
    doItNotWith(CONSTANT_4);
    doItNotWith(CONSTANT_5);
}

and two additional methods are generated:

private void doItWith(String value) {
    someMethod(someField, value, true);
}

private void doItNotWith(String value) {
    someMethod(someField, value, false);
}

The naive way is to extract all constants in contentOfSomeMethods inside local variables and use then the extract method refactoring to create the desired methods. And afterwards to inline back the local variables. But this solution doesn't scale up.

Another way is to use search and replace with regular expressions, but this is not a safe refactoring, so I could break the code without noticing it.

Do you have any better suggestions? Do you know some plugins for Eclipse that allow that?

Upvotes: 3

Views: 843

Answers (3)

JnRouvignac
JnRouvignac

Reputation: 787

Alas I know nothing in Eclipse that allows to do this today. This is something I would like to achieve one day in AutoRefactor: https://github.com/JnRouvignac/AutoRefactor/issues/8

However the road to get there is quite long.

The only ways I know today are to extract local variables then extract method (as you suggested) or use regexes (as somebody else suggested).

Upvotes: 0

Anonymous Coward
Anonymous Coward

Reputation: 3200

Do it with a regular expression and verify it.

I'm assuming that each call to someMethod spans only one line. If not this method is still useful but slower.

Copy the original file.

Use ctrl+alt+h to show the Callers of someMethod and get a count of them.

Do regex search and replaces restricted to the proper area :
Find : someMethod(someField,([ ]*CONSTANT_[0-9]+)[ ]*,[ ]*true[ ]*)[ ]*;
Replace : doItWith("$1");
Find : someMethod(someField,([ ]*CONSTANT_[0-9]+)[ ]*,[ ]*false[ ]*)[ ]*;
Replace : doItNotWith("$1");

Make a diff of the original file and the new file showing only the lines of the original file which have changed.

diff --changed-group-format='%<' --unchanged-group-format='' original.java refactored.java | wc

You should get the same number of lines as you got in the callers of someMethod.

If the calls to someMethod are multiline, or if you want greater verification, just drop | wc to see the lines which were modified in the original file to ensure that only the correct lines have been modified.

Upvotes: 0

Tunaki
Tunaki

Reputation: 137299

I don't know of any utility that would do this directly.

I think using a regular expression is the only to go. First, you will need to create the two target methods doItWith and doItNotWith. Then, you can highlight the contents of the method contentOfSomeMethods, hit Ctrl+F, and use the following regular expressions:

Find: someMethod\(someField, (\w*), true\);
Replace with: doItWith(\1);

and then

Find: someMethod\(someField, (\w*), false\);
Replace with: doItNotWith(\1);

Be sure to check "Regular Expressions" and "Selected lines". Here's a picture of it:

enter image description here

The regular expressions match the constant that is used inside the function call with (\w*) and then it is used during the replacement with \1. Using this regular expression only on the selected lines minimizes the chance of breaking unrelated code.

Upvotes: 1

Related Questions