mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-161007 Support method chains and comparingInt/Long/Double
This commit is contained in:
+140
-34
@@ -15,20 +15,25 @@
|
||||
*/
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.siyeh.ig.psiutils.MethodUtils;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ComparatorCombinatorsInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
@SuppressWarnings("DialogTitleCapitalization")
|
||||
@Override
|
||||
public void visitLambdaExpression(PsiLambdaExpression lambda) {
|
||||
super.visitLambdaExpression(lambda);
|
||||
@@ -40,19 +45,29 @@ public class ComparatorCombinatorsInspection extends BaseJavaBatchLocalInspectio
|
||||
if(MethodUtils.isCompareToCall(methodCall)) {
|
||||
PsiExpression left = methodCall.getMethodExpression().getQualifierExpression();
|
||||
PsiExpression right = methodCall.getArgumentList().getExpressions()[0];
|
||||
if(left instanceof PsiMethodCallExpression && right instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression leftCall = (PsiMethodCallExpression)left;
|
||||
PsiMethodCallExpression rightCall = (PsiMethodCallExpression)right;
|
||||
if(leftCall.getArgumentList().getExpressions().length == 0 &&
|
||||
rightCall.getArgumentList().getExpressions().length == 0) {
|
||||
PsiMethod leftMethod = leftCall.resolveMethod();
|
||||
PsiMethod rightMethod = rightCall.resolveMethod();
|
||||
if(leftMethod != null && rightMethod != null && leftMethod == rightMethod) {
|
||||
if (areLambdaParameters(lambda, leftCall.getMethodExpression().getQualifierExpression(),
|
||||
rightCall.getMethodExpression().getQualifierExpression())) {
|
||||
//noinspection DialogTitleCapitalization
|
||||
holder.registerProblem(lambda, "Can be replaced with Comparator.comparing", new ReplaceWithComparatorFix());
|
||||
}
|
||||
if(areEquivalent(lambda.getParameterList().getParameters(), left, right)) {
|
||||
holder.registerProblem(lambda, "Can be replaced with Comparator.comparing", new ReplaceWithComparatorFix("comparing"));
|
||||
}
|
||||
} else {
|
||||
PsiMethod method = methodCall.resolveMethod();
|
||||
if(method != null && method.getName().equals("compare")) {
|
||||
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
|
||||
PsiClass compareClass = method.getContainingClass();
|
||||
if(args.length == 2 && compareClass != null) {
|
||||
String replacementMethodName;
|
||||
if(CommonClassNames.JAVA_LANG_DOUBLE.equals(compareClass.getQualifiedName())) {
|
||||
replacementMethodName = "comparingDouble";
|
||||
}
|
||||
else if(CommonClassNames.JAVA_LANG_INTEGER.equals(compareClass.getQualifiedName())) {
|
||||
replacementMethodName = "comparingInt";
|
||||
}
|
||||
else if(CommonClassNames.JAVA_LANG_LONG.equals(compareClass.getQualifiedName())) {
|
||||
replacementMethodName = "comparingLong";
|
||||
}
|
||||
else return;
|
||||
if(areEquivalent(lambda.getParameterList().getParameters(), args[0], args[1])) {
|
||||
holder.registerProblem(lambda, "Can be replaced with Comparator." + replacementMethodName,
|
||||
new ReplaceWithComparatorFix(replacementMethodName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,15 +78,49 @@ public class ComparatorCombinatorsInspection extends BaseJavaBatchLocalInspectio
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean areLambdaParameters(PsiLambdaExpression lambda, PsiExpression left, PsiExpression right) {
|
||||
PsiParameter[] parameters = lambda.getParameterList().getParameters();
|
||||
return left instanceof PsiReferenceExpression &&
|
||||
right instanceof PsiReferenceExpression &&
|
||||
((PsiReferenceExpression)left).resolve() == parameters[0] &&
|
||||
((PsiReferenceExpression)right).resolve() == parameters[1];
|
||||
private static boolean areEquivalent(PsiParameter[] parameters, PsiExpression left, PsiExpression right) {
|
||||
if(left == null && right == null) return true;
|
||||
if(left instanceof PsiMethodCallExpression && right instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression leftCall = (PsiMethodCallExpression)left;
|
||||
PsiMethodCallExpression rightCall = (PsiMethodCallExpression)right;
|
||||
PsiMethod leftMethod = leftCall.resolveMethod();
|
||||
PsiMethod rightMethod = rightCall.resolveMethod();
|
||||
if(leftMethod == null || leftMethod != rightMethod) return false;
|
||||
PsiExpression[] leftArgs = leftCall.getArgumentList().getExpressions();
|
||||
PsiExpression[] rightArgs = rightCall.getArgumentList().getExpressions();
|
||||
return leftArgs.length == rightArgs.length &&
|
||||
areEquivalent(parameters, leftCall.getMethodExpression(), rightCall.getMethodExpression()) &&
|
||||
!StreamEx.zip(leftArgs, rightArgs, (leftArg, rightArg) -> areEquivalent(parameters, leftArg, rightArg)).has(Boolean.FALSE);
|
||||
}
|
||||
if(left instanceof PsiReferenceExpression && right instanceof PsiReferenceExpression) {
|
||||
PsiReferenceExpression leftRef = (PsiReferenceExpression)left;
|
||||
PsiReferenceExpression rightRef = (PsiReferenceExpression)right;
|
||||
PsiElement leftElement = leftRef.resolve();
|
||||
PsiElement rightElement = rightRef.resolve();
|
||||
if(leftElement instanceof PsiParameter) {
|
||||
return leftElement == parameters[0] && rightElement == parameters[1];
|
||||
}
|
||||
if(leftElement != rightElement) return false;
|
||||
PsiType[] leftTypes = leftRef.getTypeParameters();
|
||||
PsiType[] rightTypes = leftRef.getTypeParameters();
|
||||
return leftTypes.length == rightTypes.length && !StreamEx.zip(leftTypes, rightTypes, Objects::equals).has(Boolean.FALSE)
|
||||
&& areEquivalent(parameters, leftRef.getQualifierExpression(), rightRef.getQualifierExpression());
|
||||
}
|
||||
if(left instanceof PsiTypeCastExpression && right instanceof PsiTypeCastExpression) {
|
||||
PsiTypeCastExpression leftCast = (PsiTypeCastExpression)left;
|
||||
PsiTypeCastExpression rightCast = (PsiTypeCastExpression)right;
|
||||
return leftCast.getCastType() == rightCast.getCastType() &&
|
||||
areEquivalent(parameters, leftCast.getOperand(), rightCast.getOperand());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static class ReplaceWithComparatorFix implements LocalQuickFix {
|
||||
private final String myMethodName;
|
||||
|
||||
public ReplaceWithComparatorFix(String methodName) {
|
||||
myMethodName = methodName;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@@ -84,7 +133,7 @@ public class ComparatorCombinatorsInspection extends BaseJavaBatchLocalInspectio
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Replace with Comparator.comparing";
|
||||
return "Replace with Comparator." + myMethodName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,22 +144,79 @@ public class ComparatorCombinatorsInspection extends BaseJavaBatchLocalInspectio
|
||||
PsiElement body = lambda.getBody();
|
||||
if (!(body instanceof PsiMethodCallExpression)) return;
|
||||
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)body;
|
||||
if (!MethodUtils.isCompareToCall(methodCall)) return;
|
||||
PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression();
|
||||
if (!(qualifier instanceof PsiMethodCallExpression)) return;
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)qualifier;
|
||||
if (call.getArgumentList().getExpressions().length != 0) return;
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null) return;
|
||||
PsiClass methodClass = method.getContainingClass();
|
||||
if (methodClass == null) return;
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
|
||||
PsiExpression keyExtractor = null;
|
||||
String methodName = null;
|
||||
if (MethodUtils.isCompareToCall(methodCall)) {
|
||||
methodName = "comparing";
|
||||
keyExtractor = methodCall.getMethodExpression().getQualifierExpression();
|
||||
} else {
|
||||
PsiMethod method = methodCall.resolveMethod();
|
||||
if(method != null && method.getName().equals("compare")) {
|
||||
PsiClass containingClass = method.getContainingClass();
|
||||
if(containingClass != null) {
|
||||
String className = containingClass.getQualifiedName();
|
||||
if(className != null) {
|
||||
PsiExpression[] args = methodCall.getArgumentList().getExpressions();
|
||||
if(args.length != 2) return;
|
||||
keyExtractor = args[0];
|
||||
switch (className) {
|
||||
case CommonClassNames.JAVA_LANG_LONG:
|
||||
methodName = "comparingLong";
|
||||
break;
|
||||
case CommonClassNames.JAVA_LANG_INTEGER:
|
||||
methodName = "comparingInt";
|
||||
break;
|
||||
case CommonClassNames.JAVA_LANG_DOUBLE:
|
||||
methodName = "comparingDouble";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(methodName == null || keyExtractor == null) return;
|
||||
PsiParameter parameter = lambda.getParameterList().getParameters()[0];
|
||||
String parameterName = parameter.getName();
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiExpression replacement =
|
||||
factory.createExpressionFromText("java.util.Comparator.comparing(" + methodClass.getQualifiedName() + "::" + method.getName() + ")",
|
||||
element);
|
||||
PsiExpression replacement = factory
|
||||
.createExpressionFromText("java.util.Comparator." + methodName + "(" + parameterName + " -> " + keyExtractor.getText() + ")",
|
||||
element);
|
||||
PsiElement result = lambda.replace(replacement);
|
||||
normalizeLambda(((PsiMethodCallExpression)result).getArgumentList().getExpressions()[0], factory);
|
||||
CodeStyleManager.getInstance(project).reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(result));
|
||||
}
|
||||
|
||||
private static void normalizeLambda(PsiExpression expression, PsiElementFactory factory) {
|
||||
if(!(expression instanceof PsiLambdaExpression)) return;
|
||||
PsiLambdaExpression lambda = (PsiLambdaExpression)expression;
|
||||
PsiParameter[] parameters = lambda.getParameterList().getParameters();
|
||||
PsiElement body = lambda.getBody();
|
||||
if(body == null) return;
|
||||
String methodReference =
|
||||
LambdaCanBeMethodReferenceInspection.convertToMethodReference(body, parameters, lambda.getFunctionalInterfaceType(), lambda);
|
||||
if(methodReference != null) {
|
||||
lambda.replace(factory.createExpressionFromText(methodReference, lambda));
|
||||
} else {
|
||||
PsiParameter parameter = parameters[0];
|
||||
String name = parameter.getName();
|
||||
String nameCandidate = null;
|
||||
if(name != null) {
|
||||
if(name.length() > 1 && name.endsWith("1")) {
|
||||
nameCandidate = name.substring(0, name.length()-1);
|
||||
}
|
||||
}
|
||||
if(nameCandidate != null) {
|
||||
String newName = JavaCodeStyleManager.getInstance(expression.getProject()).suggestUniqueVariableName(nameCandidate, lambda, true);
|
||||
if(newName.equals(nameCandidate)) {
|
||||
Collection<PsiReferenceExpression> references = PsiTreeUtil.collectElementsOfType(body, PsiReferenceExpression.class);
|
||||
StreamEx.of(references).filter(ref -> ref.resolve() == parameter).map(PsiJavaCodeReferenceElement::getReferenceNameElement)
|
||||
.nonNull().forEach(nameElement -> nameElement.replace(factory.createIdentifier(newName)));
|
||||
parameter.setName(newName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace with Comparator.comparing" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
void sort(List<Person> persons) {
|
||||
persons.sort(Comparator.comparing(p -> p.getName().toLowerCase(Locale.ENGLISH)));
|
||||
}
|
||||
|
||||
interface Person {
|
||||
String getName();
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace with Comparator.comparingDouble" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
void sort(List<Person> persons) {
|
||||
persons.sort(Comparator.comparingDouble(p -> p.getName().length()));
|
||||
}
|
||||
|
||||
interface Person {
|
||||
String getName();
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Replace with Comparator.comparing" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
void sort(List<Person> persons) {
|
||||
String p;
|
||||
persons.sort(Comparator.comparing(p1 -> p1.name));
|
||||
}
|
||||
|
||||
class Person {
|
||||
String name;
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Replace with Comparator.comparing" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
void sort(List<Person> persons) {
|
||||
persons.sort((p1, p2) -> p1.getName()<caret>.toLowerCase(Locale.ENGLISH)
|
||||
.compareTo(p2.getName().toLowerCase(Locale.ENGLISH)));
|
||||
}
|
||||
|
||||
interface Person {
|
||||
String getName();
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Replace with Comparator.comparing" "false"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
void sort(List<Person> persons) {
|
||||
persons.sort((p1, p2) -> p1.getName()<caret>.toLowerCase(Locale.ENGLISH)
|
||||
.compareTo(p2.getName().toLowerCase(Locale.CANADA)));
|
||||
}
|
||||
|
||||
interface Person {
|
||||
String getName();
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace with Comparator.comparingDouble" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
void sort(List<Person> persons) {
|
||||
persons.sort((p1, p2) -> Double.com<caret>pare(p1.getName().length(), p2.getName().length()));
|
||||
}
|
||||
|
||||
interface Person {
|
||||
String getName();
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Replace with Comparator.comparing" "true"
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Main {
|
||||
void sort(List<Person> persons) {
|
||||
String p;
|
||||
persons.sort((p1, p2) -> p1.name.compar<caret>eTo(p2.name));
|
||||
}
|
||||
|
||||
class Person {
|
||||
String name;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user