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 9c227fb09d33..4129b6172534 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 @@ -17,7 +17,6 @@ package com.intellij.refactoring.typeMigration.rules.guava; import com.intellij.codeInspection.AnonymousCanBeLambdaInspection; import com.intellij.psi.*; -import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; import com.intellij.refactoring.typeMigration.rules.TypeConversionRule; @@ -27,8 +26,6 @@ import com.intellij.util.containers.hash.HashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; import java.util.Map; /** @@ -59,7 +56,8 @@ public abstract class BaseGuavaTypeConversionRule extends TypeConversionRule { @Nullable protected TypeConversionDescriptorBase findConversionForVariableReference(@NotNull PsiReferenceExpression referenceExpression, - @NotNull PsiVariable psiVariable) { + @NotNull PsiVariable psiVariable, + @Nullable PsiExpression context) { return null; } @@ -101,7 +99,7 @@ public abstract class BaseGuavaTypeConversionRule extends TypeConversionRule { else if (context instanceof PsiReferenceExpression) { final PsiElement resolvedElement = ((PsiReferenceExpression)context).resolve(); if (resolvedElement instanceof PsiVariable) { - return findConversionForVariableReference((PsiReferenceExpression)context, (PsiVariable)resolvedElement); + return findConversionForVariableReference((PsiReferenceExpression)context, (PsiVariable)resolvedElement, context); } } return null; 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 4757e3a31a8d..35b283351199 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 @@ -15,6 +15,7 @@ */ package com.intellij.refactoring.typeMigration.rules.guava; +import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiReferenceExpression; import com.intellij.psi.PsiVariable; import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; @@ -39,7 +40,7 @@ public class GuavaFunctionConversionRule extends BaseGuavaTypeConversionRule { @Nullable @Override protected TypeConversionDescriptorBase findConversionForVariableReference(@NotNull PsiReferenceExpression referenceExpression, - @NotNull PsiVariable psiVariable) { + @NotNull PsiVariable psiVariable, PsiExpression context) { return new TypeConversionDescriptor("$f$", "$f$::apply"); } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionRule.java index 4e358bba4d7e..9a9fb5ec4c29 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionRule.java @@ -32,6 +32,7 @@ import java.util.Map; public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule { private final static Logger LOG = Logger.getInstance(GuavaOptionalConversionRule.class); + public final static String OPTIONAL_CONVERTOR_PATTERN = "Optional.fromNullable($o$.orElse(null))"; public final static String GUAVA_OPTIONAL = "com.google.common.base.Optional"; public final static String JAVA_OPTIONAL = "java.util.Optional"; @@ -46,17 +47,7 @@ public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule { if (!(context instanceof PsiMethodCallExpression)) { if ("or".equals(methodName)) { PsiMethodCallExpression methodCallExpression = null; - if (context instanceof PsiMethodCallExpression) { - final PsiClass aClass = getParameterClass(method); - if (aClass != null) { - final String qName = aClass.getQualifiedName(); - String pattern = - GUAVA_OPTIONAL.equals(qName) ? "java.util.Optional.ofNullable($expr$.get())" : "java.util.Optional.ofNullable($expr$)"; - return new TypeConversionDescriptor("$expr$", pattern); - } - return null; - } - else if (context.getParent() instanceof PsiMethodCallExpression) { + if (context.getParent() instanceof PsiMethodCallExpression) { methodCallExpression = (PsiMethodCallExpression)context.getParent(); } if (methodCallExpression == null) { @@ -67,7 +58,15 @@ public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule { final String qName = aClass.getQualifiedName(); if (GUAVA_OPTIONAL.equals(qName)) { TypeConversionDescriptor descriptor = - new TypeConversionDescriptor("$val$.or($other$)", "java.util.Optional.ofNullable($val$.orElseGet($other$::get))"); + new TypeConversionDescriptor(null, "java.util.Optional.ofNullable($val$.orElseGet($o$::get))") { + @Override + public PsiExpression replace(PsiExpression expression) { + setStringToReplace("$val$.or(" + + GuavaOptionalConversionUtil.simplifyParameterPattern((PsiMethodCallExpression)expression) + + ")"); + return super.replace(expression); + } + }; if (to != null) { descriptor.withConversionType(to); } @@ -80,7 +79,7 @@ public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule { return null; } else if ("transform".equals(methodName)) { - final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)(context instanceof PsiMethodCallExpression ? context : context.getParent()); + final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)(context.getParent()); final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions(); if (arguments.length != 1) { return null; @@ -107,7 +106,11 @@ public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule { @Nullable @Override protected TypeConversionDescriptorBase findConversionForVariableReference(@NotNull PsiReferenceExpression referenceExpression, - @NotNull PsiVariable psiVariable) { + @NotNull PsiVariable psiVariable, + @Nullable PsiExpression context) { + if (GuavaOptionalConversionUtil.isOptionalOrContext(context)) { + return new TypeConversionDescriptor("$o$", "com.google.common.base." + OPTIONAL_CONVERTOR_PATTERN); + } return new TypeConversionDescriptor("$o$", "$o$::get"); } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionUtil.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionUtil.java new file mode 100644 index 000000000000..828e58b2402c --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionUtil.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.refactoring.typeMigration.rules.guava; + +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiMatcherExpression; +import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; +import com.intellij.structuralsearch.MatchOptions; +import com.intellij.structuralsearch.MatchResult; +import com.intellij.structuralsearch.Matcher; +import com.intellij.structuralsearch.plugin.replace.impl.Replacer; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Dmitry Batkovich + */ +public class GuavaOptionalConversionUtil { + static boolean isOptionalOrContext(@Nullable PsiExpression context) { + if (context == null) return false; + final PsiElement parent = context.getParent(); + if (parent == null) return false; + final PsiElement maybeMethodCall = parent.getParent(); + if (!(maybeMethodCall instanceof PsiMethodCallExpression)) return false; + final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)maybeMethodCall; + final int argumentLength = methodCall.getArgumentList().getExpressions().length; + if (argumentLength != 1) return false; + final PsiMethod resolvedMethod = methodCall.resolveMethod(); + if (resolvedMethod == null || !"or".equals(resolvedMethod.getName())) return false; + final PsiClass aClass = resolvedMethod.getContainingClass(); + return aClass != null && GuavaOptionalConversionRule.GUAVA_OPTIONAL.equals(aClass.getQualifiedName()); + } + + static String simplifyParameterPattern(PsiMethodCallExpression methodCall) { + final PsiExpressionList argumentList = methodCall.getArgumentList(); + final PsiExpression[] expressions = argumentList.getExpressions(); + if (expressions.length == 1) { + final PsiExpression expression = expressions[0]; + Matcher matcher = new Matcher(methodCall.getProject()); + final MatchOptions options = new MatchOptions(); + options.setFileType(StdFileTypes.JAVA); + final List results = + matcher.testFindMatches(expression.getText(), GuavaOptionalConversionRule.OPTIONAL_CONVERTOR_PATTERN, options, false); + if (!results.isEmpty()) { + final MatchResult result = results.get(0); + if (result.getStart() == 0 && result.getEnd() == -1) { + return GuavaOptionalConversionRule.OPTIONAL_CONVERTOR_PATTERN; + } + } + } + return "$o$"; + } +} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaPredicateConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaPredicateConversionRule.java index 3fdb2e50123d..6848ccd8d61a 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaPredicateConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaPredicateConversionRule.java @@ -15,6 +15,7 @@ */ package com.intellij.refactoring.typeMigration.rules.guava; +import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiReferenceExpression; import com.intellij.psi.PsiVariable; import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; @@ -39,7 +40,7 @@ public class GuavaPredicateConversionRule extends BaseGuavaTypeConversionRule { @Nullable @Override protected TypeConversionDescriptorBase findConversionForVariableReference(@NotNull PsiReferenceExpression referenceExpression, - @NotNull PsiVariable psiVariable) { + @NotNull PsiVariable psiVariable, PsiExpression context) { return new TypeConversionDescriptor("$p$", "$p$::test"); } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaSupplierConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaSupplierConversionRule.java index ecf89cf091e4..ed1b3efa448c 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaSupplierConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaSupplierConversionRule.java @@ -15,6 +15,7 @@ */ package com.intellij.refactoring.typeMigration.rules.guava; +import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiReferenceExpression; import com.intellij.psi.PsiVariable; import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; @@ -39,7 +40,7 @@ public class GuavaSupplierConversionRule extends BaseGuavaTypeConversionRule { @Nullable @Override protected TypeConversionDescriptorBase findConversionForVariableReference(@NotNull PsiReferenceExpression referenceExpression, - @NotNull PsiVariable psiVariable) { + @NotNull PsiVariable psiVariable, PsiExpression context) { return new TypeConversionDescriptor("$f$", "$f$::get"); } diff --git a/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java b/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java index 2d0bd5c92d85..8ac65d0a2ae8 100644 --- a/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java +++ b/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java @@ -198,6 +198,10 @@ public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase { doTest(); } + public void testSimplifyOptionalComposition() { + doTest(); + } + private void doTestNoQuickFixes(final Class... quickFixesClasses) { myFixture.configureByFile(getTestName(true) + ".java"); myFixture.enableInspections(new GuavaInspection()); diff --git a/java/typeMigration/testData/inspections/guava/simplifyOptionalComposition.java b/java/typeMigration/testData/inspections/guava/simplifyOptionalComposition.java new file mode 100644 index 000000000000..a0011fef634c --- /dev/null +++ b/java/typeMigration/testData/inspections/guava/simplifyOptionalComposition.java @@ -0,0 +1,7 @@ +import com.google.common.base.Optional; + +class SecondChoice { + public void ors(Optional p1, java.util.Optional p2) { + Optional o = p1.or(Optional.fromNullable(p2.orElse(null))); + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/inspections/guava/simplifyOptionalComposition_after.java b/java/typeMigration/testData/inspections/guava/simplifyOptionalComposition_after.java new file mode 100644 index 000000000000..15e86a6a6f61 --- /dev/null +++ b/java/typeMigration/testData/inspections/guava/simplifyOptionalComposition_after.java @@ -0,0 +1,7 @@ +import java.util.Optional; + +class SecondChoice { + public void ors(Optional p1, java.util.Optional p2) { + java.util.Optional o = java.util.Optional.ofNullable(p1.orElseGet(p2::get)); + } +} \ No newline at end of file