update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInspection / unneededThrows / RedundantThrows.java
blob690b73ce598743c5826f3f6ab10d65ae0ee5184d
1 /*
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;
44 /**
45 * @author max
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";
53 @Nullable
54 public CommonProblemDescriptor[] checkElement(RefEntity refEntity,
55 AnalysisScope scope,
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));
95 else {
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()]);
108 return null;
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;
115 PsiClass remote =
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);
131 return true;
140 return false;
143 @NotNull
144 public String getDisplayName() {
145 return DISPLAY_NAME;
148 @NotNull
149 public String getGroupDisplayName() {
150 return GroupNames.DECLARATION_REDUNDANCY;
153 @NotNull
154 public String getShortName() {
155 return SHORT_NAME;
158 private LocalQuickFix getFix(final ProblemDescriptionsProcessor processor, final String hint) {
159 QuickFix fix = myQuickFixes.get(hint);
160 if (fix == null) {
161 fix = new MyQuickFix(processor, hint);
162 if (hint != null) {
163 myQuickFixes.put(hint, fix);
166 return (LocalQuickFix)fix;
170 @Nullable
171 public QuickFix getQuickFix(String hint) {
172 return getFix(null, hint);
175 @Nullable
176 public String getHint(final QuickFix fix) {
177 final List<String> hints = myQuickFixes.getKeysByValue(fix);
178 LOG.assertTrue(hints != null && hints.size() == 1);
179 return hints.get(0);
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;
188 myHint = hint;
191 @NotNull
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);
207 else {
208 final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethod.class);
209 if (psiMethod != null) {
210 removeExcessiveThrows(null, psiMethod, new CommonProblemDescriptor[]{descriptor});
215 @NotNull
216 public String getFamilyName() {
217 return getName();
220 private void removeExcessiveThrows(@Nullable RefMethod refMethod, @Nullable final PsiModifierListOwner element, final CommonProblemDescriptor[] problems) {
221 try {
222 @Nullable final PsiMethod psiMethod;
223 if (element == null) {
224 LOG.assertTrue(refMethod != null);
225 psiMethod = (PsiMethod)refMethod.getElement();
227 else {
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);
240 } else {
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);
247 break;
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) {
261 LOG.error(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());
283 } else {
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);
290 return true;