Essentials/Essentials/src/main/java/com/earth2me/essentials/utils/FormatUtil.java

294 lines
12 KiB
Java

package com.earth2me.essentials.utils;
import net.ess3.api.IUser;
import org.bukkit.ChatColor;
import java.awt.Color;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class FormatUtil {
public static final Pattern IPPATTERN = Pattern.compile("^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
private static final Set<ChatColor> COLORS = EnumSet.of(ChatColor.BLACK, ChatColor.DARK_BLUE, ChatColor.DARK_GREEN, ChatColor.DARK_AQUA, ChatColor.DARK_RED, ChatColor.DARK_PURPLE, ChatColor.GOLD, ChatColor.GRAY, ChatColor.DARK_GRAY, ChatColor.BLUE, ChatColor.GREEN, ChatColor.AQUA, ChatColor.RED, ChatColor.LIGHT_PURPLE, ChatColor.YELLOW, ChatColor.WHITE);
private static final Set<ChatColor> FORMATS = EnumSet.of(ChatColor.BOLD, ChatColor.STRIKETHROUGH, ChatColor.UNDERLINE, ChatColor.ITALIC, ChatColor.RESET);
private static final Set<ChatColor> MAGIC = EnumSet.of(ChatColor.MAGIC);
//Vanilla patterns used to strip existing formats
private static final Pattern STRIP_ALL_PATTERN = Pattern.compile(ChatColor.COLOR_CHAR + "+([0-9a-fk-orA-FK-OR])");
//Pattern used to strip md_5 legacy hex hack
private static final Pattern STRIP_RGB_PATTERN = Pattern.compile(ChatColor.COLOR_CHAR + "x((?:" + ChatColor.COLOR_CHAR + "[0-9a-fA-F]){6})");
//Essentials '&' convention colour codes
private static final Pattern REPLACE_ALL_PATTERN = Pattern.compile("(&)?&([0-9a-fk-orA-FK-OR])");
private static final Pattern REPLACE_ALL_RGB_PATTERN = Pattern.compile("(&)?&#([0-9a-fA-F]{6})");
//Used to prepare xmpp output
private static final Pattern LOGCOLOR_PATTERN = Pattern.compile("\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]");
private static final Pattern URL_PATTERN = Pattern.compile("((?:(?:https?)://)?[\\w-_\\.]{2,})\\.([a-zA-Z]{2,3}(?:/\\S+)?)");
//Used to strip ANSI control codes from console
private static final Pattern ANSI_CONTROL_PATTERN = Pattern.compile("\u001B(?:\\[0?m|\\[38;2(?:;\\d{1,3}){3}m|\\[([0-9]{1,2}[;m]?){3})");
private FormatUtil() {
}
//This method is used to simply strip the native minecraft colour codes
public static String stripFormat(final String input) {
if (input == null) {
return null;
}
return ChatColor.stripColor(input);
}
//This method is used to simply strip the & convention colour codes
public static String stripEssentialsFormat(final String input) {
if (input == null) {
return null;
}
return stripColor(input, REPLACE_ALL_PATTERN);
}
public static String stripAnsi(final String input) {
if (input == null) {
return null;
}
return stripColor(input, ANSI_CONTROL_PATTERN);
}
//This is the general permission sensitive message format function, checks for urls.
public static String formatMessage(final IUser user, final String permBase, final String input) {
if (input == null) {
return null;
}
String message = formatString(user, permBase, input);
if (!user.isAuthorized(permBase + ".url")) {
message = FormatUtil.blockURL(message);
}
return message;
}
//This method is used to simply replace the ess colour codes with minecraft ones, ie &c
public static String replaceFormat(final String input) {
if (input == null) {
return null;
}
return replaceColor(input, EnumSet.allOf(ChatColor.class), true);
}
static String replaceColor(final String input, final Set<ChatColor> supported, final boolean rgb) {
final StringBuffer legacyBuilder = new StringBuffer();
final Matcher legacyMatcher = REPLACE_ALL_PATTERN.matcher(input);
legacyLoop:
while (legacyMatcher.find()) {
final boolean isEscaped = legacyMatcher.group(1) != null;
if (!isEscaped) {
final char code = legacyMatcher.group(2).toLowerCase(Locale.ROOT).charAt(0);
for (final ChatColor color : supported) {
if (color.getChar() == code) {
legacyMatcher.appendReplacement(legacyBuilder, ChatColor.COLOR_CHAR + "$2");
continue legacyLoop;
}
}
}
// Don't change & to section sign (or replace two &'s with one)
legacyMatcher.appendReplacement(legacyBuilder, "&$2");
}
legacyMatcher.appendTail(legacyBuilder);
if (rgb) {
final StringBuffer rgbBuilder = new StringBuffer();
final Matcher rgbMatcher = REPLACE_ALL_RGB_PATTERN.matcher(legacyBuilder.toString());
while (rgbMatcher.find()) {
final boolean isEscaped = rgbMatcher.group(1) != null;
if (!isEscaped) {
try {
final String hexCode = rgbMatcher.group(2);
rgbMatcher.appendReplacement(rgbBuilder, parseHexColor(hexCode));
continue;
} catch (final NumberFormatException ignored) {
}
}
rgbMatcher.appendReplacement(rgbBuilder, "&#$2");
}
rgbMatcher.appendTail(rgbBuilder);
return rgbBuilder.toString();
}
return legacyBuilder.toString();
}
/**
* @throws NumberFormatException If the provided hex color code is invalid or if version is lower than 1.16.
*/
public static String parseHexColor(String hexColor) throws NumberFormatException {
if (VersionUtil.getServerBukkitVersion().isLowerThan(VersionUtil.v1_16_1_R01)) {
throw new NumberFormatException("Cannot use RGB colors in versions < 1.16");
}
if (hexColor.startsWith("#")) {
hexColor = hexColor.substring(1); //fuck you im reassigning this.
}
if (hexColor.length() != 6) {
throw new NumberFormatException("Invalid hex length");
}
Color.decode("#" + hexColor);
final StringBuilder assembledColorCode = new StringBuilder();
assembledColorCode.append(ChatColor.COLOR_CHAR + "x");
for (final char curChar : hexColor.toCharArray()) {
assembledColorCode.append(ChatColor.COLOR_CHAR).append(curChar);
}
return assembledColorCode.toString();
}
static String stripColor(final String input, final Set<ChatColor> strip) {
final StringBuffer builder = new StringBuffer();
final Matcher matcher = STRIP_ALL_PATTERN.matcher(input);
searchLoop:
while (matcher.find()) {
final char code = matcher.group(1).toLowerCase(Locale.ROOT).charAt(0);
for (final ChatColor color : strip) {
if (color.getChar() == code) {
matcher.appendReplacement(builder, "");
continue searchLoop;
}
}
// Don't replace
matcher.appendReplacement(builder, "$0");
}
matcher.appendTail(builder);
return builder.toString();
}
public static String unformatString(final String message) {
if (message == null) {
return null;
}
return unformatString(message, EnumSet.allOf(ChatColor.class), true);
}
public static String unformatString(final IUser user, final String permBase, final String message) {
if (message == null) {
return null;
}
return unformatString(message, getSupported(user, permBase), user.isAuthorized(permBase + ".rgb"));
}
public static String unformatString(String message, final EnumSet<ChatColor> supported, boolean rgb) {
if (message == null) {
return null;
}
final StringBuffer rgbBuilder = new StringBuffer();
final Matcher rgbMatcher = STRIP_RGB_PATTERN.matcher(message);
while (rgbMatcher.find()) {
final String code = rgbMatcher.group(1).replace(String.valueOf(ChatColor.COLOR_CHAR), "");
if (rgb) {
rgbMatcher.appendReplacement(rgbBuilder, "&#" + code);
continue;
}
rgbMatcher.appendReplacement(rgbBuilder, "");
}
rgbMatcher.appendTail(rgbBuilder);
message = rgbBuilder.toString(); // arreter de parler
// Legacy Colors
final StringBuffer builder = new StringBuffer();
final Matcher matcher = STRIP_ALL_PATTERN.matcher(message);
searchLoop:
while (matcher.find()) {
final char code = matcher.group(1).toLowerCase(Locale.ROOT).charAt(0);
for (final ChatColor color : supported) {
if (color.getChar() == code) {
matcher.appendReplacement(builder, "&" + code);
continue searchLoop;
}
}
matcher.appendReplacement(builder, "");
}
matcher.appendTail(builder);
return builder.toString();
}
//This is the general permission sensitive message format function, does not touch urls.
public static String formatString(final IUser user, final String permBase, String message) {
if (message == null) {
return null;
}
final EnumSet<ChatColor> supported = getSupported(user, permBase);
final EnumSet<ChatColor> strip = EnumSet.complementOf(supported);
final boolean rgb = user.isAuthorized(permBase + ".rgb");
if (!supported.isEmpty() || rgb) {
message = replaceColor(message, supported, rgb);
}
if (!strip.isEmpty()) {
message = stripColor(message, strip);
}
return message;
}
private static EnumSet<ChatColor> getSupported(final IUser user, final String permBase) {
final EnumSet<ChatColor> supported = EnumSet.noneOf(ChatColor.class);
if (user.isAuthorized(permBase + ".color")) {
supported.addAll(COLORS);
}
if (user.isAuthorized(permBase + ".format")) {
supported.addAll(FORMATS);
}
if (user.isAuthorized(permBase + ".magic")) {
supported.addAll(MAGIC);
}
for (final ChatColor chatColor : ChatColor.values()) {
String colorName = chatColor.name();
if (chatColor == ChatColor.MAGIC) {
// Bukkit's name doesn't match with vanilla's
colorName = "obfuscated";
}
final String node = permBase + "." + colorName.toLowerCase(Locale.ROOT);
// Only handle individual colors that are explicitly added or removed.
if (!user.isPermissionSet(node)) {
continue;
}
if (user.isAuthorized(node)) {
supported.add(chatColor);
} else {
supported.remove(chatColor);
}
}
return supported;
}
public static String stripLogColorFormat(final String input) {
if (input == null) {
return null;
}
return stripColor(input, LOGCOLOR_PATTERN);
}
static String stripColor(final String input, final Pattern pattern) {
return pattern.matcher(input).replaceAll("");
}
public static String lastCode(final String input) {
final int pos = input.lastIndexOf(ChatColor.COLOR_CHAR);
if (pos == -1 || (pos + 1) == input.length()) {
return "";
}
return input.substring(pos, pos + 2);
}
static String blockURL(final String input) {
if (input == null) {
return null;
}
String text = URL_PATTERN.matcher(input).replaceAll("$1 $2");
while (URL_PATTERN.matcher(text).find()) {
text = URL_PATTERN.matcher(text).replaceAll("$1 $2");
}
return text;
}
public static boolean validIP(final String ipAddress) {
return IPPATTERN.matcher(ipAddress).matches();
}
}