mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-10 14:24:34 -05:00
android support! thanks to TachiWeb devs.
This commit is contained in:
433
AndroidCompat/src/main/java/org/json/JSONStringer.java
Normal file
433
AndroidCompat/src/main/java/org/json/JSONStringer.java
Normal file
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
// Note: this class was written without inspecting the non-free org.json sourcecode.
|
||||
|
||||
/**
|
||||
* Implements {@link JSONObject#toString} and {@link JSONArray#toString}. Most
|
||||
* application developers should use those methods directly and disregard this
|
||||
* API. For example:<pre>
|
||||
* JSONObject object = ...
|
||||
* String json = object.toString();</pre>
|
||||
*
|
||||
* <p>Stringers only encode well-formed JSON strings. In particular:
|
||||
* <ul>
|
||||
* <li>The stringer must have exactly one top-level array or object.
|
||||
* <li>Lexical scopes must be balanced: every call to {@link #array} must
|
||||
* have a matching call to {@link #endArray} and every call to {@link
|
||||
* #object} must have a matching call to {@link #endObject}.
|
||||
* <li>Arrays may not contain keys (property names).
|
||||
* <li>Objects must alternate keys (property names) and values.
|
||||
* <li>Values are inserted with either literal {@link #value(Object) value}
|
||||
* calls, or by nesting arrays or objects.
|
||||
* </ul>
|
||||
* Calls that would result in a malformed JSON string will fail with a
|
||||
* {@link JSONException}.
|
||||
*
|
||||
* <p>This class provides no facility for pretty-printing (ie. indenting)
|
||||
* output. To encode indented output, use {@link JSONObject#toString(int)} or
|
||||
* {@link JSONArray#toString(int)}.
|
||||
*
|
||||
* <p>Some implementations of the API support at most 20 levels of nesting.
|
||||
* Attempts to create more than 20 levels of nesting may fail with a {@link
|
||||
* JSONException}.
|
||||
*
|
||||
* <p>Each stringer may be used to encode a single top level value. Instances of
|
||||
* this class are not thread safe. Although this class is nonfinal, it was not
|
||||
* designed for inheritance and should not be subclassed. In particular,
|
||||
* self-use by overrideable methods is not specified. See <i>Effective Java</i>
|
||||
* Item 17, "Design and Document or inheritance or else prohibit it" for further
|
||||
* information.
|
||||
*/
|
||||
public class JSONStringer {
|
||||
|
||||
/**
|
||||
* The output data, containing at most one top-level array or object.
|
||||
*/
|
||||
final StringBuilder out = new StringBuilder();
|
||||
/**
|
||||
* Unlike the original implementation, this stack isn't limited to 20
|
||||
* levels of nesting.
|
||||
*/
|
||||
private final List<Scope> stack = new ArrayList<Scope>();
|
||||
/**
|
||||
* A string containing a full set of spaces for a single level of
|
||||
* indentation, or null for no pretty printing.
|
||||
*/
|
||||
private final String indent;
|
||||
|
||||
public JSONStringer() {
|
||||
indent = null;
|
||||
}
|
||||
|
||||
JSONStringer(int indentSpaces) {
|
||||
char[] indentChars = new char[indentSpaces];
|
||||
Arrays.fill(indentChars, ' ');
|
||||
indent = new String(indentChars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins encoding a new array. Each call to this method must be paired with
|
||||
* a call to {@link #endArray}.
|
||||
*
|
||||
* @return this stringer.
|
||||
*/
|
||||
public JSONStringer array() throws JSONException {
|
||||
return open(Scope.EMPTY_ARRAY, "[");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends encoding the current array.
|
||||
*
|
||||
* @return this stringer.
|
||||
*/
|
||||
public JSONStringer endArray() throws JSONException {
|
||||
return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins encoding a new object. Each call to this method must be paired
|
||||
* with a call to {@link #endObject}.
|
||||
*
|
||||
* @return this stringer.
|
||||
*/
|
||||
public JSONStringer object() throws JSONException {
|
||||
return open(Scope.EMPTY_OBJECT, "{");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends encoding the current object.
|
||||
*
|
||||
* @return this stringer.
|
||||
*/
|
||||
public JSONStringer endObject() throws JSONException {
|
||||
return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Enters a new scope by appending any necessary whitespace and the given
|
||||
* bracket.
|
||||
*/
|
||||
JSONStringer open(Scope empty, String openBracket) throws JSONException {
|
||||
if (stack.isEmpty() && out.length() > 0) {
|
||||
throw new JSONException("Nesting problem: multiple top-level roots");
|
||||
}
|
||||
beforeValue();
|
||||
stack.add(empty);
|
||||
out.append(openBracket);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the current scope by appending any necessary whitespace and the
|
||||
* given bracket.
|
||||
*/
|
||||
JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException {
|
||||
Scope context = peek();
|
||||
if (context != nonempty && context != empty) {
|
||||
throw new JSONException("Nesting problem");
|
||||
}
|
||||
|
||||
stack.remove(stack.size() - 1);
|
||||
if (context == nonempty) {
|
||||
newline();
|
||||
}
|
||||
out.append(closeBracket);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value on the top of the stack.
|
||||
*/
|
||||
private Scope peek() throws JSONException {
|
||||
if (stack.isEmpty()) {
|
||||
throw new JSONException("Nesting problem");
|
||||
}
|
||||
return stack.get(stack.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the value on the top of the stack with the given value.
|
||||
*/
|
||||
private void replaceTop(Scope topOfStack) {
|
||||
stack.set(stack.size() - 1, topOfStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code value}.
|
||||
*
|
||||
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
|
||||
* Integer, Long, Double or null. May not be {@link Double#isNaN() NaNs}
|
||||
* or {@link Double#isInfinite() infinities}.
|
||||
* @return this stringer.
|
||||
*/
|
||||
public JSONStringer value(Object value) throws JSONException {
|
||||
if (stack.isEmpty()) {
|
||||
throw new JSONException("Nesting problem");
|
||||
}
|
||||
|
||||
if (value instanceof JSONArray) {
|
||||
((JSONArray) value).writeTo(this);
|
||||
return this;
|
||||
|
||||
} else if (value instanceof JSONObject) {
|
||||
((JSONObject) value).writeTo(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
beforeValue();
|
||||
|
||||
if (value == null
|
||||
|| value instanceof Boolean
|
||||
|| value == JSONObject.NULL) {
|
||||
out.append(value);
|
||||
|
||||
} else if (value instanceof Number) {
|
||||
out.append(JSONObject.numberToString((Number) value));
|
||||
|
||||
} else {
|
||||
string(value.toString());
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code value} to this stringer.
|
||||
*
|
||||
* @return this stringer.
|
||||
*/
|
||||
public JSONStringer value(boolean value) throws JSONException {
|
||||
if (stack.isEmpty()) {
|
||||
throw new JSONException("Nesting problem");
|
||||
}
|
||||
beforeValue();
|
||||
out.append(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code value} to this stringer.
|
||||
*
|
||||
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or
|
||||
* {@link Double#isInfinite() infinities}.
|
||||
* @return this stringer.
|
||||
*/
|
||||
public JSONStringer value(double value) throws JSONException {
|
||||
if (stack.isEmpty()) {
|
||||
throw new JSONException("Nesting problem");
|
||||
}
|
||||
beforeValue();
|
||||
out.append(JSONObject.numberToString(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes {@code value} to this stringer.
|
||||
*
|
||||
* @return this stringer.
|
||||
*/
|
||||
public JSONStringer value(long value) throws JSONException {
|
||||
if (stack.isEmpty()) {
|
||||
throw new JSONException("Nesting problem");
|
||||
}
|
||||
beforeValue();
|
||||
out.append(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void string(String value) {
|
||||
out.append("\"");
|
||||
for (int i = 0, length = value.length(); i < length; i++) {
|
||||
char c = value.charAt(i);
|
||||
|
||||
/*
|
||||
* From RFC 4627, "All Unicode characters may be placed within the
|
||||
* quotation marks except for the characters that must be escaped:
|
||||
* quotation mark, reverse solidus, and the control characters
|
||||
* (U+0000 through U+001F)."
|
||||
*/
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
out.append('\\').append(c);
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
out.append("\\t");
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
out.append("\\b");
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
out.append("\\n");
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
out.append("\\r");
|
||||
break;
|
||||
|
||||
case '\f':
|
||||
out.append("\\f");
|
||||
break;
|
||||
|
||||
default:
|
||||
if (c <= 0x1F) {
|
||||
out.append(String.format("\\u%04x", (int) c));
|
||||
} else {
|
||||
out.append(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
out.append("\"");
|
||||
}
|
||||
|
||||
private void newline() {
|
||||
if (indent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
out.append("\n");
|
||||
for (int i = 0; i < stack.size(); i++) {
|
||||
out.append(indent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the key (property name) to this stringer.
|
||||
*
|
||||
* @param name the name of the forthcoming value. May not be null.
|
||||
* @return this stringer.
|
||||
*/
|
||||
public JSONStringer key(String name) throws JSONException {
|
||||
if (name == null) {
|
||||
throw new JSONException("Names must be non-null");
|
||||
}
|
||||
beforeKey();
|
||||
string(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts any necessary separators and whitespace before a name. Also
|
||||
* adjusts the stack to expect the key's value.
|
||||
*/
|
||||
private void beforeKey() throws JSONException {
|
||||
Scope context = peek();
|
||||
if (context == Scope.NONEMPTY_OBJECT) { // first in object
|
||||
out.append(',');
|
||||
} else if (context != Scope.EMPTY_OBJECT) { // not in an object!
|
||||
throw new JSONException("Nesting problem");
|
||||
}
|
||||
newline();
|
||||
replaceTop(Scope.DANGLING_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts any necessary separators and whitespace before a literal value,
|
||||
* inline array, or inline object. Also adjusts the stack to expect either a
|
||||
* closing bracket or another element.
|
||||
*/
|
||||
private void beforeValue() throws JSONException {
|
||||
if (stack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Scope context = peek();
|
||||
if (context == Scope.EMPTY_ARRAY) { // first in array
|
||||
replaceTop(Scope.NONEMPTY_ARRAY);
|
||||
newline();
|
||||
} else if (context == Scope.NONEMPTY_ARRAY) { // another in array
|
||||
out.append(',');
|
||||
newline();
|
||||
} else if (context == Scope.DANGLING_KEY) { // value for key
|
||||
out.append(indent == null ? ":" : ": ");
|
||||
replaceTop(Scope.NONEMPTY_OBJECT);
|
||||
} else if (context != Scope.NULL) {
|
||||
throw new JSONException("Nesting problem");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoded JSON string.
|
||||
*
|
||||
* <p>If invoked with unterminated arrays or unclosed objects, this method's
|
||||
* return value is undefined.
|
||||
*
|
||||
* <p><strong>Warning:</strong> although it contradicts the general contract
|
||||
* of {@link Object#toString}, this method returns null if the stringer
|
||||
* contains no data.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return out.length() == 0 ? null : out.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lexical scoping elements within this stringer, necessary to insert the
|
||||
* appropriate separator characters (ie. commas and colons) and to detect
|
||||
* nesting errors.
|
||||
*/
|
||||
enum Scope {
|
||||
|
||||
/**
|
||||
* An array with no elements requires no separators or newlines before
|
||||
* it is closed.
|
||||
*/
|
||||
EMPTY_ARRAY,
|
||||
|
||||
/**
|
||||
* A array with at least one value requires a comma and newline before
|
||||
* the next element.
|
||||
*/
|
||||
NONEMPTY_ARRAY,
|
||||
|
||||
/**
|
||||
* An object with no keys or values requires no separators or newlines
|
||||
* before it is closed.
|
||||
*/
|
||||
EMPTY_OBJECT,
|
||||
|
||||
/**
|
||||
* An object whose most recent element is a key. The next element must
|
||||
* be a value.
|
||||
*/
|
||||
DANGLING_KEY,
|
||||
|
||||
/**
|
||||
* An object with at least one name/value pair requires a comma and
|
||||
* newline before the next element.
|
||||
*/
|
||||
NONEMPTY_OBJECT,
|
||||
|
||||
/**
|
||||
* A special bracketless array needed by JSONStringer.join() and
|
||||
* JSONObject.quote() only. Not used for JSON encoding.
|
||||
*/
|
||||
NULL,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user