blob: 3e0213cdf631f8a5ea3a8d8f5c8a4aa699160f8f [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.codeInsight.daemon.impl;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.JDOMExternalizable;
import com.intellij.openapi.util.JDOMExternalizableStringList;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.util.concurrency.AtomicFieldUpdater;
import com.intellij.util.containers.ConcurrentHashMap;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TObjectIntHashMap;
import gnu.trove.TObjectIntProcedure;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
/**
* User: anna
* Date: 24-Feb-2006
*/
public class SeverityRegistrar implements JDOMExternalizable, Comparator<HighlightSeverity> {
@NonNls private static final String INFO_TAG = "info";
@NonNls private static final String COLOR_ATTRIBUTE = "color";
private final Map<String, SeverityBasedTextAttributes> myMap = new ConcurrentHashMap<String, SeverityBasedTextAttributes>();
private final Map<String, Color> myRendererColors = new ConcurrentHashMap<String, Color>();
private volatile OrderMap myOrderMap;
private JDOMExternalizableStringList myReadOrder;
private static final Map<String, HighlightInfoType> STANDARD_SEVERITIES = new ConcurrentHashMap<String, HighlightInfoType>();
public SeverityRegistrar() {
}
static {
registerStandard(HighlightInfoType.ERROR, HighlightSeverity.ERROR);
registerStandard(HighlightInfoType.WARNING, HighlightSeverity.WARNING);
registerStandard(HighlightInfoType.INFO, HighlightSeverity.INFO);
registerStandard(HighlightInfoType.WEAK_WARNING, HighlightSeverity.WEAK_WARNING);
registerStandard(HighlightInfoType.GENERIC_WARNINGS_OR_ERRORS_FROM_SERVER, HighlightSeverity.GENERIC_SERVER_ERROR_OR_WARNING);
}
public static void registerStandard(@NotNull HighlightInfoType highlightInfoType, @NotNull HighlightSeverity highlightSeverity) {
STANDARD_SEVERITIES.put(highlightSeverity.getName(), highlightInfoType);
}
@NotNull
public static SeverityRegistrar getSeverityRegistrar(@Nullable Project project) {
return project == null
? InspectionProfileManager.getInstance().getSeverityRegistrar()
: InspectionProjectProfileManager.getInstance(project).getSeverityRegistrar();
}
public void registerSeverity(@NotNull SeverityBasedTextAttributes info, Color renderColor) {
final HighlightSeverity severity = info.getType().getSeverity(null);
myMap.put(severity.getName(), info);
myRendererColors.put(severity.getName(), renderColor);
myOrderMap = null;
HighlightDisplayLevel.registerSeverity(severity, renderColor);
}
public SeverityBasedTextAttributes unregisterSeverity(@NotNull HighlightSeverity severity){
return myMap.remove(severity.getName());
}
@NotNull
public HighlightInfoType.HighlightInfoTypeImpl getHighlightInfoTypeBySeverity(@NotNull HighlightSeverity severity) {
HighlightInfoType infoType = STANDARD_SEVERITIES.get(severity.getName());
if (infoType != null) {
return (HighlightInfoType.HighlightInfoTypeImpl)infoType;
}
if (severity == HighlightSeverity.INFORMATION){
return (HighlightInfoType.HighlightInfoTypeImpl)HighlightInfoType.INFORMATION;
}
final SeverityBasedTextAttributes type = getAttributesBySeverity(severity);
return (HighlightInfoType.HighlightInfoTypeImpl)(type == null ? HighlightInfoType.WARNING : type.getType());
}
private SeverityBasedTextAttributes getAttributesBySeverity(@NotNull HighlightSeverity severity) {
return myMap.get(severity.getName());
}
@Nullable
public TextAttributes getTextAttributesBySeverity(@NotNull HighlightSeverity severity) {
final SeverityBasedTextAttributes infoType = getAttributesBySeverity(severity);
if (infoType != null) {
return infoType.getAttributes();
}
return null;
}
@Override
public void readExternal(Element element) throws InvalidDataException {
myMap.clear();
myRendererColors.clear();
final List children = element.getChildren(INFO_TAG);
for (Object child : children) {
final Element infoElement = (Element)child;
final SeverityBasedTextAttributes highlightInfo = new SeverityBasedTextAttributes(infoElement);
Color color = null;
final String colorStr = infoElement.getAttributeValue(COLOR_ATTRIBUTE);
if (colorStr != null){
color = new Color(Integer.parseInt(colorStr, 16));
}
registerSeverity(highlightInfo, color);
}
myReadOrder = new JDOMExternalizableStringList();
myReadOrder.readExternal(element);
OrderMap orderMap = new OrderMap(myReadOrder.size());
for (int i = 0; i < myReadOrder.size(); i++) {
String name = myReadOrder.get(i);
HighlightSeverity severity = getSeverity(name);
if (severity == null) continue;
orderMap.put(severity, i);
}
final List<HighlightSeverity> knownSeverities = getDefaultOrder();
orderMap.retainEntries(new TObjectIntProcedure<HighlightSeverity>() {
@Override
public boolean execute(HighlightSeverity severity, int order) {
return knownSeverities.contains(severity);
}
});
if (orderMap.isEmpty()) {
orderMap = fromList(knownSeverities);
}
else {
//enforce include all known
List<HighlightSeverity> list = getOrderAsList(orderMap);
for (int i = 0; i < knownSeverities.size(); i++) {
HighlightSeverity stdSeverity = knownSeverities.get(i);
if (!list.contains(stdSeverity)) {
for (int oIdx = 0; oIdx < list.size(); oIdx++) {
HighlightSeverity orderSeverity = list.get(oIdx);
HighlightInfoType type = STANDARD_SEVERITIES.get(orderSeverity.getName());
if (type != null && knownSeverities.indexOf(type.getSeverity(null)) > i) {
list.add(oIdx, stdSeverity);
myReadOrder = null;
break;
}
}
}
}
orderMap = fromList(list);
}
myOrderMap = orderMap;
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
List<HighlightSeverity> list = getOrderAsList(getOrderMap());
for (HighlightSeverity severity : list) {
Element info = new Element(INFO_TAG);
String severityName = severity.getName();
final SeverityBasedTextAttributes infoType = getAttributesBySeverity(severity);
if (infoType != null) {
infoType.writeExternal(info);
final Color color = myRendererColors.get(severityName);
if (color != null) {
info.setAttribute(COLOR_ATTRIBUTE, Integer.toString(color.getRGB() & 0xFFFFFF, 16));
}
element.addContent(info);
}
}
if (myReadOrder != null && !myReadOrder.isEmpty()) {
myReadOrder.writeExternal(element);
}
else if (!getDefaultOrder().equals(list)) {
final JDOMExternalizableStringList ext = new JDOMExternalizableStringList(Collections.nCopies(getOrderMap().size(), ""));
getOrderMap().forEachEntry(new TObjectIntProcedure<HighlightSeverity>() {
@Override
public boolean execute(HighlightSeverity orderSeverity, int oIdx) {
ext.set(oIdx, orderSeverity.getName());
return true;
}
});
ext.writeExternal(element);
}
}
@NotNull
private List<HighlightSeverity> getOrderAsList(@NotNull OrderMap orderMap) {
List<HighlightSeverity> list = new ArrayList<HighlightSeverity>();
for (Object o : orderMap.keys()) {
list.add((HighlightSeverity)o);
}
Collections.sort(list, this);
return list;
}
public int getSeveritiesCount() {
return createCurrentSeverityNames().size();
}
public HighlightSeverity getSeverityByIndex(final int i) {
final HighlightSeverity[] found = new HighlightSeverity[1];
getOrderMap().forEachEntry(new TObjectIntProcedure<HighlightSeverity>() {
@Override
public boolean execute(HighlightSeverity severity, int order) {
if (order == i) {
found[0] = severity;
return false;
}
return true;
}
});
return found[0];
}
public int getSeverityMaxIndex() {
int[] values = getOrderMap().getValues();
int max = values[0];
for(int i = 1; i < values.length; ++i) if (values[i] > max) max = values[i];
return max;
}
@Nullable
public HighlightSeverity getSeverity(@NotNull String name) {
final HighlightInfoType type = STANDARD_SEVERITIES.get(name);
if (type != null) return type.getSeverity(null);
final SeverityBasedTextAttributes attributes = myMap.get(name);
if (attributes != null) return attributes.getSeverity();
return null;
}
@NotNull
private List<String> createCurrentSeverityNames() {
List<String> list = new ArrayList<String>();
list.addAll(STANDARD_SEVERITIES.keySet());
list.addAll(myMap.keySet());
ContainerUtil.sort(list);
return list;
}
public Icon getRendererIconByIndex(int i) {
final HighlightSeverity severity = getSeverityByIndex(i);
HighlightDisplayLevel level = HighlightDisplayLevel.find(severity);
if (level != null) {
return level.getIcon();
}
return HighlightDisplayLevel.createIconByMask(myRendererColors.get(severity.getName()));
}
public boolean isSeverityValid(@NotNull String severityName) {
return createCurrentSeverityNames().contains(severityName);
}
@Override
public int compare(final HighlightSeverity s1, final HighlightSeverity s2) {
OrderMap orderMap = getOrderMap();
int o1 = orderMap.getOrder(s1, -1);
int o2 = orderMap.getOrder(s2, -1);
return o1 - o2;
}
@NotNull
private OrderMap getOrderMap() {
OrderMap orderMap;
OrderMap defaultOrder = null;
while ((orderMap = myOrderMap) == null) {
if (defaultOrder == null) {
defaultOrder = fromList(getDefaultOrder());
}
boolean replaced = ORDER_MAP_UPDATER.compareAndSet(this, null, defaultOrder);
if (replaced) {
orderMap = defaultOrder;
break;
}
}
return orderMap;
}
private static final AtomicFieldUpdater<SeverityRegistrar, OrderMap> ORDER_MAP_UPDATER = AtomicFieldUpdater.forFieldOfType(SeverityRegistrar.class, OrderMap.class);
@NotNull
private static OrderMap fromList(@NotNull List<HighlightSeverity> orderList) {
OrderMap orderMap = new OrderMap(orderList.size());
for (int i = 0; i < orderList.size(); i++) {
HighlightSeverity severity = orderList.get(i);
orderMap.put(severity, i);
}
orderMap.trimToSize();
return orderMap;
}
@NotNull
private List<HighlightSeverity> getDefaultOrder() {
Collection<SeverityBasedTextAttributes> values = myMap.values();
List<HighlightSeverity> order = new ArrayList<HighlightSeverity>(STANDARD_SEVERITIES.size() + values.size());
for (HighlightInfoType type : STANDARD_SEVERITIES.values()) {
order.add(type.getSeverity(null));
}
for (SeverityBasedTextAttributes attributes : values) {
order.add(attributes.getSeverity());
}
ContainerUtil.sort(order);
return order;
}
public void setOrder(@NotNull List<HighlightSeverity> orderList) {
myOrderMap = fromList(orderList);
myReadOrder = null;
}
public int getSeverityIdx(@NotNull HighlightSeverity severity) {
return getOrderMap().getOrder(severity, -1);
}
public boolean isDefaultSeverity(@NotNull HighlightSeverity severity) {
return STANDARD_SEVERITIES.containsKey(severity.myName);
}
public static boolean isGotoBySeverityEnabled(@NotNull HighlightSeverity minSeverity) {
for (SeveritiesProvider provider : Extensions.getExtensions(SeveritiesProvider.EP_NAME)) {
if (provider.isGotoBySeverityEnabled(minSeverity)) return true;
}
return minSeverity != HighlightSeverity.INFORMATION;
}
private static class OrderMap extends TObjectIntHashMap<HighlightSeverity> {
private OrderMap(int initialCapacity) {
super(initialCapacity);
}
private int getOrder(@NotNull HighlightSeverity severity, int defaultOrder) {
int index = index(severity);
return index < 0 ? defaultOrder : _values[index];
}
}
public static class SeverityBasedTextAttributes {
private final TextAttributes myAttributes;
private final HighlightInfoType.HighlightInfoTypeImpl myType;
//read external
public SeverityBasedTextAttributes(@NotNull Element element) throws InvalidDataException {
this(new TextAttributes(element), new HighlightInfoType.HighlightInfoTypeImpl(element));
}
public SeverityBasedTextAttributes(@NotNull TextAttributes attributes, @NotNull HighlightInfoType.HighlightInfoTypeImpl type) {
myAttributes = attributes;
myType = type;
}
@NotNull
public TextAttributes getAttributes() {
return myAttributes;
}
@NotNull
public HighlightInfoType.HighlightInfoTypeImpl getType() {
return myType;
}
private void writeExternal(@NotNull Element element) throws WriteExternalException {
myAttributes.writeExternal(element);
myType.writeExternal(element);
}
@NotNull
public HighlightSeverity getSeverity() {
return myType.getSeverity(null);
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final SeverityBasedTextAttributes that = (SeverityBasedTextAttributes)o;
if (!myAttributes.equals(that.myAttributes)) return false;
if (!myType.equals(that.myType)) return false;
return true;
}
public int hashCode() {
int result = myAttributes.hashCode();
result = 31 * result + myType.hashCode();
return result;
}
}
@NotNull
Collection<SeverityBasedTextAttributes> allRegisteredAttributes() {
return new ArrayList<SeverityBasedTextAttributes>(myMap.values());
}
@NotNull
Collection<HighlightInfoType> standardSeverities() {
return STANDARD_SEVERITIES.values();
}
}