/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.test.lib.json;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public interface JSONValue {
public final class JSONObject implements JSONValue {
private final Map<String, JSONValue> value;
public JSONObject(Map<String, JSONValue> value) {
this.value = value;
}
@Override
public JSONObject asObject() {
return this;
}
public JSONValue get(String k) {
return value.get(k);
}
@Override
public int size() {
return value.size();
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("{");
for (var key : value.keySet()) {
builder.append("\"");
builder.append(key);
builder.append("\":");
builder.append(value.get(key).toString());
builder.append(",");
}
var end = builder.length() - 1;
if (builder.charAt(end) == ',') {
builder.deleteCharAt(end);
}
builder.append("}");
return builder.toString();
}
}
public final class JSONString implements JSONValue {
private final String value;
public JSONString(String value) {
this.value = value;
}
@Override
public String asString() {
return value;
}
@Override
public String toString() {
if (value == null) {
return "null";
}
var builder = new StringBuilder();
builder.append("\"");
for (var i = 0; i < value.length(); i++) {
var c = value.charAt(i);
switch (c) {
case '"':
builder.append("\\\"");
break;
case '\\':
builder.append("\\\\");
break;
case '/':
builder.append("\\/");
break;
case '\b':
builder.append("\\b");
break;
case '\f':
builder.append("\\f");
break;
case '\n':
builder.append("\\n");
break;
case '\r':
builder.append("\\r");
break;
case '\t':
builder.append("\\t");
break;
default:
builder.append(c);
break;
}
}
builder.append("\"");
return builder.toString();
}
}
public final class JSONArray implements JSONValue, Iterable<JSONValue> {
private final List<JSONValue> values;
public JSONArray(List<JSONValue> array) {
this.values = array;
}
@Override
public JSONArray asArray() {
return this;
}
public JSONValue get(int i) {
return values.get(i);
}
public int size() {
return values.size();
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append("[");
for (var i = 0; i < size(); i++) {
builder.append(get(i).toString());
if (i != (size() - 1)) {
builder.append(",");
}
}
builder.append("]");
return builder.toString();
}
@Override
public Iterator<JSONValue> iterator() {
return values.iterator();
}
}
class JSONParser {
private int pos = 0;
private String input;
JSONParser() {
}
private IllegalStateException failure(String message) {
return new IllegalStateException(String.format("[%d]: %s : %s", pos, message, input));
}
private char current() {
return input.charAt(pos);
}
private void advance() {
pos++;
}
private boolean hasInput() {
return pos < input.length();
}
private void expectMoreInput(String message) {
if (!hasInput()) {
throw failure(message);
}
}
private char next(String message) {
advance();
if (!hasInput()) {
throw failure(message);
}
return current();
}
private void expect(char c) {
var msg = String.format("Expected character %c", c);
var n = next(msg);
if (n != c) {
throw failure(msg);
}
}
private JSONString parseBoolean() {
if (current() == 't') {
expect('r');
expect('u');
expect('e');
advance();
return new JSONString("true");
}
if (current() == 'f') {
expect('a');
expect('l');
expect('s');
expect('e');
advance();
return new JSONString("false");
}
throw failure("a boolean can only be 'true' or 'false'");
}
private JSONValue parseNumber() {
var isInteger = true;
var builder = new StringBuilder();
if (current() == '-') {
builder.append(current());
advance();
expectMoreInput("a number cannot consist of only '-'");
}
if (current() == '0') {
builder.append(current());
advance();
if (hasInput() && current() == '.') {
isInteger = false;
builder.append(current());
advance();
expectMoreInput("a number cannot end with '.'");
if (!isDigit(current())) {
throw failure("must be at least one digit after '.'");
}
while (hasInput() && isDigit(current())) {
builder.append(current());
advance();
}
}
} else {
while (hasInput() && isDigit(current())) {
builder.append(current());
advance();
}
if (hasInput() && current() == '.') {
isInteger = false;
builder.append(current());
advance();
expectMoreInput("a number cannot end with '.'");
if (!isDigit(current())) {
throw failure("must be at least one digit after '.'");
}
while (hasInput() && isDigit(current())) {
builder.append(current());
advance();
}
}
}
if (hasInput() && (current() == 'e' || current() == 'E')) {
isInteger = false;
builder.append(current());
advance();
expectMoreInput("a number cannot end with 'e' or 'E'");
if (current() == '+' || current() == '-') {
builder.append(current());
advance();
}
if (!isDigit(current())) {
throw failure("a digit must follow {'e','E'}{'+','-'}");
}
while (hasInput() && isDigit(current())) {
builder.append(current());
advance();
}
}
var value = builder.toString();
if (isInteger) {
new BigInteger(value);
return new JSONString(value);
} else {
Double.parseDouble(value);
return new JSONString(value);
}
}
private JSONString parseString() {
var missingEndChar = "string is not terminated with '\"'";
var builder = new StringBuilder();
for (var c = next(missingEndChar); c != '"'; c = next(missingEndChar)) {
if (c == '\\') {
var n = next(missingEndChar);
switch (n) {
case '"':
builder.append("\"");
break;
case '\\':
builder.append("\\");
break;
case '/':
builder.append("/");
break;
case 'b':
builder.append("\b");
break;
case 'f':
builder.append("\f");
break;
case 'n':
builder.append("\n");
break;
case 'r':
builder.append("\r");
break;
case 't':
builder.append("\t");
break;
case 'u':
var u1 = next(missingEndChar);
var u2 = next(missingEndChar);
var u3 = next(missingEndChar);
var u4 = next(missingEndChar);
var cp = Integer.parseInt(String.format("%c%c%c%c", u1, u2, u3, u4), 16);
builder.append(new String(new int[]{cp}, 0, 1));
break;
default:
throw failure(String.format("Unexpected escaped character '%c'", n));
}
} else {
builder.append(c);
}
}
advance(); // step beyond closing "
return new JSONString(builder.toString());
}
private JSONArray parseArray() {
var error = "array is not terminated with ']'";
var list = new ArrayList<JSONValue>();
advance(); // step beyond opening '['
consumeWhitespace();
expectMoreInput(error);
while (current() != ']') {
var val = parseValue();
list.add(val);
expectMoreInput(error);
if (current() == ',') {
advance();
}
expectMoreInput(error);
}
advance(); // step beyond closing ']'
return new JSONArray(list);
}
public JSONString parseNull() {
expect('u');
expect('l');
expect('l');
advance();
return new JSONString(null);
}
public JSONObject parseObject() {
var error = "object is not terminated with '}'";
var map = new HashMap<String, JSONValue>();
advance(); // step beyond opening '{'
consumeWhitespace();
expectMoreInput(error);
while (current() != '}') {
var key = parseValue();
if (!(key instanceof JSONString)) {
throw failure("a field must of type string");
}
if (!hasInput() || current() != ':') {
throw failure("a field must be followed by ':'");
}
advance(); // skip ':'
var val = parseValue();
map.put(key.asString(), val);
expectMoreInput(error);
if (current() == ',') {
advance();
}
expectMoreInput(error);
}
advance(); // step beyond '}'
return new JSONObject(map);
}
private boolean isDigit(char c) {
return c >= '0' && c <= '9';
}
private boolean isStartOfNumber(char c) {
return isDigit(c) || c == '-';
}
private boolean isStartOfString(char c) {
return c == '"';
}
private boolean isStartOfBoolean(char c) {
return c == 't' || c == 'f';
}
private boolean isStartOfArray(char c) {
return c == '[';
}
private boolean isStartOfNull(char c) {
return c == 'n';
}
private boolean isWhitespace(char c) {
return c == '\r' ||
c == '\n' ||
c == '\t' ||
c == ' ';
}
private boolean isStartOfObject(char c) {
return c == '{';
}
private void consumeWhitespace() {
while (hasInput() && isWhitespace(current())) {
advance();
}
}
public JSONValue parseValue() {
JSONValue ret = null;
consumeWhitespace();
if (hasInput()) {
var c = current();
if (isStartOfNumber(c)) {
ret = parseNumber();
} else if (isStartOfString(c)) {
ret = parseString();
} else if (isStartOfBoolean(c)) {
ret = parseBoolean();
} else if (isStartOfArray(c)) {
ret = parseArray();
} else if (isStartOfNull(c)) {
ret = parseNull();
} else if (isStartOfObject(c)) {
ret = parseObject();
} else {
throw failure("not a valid start of a JSON value");
}
}
consumeWhitespace();
return ret;
}
public JSONValue parse(String s) {
if (s == null || s.equals("")) {
return null;
}
pos = 0;
input = s;
var result = parseValue();
if (hasInput()) {
throw failure("can only have one top-level JSON value");
}
return result;
}
}
public static JSONValue parse(String s) {
return new JSONParser().parse(s);
}
default int size() {
throw new IllegalStateException("Size operation unsupported");
}
default String asString() {
throw new IllegalStateException("Unsupported conversion to String");
}
default JSONArray asArray() {
throw new IllegalStateException("Unsupported conversion to array");
}
default JSONObject asObject() {
throw new IllegalStateException("Unsupported conversion to object");
}
default JSONValue get(String field) {
return asObject().get(field);
}
}
¤ Dauer der Verarbeitung: 0.5 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.
|