diff --git a/src/main/java/info/ronjenkins/slf4bukkit/AnsiColorMapper.java b/src/main/java/info/ronjenkins/slf4bukkit/AnsiColorMapper.java new file mode 100644 index 0000000..6ced4e0 --- /dev/null +++ b/src/main/java/info/ronjenkins/slf4bukkit/AnsiColorMapper.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 Ronald Jack Jenkins Jr. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package info.ronjenkins.slf4bukkit; + +import com.google.common.collect.ImmutableMap; + +import org.bukkit.ChatColor; +import org.fusesource.jansi.Ansi; +import org.fusesource.jansi.Ansi.Attribute; + +import java.util.Map; + +/** + * Maps {@link ChatColor} values to their JAnsi equivalents. + * + *

This class might not always be instantiable as jansi might not be present at runtime.

+ * + * @author Ronald Jack Jenkins Jr. + */ +final class AnsiColorMapper implements ColorMapper { + + AnsiColorMapper() throws Throwable { + // Calling this constructor could result in a Throwable because JAnsi, which is required to execute code + // in this class, might not be present at runtime, thus expected classes are not found. + } + + // @formatter:off + private final Map MAP = ImmutableMap.builder() + .put(ChatColor.BLACK, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).boldOff().toString()) + .put(ChatColor.DARK_BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).boldOff().toString()) + .put(ChatColor.DARK_GREEN, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.GREEN).boldOff().toString()) + .put(ChatColor.DARK_AQUA, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.CYAN).boldOff().toString()) + .put(ChatColor.DARK_RED, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.RED).boldOff().toString()) + .put(ChatColor.DARK_PURPLE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.MAGENTA).boldOff().toString()) + .put(ChatColor.GOLD, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.YELLOW).boldOff().toString()) + .put(ChatColor.GRAY, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.WHITE).boldOff().toString()) + .put(ChatColor.DARK_GRAY, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).bold().toString()) + .put(ChatColor.BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).bold().toString()) + .put(ChatColor.GREEN, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.GREEN).bold().toString()) + .put(ChatColor.AQUA, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.CYAN).bold().toString()) + .put(ChatColor.RED, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.RED).bold().toString()) + .put(ChatColor.LIGHT_PURPLE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.MAGENTA).bold().toString()) + .put(ChatColor.YELLOW, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.YELLOW).bold().toString()) + .put(ChatColor.WHITE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.WHITE).bold().toString()) + .put(ChatColor.MAGIC, Ansi.ansi().a(Attribute.BLINK_SLOW).toString()) + .put(ChatColor.BOLD, Ansi.ansi().a(Attribute.UNDERLINE_DOUBLE).toString()) + .put(ChatColor.STRIKETHROUGH, Ansi.ansi().a(Attribute.STRIKETHROUGH_ON).toString()) + .put(ChatColor.UNDERLINE, Ansi.ansi().a(Attribute.UNDERLINE).toString()) + .put(ChatColor.ITALIC, Ansi.ansi().a(Attribute.ITALIC).toString()) + .put(ChatColor.RESET, Ansi.ansi().a(Attribute.RESET).toString()) + .build(); + // @formatter:on + + @Override + public String map(final String input) { + if (input == null) { + return ""; + } + String output = input; + for (final Map.Entry mapping : MAP.entrySet()) { + output = output.replace(mapping.getKey().toString(), mapping.getValue()); + } + return output; + } + +} diff --git a/src/main/java/info/ronjenkins/slf4bukkit/ColorMapper.java b/src/main/java/info/ronjenkins/slf4bukkit/ColorMapper.java index 506b490..bafaccb 100644 --- a/src/main/java/info/ronjenkins/slf4bukkit/ColorMapper.java +++ b/src/main/java/info/ronjenkins/slf4bukkit/ColorMapper.java @@ -16,63 +16,21 @@ */ package info.ronjenkins.slf4bukkit; -import java.util.Map; - import org.bukkit.ChatColor; -import org.fusesource.jansi.Ansi; -import org.fusesource.jansi.Ansi.Attribute; - -import com.google.common.collect.ImmutableMap; /** - * Utility class that maps {@link ChatColor} values to their JAnsi equivalents, + * Utility class that maps {@link ChatColor} values to their equivalents, * so that messages logged to the console are formatted correctly. - * - * @author Ronald Jack Jenkins Jr. */ -public final class ColorMapper { - - // @formatter:off - private static final Map MAP = ImmutableMap.builder() - .put(ChatColor.BLACK, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).boldOff().toString()) - .put(ChatColor.DARK_BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).boldOff().toString()) - .put(ChatColor.DARK_GREEN, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.GREEN).boldOff().toString()) - .put(ChatColor.DARK_AQUA, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.CYAN).boldOff().toString()) - .put(ChatColor.DARK_RED, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.RED).boldOff().toString()) - .put(ChatColor.DARK_PURPLE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.MAGENTA).boldOff().toString()) - .put(ChatColor.GOLD, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.YELLOW).boldOff().toString()) - .put(ChatColor.GRAY, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.WHITE).boldOff().toString()) - .put(ChatColor.DARK_GRAY, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLACK).bold().toString()) - .put(ChatColor.BLUE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.BLUE).bold().toString()) - .put(ChatColor.GREEN, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.GREEN).bold().toString()) - .put(ChatColor.AQUA, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.CYAN).bold().toString()) - .put(ChatColor.RED, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.RED).bold().toString()) - .put(ChatColor.LIGHT_PURPLE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.MAGENTA).bold().toString()) - .put(ChatColor.YELLOW, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.YELLOW).bold().toString()) - .put(ChatColor.WHITE, Ansi.ansi().a(Attribute.RESET).fg(Ansi.Color.WHITE).bold().toString()) - .put(ChatColor.MAGIC, Ansi.ansi().a(Attribute.BLINK_SLOW).toString()) - .put(ChatColor.BOLD, Ansi.ansi().a(Attribute.UNDERLINE_DOUBLE).toString()) - .put(ChatColor.STRIKETHROUGH, Ansi.ansi().a(Attribute.STRIKETHROUGH_ON).toString()) - .put(ChatColor.UNDERLINE, Ansi.ansi().a(Attribute.UNDERLINE).toString()) - .put(ChatColor.ITALIC, Ansi.ansi().a(Attribute.ITALIC).toString()) - .put(ChatColor.RESET, Ansi.ansi().a(Attribute.RESET).toString()) - .build(); - // @formatter:on +public interface ColorMapper { /** - * Translates {@link ChatColor} directives to their JAnsi equivalents. + * Translates {@link ChatColor} directives to their equivalents. * * @param input * null is coerced to the empty string. * @return never null. */ - public static String map(final String input) { - if (input == null) { return ""; } - String output = input; - for (final Map.Entry mapping : ColorMapper.MAP.entrySet()) { - output = output.replace(mapping.getKey().toString(), mapping.getValue()); - } - return output; - } + String map(String input); } diff --git a/src/main/java/info/ronjenkins/slf4bukkit/ColorMapperFactory.java b/src/main/java/info/ronjenkins/slf4bukkit/ColorMapperFactory.java new file mode 100644 index 0000000..67a0c58 --- /dev/null +++ b/src/main/java/info/ronjenkins/slf4bukkit/ColorMapperFactory.java @@ -0,0 +1,23 @@ +package info.ronjenkins.slf4bukkit; + +/** + * Creates {@code ColorMapper} instances. + * + * @see ColorMapper + */ +public final class ColorMapperFactory { + + /** + * Creates an new {@code ColorMapper} instance. + * + * @return a new instance + */ + public static ColorMapper create() { + try { + return new AnsiColorMapper(); + } catch (Throwable throwable) { + return new NotSupportedColorMapper(); + } + } + +} diff --git a/src/main/java/info/ronjenkins/slf4bukkit/NotSupportedColorMapper.java b/src/main/java/info/ronjenkins/slf4bukkit/NotSupportedColorMapper.java new file mode 100644 index 0000000..d67fda4 --- /dev/null +++ b/src/main/java/info/ronjenkins/slf4bukkit/NotSupportedColorMapper.java @@ -0,0 +1,12 @@ +package info.ronjenkins.slf4bukkit; + +/** + * Does not do any mapping but simply returns the given string or, if {@code null} is given, an empty string. + */ +final class NotSupportedColorMapper implements ColorMapper { + + @Override + public String map(final String input) { + return input == null ? "" : input; + } +} diff --git a/src/main/java/org/slf4j/impl/BukkitLoggerAdapter.java b/src/main/java/org/slf4j/impl/BukkitLoggerAdapter.java index 9ffb5e9..1e53511 100644 --- a/src/main/java/org/slf4j/impl/BukkitLoggerAdapter.java +++ b/src/main/java/org/slf4j/impl/BukkitLoggerAdapter.java @@ -45,6 +45,7 @@ package org.slf4j.impl; import info.ronjenkins.slf4bukkit.ColorMapper; +import info.ronjenkins.slf4bukkit.ColorMapperFactory; import info.ronjenkins.slf4bukkit.ColorMarker; import java.io.IOException; @@ -218,6 +219,7 @@ public final class BukkitLoggerAdapter implements Logger { // The logger name. private final String name; // The short name of this simple log instance + private final ColorMapper mapper = ColorMapperFactory.create(); private transient String shortLogName = null; // NOTE: BukkitPluginLoggerAdapter constructor should have only package access @@ -1043,6 +1045,6 @@ public final class BukkitLoggerAdapter implements Logger { // Log the message. logger.log(BukkitLoggerAdapter.slf4jLevelIntToBukkitJULLevel(level), - ColorMapper.map(buf.toString())); + mapper.map(buf.toString())); } }