294 lines
12 KiB
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();
|
|
}
|
|
}
|