2 * Copyright (c) 2006 Your Corporation. All Rights Reserved.
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
;
27 public class BooleanMethodIsAlwaysInvertedInspection
extends GlobalJavaInspectionTool
{
28 private static final Key
<Boolean
> ALWAYS_INVERTED
= Key
.create("ALWAYS_INVERTED_METHOD");
31 public String
getDisplayName() {
32 return InspectionsBundle
.message("boolean.method.is.always.inverted.display.name");
36 public String
getGroupDisplayName() {
42 public String
getShortName() {
43 return "BooleanMethodIsAlwaysInverted";
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)};
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;
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
);
96 traverseSuperMethods(refMethod
, context
, usagesProcessor
);
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
)) {
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());
159 public QuickFix
getQuickFix(final String hint
) {
160 return new InvertMethodFix();
163 private static class InvertMethodFix
implements LocalQuickFix
{
166 public String
getName() {
167 return "Invert method";
171 public String
getFamilyName() {
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());