Migrate to simplified collection: Map support

This commit is contained in:
Tagir Valeev
2017-02-27 17:23:21 +07:00
parent 05a2a29300
commit 0deb0dac6f
7 changed files with 118 additions and 31 deletions
@@ -46,12 +46,16 @@ import static com.intellij.util.ObjectUtils.tryCast;
public class Java9CollectionFactoryInspection extends BaseLocalInspectionTool {
private static final CallMatcher UNMODIFIABLE_SET =
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_COLLECTIONS, "unmodifiableSet").parameterCount(1);
private static final CallMatcher UNMODIFIABLE_MAP =
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_COLLECTIONS, "unmodifiableMap").parameterCount(1);
private static final CallMatcher UNMODIFIABLE_LIST =
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_COLLECTIONS, "unmodifiableList").parameterCount(1);
private static final CallMatcher ARRAYS_AS_LIST =
CallMatcher.staticCall(CommonClassNames.JAVA_UTIL_ARRAYS, "asList");
private static final CallMatcher COLLECTION_ADD =
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "add").parameterCount(1);
private static final CallMatcher MAP_PUT =
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_MAP, "put").parameterCount(2);
private static final CallMatcher STREAM_COLLECT =
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_STREAM_STREAM, "collect").parameterCount(1);
private static final CallMatcher STREAM_OF =
@@ -63,6 +67,7 @@ public class Java9CollectionFactoryInspection extends BaseLocalInspectionTool {
private static final CallMapper<PrepopulatedCollectionModel> MAPPER = new CallMapper<PrepopulatedCollectionModel>()
.register(UNMODIFIABLE_SET, call -> PrepopulatedCollectionModel.fromSet(call.getArgumentList().getExpressions()[0]))
.register(UNMODIFIABLE_MAP, call -> PrepopulatedCollectionModel.fromMap(call.getArgumentList().getExpressions()[0]))
.register(UNMODIFIABLE_LIST, call -> PrepopulatedCollectionModel.fromList(call.getArgumentList().getExpressions()[0]));
public boolean IGNORE_NON_CONSTANT = false;
@@ -122,7 +127,8 @@ public class Java9CollectionFactoryInspection extends BaseLocalInspectionTool {
}
public boolean isValid() {
return !myHasNulls && !myRepeatingKeys;
boolean mapOfTooManyParameters = myType.equals("Map") && myContent.size() > 20;
return !myHasNulls && !myRepeatingKeys && !mapOfTooManyParameters;
}
private StreamEx<PsiExpression> keyExpressions() {
@@ -143,54 +149,56 @@ public class Java9CollectionFactoryInspection extends BaseLocalInspectionTool {
if (ARRAYS_AS_LIST.test(call)) {
return new PrepopulatedCollectionModel(Arrays.asList(call.getArgumentList().getExpressions()), Collections.emptyList(), "List");
}
if(STREAM_COLLECT.test(call) && COLLECTORS_TO_LIST.matches(call.getArgumentList().getExpressions()[0])) {
PsiMethodCallExpression qualifier = MethodCallUtils.getQualifierMethodCall(call);
if(STREAM_OF.matches(qualifier)) {
return new PrepopulatedCollectionModel(Arrays.asList(qualifier.getArgumentList().getExpressions()), Collections.emptyList(),
"List");
}
}
return fromCollect(call, "List", COLLECTORS_TO_LIST);
}
if(listDefinition instanceof PsiNewExpression) {
return fromNewExpression((PsiNewExpression)listDefinition, "List", CommonClassNames.JAVA_UTIL_ARRAY_LIST);
}
if (listDefinition instanceof PsiReferenceExpression) {
PsiLocalVariable variable = tryCast(((PsiReferenceExpression)listDefinition).resolve(), PsiLocalVariable.class);
if (variable != null) {
return fromVariable(variable, listDefinition, "List", CommonClassNames.JAVA_UTIL_ARRAY_LIST);
}
return fromVariable((PsiReferenceExpression)listDefinition, "List", CommonClassNames.JAVA_UTIL_ARRAY_LIST, COLLECTION_ADD);
}
return null;
}
public static PrepopulatedCollectionModel fromSet(PsiExpression setDefinition) {
setDefinition = PsiUtil.skipParenthesizedExprDown(setDefinition);
if(setDefinition instanceof PsiNewExpression) {
if (setDefinition instanceof PsiMethodCallExpression) {
return fromCollect((PsiMethodCallExpression)setDefinition, "Set", COLLECTORS_TO_SET);
}
if (setDefinition instanceof PsiNewExpression) {
return fromNewExpression((PsiNewExpression)setDefinition, "Set", CommonClassNames.JAVA_UTIL_HASH_SET);
}
if(setDefinition instanceof PsiMethodCallExpression) {
PsiMethodCallExpression call = (PsiMethodCallExpression)setDefinition;
if(STREAM_COLLECT.test(call) && COLLECTORS_TO_SET.matches(call.getArgumentList().getExpressions()[0])) {
PsiMethodCallExpression qualifier = MethodCallUtils.getQualifierMethodCall(call);
if(STREAM_OF.matches(qualifier)) {
return new PrepopulatedCollectionModel(Arrays.asList(qualifier.getArgumentList().getExpressions()), Collections.emptyList(),
"Set");
}
}
}
if (setDefinition instanceof PsiReferenceExpression) {
PsiLocalVariable variable = tryCast(((PsiReferenceExpression)setDefinition).resolve(), PsiLocalVariable.class);
if (variable != null) {
return fromVariable(variable, setDefinition, "Set", CommonClassNames.JAVA_UTIL_HASH_SET);
return fromVariable((PsiReferenceExpression)setDefinition, "Set", CommonClassNames.JAVA_UTIL_HASH_SET, COLLECTION_ADD);
}
return null;
}
public static PrepopulatedCollectionModel fromMap(PsiExpression mapDefinition) {
mapDefinition = PsiUtil.skipParenthesizedExprDown(mapDefinition);
if (mapDefinition instanceof PsiReferenceExpression) {
return fromVariable((PsiReferenceExpression)mapDefinition, "Map", CommonClassNames.JAVA_UTIL_HASH_MAP, MAP_PUT);
}
return null;
}
@Nullable
private static PrepopulatedCollectionModel fromCollect(PsiMethodCallExpression call, String typeName, CallMatcher collector) {
if (STREAM_COLLECT.test(call) && collector.matches(call.getArgumentList().getExpressions()[0])) {
PsiMethodCallExpression qualifier = MethodCallUtils.getQualifierMethodCall(call);
if (STREAM_OF.matches(qualifier)) {
return new PrepopulatedCollectionModel(Arrays.asList(qualifier.getArgumentList().getExpressions()), Collections.emptyList(),
typeName);
}
}
return null;
}
@Nullable
private static PrepopulatedCollectionModel fromVariable(PsiLocalVariable variable,
PsiExpression expression,
String typeName, String collectionClass) {
private static PrepopulatedCollectionModel fromVariable(PsiReferenceExpression expression,
String typeName, String collectionClass, CallMatcher addMethod) {
PsiLocalVariable variable = tryCast(expression.resolve(), PsiLocalVariable.class);
if (variable == null) return null;
PsiCodeBlock block = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
PsiDeclarationStatement declaration = PsiTreeUtil.getParentOfType(variable, PsiDeclarationStatement.class);
if (block == null || declaration == null) return null;
@@ -211,9 +219,9 @@ public class Java9CollectionFactoryInspection extends BaseLocalInspectionTool {
if (PsiTreeUtil.isAncestor(cur, expression, false)) break;
if (!(cur instanceof PsiExpressionStatement)) return null;
PsiMethodCallExpression call = tryCast(((PsiExpressionStatement)cur).getExpression(), PsiMethodCallExpression.class);
if (!COLLECTION_ADD.test(call)) return null;
if (!addMethod.test(call)) return null;
if (!refs.remove(call.getMethodExpression().getQualifierExpression())) return null;
contents.add(call.getArgumentList().getExpressions()[0]);
contents.addAll(Arrays.asList(call.getArgumentList().getExpressions()));
elementsToRemove.add(cur);
}
if (!refs.isEmpty()) return null;
@@ -0,0 +1,9 @@
// "Replace with 'Map.of' call" "true"
import java.util.*;
public class Test {
public void test() {
Map<String, String> myMap;
myMap = Map.of("a", "1", "b", "1", "c", "1", "d", "1", "e", "1", "f", "1", "g", "1", "h", "1", "i", "1", "j", "1");
}
}
@@ -0,0 +1,9 @@
// "Replace with 'Map.of' call" "true"
import java.util.*;
public class Test {
public void test() {
Map<String, String> myMap;
myMap = Map.of("a", "b", "c", "b");
}
}
@@ -0,0 +1,19 @@
// "Replace with 'Map.of' call" "true"
import java.util.*;
public class Test {
public void test() {
Map<String, String> myMap = new HashMap<>();
myMap.put("a", "1");
myMap.put("b", "1");
myMap.put("c", "1");
myMap.put("d", "1");
myMap.put("e", "1");
myMap.put("f", "1");
myMap.put("g", "1");
myMap.put("h", "1");
myMap.put("i", "1");
myMap.put("j", "1");
myMap = Collections.unmodifia<caret>bleMap(myMap);
}
}
@@ -0,0 +1,20 @@
// "Replace with 'Map.of' call" "false"
import java.util.*;
public class Test {
public void test() {
Map<String, String> myMap = new HashMap<>();
myMap.put("a", "1");
myMap.put("b", "1");
myMap.put("c", "1");
myMap.put("d", "1");
myMap.put("e", "1");
myMap.put("f", "1");
myMap.put("g", "1");
myMap.put("h", "1");
myMap.put("i", "1");
myMap.put("j", "1");
myMap.put("k", "1");
myMap = Collections.unmodifia<caret>bleMap(myMap);
}
}
@@ -0,0 +1,11 @@
// "Replace with 'Map.of' call" "false"
import java.util.*;
public class Test {
public void test() {
Map<String, String> myMap = new HashMap<>();
myMap.put("a", "b");
myMap.put("a", "c");
myMap = Collections.unmo<caret>difiableMap(myMap);
}
}
@@ -0,0 +1,11 @@
// "Replace with 'Map.of' call" "true"
import java.util.*;
public class Test {
public void test() {
Map<String, String> myMap = new HashMap<>();
myMap.put("a", "b");
myMap.put("c", "b");
myMap = Collections.unmo<caret>difiableMap(myMap);
}
}