Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / plugins / InspectionGadgets / src / com / intellij / codeInspection / booleanIsAlwaysInverted / BooleanMethodIsAlwaysInvertedInspection.java
blob6eb70208d301e54337994c72d7c278e01b5b1d12
1 /*
2 * Copyright (c) 2006 Your Corporation. All Rights Reserved.
3 */
4 package com.intellij.codeInspection.booleanIsAlwaysInverted;
6 import com.intellij.analysis.AnalysisScope;
7 import com.intellij.codeInspection.*;
8 import com.intellij.codeInspection.reference.*;
9 import com.intellij.ide.DataManager;
10 import com.intellij.openapi.project.Project;
11 import com.intellij.openapi.util.Key;
12 import com.intellij.psi.*;
13 import com.intellij.psi.tree.IElementType;
14 import com.intellij.psi.util.PsiTreeUtil;
15 import com.intellij.refactoring.JavaRefactoringActionHandlerFactory;
16 import com.intellij.refactoring.RefactoringActionHandler;
17 import org.jetbrains.annotations.NonNls;
18 import org.jetbrains.annotations.NotNull;
19 import org.jetbrains.annotations.Nullable;
21 import java.util.Collection;
23 /**
24 * User: anna
25 * Date: 06-Jan-2006
27 public class BooleanMethodIsAlwaysInvertedInspection extends GlobalJavaInspectionTool {
28 private static final Key<Boolean> ALWAYS_INVERTED = Key.create("ALWAYS_INVERTED_METHOD");
30 @NotNull
31 public String getDisplayName() {
32 return InspectionsBundle.message("boolean.method.is.always.inverted.display.name");
35 @NotNull
36 public String getGroupDisplayName() {
37 return "";
40 @NotNull
41 @NonNls
42 public String getShortName() {
43 return "BooleanMethodIsAlwaysInverted";
46 @Nullable
47 public RefGraphAnnotator getAnnotator(final RefManager refManager) {
48 return new BooleanInvertedAnnotator();
51 public CommonProblemDescriptor[] checkElement(RefEntity refEntity, AnalysisScope scope, final InspectionManager manager, final GlobalInspectionContext globalContext) {
52 if (refEntity instanceof RefMethod) {
53 RefMethod refMethod = (RefMethod)refEntity;
54 if (!refMethod.isReferenced()) return null;
55 if (hasNonInvertedCalls(refMethod)) return null;
56 if (refMethod.getSuperMethods().size() > 0) return null;
57 final PsiMethod psiMethod = (PsiMethod)refMethod.getElement();
58 final PsiIdentifier psiIdentifier = psiMethod.getNameIdentifier();
59 if (psiIdentifier != null) {
60 return new ProblemDescriptor[] { manager.createProblemDescriptor(psiIdentifier,
61 InspectionsBundle.message("boolean.method.is.always.inverted.problem.descriptor"),
62 new InvertMethodFix(),
63 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false)};
66 return null;
69 private static boolean hasNonInvertedCalls(final RefMethod refMethod) {
70 final Boolean alwaysInverted = refMethod.getUserData(ALWAYS_INVERTED);
71 if (alwaysInverted == null) return true;
72 if (refMethod.isExternalOverride()) return true;
73 if (refMethod.isReferenced() && !alwaysInverted.booleanValue()) return true;
74 final Collection<RefMethod> superMethods = refMethod.getSuperMethods();
75 for (RefMethod superMethod : superMethods) {
76 if (hasNonInvertedCalls(superMethod)) return true;
78 return false;
81 protected boolean queryExternalUsagesRequests(final RefManager manager, final GlobalJavaInspectionContext context,
82 final ProblemDescriptionsProcessor descriptionsProcessor) {
83 manager.iterate(new RefJavaVisitor() {
84 @Override public void visitMethod(final RefMethod refMethod) {
85 if (descriptionsProcessor.getDescriptions(refMethod) != null) { //suspicious method -> need to check external usages
86 final GlobalJavaInspectionContext.UsagesProcessor usagesProcessor = new GlobalJavaInspectionContext.UsagesProcessor() {
87 public boolean process(PsiReference psiReference) {
88 final PsiElement psiReferenceExpression = psiReference.getElement();
89 if (psiReferenceExpression instanceof PsiReferenceExpression &&
90 !isInvertedMethodCall((PsiReferenceExpression) psiReferenceExpression)) {
91 descriptionsProcessor.ignoreElement(refMethod);
93 return false;
96 traverseSuperMethods(refMethod, context, usagesProcessor);
99 });
100 return false;
103 private static void traverseSuperMethods(RefMethod refMethod, GlobalJavaInspectionContext globalContext, GlobalJavaInspectionContext.UsagesProcessor processor){
104 final Collection<RefMethod> superMethods = refMethod.getSuperMethods();
105 for (RefMethod superMethod : superMethods) {
106 traverseSuperMethods(superMethod, globalContext, processor);
108 globalContext.enqueueMethodUsagesProcessor(refMethod, processor);
111 private static void checkMethodCall(RefElement refWhat, final PsiElement element) {
112 if (!(refWhat instanceof RefMethod)) return;
113 final RefMethod refMethod = (RefMethod)refWhat;
114 final PsiElement psiElement = refMethod.getElement();
115 if (!(psiElement instanceof PsiMethod)) return;
116 final PsiMethod psiMethod = (PsiMethod)psiElement;
117 if (!(PsiType.BOOLEAN.equals(psiMethod.getReturnType()))) return;
118 element.accept(new JavaRecursiveElementVisitor() {
119 @Override public void visitMethodCallExpression(PsiMethodCallExpression call) {
120 super.visitMethodCallExpression(call);
121 final PsiReferenceExpression methodExpression = call.getMethodExpression();
122 if (methodExpression.isReferenceTo(psiMethod)) {
123 if (isInvertedMethodCall(methodExpression)) return;
124 refMethod.putUserData(ALWAYS_INVERTED, Boolean.FALSE);
130 private static boolean isInvertedMethodCall(final PsiReferenceExpression methodExpression) {
131 final PsiPrefixExpression prefixExpression = PsiTreeUtil.getParentOfType(methodExpression, PsiPrefixExpression.class);
132 if (methodExpression.getQualifierExpression() instanceof PsiSuperExpression) return true; //don't flag super calls
133 if (prefixExpression != null) {
134 final PsiJavaToken sign = prefixExpression.getOperationSign();
135 final IElementType tokenType = sign.getTokenType();
136 if (tokenType.equals(JavaTokenType.EXCL)) {
137 return true;
140 return false;
143 private static class BooleanInvertedAnnotator extends RefGraphAnnotator {
144 public void onInitialize(RefElement refElement) {
145 if (refElement instanceof RefMethod) {
146 final PsiElement element = refElement.getElement();
147 if (!(element instanceof PsiMethod)) return;
148 if (((PsiMethod)element).getReturnType() != PsiType.BOOLEAN) return;
149 refElement.putUserData(ALWAYS_INVERTED, Boolean.TRUE); //initial mark boolean methods
153 public void onMarkReferenced(RefElement refWhat, RefElement refFrom, boolean referencedFromClassInitializer) {
154 checkMethodCall(refWhat, refFrom.getElement());
158 @Override
159 public QuickFix getQuickFix(final String hint) {
160 return new InvertMethodFix();
163 private static class InvertMethodFix implements LocalQuickFix {
165 @NotNull
166 public String getName() {
167 return "Invert method";
170 @NotNull
171 public String getFamilyName() {
172 return getName();
175 public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
176 final PsiElement element = descriptor.getPsiElement();
177 final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
178 assert psiMethod != null;
179 final RefactoringActionHandler invertBooleanHandler = JavaRefactoringActionHandlerFactory.getInstance().createInvertBooleanHandler();
180 invertBooleanHandler.invoke(project, new PsiElement[]{psiMethod}, DataManager.getInstance().getDataContext());