Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInspection / canBeFinal / CanBeFinalInspection.java
blobe1269c8cb59c773f47ff2b7024b6bdfb64be63c8
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.
18 * Created by IntelliJ IDEA.
19 * User: max
20 * Date: Dec 24, 2001
21 * Time: 2:46:32 PM
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
25 package com.intellij.codeInspection.canBeFinal;
27 import com.intellij.analysis.AnalysisScope;
28 import com.intellij.codeInsight.daemon.GroupNames;
29 import com.intellij.codeInspection.*;
30 import com.intellij.codeInspection.reference.*;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.project.Project;
33 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
34 import com.intellij.psi.*;
35 import com.intellij.psi.util.PsiTreeUtil;
36 import com.intellij.psi.util.PsiUtil;
37 import com.intellij.psi.util.PsiUtilBase;
38 import com.intellij.util.IncorrectOperationException;
39 import org.jetbrains.annotations.NonNls;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
43 import javax.swing.*;
44 import javax.swing.event.ChangeEvent;
45 import javax.swing.event.ChangeListener;
46 import java.awt.*;
48 public class CanBeFinalInspection extends GlobalJavaInspectionTool {
49 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.canBeFinal.CanBeFinalInspection");
51 public boolean REPORT_CLASSES = false;
52 public boolean REPORT_METHODS = false;
53 public boolean REPORT_FIELDS = true;
54 public static final String DISPLAY_NAME = InspectionsBundle.message("inspection.can.be.final.display.name");
55 @NonNls public static final String SHORT_NAME = "CanBeFinal";
56 @NonNls private static final String QUICK_FIX_NAME = InspectionsBundle.message("inspection.can.be.final.accept.quickfix");
58 private class OptionsPanel extends JPanel {
59 private final JCheckBox myReportClassesCheckbox;
60 private final JCheckBox myReportMethodsCheckbox;
61 private final JCheckBox myReportFieldsCheckbox;
63 private OptionsPanel() {
64 super(new GridBagLayout());
66 GridBagConstraints gc = new GridBagConstraints();
67 gc.weighty = 0;
68 gc.weightx = 1;
69 gc.fill = GridBagConstraints.HORIZONTAL;
70 gc.anchor = GridBagConstraints.NORTHWEST;
73 myReportClassesCheckbox = new JCheckBox(InspectionsBundle.message("inspection.can.be.final.option"));
74 myReportClassesCheckbox.setSelected(REPORT_CLASSES);
75 myReportClassesCheckbox.getModel().addChangeListener(new ChangeListener() {
76 public void stateChanged(ChangeEvent e) {
77 REPORT_CLASSES = myReportClassesCheckbox.isSelected();
79 });
80 gc.gridy = 0;
81 add(myReportClassesCheckbox, gc);
83 myReportMethodsCheckbox = new JCheckBox(InspectionsBundle.message("inspection.can.be.final.option1"));
84 myReportMethodsCheckbox.setSelected(REPORT_METHODS);
85 myReportMethodsCheckbox.getModel().addChangeListener(new ChangeListener() {
86 public void stateChanged(ChangeEvent e) {
87 REPORT_METHODS = myReportMethodsCheckbox.isSelected();
89 });
90 gc.gridy++;
91 add(myReportMethodsCheckbox, gc);
93 myReportFieldsCheckbox = new JCheckBox(InspectionsBundle.message("inspection.can.be.final.option2"));
94 myReportFieldsCheckbox.setSelected(REPORT_FIELDS);
95 myReportFieldsCheckbox.getModel().addChangeListener(new ChangeListener() {
96 public void stateChanged(ChangeEvent e) {
97 REPORT_FIELDS = myReportFieldsCheckbox.isSelected();
99 });
101 gc.weighty = 1;
102 gc.gridy++;
103 add(myReportFieldsCheckbox, gc);
107 public boolean isReportClasses() {
108 return REPORT_CLASSES;
111 public boolean isReportMethods() {
112 return REPORT_METHODS;
115 public boolean isReportFields() {
116 return REPORT_FIELDS;
119 public JComponent createOptionsPanel() {
120 return new OptionsPanel();
123 @Nullable
124 public RefGraphAnnotator getAnnotator(final RefManager refManager) {
125 return new CanBeFinalAnnotator(refManager);
129 @Nullable
130 public CommonProblemDescriptor[] checkElement(final RefEntity refEntity,
131 final AnalysisScope scope,
132 final InspectionManager manager,
133 final GlobalInspectionContext globalContext,
134 final ProblemDescriptionsProcessor processor) {
135 if (refEntity instanceof RefJavaElement) {
136 final RefJavaElement refElement = (RefJavaElement)refEntity;
137 if (refElement instanceof RefParameter) return null;
138 if (!refElement.isReferenced()) return null;
139 if (refElement.isSyntheticJSP()) return null;
140 if (refElement.isFinal()) return null;
141 if (!((RefElementImpl)refElement).checkFlag(CanBeFinalAnnotator.CAN_BE_FINAL_MASK)) return null;
143 PsiIdentifier psiIdentifier = null;
144 if (refElement instanceof RefClass) {
145 RefClass refClass = (RefClass)refElement;
146 if (refClass.isInterface() || refClass.isAnonymous() || refClass.isAbstract()) return null;
147 if (!isReportClasses()) return null;
148 psiIdentifier = refClass.getElement().getNameIdentifier();
150 else if (refElement instanceof RefMethod) {
151 RefMethod refMethod = (RefMethod)refElement;
152 if (refMethod.getOwnerClass().isFinal()) return null;
153 if (!isReportMethods()) return null;
154 psiIdentifier = ((PsiMethod)refMethod.getElement()).getNameIdentifier();
156 else if (refElement instanceof RefField) {
157 if (!isReportFields()) return null;
158 psiIdentifier = ((RefField)refElement).getElement().getNameIdentifier();
162 if (psiIdentifier != null) {
163 return new ProblemDescriptor[]{manager.createProblemDescriptor(psiIdentifier, InspectionsBundle.message(
164 "inspection.export.results.can.be.final.description"), new AcceptSuggested(globalContext.getRefManager()),
165 ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false)};
168 return null;
171 protected boolean queryExternalUsagesRequests(final RefManager manager, final GlobalJavaInspectionContext globalContext,
172 final ProblemDescriptionsProcessor problemsProcessor) {
173 for (RefElement entryPoint : globalContext.getEntryPointsManager(manager).getEntryPoints()) {
174 problemsProcessor.ignoreElement(entryPoint);
177 manager.iterate(new RefJavaVisitor() {
178 @Override public void visitElement(RefEntity refEntity) {
179 if (problemsProcessor.getDescriptions(refEntity) == null) return;
180 refEntity.accept(new RefJavaVisitor() {
181 @Override public void visitMethod(final RefMethod refMethod) {
182 if (!refMethod.isStatic() && !PsiModifier.PRIVATE.equals(refMethod.getAccessModifier()) &&
183 !(refMethod instanceof RefImplicitConstructor)) {
184 globalContext.enqueueDerivedMethodsProcessor(refMethod, new GlobalJavaInspectionContext.DerivedMethodsProcessor() {
185 public boolean process(PsiMethod derivedMethod) {
186 ((RefElementImpl)refMethod).setFlag(false, CanBeFinalAnnotator.CAN_BE_FINAL_MASK);
187 problemsProcessor.ignoreElement(refMethod);
188 return false;
194 @Override public void visitClass(final RefClass refClass) {
195 if (!refClass.isAnonymous()) {
196 globalContext.enqueueDerivedClassesProcessor(refClass, new GlobalJavaInspectionContext.DerivedClassesProcessor() {
197 public boolean process(PsiClass inheritor) {
198 ((RefClassImpl)refClass).setFlag(false, CanBeFinalAnnotator.CAN_BE_FINAL_MASK);
199 problemsProcessor.ignoreElement(refClass);
200 return false;
206 @Override public void visitField(final RefField refField) {
207 globalContext.enqueueFieldUsagesProcessor(refField, new GlobalJavaInspectionContext.UsagesProcessor() {
208 public boolean process(PsiReference psiReference) {
209 PsiElement expression = psiReference.getElement();
210 if (expression instanceof PsiReferenceExpression && PsiUtil.isAccessedForWriting((PsiExpression)expression)) {
211 ((RefFieldImpl)refField).setFlag(false, CanBeFinalAnnotator.CAN_BE_FINAL_MASK);
212 problemsProcessor.ignoreElement(refField);
213 return false;
215 return true;
224 return false;
228 @Nullable
229 public QuickFix getQuickFix(final String hint) {
230 return new AcceptSuggested(null);
233 @NotNull
234 public String getDisplayName() {
235 return DISPLAY_NAME;
238 @NotNull
239 public String getGroupDisplayName() {
240 return GroupNames.DECLARATION_REDUNDANCY;
243 @NotNull
244 public String getShortName() {
245 return SHORT_NAME;
248 private static class AcceptSuggested implements LocalQuickFix {
249 private final RefManager myManager;
251 public AcceptSuggested(final RefManager manager) {
252 myManager = manager;
255 @NotNull
256 public String getName() {
257 return QUICK_FIX_NAME;
260 @NotNull
261 public String getFamilyName() {
262 return getName();
265 public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
266 if (ReadonlyStatusHandler.getInstance(project)
267 .ensureFilesWritable(PsiUtilBase.getVirtualFile(descriptor.getPsiElement())).hasReadonlyFiles()) return;
268 final PsiElement element = descriptor.getPsiElement();
269 final PsiModifierListOwner psiElement = PsiTreeUtil.getParentOfType(element, PsiModifierListOwner.class);
270 if (psiElement != null) {
271 RefJavaElement refElement = (RefJavaElement)(myManager != null ? myManager.getReference(psiElement) : null);
272 try {
273 if (psiElement instanceof PsiVariable) {
274 ((PsiVariable)psiElement).normalizeDeclaration();
276 final PsiModifierList modifierList = psiElement.getModifierList();
277 LOG.assertTrue(modifierList != null);
278 modifierList.setModifierProperty(PsiModifier.FINAL, true);
280 catch (IncorrectOperationException e) {
281 LOG.error(e);
284 if (refElement != null) {
285 RefJavaUtil.getInstance().setIsFinal(refElement, true);