/*
* Copyright 2016 the original author or authors.
*
* 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 org.gradle.internal.logging.console;
import com.google.common.collect.Lists;
import org.fusesource.jansi.Ansi;
import org.gradle.internal.logging.text.Style;
import org.gradle.internal.logging.text.StyledTextOutput;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.fusesource.jansi.Ansi.Attribute;
import static org.fusesource.jansi.Ansi.Color.DEFAULT;
import static org.gradle.internal.logging.text.StyledTextOutput.Style.*;
import static org.gradle.internal.logging.text.StyledTextOutput.Style.Error;
public class DefaultColorMap implements ColorMap {
private static final String STATUS_BAR = "statusbar";
private static final String BOLD = "bold";
private static final String COLOR_DIVIDER = "-";
/**
* Maps a {@link StyledTextOutput.Style} to the default color spec (that can be overridden by system properties)
*/
private final Map<String, String> defaults = new HashMap<String, String>();
/**
* Maps a {@link StyledTextOutput.Style} to the {@link org.gradle.internal.logging.console.ColorMap.Color} that has been created for it
*/
private final Map<String, Color> colorByStyle = new HashMap<String, Color>();
/**
* Maps a color spec to the {@link org.gradle.internal.logging.console.ColorMap.Color} that has been created for it
*/
private final Map<String, Color> colorBySpec = new HashMap<String, Color>();
private final Color noDecoration = new Color() {
public void on(Ansi ansi) {
}
public void off(Ansi ansi) {
}
};
public DefaultColorMap() {
addDefault(Info, "yellow");
addDefault(Error, "default");
addDefault(Header, "bold");
addDefault(Description, "yellow");
addDefault(ProgressStatus, "yellow");
addDefault(Identifier, "green");
addDefault(UserInput, "bold");
addDefault(Success, "green");
addDefault(SuccessHeader, Success, Header);
addDefault(Failure, "red");
addDefault(FailureHeader, Failure, Header);
addDefault(STATUS_BAR, "bold");
}
private void addDefault(StyledTextOutput.Style style, String colorSpec) {
addDefault(style.name().toLowerCase(), colorSpec);
}
private void addDefault(String style, String color) {
defaults.put(style, color);
}
private void addDefault(StyledTextOutput.Style style, StyledTextOutput.Style... styles) {
String colorSpec = getColorSpecForStyle(styles[0]);
for (int i = 1; i < styles.length; i++) {
colorSpec += COLOR_DIVIDER + getColorSpecForStyle(styles[i]);
}
addDefault(style.name().toLowerCase(), colorSpec);
}
public Color getStatusBarColor() {
return getColor(STATUS_BAR);
}
public Color getColourFor(StyledTextOutput.Style style) {
return getColor(style.name().toLowerCase());
}
@Override
public Color getColourFor(Style style) {
List<Color> colors = new ArrayList<Color>();
for (Style.Emphasis emphasis : style.getEmphasises()) {
if (emphasis.equals(Style.Emphasis.BOLD)) {
colors.add(newBoldColor());
} else if (emphasis.equals(Style.Emphasis.REVERSE)) {
colors.add(newReverseColor());
} else if (emphasis.equals(Style.Emphasis.ITALIC)) {
colors.add(newItalicColor());
}
}
if (style.getColor().equals(Style.Color.GREY)) {
colors.add(new BrightForegroundColor(Ansi.Color.BLACK));
} else {
Ansi.Color ansiColor = Ansi.Color.valueOf(style.getColor().name().toUpperCase());
if (ansiColor != DEFAULT) {
colors.add(new ForegroundColor(ansiColor));
}
}
return new CompositeColor(colors);
}
private Color getColor(String style) {
Color color = colorByStyle.get(style);
if (color == null) {
color = createColor(style);
colorByStyle.put(style, color);
}
return color;
}
private String getColorSpecForStyle(StyledTextOutput.Style style) {
return getColorSpecForStyle(style.name().toLowerCase());
}
private String getColorSpecForStyle(String style) {
return System.getProperty("org.gradle.color." + style, defaults.get(style));
}
private Color createColor(String style) {
String colorSpec = getColorSpecForStyle(style);
Color color = noDecoration;
if (colorSpec != null) {
color = createColorFromSpec(colorSpec);
colorBySpec.put(colorSpec, color);
}
return color;
}
private Color createColorFromSpec(String colorSpec) {
Color cachedColor = colorBySpec.get(colorSpec);
if (cachedColor != null) {
return cachedColor;
}
if (colorSpec.equalsIgnoreCase(BOLD)) {
return newBoldColor();
}
if (colorSpec.equalsIgnoreCase("reverse")) {
return newReverseColor();
}
if (colorSpec.equalsIgnoreCase("italic")) {
return newItalicColor();
}
if (colorSpec.contains("-")) {
String[] colors = colorSpec.split("-");
ArrayList<Color> colorList = new ArrayList(colors.length);
for (String color : colors) {
colorList.add(createColorFromSpec(color));
}
return new CompositeColor(colorList);
}
Ansi.Color ansiColor = Ansi.Color.valueOf(colorSpec.toUpperCase());
if (ansiColor != DEFAULT) {
return new ForegroundColor(ansiColor);
}
return noDecoration;
}
private static Color newBoldColor() {
// We don't use Attribute.INTENSITY_BOLD_OFF as it's rarely supported like Windows 10
return new AttributeColor(Attribute.INTENSITY_BOLD, Attribute.RESET);
}
private static Color newReverseColor() {
return new AttributeColor(Attribute.NEGATIVE_ON, Attribute.NEGATIVE_OFF);
}
private static Color newItalicColor() {
return new AttributeColor(Attribute.ITALIC, Attribute.ITALIC_OFF);
}
private static class BrightForegroundColor implements Color {
private final Ansi.Color ansiColor;
public BrightForegroundColor(Ansi.Color ansiColor) {
this.ansiColor = ansiColor;
}
public void on(Ansi ansi) {
ansi.fgBright(ansiColor);
}
public void off(Ansi ansi) {
ansi.fg(DEFAULT);
}
}
private static class ForegroundColor implements Color {
private final Ansi.Color ansiColor;
public ForegroundColor(Ansi.Color ansiColor) {
this.ansiColor = ansiColor;
}
public void on(Ansi ansi) {
ansi.fg(ansiColor);
}
public void off(Ansi ansi) {
ansi.fg(DEFAULT);
}
}
private static class AttributeColor implements Color {
private final Ansi.Attribute on;
private final Ansi.Attribute off;
public AttributeColor(Attribute on, Attribute off) {
this.on = on;
this.off = off;
}
public void on(Ansi ansi) {
ansi.a(on);
}
public void off(Ansi ansi) {
ansi.a(off);
}
}
private static class CompositeColor implements Color {
private final List<Color> colors;
public CompositeColor(List<Color> colors) {
this.colors = colors;
}
@Override
public void on(Ansi ansi) {
for (Color color : colors) {
color.on(ansi);
}
}
@Override
public void off(Ansi ansi) {
for (Color color : Lists.reverse(colors)) {
color.off(ansi);
}
}
}
}