diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompareToToEqualsIntention.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompareToToEqualsIntention.java new file mode 100644 index 000000000000..88cd821cd19c --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertCompareToToEqualsIntention.java @@ -0,0 +1,171 @@ +/* + * Copyright 2000-2014 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.codeInsight.intention.impl; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Dmitry Batkovich + */ +public class ConvertCompareToToEqualsIntention extends BaseElementAtCaretIntentionAction { + public static final String TEXT = "Convert '.compareTo()' method to '.equals()' (may change semantics)"; + + @Override + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + if (!FileModificationService.getInstance().preparePsiElementsForWrite(element)) { + return; + } + final ResolveResult resolveResult = findCompareTo(element); + assert resolveResult != null; + final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); + final Pair qualifierAndParameter = getQualifierAndParameter(resolveResult.getCompareToCall()); + final PsiExpression newExpression = + elementFactory.createExpressionFromText(String.format((resolveResult.isEqEq() ? "" : "!") + "%s.equals(%s)", qualifierAndParameter.getFirst().getText(), qualifierAndParameter.getSecond().getText()), null); + final PsiElement result = resolveResult.getBinaryExpression().replace(newExpression); + + editor.getCaretModel().moveToOffset(result.getTextOffset() + result.getTextLength()); + } + + @Override + public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) { + return findCompareTo(element) != null; + } + + private static Pair getQualifierAndParameter(PsiMethodCallExpression methodCallExpression) { + final PsiExpression qualifier = methodCallExpression.getMethodExpression().getQualifierExpression(); + assert qualifier != null; + final PsiExpression parameter = methodCallExpression.getArgumentList().getExpressions()[0]; + return Pair.create(qualifier, parameter); + } + + @Nullable + private static ResolveResult findCompareTo(PsiElement element) { + final PsiBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PsiBinaryExpression.class); + if (binaryExpression == null) { + return null; + } + final PsiJavaToken operationSign = binaryExpression.getOperationSign(); + boolean isEqEq; + if (JavaTokenType.NE.equals(operationSign.getTokenType())) { + isEqEq = false; + } else if (JavaTokenType.EQEQ.equals(operationSign.getTokenType())) { + isEqEq = true; + } else { + return null; + } + PsiMethodCallExpression compareToExpression = null; + boolean hasZero = false; + for (PsiExpression psiExpression : binaryExpression.getOperands()) { + if (compareToExpression == null && detectCompareTo(psiExpression)) { + compareToExpression = (PsiMethodCallExpression)psiExpression; + continue; + } + if (!hasZero && detectZero(psiExpression)) { + hasZero = true; + } + } + if (!hasZero || compareToExpression == null) { + return null; + } + getQualifierAndParameter(compareToExpression); + return new ResolveResult(binaryExpression, compareToExpression, isEqEq); + } + + private static boolean detectCompareTo(final @NotNull PsiExpression expression) { + if (!(expression instanceof PsiMethodCallExpression)) { + return false; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + if (methodCallExpression.getMethodExpression().getQualifierExpression() == null) { + return false; + } + final PsiMethod psiMethod = methodCallExpression.resolveMethod(); + if (psiMethod == null || !"compareTo".equals(psiMethod.getName()) || psiMethod.getParameterList().getParametersCount() != 1) { + return false; + } + if (methodCallExpression.getArgumentList().getExpressions().length != 1) { + return false; + } + final PsiClass containingClass = psiMethod.getContainingClass(); + if (containingClass == null) { + return false; + } + final PsiClass javaLangComparable = JavaPsiFacade.getInstance(expression.getProject()).findClass(CommonClassNames.JAVA_LANG_COMPARABLE, GlobalSearchScope.allScope( + expression.getProject())); + if (javaLangComparable == null) { + return false; + } + if (!containingClass.isInheritor(javaLangComparable, true)) { + return false; + } + return true; + } + + private static boolean detectZero(final @NotNull PsiExpression expression) { + if (!(expression instanceof PsiLiteralExpression)) { + return false; + } + final Object value = ((PsiLiteralExpression)expression).getValue(); + return Comparing.equal(value, 0); + } + + private static class ResolveResult { + private final PsiBinaryExpression myBinaryExpression; + private final PsiMethodCallExpression myCompareToCall; + private final boolean myEqEq; + + private ResolveResult(PsiBinaryExpression binaryExpression, PsiMethodCallExpression compareToCall, boolean eqEq) { + myBinaryExpression = binaryExpression; + myCompareToCall = compareToCall; + myEqEq = eqEq; + } + + public PsiBinaryExpression getBinaryExpression() { + return myBinaryExpression; + } + + public PsiMethodCallExpression getCompareToCall() { + return myCompareToCall; + } + + public boolean isEqEq() { + return myEqEq; + } + } + + @NotNull + @Override + public String getFamilyName() { + return TEXT; + } + + @NotNull + @Override + public String getText() { + return getFamilyName(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/convertCompareToToEquals/eqEq.java b/java/java-tests/testData/codeInsight/convertCompareToToEquals/eqEq.java new file mode 100644 index 000000000000..d4d74f9eef8d --- /dev/null +++ b/java/java-tests/testData/codeInsight/convertCompareToToEquals/eqEq.java @@ -0,0 +1,13 @@ +import java.lang.Integer; +import java.lang.String; + +class X { + void m() { + + Integer i1 = new Integer(0); + Integer i2 = new Integer(2); + + boolean b = i1.compareTo(i2) == 0; + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/convertCompareToToEquals/eqEq_after.java b/java/java-tests/testData/codeInsight/convertCompareToToEquals/eqEq_after.java new file mode 100644 index 000000000000..360b2b3df746 --- /dev/null +++ b/java/java-tests/testData/codeInsight/convertCompareToToEquals/eqEq_after.java @@ -0,0 +1,13 @@ +import java.lang.Integer; +import java.lang.String; + +class X { + void m() { + + Integer i1 = new Integer(0); + Integer i2 = new Integer(2); + + boolean b = i1.equals(i2); + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/convertCompareToToEquals/ne.java b/java/java-tests/testData/codeInsight/convertCompareToToEquals/ne.java new file mode 100644 index 000000000000..47631c581bfa --- /dev/null +++ b/java/java-tests/testData/codeInsight/convertCompareToToEquals/ne.java @@ -0,0 +1,9 @@ +import java.lang.String; + +class X { + void m() { + + boolean b = "asd".substring(1).compareTo("qwe".substring(2)) != 0; + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/convertCompareToToEquals/ne_after.java b/java/java-tests/testData/codeInsight/convertCompareToToEquals/ne_after.java new file mode 100644 index 000000000000..0ef53e0cd185 --- /dev/null +++ b/java/java-tests/testData/codeInsight/convertCompareToToEquals/ne_after.java @@ -0,0 +1,9 @@ +import java.lang.String; + +class X { + void m() { + + boolean b = !"asd".substring(1).equals("qwe".substring(2)); + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/convertCompareToToEquals/notAvailable.java b/java/java-tests/testData/codeInsight/convertCompareToToEquals/notAvailable.java new file mode 100644 index 000000000000..4242b18e0973 --- /dev/null +++ b/java/java-tests/testData/codeInsight/convertCompareToToEquals/notAvailable.java @@ -0,0 +1,9 @@ +import java.lang.String; + +class X { + void m() { + + boolean b = "asd".compareTo("qwe") == 1; + + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertCompareToToEqualsTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertCompareToToEqualsTest.java new file mode 100644 index 000000000000..3945e414078b --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/intention/ConvertCompareToToEqualsTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2014 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.codeInsight.intention; + +import com.intellij.JavaTestUtil; +import com.intellij.codeInsight.intention.impl.ConvertCompareToToEqualsIntention; +import com.intellij.testFramework.fixtures.CodeInsightTestUtil; +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; + +/** + * @author Dmitry Batkovich + */ +public class ConvertCompareToToEqualsTest extends JavaCodeInsightFixtureTestCase { + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/convertCompareToToEquals/"; + } + + public void testNe() { + doTest(); + } + + public void testEqEq() { + doTest(); + } + + public void testNotAvailable() { + doTestNotAvailable();; + } + + private void doTest() { + final String name = getTestName(true); + CodeInsightTestUtil.doIntentionTest(myFixture, ConvertCompareToToEqualsIntention.TEXT, name + ".java", name + "_after.java"); + } + + private void doTestNotAvailable() { + myFixture.configureByFile(getTestName(true) + ".java"); + assertEmpty(myFixture.filterAvailableIntentions(ConvertCompareToToEqualsIntention.TEXT)); + } + +} diff --git a/resources-en/src/intentionDescriptions/ConvertCompareToToEqualsIntention/after.java.template b/resources-en/src/intentionDescriptions/ConvertCompareToToEqualsIntention/after.java.template new file mode 100644 index 000000000000..3b3bf5e7e256 --- /dev/null +++ b/resources-en/src/intentionDescriptions/ConvertCompareToToEqualsIntention/after.java.template @@ -0,0 +1,4 @@ +void m(String s1, String s2) { + if (!s1.equals(s2)) { + } +} \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/ConvertCompareToToEqualsIntention/before.java.template b/resources-en/src/intentionDescriptions/ConvertCompareToToEqualsIntention/before.java.template new file mode 100644 index 000000000000..58a3755ee42b --- /dev/null +++ b/resources-en/src/intentionDescriptions/ConvertCompareToToEqualsIntention/before.java.template @@ -0,0 +1,4 @@ +void m(String s1, String s2) { + if (s1.compareTo(s2) != 0) { + } +} diff --git a/resources-en/src/intentionDescriptions/ConvertCompareToToEqualsIntention/description.html b/resources-en/src/intentionDescriptions/ConvertCompareToToEqualsIntention/description.html new file mode 100644 index 000000000000..c6fe60e4dacd --- /dev/null +++ b/resources-en/src/intentionDescriptions/ConvertCompareToToEqualsIntention/description.html @@ -0,0 +1,20 @@ + + + +Converts call if compareTo method to call of equals method. + + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 18bcde6c3917..7c69f7504c58 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -740,6 +740,10 @@ com.intellij.codeInsight.intention.impl.SwapIfStatementsIntentionAction Control Flow + + com.intellij.codeInsight.intention.impl.ConvertCompareToToEqualsIntention + Control Flow + com.intellij.codeInsight.intention.impl.CreateFieldFromParameterAction