blob: 450b4c2a4418a0b7c6535ebc6c7c118c74936e02 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.lang.Language;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider;
import com.intellij.util.ResourceUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xmlb.SerializationFilter;
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
import com.intellij.util.xmlb.XmlSerializationException;
import com.intellij.util.xmlb.XmlSerializer;
import gnu.trove.THashSet;
import gnu.trove.TObjectHashingStrategy;
import org.jdom.Element;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.*;
/**
* @author anna
* @since 28-Nov-2005
*/
@SuppressWarnings("JavadocReference")
public abstract class InspectionProfileEntry implements BatchSuppressableTool{
public static final String GENERAL_GROUP_NAME = InspectionsBundle.message("inspection.general.tools.group.name");
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.InspectionProfileEntry");
private static final SkipDefaultValuesSerializationFilters DEFAULT_FILTER = new SkipDefaultValuesSerializationFilters();
private static Set<String> ourBlackList = null;
private static final Object BLACK_LIST_LOCK = new Object();
private Boolean myUseNewSerializer = null;
@NonNls
@Nullable
public String getAlternativeID() {
return null;
}
@Override
public boolean isSuppressedFor(@NotNull PsiElement element) {
Set<InspectionSuppressor> suppressors = getSuppressors(element);
for (InspectionSuppressor suppressor : suppressors) {
if (isSuppressed(suppressor, element)) {
return true;
}
}
return false;
}
protected String getSuppressId() {
return getShortName();
}
@NotNull
@Override
public SuppressQuickFix[] getBatchSuppressActions(@Nullable PsiElement element) {
if (element != null) {
THashSet<SuppressQuickFix> fixes = new THashSet<SuppressQuickFix>(new TObjectHashingStrategy<SuppressQuickFix>() {
@Override
public int computeHashCode(SuppressQuickFix object) {
return object.getName().hashCode();
}
@Override
public boolean equals(SuppressQuickFix o1, SuppressQuickFix o2) {
return o1.getName().equals(o2.getName());
}
});
Set<InspectionSuppressor> suppressors = getSuppressors(element);
for (InspectionSuppressor suppressor : suppressors) {
SuppressQuickFix[] actions = suppressor.getSuppressActions(element, getShortName());
fixes.addAll(Arrays.asList(actions));
}
return fixes.toArray(new SuppressQuickFix[fixes.size()]);
}
return SuppressQuickFix.EMPTY_ARRAY;
}
private boolean isSuppressed(@Nullable InspectionSuppressor suppressor, @NotNull PsiElement element) {
if (suppressor == null) return false;
String toolId = getSuppressId();
if (suppressor.isSuppressedFor(element, toolId)) {
return true;
}
final String alternativeId = getAlternativeID();
return alternativeId != null && !alternativeId.equals(toolId) && suppressor.isSuppressedFor(element, alternativeId);
}
public static Set<InspectionSuppressor> getSuppressors(@NotNull PsiElement element) {
FileViewProvider viewProvider = element.getContainingFile().getViewProvider();
final InspectionSuppressor elementLanguageSuppressor = LanguageInspectionSuppressors.INSTANCE.forLanguage(element.getLanguage());
if (viewProvider instanceof TemplateLanguageFileViewProvider) {
LinkedHashSet<InspectionSuppressor> suppressors = new LinkedHashSet<InspectionSuppressor>();
ContainerUtil.addIfNotNull(suppressors, LanguageInspectionSuppressors.INSTANCE.forLanguage(viewProvider.getBaseLanguage()));
for (Language language : viewProvider.getLanguages()) {
ContainerUtil.addIfNotNull(suppressors, LanguageInspectionSuppressors.INSTANCE.forLanguage(language));
}
ContainerUtil.addIfNotNull(suppressors, elementLanguageSuppressor);
return suppressors;
}
return elementLanguageSuppressor != null ? Collections.singleton(elementLanguageSuppressor) : Collections.<InspectionSuppressor>emptySet();
}
public void cleanup(Project project) {
}
interface DefaultNameProvider {
@Nullable String getDefaultShortName();
@Nullable String getDefaultDisplayName();
@Nullable String getDefaultGroupDisplayName();
}
protected volatile DefaultNameProvider myNameProvider = null;
/**
* @see com.intellij.codeInspection.InspectionEP#groupDisplayName
* @see com.intellij.codeInspection.InspectionEP#groupKey
* @see com.intellij.codeInspection.InspectionEP#groupBundle
*/
@Nls
@NotNull
public String getGroupDisplayName() {
if (myNameProvider != null) {
final String name = myNameProvider.getDefaultGroupDisplayName();
if (name != null) {
return name;
}
}
LOG.error(getClass() + ": group display name should be overridden or configured via XML " + getClass());
return "";
}
/**
* @see com.intellij.codeInspection.InspectionEP#groupPath
*/
@NotNull
public String[] getGroupPath() {
String groupDisplayName = getGroupDisplayName();
if (groupDisplayName.isEmpty()) {
groupDisplayName = GENERAL_GROUP_NAME;
}
return new String[]{groupDisplayName};
}
/**
* @see com.intellij.codeInspection.InspectionEP#displayName
* @see com.intellij.codeInspection.InspectionEP#key
* @see com.intellij.codeInspection.InspectionEP#bundle
*/
@Nls
@NotNull
public String getDisplayName() {
if (myNameProvider != null) {
final String name = myNameProvider.getDefaultDisplayName();
if (name != null) {
return name;
}
}
LOG.error(getClass() + ": display name should be overridden or configured via XML " + getClass());
return "";
}
/**
* DO NOT OVERRIDE this method.
*
* @see com.intellij.codeInspection.InspectionEP#shortName
*/
@NonNls
@NotNull
public String getShortName() {
if (myNameProvider != null) {
final String name = myNameProvider.getDefaultShortName();
if (name != null) {
return name;
}
}
return getShortName(getClass().getSimpleName());
}
@NotNull
public static String getShortName(@NotNull String className) {
return StringUtil.trimEnd(StringUtil.trimEnd(className, "Inspection"),"InspectionBase");
}
/**
* DO NOT OVERRIDE this method.
*
* @see com.intellij.codeInspection.InspectionEP#level
*/
@NotNull
public HighlightDisplayLevel getDefaultLevel() {
return HighlightDisplayLevel.WARNING;
}
/**
* DO NOT OVERRIDE this method.
*
* @see com.intellij.codeInspection.InspectionEP#enabledByDefault
*/
public boolean isEnabledByDefault() {
return false;
}
/**
* This method is called each time UI is shown.
* @return null if no UI options required.
*/
@Nullable
public JComponent createOptionsPanel() {
return null;
}
/**
* Read in settings from XML config.
* Default implementation uses XmlSerializer so you may use public fields (like <code>int TOOL_OPTION</code>)
* and bean-style getters/setters (like <code>int getToolOption(), void setToolOption(int)</code>) to store your options.
*
* @param node to read settings from.
* @throws InvalidDataException if the loaded data was not valid.
*/
@SuppressWarnings("deprecation")
public void readSettings(@NotNull Element node) throws InvalidDataException {
if (useNewSerializer()) {
try {
XmlSerializer.deserializeInto(this, node);
}
catch (XmlSerializationException e) {
throw new InvalidDataException(e);
}
}
else {
//noinspection UnnecessaryFullyQualifiedName
com.intellij.openapi.util.DefaultJDOMExternalizer.readExternal(this, node);
}
}
/**
* Store current settings in XML config.
* Default implementation uses XmlSerializer so you may use public fields (like <code>int TOOL_OPTION</code>)
* and bean-style getters/setters (like <code>int getToolOption(), void setToolOption(int)</code>) to store your options.
*
* @param node to store settings to.
* @throws WriteExternalException if no data should be saved for this component.
*/
@SuppressWarnings("deprecation")
public void writeSettings(@NotNull Element node) throws WriteExternalException {
if (useNewSerializer()) {
XmlSerializer.serializeInto(this, node, getSerializationFilter());
}
else {
//noinspection UnnecessaryFullyQualifiedName
com.intellij.openapi.util.DefaultJDOMExternalizer.writeExternal(this, node);
}
}
private synchronized boolean useNewSerializer() {
if (myUseNewSerializer == null) {
myUseNewSerializer = !getBlackList().contains(getClass().getName());
}
return myUseNewSerializer;
}
private static void loadBlackList() {
ourBlackList = ContainerUtil.newHashSet();
final URL url = InspectionProfileEntry.class.getResource("inspection-black-list.txt");
if (url == null) {
LOG.error("Resource not found");
return;
}
try {
final BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
try {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.isEmpty()) ourBlackList.add(line);
}
}
finally {
reader.close();
}
}
catch (IOException e) {
LOG.error("Unable to load resource: " + url, e);
}
}
@NotNull
public static Collection<String> getBlackList() {
synchronized (BLACK_LIST_LOCK) {
if (ourBlackList == null) {
loadBlackList();
}
return ourBlackList;
}
}
/**
* Returns filter used to omit default values on saving inspection settings.
* Default implementation uses SkipDefaultValuesSerializationFilters.
*
* @return serialization filter.
*/
@SuppressWarnings("MethodMayBeStatic")
@Nullable
protected SerializationFilter getSerializationFilter() {
return DEFAULT_FILTER;
}
/**
* Initialize inspection with project. Is called on project opened for all profiles as well as on profile creation.
*
* @param project to be associated with this entry
* @deprecated this won't work for inspections configured via {@link com.intellij.codeInspection.InspectionEP}
*/
public void projectOpened(@NotNull Project project) {
}
/**
* Cleanup inspection settings corresponding to the project. Is called on project closed for all profiles as well as on profile deletion.
*
* @param project to be disassociated from this entry
* @deprecated this won't work for inspections configured via {@link com.intellij.codeInspection.InspectionEP}
*/
public void projectClosed(@NotNull Project project) {
}
/**
* Override this method to return a html inspection description. Otherwise it will be loaded from resources using ID.
*
* @return hard-code inspection description.
*/
@Nullable
public String getStaticDescription() {
return null;
}
@Nullable
public String getDescriptionFileName() {
return null;
}
@Nullable
protected URL getDescriptionUrl() {
final String fileName = getDescriptionFileName();
if (fileName == null) return null;
return ResourceUtil.getResource(getDescriptionContextClass(), "/inspectionDescriptions", fileName);
}
@NotNull
protected Class<? extends InspectionProfileEntry> getDescriptionContextClass() {
return getClass();
}
public boolean isInitialized() {
return true;
}
/**
* @return short name of tool whose results will be used
*/
@Nullable
public String getMainToolId() {
return null;
}
@Nullable
public String loadDescription() {
final String description = getStaticDescription();
if (description != null) return description;
try {
URL descriptionUrl = getDescriptionUrl();
if (descriptionUrl == null) return null;
return ResourceUtil.loadText(descriptionUrl);
}
catch (IOException ignored) { }
return null;
}
}