diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java index eaa2123506b7..3720fa755191 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationLabeler.java @@ -384,6 +384,8 @@ public class TypeMigrationLabeler { return false; } + type = JavaPsiFacade.getElementFactory(element.getProject()).createTypeFromText(type.getCanonicalText(), element); + if (resolved instanceof PsiMethod) { final PsiMethod method = ((PsiMethod)resolved); final PsiMethod[] methods = OverridingMethodsSearch.search(method, true).toArray(PsiMethod.EMPTY_ARRAY); diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeConversionDescriptor.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeConversionDescriptor.java index e1a2bd8b5ff3..bc55ba235a55 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeConversionDescriptor.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/TypeConversionDescriptor.java @@ -39,7 +39,7 @@ public class TypeConversionDescriptor extends TypeConversionDescriptorBase { myExpression = expression; } - public TypeConversionDescriptor(String stringToReplace, String replaceByString, PsiType conversionType) { + public TypeConversionDescriptor(@NonNls final String stringToReplace, @NonNls final String replaceByString, PsiType conversionType) { this(stringToReplace, replaceByString); myConversionType = conversionType; } 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 7b07be7ae768..23af27040fcf 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 @@ -21,6 +21,7 @@ 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.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,38 +31,69 @@ import java.util.Map; * @author Dmitry Batkovich */ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRule { - private static final Map DESCRIPTORS_MAP = new HashMap(); + private static final Map DESCRIPTORS_MAP = + new HashMap(); public static final String FLUENT_ITERABLE = "com.google.common.collect.FluentIterable"; + private static class TypeConversionDescriptorFactory { + private final String myStringToReplace; + private final String myReplaceByString; + private final boolean myWithLambdaParameter; + private final boolean myChainedMethod; + + public TypeConversionDescriptorFactory(String stringToReplace, String replaceByString, boolean withLambdaParameter) { + this(stringToReplace, replaceByString, withLambdaParameter, false); + } + + public TypeConversionDescriptorFactory(@NonNls final String stringToReplace, + @NonNls final String replaceByString, + boolean withLambdaParameter, + boolean chainedMethod) { + myStringToReplace = stringToReplace; + myReplaceByString = replaceByString; + myWithLambdaParameter = withLambdaParameter; + myChainedMethod = chainedMethod; + } + + public TypeConversionDescriptor create() { + return myWithLambdaParameter ? new LambdaParametersTypeConversionDescription(myStringToReplace, myReplaceByString) + : new TypeConversionDescriptor(myStringToReplace, myReplaceByString); + } + + public boolean isChainedMethod() { + return myChainedMethod; + } + } + 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$)")); + new TypeConversionDescriptorFactory("$it$.contains($o$)", "$it$.anyMatch(e -> e != null && e.equals($o$))", false)); + DESCRIPTORS_MAP.put("from", new TypeConversionDescriptorFactory("FluentIterable.from($it$)", "$it$.stream()", false, true)); + DESCRIPTORS_MAP.put("isEmpty", new TypeConversionDescriptorFactory("$q$.isEmpty()", "$q$.findAny().isPresent()", false)); + DESCRIPTORS_MAP.put("skip", new TypeConversionDescriptorFactory("$q$.skip($p$)", "$q$.skip($p$)", false, true)); + DESCRIPTORS_MAP.put("limit", new TypeConversionDescriptorFactory("$q$.limit($p$)", "$q$.limit($p$)", false, true)); + DESCRIPTORS_MAP.put("first", new TypeConversionDescriptorFactory("$q$.first()", "$q$.findFirst()", false)); + DESCRIPTORS_MAP.put("transform", new TypeConversionDescriptorFactory("$q$.transform($params$)", "$q$.map($params$)", true, true)); //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$)")); + DESCRIPTORS_MAP.put("allMatch", new TypeConversionDescriptorFactory("$it$.allMatch($c$)", "$it$." + StreamApiConstants.ALL_MATCH + "($c$)", true)); + DESCRIPTORS_MAP.put("anyMatch", new TypeConversionDescriptorFactory("$it$.anyMatch($c$)", "$it$." + StreamApiConstants.ANY_MATCH + "($c$)", true)); //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("filter", new TypeConversionDescriptorFactory("$it$.filter($p$)", "$it$." + StreamApiConstants.FILTER + "($p$)", true, true)); + DESCRIPTORS_MAP.put("first", new TypeConversionDescriptorFactory("$it$.first()", "$it$." + StreamApiConstants.FIND_FIRST + "()", false)); + DESCRIPTORS_MAP.put("firstMatch", new TypeConversionDescriptorFactory("$it$.firstMatch($p$)", "$it$.filter($p$).findFirst()", true)); + DESCRIPTORS_MAP.put("get", new TypeConversionDescriptorFactory("$it$.get($p$)", "$it$.collect(java.util.stream.Collectors.toList()).get($p$)", false)); + DESCRIPTORS_MAP.put("size", new TypeConversionDescriptorFactory("$it$.size()", "$it$.collect(java.util.stream.Collectors.toList()).size()", false)); - 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())")); + DESCRIPTORS_MAP.put("toMap", new TypeConversionDescriptorFactory("$it$.toMap($f$)", + "$it$.collect(java.util.stream.Collectors.toMap(java.util.function.Function.identity(), $f$))", false)); + DESCRIPTORS_MAP.put("toList", new TypeConversionDescriptorFactory("$it$.toList()", "$it$.collect(java.util.stream.Collectors.toList())", false)); + DESCRIPTORS_MAP.put("toSet", new TypeConversionDescriptorFactory("$it$.toSet()", "$it$.collect(java.util.stream.Collectors.toSet())", false)); + DESCRIPTORS_MAP.put("toSortedList", new TypeConversionDescriptorFactory("$it$.toSortedList($c$)", "$it$.sorted($c$).collect(java.util.stream.Collectors.toList())", false)); + DESCRIPTORS_MAP.put("toSortedSet", new TypeConversionDescriptorFactory("$it$.toSortedSet($c$)", "$it$.sorted($c$).collect(java.util.stream.Collectors.toSet())", false)); } @@ -73,8 +105,17 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu String methodName, PsiExpression context, TypeMigrationLabeler labeler) { - final TypeConversionDescriptorBase base = DESCRIPTORS_MAP.get(methodName); - return base instanceof TypeConversionDescriptor ? ((TypeConversionDescriptor)base).withConversionType(to) : null; + final TypeConversionDescriptorFactory base = DESCRIPTORS_MAP.get(methodName); + if (base != null) { + final TypeConversionDescriptor descriptor = base.create(); + if (base.isChainedMethod()) { + descriptor.withConversionType(to); + } + return descriptor; + } + else { + return null; + } } @NotNull diff --git a/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java b/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java index 70643e91f4e4..9ad74b943429 100644 --- a/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java +++ b/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java @@ -47,15 +47,27 @@ public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase { moduleBuilder.addJdk(IdeaTestUtil.getMockJdk18Path().getPath()); } - public void _testOptional() { + public void testOptional() { doTest(); } - public void _testOptional2() { + public void testOptional2() { doTest(); } - public void _testSimpleFluentIterable() { + public void testSimpleFluentIterable() { + doTest(); + } + + public void testChainedFluentIterable() { + doTest(); + } + + public void _testFluentIterableChainWithoutVariable() { + doTest(); + } + + public void _testChainedFluentIterableWithChainedInitializer() { doTest(); } diff --git a/java/typeMigration/testData/inspections/guava/chainedFluentIterable.java b/java/typeMigration/testData/inspections/guava/chainedFluentIterable.java new file mode 100644 index 000000000000..faaf193c4b2d --- /dev/null +++ b/java/typeMigration/testData/inspections/guava/chainedFluentIterable.java @@ -0,0 +1,27 @@ +import com.google.common.base.Predicate; +import com.google.common.base.Function; +import com.google.common.collect.FluentIterable; + +import java.util.ArrayList; +import java.util.List; + +class A { + void c() { + ArrayList strings = new ArrayList(); + FluentIterable it = FluentIterable.from(strings); + + List booleans = it.transform(String::isEmpty).toList(); + + boolean empty = it.transform(s -> s.trim()).transform(new Function() { + @Override + public char[] apply(String input) { + return input.toCharArray(); + } + }).skip(777).filter(new Predicate() { + @Override + public boolean apply(char[] input) { + return input.length != 10; + } + }).isEmpty(); + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/inspections/guava/chainedFluentIterable_after.java b/java/typeMigration/testData/inspections/guava/chainedFluentIterable_after.java new file mode 100644 index 000000000000..0cfd483d065f --- /dev/null +++ b/java/typeMigration/testData/inspections/guava/chainedFluentIterable_after.java @@ -0,0 +1,15 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class A { + void c() { + ArrayList strings = new ArrayList(); + Stream it = strings.stream(); + + List booleans = it.map(String::isEmpty).collect(Collectors.toList()); + + boolean empty = it.map(s -> s.trim()).map(input -> input.toCharArray()).skip(777).filter(input -> input.length != 10).findAny().isPresent(); + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/inspections/guava/optional2_after.java b/java/typeMigration/testData/inspections/guava/optional2_after.java index e69de29bb2d1..5166fb2c6ec1 100644 --- a/java/typeMigration/testData/inspections/guava/optional2_after.java +++ b/java/typeMigration/testData/inspections/guava/optional2_after.java @@ -0,0 +1,17 @@ +import java.util.Optional; + +class A { + + java.util.Optional m1() { + Optional o = Optional.empty(); + String s = o.get(); + return o; + } + + void w1() { + java.util.Optional someVar = m1(); + System.out.println(someVar.get()); + System.out.println(someVar.isPresent() ? Collections.singleton(someVar.get()) : Collections.emptySet()); + } + +} \ No newline at end of file diff --git a/java/typeMigration/testData/inspections/guava/simpleFluentIterable.java b/java/typeMigration/testData/inspections/guava/simpleFluentIterable.java index 4d8950f335c5..2702ad10e0cf 100644 --- a/java/typeMigration/testData/inspections/guava/simpleFluentIterable.java +++ b/java/typeMigration/testData/inspections/guava/simpleFluentIterable.java @@ -15,7 +15,7 @@ class A { List main(String[] args) { ArrayList strings = new ArrayList(); - FluentIterable it = FluentIterable.from(strings); + FluentIterable it = FluentIterable.from(strings); it = it.transform(input -> input.intern()); it = it.transform(String::trim); it = it.transform(new Function() { diff --git a/java/typeMigration/testData/inspections/guava/simpleFluentIterable_after.java b/java/typeMigration/testData/inspections/guava/simpleFluentIterable_after.java index 4d16b0e25980..d6f0846d5adb 100644 --- a/java/typeMigration/testData/inspections/guava/simpleFluentIterable_after.java +++ b/java/typeMigration/testData/inspections/guava/simpleFluentIterable_after.java @@ -20,8 +20,8 @@ class A { it = it.map(input -> input.intern()); it = it.map(String::trim); it = it.map(input -> { - System.out.println("do some action on " + input); - return input.substring(0, 10); + System.out.println("do some action on " + input); + return input.substring(0, 10); }); it = it.map(myFunction::apply); return it.collect(Collectors.toList());