0

I am trying to refactor the code for one of our projects which has extensively used double brace initialisation. I have a liberty(mutability is not a concern) of making use of com.google.common.collect.ImmutableMap to initialse these Map. Since there are more than 100+ usage, I wanted to do away with manual edits here.

Is there a way to effectively replace a complete code block in IntelliJ such as the following:

Map<*, *> mapOfAnyTypeKeyToAnyTypeValue = new HashMap<*, *>() {{
    put(key1, value1);
    put(key2, value2);
}};

to something of the likes

Map<*, *> mapOfAnyTypeKeyToAnyTypeValue = ImmutableMap.of(
    key1, value1, 
    key2, value2);

Ofcourse the size of entries could vary and a regular expression here might be needed. But I am not very friendly with the regex based replaces in IntelliJ.

If it could be of any help, I am using IntelliJ IDEA 2020.2.3 Preview (Community Edition).

Naman
  • 27,789
  • 26
  • 218
  • 353
  • You could use regex and replace based on groups positions. But it's not something that I can do on prima vista as its not as trivial as it sounds. Instead what I would do is Search & Replace all occurances of `new HashMap<\w+,\s*\w+>\s*\{\{` with `ImmutableMap.of(` and manually check each occurrence of a search results for both `ImmutableMap.of(` & `new HashMap`. One should also think of what other map implementations could be used. Also one should consider cases in which the diamond operator has been used. – dbl Sep 24 '20 at 07:57
  • You can check [Find and replace a captured group](https://www.jetbrains.com/help/idea/tutorial-finding-and-replacing-text-using-regular-expressions.html#capture_groups) guide on IntelliJ tutorials. – dbl Sep 24 '20 at 08:10
  • yes, you can inspect code, like right click -> analyze -> inspect code and replace all HshMap with ImmutableMap, i do not remember exactly where it is located after this steps but it is there, replace all occurrences or something like that – George Weekson Sep 24 '20 at 08:31
  • @GeorgeWeekson Thanks. Replace all occurrence is something I am aware of but what I am looking forward is to replace the pattern specifically. e.g. I would not want to replace the occurrence of `new HashMap` in `Map<*, *> mapOfAnyTypeKeyToAnyTypeValue = new HashMap<>();` – Naman Sep 24 '20 at 08:55
  • 1
    @dbl Thank you for the link. The captured group link might be useful. Would have to try it out to confirm it can match blocks of code as well. Replace all occurrence would need a regex to match all such types, hence not really straight forward. The regex you've shared doesn't really work for e.g. – Naman Sep 24 '20 at 08:58
  • @Naman the regex is actually almost there, as I've missed the `()`. The working version of it is: `new HashMap<\w+,\s*\w+>\(\)\s*\{\{` – dbl Sep 24 '20 at 10:24
  • If you are using java 9 or later, consider using [Map:of](https://docs.oracle.com/javase/9/docs/api/java/util/Map.html#of-K-V-K-V-K-V-K-V-K-V-K-V-K-V-K-V-K-V-K-V-) rather than 3rd party libraries. Also as you said, mutability is not an issue so don't instantiate such maps. – dbl Sep 24 '20 at 10:31
  • @dbl the regex search would work, but how would you suggest to replace the `put(key1, value1);...` in the same execution? Other than that, I am aware of Java-9 utilities(just not the case here), thank you. – Naman Sep 24 '20 at 11:14
  • @Naman whoops, you are right, I've totally missed that part. It is achievable with backreferences but it seems to be really dangerous operation. Yet I'm going to post an answer with an example as it is too big for a comment. – dbl Sep 24 '20 at 11:46

1 Answers1

1

Unfortunatelly IntelliJ regex processor does not allow multiple backreferences per matched group and will capture only the last value for that particular group, therefore you could achieve your goal by processing each file part by part. I find this approach time consuming yet safe enough as you can verify replacements at place.

Here is a demo of how could one achieve the goal:

This will be the sample class that I'm using: enter image description here

  1. Search & Replace all occurrances for regex pattern (project wise - enforces build failures and will highlight all the places that has to be fixed. A good idea is to check all before continuing forward): enter image description here

Steps from 2 and 3 should be repeated per declaration block individually!

  1. Select the block that contains the put declarations and Search & Replace for regex pattern(selection wise - the screenshot that I added does this class wise but it isn't correct!) enter image description here

  2. Select the closing curly brackets and manually fix them. enter image description here

Here is the code used for the demo and the resulting code:

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

public class Main
{
    public static void main(String[] args)
    {
        Map<Object, Object> aMap = new HashMap<Object, Object>() {{
            put("1", 'a');
            put(2, "b");
            put(BigInteger.valueOf(3L), new char[3]);
        }};
    }
}

transformed to:

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

public class Main
{
    public static void main(String[] args)
    {
        Map<Object, Object> aMap = ImmutableMap.of(
            "1", 'a', 2, "b", BigInteger.valueOf(3L), new char[3]);
    }
}

Regex used for step 1: new HashMap<\w+,\s*\w+>\(\)\s*\{\{ -> ImmutableMap.of(
Regex used for step 2: put\((.*)\,\s*(.*)\)\;\s+ -> $1, $2,

You will have to manually fix class imports as well.

P.S. One could write a script to do this, but generally it's not a good practice to process your codebase programatically.

dbl
  • 1,109
  • 9
  • 17
  • Another approach would be to create a custom [quick fix suggestion](https://www.jetbrains.com/help/resharper/Code_Inspection__Creating_Custom_Inspections_and_QuickFixes.html#4101e) and use it to fix each code block individually. I can try to play a bit with that as well and extend my answer if needed. – dbl Sep 24 '20 at 12:51