XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEServerManager

【Java】我的世界XFE服务器管理器

公开
关注 0 Fork 0 Star 0
UTF-8
package com.xfestudio.xfeservermanager.core.trigger;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/** Small JSON value parser used by nested trigger-variable containers. */
final class TriggerJsonValueParser {
    private final String source;
    private int position;

    private TriggerJsonValueParser(String source) {
        this.source = source;
    }

    static Object parse(String source) {
        if (source == null || source.length() > 65_536) {
            throw new IllegalArgumentException("structured variable value is too long");
        }
        TriggerJsonValueParser parser = new TriggerJsonValueParser(source);
        Object value = parser.value();
        parser.whitespace();
        if (parser.position != source.length()) throw parser.invalid("unexpected trailing content");
        return value;
    }

    private Object value() {
        whitespace();
        if (position >= source.length()) throw invalid("expected a JSON value");
        return switch (source.charAt(position)) {
            case '"' -> string();
            case '[' -> array();
            case '{' -> object();
            case 't' -> literal("true", Boolean.TRUE);
            case 'f' -> literal("false", Boolean.FALSE);
            case 'n' -> literal("null", null);
            default -> number();
        };
    }

    private List<Object> array() {
        position++;
        List<Object> result = new ArrayList<>();
        whitespace();
        if (consume(']')) return List.of();
        while (true) {
            result.add(value());
            whitespace();
            if (consume(']')) return Collections.unmodifiableList(new ArrayList<>(result));
            require(',');
        }
    }

    private Map<String, Object> object() {
        position++;
        Map<String, Object> result = new LinkedHashMap<>();
        whitespace();
        if (consume('}')) return Map.of();
        while (true) {
            whitespace();
            if (position >= source.length() || source.charAt(position) != '"') {
                throw invalid("dictionary keys must be JSON strings");
            }
            String key = string();
            whitespace();
            require(':');
            if (result.containsKey(key)) {
                throw invalid("duplicate dictionary key " + key);
            }
            result.put(key, value());
            whitespace();
            if (consume('}')) return Collections.unmodifiableMap(new LinkedHashMap<>(result));
            require(',');
        }
    }

    private String string() {
        require('"');
        StringBuilder result = new StringBuilder();
        while (position < source.length()) {
            char current = source.charAt(position++);
            if (current == '"') return result.toString();
            if (current < 0x20) throw invalid("control character in JSON string");
            if (current != '\\') {
                result.append(current);
                continue;
            }
            if (position >= source.length()) throw invalid("unfinished JSON escape");
            char escaped = source.charAt(position++);
            switch (escaped) {
                case '"', '\\', '/' -> result.append(escaped);
                case 'b' -> result.append('\b');
                case 'f' -> result.append('\f');
                case 'n' -> result.append('\n');
                case 'r' -> result.append('\r');
                case 't' -> result.append('\t');
                case 'u' -> result.append(unicode());
                default -> throw invalid("unsupported JSON escape");
            }
        }
        throw invalid("unterminated JSON string");
    }

    private char unicode() {
        if (position + 4 > source.length()) throw invalid("unfinished Unicode escape");
        try {
            char result = (char) Integer.parseInt(source.substring(position, position + 4), 16);
            position += 4;
            return result;
        } catch (NumberFormatException invalid) {
            throw invalid("invalid Unicode escape");
        }
    }

    private Object number() {
        int start = position;
        if (consume('-') && position >= source.length()) throw invalid("unfinished number");
        if (consume('0')) {
            // A leading zero is complete unless a fraction or exponent follows.
        } else {
            digits();
        }
        boolean decimal = false;
        if (consume('.')) {
            decimal = true;
            digits();
        }
        if (position < source.length() && (source.charAt(position) == 'e' || source.charAt(position) == 'E')) {
            decimal = true;
            position++;
            if (position < source.length() && (source.charAt(position) == '+' || source.charAt(position) == '-')) position++;
            digits();
        }
        String raw = source.substring(start, position);
        try {
            if (!decimal) return Long.parseLong(raw);
            double value = Double.parseDouble(raw);
            if (!Double.isFinite(value)) throw invalid("number must be finite");
            return value;
        } catch (NumberFormatException invalid) {
            throw invalid("invalid JSON number");
        }
    }

    private void digits() {
        int start = position;
        while (position < source.length() && Character.isDigit(source.charAt(position))) position++;
        if (position == start) throw invalid("expected a digit");
    }

    private Object literal(String literal, Object value) {
        if (!source.startsWith(literal, position)) throw invalid("invalid JSON value");
        position += literal.length();
        return value;
    }

    private void whitespace() {
        while (position < source.length() && Character.isWhitespace(source.charAt(position))) position++;
    }

    private boolean consume(char expected) {
        if (position < source.length() && source.charAt(position) == expected) {
            position++;
            return true;
        }
        return false;
    }

    private void require(char expected) {
        if (!consume(expected)) throw invalid("expected '" + expected + "'");
    }

    private IllegalArgumentException invalid(String message) {
        return new IllegalArgumentException(message + " at character " + (position + 1));
    }
}