Java inspection: Rename the inspection "Replace Equality with Equals" to a noun-based...
[idea/community.git] / plugins / InspectionGadgets / src / com / siyeh / ig / equality / EqualityOperatorComparesObjectsInspection.java
1 /*
2  * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.siyeh.ig.equality;
17
18 import com.intellij.codeInspection.ProblemDescriptor;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.psi.*;
21 import com.intellij.psi.tree.IElementType;
22 import com.intellij.psi.util.PsiUtil;
23 import com.intellij.psi.util.TypeConversionUtil;
24 import com.siyeh.InspectionGadgetsBundle;
25 import com.siyeh.ig.BaseInspection;
26 import com.siyeh.ig.BaseInspectionVisitor;
27 import com.siyeh.ig.InspectionGadgetsFix;
28 import com.siyeh.ig.PsiReplacementUtil;
29 import com.siyeh.ig.psiutils.ClassUtils;
30 import com.siyeh.ig.psiutils.ParenthesesUtils;
31 import org.jetbrains.annotations.Nls;
32 import org.jetbrains.annotations.NonNls;
33 import org.jetbrains.annotations.NotNull;
34
35 public class EqualityOperatorComparesObjectsInspection extends BaseInspection {
36
37   @Nls
38   @NotNull
39   @Override
40   public String getDisplayName() {
41     return InspectionGadgetsBundle.message("equality.operator.compares.objects.name");
42   }
43
44   @NotNull
45   @Override
46   protected String buildErrorString(Object... infos) {
47     return InspectionGadgetsBundle.message("equality.operator.compares.objects.descriptor", infos);
48   }
49
50   @Override
51   public BaseInspectionVisitor buildVisitor() {
52     return new ObjectEqualityVisitor();
53   }
54
55   @NotNull
56   @Override
57   protected InspectionGadgetsFix[] buildFixes(Object... infos) {
58     return new InspectionGadgetsFix[]{new EqualsFix(infos), new SafeEqualsFix(infos)};
59   }
60
61   private static void doFixImpl(@NotNull PsiElement element) {
62     final PsiBinaryExpression exp = (PsiBinaryExpression)element;
63     final PsiExpression lhs = exp.getLOperand();
64     final PsiExpression rhs = exp.getROperand();
65     if (rhs == null) {
66       return;
67     }
68     final PsiExpression strippedLhs = ParenthesesUtils.stripParentheses(lhs);
69     if (strippedLhs == null) {
70       return;
71     }
72     final PsiExpression strippedRhs = ParenthesesUtils.stripParentheses(rhs);
73     if (strippedRhs == null) {
74       return;
75     }
76     final String lhText = strippedLhs.getText();
77     final String rhText = strippedRhs.getText();
78
79     final String prefix = exp.getOperationTokenType().equals(JavaTokenType.EQEQ) ? "" : "!";
80     @NonNls final String expString;
81     if (ParenthesesUtils.getPrecedence(strippedLhs) > ParenthesesUtils.METHOD_CALL_PRECEDENCE) {
82       expString = prefix + '(' + lhText + ").equals(" + rhText + ')';
83     }
84     else {
85       expString = prefix + lhText + ".equals(" + rhText + ')';
86     }
87     PsiReplacementUtil.replaceExpression(exp, expString);
88   }
89
90   private static void doSafeFixImpl(PsiElement element) {
91     final PsiBinaryExpression exp = (PsiBinaryExpression)element;
92     final PsiExpression lhs = exp.getLOperand();
93     final PsiExpression rhs = exp.getROperand();
94     if (rhs == null) {
95       return;
96     }
97     final PsiExpression strippedLhs =
98       ParenthesesUtils.stripParentheses(lhs);
99     if (strippedLhs == null) {
100       return;
101     }
102     final PsiExpression strippedRhs =
103       ParenthesesUtils.stripParentheses(rhs);
104     if (strippedRhs == null) {
105       return;
106     }
107     final String lhsText = strippedLhs.getText();
108     final String rhsText = strippedRhs.getText();
109     final PsiJavaToken operationSign = exp.getOperationSign();
110     final IElementType tokenType = operationSign.getTokenType();
111     final String signText = operationSign.getText();
112     @NonNls final StringBuilder newExpression = new StringBuilder();
113     if (PsiUtil.isLanguageLevel7OrHigher(element) && ClassUtils.findClass("java.util.Objects", element) != null) {
114       if (tokenType.equals(JavaTokenType.NE)) {
115         newExpression.append('!');
116       }
117       newExpression.append("java.util.Objects.equals(").append(lhsText).append(',').append(rhsText).append(')');
118     }
119     else {
120       newExpression.append(lhsText).append("==null?").append(rhsText).append(signText).append(" null:");
121       if (tokenType.equals(JavaTokenType.NE)) {
122         newExpression.append('!');
123       }
124       if (ParenthesesUtils.getPrecedence(strippedLhs) > ParenthesesUtils.METHOD_CALL_PRECEDENCE) {
125         newExpression.append('(').append(lhsText).append(')');
126       }
127       else {
128         newExpression.append(lhsText);
129       }
130       newExpression.append(".equals(").append(rhsText).append(')');
131     }
132     PsiReplacementUtil.replaceExpressionAndShorten(exp, newExpression.toString());
133   }
134
135   private static class SafeEqualsFix extends InspectionGadgetsFix {
136     private final Object[] myInfos;
137
138     public SafeEqualsFix(Object... infos) {
139       myInfos = infos;
140     }
141
142     @Nls
143     @NotNull
144     @Override
145     public String getName() {
146       return InspectionGadgetsBundle.message("equality.operator.compares.objects.safe.quickfix", myInfos);
147     }
148
149     @Nls
150     @NotNull
151     @Override
152     public String getFamilyName() {
153       return InspectionGadgetsBundle.message("equality.operator.compares.objects.safe.family.quickfix");
154     }
155
156     @Override
157     protected void doFix(Project project, ProblemDescriptor descriptor) {
158       doSafeFixImpl(descriptor.getPsiElement());
159     }
160   }
161
162   private static class EqualsFix extends InspectionGadgetsFix {
163     private final Object[] myInfos;
164
165     public EqualsFix(Object... infos) {
166       myInfos = infos;
167     }
168
169     @Nls
170     @NotNull
171     @Override
172     public String getName() {
173       return InspectionGadgetsBundle.message("equality.operator.compares.objects.quickfix", myInfos);
174     }
175
176     @Nls
177     @NotNull
178     @Override
179     public String getFamilyName() {
180       return InspectionGadgetsBundle.message("equality.operator.compares.objects.family.quickfix");
181     }
182
183     @Override
184     protected void doFix(Project project, ProblemDescriptor descriptor) {
185       doFixImpl(descriptor.getPsiElement());
186     }
187   }
188
189   private static class ObjectEqualityVisitor extends BaseInspectionVisitor {
190     @Override
191     public void visitBinaryExpression(PsiBinaryExpression expression) {
192       super.visitBinaryExpression(expression);
193       final IElementType tokenType = expression.getOperationTokenType();
194       if (!tokenType.equals(JavaTokenType.NE) &&
195           !tokenType.equals(JavaTokenType.EQEQ)) {
196         return;
197       }
198       final PsiExpression lhs = expression.getLOperand();
199       final PsiType lhsType = lhs.getType();
200       if (lhsType == null || lhsType instanceof PsiPrimitiveType || TypeConversionUtil.isEnumType(lhsType)) {
201         return;
202       }
203       final PsiExpression rhs = expression.getROperand();
204       if (rhs == null) {
205         return;
206       }
207       final PsiType rhsType = rhs.getType();
208       if (rhsType == null || rhsType instanceof PsiPrimitiveType || TypeConversionUtil.isEnumType(rhsType)) {
209         return;
210       }
211       final String operationText = expression.getOperationSign().getText();
212       final String prefix = tokenType.equals(JavaTokenType.NE) ? "!" : "";
213       registerError(expression, operationText, prefix);
214     }
215   }
216 }