2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com
.intellij
.codeInspection
.unneededThrows
;
18 import com
.intellij
.analysis
.AnalysisScope
;
19 import com
.intellij
.codeInsight
.CodeInsightUtilBase
;
20 import com
.intellij
.codeInsight
.ExceptionUtil
;
21 import com
.intellij
.codeInsight
.daemon
.GroupNames
;
22 import com
.intellij
.codeInspection
.*;
23 import com
.intellij
.codeInspection
.ex
.ProblemDescriptorImpl
;
24 import com
.intellij
.codeInspection
.reference
.*;
25 import com
.intellij
.openapi
.diagnostic
.Logger
;
26 import com
.intellij
.openapi
.project
.Project
;
27 import com
.intellij
.openapi
.util
.Comparing
;
28 import com
.intellij
.openapi
.util
.Pair
;
29 import com
.intellij
.psi
.*;
30 import com
.intellij
.psi
.search
.GlobalSearchScope
;
31 import com
.intellij
.psi
.search
.searches
.AllOverridingMethodsSearch
;
32 import com
.intellij
.psi
.util
.PsiTreeUtil
;
33 import com
.intellij
.util
.IncorrectOperationException
;
34 import com
.intellij
.util
.Processor
;
35 import com
.intellij
.util
.Query
;
36 import com
.intellij
.util
.containers
.BidirectionalMap
;
37 import org
.jetbrains
.annotations
.NonNls
;
38 import org
.jetbrains
.annotations
.NotNull
;
39 import org
.jetbrains
.annotations
.Nullable
;
41 import java
.util
.ArrayList
;
42 import java
.util
.List
;
47 public class RedundantThrows
extends GlobalJavaInspectionTool
{
48 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInspection.unneededThrows.RedundantThrows");
49 private static final String DISPLAY_NAME
= InspectionsBundle
.message("inspection.redundant.throws.display.name");
50 private final BidirectionalMap
<String
, QuickFix
> myQuickFixes
= new BidirectionalMap
<String
, QuickFix
>();
51 @NonNls private static final String SHORT_NAME
= "RedundantThrows";
54 public CommonProblemDescriptor
[] checkElement(RefEntity refEntity
,
56 InspectionManager manager
,
57 GlobalInspectionContext globalContext
,
58 ProblemDescriptionsProcessor processor
) {
59 if (refEntity
instanceof RefMethod
) {
60 final RefMethod refMethod
= (RefMethod
)refEntity
;
61 if (refMethod
.isSyntheticJSP()) return null;
63 if (refMethod
.hasSuperMethods()) return null;
65 if (refMethod
.isEntry()) return null;
67 PsiClass
[] unThrown
= refMethod
.getUnThrownExceptions();
68 if (unThrown
== null) return null;
70 PsiMethod psiMethod
= (PsiMethod
)refMethod
.getElement();
71 PsiClassType
[] throwsList
= psiMethod
.getThrowsList().getReferencedTypes();
72 PsiJavaCodeReferenceElement
[] throwsRefs
= psiMethod
.getThrowsList().getReferenceElements();
73 ArrayList
<ProblemDescriptor
> problems
= null;
75 for (int i
= 0; i
< throwsList
.length
; i
++) {
76 final PsiClassType throwsType
= throwsList
[i
];
77 final String throwsClassName
= throwsType
.getClassName();
78 final PsiJavaCodeReferenceElement throwsRef
= throwsRefs
[i
];
79 if (ExceptionUtil
.isUncheckedException(throwsType
)) continue;
80 if (declaredInRemotableMethod(psiMethod
, throwsType
)) continue;
82 for (PsiClass s
: unThrown
) {
83 final PsiClass throwsResolvedType
= throwsType
.resolve();
84 if (Comparing
.equal(s
, throwsResolvedType
)) {
85 if (problems
== null) problems
= new ArrayList
<ProblemDescriptor
>(1);
87 if (refMethod
.isAbstract() || refMethod
.getOwnerClass().isInterface()) {
88 problems
.add(manager
.createProblemDescriptor(throwsRef
, InspectionsBundle
.message(
89 "inspection.redundant.throws.problem.descriptor", "<code>#ref</code>"), getFix(processor
, throwsClassName
), ProblemHighlightType
.LIKE_UNUSED_SYMBOL
));
91 else if (!refMethod
.getDerivedMethods().isEmpty()) {
92 problems
.add(manager
.createProblemDescriptor(throwsRef
, InspectionsBundle
.message(
93 "inspection.redundant.throws.problem.descriptor1", "<code>#ref</code>"), getFix(processor
, throwsClassName
), ProblemHighlightType
.LIKE_UNUSED_SYMBOL
));
96 problems
.add(manager
.createProblemDescriptor(throwsRef
, InspectionsBundle
.message(
97 "inspection.redundant.throws.problem.descriptor2", "<code>#ref</code>"), getFix(processor
, throwsClassName
), ProblemHighlightType
.LIKE_UNUSED_SYMBOL
));
103 if (problems
!= null) {
104 return problems
.toArray(new ProblemDescriptorImpl
[problems
.size()]);
111 private static boolean declaredInRemotableMethod(final PsiMethod psiMethod
, final PsiClassType throwsType
) {
112 if (!throwsType
.equalsToText("java.rmi.RemoteException")) return false;
113 PsiClass aClass
= psiMethod
.getContainingClass();
114 if (aClass
== null) return false;
116 JavaPsiFacade
.getInstance(aClass
.getProject()).findClass("java.rmi.Remote", GlobalSearchScope
.allScope(aClass
.getProject()));
117 return remote
!= null && aClass
.isInheritor(remote
, true);
121 protected boolean queryExternalUsagesRequests(final RefManager manager
, final GlobalJavaInspectionContext globalContext
,
122 final ProblemDescriptionsProcessor processor
) {
123 manager
.iterate(new RefJavaVisitor() {
124 @Override public void visitElement(RefEntity refEntity
) {
125 if (processor
.getDescriptions(refEntity
) != null) {
126 refEntity
.accept(new RefJavaVisitor() {
127 @Override public void visitMethod(final RefMethod refMethod
) {
128 globalContext
.enqueueDerivedMethodsProcessor(refMethod
, new GlobalJavaInspectionContext
.DerivedMethodsProcessor() {
129 public boolean process(PsiMethod derivedMethod
) {
130 processor
.ignoreElement(refMethod
);
144 public String
getDisplayName() {
149 public String
getGroupDisplayName() {
150 return GroupNames
.DECLARATION_REDUNDANCY
;
154 public String
getShortName() {
158 private LocalQuickFix
getFix(final ProblemDescriptionsProcessor processor
, final String hint
) {
159 QuickFix fix
= myQuickFixes
.get(hint
);
161 fix
= new MyQuickFix(processor
, hint
);
163 myQuickFixes
.put(hint
, fix
);
166 return (LocalQuickFix
)fix
;
171 public QuickFix
getQuickFix(String hint
) {
172 return getFix(null, hint
);
176 public String
getHint(final QuickFix fix
) {
177 final List
<String
> hints
= myQuickFixes
.getKeysByValue(fix
);
178 LOG
.assertTrue(hints
!= null && hints
.size() == 1);
182 private static class MyQuickFix
implements LocalQuickFix
{
183 private final ProblemDescriptionsProcessor myProcessor
;
184 private final String myHint
;
186 public MyQuickFix(final ProblemDescriptionsProcessor processor
, final String hint
) {
187 myProcessor
= processor
;
192 public String
getName() {
193 return InspectionsBundle
.message("inspection.redundant.throws.remove.quickfix");
196 public void applyFix(@NotNull Project project
, @NotNull ProblemDescriptor descriptor
) {
197 if (myProcessor
!= null) {
198 RefElement refElement
= (RefElement
)myProcessor
.getElement(descriptor
);
199 if (refElement
instanceof RefMethod
&& refElement
.isValid()) {
200 RefMethod refMethod
= (RefMethod
)refElement
;
201 final CommonProblemDescriptor
[] problems
= myProcessor
.getDescriptions(refMethod
);
202 if (problems
!= null) {
203 removeExcessiveThrows(refMethod
, null, problems
);
208 final PsiMethod psiMethod
= PsiTreeUtil
.getParentOfType(descriptor
.getPsiElement(), PsiMethod
.class);
209 if (psiMethod
!= null) {
210 removeExcessiveThrows(null, psiMethod
, new CommonProblemDescriptor
[]{descriptor
});
216 public String
getFamilyName() {
220 private void removeExcessiveThrows(@Nullable RefMethod refMethod
, @Nullable final PsiModifierListOwner element
, final CommonProblemDescriptor
[] problems
) {
222 @Nullable final PsiMethod psiMethod
;
223 if (element
== null) {
224 LOG
.assertTrue(refMethod
!= null);
225 psiMethod
= (PsiMethod
)refMethod
.getElement();
228 psiMethod
= (PsiMethod
)element
;
230 if (psiMethod
== null) return; //invalid refMethod
231 final Project project
= psiMethod
.getProject();
232 final PsiManager psiManager
= PsiManager
.getInstance(project
);
233 final List
<PsiJavaCodeReferenceElement
> refsToDelete
= new ArrayList
<PsiJavaCodeReferenceElement
>();
234 for (CommonProblemDescriptor problem
: problems
) {
235 final PsiElement psiElement
= ((ProblemDescriptor
)problem
).getPsiElement();
236 if (psiElement
instanceof PsiJavaCodeReferenceElement
) {
237 final PsiJavaCodeReferenceElement classRef
= (PsiJavaCodeReferenceElement
)psiElement
;
238 final PsiType psiType
= JavaPsiFacade
.getInstance(psiManager
.getProject()).getElementFactory().createType(classRef
);
239 removeException(refMethod
, psiType
, refsToDelete
, psiMethod
);
241 final PsiReferenceList throwsList
= psiMethod
.getThrowsList();
242 final PsiClassType
[] classTypes
= throwsList
.getReferencedTypes();
243 for (PsiClassType classType
: classTypes
) {
244 final String text
= classType
.getClassName();
245 if (Comparing
.strEqual(myHint
, text
)) {
246 removeException(refMethod
, classType
, refsToDelete
, psiMethod
);
253 //check read-only status for derived methods
254 if (!CodeInsightUtilBase
.preparePsiElementsForWrite(refsToDelete
)) return;
256 for (final PsiJavaCodeReferenceElement aRefsToDelete
: refsToDelete
) {
257 aRefsToDelete
.delete();
260 catch (IncorrectOperationException e
) {
265 private static void removeException(final RefMethod refMethod
,
266 final PsiType exceptionType
,
267 final List
<PsiJavaCodeReferenceElement
> refsToDelete
,
268 final PsiMethod psiMethod
) {
269 PsiManager psiManager
= psiMethod
.getManager();
271 PsiJavaCodeReferenceElement
[] refs
= psiMethod
.getThrowsList().getReferenceElements();
272 for (PsiJavaCodeReferenceElement ref
: refs
) {
273 PsiType refType
= JavaPsiFacade
.getInstance(psiManager
.getProject()).getElementFactory().createType(ref
);
274 if (exceptionType
.isAssignableFrom(refType
)) {
275 refsToDelete
.add(ref
);
279 if (refMethod
!= null) {
280 for (RefMethod refDerived
: refMethod
.getDerivedMethods()) {
281 removeException(refDerived
, exceptionType
, refsToDelete
, (PsiMethod
)refDerived
.getElement());
284 final Query
<Pair
<PsiMethod
,PsiMethod
>> query
= AllOverridingMethodsSearch
.search(psiMethod
.getContainingClass());
285 query
.forEach(new Processor
<Pair
<PsiMethod
, PsiMethod
>>(){
286 public boolean process(final Pair
<PsiMethod
, PsiMethod
> pair
) {
287 if (pair
.first
== psiMethod
) {
288 removeException(null, exceptionType
, refsToDelete
, pair
.second
);