d900809cfb4fa448d8b9e019a7859e74779faa0a
[idea/community.git] / samples / comparingReferences / source / com / intellij / codeInspection / ComparingReferencesInspection.java
1 package com.intellij.codeInspection;
2
3 import com.intellij.codeInsight.daemon.GroupNames;
4 import com.intellij.openapi.diagnostic.Logger;
5 import com.intellij.openapi.project.Project;
6 import com.intellij.openapi.util.Ref;
7 import com.intellij.psi.*;
8 import com.intellij.psi.tree.IElementType;
9 import com.intellij.ui.DocumentAdapter;
10 import com.intellij.util.IncorrectOperationException;
11 import org.jetbrains.annotations.NonNls;
12 import org.jetbrains.annotations.NotNull;
13
14 import javax.swing.*;
15 import javax.swing.event.DocumentEvent;
16 import java.awt.*;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.StringTokenizer;
20
21 /**
22  * @author max
23  */
24 public class ComparingReferencesInspection extends BaseJavaLocalInspectionTool {
25   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ComparingReferencesInspection");
26
27   private final LocalQuickFix myQuickFix = new MyQuickFix();
28
29   @SuppressWarnings({"WeakerAccess"}) @NonNls public String CHECKED_CLASSES = "java.lang.String;java.util.Date";
30   @NonNls private static final String DESCRIPTION_TEMPLATE = InspectionsBundle.message("inspection.comparing.references.problem.descriptor");
31
32   @NotNull
33   public String getDisplayName() {
34    // return InspectionsBundle.message("inspection.comparing.references.display.name");
35       return "'==' or '!=' instead of 'equals()'";
36   }
37
38   @NotNull
39   public String getGroupDisplayName() {
40     return GroupNames.BUGS_GROUP_NAME;
41   }
42
43   @NotNull
44   public String getShortName() {
45     return "ComparingReferences";
46   }
47
48   private boolean isCheckedType(PsiType type) {
49     if (!(type instanceof PsiClassType)) return false;
50
51     StringTokenizer tokenizer = new StringTokenizer(CHECKED_CLASSES, ";");
52     while (tokenizer.hasMoreTokens()) {
53       String className = tokenizer.nextToken();
54       if (type.equalsToText(className)) return true;
55     }
56
57     return false;
58   }
59
60     @NotNull
61     @Override
62     public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
63      return new JavaElementVisitor() {
64
65          @Override
66          public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) {
67          }
68
69
70       @Override public void visitBinaryExpression(PsiBinaryExpression expression) {
71           super.visitBinaryExpression(expression);
72           IElementType opSign = expression.getOperationSign().getTokenType();
73           if (opSign == JavaTokenType.EQEQ || opSign == JavaTokenType.NE) {
74               PsiExpression lOperand = expression.getLOperand();
75               PsiExpression rOperand = expression.getROperand();
76               if (rOperand == null || isNullLiteral(lOperand) || isNullLiteral(rOperand)) return;
77
78               PsiType lType = lOperand.getType();
79               PsiType rType = rOperand.getType();
80
81               if (isCheckedType(lType) || isCheckedType(rType)) {
82                   holder.registerProblem(expression,
83                           DESCRIPTION_TEMPLATE, myQuickFix);
84               }
85           }
86       }
87     };
88   }
89
90   private static boolean isNullLiteral(PsiExpression expr) {
91     return expr instanceof PsiLiteralExpression && "null".equals(expr.getText());
92   }
93
94   private static class MyQuickFix implements LocalQuickFix {
95     @NotNull
96     public String getName() {
97       return InspectionsBundle.message("inspection.comparing.references.use.quickfix");
98     }
99
100     public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
101       try {
102         PsiBinaryExpression binaryExpression = (PsiBinaryExpression)descriptor.getPsiElement();
103         IElementType opSign = binaryExpression.getOperationSign().getTokenType();
104         PsiExpression lExpr = binaryExpression.getLOperand();
105         PsiExpression rExpr = binaryExpression.getROperand();
106         if (rExpr == null)
107           return;
108
109         PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
110         PsiMethodCallExpression equalsCall = (PsiMethodCallExpression)factory.createExpressionFromText("a.equals(b)", null);
111
112         equalsCall.getMethodExpression().getQualifierExpression().replace(lExpr);
113         equalsCall.getArgumentList().getExpressions()[0].replace(rExpr);
114
115         PsiExpression result = (PsiExpression)binaryExpression.replace(equalsCall);
116
117         if (opSign == JavaTokenType.NE) {
118           PsiPrefixExpression negation = (PsiPrefixExpression)factory.createExpressionFromText("!a", null);
119           negation.getOperand().replace(result);
120           result.replace(negation);
121         }
122       }
123       catch (IncorrectOperationException e) {
124         LOG.error(e);
125       }
126     }
127
128     @NotNull
129     public String getFamilyName() {
130       return getName();
131     }
132   }
133
134   public JComponent createOptionsPanel() {
135     JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
136     final JTextField checkedClasses = new JTextField(CHECKED_CLASSES);
137     checkedClasses.getDocument().addDocumentListener(new DocumentAdapter() {
138       public void textChanged(DocumentEvent event) {
139         CHECKED_CLASSES = checkedClasses.getText();
140       }
141     });
142
143     panel.add(checkedClasses);
144     return panel;
145   }
146
147   public boolean isEnabledByDefault() {
148     return true;
149   }
150 }