Inspections - pass onTheFly into ProblemDescriptors & use it to create LAZY refs...
[fedora-idea.git] / plugins / properties / src / com / intellij / lang / properties / UnusedPropertyInspection.java
blobe2b0b02a06a27ed8a1a902826b0d9e8047c5fc0d
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.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;
45 /**
46 * @author cdr
48 public class UnusedPropertyInspection extends LocalInspectionTool implements CustomSuppressableInspectionTool {
49 private static final Logger LOG = Logger.getInstance("#com.intellij.lang.properties.UnusedPropertyInspection");
50 @NotNull
51 public String getGroupDisplayName() {
52 return PropertiesBundle.message("properties.files.inspection.group.display.name");
55 @NotNull
56 public String getDisplayName() {
57 return PropertiesBundle.message("unused.property.inspection.display.name");
60 @NotNull
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();
82 if (usage == null) {
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,
90 isOnTheFly);
91 synchronized (descriptors) {
92 descriptors.add(descriptor);
96 return true;
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);
112 PropertiesFile file;
113 if (property == null) {
114 PsiFile containingFile = element.getContainingFile();
115 if (containingFile instanceof PropertiesFile) {
116 file = (PropertiesFile)containingFile;
118 else {
119 return false;
122 else {
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")) {
139 return true;
141 leaf = leaf.getNextSibling();
144 return false;
147 private static class SuppressSinglePropertyFix extends SuppressIntentionAction {
149 @NotNull
150 public String getText() {
151 return PropertiesBundle.message("unused.property.suppress.for.property");
154 @NotNull
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 {
182 @NotNull
183 public String getText() {
184 return PropertiesBundle.message("unused.property.suppress.for.file");
187 @NotNull
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");