PY-11855 Run manage.py task improvements
[idea/community.git] / platform / analysis-api / src / com / intellij / codeInspection / InspectionProfileEntry.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.codeInspection;
17
18 import com.intellij.codeHighlighting.HighlightDisplayLevel;
19 import com.intellij.lang.Language;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.util.InvalidDataException;
23 import com.intellij.openapi.util.WriteExternalException;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.psi.FileViewProvider;
26 import com.intellij.psi.PsiElement;
27 import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider;
28 import com.intellij.util.ResourceUtil;
29 import com.intellij.util.containers.ContainerUtil;
30 import com.intellij.util.xmlb.SerializationFilter;
31 import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
32 import com.intellij.util.xmlb.XmlSerializationException;
33 import com.intellij.util.xmlb.XmlSerializer;
34 import gnu.trove.THashSet;
35 import gnu.trove.TObjectHashingStrategy;
36 import org.jdom.Element;
37 import org.jetbrains.annotations.Nls;
38 import org.jetbrains.annotations.NonNls;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41
42 import javax.swing.*;
43 import java.io.BufferedReader;
44 import java.io.IOException;
45 import java.io.InputStreamReader;
46 import java.net.URL;
47 import java.util.*;
48
49 /**
50  * @author anna
51  * @since 28-Nov-2005
52  */
53 @SuppressWarnings("JavadocReference")
54 public abstract class InspectionProfileEntry implements BatchSuppressableTool{
55   public static final String GENERAL_GROUP_NAME = InspectionsBundle.message("inspection.general.tools.group.name");
56
57   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.InspectionProfileEntry");
58
59   private static final SkipDefaultValuesSerializationFilters DEFAULT_FILTER = new SkipDefaultValuesSerializationFilters();
60   private static Set<String> ourBlackList = null;
61   private static final Object BLACK_LIST_LOCK = new Object();
62   private Boolean myUseNewSerializer = null;
63
64   @NonNls
65   @Nullable
66   public String getAlternativeID() {
67     return null;
68   }
69
70   @Override
71   public boolean isSuppressedFor(@NotNull PsiElement element) {
72     Set<InspectionSuppressor> suppressors = getSuppressors(element);
73     String toolId = getSuppressId();
74     for (InspectionSuppressor suppressor : suppressors) {
75       if (isSuppressed(toolId, suppressor, element)) {
76         return true;
77       }
78     }
79     return false;
80   }
81
82   @NotNull
83   protected String getSuppressId() {
84     return getShortName();
85   }
86
87   @NotNull
88   @Override
89   public SuppressQuickFix[] getBatchSuppressActions(@Nullable PsiElement element) {
90     if (element == null) {
91       return SuppressQuickFix.EMPTY_ARRAY;
92     }
93     Set<SuppressQuickFix> fixes = new THashSet<SuppressQuickFix>(new TObjectHashingStrategy<SuppressQuickFix>() {
94       @Override
95       public int computeHashCode(SuppressQuickFix object) {
96         return object.getName().hashCode();
97       }
98
99       @Override
100       public boolean equals(SuppressQuickFix o1, SuppressQuickFix o2) {
101         return o1.getName().equals(o2.getName());
102       }
103     });
104     Set<InspectionSuppressor> suppressors = getSuppressors(element);
105     for (InspectionSuppressor suppressor : suppressors) {
106       SuppressQuickFix[] actions = suppressor.getSuppressActions(element, getShortName());
107       fixes.addAll(Arrays.asList(actions));
108     }
109     return fixes.toArray(new SuppressQuickFix[fixes.size()]);
110   }
111
112   private boolean isSuppressed(@NotNull String toolId,
113                                @NotNull InspectionSuppressor suppressor,
114                                @NotNull PsiElement element) {
115     if (suppressor.isSuppressedFor(element, toolId)) {
116       return true;
117     }
118     final String alternativeId = getAlternativeID();
119     return alternativeId != null && !alternativeId.equals(toolId) && suppressor.isSuppressedFor(element, alternativeId);
120   }
121
122   @NotNull
123   public static Set<InspectionSuppressor> getSuppressors(@NotNull PsiElement element) {
124     FileViewProvider viewProvider = element.getContainingFile().getViewProvider();
125     final InspectionSuppressor elementLanguageSuppressor = LanguageInspectionSuppressors.INSTANCE.forLanguage(element.getLanguage());
126     if (viewProvider instanceof TemplateLanguageFileViewProvider) {
127       Set<InspectionSuppressor> suppressors = new LinkedHashSet<InspectionSuppressor>();
128       ContainerUtil.addIfNotNull(suppressors, LanguageInspectionSuppressors.INSTANCE.forLanguage(viewProvider.getBaseLanguage()));
129       for (Language language : viewProvider.getLanguages()) {
130         ContainerUtil.addIfNotNull(suppressors, LanguageInspectionSuppressors.INSTANCE.forLanguage(language));
131       }
132       ContainerUtil.addIfNotNull(suppressors, elementLanguageSuppressor);
133       return suppressors;
134     }
135     return elementLanguageSuppressor != null ? Collections.singleton(elementLanguageSuppressor) : Collections.<InspectionSuppressor>emptySet();
136   }
137
138   public void cleanup(Project project) {
139
140   }
141
142   interface DefaultNameProvider {
143     @Nullable String getDefaultShortName();
144     @Nullable String getDefaultDisplayName();
145     @Nullable String getDefaultGroupDisplayName();
146   }
147
148   protected volatile DefaultNameProvider myNameProvider = null;
149
150   /**
151    * @see com.intellij.codeInspection.InspectionEP#groupDisplayName
152    * @see com.intellij.codeInspection.InspectionEP#groupKey
153    * @see com.intellij.codeInspection.InspectionEP#groupBundle
154    */
155   @Nls
156   @NotNull
157   public String getGroupDisplayName() {
158     if (myNameProvider != null) {
159       final String name = myNameProvider.getDefaultGroupDisplayName();
160       if (name != null) {
161         return name;
162       }
163     }
164     LOG.error(getClass() + ": group display name should be overridden or configured via XML " + getClass());
165     return "";
166   }
167
168   /**
169    * @see com.intellij.codeInspection.InspectionEP#groupPath
170    */
171   @NotNull
172   public String[] getGroupPath() {
173     String groupDisplayName = getGroupDisplayName();
174     if (groupDisplayName.isEmpty()) {
175       groupDisplayName = GENERAL_GROUP_NAME;
176     }
177     return new String[]{groupDisplayName};
178   }
179
180   /**
181    * @see com.intellij.codeInspection.InspectionEP#displayName
182    * @see com.intellij.codeInspection.InspectionEP#key
183    * @see com.intellij.codeInspection.InspectionEP#bundle
184    */
185   @Nls
186   @NotNull
187   public String getDisplayName() {
188     if (myNameProvider != null) {
189       final String name = myNameProvider.getDefaultDisplayName();
190       if (name != null) {
191         return name;
192       }
193     }
194     LOG.error(getClass() + ": display name should be overridden or configured via XML " + getClass());
195     return "";
196   }
197
198   /**
199    * DO NOT OVERRIDE this method.
200    *
201    * @see com.intellij.codeInspection.InspectionEP#shortName
202    */
203   @NonNls
204   @NotNull
205   public String getShortName() {
206     if (myNameProvider != null) {
207       final String name = myNameProvider.getDefaultShortName();
208       if (name != null) {
209         return name;
210       }
211     }
212     return getShortName(getClass().getSimpleName());
213   }
214
215   @NotNull
216   public static String getShortName(@NotNull String className) {
217     return StringUtil.trimEnd(StringUtil.trimEnd(className, "Inspection"),"InspectionBase");
218   }
219
220   /**
221    * DO NOT OVERRIDE this method.
222    *
223    * @see com.intellij.codeInspection.InspectionEP#level
224    */
225   @NotNull
226   public HighlightDisplayLevel getDefaultLevel() {
227     return HighlightDisplayLevel.WARNING;
228   }
229
230   /**
231    * DO NOT OVERRIDE this method.
232    *
233    * @see com.intellij.codeInspection.InspectionEP#enabledByDefault
234    */
235   public boolean isEnabledByDefault() {
236     return false;
237   }
238
239   /**
240    * This method is called each time UI is shown.
241    * @return null if no UI options required.
242    */
243   @Nullable
244   public JComponent createOptionsPanel() {
245     return null;
246   }
247
248   /**
249    * Read in settings from XML config.
250    * Default implementation uses XmlSerializer so you may use public fields (like <code>int TOOL_OPTION</code>)
251    * and bean-style getters/setters (like <code>int getToolOption(), void setToolOption(int)</code>) to store your options.
252    *
253    * @param node to read settings from.
254    * @throws InvalidDataException if the loaded data was not valid.
255    */
256   @SuppressWarnings("deprecation")
257   public void readSettings(@NotNull Element node) throws InvalidDataException {
258     if (useNewSerializer()) {
259       try {
260         XmlSerializer.deserializeInto(this, node);
261       }
262       catch (XmlSerializationException e) {
263         throw new InvalidDataException(e);
264       }
265     }
266     else {
267       //noinspection UnnecessaryFullyQualifiedName
268       com.intellij.openapi.util.DefaultJDOMExternalizer.readExternal(this, node);
269     }
270   }
271
272   /**
273    * Store current settings in XML config.
274    * Default implementation uses XmlSerializer so you may use public fields (like <code>int TOOL_OPTION</code>)
275    * and bean-style getters/setters (like <code>int getToolOption(), void setToolOption(int)</code>) to store your options.
276    *
277    * @param node to store settings to.
278    * @throws WriteExternalException if no data should be saved for this component.
279    */
280   @SuppressWarnings("deprecation")
281   public void writeSettings(@NotNull Element node) throws WriteExternalException {
282     if (useNewSerializer()) {
283       XmlSerializer.serializeInto(this, node, getSerializationFilter());
284     }
285     else {
286       //noinspection UnnecessaryFullyQualifiedName
287       com.intellij.openapi.util.DefaultJDOMExternalizer.writeExternal(this, node);
288     }
289   }
290
291   private synchronized boolean useNewSerializer() {
292     if (myUseNewSerializer == null) {
293       myUseNewSerializer = !getBlackList().contains(getClass().getName());
294     }
295     return myUseNewSerializer;
296   }
297
298   private static void loadBlackList() {
299     ourBlackList = ContainerUtil.newHashSet();
300
301     final URL url = InspectionProfileEntry.class.getResource("inspection-black-list.txt");
302     if (url == null) {
303       LOG.error("Resource not found");
304       return;
305     }
306
307     try {
308       final BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
309       try {
310         String line;
311         while ((line = reader.readLine()) != null) {
312           line = line.trim();
313           if (!line.isEmpty()) ourBlackList.add(line);
314         }
315       }
316       finally {
317         reader.close();
318       }
319     }
320     catch (IOException e) {
321       LOG.error("Unable to load resource: " + url, e);
322     }
323   }
324
325   @NotNull
326   public static Collection<String> getBlackList() {
327     synchronized (BLACK_LIST_LOCK) {
328       if (ourBlackList == null) {
329         loadBlackList();
330       }
331       return ourBlackList;
332     }
333   }
334
335   /**
336    * Returns filter used to omit default values on saving inspection settings.
337    * Default implementation uses SkipDefaultValuesSerializationFilters.
338    *
339    * @return serialization filter.
340    */
341   @SuppressWarnings("MethodMayBeStatic")
342   @Nullable
343   protected SerializationFilter getSerializationFilter() {
344     return DEFAULT_FILTER;
345   }
346
347   /**
348    * Initialize inspection with project. Is called on project opened for all profiles as well as on profile creation.
349    *
350    * @param project to be associated with this entry
351    * @deprecated this won't work for inspections configured via {@link com.intellij.codeInspection.InspectionEP}
352    */
353   public void projectOpened(@NotNull Project project) {
354   }
355
356   /**
357    * Cleanup inspection settings corresponding to the project. Is called on project closed for all profiles as well as on profile deletion.
358    *
359    * @param project to be disassociated from this entry
360    * @deprecated this won't work for inspections configured via {@link com.intellij.codeInspection.InspectionEP}
361    */
362   public void projectClosed(@NotNull Project project) {
363   }
364
365   /**
366    * Override this method to return a html inspection description. Otherwise it will be loaded from resources using ID.
367    *
368    * @return hard-code inspection description.
369    */
370   @Nullable
371   public String getStaticDescription() {
372     return null;
373   }
374
375   @Nullable
376   public String getDescriptionFileName() {
377     return null;
378   }
379
380   @Nullable
381   protected URL getDescriptionUrl() {
382     final String fileName = getDescriptionFileName();
383     if (fileName == null) return null;
384     return ResourceUtil.getResource(getDescriptionContextClass(), "/inspectionDescriptions", fileName);
385   }
386
387   @NotNull
388   protected Class<? extends InspectionProfileEntry> getDescriptionContextClass() {
389     return getClass();
390   }
391
392   public boolean isInitialized() {
393     return true;
394   }
395
396   /**
397    * @return short name of tool whose results will be used
398    */
399   @Nullable
400   public String getMainToolId() {
401     return null;
402   }
403
404   @Nullable
405   public String loadDescription() {
406     final String description = getStaticDescription();
407     if (description != null) return description;
408
409     try {
410       URL descriptionUrl = getDescriptionUrl();
411       if (descriptionUrl == null) return null;
412       return ResourceUtil.loadText(descriptionUrl);
413     }
414     catch (IOException ignored) { }
415
416     return null;
417   }
418 }