diff --git a/src/main/java/dev/latvian/apps/webutils/ansi/Ansi.java b/src/main/java/dev/latvian/apps/webutils/ansi/Ansi.java index f9266d7..cff1966 100644 --- a/src/main/java/dev/latvian/apps/webutils/ansi/Ansi.java +++ b/src/main/java/dev/latvian/apps/webutils/ansi/Ansi.java @@ -1,8 +1,11 @@ package dev.latvian.apps.webutils.ansi; import dev.latvian.apps.webutils.TimeUtils; +import dev.latvian.apps.webutils.json.JSON; +import org.jetbrains.annotations.Nullable; import java.util.Date; +import java.util.Map; import java.util.regex.Pattern; /** @@ -39,6 +42,87 @@ static AnsiComponent of(Object text) { return text instanceof AnsiComponent ac ? ac : new AnsiComponent(text); } + static AnsiComponent ofObject(Object object) { + return ofObject0(object, 0); + } + + private static AnsiComponent ofObject0(@Nullable Object object, int depth) { + if (object == null || object == JSON.NULL) { + return Ansi.darkRed("null"); + } else if (object instanceof Map map) { + var builder = Ansi.of().treeColor(depth); + builder.append('{'); + boolean first = true; + + for (var entry : map.entrySet()) { + if (first) { + first = false; + } else { + builder.append(','); + } + + builder.append(Ansi.cyan("\"" + entry.getKey() + "\"")); + builder.append(':'); + builder.append(ofObject0(entry.getValue(), depth + 1)); + } + + builder.append('}'); + return builder; + } else if (object instanceof Iterable itr) { + var builder = Ansi.of(); + builder.append('['); + boolean first = true; + + for (var e : itr) { + if (first) { + first = false; + } else { + builder.append(','); + } + + builder.append(ofObject0(e, depth + 1)); + } + + builder.append(']'); + return builder; + } else if (object instanceof CharSequence) { + return Ansi.orange("\"" + object + "\""); + } else if (object instanceof Boolean b) { + return b ? Ansi.green("true") : Ansi.red("false"); + } else if (object instanceof Number) { + return Ansi.purple(object); + } else if (object.getClass().isRecord()) { + var builder = Ansi.of().treeColor(depth); + builder.append('{'); + boolean first = true; + + for (var component : object.getClass().getRecordComponents()) { + if (first) { + first = false; + } else { + builder.append(','); + } + + builder.append(Ansi.cyan("\"" + component.getName() + "\"")); + builder.append(':'); + + try { + builder.append(ofObject0(component.getAccessor().invoke(object), depth + 1)); + } catch (Exception ex) { + builder.append(darkRed("").whiteBg()); + ex.printStackTrace(); + } + } + + builder.append('}'); + return builder; + } else if (object.getClass().isEnum()) { + return Ansi.orange(object); + } else { + return Ansi.lightGray(object.toString()); + } + } + static AnsiComponent bold(Object text) { return of(text).bold(); } diff --git a/src/main/java/dev/latvian/apps/webutils/ansi/AnsiComponent.java b/src/main/java/dev/latvian/apps/webutils/ansi/AnsiComponent.java index f41806b..a6cddca 100644 --- a/src/main/java/dev/latvian/apps/webutils/ansi/AnsiComponent.java +++ b/src/main/java/dev/latvian/apps/webutils/ansi/AnsiComponent.java @@ -8,33 +8,33 @@ public class AnsiComponent implements AnsiAppendable { public String text; private int color = -1; private int bgColor = -1; - private boolean bold = false; - private boolean italic = false; - private boolean underline = false; - private boolean blink = false; - private boolean reverse = false; - private boolean hidden = false; - private boolean strikethrough = false; + private int bold = -1; + private int italic = -1; + private int underline = -1; + private int blink = -1; + private int reverse = -1; + private int hidden = -1; + private int strikethrough = -1; private List children; public AnsiComponent(Object text) { this.text = String.valueOf(text); } - private static boolean appendMode(StringBuilder builder, boolean semi, boolean prevMode, boolean mode, char code, char resetCode, AnsiContext ctx, String debug) { + private static boolean appendMode(StringBuilder builder, boolean semi, int prevMode, int mode, char code, char resetCode, AnsiContext ctx, String debug) { if (prevMode != mode) { if (semi) { builder.append(';'); } if (ctx.debug()) { - if (mode) { + if (mode == 1) { builder.append(debug); } else { builder.append('!'); builder.append(debug); } - } else if (mode) { + } else if (mode == 1) { builder.append(code); } else { builder.append('2'); @@ -117,13 +117,13 @@ public void appendAnsi(StringBuilder builder, AnsiContext ctx) { int pColor = parent == null ? -1 : parent.color; int pBgColor = parent == null ? -1 : parent.bgColor; - boolean pBold = parent != null && parent.bold; - boolean pItalic = parent != null && parent.italic; - boolean pUnderline = parent != null && parent.underline; - boolean pBlink = parent != null && parent.blink; - boolean pReverse = parent != null && parent.reverse; - boolean pHidden = parent != null && parent.hidden; - boolean pStrikethrough = parent != null && parent.strikethrough; + int pBold = parent == null ? -1 : parent.bold; + int pItalic = parent == null ? -1 : parent.italic; + int pUnderline = parent == null ? -1 : parent.underline; + int pBlink = parent == null ? -1 : parent.blink; + int pReverse = parent == null ? -1 : parent.reverse; + int pHidden = parent == null ? -1 : parent.hidden; + int pStrikethrough = parent == null ? -1 : parent.strikethrough; boolean changed = color != pColor || bgColor != pBgColor || bold != pBold || italic != pItalic || underline != pUnderline || blink != pBlink || reverse != pReverse || hidden != pHidden || strikethrough != pStrikethrough; if (changed) { @@ -230,37 +230,37 @@ public AnsiComponent append(Object text) { } public AnsiComponent bold() { - bold = true; + bold = 1; return this; } public AnsiComponent italic() { - italic = true; + italic = 1; return this; } public AnsiComponent underline() { - underline = true; + underline = 1; return this; } public AnsiComponent blink() { - blink = true; + blink = 1; return this; } public AnsiComponent reverse() { - reverse = true; + reverse = 1; return this; } public AnsiComponent hidden() { - hidden = true; + hidden = 1; return this; } public AnsiComponent strikethrough() { - strikethrough = true; + strikethrough = 1; return this; } @@ -341,7 +341,7 @@ public AnsiComponent error() { return white().darkRedBg(); } - public AnsiComponent debugColor(int index) { + public AnsiComponent treeColor(int index) { return switch (index & 3) { case 0 -> red(); case 1 -> green(); diff --git a/src/main/java/dev/latvian/apps/webutils/ansi/AnsiJava.java b/src/main/java/dev/latvian/apps/webutils/ansi/AnsiJava.java deleted file mode 100644 index c536ef7..0000000 --- a/src/main/java/dev/latvian/apps/webutils/ansi/AnsiJava.java +++ /dev/null @@ -1,62 +0,0 @@ -package dev.latvian.apps.webutils.ansi; - -import dev.latvian.apps.webutils.json.JSON; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public class AnsiJava { - public static AnsiComponent of(Object object) { - return of0(object, 0); - } - - private static AnsiComponent of0(@Nullable Object object, int depth) { - if (object == null || object == JSON.NULL) { - return Ansi.darkRed("null"); - } else if (object instanceof Map map) { - var builder = Ansi.of().debugColor(depth); - builder.append('{'); - boolean first = true; - - for (var entry : map.entrySet()) { - if (first) { - first = false; - } else { - builder.append(','); - } - - builder.append(Ansi.cyan("\"" + entry.getKey() + "\"")); - builder.append(':'); - builder.append(of0(entry.getValue(), depth + 1)); - } - - builder.append('}'); - return builder; - } else if (object instanceof Iterable itr) { - var builder = Ansi.of(); - builder.append('['); - boolean first = true; - - for (var e : itr) { - if (first) { - first = false; - } else { - builder.append(','); - } - - builder.append(of0(e, depth + 1)); - } - - builder.append(']'); - return builder; - } else if (object instanceof CharSequence) { - return Ansi.orange("\"" + object + "\""); - } else if (object instanceof Boolean b) { - return b ? Ansi.green("true") : Ansi.red("false"); - } else if (object instanceof Number) { - return Ansi.purple(object); - } else { - return Ansi.lightGray(object.toString()); - } - } -} diff --git a/src/main/java/dev/latvian/apps/webutils/json/BufferedJSONReader.java b/src/main/java/dev/latvian/apps/webutils/json/BufferedJSONReader.java index 78e93ba..3b1ee37 100644 --- a/src/main/java/dev/latvian/apps/webutils/json/BufferedJSONReader.java +++ b/src/main/java/dev/latvian/apps/webutils/json/BufferedJSONReader.java @@ -5,10 +5,14 @@ public class BufferedJSONReader implements JSONReader { private final JSON json; private final Reader reader; + private boolean move; + private char peek; public BufferedJSONReader(JSON json, Reader reader) { this.json = json; this.reader = reader; + this.move = true; + this.peek = 0; } @Override @@ -18,22 +22,22 @@ public JSON json() { @Override public char read() { - try { - return (char) reader.read(); - } catch (Exception e) { - throw new IllegalStateException(e); - } + move = true; + return peek(); } @Override public char peek() { - try { - reader.mark(1); - int c = reader.read(); - reader.reset(); - return (char) c; - } catch (Exception e) { - throw new IllegalStateException(e); + if (move) { + move = false; + + try { + peek = (char) reader.read(); + } catch (Exception e) { + throw new IllegalStateException(e); + } } + + return peek; } } diff --git a/src/main/java/dev/latvian/apps/webutils/json/JSONReader.java b/src/main/java/dev/latvian/apps/webutils/json/JSONReader.java index 38fd989..64c3781 100644 --- a/src/main/java/dev/latvian/apps/webutils/json/JSONReader.java +++ b/src/main/java/dev/latvian/apps/webutils/json/JSONReader.java @@ -28,7 +28,6 @@ default void skipWhitespace() { } } - @SuppressWarnings("unchecked") default T adapt(Class type) { return json().adapt(readValue(), type); } @@ -115,17 +114,43 @@ default Number readNumber() { var c = peek(); - while (c == '-' || c == '+' || c == '.' || (c >= '0' && c <= '9')) { + if (c == '-' || c == '+') { builder.append(read()); c = peek(); } - String str = builder.toString(); + boolean floating = false; - if (str.contains(".")) { + while (true) { + if (c == '.') { + if (floating) { + throw new IllegalStateException("Invalid number: Multiple '.'"); + } else { + floating = true; + } + + builder.append(read()); + } else if (c == 'E' || c == 'e') { + builder.append(read()); + + if (peek() == '-') { + builder.append(read()); + } + } else if (c >= '0' && c <= '9') { + builder.append(read()); + } else { + break; + } + + c = peek(); + } + + var str = builder.toString(); + + if (floating) { return Double.parseDouble(str); } else { - return Long.parseLong(str); + return Long.decode(str); } } diff --git a/src/test/java/dev/latvian/apps/webutils/test/AnsiTests.java b/src/test/java/dev/latvian/apps/webutils/test/AnsiTests.java index cbcf287..8a644c7 100644 --- a/src/test/java/dev/latvian/apps/webutils/test/AnsiTests.java +++ b/src/test/java/dev/latvian/apps/webutils/test/AnsiTests.java @@ -9,7 +9,7 @@ public class AnsiTests { public void components() { var c = Ansi.of(); c.append("GET 200 "); - c.append(Ansi.cyan("Test")); + c.append(Ansi.cyan("Test").underline()); c.append(" /test"); var c1 = Ansi.of("WEB ").green().append(c); diff --git a/src/test/java/dev/latvian/apps/webutils/test/JSONTests.java b/src/test/java/dev/latvian/apps/webutils/test/JSONTests.java index 02b8222..4fb585b 100644 --- a/src/test/java/dev/latvian/apps/webutils/test/JSONTests.java +++ b/src/test/java/dev/latvian/apps/webutils/test/JSONTests.java @@ -1,5 +1,6 @@ package dev.latvian.apps.webutils.test; +import dev.latvian.apps.webutils.ansi.Ansi; import dev.latvian.apps.webutils.json.JSON; import dev.latvian.apps.webutils.json.JSONObject; import org.junit.jupiter.api.Assertions; @@ -33,4 +34,16 @@ public void adapt() { Assertions.assertEquals(config.discord[0].clientId, "7"); Assertions.assertEquals(config.discord[0].clientSecret, "shh"); } + + @Test + public void readRemoteObject() throws Exception { + // {"hours":672,"total_launches":7864423,"hourly":11700.028795489174,"ml":[{"ml":1,"fraction":0.9412095458242773,"launches":7402070},{"ml":2,"fraction":0.05879045417572275,"launches":462353}],"mc":[{"mc":2001,"fraction":0.6443314404629558,"launches":5067295},{"mc":1902,"fraction":0.3556624561013567,"launches":2797080},{"mc":2004,"fraction":6.103435687525964E-6,"launches":48}],"mlmc":[{"ml":1,"mc":2001,"fraction":0.6154869848684386,"launches":4840450},{"ml":1,"mc":1902,"fraction":0.3257164575201512,"launches":2561572},{"ml":2,"mc":1902,"fraction":0.029945998581205512,"launches":235508},{"ml":2,"mc":2001,"fraction":0.028844455594517232,"launches":226845},{"ml":1,"mc":2004,"fraction":6.103435687525964E-6,"launches":48}],"launches":[{"version":"2001.6.4-build.114","fraction":0.26559062247796184,"launches":2088717},{"version":"2001.6.4-build.120","fraction":0.21153312836809515,"launches":1663586},{"version":"1902.6.2-build.45","fraction":0.1828411315108559,"launches":1437940},{"version":"2001.6.4-build.127","fraction":0.0810527358459737,"launches":637433},{"version":"1902.6.2-build.3","fraction":0.038601560470488426,"launches":303579},{"version":"2001.6.4-build.95","fraction":0.03645264248883866,"launches":286679},{"version":"1902.6.2-build.15","fraction":0.02442060911525232,"launches":192054},{"version":"1902.6.2-build.50","fraction":0.018523800156731142,"launches":145679},{"version":"2001.6.3-build.83","fraction":0.018382658206457105,"launches":144569},{"version":"1902.6.2-build.27","fraction":0.01564005394928528,"launches":123000},{"version":"","fraction":1.3987040117246999E-6,"launches":11}]} + var content = "{\"hours\":672,\"total_launches\":7864423,\"hourly\":11700.028795489174,\"ml\":[{\"ml\":1,\"fraction\":0.9412095458242773,\"launches\":7402070},{\"ml\":2,\"fraction\":0.05879045417572275,\"launches\":462353}],\"mc\":[{\"mc\":2001,\"fraction\":0.6443314404629558,\"launches\":5067295},{\"mc\":1902,\"fraction\":0.3556624561013567,\"launches\":2797080},{\"mc\":2004,\"fraction\":6.103435687525964E-6,\"launches\":48}],\"mlmc\":[{\"ml\":1,\"mc\":2001,\"fraction\":0.6154869848684386,\"launches\":4840450},{\"ml\":1,\"mc\":1902,\"fraction\":0.3257164575201512,\"launches\":2561572},{\"ml\":2,\"mc\":1902,\"fraction\":0.029945998581205512,\"launches\":235508},{\"ml\":2,\"mc\":2001,\"fraction\":0.028844455594517232,\"launches\":226845},{\"ml\":1,\"mc\":2004,\"fraction\":6.103435687525964E-6,\"launches\":48}],\"launches\":[{\"version\":\"2001.6.4-build.114\",\"fraction\":0.26559062247796184,\"launches\":2088717},{\"version\":\"2001.6.4-build.120\",\"fraction\":0.21153312836809515,\"launches\":1663586},{\"version\":\"1902.6.2-build.45\",\"fraction\":0.1828411315108559,\"launches\":1437940},{\"version\":\"2001.6.4-build.127\",\"fraction\":0.0810527358459737,\"launches\":637433},{\"version\":\"1902.6.2-build.3\",\"fraction\":0.038601560470488426,\"launches\":303579},{\"version\":\"2001.6.4-build.95\",\"fraction\":0.03645264248883866,\"launches\":286679},{\"version\":\"1902.6.2-build.15\",\"fraction\":0.02442060911525232,\"launches\":192054},{\"version\":\"1902.6.2-build.50\",\"fraction\":0.018523800156731142,\"launches\":145679},{\"version\":\"2001.6.3-build.83\",\"fraction\":0.018382658206457105,\"launches\":144569},{\"version\":\"1902.6.2-build.27\",\"fraction\":0.01564005394928528,\"launches\":123000},{\"version\":\"\",\"fraction\":1.3987040117246999E-6,\"launches\":11}]}"; + Ansi.log(content); + var json = JSON.DEFAULT.read(content).readObject(); + Ansi.log(Ansi.ofObject(json)); + + Ansi.log(json.asInt("total_launches")); + Ansi.log(json.asDouble("hourly")); + } }