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.
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
;
44 import javax
.swing
.event
.ChangeEvent
;
45 import javax
.swing
.event
.ChangeListener
;
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();
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();
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();
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();
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();
124 public RefGraphAnnotator
getAnnotator(final RefManager refManager
) {
125 return new CanBeFinalAnnotator(refManager
);
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)};
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
);
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
);
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
);
229 public QuickFix
getQuickFix(final String hint
) {
230 return new AcceptSuggested(null);
234 public String
getDisplayName() {
239 public String
getGroupDisplayName() {
240 return GroupNames
.DECLARATION_REDUNDANCY
;
244 public String
getShortName() {
248 private static class AcceptSuggested
implements LocalQuickFix
{
249 private final RefManager myManager
;
251 public AcceptSuggested(final RefManager manager
) {
256 public String
getName() {
257 return QUICK_FIX_NAME
;
261 public String
getFamilyName() {
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);
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
) {
284 if (refElement
!= null) {
285 RefJavaUtil
.getInstance().setIsFinal(refElement
, true);