Run CI orchestration and setup as Java 25 source programs
Some checks failed
Build, test and publish / verify (push) Failing after 11s
Some checks failed
Build, test and publish / verify (push) Failing after 11s
This commit is contained in:
102
ci/Json.java
Normal file
102
ci/Json.java
Normal file
@@ -0,0 +1,102 @@
|
||||
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