diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java index 18e7c1729e3a..cdba44033905 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java @@ -211,10 +211,12 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection .createExpressionFromText("new " + expectedType.getCanonicalText() + "[]{" + anonymousClass.getText() + "}", anonymousClass); PsiArrayInitializerExpression initializer = newArrayExpression.getArrayInitializer(); LOG.assertTrue(initializer != null); - return replacePsiElementWithLambda(initializer.getInitializers()[0], true); + return replacePsiElementWithLambda(initializer.getInitializers()[0], true, false); } - public static PsiExpression replacePsiElementWithLambda(@NotNull PsiElement element, final boolean ignoreEqualsMethod) { + public static PsiExpression replacePsiElementWithLambda(@NotNull PsiElement element, + final boolean ignoreEqualsMethod, + boolean forceIgnoreTypeCast) { if (element instanceof PsiNewExpression) { if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return null; final PsiAnonymousClass anonymousClass = ((PsiNewExpression)element).getAnonymousClass(); @@ -282,6 +284,11 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection } ChangeContextUtil.decodeContextInfo(lambdaExpression, null, null); + final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project); + if (forceIgnoreTypeCast) { + return (PsiExpression)javaCodeStyleManager.shortenClassReferences(elementFactory.createExpressionFromText(withoutTypesDeclared, lambdaExpression)); + } + PsiTypeCastExpression typeCast = (PsiTypeCastExpression)elementFactory .createExpressionFromText("(" + canonicalText + ")" + withoutTypesDeclared, lambdaExpression); final PsiExpression typeCastOperand = typeCast.getOperand(); @@ -298,7 +305,7 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection LOG.assertTrue(operand != null); return (PsiExpression)typeCast.replace(operand); } - return (PsiExpression)JavaCodeStyleManager.getInstance(project).shortenClassReferences(typeCast); + return (PsiExpression)javaCodeStyleManager.shortenClassReferences(typeCast); } return null; } @@ -320,7 +327,7 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { final PsiElement element = descriptor.getPsiElement(); if (element != null) { - replacePsiElementWithLambda(element, false); + replacePsiElementWithLambda(element, false, true); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFunctionAndPredicateConverter.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFunctionAndPredicateConverter.java index 331e9f8ddaf6..50030a769c00 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFunctionAndPredicateConverter.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/GuavaFunctionAndPredicateConverter.java @@ -56,7 +56,7 @@ public class GuavaFunctionAndPredicateConverter { if (expression instanceof PsiNewExpression) { final PsiAnonymousClass anonymousClass = ((PsiNewExpression)expression).getAnonymousClass(); if (anonymousClass != null && AnonymousCanBeLambdaInspection.canBeConvertedToLambda(anonymousClass, true)) { - final PsiExpression lambdaExpression = AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true); + final PsiExpression lambdaExpression = AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true, true); LOG.assertTrue(lambdaExpression != null); return lambdaExpression.getText(); } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java index 69cca8caf97d..7a31db0f2d95 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java @@ -16,8 +16,7 @@ package com.intellij.refactoring.typeMigration.inspections; import com.intellij.codeInsight.daemon.impl.quickfix.VariableTypeFix; -import com.intellij.codeInspection.BaseJavaLocalInspectionTool; -import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.codeInspection.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.AtomicNotNullLazyValue; @@ -32,7 +31,8 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashMap; import org.jetbrains.annotations.NotNull; -import java.util.Collection; +import java.util.ArrayList; +import java.util.List; import java.util.Map; /** @@ -40,7 +40,9 @@ import java.util.Map; */ @SuppressWarnings("DialogTitleCapitalization") public class GuavaInspection extends BaseJavaLocalInspectionTool { - private final static String PROBLEM_DESCRIPTION = "Guava's functional primitives can be replaced by Java API"; + private final static Logger LOG = Logger.getInstance(GuavaInspection.class); + + private final static String PROBLEM_DESCRIPTION_FOR_VARIABLE = "Guava's functional primitives can be replaced by Java API"; @NotNull @Override @@ -84,15 +86,28 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool { final String qName = psiClass.getQualifiedName(); final PsiClass targetClass = myGuavaClassConversions.getValue().get(qName); if (targetClass != null) { - final Collection typeParameters = resolveResult.getSubstitutor().getSubstitutionMap().values(); - final PsiClassType targetType = - JavaPsiFacade.getElementFactory(holder.getProject()).createType(targetClass, typeParameters.toArray(new PsiType[typeParameters.size()])); - final VariableTypeFix fix = TypeMigrationVariableTypeFixProvider.createTypeMigrationFix(variable, targetType); - holder.registerProblem(variable, PROBLEM_DESCRIPTION, fix); + final VariableTypeFix fix = TypeMigrationVariableTypeFixProvider.createTypeMigrationFix(variable, addTypeParameters(type, resolveResult, targetClass)); + holder.registerProblem(variable, PROBLEM_DESCRIPTION_FOR_VARIABLE, fix); } } } } + + private PsiClassType addTypeParameters(PsiType currentType, PsiClassType.ClassResolveResult currentTypeResolveResult, PsiClass targetClass) { + final Map substitutionMap = currentTypeResolveResult.getSubstitutor().getSubstitutionMap(); + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(holder.getProject()); + if (substitutionMap.size() == 1) { + return elementFactory.createType(targetClass, ContainerUtil.getFirstItem(substitutionMap.values())); + } else { + LOG.assertTrue(substitutionMap.size() == 2); + LOG.assertTrue(GuavaFunctionConversionRule.JAVA_UTIL_FUNCTION_FUNCTION.equals(targetClass.getQualifiedName())); + final PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(currentType); + final List types = new ArrayList(substitutionMap.values()); + types.remove(returnType); + final PsiType parameterType = types.get(0); + return elementFactory.createType(targetClass, parameterType, returnType); + } + } }; } } \ No newline at end of file diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/BaseGuavaTypeConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/BaseGuavaTypeConversionRule.java index b2e5b1852945..8daec6de5c7d 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/BaseGuavaTypeConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/BaseGuavaTypeConversionRule.java @@ -42,7 +42,7 @@ public abstract class BaseGuavaTypeConversionRule extends TypeConversionRule { } }; - protected abstract void fillSimpleDescriptors(Map descriptorsMap); + protected void fillSimpleDescriptors(Map descriptorsMap) {}; @Nullable protected TypeConversionDescriptorBase findConversionForMethod(@NotNull PsiType from, @@ -84,7 +84,7 @@ public abstract class BaseGuavaTypeConversionRule extends TypeConversionRule { return new TypeConversionDescriptorBase() { @Override public PsiExpression replace(PsiExpression expression) throws IncorrectOperationException { - return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, false); + return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, false, true); }; }; } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java index 701f210c575b..7b07be7ae768 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java @@ -16,9 +16,13 @@ package com.intellij.refactoring.typeMigration.rules.guava; import com.intellij.codeInspection.java18StreamApi.StreamApiConstants; +import com.intellij.psi.*; import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; +import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; +import com.intellij.util.containers.hash.HashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Map; @@ -26,42 +30,57 @@ import java.util.Map; * @author Dmitry Batkovich */ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRule { + private static final Map DESCRIPTORS_MAP = new HashMap(); + public static final String FLUENT_ITERABLE = "com.google.common.collect.FluentIterable"; + + static { + DESCRIPTORS_MAP.put("contains", + new TypeConversionDescriptor("$it$.contains($o$)", "$it$.anyMatch(e -> e != null && e.equals(%s))")); + DESCRIPTORS_MAP.put("from", new TypeConversionDescriptor("FluentIterable.from($it$)", "$it$.stream()")); + DESCRIPTORS_MAP.put("isEmpty", new TypeConversionDescriptor("$q$.isEmpty()", "$q$.findAny().isPresent()")); + DESCRIPTORS_MAP.put("skip", new TypeConversionDescriptor("$q$.skip($p$)", "$q$.skip($p$)")); + DESCRIPTORS_MAP.put("limit", new TypeConversionDescriptor("$q$.limit($p$)", "$q$.limit($p$)")); + DESCRIPTORS_MAP.put("first", new TypeConversionDescriptor("$q$.first()", "$q$.findFirst()")); + DESCRIPTORS_MAP.put("transform", new LambdaParametersTypeConversionDescription("$q$.transform($params$)", "$q$.map($params$)")); + //TODO support + //DESCRIPTORS_MAP.put("transformAndConcat", new TransformAndConcatDescriptorBase("$q$.transformAndConcat($params$)", "$q$.flatMap($params$)")); + + DESCRIPTORS_MAP.put("allMatch", new LambdaParametersTypeConversionDescription("$it$.allMatch($c$)", "$it$." + StreamApiConstants.ALL_MATCH + "($c$)")); + DESCRIPTORS_MAP.put("anyMatch", new LambdaParametersTypeConversionDescription("$it$.anyMatch($c$)", "$it$." + StreamApiConstants.ANY_MATCH + "($c$)")); + + //TODO add another filter processor + DESCRIPTORS_MAP.put("filter", new LambdaParametersTypeConversionDescription("$it$.filter($p$)", "$it$." + StreamApiConstants.FILTER + "($p$)")); + DESCRIPTORS_MAP.put("first", new TypeConversionDescriptor("$it$.first()", "$it$." + StreamApiConstants.FIND_FIRST + "()")); + DESCRIPTORS_MAP.put("firstMatch", new LambdaParametersTypeConversionDescription("$it$.firstMatch($p$)", "$it$.filter($p$).findFirst()")); + DESCRIPTORS_MAP.put("get", new TypeConversionDescriptor("$it$.get($p$)", "$it$.collect(java.util.stream.Collectors.toList()).get($p$)")); + DESCRIPTORS_MAP.put("size", new TypeConversionDescriptor("$it$.size()", "$it$.collect(java.util.stream.Collectors.toList()).size()")); + + DESCRIPTORS_MAP.put("toMap", new TypeConversionDescriptor("$it$.toMap($f$)", + "$it$.collect(java.util.stream.Collectors.toMap(java.util.function.Function.identity(), $f$))")); + DESCRIPTORS_MAP.put("toList", new TypeConversionDescriptor("$it$.toList()", "$it$.collect(java.util.stream.Collectors.toList())")); + DESCRIPTORS_MAP.put("toSet", new TypeConversionDescriptor("$it$.toSet()", "$it$.collect(java.util.stream.Collectors.toSet())")); + DESCRIPTORS_MAP.put("toSortedList", new TypeConversionDescriptor("$it$.toSortedList($c$)", "$it$.sorted($c$).collect(java.util.stream.Collectors.toList())")); + DESCRIPTORS_MAP.put("toSortedSet", new TypeConversionDescriptor("$it$.toSortedSet($c$)", "$it$.sorted($c$).collect(java.util.stream.Collectors.toSet())")); + + } + + @Nullable @Override - protected void fillSimpleDescriptors(Map descriptorsMap) { - descriptorsMap.put("contains", - new TypeConversionDescriptor("$it$.contains($o$)", "$it$.anyMatch(e -> e != null && e.equals(%s))")); - descriptorsMap.put("from", new TypeConversionDescriptor("FluentIterable.from($it$)", "$it$.stream()")); - descriptorsMap.put("isEmpty", new TypeConversionDescriptor("$q$.isEmpty()", "$q$.findAny().isPresent()")); - descriptorsMap.put("skip", new TypeConversionDescriptorBase()); - descriptorsMap.put("limit", new TypeConversionDescriptorBase()); - descriptorsMap.put("transform", new LambdaParametersTypeConversionDescription("$q$.transform($params$)", "$q$.map($params$)")); - descriptorsMap.put("transformAndConcat", - new LambdaParametersTypeConversionDescription("$q$.transformAndConcat($params$)", "$q$.flatMap($params$)")); - descriptorsMap.put("first", new TypeConversionDescriptor("$q$.first()", "$q$.findFirst()")); - - descriptorsMap.put("allMatch", new TypeConversionDescriptor("$it$.allMatch($c$)", "$it$." + StreamApiConstants.ALL_MATCH + "($c$)")); - descriptorsMap.put("anyMatch", new TypeConversionDescriptor("$it$.anyMatch($c$)", "$it$." + StreamApiConstants.ANY_MATCH + "($c$)")); - - descriptorsMap.put("filter", new TypeConversionDescriptor("$it$.filter($p$)", "$it$." + StreamApiConstants.FILTER + "($p$)")); - descriptorsMap.put("first", new TypeConversionDescriptor("$it$.first()", "$it$." + StreamApiConstants.FIND_FIRST + "()")); - descriptorsMap.put("firstMatch", new TypeConversionDescriptor("$it$.firstMatch($p$)", "$it$.filter($p$).findFirst()")); - descriptorsMap.put("get", new TypeConversionDescriptor("$it$.get($p$)", "$it$.collect(java.util.stream.Collectors.toList()).get($p$)")); - descriptorsMap.put("size", new TypeConversionDescriptor("$it$.size()", "$it$.collect(java.util.stream.Collectors.toList()).size()")); - - descriptorsMap.put("toMap", new TypeConversionDescriptor("$it$.toMap($f$)", - "$it$.collect(java.util.stream.Collectors.toMap(java.util.function.Function.identity(), $f$))")); - descriptorsMap.put("toList", new TypeConversionDescriptor("$it$.toList()", "$it$.collect(java.util.stream.Collectors.toList())")); - descriptorsMap.put("toSet", new TypeConversionDescriptor("$it$.toSet()", "$it$.collect(java.util.stream.Collectors.toSet())")); - descriptorsMap.put("toSortedList", new TypeConversionDescriptor("$it$.toSortedList($c$)", "$it$.sorted($c$).collect(java.util.stream.Collectors.toList())")); - descriptorsMap.put("toSortedSet", new TypeConversionDescriptor("$it$.toSortedSet($c$)", "$it$.sorted($c$).collect(java.util.stream.Collectors.toSet())")); - + protected TypeConversionDescriptorBase findConversionForMethod(@NotNull PsiType from, + @NotNull PsiType to, + @NotNull PsiMethod method, + String methodName, + PsiExpression context, + TypeMigrationLabeler labeler) { + final TypeConversionDescriptorBase base = DESCRIPTORS_MAP.get(methodName); + return base instanceof TypeConversionDescriptor ? ((TypeConversionDescriptor)base).withConversionType(to) : null; } @NotNull @Override public String ruleFromClass() { - return "com.google.common.collect.FluentIterable"; + return FLUENT_ITERABLE; } @NotNull diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFunctionConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFunctionConversionRule.java index 058b7de7fd09..4628cefb882b 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFunctionConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFunctionConversionRule.java @@ -29,6 +29,8 @@ import java.util.Map; * @author Dmitry Batkovich */ public class GuavaFunctionConversionRule extends BaseGuavaTypeConversionRule { + public static final String JAVA_UTIL_FUNCTION_FUNCTION = "java.util.function.Function"; + @Override protected void fillSimpleDescriptors(Map descriptorsMap) { descriptorsMap.put("apply", new TypeConversionDescriptorBase()); @@ -43,6 +45,6 @@ public class GuavaFunctionConversionRule extends BaseGuavaTypeConversionRule { @NotNull @Override public String ruleToClass() { - return "java.util.function.Function"; + return JAVA_UTIL_FUNCTION_FUNCTION; } } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/LambdaParametersTypeConversionDescription.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/LambdaParametersTypeConversionDescription.java index 7bcb9f3e8e4c..c70598eeb77d 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/LambdaParametersTypeConversionDescription.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/LambdaParametersTypeConversionDescription.java @@ -27,22 +27,30 @@ import org.jetbrains.annotations.NonNls; public class LambdaParametersTypeConversionDescription extends TypeConversionDescriptor { private static final Logger LOG = Logger.getInstance(LambdaParametersTypeConversionDescription.class); + public LambdaParametersTypeConversionDescription(@NonNls String stringToReplace, @NonNls String replaceByString, PsiType conversionType) { + super(stringToReplace, replaceByString, conversionType); + } + public LambdaParametersTypeConversionDescription(@NonNls String stringToReplace, @NonNls String replaceByString) { super(stringToReplace, replaceByString); } @Override - public PsiExpression replace(PsiExpression expression) { + public final PsiExpression replace(PsiExpression expression) { LOG.assertTrue(expression instanceof PsiMethodCallExpression); PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression; final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions(); if (arguments.length == 1) { final PsiExpression functionArg = arguments[0]; - convertParameter(functionArg); + customizeParameter(convertParameter(functionArg)); } return super.replace(expression); } + protected void customizeParameter(PsiExpression parameter) { + + } + private static PsiExpression addApplyReference(final PsiExpression expression) { return (PsiExpression)expression.replace(JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(expression.getText() + "::apply", null)); } @@ -52,7 +60,7 @@ public class LambdaParametersTypeConversionDescription extends TypeConversionDes final PsiAnonymousClass anonymousClass = ((PsiNewExpression)expression).getAnonymousClass(); if (anonymousClass != null) { if (AnonymousCanBeLambdaInspection.canBeConvertedToLambda(anonymousClass, true)) { - AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true); + AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true, true); } } else {