IntelliJ Structural Search doesn't replace all method calls in a chain/fluent interface

709 views Asked by At

Consider the following complete example of a Builder style class.

package com.derp;

class MyBuilder {

    public MyBuilder set(String key, Object val) {
        return this;
    }

    public MyBuilder setFoo(Object val) {
        return this;
    }

    public MyBuilder setBar(Object val) {
        return this;
    }

    public MyBuilder setBaz(Object val) {
        return this;
    }
}

public class Main {

    public static void main(String[] args) {
        // ** Example 1
        MyBuilder a = new MyBuilder();
        a.set("foo", 1);
        a.set("bar", 2);
        a.set("baz", 3);

        // ** Example 2
        MyBuilder b = new MyBuilder()
                .set("foo", 1)
                .set("bar", 2)
                .set("baz", 3);
    }
}

Using IntelliJ's Structural Search I'd like to refactor method invocations of the form set("foo", value) to setFoo(value) everywhere it's being invoked on a certain class/interface, for all String literals (not just "foo").

This only somewhat works with a chain of method calls (AKA a "fluent" interface), in which the set() method in question returns this.

In the main method above, what I've labeled as Example 1 gets refactored properly, but Example 2 ends up like this, which is not helpful:

MyBuilder b = new MyBuilder()
    .set("foo", 1)
    .set("bar", 2).setBaz(3);

I need set("foo", 1) and set("bar", 2) to be updated as well here.


Can IntelliJ handle this use case?

The following are the details of the options I have passed into Structural Search and Replace. I've tried tweaking the options around "occurrences count" but sadly haven't come up with any option that worked. Here's the template details.

Search template: $Builder$.set($Key$, $Val$)

Replacement template: $Builder$.set$Name$($Val$)

Variables:

$Builder$ Expression type (regexp): com\.derp\.MyBuilder

$Key$ Expression type (regexp): java\.lang\.String

$Name$ Script text: Key.getText().replace("\"", "").capitalize()

1

There are 1 answers

1
Bas Leijdekkers On

This is a limitation of Structural Search & Replace, and is not possible at the moment. What you can do is invoke Replace multiple times. Each time one instance of the chain will be found. You will have to keep doing this until no more instances are found.

Or slightly more convenient, you can create a Structural Search Inspection from your templates. This will find all instances. But you will have to invoke the quick fix on each instance of the chain separately or it won't work.

You may want to submit a bug report for this issue.