From 114f4f22096f56280b7dcd0ecbb216b207576f43 Mon Sep 17 00:00:00 2001 From: ME1312 Date: Tue, 30 Oct 2018 15:12:53 -0400 Subject: [PATCH] Add support for unicode escapes outside of the char limit --- .../SubServers/Bungee/Library/Util.java | 65 ++++++++---- .../Client/Bukkit/Library/Util.java | 65 ++++++++---- .../Client/Sponge/Library/Util.java | 65 ++++++++---- .../Console/AnsiHTMLColorStream.java | 100 +++++++++++++----- SubServers.Host/pom.xml | 2 +- .../ME1312/SubServers/Sync/Library/Util.java | 65 ++++++++---- 6 files changed, 251 insertions(+), 111 deletions(-) diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/Util.java b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/Util.java index b28e079c..c627881b 100644 --- a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/Util.java +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/Util.java @@ -328,29 +328,29 @@ public final class Util { * @return Unescaped String */ public static String unescapeJavaString(String str) { - StringBuilder sb = new StringBuilder(str.length()); for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); + int ch = str.codePointAt(i); if (ch == '\\') { - char nextChar = (i == str.length() - 1) ? '\\' : str - .charAt(i + 1); + int nextChar = (i == str.length() - 1) ? '\\' : str + .codePointAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { - String code = "" + nextChar; + StringBuilder code = new StringBuilder(); + code.appendCodePoint(nextChar); i++; - if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' - && str.charAt(i + 1) <= '7') { - code += str.charAt(i + 1); + if ((i < str.length() - 1) && str.codePointAt(i + 1) >= '0' + && str.codePointAt(i + 1) <= '7') { + code.appendCodePoint(str.codePointAt(i + 1)); i++; - if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' - && str.charAt(i + 1) <= '7') { - code += str.charAt(i + 1); + if ((i < str.length() - 1) && str.codePointAt(i + 1) >= '0' + && str.codePointAt(i + 1) <= '7') { + code.appendCodePoint(str.codePointAt(i + 1)); i++; } } - sb.append((char) Integer.parseInt(code, 8)); + sb.append((char) Integer.parseInt(code.toString(), 8)); continue; } switch (nextChar) { @@ -378,22 +378,45 @@ public final class Util { case '\'': ch = '\''; break; - // Hex Unicode: u???? + // Hex Unicode Char: u???? + // Hex Unicode Codepoint: u{??????} case 'u': - if (i >= str.length() - 5) { + try { + if (i >= str.length() - 4) throw new IllegalStateException(); + StringBuilder escape = new StringBuilder(); + int offset = 2; + + if (str.codePointAt(i + 2) != '{') { + if (i >= str.length() - 5) throw new IllegalStateException(); + while (offset <= 5) { + Integer.toString(str.codePointAt(i + offset), 16); + escape.appendCodePoint(str.codePointAt(i + offset)); + offset++; + } + offset--; + } else { + offset++; + while (str.codePointAt(i + offset) != '}') { + Integer.toString(str.codePointAt(i + offset), 16); + escape.appendCodePoint(str.codePointAt(i + offset)); + offset++; + } + } + sb.append(new String(new int[]{ + Integer.parseInt(escape.toString(), 16) + }, 0, 1)); + + i += offset; + continue; + } catch (Throwable e){ + sb.append('\\'); ch = 'u'; break; } - int code = Integer.parseInt( - "" + str.charAt(i + 2) + str.charAt(i + 3) - + str.charAt(i + 4) + str.charAt(i + 5), 16); - sb.append(Character.toChars(code)); - i += 5; - continue; } i++; } - sb.append(ch); + sb.appendCodePoint(ch); } return sb.toString(); } diff --git a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/Library/Util.java b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/Library/Util.java index c837a070..082b6e1f 100644 --- a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/Library/Util.java +++ b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/Library/Util.java @@ -249,29 +249,29 @@ public final class Util { * @return Unescaped String */ public static String unescapeJavaString(String str) { - StringBuilder sb = new StringBuilder(str.length()); for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); + int ch = str.codePointAt(i); if (ch == '\\') { - char nextChar = (i == str.length() - 1) ? '\\' : str - .charAt(i + 1); + int nextChar = (i == str.length() - 1) ? '\\' : str + .codePointAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { - String code = "" + nextChar; + StringBuilder code = new StringBuilder(); + code.appendCodePoint(nextChar); i++; - if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' - && str.charAt(i + 1) <= '7') { - code += str.charAt(i + 1); + if ((i < str.length() - 1) && str.codePointAt(i + 1) >= '0' + && str.codePointAt(i + 1) <= '7') { + code.appendCodePoint(str.codePointAt(i + 1)); i++; - if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' - && str.charAt(i + 1) <= '7') { - code += str.charAt(i + 1); + if ((i < str.length() - 1) && str.codePointAt(i + 1) >= '0' + && str.codePointAt(i + 1) <= '7') { + code.appendCodePoint(str.codePointAt(i + 1)); i++; } } - sb.append((char) Integer.parseInt(code, 8)); + sb.append((char) Integer.parseInt(code.toString(), 8)); continue; } switch (nextChar) { @@ -299,22 +299,45 @@ public final class Util { case '\'': ch = '\''; break; - // Hex Unicode: u???? + // Hex Unicode Char: u???? + // Hex Unicode Codepoint: u{??????} case 'u': - if (i >= str.length() - 5) { + try { + if (i >= str.length() - 4) throw new IllegalStateException(); + StringBuilder escape = new StringBuilder(); + int offset = 2; + + if (str.codePointAt(i + 2) != '{') { + if (i >= str.length() - 5) throw new IllegalStateException(); + while (offset <= 5) { + Integer.toString(str.codePointAt(i + offset), 16); + escape.appendCodePoint(str.codePointAt(i + offset)); + offset++; + } + offset--; + } else { + offset++; + while (str.codePointAt(i + offset) != '}') { + Integer.toString(str.codePointAt(i + offset), 16); + escape.appendCodePoint(str.codePointAt(i + offset)); + offset++; + } + } + sb.append(new String(new int[]{ + Integer.parseInt(escape.toString(), 16) + }, 0, 1)); + + i += offset; + continue; + } catch (Throwable e){ + sb.append('\\'); ch = 'u'; break; } - int code = Integer.parseInt( - "" + str.charAt(i + 2) + str.charAt(i + 3) - + str.charAt(i + 4) + str.charAt(i + 5), 16); - sb.append(Character.toChars(code)); - i += 5; - continue; } i++; } - sb.append(ch); + sb.appendCodePoint(ch); } return sb.toString(); } diff --git a/SubServers.Client/Sponge/src/net/ME1312/SubServers/Client/Sponge/Library/Util.java b/SubServers.Client/Sponge/src/net/ME1312/SubServers/Client/Sponge/Library/Util.java index bc0f394d..989ae67c 100644 --- a/SubServers.Client/Sponge/src/net/ME1312/SubServers/Client/Sponge/Library/Util.java +++ b/SubServers.Client/Sponge/src/net/ME1312/SubServers/Client/Sponge/Library/Util.java @@ -249,29 +249,29 @@ public final class Util { * @return Unescaped String */ public static String unescapeJavaString(String str) { - StringBuilder sb = new StringBuilder(str.length()); for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); + int ch = str.codePointAt(i); if (ch == '\\') { - char nextChar = (i == str.length() - 1) ? '\\' : str - .charAt(i + 1); + int nextChar = (i == str.length() - 1) ? '\\' : str + .codePointAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { - String code = "" + nextChar; + StringBuilder code = new StringBuilder(); + code.appendCodePoint(nextChar); i++; - if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' - && str.charAt(i + 1) <= '7') { - code += str.charAt(i + 1); + if ((i < str.length() - 1) && str.codePointAt(i + 1) >= '0' + && str.codePointAt(i + 1) <= '7') { + code.appendCodePoint(str.codePointAt(i + 1)); i++; - if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' - && str.charAt(i + 1) <= '7') { - code += str.charAt(i + 1); + if ((i < str.length() - 1) && str.codePointAt(i + 1) >= '0' + && str.codePointAt(i + 1) <= '7') { + code.appendCodePoint(str.codePointAt(i + 1)); i++; } } - sb.append((char) Integer.parseInt(code, 8)); + sb.append((char) Integer.parseInt(code.toString(), 8)); continue; } switch (nextChar) { @@ -299,22 +299,45 @@ public final class Util { case '\'': ch = '\''; break; - // Hex Unicode: u???? + // Hex Unicode Char: u???? + // Hex Unicode Codepoint: u{??????} case 'u': - if (i >= str.length() - 5) { + try { + if (i >= str.length() - 4) throw new IllegalStateException(); + StringBuilder escape = new StringBuilder(); + int offset = 2; + + if (str.codePointAt(i + 2) != '{') { + if (i >= str.length() - 5) throw new IllegalStateException(); + while (offset <= 5) { + Integer.toString(str.codePointAt(i + offset), 16); + escape.appendCodePoint(str.codePointAt(i + offset)); + offset++; + } + offset--; + } else { + offset++; + while (str.codePointAt(i + offset) != '}') { + Integer.toString(str.codePointAt(i + offset), 16); + escape.appendCodePoint(str.codePointAt(i + offset)); + offset++; + } + } + sb.append(new String(new int[]{ + Integer.parseInt(escape.toString(), 16) + }, 0, 1)); + + i += offset; + continue; + } catch (Throwable e){ + sb.append('\\'); ch = 'u'; break; } - int code = Integer.parseInt( - "" + str.charAt(i + 2) + str.charAt(i + 3) - + str.charAt(i + 4) + str.charAt(i + 5), 16); - sb.append(Character.toChars(code)); - i += 5; - continue; } i++; } - sb.append(ch); + sb.appendCodePoint(ch); } return sb.toString(); } diff --git a/SubServers.Console/src/net/ME1312/SubServers/Console/AnsiHTMLColorStream.java b/SubServers.Console/src/net/ME1312/SubServers/Console/AnsiHTMLColorStream.java index c53a2496..98fc7b88 100644 --- a/SubServers.Console/src/net/ME1312/SubServers/Console/AnsiHTMLColorStream.java +++ b/SubServers.Console/src/net/ME1312/SubServers/Console/AnsiHTMLColorStream.java @@ -6,17 +6,19 @@ import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; public class AnsiHTMLColorStream extends AnsiOutputStream { - private boolean concealOn = false; private static final String[] ANSI_COLOR_MAP = new String[]{"000000", "cd0000", "25bc24", "e1e100", "0000ee", "cd00cd", "00e1e1", "ffffff"}; private static final byte[] BYTES_NBSP = " ".getBytes(); private static final byte[] BYTES_QUOT = """.getBytes(); private static final byte[] BYTES_AMP = "&".getBytes(); private static final byte[] BYTES_LT = "<".getBytes(); private static final byte[] BYTES_GT = ">".getBytes(); - private List closingAttributes = new ArrayList(); + private LinkedList closingAttributes = new LinkedList(); + private boolean underline = false; + private boolean strikethrough = false; public void close() throws IOException { this.closeAttributes(); @@ -33,22 +35,45 @@ public class AnsiHTMLColorStream extends AnsiOutputStream { private void writeAttribute(String s) throws IOException { this.write("<" + s + ">"); - this.closingAttributes.add(0, s.split(" ", 2)[0]); + this.closingAttributes.add(0, s); + } + + private void closeAttribute(String s) throws IOException { + LinkedList closedAttributes = new LinkedList(); + LinkedList closingAttributes = new LinkedList(); + LinkedList unclosedAttributes = new LinkedList(); + + closingAttributes.addAll(this.closingAttributes); + for (String attr : closingAttributes) { + if (attr.toLowerCase().startsWith(s.toLowerCase())) { + for (String a : unclosedAttributes) { + closedAttributes.add(0, a); + this.write(""); + } + this.closingAttributes.removeFirstOccurrence(attr); + unclosedAttributes.clear(); + this.write(""); + } else { + unclosedAttributes.add(attr); + } + } + for (String attr : closedAttributes) { + this.write("<" + attr + ">"); + } } private void closeAttributes() throws IOException { - Iterator i$ = this.closingAttributes.iterator(); - - while(i$.hasNext()) { - String attr = (String)i$.next(); - this.write(""); + for (String attr : closingAttributes) { + this.write(""); } + this.underline = false; + this.strikethrough = false; this.closingAttributes.clear(); } private boolean nbsp = true; - public void write(int data) throws IOException { + @Override public void write(int data) throws IOException { if (data == 32) { if (nbsp) this.out.write(BYTES_NBSP); else super.write(data); @@ -79,43 +104,66 @@ public class AnsiHTMLColorStream extends AnsiOutputStream { this.closeAttributes(); } + private String parseTextDecoration() { + String dec = ""; + if (underline) dec += " underline"; + if (strikethrough) dec += " line-through"; + if (dec.length() <= 0) dec += " none"; + + return dec.substring(1); + } + + @Override protected void processSetAttribute(int attribute) throws IOException { switch(attribute) { case 1: this.writeAttribute("b"); break; - case 4: - this.writeAttribute("u"); - case 7: - case 27: - default: + case 3: + this.writeAttribute("i"); break; - case 8: - this.write("\u001b[8m"); - this.concealOn = true; + case 4: + this.closeAttribute("span class=\"ansi-decoration"); + this.underline = true; + this.writeAttribute("span class=\"ansi-decoration\" style=\"text-decoration: " + parseTextDecoration() + ";\""); + break; + case 9: + this.closeAttribute("span class=\"ansi-decoration"); + this.strikethrough = true; + this.writeAttribute("span class=\"ansi-decoration\" style=\"text-decoration: " + parseTextDecoration() + ";\""); break; case 22: - this.closeAttributes(); + this.closeAttribute("b"); + break; + case 23: + this.closeAttribute("i"); break; case 24: - this.closeAttributes(); + this.closeAttribute("span class=\"ansi-decoration"); + this.underline = false; + this.writeAttribute("span class=\"ansi-decoration\" style=\"text-decoration: " + parseTextDecoration() + ";\""); + break; + case 29: + this.closeAttribute("span class=\"ansi-decoration"); + this.strikethrough = false; + this.writeAttribute("span class=\"ansi-decoration\" style=\"text-decoration: " + parseTextDecoration() + ";\""); + break; + default: + break; } - } + @Override protected void processAttributeRest() throws IOException { - if (this.concealOn) { - this.write("\u001b[0m"); - this.concealOn = false; - } - this.closeAttributes(); } + @Override protected void processSetForegroundColor(int color) throws IOException { - this.writeAttribute("span class=\"ansi\" style=\"color: #" + ANSI_COLOR_MAP[color] + ";\""); + this.writeAttribute("span class=\"ansi-foreground\" style=\"color: #" + ANSI_COLOR_MAP[color] + ";\""); } + @Override protected void processSetBackgroundColor(int color) throws IOException { this.writeAttribute("span class=\"ansi-background\" style=\"background-color: #" + ANSI_COLOR_MAP[color] + ";\""); } diff --git a/SubServers.Host/pom.xml b/SubServers.Host/pom.xml index c8e8700d..df8f4aa1 100644 --- a/SubServers.Host/pom.xml +++ b/SubServers.Host/pom.xml @@ -20,7 +20,7 @@ net.ME1312.Galaxi GalaxiEngine - 18w43g + 18w44a compile diff --git a/SubServers.Sync/src/net/ME1312/SubServers/Sync/Library/Util.java b/SubServers.Sync/src/net/ME1312/SubServers/Sync/Library/Util.java index f4e8d47f..4f77d43b 100644 --- a/SubServers.Sync/src/net/ME1312/SubServers/Sync/Library/Util.java +++ b/SubServers.Sync/src/net/ME1312/SubServers/Sync/Library/Util.java @@ -249,29 +249,29 @@ public final class Util { * @return Unescaped String */ public static String unescapeJavaString(String str) { - StringBuilder sb = new StringBuilder(str.length()); for (int i = 0; i < str.length(); i++) { - char ch = str.charAt(i); + int ch = str.codePointAt(i); if (ch == '\\') { - char nextChar = (i == str.length() - 1) ? '\\' : str - .charAt(i + 1); + int nextChar = (i == str.length() - 1) ? '\\' : str + .codePointAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { - String code = "" + nextChar; + StringBuilder code = new StringBuilder(); + code.appendCodePoint(nextChar); i++; - if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' - && str.charAt(i + 1) <= '7') { - code += str.charAt(i + 1); + if ((i < str.length() - 1) && str.codePointAt(i + 1) >= '0' + && str.codePointAt(i + 1) <= '7') { + code.appendCodePoint(str.codePointAt(i + 1)); i++; - if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' - && str.charAt(i + 1) <= '7') { - code += str.charAt(i + 1); + if ((i < str.length() - 1) && str.codePointAt(i + 1) >= '0' + && str.codePointAt(i + 1) <= '7') { + code.appendCodePoint(str.codePointAt(i + 1)); i++; } } - sb.append((char) Integer.parseInt(code, 8)); + sb.append((char) Integer.parseInt(code.toString(), 8)); continue; } switch (nextChar) { @@ -299,22 +299,45 @@ public final class Util { case '\'': ch = '\''; break; - // Hex Unicode: u???? + // Hex Unicode Char: u???? + // Hex Unicode Codepoint: u{??????} case 'u': - if (i >= str.length() - 5) { + try { + if (i >= str.length() - 4) throw new IllegalStateException(); + StringBuilder escape = new StringBuilder(); + int offset = 2; + + if (str.codePointAt(i + 2) != '{') { + if (i >= str.length() - 5) throw new IllegalStateException(); + while (offset <= 5) { + Integer.toString(str.codePointAt(i + offset), 16); + escape.appendCodePoint(str.codePointAt(i + offset)); + offset++; + } + offset--; + } else { + offset++; + while (str.codePointAt(i + offset) != '}') { + Integer.toString(str.codePointAt(i + offset), 16); + escape.appendCodePoint(str.codePointAt(i + offset)); + offset++; + } + } + sb.append(new String(new int[]{ + Integer.parseInt(escape.toString(), 16) + }, 0, 1)); + + i += offset; + continue; + } catch (Throwable e){ + sb.append('\\'); ch = 'u'; break; } - int code = Integer.parseInt( - "" + str.charAt(i + 2) + str.charAt(i + 3) - + str.charAt(i + 4) + str.charAt(i + 5), 16); - sb.append(Character.toChars(code)); - i += 5; - continue; } i++; } - sb.append(ch); + sb.appendCodePoint(ch); } return sb.toString(); }