package com.oracle.svm.hosted.dashboard;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
public class ToJson {
private static final String EMPTY = "";
private static final String NEW_LINE = "\n";
private static final char TAB = '\t';
private static final String OPEN_OBJECT = "{";
private static final String CLOSE_OBJECT = "}";
private static final String OPEN_ARRAY = "[";
private static final String CLOSE_ARRAY = "]";
private static final char QUOTE = '\"';
private static final char SLASH = '\\';
private static final BitSet ESC;
private static final Map<Character, Character> MAP;
static {
ESC = Arrays.asList('\\', '\"', '\b', '\f', '\n', '\r', '\t', '/').stream().collect(BitSet::new, BitSet::set, BitSet::or);
MAP = new HashMap<>();
MAP.put('\b', 'b');
MAP.put('\f', 'f');
MAP.put('\n', 'n');
MAP.put('\r', 'r');
MAP.put('\t', 't');
}
private final boolean pretty;
private final String newPrefix;
private final String prefi;
private final String colon;
private int depth = 0;
private String pref;
private final List<String> prepends = new ArrayList<String>() {
private static final long serialVersionUID = 1L;
@Override
public String get(int index) {
while (size() <= index) {
add(getFill(size()));
}
return super.get(index);
}
private String getFill(int size) {
return size == 0 ? EMPTY : (super.get(size - 1) + TAB);
}
};
public ToJson() {
this(false);
}
public ToJson(boolean pretty) {
this.pretty = pretty;
if (pretty) {
prefi = ",\n";
newPrefix = NEW_LINE;
colon = ": ";
} else {
prefi = ",";
newPrefix = EMPTY;
colon = ":";
}
pref = newPrefix;
}
public void (PrintWriter writer) {
writer.append(OPEN_OBJECT);
++depth;
}
private void prepend(PrintWriter writer) {
if (pretty) {
writer.append(prepends.get(depth));
}
}
public void put(PrintWriter writer, String name, JsonValue value) {
if (depth == 0) {
printHeader(writer);
}
if (value != null) {
writer.print(pref);
putProperty(writer, name, value);
pref = prefi;
}
}
private void putProperty(PrintWriter writer, String name, JsonValue value) {
prepend(writer);
dumpString(writer, name);
writer.append(colon);
value.dump(writer, this);
}
private static void dumpString(PrintWriter writer, String string) {
writer.append(QUOTE);
for (int i = 0, n = string.length(); i < n; ++i) {
char c = string.charAt(i);
if (ESC.get(c)) {
writer.append(SLASH);
writer.append(MAP.getOrDefault(c, c));
} else {
writer.append(c);
}
}
writer.append(QUOTE);
}
private static void dumpElement(PrintWriter writer, String element) {
writer.print(element);
}
private static void dumpNumber(PrintWriter writer, Number number) {
writer.print(number);
}
private void dumpArray(PrintWriter writer, Stream<JsonValue> values) {
writer.append(OPEN_ARRAY);
++depth;
String[] prefix = new String[]{newPrefix};
values.sequential().forEach(val -> {
if (val != null) {
writer.print(prefix[0]);
prepend(writer);
val.dump(writer, this);
prefix[0] = this.prefi;
}
});
--depth;
if (pretty && !prefix[0].equals(NEW_LINE)) {
writer.append(NEW_LINE);
prepend(writer);
}
writer.append(CLOSE_ARRAY);
}
private void dumpObject(PrintWriter writer, Stream<String> names, Function<String, JsonValue> func) {
writer.append(OPEN_OBJECT);
++depth;
String[] prefix = new String[]{newPrefix};
names.sequential().forEach(name -> {
JsonValue val = func.apply(name);
if (val != null) {
writer.print(prefix[0]);
putProperty(writer, name, val);
prefix[0] = this.prefi;
}
});
--depth;
if (pretty && !prefix[0].equals(NEW_LINE)) {
writer.append(NEW_LINE);
prepend(writer);
}
writer.append(CLOSE_OBJECT);
}
public void close(PrintWriter writer) {
--depth;
assert depth == 0;
writer.append(CLOSE_OBJECT);
}
public abstract static class JsonValue {
public static final JsonValue NULL = JsonElement.get("null");
public static final JsonValue TRUE = JsonElement.get("true");
public static final JsonValue FALSE = JsonElement.get("false");
abstract void dump(PrintWriter writer, ToJson access);
protected void build() {
}
}
private abstract static class JsonElement extends JsonValue {
static JsonElement get(String element) {
return new JsonElement() {
@Override
String getElement() {
return element;
}
};
}
abstract String getElement();
@Override
final void dump(PrintWriter writer, ToJson access) {
ToJson.dumpElement(writer, this.getElement());
}
}
public abstract static class JsonNumber extends JsonValue {
public static JsonNumber get(Number number) {
return number == null ? null : new JsonNumber() {
@Override
Number getNumber() {
return number;
}
};
}
abstract Number getNumber();
@Override
final void dump(PrintWriter writer, ToJson access) {
build();
Number number = getNumber();
if (number == null) {
JsonValue.NULL.dump(writer, access);
} else {
ToJson.dumpNumber(writer, number);
}
}
}
public abstract static class JsonString extends JsonValue {
public static JsonString get(String string) {
return string == null ? null : new JsonString() {
@Override
String getString() {
return string;
}
};
}
abstract String getString();
@Override
final void dump(PrintWriter writer, ToJson access) {
build();
String string = getString();
if (string == null) {
JsonValue.NULL.dump(writer, access);
} else {
ToJson.dumpString(writer, string);
}
}
}
public abstract static class JsonArray extends JsonValue {
public static JsonArray get(Stream<JsonValue> values) {
return values == null ? null : new JsonArray() {
@Override
Stream<JsonValue> getValues() {
return values;
}
};
}
abstract Stream<JsonValue> getValues();
@Override
final void dump(PrintWriter writer, ToJson access) {
build();
Stream<JsonValue> values = getValues();
if (values == null) {
JsonValue.NULL.dump(writer, access);
} else {
access.dumpArray(writer, values);
}
}
}
public abstract static class JsonObject extends JsonValue {
public static JsonObject get(Stream<String> names, Function<String, JsonValue> func) {
return names == null || func == null ? null : new JsonObject() {
@Override
Stream<String> getNames() {
return names;
}
@Override
JsonValue getValue(String name) {
return func.apply(name);
}
};
}
abstract Stream<String> getNames();
abstract JsonValue getValue(String name);
@Override
final void dump(PrintWriter writer, ToJson access) {
build();
Stream<String> names = getNames();
if (names == null) {
JsonValue.NULL.dump(writer, access);
} else {
access.dumpObject(writer, names, this::getValue);
}
}
}
}