mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-160364, IDEA-160301 (SimplifyStreamApiCallChainsInspection.java)
Inspection to replace Collections.emptyList/Set().stream() with Stream.empty() and Collections.singleton[List](xyz).stream() with Stream.of(xyz) Arrays.<String[]>asList(stringArray).stream() is incorrectly converted to Arrays.<String[]>stream(stringArray)
This commit is contained in:
+128
-30
@@ -16,11 +16,11 @@
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.util.ClassUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
@@ -31,13 +31,16 @@ import org.jetbrains.annotations.Nullable;
|
||||
* @author Pavel.Dolgov
|
||||
*/
|
||||
public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance("#" + SimplifyStreamApiCallChainsInspection.class.getName());
|
||||
|
||||
private static final String FOR_EACH_METHOD = "forEach";
|
||||
private static final String FOR_EACH_ORDERED_METHOD = "forEachOrdered";
|
||||
private static final String STREAM_METHOD = "stream";
|
||||
private static final String EMPTY_METHOD = "empty";
|
||||
private static final String AS_LIST_METHOD = "asList";
|
||||
private static final String OF_METHOD = "of";
|
||||
private static final String EMPTY_LIST_METHOD = "emptyList";
|
||||
private static final String EMPTY_SET_METHOD = "emptySet";
|
||||
private static final String SINGLETON_LIST_METHOD = "singletonList";
|
||||
private static final String SINGLETON_METHOD = "singleton";
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
@@ -54,33 +57,55 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression methodCall) {
|
||||
if (isCallOf(methodCall, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0)) {
|
||||
final PsiMethod method = methodCall.resolveMethod();
|
||||
if (isCallOf(method, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0)) {
|
||||
final PsiMethodCallExpression qualifierCall = getQualifierMethodCall(methodCall);
|
||||
if (isCallOf(qualifierCall, CommonClassNames.JAVA_UTIL_ARRAYS, AS_LIST_METHOD, 1)) {
|
||||
final PsiExpression[] argumentExpressions = qualifierCall.getArgumentList().getExpressions();
|
||||
if (argumentExpressions.length == 1 && argumentExpressions[0].getType() instanceof PsiArrayType) {
|
||||
holder.registerProblem(methodCall, null, "Arrays.asList().stream() can be replaced with Arrays.stream()",
|
||||
new ArraysAsListSingleArrayFix());
|
||||
if (qualifierCall == null) return;
|
||||
final PsiMethod qualifier = qualifierCall.resolveMethod();
|
||||
ReplaceCollectionStreamFix fix = null;
|
||||
if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_ARRAYS, AS_LIST_METHOD, 1)) {
|
||||
if (hasSingleArrayArgument(qualifierCall)) {
|
||||
fix = new ArraysAsListSingleArrayFix();
|
||||
}
|
||||
else {
|
||||
holder.registerProblem(methodCall, null, "Arrays.asList().stream() can be replaced with Stream.of()",
|
||||
new ArraysAsListVarargFix());
|
||||
fix = new ReplaceWithStreamOfFix("Arrays.asList()");
|
||||
}
|
||||
}
|
||||
else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, SINGLETON_LIST_METHOD, 1)) {
|
||||
if(!hasSingleArrayArgument(qualifierCall)) {
|
||||
fix = new ReplaceSingletonWithStreamOfFix("Collections.singletonList()");
|
||||
}
|
||||
}
|
||||
else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, SINGLETON_METHOD, 1)) {
|
||||
if(!hasSingleArrayArgument(qualifierCall)) {
|
||||
fix = new ReplaceSingletonWithStreamOfFix("Collections.singleton()");
|
||||
}
|
||||
}
|
||||
else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, EMPTY_LIST_METHOD, 0)) {
|
||||
fix = new ReplaceWithStreamEmptyFix(EMPTY_LIST_METHOD);
|
||||
}
|
||||
else if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTIONS, EMPTY_SET_METHOD, 0)) {
|
||||
fix = new ReplaceWithStreamEmptyFix(EMPTY_SET_METHOD);
|
||||
}
|
||||
if (fix != null) {
|
||||
holder.registerProblem(methodCall, null, fix.getMessage(), fix);
|
||||
}
|
||||
}
|
||||
else {
|
||||
final String name;
|
||||
if (isCallOf(methodCall, CommonClassNames.JAVA_UTIL_STREAM_STREAM, FOR_EACH_METHOD, 1)) {
|
||||
if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, FOR_EACH_METHOD, 1)) {
|
||||
name = FOR_EACH_METHOD;
|
||||
}
|
||||
else if (isCallOf(methodCall, CommonClassNames.JAVA_UTIL_STREAM_STREAM, FOR_EACH_ORDERED_METHOD, 1)) {
|
||||
else if (isCallOf(method, CommonClassNames.JAVA_UTIL_STREAM_STREAM, FOR_EACH_ORDERED_METHOD, 1)) {
|
||||
name = FOR_EACH_ORDERED_METHOD;
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
final PsiMethodCallExpression qualifierCall = getQualifierMethodCall(methodCall);
|
||||
if (isCallOf(qualifierCall, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0)) {
|
||||
if (qualifierCall == null) return;
|
||||
final PsiMethod qualifier = qualifierCall.resolveMethod();
|
||||
if (isCallOf(qualifier, CommonClassNames.JAVA_UTIL_COLLECTION, STREAM_METHOD, 0)) {
|
||||
String message = "Collection.stream()." + name + "() can be replaced with Collection.forEach()";
|
||||
final LocalQuickFix fix;
|
||||
if (FOR_EACH_METHOD.equals(name)) {
|
||||
@@ -97,6 +122,25 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
};
|
||||
}
|
||||
|
||||
static boolean hasSingleArrayArgument(PsiMethodCallExpression qualifierCall) {
|
||||
final PsiExpression[] argumentExpressions = qualifierCall.getArgumentList().getExpressions();
|
||||
if (argumentExpressions.length == 1) {
|
||||
PsiType type = argumentExpressions[0].getType();
|
||||
if(type instanceof PsiArrayType) {
|
||||
PsiType methodType = qualifierCall.getType();
|
||||
// Rule out cases like Arrays.<String[]>asList(stringArr)
|
||||
if(methodType instanceof PsiClassType) {
|
||||
PsiType[] parameters = ((PsiClassType)methodType).getParameters();
|
||||
if(parameters.length == 1 && parameters[0].equals(type))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiMethodCallExpression getQualifierMethodCall(PsiMethodCallExpression methodCall) {
|
||||
final PsiExpression qualifierExpression = methodCall.getMethodExpression().getQualifierExpression();
|
||||
if (qualifierExpression instanceof PsiMethodCallExpression) {
|
||||
@@ -116,13 +160,12 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
}
|
||||
|
||||
@Contract("null, _, _, _ -> false")
|
||||
protected static boolean isCallOf(@Nullable PsiMethodCallExpression expression,
|
||||
protected static boolean isCallOf(@Nullable PsiMethod method,
|
||||
@NotNull String className,
|
||||
@NotNull String methodName,
|
||||
int parametersCount) {
|
||||
if (expression == null) return false;
|
||||
final PsiMethod method = expression.resolveMethod();
|
||||
if (method != null && methodName.equals(method.getName()) && method.getParameterList().getParametersCount() == parametersCount) {
|
||||
if (method == null) return false;
|
||||
if (methodName.equals(method.getName()) && method.getParameterList().getParametersCount() == parametersCount) {
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
if (containingClass != null && className.equals(containingClass.getQualifiedName())) {
|
||||
return true;
|
||||
@@ -159,15 +202,34 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
@Nullable PsiExpression qualifierExpression);
|
||||
}
|
||||
|
||||
private static abstract class ArraysAsListFix extends CallChainFixBase {
|
||||
private static abstract class ReplaceCollectionStreamFix extends CallChainFixBase {
|
||||
private final String myClassName;
|
||||
private final String myMethodName;
|
||||
private final String myQualifierCall;
|
||||
|
||||
private ArraysAsListFix(String className, String methodName) {
|
||||
private ReplaceCollectionStreamFix(String qualifierCall, String className, String methodName) {
|
||||
myQualifierCall = qualifierCall;
|
||||
myClassName = className;
|
||||
myMethodName = methodName;
|
||||
}
|
||||
|
||||
String getMessage() {
|
||||
return myQualifierCall + ".stream() can be replaced with " + ClassUtil.extractClassName(myClassName) + "." + myMethodName + "()";
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Replace " + myQualifierCall + ".stream() with " + ClassUtil.extractClassName(myClassName) + "." + myMethodName + "()";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected String getTypeParameter(@NotNull PsiMethodCallExpression qualifierCall) {
|
||||
PsiType[] parameters = qualifierCall.getMethodExpression().getTypeParameters();
|
||||
return parameters.length == 1 ? parameters[0].getCanonicalText() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void replaceMethodCall(@NotNull PsiMethodCallExpression methodCall,
|
||||
@NotNull PsiMethodCallExpression qualifierCall,
|
||||
@@ -175,11 +237,12 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
methodCall.getArgumentList().replace(qualifierCall.getArgumentList());
|
||||
|
||||
final Project project = methodCall.getProject();
|
||||
PsiType[] parameters = qualifierCall.getMethodExpression().getTypeParameters();
|
||||
String typeParameter = getTypeParameter(qualifierCall);
|
||||
String replacement;
|
||||
if(parameters.length == 1) {
|
||||
replacement = myClassName + ".<" + parameters[0].getCanonicalText() + ">" + myMethodName;
|
||||
} else {
|
||||
if (typeParameter != null) {
|
||||
replacement = myClassName + ".<" + typeParameter + ">" + myMethodName;
|
||||
}
|
||||
else {
|
||||
replacement = myClassName + "." + myMethodName;
|
||||
}
|
||||
final PsiExpression newMethodExpression = JavaPsiFacade.getElementFactory(project).createExpressionFromText(replacement, methodCall);
|
||||
@@ -187,22 +250,44 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArraysAsListVarargFix extends ArraysAsListFix {
|
||||
private ArraysAsListVarargFix() {
|
||||
super(CommonClassNames.JAVA_UTIL_STREAM_STREAM, OF_METHOD);
|
||||
private static class ReplaceWithStreamOfFix extends ReplaceCollectionStreamFix {
|
||||
private ReplaceWithStreamOfFix(String qualifierCall) {
|
||||
super(qualifierCall, CommonClassNames.JAVA_UTIL_STREAM_STREAM, OF_METHOD);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Replace Arrays.asList().stream() with Stream.of()";
|
||||
return "Replace with Stream.of()";
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArraysAsListSingleArrayFix extends ArraysAsListFix {
|
||||
private static class ReplaceSingletonWithStreamOfFix extends ReplaceWithStreamOfFix {
|
||||
private ReplaceSingletonWithStreamOfFix(String qualifierCall) {
|
||||
super(qualifierCall);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getTypeParameter(@NotNull PsiMethodCallExpression qualifierCall) {
|
||||
String typeParameter = super.getTypeParameter(qualifierCall);
|
||||
if(typeParameter != null)
|
||||
return typeParameter;
|
||||
PsiType[] argTypes = qualifierCall.getArgumentList().getExpressionTypes();
|
||||
if(argTypes.length == 1) {
|
||||
PsiType argType = argTypes[0];
|
||||
if(argType instanceof PsiArrayType) {
|
||||
return argType.getCanonicalText();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ArraysAsListSingleArrayFix extends ReplaceCollectionStreamFix {
|
||||
private ArraysAsListSingleArrayFix() {
|
||||
super(CommonClassNames.JAVA_UTIL_ARRAYS, STREAM_METHOD);
|
||||
super("Arrays.asList()", CommonClassNames.JAVA_UTIL_ARRAYS, STREAM_METHOD);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@@ -213,6 +298,19 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReplaceWithStreamEmptyFix extends ReplaceCollectionStreamFix {
|
||||
private ReplaceWithStreamEmptyFix(String qualifierMethodName) {
|
||||
super("Collections." + qualifierMethodName + "()", CommonClassNames.JAVA_UTIL_STREAM_STREAM, EMPTY_METHOD);
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Replace with Stream.empty()";
|
||||
}
|
||||
}
|
||||
|
||||
private static class CollectionForEachFix extends CallChainFixBase {
|
||||
@Nls
|
||||
@NotNull
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Arrays.asList().stream() with Stream.of()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ArraysStreamSingleElementArray {
|
||||
Stream<String[]> stream(String[] args) {
|
||||
return Stream.<String[]>of(args);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.emptyList().stream() with Stream.empty()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionEmptyListStream {
|
||||
Stream<String> stream(String[] args) {
|
||||
return args.length == 1 ? Stream.<String>empty() : Arrays.stream(args);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.emptySet().stream() with Stream.empty()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionEmptySetStream {
|
||||
Stream<String> stream(String[] args) {
|
||||
return args.length == 1 ? Stream.<String>empty() : Arrays.stream(args);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.singleton().stream() with Stream.of()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionSingletonArrayStream {
|
||||
Stream<String[]> stream(String[] args) {
|
||||
return Stream.<String[]>of(args);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.singletonList().stream() with Stream.of()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionSingletonListStream {
|
||||
Stream<String> stream(String[] args) {
|
||||
return Stream.of("xyz");
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.singleton().stream() with Stream.of()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionSingletonStream {
|
||||
Stream<String> stream(String[] args) {
|
||||
return Stream.<String>of("xyz");
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Arrays.asList().stream() with Stream.of()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ArraysStreamSingleElementArray {
|
||||
Stream<String[]> stream(String[] args) {
|
||||
return Arrays.<Strin<caret>g[]>asList(args).stream();
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.emptyList().stream() with Stream.empty()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionEmptyListStream {
|
||||
Stream<String> stream(String[] args) {
|
||||
return args.length == 1 ? Col<caret>lections.<String>emptyList().stream() : Arrays.stream(args);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.emptySet().stream() with Stream.empty()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionEmptySetStream {
|
||||
Stream<String> stream(String[] args) {
|
||||
return args.length == 1 ? Col<caret>lections.<String>emptySet().stream() : Arrays.stream(args);
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.singleton().stream() with Stream.of()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionSingletonArrayStream {
|
||||
Stream<String[]> stream(String[] args) {
|
||||
return Col<caret>lections.singleton(args).stream();
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.singletonList().stream() with Stream.of()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionSingletonListStream {
|
||||
Stream<String> stream(String[] args) {
|
||||
return Collections.singletonList("xyz").strea<caret>m();
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace Collections.singleton().stream() with Stream.of()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class CollectionSingletonStream {
|
||||
Stream<String> stream(String[] args) {
|
||||
return Col<caret>lections.<String>singleton("xyz").stream();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user