S19: consume versioned engineering resources and move landscape documentation
All checks were successful
Build, test and publish / verify (push) Successful in 8m54s
All checks were successful
Build, test and publish / verify (push) Successful in 8m54s
This commit is contained in:
154
generated/ci/Json.java
Normal file
154
generated/ci/Json.java
Normal file
@@ -0,0 +1,154 @@
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
|
||||
/** Small dependency-free JSON codec for API/configuration objects, not application data. */
|
||||
final class Json {
|
||||
static Object parse(String source) {
|
||||
Parser parser = new Parser(source);
|
||||
Object result = parser.value();
|
||||
parser.space();
|
||||
if (parser.pos != source.length()) throw parser.invalid();
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String, Object> object(Object value) {
|
||||
if (!(value instanceof Map<?, ?>)) throw new IllegalArgumentException("Expected a JSON object");
|
||||
return (Map<String, Object>) value;
|
||||
}
|
||||
|
||||
static String write(Object value) {
|
||||
if (value == null) return "null";
|
||||
if (value instanceof String text) {
|
||||
StringBuilder out = new StringBuilder("\"");
|
||||
for (char c : text.toCharArray()) {
|
||||
switch (c) {
|
||||
case '"' -> out.append("\\\"");
|
||||
case '\\' -> out.append("\\\\");
|
||||
case '\n' -> out.append("\\n");
|
||||
case '\r' -> out.append("\\r");
|
||||
case '\t' -> out.append("\\t");
|
||||
default -> {
|
||||
if (c < 32) out.append(String.format("\\u%04x", (int) c));
|
||||
else out.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return out.append('"').toString();
|
||||
}
|
||||
if (value instanceof Boolean
|
||||
|| value instanceof BigDecimal
|
||||
|| value instanceof Integer
|
||||
|| value instanceof Long) return value.toString();
|
||||
if (value instanceof Map<?, ?> map) {
|
||||
StringJoiner out = new StringJoiner(",", "{", "}");
|
||||
map.forEach((k, v) -> out.add(write(Objects.toString(k)) + ":" + write(v)));
|
||||
return out.toString();
|
||||
}
|
||||
if (value instanceof List<?> list) {
|
||||
StringJoiner out = new StringJoiner(",", "[", "]");
|
||||
list.forEach(v -> out.add(write(v)));
|
||||
return out.toString();
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported JSON value");
|
||||
}
|
||||
|
||||
private static final class Parser {
|
||||
final String source;
|
||||
int pos;
|
||||
|
||||
Parser(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
IllegalArgumentException invalid() {
|
||||
return new IllegalArgumentException("Invalid JSON at offset " + pos);
|
||||
}
|
||||
|
||||
void space() {
|
||||
while (pos < source.length() && " \r\n\t".indexOf(source.charAt(pos)) >= 0) pos++;
|
||||
}
|
||||
|
||||
boolean take(char c) {
|
||||
space();
|
||||
if (pos < source.length() && source.charAt(pos) == c) {
|
||||
pos++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Object value() {
|
||||
space();
|
||||
if (pos >= source.length()) throw invalid();
|
||||
char c = source.charAt(pos);
|
||||
if (c == '"') return string();
|
||||
if (take('{')) {
|
||||
Map<String, Object> out = new LinkedHashMap<>();
|
||||
if (take('}')) return out;
|
||||
do {
|
||||
space();
|
||||
if (pos >= source.length() || source.charAt(pos) != '"') throw invalid();
|
||||
String key = string();
|
||||
if (!take(':') || out.containsKey(key)) throw invalid();
|
||||
out.put(key, value());
|
||||
} while (take(','));
|
||||
if (!take('}')) throw invalid();
|
||||
return out;
|
||||
}
|
||||
if (take('[')) {
|
||||
List<Object> out = new ArrayList<>();
|
||||
if (take(']')) return out;
|
||||
do {
|
||||
out.add(value());
|
||||
} while (take(','));
|
||||
if (!take(']')) throw invalid();
|
||||
return out;
|
||||
}
|
||||
for (String token : List.of("true", "false", "null")) {
|
||||
if (source.startsWith(token, pos)) {
|
||||
pos += token.length();
|
||||
return token.equals("null") ? null : Boolean.valueOf(token);
|
||||
}
|
||||
}
|
||||
int start = pos;
|
||||
while (pos < source.length() && "-+0123456789.eE".indexOf(source.charAt(pos)) >= 0) pos++;
|
||||
String number = source.substring(start, pos);
|
||||
if (!number.matches("-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?")) throw invalid();
|
||||
return new BigDecimal(number);
|
||||
}
|
||||
|
||||
String string() {
|
||||
pos++;
|
||||
StringBuilder out = new StringBuilder();
|
||||
while (pos < source.length()) {
|
||||
char c = source.charAt(pos++);
|
||||
if (c == '"') return out.toString();
|
||||
if (c < 32) throw invalid();
|
||||
if (c != '\\') {
|
||||
out.append(c);
|
||||
continue;
|
||||
}
|
||||
if (pos >= source.length()) throw invalid();
|
||||
char escaped = source.charAt(pos++);
|
||||
switch (escaped) {
|
||||
case '"', '\\', '/' -> out.append(escaped);
|
||||
case 'b' -> out.append('\b');
|
||||
case 'f' -> out.append('\f');
|
||||
case 'n' -> out.append('\n');
|
||||
case 'r' -> out.append('\r');
|
||||
case 't' -> out.append('\t');
|
||||
case 'u' -> {
|
||||
if (pos + 4 > source.length()) throw invalid();
|
||||
String digits = source.substring(pos, pos + 4);
|
||||
if (!digits.matches("[0-9a-fA-F]{4}")) throw invalid();
|
||||
out.append((char) Integer.parseInt(digits, 16));
|
||||
pos += 4;
|
||||
}
|
||||
default -> throw invalid();
|
||||
}
|
||||
}
|
||||
throw invalid();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user