From 0393b4fe2f1cf081588bd4aa785ba0708bb9379a Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 23 Nov 2017 11:52:59 +0700 Subject: [PATCH] IDEA-182206 Simplification for Arrays.asList().sublist().toArray() --- .../RedundantCollectionOperation.html | 7 + .../afterAsListToArraySimple.java | 10 + .../afterAsListToArraySubList.java | 10 + .../afterAsListToArraySubListDiff.java | 10 + .../afterAsListToArraySubListZero.java | 10 + .../afterAsListToArraySubListZeroObject.java | 10 + .../beforeAsListToArraySimple.java | 10 + .../beforeAsListToArraySubList.java | 10 + .../beforeAsListToArraySubListDiff.java | 10 + ...eforeAsListToArraySubListSizeMismatch.java | 10 + .../beforeAsListToArraySubListZero.java | 10 + .../beforeAsListToArraySubListZeroObject.java | 10 + .../beforeAsListToArrayTypeMismatch.java | 11 + ...dantCollectionOperationInspectionTest.java | 25 +++ .../siyeh/ig/psiutils/ExpressionUtils.java | 31 +++ ...edundantCollectionOperationInspection.java | 210 ++++++++++++++++++ 16 files changed, 394 insertions(+) create mode 100644 java/java-impl/src/inspectionDescriptions/RedundantCollectionOperation.html create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySimple.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubList.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListDiff.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListZero.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListZeroObject.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySimple.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubList.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListDiff.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListSizeMismatch.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListZero.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListZeroObject.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArrayTypeMismatch.java create mode 100644 java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RedundantCollectionOperationInspectionTest.java create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/redundancy/RedundantCollectionOperationInspection.java diff --git a/java/java-impl/src/inspectionDescriptions/RedundantCollectionOperation.html b/java/java-impl/src/inspectionDescriptions/RedundantCollectionOperation.html new file mode 100644 index 000000000000..48648ae3cee4 --- /dev/null +++ b/java/java-impl/src/inspectionDescriptions/RedundantCollectionOperation.html @@ -0,0 +1,7 @@ + + +Reports unnecessarily complex collection operations which have simpler alternatives. + +New in 2018.1 + + \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySimple.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySimple.java new file mode 100644 index 000000000000..69f5a3bcaf55 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySimple.java @@ -0,0 +1,10 @@ +// "Replace with 'clone()'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + String[] get() { + return arr.clone(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubList.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubList.java new file mode 100644 index 000000000000..e96e9aad92ee --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubList.java @@ -0,0 +1,10 @@ +// "Replace with 'Arrays.copyOfRange'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + String[] get() { + return Arrays.copyOfRange(arr, 1, 3); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListDiff.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListDiff.java new file mode 100644 index 000000000000..a69959cd91e7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListDiff.java @@ -0,0 +1,10 @@ +// "Replace with 'Arrays.copyOfRange'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + CharSequence[] get(int from, int to) { + return Arrays.copyOfRange(arr, from, to, CharSequence[].class); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListZero.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListZero.java new file mode 100644 index 000000000000..dc686f255973 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListZero.java @@ -0,0 +1,10 @@ +// "Replace with 'Arrays.copyOf'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + String[] get(int newLength) { + return Arrays.copyOf(arr, newLength); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListZeroObject.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListZeroObject.java new file mode 100644 index 000000000000..4f95fb6c663c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/afterAsListToArraySubListZeroObject.java @@ -0,0 +1,10 @@ +// "Replace with 'Arrays.copyOf'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + Object[] get(int newLength) { + return Arrays.copyOf(arr, newLength, Object[].class); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySimple.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySimple.java new file mode 100644 index 000000000000..86d9b7b835a9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySimple.java @@ -0,0 +1,10 @@ +// "Replace with 'clone()'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + String[] get() { + return Arrays.asList(arr).toArray(new String[0]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubList.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubList.java new file mode 100644 index 000000000000..482136a0d4b0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubList.java @@ -0,0 +1,10 @@ +// "Replace with 'Arrays.copyOfRange'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + String[] get() { + return Arrays.asList(arr).subList(1, 3).toArray(new String[2]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListDiff.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListDiff.java new file mode 100644 index 000000000000..d03a0aa1ce6e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListDiff.java @@ -0,0 +1,10 @@ +// "Replace with 'Arrays.copyOfRange'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + CharSequence[] get(int from, int to) { + return Arrays.asList(arr).subList(from, to).toArray(new CharSequence[to-from]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListSizeMismatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListSizeMismatch.java new file mode 100644 index 000000000000..b8eb519f34a7 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListSizeMismatch.java @@ -0,0 +1,10 @@ +// "Replace with 'Arrays.copyOfRange'" "false" +import java.util.Arrays; + +class Test { + String[] arr; + + String[] get() { + return Arrays.asList(arr).subList(1, 3).toArray(new String[3]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListZero.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListZero.java new file mode 100644 index 000000000000..2e96c83777cd --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListZero.java @@ -0,0 +1,10 @@ +// "Replace with 'Arrays.copyOf'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + String[] get(int newLength) { + return Arrays.asList(arr).subList(0, newLength).toArray(new String[newLength]); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListZeroObject.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListZeroObject.java new file mode 100644 index 000000000000..559c8e935d52 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArraySubListZeroObject.java @@ -0,0 +1,10 @@ +// "Replace with 'Arrays.copyOf'" "true" +import java.util.Arrays; + +class Test { + String[] arr; + + Object[] get(int newLength) { + return Arrays.asList(arr).subList(0, newLength).toArray(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArrayTypeMismatch.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArrayTypeMismatch.java new file mode 100644 index 000000000000..42b25632e284 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation/beforeAsListToArrayTypeMismatch.java @@ -0,0 +1,11 @@ +// "Replace with 'clone()'" "false" +import java.util.Arrays; + +class Test { + String[] arr; + + CharSequence[] get() { + // Array type mismatch + return Arrays.asList(arr).toArray(new CharSequence[0]); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RedundantCollectionOperationInspectionTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RedundantCollectionOperationInspectionTest.java new file mode 100644 index 000000000000..502018485ce0 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/quickFix/RedundantCollectionOperationInspectionTest.java @@ -0,0 +1,25 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.java.codeInsight.daemon.quickFix; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; +import com.intellij.codeInspection.LocalInspectionTool; +import com.siyeh.ig.redundancy.RedundantCollectionOperationInspection; +import org.jetbrains.annotations.NotNull; + + +public class RedundantCollectionOperationInspectionTest extends LightQuickFixParameterizedTestCase { + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return new LocalInspectionTool[]{ + new RedundantCollectionOperationInspection() + }; + } + + public void test() { doAllTests(); } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/redundantCollectionOperation"; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java index ba085a453e52..1242351bbaa2 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java @@ -17,6 +17,7 @@ package com.siyeh.ig.psiutils; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.NullableNotNullManager; +import com.intellij.codeInsight.PsiEquivalenceUtil; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.search.searches.ReferencesSearch; @@ -38,6 +39,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; import java.util.stream.Stream; +import static com.intellij.util.ObjectUtils.tryCast; + public class ExpressionUtils { @NonNls static final Set convertableBoxedClassNames = new HashSet<>(3); static { @@ -1142,4 +1145,32 @@ public class ExpressionUtils { } return false; } + + /** + * Checks whether diff-expression represents a difference between from-expression and to-expression + * + * @param from from-expression + * @param to to-expression + * @param diff diff-expression + * @return true if diff = to - from + */ + public static boolean isDifference(@NotNull PsiExpression from, @NotNull PsiExpression to, @NotNull PsiExpression diff) { + diff = PsiUtil.skipParenthesizedExprDown(diff); + if (diff == null) return false; + if (isZero(from) && PsiEquivalenceUtil.areElementsEquivalent(to, diff)) return true; + if (diff instanceof PsiBinaryExpression && ((PsiBinaryExpression)diff).getOperationTokenType().equals(JavaTokenType.MINUS)) { + PsiExpression left = ((PsiBinaryExpression)diff).getLOperand(); + PsiExpression right = ((PsiBinaryExpression)diff).getROperand(); + if (right != null && PsiEquivalenceUtil.areElementsEquivalent(to, left) && PsiEquivalenceUtil.areElementsEquivalent(from, right)) { + return true; + } + } + Integer fromConstant = tryCast(computeConstantExpression(from), Integer.class); + if (fromConstant == null) return false; + Integer toConstant = tryCast(computeConstantExpression(to), Integer.class); + if (toConstant == null) return false; + Integer diffConstant = tryCast(computeConstantExpression(diff), Integer.class); + if (diffConstant == null) return false; + return diffConstant == toConstant - fromConstant; + } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/redundancy/RedundantCollectionOperationInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/redundancy/RedundantCollectionOperationInspection.java new file mode 100644 index 000000000000..69c1f3123d8e --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/redundancy/RedundantCollectionOperationInspection.java @@ -0,0 +1,210 @@ +// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.siyeh.ig.redundancy; + +import com.intellij.codeInsight.PsiEquivalenceUtil; +import com.intellij.codeInspection.*; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.ArrayUtil; +import com.siyeh.ig.callMatcher.CallMapper; +import com.siyeh.ig.callMatcher.CallMatcher; +import com.siyeh.ig.psiutils.CommentTracker; +import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.psiutils.MethodCallUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Function; + +import static com.intellij.util.ObjectUtils.tryCast; +import static com.siyeh.ig.callMatcher.CallMatcher.*; + +public class RedundantCollectionOperationInspection extends AbstractBaseJavaLocalInspectionTool implements CleanupLocalInspectionTool { + private static final CallMatcher TO_ARRAY = + anyOf( + instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "toArray").parameterCount(0), + instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "toArray").parameterTypes("T[]")); + private static final CallMatcher SUBLIST = + instanceCall(CommonClassNames.JAVA_UTIL_LIST, "subList").parameterTypes("int", "int"); + private static final CallMatcher AS_LIST = + staticCall(CommonClassNames.JAVA_UTIL_ARRAYS, "asList").parameterCount(1); + + private static final CallMapper HANDLERS = + new CallMapper() + .register(TO_ARRAY, SimplifyToArrayHandler.handler()); + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + if (!PsiUtil.isLanguageLevel6OrHigher(holder.getFile())) { + return PsiElementVisitor.EMPTY_VISITOR; + } + return new JavaElementVisitor() { + @Override + public void visitMethodCallExpression(PsiMethodCallExpression call) { + PsiElement nameElement = call.getMethodExpression().getReferenceNameElement(); + if (nameElement == null) return; + RedundantCollectionOperationHandler handler = HANDLERS.mapFirst(call); + if (handler == null) return; + holder.registerProblem(nameElement, handler.getProblemName(), new RedundantCollectionOperationFix(handler)); + } + }; + } + + interface RedundantCollectionOperationHandler { + + String getProblemName(); + + String getFixName(); + + void performFix(@NotNull Project project, @NotNull PsiMethodCallExpression call); + } + + private static class SimplifyToArrayHandler implements RedundantCollectionOperationHandler { + private final String myReplacementMethod; + @NotNull private final SmartPsiElementPointer myArrayPtr; + private final SmartPsiElementPointer myFromPtr; + private final SmartPsiElementPointer myToPtr; + @NotNull private final String mySourceComponentType; + @NotNull private final String myTargetComponentType; + + private SimplifyToArrayHandler(PsiExpression from, + PsiExpression to, + @NotNull PsiExpression array, + @NotNull String sourceComponentType, + @NotNull String targetComponentType) { + SmartPointerManager manager = SmartPointerManager.getInstance(array.getProject()); + myArrayPtr = manager.createSmartPsiElementPointer(array); + myFromPtr = from == null ? null : manager.createSmartPsiElementPointer(from); + myToPtr = to == null ? null : manager.createSmartPsiElementPointer(to); + mySourceComponentType = sourceComponentType; + myTargetComponentType = targetComponentType; + if (from == null && to == null) { + myReplacementMethod = "clone()"; + } + else if (ExpressionUtils.isZero(from)) { + myReplacementMethod = "Arrays.copyOf"; + } + else { + myReplacementMethod = "Arrays.copyOfRange"; + } + } + + @Override + public String getProblemName() { + return "Unnecessary collection created to copy an array"; + } + + @Override + public String getFixName() { + return "Replace with '" + myReplacementMethod + "'"; + } + + @Override + public void performFix(@NotNull Project project, @NotNull PsiMethodCallExpression call) { + PsiExpression array = myArrayPtr.getElement(); + if (array == null) return; + PsiExpression from = myFromPtr == null ? null : myFromPtr.getElement(); + PsiExpression to = myToPtr == null ? null : myToPtr.getElement(); + if ((from == null) != (to == null)) return; + CommentTracker ct = new CommentTracker(); + String replacement; + String suffix = ""; + if (!mySourceComponentType.equals(myTargetComponentType)) { + suffix = "," + myTargetComponentType + "[].class"; + } + if (from == null) { + replacement = ParenthesesUtils.getText(ct.markUnchanged(array), ParenthesesUtils.POSTFIX_PRECEDENCE) + ".clone()"; + } + else if (ExpressionUtils.isZero(from)) { + replacement = CommonClassNames.JAVA_UTIL_ARRAYS + ".copyOf(" + ct.text(array) + "," + ct.text(to) + suffix + ")"; + } + else { + replacement = + CommonClassNames.JAVA_UTIL_ARRAYS + ".copyOfRange(" + ct.text(array) + "," + ct.text(from) + "," + ct.text(to) + suffix + ")"; + } + ct.replaceAndRestoreComments(call, replacement); + } + + public static Function handler() { + return call -> { + PsiExpression arg = ArrayUtil.getFirstElement(call.getArgumentList().getExpressions()); + PsiExpression arrayLength = null; + String targetComponentType; + if (arg != null) { + if (!(arg instanceof PsiNewExpression)) return null; + PsiJavaCodeReferenceElement classRef = ((PsiNewExpression)arg).getClassReference(); + if (classRef == null) return null; + targetComponentType = classRef.getQualifiedName(); + PsiExpression[] dimensions = ((PsiNewExpression)arg).getArrayDimensions(); + if (dimensions.length != 1) return null; + if (!ExpressionUtils.isZero(dimensions[0])) { + arrayLength = dimensions[0]; + } + } + else { + targetComponentType = CommonClassNames.JAVA_LANG_OBJECT; + } + PsiExpression from = null; + PsiExpression to = null; + PsiMethodCallExpression qualifier = MethodCallUtils.getQualifierMethodCall(call); + if (SUBLIST.test(qualifier)) { + PsiExpression[] subListArgs = qualifier.getArgumentList().getExpressions(); + from = subListArgs[0]; + to = subListArgs[1]; + qualifier = MethodCallUtils.getQualifierMethodCall(qualifier); + } + if (!AS_LIST.test(qualifier) || MethodCallUtils.isVarArgCall(qualifier)) return null; + PsiExpression array = qualifier.getArgumentList().getExpressions()[0]; + PsiArrayType sourceArrayType = tryCast(array.getType(), PsiArrayType.class); + if (sourceArrayType == null) return null; + PsiClass componentClass = PsiUtil.resolveClassInClassTypeOnly(sourceArrayType.getComponentType()); + if (componentClass == null) return null; + String sourceComponentType = componentClass.getQualifiedName(); + if (sourceComponentType == null) return null; + if (from != null && to != null) { + if (arrayLength != null && !ExpressionUtils.isDifference(from, to, arrayLength)) return null; + } + else { + if (!sourceComponentType.equals(targetComponentType)) return null; + if (arrayLength != null) { + PsiExpression arrayFromLength = ExpressionUtils.getArrayFromLengthExpression(arrayLength); + if (arrayFromLength == null || !PsiEquivalenceUtil.areElementsEquivalent(array, arrayFromLength)) return null; + } + } + return new SimplifyToArrayHandler(from, to, array, sourceComponentType, targetComponentType); + }; + } + } + + private static class RedundantCollectionOperationFix implements LocalQuickFix { + private RedundantCollectionOperationHandler myHandler; + + public RedundantCollectionOperationFix( + RedundantCollectionOperationHandler handler) {myHandler = handler;} + + @Nls + @NotNull + @Override + public String getName() { + return myHandler.getFixName(); + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Simplify collection operation"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiMethodCallExpression.class); + if (call == null) return; + myHandler.performFix(project, call); + } + } +}