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
.lang
.properties
;
18 import com
.intellij
.codeInsight
.CodeInsightUtilBase
;
19 import com
.intellij
.codeInspection
.*;
20 import com
.intellij
.concurrency
.JobUtil
;
21 import com
.intellij
.lang
.ASTNode
;
22 import com
.intellij
.lang
.properties
.psi
.PropertiesFile
;
23 import com
.intellij
.lang
.properties
.psi
.Property
;
24 import com
.intellij
.openapi
.diagnostic
.Logger
;
25 import com
.intellij
.openapi
.editor
.Document
;
26 import com
.intellij
.openapi
.editor
.Editor
;
27 import com
.intellij
.openapi
.module
.Module
;
28 import com
.intellij
.openapi
.module
.ModuleUtil
;
29 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
30 import com
.intellij
.openapi
.progress
.ProgressManager
;
31 import com
.intellij
.openapi
.project
.Project
;
32 import com
.intellij
.psi
.*;
33 import com
.intellij
.psi
.search
.GlobalSearchScope
;
34 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
35 import com
.intellij
.psi
.util
.PsiTreeUtil
;
36 import com
.intellij
.util
.IncorrectOperationException
;
37 import com
.intellij
.util
.Processor
;
38 import com
.intellij
.util
.SmartList
;
39 import org
.jetbrains
.annotations
.NonNls
;
40 import org
.jetbrains
.annotations
.NotNull
;
41 import org
.jetbrains
.annotations
.Nullable
;
43 import java
.util
.List
;
48 public class UnusedPropertyInspection
extends LocalInspectionTool
implements CustomSuppressableInspectionTool
{
49 private static final Logger LOG
= Logger
.getInstance("#com.intellij.lang.properties.UnusedPropertyInspection");
51 public String
getGroupDisplayName() {
52 return PropertiesBundle
.message("properties.files.inspection.group.display.name");
56 public String
getDisplayName() {
57 return PropertiesBundle
.message("unused.property.inspection.display.name");
61 public String
getShortName() {
62 return "UnusedProperty";
65 public ProblemDescriptor
[] checkFile(@NotNull PsiFile file
, @NotNull final InspectionManager manager
, final boolean isOnTheFly
) {
66 if (!(file
instanceof PropertiesFile
)) return null;
67 final List
<Property
> properties
= ((PropertiesFile
)file
).getProperties();
68 Module module
= ModuleUtil
.findModuleForPsiElement(file
);
69 if (module
== null) return null;
70 final List
<ProblemDescriptor
> descriptors
= new SmartList
<ProblemDescriptor
>();
72 final GlobalSearchScope searchScope
= GlobalSearchScope
.moduleWithDependentsScope(module
);
73 final ProgressIndicator original
= ProgressManager
.getInstance().getProgressIndicator();
74 JobUtil
.invokeConcurrentlyUnderMyProgress(properties
, new Processor
<Property
>() {
75 public boolean process(final Property property
) {
76 if (original
!= null) {
77 if (original
.isCanceled()) return false;
78 original
.setText(PropertiesBundle
.message("searching.for.property.key.progress.text", property
.getUnescapedKey()));
81 final PsiReference usage
= ReferencesSearch
.search(property
, searchScope
, false).findFirst();
83 final ASTNode propertyNode
= property
.getNode();
84 assert propertyNode
!= null;
86 ASTNode
[] nodes
= propertyNode
.getChildren(null);
87 PsiElement key
= nodes
.length
== 0 ? property
: nodes
[0].getPsi();
88 String description
= PropertiesBundle
.message("unused.property.problem.descriptor.name");
89 ProblemDescriptor descriptor
= manager
.createProblemDescriptor(key
, description
, RemovePropertyLocalFix
.INSTANCE
, ProblemHighlightType
.LIKE_UNUSED_SYMBOL
,
91 synchronized (descriptors
) {
92 descriptors
.add(descriptor
);
98 }, "Searching properties usages");
100 synchronized (descriptors
) {
101 return descriptors
.toArray(new ProblemDescriptor
[descriptors
.size()]);
106 public SuppressIntentionAction
[] getSuppressActions(final PsiElement element
) {
107 return new SuppressIntentionAction
[] {new SuppressSinglePropertyFix(), new SuppressForFile()};
110 public boolean isSuppressedFor(PsiElement element
) {
111 Property property
= PsiTreeUtil
.getParentOfType(element
, Property
.class, false);
113 if (property
== null) {
114 PsiFile containingFile
= element
.getContainingFile();
115 if (containingFile
instanceof PropertiesFile
) {
116 file
= (PropertiesFile
)containingFile
;
123 PsiElement prev
= property
.getPrevSibling();
124 while (prev
instanceof PsiWhiteSpace
|| prev
instanceof PsiComment
) {
125 if (prev
instanceof PsiComment
) {
126 @NonNls String text
= prev
.getText();
127 if (text
.contains("suppress") && text
.contains("\"unused property\"")) return true;
129 prev
= prev
.getPrevSibling();
131 file
= property
.getContainingFile();
133 PsiElement leaf
= file
.findElementAt(0);
134 while (leaf
instanceof PsiWhiteSpace
) leaf
= leaf
.getNextSibling();
136 while (leaf
instanceof PsiComment
) {
137 @NonNls String text
= leaf
.getText();
138 if (text
.contains("suppress") && text
.contains("\"unused property\"") && text
.contains("file")) {
141 leaf
= leaf
.getNextSibling();
147 private static class SuppressSinglePropertyFix
extends SuppressIntentionAction
{
150 public String
getText() {
151 return PropertiesBundle
.message("unused.property.suppress.for.property");
155 public String
getFamilyName() {
156 return PropertiesBundle
.message("unused.property.suppress.for.property");
159 public boolean isAvailable(@NotNull final Project project
, final Editor editor
, @Nullable final PsiElement element
) {
160 final Property property
= PsiTreeUtil
.getParentOfType(element
, Property
.class);
161 return property
!= null && property
.isValid();
164 public void invoke(final Project project
, final Editor editor
, final PsiElement element
) throws IncorrectOperationException
{
165 final PsiFile file
= element
.getContainingFile();
166 if (!CodeInsightUtilBase
.prepareFileForWrite(file
)) return;
168 final Property property
= PsiTreeUtil
.getParentOfType(element
, Property
.class);
169 LOG
.assertTrue(property
!= null);
170 final int start
= property
.getTextRange().getStartOffset();
172 @NonNls final Document doc
= PsiDocumentManager
.getInstance(project
).getDocument(file
);
173 LOG
.assertTrue(doc
!= null);
174 final int line
= doc
.getLineNumber(start
);
175 final int lineStart
= doc
.getLineStartOffset(line
);
177 doc
.insertString(lineStart
, "# suppress inspection \"unused property\"\n");
181 private static class SuppressForFile
extends SuppressIntentionAction
{
183 public String
getText() {
184 return PropertiesBundle
.message("unused.property.suppress.for.file");
188 public String
getFamilyName() {
189 return PropertiesBundle
.message("unused.property.suppress.for.file");
192 public boolean isAvailable(@NotNull final Project project
, final Editor editor
, @Nullable final PsiElement element
) {
193 return element
!= null && element
.isValid() && element
.getContainingFile() instanceof PropertiesFile
;
196 public void invoke(final Project project
, final Editor editor
, final PsiElement element
) throws IncorrectOperationException
{
197 final PsiFile file
= element
.getContainingFile();
198 if (!CodeInsightUtilBase
.prepareFileForWrite(file
)) return;
200 @NonNls final Document doc
= PsiDocumentManager
.getInstance(project
).getDocument(file
);
202 doc
.insertString(0, "# suppress inspection \"unused property\" for whole file\n");