Browse Source
This commit shades the use of 'com.vaadin.external.google:android-json' in the three modules that use it. The configuration processor already did that and this commit does the same for configuration-metadata and the CLI. As a result of this commit, 'android-json' is not used nor managed internally. Closes gh-45504pull/47209/head
24 changed files with 5369 additions and 22 deletions
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
## Shaded JSON |
||||
|
||||
This source was originally taken from `com.vaadin.external.google:android-json` which |
||||
provides a clean room re-implementation of the `org.json` APIs and does not include the |
||||
"Do not use for evil" clause. |
||||
@ -0,0 +1,124 @@
@@ -0,0 +1,124 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.cli.json; |
||||
|
||||
class JSON { |
||||
|
||||
static double checkDouble(double d) throws JSONException { |
||||
if (Double.isInfinite(d) || Double.isNaN(d)) { |
||||
throw new JSONException("Forbidden numeric value: " + d); |
||||
} |
||||
return d; |
||||
} |
||||
|
||||
static Boolean toBoolean(Object value) { |
||||
if (value instanceof Boolean) { |
||||
return (Boolean) value; |
||||
} |
||||
if (value instanceof String stringValue) { |
||||
if ("true".equalsIgnoreCase(stringValue)) { |
||||
return true; |
||||
} |
||||
if ("false".equalsIgnoreCase(stringValue)) { |
||||
return false; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
static Double toDouble(Object value) { |
||||
if (value instanceof Double) { |
||||
return (Double) value; |
||||
} |
||||
if (value instanceof Number) { |
||||
return ((Number) value).doubleValue(); |
||||
} |
||||
if (value instanceof String) { |
||||
try { |
||||
return Double.valueOf((String) value); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
// Ignore
|
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
static Integer toInteger(Object value) { |
||||
if (value instanceof Integer) { |
||||
return (Integer) value; |
||||
} |
||||
if (value instanceof Number) { |
||||
return ((Number) value).intValue(); |
||||
} |
||||
if (value instanceof String) { |
||||
try { |
||||
return (int) Double.parseDouble((String) value); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
// Ignore
|
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
static Long toLong(Object value) { |
||||
if (value instanceof Long) { |
||||
return (Long) value; |
||||
} |
||||
if (value instanceof Number) { |
||||
return ((Number) value).longValue(); |
||||
} |
||||
if (value instanceof String) { |
||||
try { |
||||
return (long) Double.parseDouble((String) value); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
// Ignore
|
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
static String toString(Object value) { |
||||
if (value instanceof String) { |
||||
return (String) value; |
||||
} |
||||
if (value != null) { |
||||
return String.valueOf(value); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static JSONException typeMismatch(Object indexOrName, Object actual, String requiredType) |
||||
throws JSONException { |
||||
if (actual == null) { |
||||
throw new JSONException("Value at " + indexOrName + " is null."); |
||||
} |
||||
throw new JSONException("Value " + actual + " at " + indexOrName + " of type " + actual.getClass().getName() |
||||
+ " cannot be converted to " + requiredType); |
||||
} |
||||
|
||||
public static JSONException typeMismatch(Object actual, String requiredType) throws JSONException { |
||||
if (actual == null) { |
||||
throw new JSONException("Value is null."); |
||||
} |
||||
throw new JSONException("Value " + actual + " of type " + actual.getClass().getName() |
||||
+ " cannot be converted to " + requiredType); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,669 @@
@@ -0,0 +1,669 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.cli.json; |
||||
|
||||
import java.lang.reflect.Array; |
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.Iterator; |
||||
import java.util.List; |
||||
|
||||
// Note: this class was written without inspecting the non-free org.json source code.
|
||||
|
||||
/** |
||||
* A dense indexed sequence of values. Values may be any mix of {@link JSONObject |
||||
* JSONObjects}, other {@link JSONArray JSONArrays}, Strings, Booleans, Integers, Longs, |
||||
* Doubles, {@code null} or {@link JSONObject#NULL}. Values may not be |
||||
* {@link Double#isNaN() NaNs}, {@link Double#isInfinite() infinities}, or of any type not |
||||
* listed here. |
||||
* <p> |
||||
* {@code JSONArray} has the same type coercion behavior and optional/mandatory accessors |
||||
* as {@link JSONObject}. See that class' documentation for details. |
||||
* <p> |
||||
* <strong>Warning:</strong> this class represents null in two incompatible ways: the |
||||
* standard Java {@code null} reference, and the sentinel value {@link JSONObject#NULL}. |
||||
* In particular, {@code get} fails if the requested index holds the null reference, but |
||||
* succeeds if it holds {@code JSONObject.NULL}. |
||||
* <p> |
||||
* 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 |
||||
* overridable 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 JSONArray { |
||||
|
||||
private final List<Object> values; |
||||
|
||||
/** |
||||
* Creates a {@code JSONArray} with no values. |
||||
*/ |
||||
public JSONArray() { |
||||
this.values = new ArrayList<>(); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONArray} by copying all values from the given collection. |
||||
* @param copyFrom a collection whose values are of supported types. Unsupported |
||||
* values are not permitted and will yield an array in an inconsistent state. |
||||
*/ |
||||
/* Accept a raw type for API compatibility */ |
||||
@SuppressWarnings("rawtypes") |
||||
public JSONArray(Collection copyFrom) { |
||||
this(); |
||||
if (copyFrom != null) { |
||||
for (Iterator it = copyFrom.iterator(); it.hasNext();) { |
||||
put(JSONObject.wrap(it.next())); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONArray} with values from the next array in the tokener. |
||||
* @param readFrom a tokener whose nextValue() method will yield a {@code JSONArray}. |
||||
* @throws JSONException if the parse fails or doesn't yield a {@code JSONArray}. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray(JSONTokener readFrom) throws JSONException { |
||||
/* |
||||
* Getting the parser to populate this could get tricky. Instead, just parse to |
||||
* temporary JSONArray and then steal the data from that. |
||||
*/ |
||||
Object object = readFrom.nextValue(); |
||||
if (object instanceof JSONArray) { |
||||
this.values = ((JSONArray) object).values; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(object, "JSONArray"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONArray} with values from the JSON string. |
||||
* @param json a JSON-encoded string containing an array. |
||||
* @throws JSONException if the parse fails or doesn't yield a {@code |
||||
* JSONArray}. |
||||
*/ |
||||
public JSONArray(String json) throws JSONException { |
||||
this(new JSONTokener(json)); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONArray} with values from the given primitive array. |
||||
* @param array a primitive array |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray(Object array) throws JSONException { |
||||
if (!array.getClass().isArray()) { |
||||
throw new JSONException("Not a primitive array: " + array.getClass()); |
||||
} |
||||
final int length = Array.getLength(array); |
||||
this.values = new ArrayList<>(length); |
||||
for (int i = 0; i < length; ++i) { |
||||
put(JSONObject.wrap(Array.get(array, i))); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the number of values in this array. |
||||
* @return the length of this array |
||||
*/ |
||||
public int length() { |
||||
return this.values.size(); |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value the value |
||||
* @return this array. |
||||
*/ |
||||
public JSONArray put(boolean value) { |
||||
this.values.add(value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(double value) throws JSONException { |
||||
this.values.add(JSON.checkDouble(value)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value the value |
||||
* @return this array. |
||||
*/ |
||||
public JSONArray put(int value) { |
||||
this.values.add(value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value the value |
||||
* @return this array. |
||||
*/ |
||||
public JSONArray put(long value) { |
||||
this.values.add(value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, |
||||
* Long, Double, {@link JSONObject#NULL}, or {@code null}. May not be |
||||
* {@link Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. Unsupported |
||||
* values are not permitted and will cause the array to be in an inconsistent state. |
||||
* @return this array. |
||||
*/ |
||||
public JSONArray put(Object value) { |
||||
this.values.add(value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value the value |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, boolean value) throws JSONException { |
||||
return put(index, (Boolean) value); |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, double value) throws JSONException { |
||||
return put(index, (Double) value); |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value the value |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, int value) throws JSONException { |
||||
return put(index, (Integer) value); |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value the value |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, long value) throws JSONException { |
||||
return put(index, (Long) value); |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, |
||||
* Long, Double, {@link JSONObject#NULL}, or {@code null}. May not be |
||||
* {@link Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, Object value) throws JSONException { |
||||
if (value instanceof Number) { |
||||
// deviate from the original by checking all Numbers, not just floats &
|
||||
// doubles
|
||||
JSON.checkDouble(((Number) value).doubleValue()); |
||||
} |
||||
while (this.values.size() <= index) { |
||||
this.values.add(null); |
||||
} |
||||
this.values.set(index, value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Returns true if this array has no value at {@code index}, or if its value is the |
||||
* {@code null} reference or {@link JSONObject#NULL}. |
||||
* @param index the index to set the value to |
||||
* @return true if this array has no value at {@code index} |
||||
*/ |
||||
public boolean isNull(int index) { |
||||
Object value = opt(index); |
||||
return value == null || value == JSONObject.NULL; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index}. |
||||
* @param index the index to get the value from |
||||
* @return the value at {@code index}. |
||||
* @throws JSONException if this array has no value at {@code index}, or if that value |
||||
* is the {@code null} reference. This method returns normally if the value is |
||||
* {@code JSONObject#NULL}. |
||||
*/ |
||||
public Object get(int index) throws JSONException { |
||||
try { |
||||
Object value = this.values.get(index); |
||||
if (value == null) { |
||||
throw new JSONException("Value at " + index + " is null."); |
||||
} |
||||
return value; |
||||
} |
||||
catch (IndexOutOfBoundsException e) { |
||||
throw new JSONException("Index " + index + " out of range [0.." + this.values.size() + ")"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index}, or null if the array has no value at |
||||
* {@code index}. |
||||
* @param index the index to get the value from |
||||
* @return the value at {@code index} or {@code null} |
||||
*/ |
||||
public Object opt(int index) { |
||||
if (index < 0 || index >= this.values.size()) { |
||||
return null; |
||||
} |
||||
return this.values.get(index); |
||||
} |
||||
|
||||
/** |
||||
* Removes and returns the value at {@code index}, or null if the array has no value |
||||
* at {@code index}. |
||||
* @param index the index of the value to remove |
||||
* @return the previous value at {@code index} |
||||
*/ |
||||
public Object remove(int index) { |
||||
if (index < 0 || index >= this.values.size()) { |
||||
return null; |
||||
} |
||||
return this.values.remove(index); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a boolean or can be coerced |
||||
* to a boolean. |
||||
* @param index the index to get the value from |
||||
* @return the value at {@code index} |
||||
* @throws JSONException if the value at {@code index} doesn't exist or cannot be |
||||
* coerced to a boolean. |
||||
*/ |
||||
public boolean getBoolean(int index) throws JSONException { |
||||
Object object = get(index); |
||||
Boolean result = JSON.toBoolean(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "boolean"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a boolean or can be coerced |
||||
* to a boolean. Returns false otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or {@code false} |
||||
*/ |
||||
public boolean optBoolean(int index) { |
||||
return optBoolean(index, false); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a boolean or can be coerced |
||||
* to a boolean. Returns {@code fallback} otherwise. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public boolean optBoolean(int index, boolean fallback) { |
||||
Object object = opt(index); |
||||
Boolean result = JSON.toBoolean(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a double or can be coerced |
||||
* to a double. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} |
||||
* @throws JSONException if the value at {@code index} doesn't exist or cannot be |
||||
* coerced to a double. |
||||
*/ |
||||
public double getDouble(int index) throws JSONException { |
||||
Object object = get(index); |
||||
Double result = JSON.toDouble(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "double"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a double or can be coerced |
||||
* to a double. Returns {@code NaN} otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or {@code NaN} |
||||
*/ |
||||
public double optDouble(int index) { |
||||
return optDouble(index, Double.NaN); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a double or can be coerced |
||||
* to a double. Returns {@code fallback} otherwise. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public double optDouble(int index, double fallback) { |
||||
Object object = opt(index); |
||||
Double result = JSON.toDouble(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is an int or can be coerced to |
||||
* an int. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} |
||||
* @throws JSONException if the value at {@code index} doesn't exist or cannot be |
||||
* coerced to an int. |
||||
*/ |
||||
public int getInt(int index) throws JSONException { |
||||
Object object = get(index); |
||||
Integer result = JSON.toInteger(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "int"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is an int or can be coerced to |
||||
* an int. Returns 0 otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or {@code 0} |
||||
*/ |
||||
public int optInt(int index) { |
||||
return optInt(index, 0); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is an int or can be coerced to |
||||
* an int. Returns {@code fallback} otherwise. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public int optInt(int index, int fallback) { |
||||
Object object = opt(index); |
||||
Integer result = JSON.toInteger(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a long or can be coerced to |
||||
* a long. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} |
||||
* @throws JSONException if the value at {@code index} doesn't exist or cannot be |
||||
* coerced to a long. |
||||
*/ |
||||
public long getLong(int index) throws JSONException { |
||||
Object object = get(index); |
||||
Long result = JSON.toLong(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "long"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a long or can be coerced to |
||||
* a long. Returns 0 otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or {@code 0} |
||||
*/ |
||||
public long optLong(int index) { |
||||
return optLong(index, 0L); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a long or can be coerced to |
||||
* a long. Returns {@code fallback} otherwise. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public long optLong(int index, long fallback) { |
||||
Object object = opt(index); |
||||
Long result = JSON.toLong(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists, coercing it if necessary. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} |
||||
* @throws JSONException if no such value exists. |
||||
*/ |
||||
public String getString(int index) throws JSONException { |
||||
Object object = get(index); |
||||
String result = JSON.toString(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "String"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists, coercing it if necessary. Returns |
||||
* the empty string if no such value exists. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or an empty string |
||||
*/ |
||||
public String optString(int index) { |
||||
return optString(index, ""); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists, coercing it if necessary. Returns |
||||
* {@code fallback} if no such value exists. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public String optString(int index, String fallback) { |
||||
Object object = opt(index); |
||||
String result = JSON.toString(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a {@code |
||||
* JSONArray}. |
||||
* @param index the index to get the value from |
||||
* @return the array at {@code index} |
||||
* @throws JSONException if the value doesn't exist or is not a {@code |
||||
* JSONArray}. |
||||
*/ |
||||
public JSONArray getJSONArray(int index) throws JSONException { |
||||
Object object = get(index); |
||||
if (object instanceof JSONArray) { |
||||
return (JSONArray) object; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(index, object, "JSONArray"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a {@code |
||||
* JSONArray}. Returns null otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the array at {@code index} or {@code null} |
||||
*/ |
||||
public JSONArray optJSONArray(int index) { |
||||
Object object = opt(index); |
||||
return object instanceof JSONArray ? (JSONArray) object : null; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a {@code |
||||
* JSONObject}. |
||||
* @param index the index to get the value from |
||||
* @return the object at {@code index} |
||||
* @throws JSONException if the value doesn't exist or is not a {@code |
||||
* JSONObject}. |
||||
*/ |
||||
public JSONObject getJSONObject(int index) throws JSONException { |
||||
Object object = get(index); |
||||
if (object instanceof JSONObject) { |
||||
return (JSONObject) object; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(index, object, "JSONObject"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a {@code |
||||
* JSONObject}. Returns null otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the object at {@code index} or {@code null} |
||||
*/ |
||||
public JSONObject optJSONObject(int index) { |
||||
Object object = opt(index); |
||||
return object instanceof JSONObject ? (JSONObject) object : null; |
||||
} |
||||
|
||||
/** |
||||
* Returns a new object whose values are the values in this array, and whose names are |
||||
* the values in {@code names}. Names and values are paired up by index from 0 through |
||||
* to the shorter array's length. Names that are not strings will be coerced to |
||||
* strings. This method returns null if either array is empty. |
||||
* @param names the property names |
||||
* @return a json object |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONObject toJSONObject(JSONArray names) throws JSONException { |
||||
JSONObject result = new JSONObject(); |
||||
int length = Math.min(names.length(), this.values.size()); |
||||
if (length == 0) { |
||||
return null; |
||||
} |
||||
for (int i = 0; i < length; i++) { |
||||
String name = JSON.toString(names.opt(i)); |
||||
result.put(name, opt(i)); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns a new string by alternating this array's values with {@code |
||||
* separator}. This array's string values are quoted and have their special characters |
||||
* escaped. For example, the array containing the strings '12" pizza', 'taco' and |
||||
* 'soda' joined on '+' returns this: <pre>"12\" pizza"+"taco"+"soda"</pre> |
||||
* @param separator the separator to use |
||||
* @return the joined value |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public String join(String separator) throws JSONException { |
||||
JSONStringer stringer = new JSONStringer(); |
||||
stringer.open(JSONStringer.Scope.NULL, ""); |
||||
for (int i = 0, size = this.values.size(); i < size; i++) { |
||||
if (i > 0) { |
||||
stringer.out.append(separator); |
||||
} |
||||
stringer.value(this.values.get(i)); |
||||
} |
||||
stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); |
||||
return stringer.out.toString(); |
||||
} |
||||
|
||||
/** |
||||
* Encodes this array as a compact JSON string, such as: <pre>[94043,90210]</pre> |
||||
* @return a compact JSON string representation of this array |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
try { |
||||
JSONStringer stringer = new JSONStringer(); |
||||
writeTo(stringer); |
||||
return stringer.toString(); |
||||
} |
||||
catch (JSONException e) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Encodes this array as a human-readable JSON string for debugging, such as: <pre> |
||||
* [ |
||||
* 94043, |
||||
* 90210 |
||||
* ]</pre> |
||||
* @param indentSpaces the number of spaces to indent for each level of nesting. |
||||
* @return a human-readable JSON string of this array |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public String toString(int indentSpaces) throws JSONException { |
||||
JSONStringer stringer = new JSONStringer(indentSpaces); |
||||
writeTo(stringer); |
||||
return stringer.toString(); |
||||
} |
||||
|
||||
void writeTo(JSONStringer stringer) throws JSONException { |
||||
stringer.array(); |
||||
for (Object value : this.values) { |
||||
stringer.value(value); |
||||
} |
||||
stringer.endArray(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
return o instanceof JSONArray && ((JSONArray) o).values.equals(this.values); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
// diverge from the original, which doesn't implement hashCode
|
||||
return this.values.hashCode(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.cli.json; |
||||
|
||||
// Note: this class was written without inspecting the non-free org.json source code.
|
||||
|
||||
/** |
||||
* Thrown to indicate a problem with the JSON API. Such problems include: |
||||
* <ul> |
||||
* <li>Attempts to parse or construct malformed documents |
||||
* <li>Use of null as a name |
||||
* <li>Use of numeric types not available to JSON, such as {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* <li>Lookups using an out of range index or nonexistent name |
||||
* <li>Type mismatches on lookups |
||||
* </ul> |
||||
* <p> |
||||
* Although this is a checked exception, it is rarely recoverable. Most callers should |
||||
* simply wrap this exception in an unchecked exception and rethrow: <pre class="code"> |
||||
* public JSONArray toJSONObject() { |
||||
* try { |
||||
* JSONObject result = new JSONObject(); |
||||
* ... |
||||
* } catch (JSONException e) { |
||||
* throw new RuntimeException(e); |
||||
* } |
||||
* }</pre> |
||||
*/ |
||||
public class JSONException extends Exception { |
||||
|
||||
public JSONException(String s) { |
||||
super(s); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,836 @@
@@ -0,0 +1,836 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.cli.json; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.Iterator; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
// Note: this class was written without inspecting the non-free org.json source code.
|
||||
|
||||
/** |
||||
* A modifiable set of name/value mappings. Names are unique, non-null strings. Values may |
||||
* be any mix of {@link JSONObject JSONObjects}, {@link JSONArray JSONArrays}, Strings, |
||||
* Booleans, Integers, Longs, Doubles or {@link #NULL}. Values may not be {@code null}, |
||||
* {@link Double#isNaN() NaNs}, {@link Double#isInfinite() infinities}, or of any type not |
||||
* listed here. |
||||
* <p> |
||||
* This class can coerce values to another type when requested. |
||||
* <ul> |
||||
* <li>When the requested type is a boolean, strings will be coerced using a |
||||
* case-insensitive comparison to "true" and "false". |
||||
* <li>When the requested type is a double, other {@link Number} types will be coerced |
||||
* using {@link Number#doubleValue() doubleValue}. Strings that can be coerced using |
||||
* {@link Double#valueOf(String)} will be. |
||||
* <li>When the requested type is an int, other {@link Number} types will be coerced using |
||||
* {@link Number#intValue() intValue}. Strings that can be coerced using |
||||
* {@link Double#valueOf(String)} will be, and then cast to int. |
||||
* <li><a id="lossy">When the requested type is a long, other {@link Number} types will be |
||||
* coerced using {@link Number#longValue() longValue}. Strings that can be coerced using |
||||
* {@link Double#valueOf(String)} will be, and then cast to long. This two-step conversion |
||||
* is lossy for very large values. For example, the string "9223372036854775806" yields |
||||
* the long 9223372036854775807.</a> |
||||
* <li>When the requested type is a String, other non-null values will be coerced using |
||||
* {@link String#valueOf(Object)}. Although null cannot be coerced, the sentinel value |
||||
* {@link JSONObject#NULL} is coerced to the string "null". |
||||
* </ul> |
||||
* <p> |
||||
* This class can look up both mandatory and optional values: |
||||
* <ul> |
||||
* <li>Use <code>get<i>Type</i>()</code> to retrieve a mandatory value. This fails with a |
||||
* {@code JSONException} if the requested name has no value or if the value cannot be |
||||
* coerced to the requested type. |
||||
* <li>Use <code>opt<i>Type</i>()</code> to retrieve an optional value. This returns a |
||||
* system- or user-supplied default if the requested name has no value or if the value |
||||
* cannot be coerced to the requested type. |
||||
* </ul> |
||||
* <p> |
||||
* <strong>Warning:</strong> this class represents null in two incompatible ways: the |
||||
* standard Java {@code null} reference, and the sentinel value {@link JSONObject#NULL}. |
||||
* In particular, calling {@code put(name, null)} removes the named entry from the object |
||||
* but {@code put(name, JSONObject.NULL)} stores an entry whose value is |
||||
* {@code JSONObject.NULL}. |
||||
* <p> |
||||
* 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 JSONObject { |
||||
|
||||
private static final Double NEGATIVE_ZERO = -0d; |
||||
|
||||
/** |
||||
* A sentinel value used to explicitly define a name with no value. Unlike |
||||
* {@code null}, names with this value: |
||||
* <ul> |
||||
* <li>show up in the {@link #names} array |
||||
* <li>show up in the {@link #keys} iterator |
||||
* <li>return {@code true} for {@link #has(String)} |
||||
* <li>do not throw on {@link #get(String)} |
||||
* <li>are included in the encoded JSON string. |
||||
* </ul> |
||||
* <p> |
||||
* This value violates the general contract of {@link Object#equals} by returning true |
||||
* when compared to {@code null}. Its {@link #toString} method returns "null". |
||||
*/ |
||||
public static final Object NULL = new Object() { |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
return o == this || o == null; // API specifies this broken equals
|
||||
// implementation
|
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "null"; |
||||
} |
||||
|
||||
}; |
||||
|
||||
private final Map<String, Object> nameValuePairs; |
||||
|
||||
/** |
||||
* Creates a {@code JSONObject} with no name/value mappings. |
||||
*/ |
||||
public JSONObject() { |
||||
this.nameValuePairs = new LinkedHashMap<>(); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONObject} by copying all name/value mappings from the given |
||||
* map. |
||||
* @param copyFrom a map whose keys are of type {@link String} and whose values are of |
||||
* supported types. |
||||
* @throws NullPointerException if any of the map's keys are null. |
||||
*/ |
||||
/* (accept a raw type for API compatibility) */ |
||||
@SuppressWarnings("rawtypes") |
||||
public JSONObject(Map copyFrom) { |
||||
this(); |
||||
Map<?, ?> contentsTyped = copyFrom; |
||||
for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) { |
||||
/* |
||||
* Deviate from the original by checking that keys are non-null and of the |
||||
* proper type. (We still defer validating the values). |
||||
*/ |
||||
String key = (String) entry.getKey(); |
||||
if (key == null) { |
||||
throw new NullPointerException("key == null"); |
||||
} |
||||
this.nameValuePairs.put(key, wrap(entry.getValue())); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONObject} with name/value mappings from the next object in |
||||
* the tokener. |
||||
* @param readFrom a tokener whose nextValue() method will yield a {@code JSONObject}. |
||||
* @throws JSONException if the parse fails or doesn't yield a {@code JSONObject}. |
||||
*/ |
||||
public JSONObject(JSONTokener readFrom) throws JSONException { |
||||
/* |
||||
* Getting the parser to populate this could get tricky. Instead, just parse to |
||||
* temporary JSONObject and then steal the data from that. |
||||
*/ |
||||
Object object = readFrom.nextValue(); |
||||
if (object instanceof JSONObject) { |
||||
this.nameValuePairs = ((JSONObject) object).nameValuePairs; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(object, "JSONObject"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONObject} with name/value mappings from the JSON string. |
||||
* @param json a JSON-encoded string containing an object. |
||||
* @throws JSONException if the parse fails or doesn't yield a {@code |
||||
* JSONObject}. |
||||
*/ |
||||
public JSONObject(String json) throws JSONException { |
||||
this(new JSONTokener(json)); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONObject} by copying mappings for the listed names from the |
||||
* given object. Names that aren't present in {@code copyFrom} will be skipped. |
||||
* @param copyFrom the source |
||||
* @param names the property names |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject(JSONObject copyFrom, String[] names) throws JSONException { |
||||
this(); |
||||
for (String name : names) { |
||||
Object value = copyFrom.opt(name); |
||||
if (value != null) { |
||||
this.nameValuePairs.put(name, value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the number of name/value mappings in this object. |
||||
* @return the number of name/value mappings in this object |
||||
*/ |
||||
public int length() { |
||||
return this.nameValuePairs.size(); |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. |
||||
* @param name the name of the property |
||||
* @param value the value of the property |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, boolean value) throws JSONException { |
||||
this.nameValuePairs.put(checkName(name), value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. |
||||
* @param name the name of the property |
||||
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, double value) throws JSONException { |
||||
this.nameValuePairs.put(checkName(name), JSON.checkDouble(value)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. |
||||
* @param name the name of the property |
||||
* @param value the value of the property |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, int value) throws JSONException { |
||||
this.nameValuePairs.put(checkName(name), value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. |
||||
* @param name the name of the property |
||||
* @param value the value of the property |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, long value) throws JSONException { |
||||
this.nameValuePairs.put(checkName(name), value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. If the value is {@code null}, any existing mapping for {@code name} |
||||
* is removed. |
||||
* @param name the name of the property |
||||
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, |
||||
* Long, Double, {@link #NULL}, or {@code null}. May not be {@link Double#isNaN() |
||||
* NaNs} or {@link Double#isInfinite() infinities}. |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, Object value) throws JSONException { |
||||
if (value == null) { |
||||
this.nameValuePairs.remove(name); |
||||
return this; |
||||
} |
||||
if (value instanceof Number) { |
||||
// deviate from the original by checking all Numbers, not just floats &
|
||||
// doubles
|
||||
JSON.checkDouble(((Number) value).doubleValue()); |
||||
} |
||||
this.nameValuePairs.put(checkName(name), value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Equivalent to {@code put(name, value)} when both parameters are non-null; does |
||||
* nothing otherwise. |
||||
* @param name the name of the property |
||||
* @param value the value of the property |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject putOpt(String name, Object value) throws JSONException { |
||||
if (name == null || value == null) { |
||||
return this; |
||||
} |
||||
return put(name, value); |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the array already mapped to {@code name}. If this object |
||||
* has no mapping for {@code name}, this inserts a new mapping. If the mapping exists |
||||
* but its value is not an array, the existing and new values are inserted in order |
||||
* into a new array which is itself mapped to {@code name}. In aggregate, this allows |
||||
* values to be added to a mapping one at a time. |
||||
* @param name the name of the property |
||||
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, |
||||
* Long, Double, {@link #NULL} or null. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject accumulate(String name, Object value) throws JSONException { |
||||
Object current = this.nameValuePairs.get(checkName(name)); |
||||
if (current == null) { |
||||
return put(name, value); |
||||
} |
||||
|
||||
// check in accumulate, since array.put(Object) doesn't do any checking
|
||||
if (value instanceof Number) { |
||||
JSON.checkDouble(((Number) value).doubleValue()); |
||||
} |
||||
|
||||
if (current instanceof JSONArray array) { |
||||
array.put(value); |
||||
} |
||||
else { |
||||
JSONArray array = new JSONArray(); |
||||
array.put(current); |
||||
array.put(value); |
||||
this.nameValuePairs.put(name, array); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
String checkName(String name) throws JSONException { |
||||
if (name == null) { |
||||
throw new JSONException("Names must be non-null"); |
||||
} |
||||
return name; |
||||
} |
||||
|
||||
/** |
||||
* Removes the named mapping if it exists; does nothing otherwise. |
||||
* @param name the name of the property |
||||
* @return the value previously mapped by {@code name}, or null if there was no such |
||||
* mapping. |
||||
*/ |
||||
public Object remove(String name) { |
||||
return this.nameValuePairs.remove(name); |
||||
} |
||||
|
||||
/** |
||||
* Returns true if this object has no mapping for {@code name} or if it has a mapping |
||||
* whose value is {@link #NULL}. |
||||
* @param name the name of the property |
||||
* @return true if this object has no mapping for {@code name} |
||||
*/ |
||||
public boolean isNull(String name) { |
||||
Object value = this.nameValuePairs.get(name); |
||||
return value == null || value == NULL; |
||||
} |
||||
|
||||
/** |
||||
* Returns true if this object has a mapping for {@code name}. The mapping may be |
||||
* {@link #NULL}. |
||||
* @param name the name of the property |
||||
* @return true if this object has a mapping for {@code name} |
||||
*/ |
||||
public boolean has(String name) { |
||||
return this.nameValuePairs.containsKey(name); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name}. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if no such mapping exists. |
||||
*/ |
||||
public Object get(String name) throws JSONException { |
||||
Object result = this.nameValuePairs.get(name); |
||||
if (result == null) { |
||||
throw new JSONException("No value for " + name); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name}, or null if no such mapping exists. |
||||
* @param name the name of the property |
||||
* @return the value or {@code null} |
||||
*/ |
||||
public Object opt(String name) { |
||||
return this.nameValuePairs.get(name); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a boolean or can be |
||||
* coerced to a boolean. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or cannot be coerced to a |
||||
* boolean. |
||||
*/ |
||||
public boolean getBoolean(String name) throws JSONException { |
||||
Object object = get(name); |
||||
Boolean result = JSON.toBoolean(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "boolean"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a boolean or can be |
||||
* coerced to a boolean. Returns false otherwise. |
||||
* @param name the name of the property |
||||
* @return the value or {@code null} |
||||
*/ |
||||
public boolean optBoolean(String name) { |
||||
return optBoolean(name, false); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a boolean or can be |
||||
* coerced to a boolean. Returns {@code fallback} otherwise. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public boolean optBoolean(String name, boolean fallback) { |
||||
Object object = opt(name); |
||||
Boolean result = JSON.toBoolean(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a double or can be |
||||
* coerced to a double. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or cannot be coerced to a |
||||
* double. |
||||
*/ |
||||
public double getDouble(String name) throws JSONException { |
||||
Object object = get(name); |
||||
Double result = JSON.toDouble(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "double"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a double or can be |
||||
* coerced to a double. Returns {@code NaN} otherwise. |
||||
* @param name the name of the property |
||||
* @return the value or {@code NaN} |
||||
*/ |
||||
public double optDouble(String name) { |
||||
return optDouble(name, Double.NaN); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a double or can be |
||||
* coerced to a double. Returns {@code fallback} otherwise. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public double optDouble(String name, double fallback) { |
||||
Object object = opt(name); |
||||
Double result = JSON.toDouble(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is an int or can be |
||||
* coerced to an int. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or cannot be coerced to an int. |
||||
*/ |
||||
public int getInt(String name) throws JSONException { |
||||
Object object = get(name); |
||||
Integer result = JSON.toInteger(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "int"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is an int or can be |
||||
* coerced to an int. Returns 0 otherwise. |
||||
* @param name the name of the property |
||||
* @return the value of {@code 0} |
||||
*/ |
||||
public int optInt(String name) { |
||||
return optInt(name, 0); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is an int or can be |
||||
* coerced to an int. Returns {@code fallback} otherwise. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public int optInt(String name, int fallback) { |
||||
Object object = opt(name); |
||||
Integer result = JSON.toInteger(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a long or can be |
||||
* coerced to a long. Note that JSON represents numbers as doubles, so this is |
||||
* <a href="#lossy">lossy</a>; use strings to transfer numbers over JSON. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or cannot be coerced to a long. |
||||
*/ |
||||
public long getLong(String name) throws JSONException { |
||||
Object object = get(name); |
||||
Long result = JSON.toLong(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "long"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a long or can be |
||||
* coerced to a long. Returns 0 otherwise. Note that JSON represents numbers as |
||||
* doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via |
||||
* JSON. |
||||
* @param name the name of the property |
||||
* @return the value or {@code 0L} |
||||
*/ |
||||
public long optLong(String name) { |
||||
return optLong(name, 0L); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a long or can be |
||||
* coerced to a long. Returns {@code fallback} otherwise. Note that JSON represents |
||||
* numbers as doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer |
||||
* numbers over JSON. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public long optLong(String name, long fallback) { |
||||
Object object = opt(name); |
||||
Long result = JSON.toLong(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists, coercing it if necessary. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if no such mapping exists. |
||||
*/ |
||||
public String getString(String name) throws JSONException { |
||||
Object object = get(name); |
||||
String result = JSON.toString(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "String"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists, coercing it if necessary. |
||||
* Returns the empty string if no such mapping exists. |
||||
* @param name the name of the property |
||||
* @return the value or an empty string |
||||
*/ |
||||
public String optString(String name) { |
||||
return optString(name, ""); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists, coercing it if necessary. |
||||
* Returns {@code fallback} if no such mapping exists. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public String optString(String name, String fallback) { |
||||
Object object = opt(name); |
||||
String result = JSON.toString(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a {@code |
||||
* JSONArray}. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or is not a {@code |
||||
* JSONArray}. |
||||
*/ |
||||
public JSONArray getJSONArray(String name) throws JSONException { |
||||
Object object = get(name); |
||||
if (object instanceof JSONArray) { |
||||
return (JSONArray) object; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(name, object, "JSONArray"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a {@code |
||||
* JSONArray}. Returns null otherwise. |
||||
* @param name the name of the property |
||||
* @return the value or {@code null} |
||||
*/ |
||||
public JSONArray optJSONArray(String name) { |
||||
Object object = opt(name); |
||||
return object instanceof JSONArray ? (JSONArray) object : null; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a {@code |
||||
* JSONObject}. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or is not a {@code |
||||
* JSONObject}. |
||||
*/ |
||||
public JSONObject getJSONObject(String name) throws JSONException { |
||||
Object object = get(name); |
||||
if (object instanceof JSONObject) { |
||||
return (JSONObject) object; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(name, object, "JSONObject"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a {@code |
||||
* JSONObject}. Returns null otherwise. |
||||
* @param name the name of the property |
||||
* @return the value or {@code null} |
||||
*/ |
||||
public JSONObject optJSONObject(String name) { |
||||
Object object = opt(name); |
||||
return object instanceof JSONObject ? (JSONObject) object : null; |
||||
} |
||||
|
||||
/** |
||||
* Returns an array with the values corresponding to {@code names}. The array contains |
||||
* null for names that aren't mapped. This method returns null if {@code names} is |
||||
* either null or empty. |
||||
* @param names the names of the properties |
||||
* @return the array |
||||
*/ |
||||
public JSONArray toJSONArray(JSONArray names) { |
||||
JSONArray result = new JSONArray(); |
||||
if (names == null) { |
||||
return null; |
||||
} |
||||
int length = names.length(); |
||||
if (length == 0) { |
||||
return null; |
||||
} |
||||
for (int i = 0; i < length; i++) { |
||||
String name = JSON.toString(names.opt(i)); |
||||
result.put(opt(name)); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns an iterator of the {@code String} names in this object. The returned |
||||
* iterator supports {@link Iterator#remove() remove}, which will remove the |
||||
* corresponding mapping from this object. If this object is modified after the |
||||
* iterator is returned, the iterator's behavior is undefined. The order of the keys |
||||
* is undefined. |
||||
* @return the keys |
||||
*/ |
||||
/* Return a raw type for API compatibility */ |
||||
@SuppressWarnings("rawtypes") |
||||
public Iterator keys() { |
||||
return this.nameValuePairs.keySet().iterator(); |
||||
} |
||||
|
||||
/** |
||||
* Returns an array containing the string names in this object. This method returns |
||||
* null if this object contains no mappings. |
||||
* @return the array |
||||
*/ |
||||
public JSONArray names() { |
||||
return this.nameValuePairs.isEmpty() ? null : new JSONArray(new ArrayList<>(this.nameValuePairs.keySet())); |
||||
} |
||||
|
||||
/** |
||||
* Encodes this object as a compact JSON string, such as: |
||||
* <pre>{"query":"Pizza","locations":[94043,90210]}</pre> |
||||
* @return a string representation of the object. |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
try { |
||||
JSONStringer stringer = new JSONStringer(); |
||||
writeTo(stringer); |
||||
return stringer.toString(); |
||||
} |
||||
catch (JSONException e) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Encodes this object as a human-readable JSON string for debugging, such as: <pre> |
||||
* { |
||||
* "query": "Pizza", |
||||
* "locations": [ |
||||
* 94043, |
||||
* 90210 |
||||
* ] |
||||
* }</pre> |
||||
* @param indentSpaces the number of spaces to indent for each level of nesting. |
||||
* @return a string representation of the object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public String toString(int indentSpaces) throws JSONException { |
||||
JSONStringer stringer = new JSONStringer(indentSpaces); |
||||
writeTo(stringer); |
||||
return stringer.toString(); |
||||
} |
||||
|
||||
void writeTo(JSONStringer stringer) throws JSONException { |
||||
stringer.object(); |
||||
for (Map.Entry<String, Object> entry : this.nameValuePairs.entrySet()) { |
||||
stringer.key(entry.getKey()).value(entry.getValue()); |
||||
} |
||||
stringer.endObject(); |
||||
} |
||||
|
||||
/** |
||||
* Encodes the number as a JSON string. |
||||
* @param number a finite value. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return the encoded value |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public static String numberToString(Number number) throws JSONException { |
||||
if (number == null) { |
||||
throw new JSONException("Number must be non-null"); |
||||
} |
||||
|
||||
double doubleValue = number.doubleValue(); |
||||
JSON.checkDouble(doubleValue); |
||||
|
||||
// the original returns "-0" instead of "-0.0" for negative zero
|
||||
if (number.equals(NEGATIVE_ZERO)) { |
||||
return "-0"; |
||||
} |
||||
|
||||
long longValue = number.longValue(); |
||||
if (doubleValue == longValue) { |
||||
return Long.toString(longValue); |
||||
} |
||||
|
||||
return number.toString(); |
||||
} |
||||
|
||||
/** |
||||
* Encodes {@code data} as a JSON string. This applies quotes and any necessary |
||||
* character escaping. |
||||
* @param data the string to encode. Null will be interpreted as an empty string. |
||||
* @return the quoted value |
||||
*/ |
||||
public static String quote(String data) { |
||||
if (data == null) { |
||||
return "\"\""; |
||||
} |
||||
try { |
||||
JSONStringer stringer = new JSONStringer(); |
||||
stringer.open(JSONStringer.Scope.NULL, ""); |
||||
stringer.value(data); |
||||
stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); |
||||
return stringer.toString(); |
||||
} |
||||
catch (JSONException e) { |
||||
throw new AssertionError(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Wraps the given object if necessary. |
||||
* <p> |
||||
* If the object is null or, returns {@link #NULL}. If the object is a |
||||
* {@code JSONArray} or {@code JSONObject}, no wrapping is necessary. If the object is |
||||
* {@code NULL}, no wrapping is necessary. If the object is an array or |
||||
* {@code Collection}, returns an equivalent {@code JSONArray}. If the object is a |
||||
* {@code Map}, returns an equivalent {@code JSONObject}. If the object is a primitive |
||||
* wrapper type or {@code String}, returns the object. Otherwise if the object is from |
||||
* a {@code java} package, returns the result of {@code toString}. If wrapping fails, |
||||
* returns null. |
||||
* @param o the object to wrap |
||||
* @return the wrapped object |
||||
*/ |
||||
@SuppressWarnings("rawtypes") |
||||
public static Object wrap(Object o) { |
||||
if (o == null) { |
||||
return NULL; |
||||
} |
||||
if (o instanceof JSONArray || o instanceof JSONObject) { |
||||
return o; |
||||
} |
||||
if (o.equals(NULL)) { |
||||
return o; |
||||
} |
||||
try { |
||||
if (o instanceof Collection) { |
||||
return new JSONArray((Collection) o); |
||||
} |
||||
else if (o.getClass().isArray()) { |
||||
return new JSONArray(o); |
||||
} |
||||
if (o instanceof Map) { |
||||
return new JSONObject((Map) o); |
||||
} |
||||
if (o instanceof Boolean || o instanceof Byte || o instanceof Character || o instanceof Double |
||||
|| o instanceof Float || o instanceof Integer || o instanceof Long || o instanceof Short |
||||
|| o instanceof String) { |
||||
return o; |
||||
} |
||||
if (o.getClass().getPackage().getName().startsWith("java.")) { |
||||
return o.toString(); |
||||
} |
||||
} |
||||
catch (Exception ex) { |
||||
// Ignore
|
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,429 @@
@@ -0,0 +1,429 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.cli.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 source code.
|
||||
|
||||
/** |
||||
* 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(); |
||||
|
||||
/** |
||||
* Lexical scoping elements within this stringer, necessary to insert the appropriate |
||||
* separator characters (i.e. 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, |
||||
|
||||
/** |
||||
* An 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 |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Unlike the original implementation, this stack isn't limited to 20 levels of |
||||
* nesting. |
||||
*/ |
||||
private final List<Scope> stack = new ArrayList<>(); |
||||
|
||||
/** |
||||
* 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() { |
||||
this.indent = null; |
||||
} |
||||
|
||||
JSONStringer(int indentSpaces) { |
||||
char[] indentChars = new char[indentSpaces]; |
||||
Arrays.fill(indentChars, ' '); |
||||
this.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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer array() throws JSONException { |
||||
return open(Scope.EMPTY_ARRAY, "["); |
||||
} |
||||
|
||||
/** |
||||
* Ends encoding the current array. |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer object() throws JSONException { |
||||
return open(Scope.EMPTY_OBJECT, "{"); |
||||
} |
||||
|
||||
/** |
||||
* Ends encoding the current object. |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
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. |
||||
* @param empty any necessary whitespace |
||||
* @param openBracket the open bracket |
||||
* @return this object |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
JSONStringer open(Scope empty, String openBracket) throws JSONException { |
||||
if (this.stack.isEmpty() && !this.out.isEmpty()) { |
||||
throw new JSONException("Nesting problem: multiple top-level roots"); |
||||
} |
||||
beforeValue(); |
||||
this.stack.add(empty); |
||||
this.out.append(openBracket); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Closes the current scope by appending any necessary whitespace and the given |
||||
* bracket. |
||||
* @param empty any necessary whitespace |
||||
* @param nonempty the current scope |
||||
* @param closeBracket the close bracket |
||||
* @return the JSON stringer |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException { |
||||
Scope context = peek(); |
||||
if (context != nonempty && context != empty) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
|
||||
this.stack.remove(this.stack.size() - 1); |
||||
if (context == nonempty) { |
||||
newline(); |
||||
} |
||||
this.out.append(closeBracket); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value on the top of the stack. |
||||
* @return the scope |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private Scope peek() throws JSONException { |
||||
if (this.stack.isEmpty()) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
return this.stack.get(this.stack.size() - 1); |
||||
} |
||||
|
||||
/** |
||||
* Replace the value on the top of the stack with the given value. |
||||
* @param topOfStack the scope at the top of the stack |
||||
*/ |
||||
private void replaceTop(Scope topOfStack) { |
||||
this.stack.set(this.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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer value(Object value) throws JSONException { |
||||
if (this.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) { |
||||
this.out.append(value); |
||||
|
||||
} |
||||
else if (value instanceof Number) { |
||||
this.out.append(JSONObject.numberToString((Number) value)); |
||||
|
||||
} |
||||
else { |
||||
string(value.toString()); |
||||
} |
||||
|
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Encodes {@code value} to this stringer. |
||||
* @param value the value to encode |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer value(boolean value) throws JSONException { |
||||
if (this.stack.isEmpty()) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
beforeValue(); |
||||
this.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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer value(double value) throws JSONException { |
||||
if (this.stack.isEmpty()) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
beforeValue(); |
||||
this.out.append(JSONObject.numberToString(value)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Encodes {@code value} to this stringer. |
||||
* @param value the value to encode |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer value(long value) throws JSONException { |
||||
if (this.stack.isEmpty()) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
beforeValue(); |
||||
this.out.append(value); |
||||
return this; |
||||
} |
||||
|
||||
private void string(String value) { |
||||
this.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 '"', '\\', '/' -> this.out.append('\\').append(c); |
||||
case '\t' -> this.out.append("\\t"); |
||||
case '\b' -> this.out.append("\\b"); |
||||
case '\n' -> this.out.append("\\n"); |
||||
case '\r' -> this.out.append("\\r"); |
||||
case '\f' -> this.out.append("\\f"); |
||||
default -> { |
||||
if (c <= 0x1F) { |
||||
this.out.append(String.format("\\u%04x", (int) c)); |
||||
} |
||||
else { |
||||
this.out.append(c); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
this.out.append("\""); |
||||
} |
||||
|
||||
private void newline() { |
||||
if (this.indent == null) { |
||||
return; |
||||
} |
||||
|
||||
this.out.append("\n"); |
||||
this.out.append(this.indent.repeat(this.stack.size())); |
||||
} |
||||
|
||||
/** |
||||
* Encodes the key (property name) to this stringer. |
||||
* @param name the name of the forthcoming value. May not be null. |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private void beforeKey() throws JSONException { |
||||
Scope context = peek(); |
||||
if (context == Scope.NONEMPTY_OBJECT) { // first in object
|
||||
this.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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private void beforeValue() throws JSONException { |
||||
if (this.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
|
||||
this.out.append(','); |
||||
newline(); |
||||
} |
||||
else if (context == Scope.DANGLING_KEY) { // value for key
|
||||
this.out.append(this.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. |
||||
* @return the encoded JSON string. |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
return this.out.isEmpty() ? null : this.out.toString(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,555 @@
@@ -0,0 +1,555 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.cli.json; |
||||
|
||||
// Note: this class was written without inspecting the non-free org.json source code.
|
||||
|
||||
/** |
||||
* Parses a JSON (<a href="https://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>) encoded |
||||
* string into the corresponding object. Most clients of this class will use only need the |
||||
* {@link #JSONTokener(String) constructor} and {@link #nextValue} method. Example usage: |
||||
* <pre> |
||||
* String json = "{" |
||||
* + " \"query\": \"Pizza\", " |
||||
* + " \"locations\": [ 94043, 90210 ] " |
||||
* + "}"; |
||||
* |
||||
* JSONObject object = (JSONObject) new JSONTokener(json).nextValue(); |
||||
* String query = object.getString("query"); |
||||
* JSONArray locations = object.getJSONArray("locations");</pre> |
||||
* <p> |
||||
* For best interoperability and performance use JSON that complies with RFC 4627, such as |
||||
* that generated by {@link JSONStringer}. For legacy reasons this parser is lenient, so a |
||||
* successful parse does not indicate that the input string was valid JSON. All the |
||||
* following syntax errors will be ignored: |
||||
* <ul> |
||||
* <li>End of line comments starting with {@code //} or {@code #} and ending with a
|
||||
* newline character. |
||||
* <li>C-style comments starting with {@code /*} and ending with {@code *}{@code /}. Such |
||||
* comments may not be nested. |
||||
* <li>Strings that are unquoted or {@code 'single quoted'}. |
||||
* <li>Hexadecimal integers prefixed with {@code 0x} or {@code 0X}. |
||||
* <li>Octal integers prefixed with {@code 0}. |
||||
* <li>Array elements separated by {@code ;}. |
||||
* <li>Unnecessary array separators. These are interpreted as if null was the omitted |
||||
* value. |
||||
* <li>Key-value pairs separated by {@code =} or {@code =>}. |
||||
* <li>Key-value pairs separated by {@code ;}. |
||||
* </ul> |
||||
* <p> |
||||
* Each tokener may be used to parse a single JSON string. 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 JSONTokener { |
||||
|
||||
/** |
||||
* The input JSON. |
||||
*/ |
||||
private final String in; |
||||
|
||||
/** |
||||
* The index of the next character to be returned by {@link #next}. When the input is |
||||
* exhausted, this equals the input's length. |
||||
*/ |
||||
private int pos; |
||||
|
||||
/** |
||||
* @param in JSON encoded string. Null is not permitted and will yield a tokener that |
||||
* throws {@code NullPointerExceptions} when methods are called. |
||||
*/ |
||||
public JSONTokener(String in) { |
||||
// consume an optional byte order mark (BOM) if it exists
|
||||
if (in != null && in.startsWith("\ufeff")) { |
||||
in = in.substring(1); |
||||
} |
||||
this.in = in; |
||||
} |
||||
|
||||
/** |
||||
* Returns the next value from the input. |
||||
* @return a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long, |
||||
* Double or {@link JSONObject#NULL}. |
||||
* @throws JSONException if the input is malformed. |
||||
*/ |
||||
public Object nextValue() throws JSONException { |
||||
int c = nextCleanInternal(); |
||||
switch (c) { |
||||
case -1: |
||||
throw syntaxError("End of input"); |
||||
|
||||
case '{': |
||||
return readObject(); |
||||
|
||||
case '[': |
||||
return readArray(); |
||||
|
||||
case '\'', '"': |
||||
return nextString((char) c); |
||||
|
||||
default: |
||||
this.pos--; |
||||
return readLiteral(); |
||||
} |
||||
} |
||||
|
||||
private int nextCleanInternal() throws JSONException { |
||||
while (this.pos < this.in.length()) { |
||||
int c = this.in.charAt(this.pos++); |
||||
switch (c) { |
||||
case '\t', ' ', '\n', '\r': |
||||
continue; |
||||
|
||||
case '/': |
||||
if (this.pos == this.in.length()) { |
||||
return c; |
||||
} |
||||
|
||||
char peek = this.in.charAt(this.pos); |
||||
switch (peek) { |
||||
case '*': |
||||
// skip a /* c-style comment */
|
||||
this.pos++; |
||||
int commentEnd = this.in.indexOf("*/", this.pos); |
||||
if (commentEnd == -1) { |
||||
throw syntaxError("Unterminated comment"); |
||||
} |
||||
this.pos = commentEnd + 2; |
||||
continue; |
||||
|
||||
case '/': |
||||
// skip a // end-of-line comment
|
||||
this.pos++; |
||||
skipToEndOfLine(); |
||||
continue; |
||||
|
||||
default: |
||||
return c; |
||||
} |
||||
|
||||
case '#': |
||||
/* |
||||
* Skip a # hash end-of-line comment. The JSON RFC doesn't specify |
||||
* this behavior, but it's required to parse existing documents. See |
||||
* https://b/2571423.
|
||||
*/ |
||||
skipToEndOfLine(); |
||||
continue; |
||||
|
||||
default: |
||||
return c; |
||||
} |
||||
} |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
/** |
||||
* Advances the position until after the next newline character. If the line is |
||||
* terminated by "\r\n", the '\n' must be consumed as whitespace by the caller. |
||||
*/ |
||||
private void skipToEndOfLine() { |
||||
for (; this.pos < this.in.length(); this.pos++) { |
||||
char c = this.in.charAt(this.pos); |
||||
if (c == '\r' || c == '\n') { |
||||
this.pos++; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the string up to but not including {@code quote}, unescaping any character |
||||
* escape sequences encountered along the way. The opening quote should have already |
||||
* been read. This consumes the closing quote, but does not include it in the returned |
||||
* string. |
||||
* @param quote either ' or ". |
||||
* @return the string up to but not including {@code quote} |
||||
* @throws NumberFormatException if any unicode escape sequences are malformed. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public String nextString(char quote) throws JSONException { |
||||
/* |
||||
* For strings that are free of escape sequences, we can just extract the result |
||||
* as a substring of the input. But if we encounter an escape sequence, we need to |
||||
* use a StringBuilder to compose the result. |
||||
*/ |
||||
StringBuilder builder = null; |
||||
|
||||
/* the index of the first character not yet appended to the builder. */ |
||||
int start = this.pos; |
||||
|
||||
while (this.pos < this.in.length()) { |
||||
int c = this.in.charAt(this.pos++); |
||||
if (c == quote) { |
||||
if (builder == null) { |
||||
// a new string avoids leaking memory
|
||||
return new String(this.in.substring(start, this.pos - 1)); |
||||
} |
||||
else { |
||||
builder.append(this.in, start, this.pos - 1); |
||||
return builder.toString(); |
||||
} |
||||
} |
||||
|
||||
if (c == '\\') { |
||||
if (this.pos == this.in.length()) { |
||||
throw syntaxError("Unterminated escape sequence"); |
||||
} |
||||
if (builder == null) { |
||||
builder = new StringBuilder(); |
||||
} |
||||
builder.append(this.in, start, this.pos - 1); |
||||
builder.append(readEscapeCharacter()); |
||||
start = this.pos; |
||||
} |
||||
} |
||||
|
||||
throw syntaxError("Unterminated string"); |
||||
} |
||||
|
||||
/** |
||||
* Unescapes the character identified by the character or characters that immediately |
||||
* follow a backslash. The backslash '\' should have already been read. This supports |
||||
* both unicode escapes "u000A" and two-character escapes "\n". |
||||
* @return the unescaped char |
||||
* @throws NumberFormatException if any unicode escape sequences are malformed. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private char readEscapeCharacter() throws JSONException { |
||||
char escaped = this.in.charAt(this.pos++); |
||||
switch (escaped) { |
||||
case 'u': |
||||
if (this.pos + 4 > this.in.length()) { |
||||
throw syntaxError("Unterminated escape sequence"); |
||||
} |
||||
String hex = this.in.substring(this.pos, this.pos + 4); |
||||
this.pos += 4; |
||||
return (char) Integer.parseInt(hex, 16); |
||||
|
||||
case 't': |
||||
return '\t'; |
||||
|
||||
case 'b': |
||||
return '\b'; |
||||
|
||||
case 'n': |
||||
return '\n'; |
||||
|
||||
case 'r': |
||||
return '\r'; |
||||
|
||||
case 'f': |
||||
return '\f'; |
||||
|
||||
case '\'', '"', '\\': |
||||
default: |
||||
return escaped; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Reads a null, boolean, numeric or unquoted string literal value. Numeric values |
||||
* will be returned as an Integer, Long, or Double, in that order of preference. |
||||
* @return a literal value |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private Object readLiteral() throws JSONException { |
||||
String literal = nextToInternal("{}[]/\\:,=;# \t\f"); |
||||
|
||||
if (literal.isEmpty()) { |
||||
throw syntaxError("Expected literal value"); |
||||
} |
||||
else if ("null".equalsIgnoreCase(literal)) { |
||||
return JSONObject.NULL; |
||||
} |
||||
else if ("true".equalsIgnoreCase(literal)) { |
||||
return Boolean.TRUE; |
||||
} |
||||
else if ("false".equalsIgnoreCase(literal)) { |
||||
return Boolean.FALSE; |
||||
} |
||||
|
||||
/* try to parse as an integral type... */ |
||||
if (literal.indexOf('.') == -1) { |
||||
int base = 10; |
||||
String number = literal; |
||||
if (number.startsWith("0x") || number.startsWith("0X")) { |
||||
number = number.substring(2); |
||||
base = 16; |
||||
} |
||||
else if (number.startsWith("0") && number.length() > 1) { |
||||
number = number.substring(1); |
||||
base = 8; |
||||
} |
||||
try { |
||||
long longValue = Long.parseLong(number, base); |
||||
if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { |
||||
return (int) longValue; |
||||
} |
||||
else { |
||||
return longValue; |
||||
} |
||||
} |
||||
catch (NumberFormatException e) { |
||||
/* |
||||
* This only happens for integral numbers greater than Long.MAX_VALUE, |
||||
* numbers in exponential form (5e-10) and unquoted strings. Fall through |
||||
* to try floating point. |
||||
*/ |
||||
} |
||||
} |
||||
|
||||
/* ...next try to parse as a floating point... */ |
||||
try { |
||||
return Double.valueOf(literal); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
// Ignore
|
||||
} |
||||
|
||||
/* ... finally give up. We have an unquoted string */ |
||||
return new String(literal); // a new string avoids leaking memory
|
||||
} |
||||
|
||||
/** |
||||
* Returns the string up to but not including any of the given characters or a newline |
||||
* character. This does not consume the excluded character. |
||||
* @return the string up to but not including any of the given characters or a newline |
||||
* character |
||||
*/ |
||||
private String nextToInternal(String excluded) { |
||||
int start = this.pos; |
||||
for (; this.pos < this.in.length(); this.pos++) { |
||||
char c = this.in.charAt(this.pos); |
||||
if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) { |
||||
return this.in.substring(start, this.pos); |
||||
} |
||||
} |
||||
return this.in.substring(start); |
||||
} |
||||
|
||||
/** |
||||
* Reads a sequence of key/value pairs and the trailing closing brace '}' of an |
||||
* object. The opening brace '{' should have already been read. |
||||
* @return an object |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private JSONObject readObject() throws JSONException { |
||||
JSONObject result = new JSONObject(); |
||||
|
||||
/* Peek to see if this is the empty object. */ |
||||
int first = nextCleanInternal(); |
||||
if (first == '}') { |
||||
return result; |
||||
} |
||||
else if (first != -1) { |
||||
this.pos--; |
||||
} |
||||
|
||||
while (true) { |
||||
Object name = nextValue(); |
||||
if (!(name instanceof String)) { |
||||
if (name == null) { |
||||
throw syntaxError("Names cannot be null"); |
||||
} |
||||
else { |
||||
throw syntaxError( |
||||
"Names must be strings, but " + name + " is of type " + name.getClass().getName()); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* Expect the name/value separator to be either a colon ':', an equals sign |
||||
* '=', or an arrow "=>". The last two are bogus but we include them because |
||||
* that's what the original implementation did. |
||||
*/ |
||||
int separator = nextCleanInternal(); |
||||
if (separator != ':' && separator != '=') { |
||||
throw syntaxError("Expected ':' after " + name); |
||||
} |
||||
if (this.pos < this.in.length() && this.in.charAt(this.pos) == '>') { |
||||
this.pos++; |
||||
} |
||||
|
||||
result.put((String) name, nextValue()); |
||||
|
||||
switch (nextCleanInternal()) { |
||||
case '}': |
||||
return result; |
||||
case ';', ',': |
||||
continue; |
||||
default: |
||||
throw syntaxError("Unterminated object"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Reads a sequence of values and the trailing closing brace ']' of an array. The |
||||
* opening brace '[' should have already been read. Note that "[]" yields an empty |
||||
* array, but "[,]" returns a two-element array equivalent to "[null,null]". |
||||
* @return an array |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private JSONArray readArray() throws JSONException { |
||||
JSONArray result = new JSONArray(); |
||||
|
||||
/* to cover input that ends with ",]". */ |
||||
boolean hasTrailingSeparator = false; |
||||
|
||||
while (true) { |
||||
switch (nextCleanInternal()) { |
||||
case -1: |
||||
throw syntaxError("Unterminated array"); |
||||
case ']': |
||||
if (hasTrailingSeparator) { |
||||
result.put(null); |
||||
} |
||||
return result; |
||||
case ',', ';': |
||||
/* A separator without a value first means "null". */ |
||||
result.put(null); |
||||
hasTrailingSeparator = true; |
||||
continue; |
||||
default: |
||||
this.pos--; |
||||
} |
||||
|
||||
result.put(nextValue()); |
||||
|
||||
switch (nextCleanInternal()) { |
||||
case ']': |
||||
return result; |
||||
case ',', ';': |
||||
hasTrailingSeparator = true; |
||||
continue; |
||||
default: |
||||
throw syntaxError("Unterminated array"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns an exception containing the given message plus the current position and the |
||||
* entire input string. |
||||
* @param message the message |
||||
* @return an exception |
||||
*/ |
||||
public JSONException syntaxError(String message) { |
||||
return new JSONException(message + this); |
||||
} |
||||
|
||||
/** |
||||
* Returns the current position and the entire input string. |
||||
* @return the current position and the entire input string. |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
// consistent with the original implementation
|
||||
return " at character " + this.pos + " of " + this.in; |
||||
} |
||||
|
||||
/* |
||||
* Legacy APIs. |
||||
* |
||||
* None of the methods below are on the critical path of parsing JSON documents. They |
||||
* exist only because they were exposed by the original implementation and may be used |
||||
* by some clients. |
||||
*/ |
||||
|
||||
public boolean more() { |
||||
return this.pos < this.in.length(); |
||||
} |
||||
|
||||
public char next() { |
||||
return this.pos < this.in.length() ? this.in.charAt(this.pos++) : '\0'; |
||||
} |
||||
|
||||
public char next(char c) throws JSONException { |
||||
char result = next(); |
||||
if (result != c) { |
||||
throw syntaxError("Expected " + c + " but was " + result); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public char nextClean() throws JSONException { |
||||
int nextCleanInt = nextCleanInternal(); |
||||
return nextCleanInt == -1 ? '\0' : (char) nextCleanInt; |
||||
} |
||||
|
||||
public String next(int length) throws JSONException { |
||||
if (this.pos + length > this.in.length()) { |
||||
throw syntaxError(length + " is out of bounds"); |
||||
} |
||||
String result = this.in.substring(this.pos, this.pos + length); |
||||
this.pos += length; |
||||
return result; |
||||
} |
||||
|
||||
public String nextTo(String excluded) { |
||||
if (excluded == null) { |
||||
throw new NullPointerException("excluded == null"); |
||||
} |
||||
return nextToInternal(excluded).trim(); |
||||
} |
||||
|
||||
public String nextTo(char excluded) { |
||||
return nextToInternal(String.valueOf(excluded)).trim(); |
||||
} |
||||
|
||||
public void skipPast(String thru) { |
||||
int thruStart = this.in.indexOf(thru, this.pos); |
||||
this.pos = thruStart == -1 ? this.in.length() : (thruStart + thru.length()); |
||||
} |
||||
|
||||
public char skipTo(char to) { |
||||
int index = this.in.indexOf(to, this.pos); |
||||
if (index != -1) { |
||||
this.pos = index; |
||||
return to; |
||||
} |
||||
else { |
||||
return '\0'; |
||||
} |
||||
} |
||||
|
||||
public void back() { |
||||
if (--this.pos == -1) { |
||||
this.pos = 0; |
||||
} |
||||
} |
||||
|
||||
public static int dehexchar(char hex) { |
||||
if (hex >= '0' && hex <= '9') { |
||||
return hex - '0'; |
||||
} |
||||
else if (hex >= 'A' && hex <= 'F') { |
||||
return hex - 'A' + 10; |
||||
} |
||||
else if (hex >= 'a' && hex <= 'f') { |
||||
return hex - 'a' + 10; |
||||
} |
||||
else { |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
## Shaded JSON |
||||
|
||||
This source was originally taken from `com.vaadin.external.google:android-json` which |
||||
provides a clean room re-implementation of the `org.json` APIs and does not include the |
||||
"Do not use for evil" clause. |
||||
@ -0,0 +1,124 @@
@@ -0,0 +1,124 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.configurationmetadata.json; |
||||
|
||||
class JSON { |
||||
|
||||
static double checkDouble(double d) throws JSONException { |
||||
if (Double.isInfinite(d) || Double.isNaN(d)) { |
||||
throw new JSONException("Forbidden numeric value: " + d); |
||||
} |
||||
return d; |
||||
} |
||||
|
||||
static Boolean toBoolean(Object value) { |
||||
if (value instanceof Boolean) { |
||||
return (Boolean) value; |
||||
} |
||||
if (value instanceof String stringValue) { |
||||
if ("true".equalsIgnoreCase(stringValue)) { |
||||
return true; |
||||
} |
||||
if ("false".equalsIgnoreCase(stringValue)) { |
||||
return false; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
static Double toDouble(Object value) { |
||||
if (value instanceof Double) { |
||||
return (Double) value; |
||||
} |
||||
if (value instanceof Number) { |
||||
return ((Number) value).doubleValue(); |
||||
} |
||||
if (value instanceof String) { |
||||
try { |
||||
return Double.valueOf((String) value); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
// Ignore
|
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
static Integer toInteger(Object value) { |
||||
if (value instanceof Integer) { |
||||
return (Integer) value; |
||||
} |
||||
if (value instanceof Number) { |
||||
return ((Number) value).intValue(); |
||||
} |
||||
if (value instanceof String) { |
||||
try { |
||||
return (int) Double.parseDouble((String) value); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
// Ignore
|
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
static Long toLong(Object value) { |
||||
if (value instanceof Long) { |
||||
return (Long) value; |
||||
} |
||||
if (value instanceof Number) { |
||||
return ((Number) value).longValue(); |
||||
} |
||||
if (value instanceof String) { |
||||
try { |
||||
return (long) Double.parseDouble((String) value); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
// Ignore
|
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
static String toString(Object value) { |
||||
if (value instanceof String) { |
||||
return (String) value; |
||||
} |
||||
if (value != null) { |
||||
return String.valueOf(value); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static JSONException typeMismatch(Object indexOrName, Object actual, String requiredType) |
||||
throws JSONException { |
||||
if (actual == null) { |
||||
throw new JSONException("Value at " + indexOrName + " is null."); |
||||
} |
||||
throw new JSONException("Value " + actual + " at " + indexOrName + " of type " + actual.getClass().getName() |
||||
+ " cannot be converted to " + requiredType); |
||||
} |
||||
|
||||
public static JSONException typeMismatch(Object actual, String requiredType) throws JSONException { |
||||
if (actual == null) { |
||||
throw new JSONException("Value is null."); |
||||
} |
||||
throw new JSONException("Value " + actual + " of type " + actual.getClass().getName() |
||||
+ " cannot be converted to " + requiredType); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,669 @@
@@ -0,0 +1,669 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.configurationmetadata.json; |
||||
|
||||
import java.lang.reflect.Array; |
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.Iterator; |
||||
import java.util.List; |
||||
|
||||
// Note: this class was written without inspecting the non-free org.json source code.
|
||||
|
||||
/** |
||||
* A dense indexed sequence of values. Values may be any mix of {@link JSONObject |
||||
* JSONObjects}, other {@link JSONArray JSONArrays}, Strings, Booleans, Integers, Longs, |
||||
* Doubles, {@code null} or {@link JSONObject#NULL}. Values may not be |
||||
* {@link Double#isNaN() NaNs}, {@link Double#isInfinite() infinities}, or of any type not |
||||
* listed here. |
||||
* <p> |
||||
* {@code JSONArray} has the same type coercion behavior and optional/mandatory accessors |
||||
* as {@link JSONObject}. See that class' documentation for details. |
||||
* <p> |
||||
* <strong>Warning:</strong> this class represents null in two incompatible ways: the |
||||
* standard Java {@code null} reference, and the sentinel value {@link JSONObject#NULL}. |
||||
* In particular, {@code get} fails if the requested index holds the null reference, but |
||||
* succeeds if it holds {@code JSONObject.NULL}. |
||||
* <p> |
||||
* 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 |
||||
* overridable 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 JSONArray { |
||||
|
||||
private final List<Object> values; |
||||
|
||||
/** |
||||
* Creates a {@code JSONArray} with no values. |
||||
*/ |
||||
public JSONArray() { |
||||
this.values = new ArrayList<>(); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONArray} by copying all values from the given collection. |
||||
* @param copyFrom a collection whose values are of supported types. Unsupported |
||||
* values are not permitted and will yield an array in an inconsistent state. |
||||
*/ |
||||
/* Accept a raw type for API compatibility */ |
||||
@SuppressWarnings("rawtypes") |
||||
public JSONArray(Collection copyFrom) { |
||||
this(); |
||||
if (copyFrom != null) { |
||||
for (Iterator it = copyFrom.iterator(); it.hasNext();) { |
||||
put(JSONObject.wrap(it.next())); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONArray} with values from the next array in the tokener. |
||||
* @param readFrom a tokener whose nextValue() method will yield a {@code JSONArray}. |
||||
* @throws JSONException if the parse fails or doesn't yield a {@code JSONArray}. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray(JSONTokener readFrom) throws JSONException { |
||||
/* |
||||
* Getting the parser to populate this could get tricky. Instead, just parse to |
||||
* temporary JSONArray and then steal the data from that. |
||||
*/ |
||||
Object object = readFrom.nextValue(); |
||||
if (object instanceof JSONArray) { |
||||
this.values = ((JSONArray) object).values; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(object, "JSONArray"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONArray} with values from the JSON string. |
||||
* @param json a JSON-encoded string containing an array. |
||||
* @throws JSONException if the parse fails or doesn't yield a {@code |
||||
* JSONArray}. |
||||
*/ |
||||
public JSONArray(String json) throws JSONException { |
||||
this(new JSONTokener(json)); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONArray} with values from the given primitive array. |
||||
* @param array a primitive array |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray(Object array) throws JSONException { |
||||
if (!array.getClass().isArray()) { |
||||
throw new JSONException("Not a primitive array: " + array.getClass()); |
||||
} |
||||
final int length = Array.getLength(array); |
||||
this.values = new ArrayList<>(length); |
||||
for (int i = 0; i < length; ++i) { |
||||
put(JSONObject.wrap(Array.get(array, i))); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the number of values in this array. |
||||
* @return the length of this array |
||||
*/ |
||||
public int length() { |
||||
return this.values.size(); |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value the value |
||||
* @return this array. |
||||
*/ |
||||
public JSONArray put(boolean value) { |
||||
this.values.add(value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(double value) throws JSONException { |
||||
this.values.add(JSON.checkDouble(value)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value the value |
||||
* @return this array. |
||||
*/ |
||||
public JSONArray put(int value) { |
||||
this.values.add(value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value the value |
||||
* @return this array. |
||||
*/ |
||||
public JSONArray put(long value) { |
||||
this.values.add(value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the end of this array. |
||||
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, |
||||
* Long, Double, {@link JSONObject#NULL}, or {@code null}. May not be |
||||
* {@link Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. Unsupported |
||||
* values are not permitted and will cause the array to be in an inconsistent state. |
||||
* @return this array. |
||||
*/ |
||||
public JSONArray put(Object value) { |
||||
this.values.add(value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value the value |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, boolean value) throws JSONException { |
||||
return put(index, (Boolean) value); |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, double value) throws JSONException { |
||||
return put(index, (Double) value); |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value the value |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, int value) throws JSONException { |
||||
return put(index, (Integer) value); |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value the value |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, long value) throws JSONException { |
||||
return put(index, (Long) value); |
||||
} |
||||
|
||||
/** |
||||
* Sets the value at {@code index} to {@code value}, null padding this array to the |
||||
* required length if necessary. If a value already exists at {@code |
||||
* index}, it will be replaced. |
||||
* @param index the index to set the value to |
||||
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, |
||||
* Long, Double, {@link JSONObject#NULL}, or {@code null}. May not be |
||||
* {@link Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. |
||||
* @return this array. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONArray put(int index, Object value) throws JSONException { |
||||
if (value instanceof Number) { |
||||
// deviate from the original by checking all Numbers, not just floats &
|
||||
// doubles
|
||||
JSON.checkDouble(((Number) value).doubleValue()); |
||||
} |
||||
while (this.values.size() <= index) { |
||||
this.values.add(null); |
||||
} |
||||
this.values.set(index, value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Returns true if this array has no value at {@code index}, or if its value is the |
||||
* {@code null} reference or {@link JSONObject#NULL}. |
||||
* @param index the index to set the value to |
||||
* @return true if this array has no value at {@code index} |
||||
*/ |
||||
public boolean isNull(int index) { |
||||
Object value = opt(index); |
||||
return value == null || value == JSONObject.NULL; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index}. |
||||
* @param index the index to get the value from |
||||
* @return the value at {@code index}. |
||||
* @throws JSONException if this array has no value at {@code index}, or if that value |
||||
* is the {@code null} reference. This method returns normally if the value is |
||||
* {@code JSONObject#NULL}. |
||||
*/ |
||||
public Object get(int index) throws JSONException { |
||||
try { |
||||
Object value = this.values.get(index); |
||||
if (value == null) { |
||||
throw new JSONException("Value at " + index + " is null."); |
||||
} |
||||
return value; |
||||
} |
||||
catch (IndexOutOfBoundsException e) { |
||||
throw new JSONException("Index " + index + " out of range [0.." + this.values.size() + ")"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index}, or null if the array has no value at |
||||
* {@code index}. |
||||
* @param index the index to get the value from |
||||
* @return the value at {@code index} or {@code null} |
||||
*/ |
||||
public Object opt(int index) { |
||||
if (index < 0 || index >= this.values.size()) { |
||||
return null; |
||||
} |
||||
return this.values.get(index); |
||||
} |
||||
|
||||
/** |
||||
* Removes and returns the value at {@code index}, or null if the array has no value |
||||
* at {@code index}. |
||||
* @param index the index of the value to remove |
||||
* @return the previous value at {@code index} |
||||
*/ |
||||
public Object remove(int index) { |
||||
if (index < 0 || index >= this.values.size()) { |
||||
return null; |
||||
} |
||||
return this.values.remove(index); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a boolean or can be coerced |
||||
* to a boolean. |
||||
* @param index the index to get the value from |
||||
* @return the value at {@code index} |
||||
* @throws JSONException if the value at {@code index} doesn't exist or cannot be |
||||
* coerced to a boolean. |
||||
*/ |
||||
public boolean getBoolean(int index) throws JSONException { |
||||
Object object = get(index); |
||||
Boolean result = JSON.toBoolean(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "boolean"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a boolean or can be coerced |
||||
* to a boolean. Returns false otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or {@code false} |
||||
*/ |
||||
public boolean optBoolean(int index) { |
||||
return optBoolean(index, false); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a boolean or can be coerced |
||||
* to a boolean. Returns {@code fallback} otherwise. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public boolean optBoolean(int index, boolean fallback) { |
||||
Object object = opt(index); |
||||
Boolean result = JSON.toBoolean(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a double or can be coerced |
||||
* to a double. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} |
||||
* @throws JSONException if the value at {@code index} doesn't exist or cannot be |
||||
* coerced to a double. |
||||
*/ |
||||
public double getDouble(int index) throws JSONException { |
||||
Object object = get(index); |
||||
Double result = JSON.toDouble(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "double"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a double or can be coerced |
||||
* to a double. Returns {@code NaN} otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or {@code NaN} |
||||
*/ |
||||
public double optDouble(int index) { |
||||
return optDouble(index, Double.NaN); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a double or can be coerced |
||||
* to a double. Returns {@code fallback} otherwise. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public double optDouble(int index, double fallback) { |
||||
Object object = opt(index); |
||||
Double result = JSON.toDouble(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is an int or can be coerced to |
||||
* an int. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} |
||||
* @throws JSONException if the value at {@code index} doesn't exist or cannot be |
||||
* coerced to an int. |
||||
*/ |
||||
public int getInt(int index) throws JSONException { |
||||
Object object = get(index); |
||||
Integer result = JSON.toInteger(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "int"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is an int or can be coerced to |
||||
* an int. Returns 0 otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or {@code 0} |
||||
*/ |
||||
public int optInt(int index) { |
||||
return optInt(index, 0); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is an int or can be coerced to |
||||
* an int. Returns {@code fallback} otherwise. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public int optInt(int index, int fallback) { |
||||
Object object = opt(index); |
||||
Integer result = JSON.toInteger(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a long or can be coerced to |
||||
* a long. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} |
||||
* @throws JSONException if the value at {@code index} doesn't exist or cannot be |
||||
* coerced to a long. |
||||
*/ |
||||
public long getLong(int index) throws JSONException { |
||||
Object object = get(index); |
||||
Long result = JSON.toLong(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "long"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a long or can be coerced to |
||||
* a long. Returns 0 otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or {@code 0} |
||||
*/ |
||||
public long optLong(int index) { |
||||
return optLong(index, 0L); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a long or can be coerced to |
||||
* a long. Returns {@code fallback} otherwise. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public long optLong(int index, long fallback) { |
||||
Object object = opt(index); |
||||
Long result = JSON.toLong(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists, coercing it if necessary. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} |
||||
* @throws JSONException if no such value exists. |
||||
*/ |
||||
public String getString(int index) throws JSONException { |
||||
Object object = get(index); |
||||
String result = JSON.toString(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(index, object, "String"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists, coercing it if necessary. Returns |
||||
* the empty string if no such value exists. |
||||
* @param index the index to get the value from |
||||
* @return the {@code value} or an empty string |
||||
*/ |
||||
public String optString(int index) { |
||||
return optString(index, ""); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists, coercing it if necessary. Returns |
||||
* {@code fallback} if no such value exists. |
||||
* @param index the index to get the value from |
||||
* @param fallback the fallback value |
||||
* @return the value at {@code index} of {@code fallback} |
||||
*/ |
||||
public String optString(int index, String fallback) { |
||||
Object object = opt(index); |
||||
String result = JSON.toString(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a {@code |
||||
* JSONArray}. |
||||
* @param index the index to get the value from |
||||
* @return the array at {@code index} |
||||
* @throws JSONException if the value doesn't exist or is not a {@code |
||||
* JSONArray}. |
||||
*/ |
||||
public JSONArray getJSONArray(int index) throws JSONException { |
||||
Object object = get(index); |
||||
if (object instanceof JSONArray) { |
||||
return (JSONArray) object; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(index, object, "JSONArray"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a {@code |
||||
* JSONArray}. Returns null otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the array at {@code index} or {@code null} |
||||
*/ |
||||
public JSONArray optJSONArray(int index) { |
||||
Object object = opt(index); |
||||
return object instanceof JSONArray ? (JSONArray) object : null; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a {@code |
||||
* JSONObject}. |
||||
* @param index the index to get the value from |
||||
* @return the object at {@code index} |
||||
* @throws JSONException if the value doesn't exist or is not a {@code |
||||
* JSONObject}. |
||||
*/ |
||||
public JSONObject getJSONObject(int index) throws JSONException { |
||||
Object object = get(index); |
||||
if (object instanceof JSONObject) { |
||||
return (JSONObject) object; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(index, object, "JSONObject"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value at {@code index} if it exists and is a {@code |
||||
* JSONObject}. Returns null otherwise. |
||||
* @param index the index to get the value from |
||||
* @return the object at {@code index} or {@code null} |
||||
*/ |
||||
public JSONObject optJSONObject(int index) { |
||||
Object object = opt(index); |
||||
return object instanceof JSONObject ? (JSONObject) object : null; |
||||
} |
||||
|
||||
/** |
||||
* Returns a new object whose values are the values in this array, and whose names are |
||||
* the values in {@code names}. Names and values are paired up by index from 0 through |
||||
* to the shorter array's length. Names that are not strings will be coerced to |
||||
* strings. This method returns null if either array is empty. |
||||
* @param names the property names |
||||
* @return a json object |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONObject toJSONObject(JSONArray names) throws JSONException { |
||||
JSONObject result = new JSONObject(); |
||||
int length = Math.min(names.length(), this.values.size()); |
||||
if (length == 0) { |
||||
return null; |
||||
} |
||||
for (int i = 0; i < length; i++) { |
||||
String name = JSON.toString(names.opt(i)); |
||||
result.put(name, opt(i)); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns a new string by alternating this array's values with {@code |
||||
* separator}. This array's string values are quoted and have their special characters |
||||
* escaped. For example, the array containing the strings '12" pizza', 'taco' and |
||||
* 'soda' joined on '+' returns this: <pre>"12\" pizza"+"taco"+"soda"</pre> |
||||
* @param separator the separator to use |
||||
* @return the joined value |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public String join(String separator) throws JSONException { |
||||
JSONStringer stringer = new JSONStringer(); |
||||
stringer.open(JSONStringer.Scope.NULL, ""); |
||||
for (int i = 0, size = this.values.size(); i < size; i++) { |
||||
if (i > 0) { |
||||
stringer.out.append(separator); |
||||
} |
||||
stringer.value(this.values.get(i)); |
||||
} |
||||
stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); |
||||
return stringer.out.toString(); |
||||
} |
||||
|
||||
/** |
||||
* Encodes this array as a compact JSON string, such as: <pre>[94043,90210]</pre> |
||||
* @return a compact JSON string representation of this array |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
try { |
||||
JSONStringer stringer = new JSONStringer(); |
||||
writeTo(stringer); |
||||
return stringer.toString(); |
||||
} |
||||
catch (JSONException e) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Encodes this array as a human-readable JSON string for debugging, such as: <pre> |
||||
* [ |
||||
* 94043, |
||||
* 90210 |
||||
* ]</pre> |
||||
* @param indentSpaces the number of spaces to indent for each level of nesting. |
||||
* @return a human-readable JSON string of this array |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public String toString(int indentSpaces) throws JSONException { |
||||
JSONStringer stringer = new JSONStringer(indentSpaces); |
||||
writeTo(stringer); |
||||
return stringer.toString(); |
||||
} |
||||
|
||||
void writeTo(JSONStringer stringer) throws JSONException { |
||||
stringer.array(); |
||||
for (Object value : this.values) { |
||||
stringer.value(value); |
||||
} |
||||
stringer.endArray(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
return o instanceof JSONArray && ((JSONArray) o).values.equals(this.values); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
// diverge from the original, which doesn't implement hashCode
|
||||
return this.values.hashCode(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.configurationmetadata.json; |
||||
|
||||
// Note: this class was written without inspecting the non-free org.json source code.
|
||||
|
||||
/** |
||||
* Thrown to indicate a problem with the JSON API. Such problems include: |
||||
* <ul> |
||||
* <li>Attempts to parse or construct malformed documents |
||||
* <li>Use of null as a name |
||||
* <li>Use of numeric types not available to JSON, such as {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* <li>Lookups using an out of range index or nonexistent name |
||||
* <li>Type mismatches on lookups |
||||
* </ul> |
||||
* <p> |
||||
* Although this is a checked exception, it is rarely recoverable. Most callers should |
||||
* simply wrap this exception in an unchecked exception and rethrow: <pre class="code"> |
||||
* public JSONArray toJSONObject() { |
||||
* try { |
||||
* JSONObject result = new JSONObject(); |
||||
* ... |
||||
* } catch (JSONException e) { |
||||
* throw new RuntimeException(e); |
||||
* } |
||||
* }</pre> |
||||
*/ |
||||
public class JSONException extends Exception { |
||||
|
||||
public JSONException(String s) { |
||||
super(s); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,836 @@
@@ -0,0 +1,836 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.configurationmetadata.json; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.Iterator; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
// Note: this class was written without inspecting the non-free org.json source code.
|
||||
|
||||
/** |
||||
* A modifiable set of name/value mappings. Names are unique, non-null strings. Values may |
||||
* be any mix of {@link JSONObject JSONObjects}, {@link JSONArray JSONArrays}, Strings, |
||||
* Booleans, Integers, Longs, Doubles or {@link #NULL}. Values may not be {@code null}, |
||||
* {@link Double#isNaN() NaNs}, {@link Double#isInfinite() infinities}, or of any type not |
||||
* listed here. |
||||
* <p> |
||||
* This class can coerce values to another type when requested. |
||||
* <ul> |
||||
* <li>When the requested type is a boolean, strings will be coerced using a |
||||
* case-insensitive comparison to "true" and "false". |
||||
* <li>When the requested type is a double, other {@link Number} types will be coerced |
||||
* using {@link Number#doubleValue() doubleValue}. Strings that can be coerced using |
||||
* {@link Double#valueOf(String)} will be. |
||||
* <li>When the requested type is an int, other {@link Number} types will be coerced using |
||||
* {@link Number#intValue() intValue}. Strings that can be coerced using |
||||
* {@link Double#valueOf(String)} will be, and then cast to int. |
||||
* <li><a id="lossy">When the requested type is a long, other {@link Number} types will be |
||||
* coerced using {@link Number#longValue() longValue}. Strings that can be coerced using |
||||
* {@link Double#valueOf(String)} will be, and then cast to long. This two-step conversion |
||||
* is lossy for very large values. For example, the string "9223372036854775806" yields |
||||
* the long 9223372036854775807.</a> |
||||
* <li>When the requested type is a String, other non-null values will be coerced using |
||||
* {@link String#valueOf(Object)}. Although null cannot be coerced, the sentinel value |
||||
* {@link JSONObject#NULL} is coerced to the string "null". |
||||
* </ul> |
||||
* <p> |
||||
* This class can look up both mandatory and optional values: |
||||
* <ul> |
||||
* <li>Use <code>get<i>Type</i>()</code> to retrieve a mandatory value. This fails with a |
||||
* {@code JSONException} if the requested name has no value or if the value cannot be |
||||
* coerced to the requested type. |
||||
* <li>Use <code>opt<i>Type</i>()</code> to retrieve an optional value. This returns a |
||||
* system- or user-supplied default if the requested name has no value or if the value |
||||
* cannot be coerced to the requested type. |
||||
* </ul> |
||||
* <p> |
||||
* <strong>Warning:</strong> this class represents null in two incompatible ways: the |
||||
* standard Java {@code null} reference, and the sentinel value {@link JSONObject#NULL}. |
||||
* In particular, calling {@code put(name, null)} removes the named entry from the object |
||||
* but {@code put(name, JSONObject.NULL)} stores an entry whose value is |
||||
* {@code JSONObject.NULL}. |
||||
* <p> |
||||
* 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 JSONObject { |
||||
|
||||
private static final Double NEGATIVE_ZERO = -0d; |
||||
|
||||
/** |
||||
* A sentinel value used to explicitly define a name with no value. Unlike |
||||
* {@code null}, names with this value: |
||||
* <ul> |
||||
* <li>show up in the {@link #names} array |
||||
* <li>show up in the {@link #keys} iterator |
||||
* <li>return {@code true} for {@link #has(String)} |
||||
* <li>do not throw on {@link #get(String)} |
||||
* <li>are included in the encoded JSON string. |
||||
* </ul> |
||||
* <p> |
||||
* This value violates the general contract of {@link Object#equals} by returning true |
||||
* when compared to {@code null}. Its {@link #toString} method returns "null". |
||||
*/ |
||||
public static final Object NULL = new Object() { |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
return o == this || o == null; // API specifies this broken equals
|
||||
// implementation
|
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return "null"; |
||||
} |
||||
|
||||
}; |
||||
|
||||
private final Map<String, Object> nameValuePairs; |
||||
|
||||
/** |
||||
* Creates a {@code JSONObject} with no name/value mappings. |
||||
*/ |
||||
public JSONObject() { |
||||
this.nameValuePairs = new LinkedHashMap<>(); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONObject} by copying all name/value mappings from the given |
||||
* map. |
||||
* @param copyFrom a map whose keys are of type {@link String} and whose values are of |
||||
* supported types. |
||||
* @throws NullPointerException if any of the map's keys are null. |
||||
*/ |
||||
/* (accept a raw type for API compatibility) */ |
||||
@SuppressWarnings("rawtypes") |
||||
public JSONObject(Map copyFrom) { |
||||
this(); |
||||
Map<?, ?> contentsTyped = copyFrom; |
||||
for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) { |
||||
/* |
||||
* Deviate from the original by checking that keys are non-null and of the |
||||
* proper type. (We still defer validating the values). |
||||
*/ |
||||
String key = (String) entry.getKey(); |
||||
if (key == null) { |
||||
throw new NullPointerException("key == null"); |
||||
} |
||||
this.nameValuePairs.put(key, wrap(entry.getValue())); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONObject} with name/value mappings from the next object in |
||||
* the tokener. |
||||
* @param readFrom a tokener whose nextValue() method will yield a {@code JSONObject}. |
||||
* @throws JSONException if the parse fails or doesn't yield a {@code JSONObject}. |
||||
*/ |
||||
public JSONObject(JSONTokener readFrom) throws JSONException { |
||||
/* |
||||
* Getting the parser to populate this could get tricky. Instead, just parse to |
||||
* temporary JSONObject and then steal the data from that. |
||||
*/ |
||||
Object object = readFrom.nextValue(); |
||||
if (object instanceof JSONObject) { |
||||
this.nameValuePairs = ((JSONObject) object).nameValuePairs; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(object, "JSONObject"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONObject} with name/value mappings from the JSON string. |
||||
* @param json a JSON-encoded string containing an object. |
||||
* @throws JSONException if the parse fails or doesn't yield a {@code |
||||
* JSONObject}. |
||||
*/ |
||||
public JSONObject(String json) throws JSONException { |
||||
this(new JSONTokener(json)); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@code JSONObject} by copying mappings for the listed names from the |
||||
* given object. Names that aren't present in {@code copyFrom} will be skipped. |
||||
* @param copyFrom the source |
||||
* @param names the property names |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject(JSONObject copyFrom, String[] names) throws JSONException { |
||||
this(); |
||||
for (String name : names) { |
||||
Object value = copyFrom.opt(name); |
||||
if (value != null) { |
||||
this.nameValuePairs.put(name, value); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the number of name/value mappings in this object. |
||||
* @return the number of name/value mappings in this object |
||||
*/ |
||||
public int length() { |
||||
return this.nameValuePairs.size(); |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. |
||||
* @param name the name of the property |
||||
* @param value the value of the property |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, boolean value) throws JSONException { |
||||
this.nameValuePairs.put(checkName(name), value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. |
||||
* @param name the name of the property |
||||
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, double value) throws JSONException { |
||||
this.nameValuePairs.put(checkName(name), JSON.checkDouble(value)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. |
||||
* @param name the name of the property |
||||
* @param value the value of the property |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, int value) throws JSONException { |
||||
this.nameValuePairs.put(checkName(name), value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. |
||||
* @param name the name of the property |
||||
* @param value the value of the property |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, long value) throws JSONException { |
||||
this.nameValuePairs.put(checkName(name), value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Maps {@code name} to {@code value}, clobbering any existing name/value mapping with |
||||
* the same name. If the value is {@code null}, any existing mapping for {@code name} |
||||
* is removed. |
||||
* @param name the name of the property |
||||
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, |
||||
* Long, Double, {@link #NULL}, or {@code null}. May not be {@link Double#isNaN() |
||||
* NaNs} or {@link Double#isInfinite() infinities}. |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject put(String name, Object value) throws JSONException { |
||||
if (value == null) { |
||||
this.nameValuePairs.remove(name); |
||||
return this; |
||||
} |
||||
if (value instanceof Number) { |
||||
// deviate from the original by checking all Numbers, not just floats &
|
||||
// doubles
|
||||
JSON.checkDouble(((Number) value).doubleValue()); |
||||
} |
||||
this.nameValuePairs.put(checkName(name), value); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Equivalent to {@code put(name, value)} when both parameters are non-null; does |
||||
* nothing otherwise. |
||||
* @param name the name of the property |
||||
* @param value the value of the property |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject putOpt(String name, Object value) throws JSONException { |
||||
if (name == null || value == null) { |
||||
return this; |
||||
} |
||||
return put(name, value); |
||||
} |
||||
|
||||
/** |
||||
* Appends {@code value} to the array already mapped to {@code name}. If this object |
||||
* has no mapping for {@code name}, this inserts a new mapping. If the mapping exists |
||||
* but its value is not an array, the existing and new values are inserted in order |
||||
* into a new array which is itself mapped to {@code name}. In aggregate, this allows |
||||
* values to be added to a mapping one at a time. |
||||
* @param name the name of the property |
||||
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, |
||||
* Long, Double, {@link #NULL} or null. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return this object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public JSONObject accumulate(String name, Object value) throws JSONException { |
||||
Object current = this.nameValuePairs.get(checkName(name)); |
||||
if (current == null) { |
||||
return put(name, value); |
||||
} |
||||
|
||||
// check in accumulate, since array.put(Object) doesn't do any checking
|
||||
if (value instanceof Number) { |
||||
JSON.checkDouble(((Number) value).doubleValue()); |
||||
} |
||||
|
||||
if (current instanceof JSONArray array) { |
||||
array.put(value); |
||||
} |
||||
else { |
||||
JSONArray array = new JSONArray(); |
||||
array.put(current); |
||||
array.put(value); |
||||
this.nameValuePairs.put(name, array); |
||||
} |
||||
return this; |
||||
} |
||||
|
||||
String checkName(String name) throws JSONException { |
||||
if (name == null) { |
||||
throw new JSONException("Names must be non-null"); |
||||
} |
||||
return name; |
||||
} |
||||
|
||||
/** |
||||
* Removes the named mapping if it exists; does nothing otherwise. |
||||
* @param name the name of the property |
||||
* @return the value previously mapped by {@code name}, or null if there was no such |
||||
* mapping. |
||||
*/ |
||||
public Object remove(String name) { |
||||
return this.nameValuePairs.remove(name); |
||||
} |
||||
|
||||
/** |
||||
* Returns true if this object has no mapping for {@code name} or if it has a mapping |
||||
* whose value is {@link #NULL}. |
||||
* @param name the name of the property |
||||
* @return true if this object has no mapping for {@code name} |
||||
*/ |
||||
public boolean isNull(String name) { |
||||
Object value = this.nameValuePairs.get(name); |
||||
return value == null || value == NULL; |
||||
} |
||||
|
||||
/** |
||||
* Returns true if this object has a mapping for {@code name}. The mapping may be |
||||
* {@link #NULL}. |
||||
* @param name the name of the property |
||||
* @return true if this object has a mapping for {@code name} |
||||
*/ |
||||
public boolean has(String name) { |
||||
return this.nameValuePairs.containsKey(name); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name}. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if no such mapping exists. |
||||
*/ |
||||
public Object get(String name) throws JSONException { |
||||
Object result = this.nameValuePairs.get(name); |
||||
if (result == null) { |
||||
throw new JSONException("No value for " + name); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name}, or null if no such mapping exists. |
||||
* @param name the name of the property |
||||
* @return the value or {@code null} |
||||
*/ |
||||
public Object opt(String name) { |
||||
return this.nameValuePairs.get(name); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a boolean or can be |
||||
* coerced to a boolean. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or cannot be coerced to a |
||||
* boolean. |
||||
*/ |
||||
public boolean getBoolean(String name) throws JSONException { |
||||
Object object = get(name); |
||||
Boolean result = JSON.toBoolean(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "boolean"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a boolean or can be |
||||
* coerced to a boolean. Returns false otherwise. |
||||
* @param name the name of the property |
||||
* @return the value or {@code null} |
||||
*/ |
||||
public boolean optBoolean(String name) { |
||||
return optBoolean(name, false); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a boolean or can be |
||||
* coerced to a boolean. Returns {@code fallback} otherwise. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public boolean optBoolean(String name, boolean fallback) { |
||||
Object object = opt(name); |
||||
Boolean result = JSON.toBoolean(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a double or can be |
||||
* coerced to a double. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or cannot be coerced to a |
||||
* double. |
||||
*/ |
||||
public double getDouble(String name) throws JSONException { |
||||
Object object = get(name); |
||||
Double result = JSON.toDouble(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "double"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a double or can be |
||||
* coerced to a double. Returns {@code NaN} otherwise. |
||||
* @param name the name of the property |
||||
* @return the value or {@code NaN} |
||||
*/ |
||||
public double optDouble(String name) { |
||||
return optDouble(name, Double.NaN); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a double or can be |
||||
* coerced to a double. Returns {@code fallback} otherwise. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public double optDouble(String name, double fallback) { |
||||
Object object = opt(name); |
||||
Double result = JSON.toDouble(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is an int or can be |
||||
* coerced to an int. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or cannot be coerced to an int. |
||||
*/ |
||||
public int getInt(String name) throws JSONException { |
||||
Object object = get(name); |
||||
Integer result = JSON.toInteger(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "int"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is an int or can be |
||||
* coerced to an int. Returns 0 otherwise. |
||||
* @param name the name of the property |
||||
* @return the value of {@code 0} |
||||
*/ |
||||
public int optInt(String name) { |
||||
return optInt(name, 0); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is an int or can be |
||||
* coerced to an int. Returns {@code fallback} otherwise. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public int optInt(String name, int fallback) { |
||||
Object object = opt(name); |
||||
Integer result = JSON.toInteger(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a long or can be |
||||
* coerced to a long. Note that JSON represents numbers as doubles, so this is |
||||
* <a href="#lossy">lossy</a>; use strings to transfer numbers over JSON. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or cannot be coerced to a long. |
||||
*/ |
||||
public long getLong(String name) throws JSONException { |
||||
Object object = get(name); |
||||
Long result = JSON.toLong(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "long"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a long or can be |
||||
* coerced to a long. Returns 0 otherwise. Note that JSON represents numbers as |
||||
* doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via |
||||
* JSON. |
||||
* @param name the name of the property |
||||
* @return the value or {@code 0L} |
||||
*/ |
||||
public long optLong(String name) { |
||||
return optLong(name, 0L); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a long or can be |
||||
* coerced to a long. Returns {@code fallback} otherwise. Note that JSON represents |
||||
* numbers as doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer |
||||
* numbers over JSON. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public long optLong(String name, long fallback) { |
||||
Object object = opt(name); |
||||
Long result = JSON.toLong(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists, coercing it if necessary. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if no such mapping exists. |
||||
*/ |
||||
public String getString(String name) throws JSONException { |
||||
Object object = get(name); |
||||
String result = JSON.toString(object); |
||||
if (result == null) { |
||||
throw JSON.typeMismatch(name, object, "String"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists, coercing it if necessary. |
||||
* Returns the empty string if no such mapping exists. |
||||
* @param name the name of the property |
||||
* @return the value or an empty string |
||||
*/ |
||||
public String optString(String name) { |
||||
return optString(name, ""); |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists, coercing it if necessary. |
||||
* Returns {@code fallback} if no such mapping exists. |
||||
* @param name the name of the property |
||||
* @param fallback a fallback value |
||||
* @return the value or {@code fallback} |
||||
*/ |
||||
public String optString(String name, String fallback) { |
||||
Object object = opt(name); |
||||
String result = JSON.toString(object); |
||||
return result != null ? result : fallback; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a {@code |
||||
* JSONArray}. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or is not a {@code |
||||
* JSONArray}. |
||||
*/ |
||||
public JSONArray getJSONArray(String name) throws JSONException { |
||||
Object object = get(name); |
||||
if (object instanceof JSONArray) { |
||||
return (JSONArray) object; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(name, object, "JSONArray"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a {@code |
||||
* JSONArray}. Returns null otherwise. |
||||
* @param name the name of the property |
||||
* @return the value or {@code null} |
||||
*/ |
||||
public JSONArray optJSONArray(String name) { |
||||
Object object = opt(name); |
||||
return object instanceof JSONArray ? (JSONArray) object : null; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a {@code |
||||
* JSONObject}. |
||||
* @param name the name of the property |
||||
* @return the value |
||||
* @throws JSONException if the mapping doesn't exist or is not a {@code |
||||
* JSONObject}. |
||||
*/ |
||||
public JSONObject getJSONObject(String name) throws JSONException { |
||||
Object object = get(name); |
||||
if (object instanceof JSONObject) { |
||||
return (JSONObject) object; |
||||
} |
||||
else { |
||||
throw JSON.typeMismatch(name, object, "JSONObject"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the value mapped by {@code name} if it exists and is a {@code |
||||
* JSONObject}. Returns null otherwise. |
||||
* @param name the name of the property |
||||
* @return the value or {@code null} |
||||
*/ |
||||
public JSONObject optJSONObject(String name) { |
||||
Object object = opt(name); |
||||
return object instanceof JSONObject ? (JSONObject) object : null; |
||||
} |
||||
|
||||
/** |
||||
* Returns an array with the values corresponding to {@code names}. The array contains |
||||
* null for names that aren't mapped. This method returns null if {@code names} is |
||||
* either null or empty. |
||||
* @param names the names of the properties |
||||
* @return the array |
||||
*/ |
||||
public JSONArray toJSONArray(JSONArray names) { |
||||
JSONArray result = new JSONArray(); |
||||
if (names == null) { |
||||
return null; |
||||
} |
||||
int length = names.length(); |
||||
if (length == 0) { |
||||
return null; |
||||
} |
||||
for (int i = 0; i < length; i++) { |
||||
String name = JSON.toString(names.opt(i)); |
||||
result.put(opt(name)); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Returns an iterator of the {@code String} names in this object. The returned |
||||
* iterator supports {@link Iterator#remove() remove}, which will remove the |
||||
* corresponding mapping from this object. If this object is modified after the |
||||
* iterator is returned, the iterator's behavior is undefined. The order of the keys |
||||
* is undefined. |
||||
* @return the keys |
||||
*/ |
||||
/* Return a raw type for API compatibility */ |
||||
@SuppressWarnings("rawtypes") |
||||
public Iterator keys() { |
||||
return this.nameValuePairs.keySet().iterator(); |
||||
} |
||||
|
||||
/** |
||||
* Returns an array containing the string names in this object. This method returns |
||||
* null if this object contains no mappings. |
||||
* @return the array |
||||
*/ |
||||
public JSONArray names() { |
||||
return this.nameValuePairs.isEmpty() ? null : new JSONArray(new ArrayList<>(this.nameValuePairs.keySet())); |
||||
} |
||||
|
||||
/** |
||||
* Encodes this object as a compact JSON string, such as: |
||||
* <pre>{"query":"Pizza","locations":[94043,90210]}</pre> |
||||
* @return a string representation of the object. |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
try { |
||||
JSONStringer stringer = new JSONStringer(); |
||||
writeTo(stringer); |
||||
return stringer.toString(); |
||||
} |
||||
catch (JSONException e) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Encodes this object as a human-readable JSON string for debugging, such as: <pre> |
||||
* { |
||||
* "query": "Pizza", |
||||
* "locations": [ |
||||
* 94043, |
||||
* 90210 |
||||
* ] |
||||
* }</pre> |
||||
* @param indentSpaces the number of spaces to indent for each level of nesting. |
||||
* @return a string representation of the object. |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public String toString(int indentSpaces) throws JSONException { |
||||
JSONStringer stringer = new JSONStringer(indentSpaces); |
||||
writeTo(stringer); |
||||
return stringer.toString(); |
||||
} |
||||
|
||||
void writeTo(JSONStringer stringer) throws JSONException { |
||||
stringer.object(); |
||||
for (Map.Entry<String, Object> entry : this.nameValuePairs.entrySet()) { |
||||
stringer.key(entry.getKey()).value(entry.getValue()); |
||||
} |
||||
stringer.endObject(); |
||||
} |
||||
|
||||
/** |
||||
* Encodes the number as a JSON string. |
||||
* @param number a finite value. May not be {@link Double#isNaN() NaNs} or |
||||
* {@link Double#isInfinite() infinities}. |
||||
* @return the encoded value |
||||
* @throws JSONException if an error occurs |
||||
*/ |
||||
public static String numberToString(Number number) throws JSONException { |
||||
if (number == null) { |
||||
throw new JSONException("Number must be non-null"); |
||||
} |
||||
|
||||
double doubleValue = number.doubleValue(); |
||||
JSON.checkDouble(doubleValue); |
||||
|
||||
// the original returns "-0" instead of "-0.0" for negative zero
|
||||
if (number.equals(NEGATIVE_ZERO)) { |
||||
return "-0"; |
||||
} |
||||
|
||||
long longValue = number.longValue(); |
||||
if (doubleValue == longValue) { |
||||
return Long.toString(longValue); |
||||
} |
||||
|
||||
return number.toString(); |
||||
} |
||||
|
||||
/** |
||||
* Encodes {@code data} as a JSON string. This applies quotes and any necessary |
||||
* character escaping. |
||||
* @param data the string to encode. Null will be interpreted as an empty string. |
||||
* @return the quoted value |
||||
*/ |
||||
public static String quote(String data) { |
||||
if (data == null) { |
||||
return "\"\""; |
||||
} |
||||
try { |
||||
JSONStringer stringer = new JSONStringer(); |
||||
stringer.open(JSONStringer.Scope.NULL, ""); |
||||
stringer.value(data); |
||||
stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); |
||||
return stringer.toString(); |
||||
} |
||||
catch (JSONException e) { |
||||
throw new AssertionError(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Wraps the given object if necessary. |
||||
* <p> |
||||
* If the object is null or, returns {@link #NULL}. If the object is a |
||||
* {@code JSONArray} or {@code JSONObject}, no wrapping is necessary. If the object is |
||||
* {@code NULL}, no wrapping is necessary. If the object is an array or |
||||
* {@code Collection}, returns an equivalent {@code JSONArray}. If the object is a |
||||
* {@code Map}, returns an equivalent {@code JSONObject}. If the object is a primitive |
||||
* wrapper type or {@code String}, returns the object. Otherwise if the object is from |
||||
* a {@code java} package, returns the result of {@code toString}. If wrapping fails, |
||||
* returns null. |
||||
* @param o the object to wrap |
||||
* @return the wrapped object |
||||
*/ |
||||
@SuppressWarnings("rawtypes") |
||||
public static Object wrap(Object o) { |
||||
if (o == null) { |
||||
return NULL; |
||||
} |
||||
if (o instanceof JSONArray || o instanceof JSONObject) { |
||||
return o; |
||||
} |
||||
if (o.equals(NULL)) { |
||||
return o; |
||||
} |
||||
try { |
||||
if (o instanceof Collection) { |
||||
return new JSONArray((Collection) o); |
||||
} |
||||
else if (o.getClass().isArray()) { |
||||
return new JSONArray(o); |
||||
} |
||||
if (o instanceof Map) { |
||||
return new JSONObject((Map) o); |
||||
} |
||||
if (o instanceof Boolean || o instanceof Byte || o instanceof Character || o instanceof Double |
||||
|| o instanceof Float || o instanceof Integer || o instanceof Long || o instanceof Short |
||||
|| o instanceof String) { |
||||
return o; |
||||
} |
||||
if (o.getClass().getPackage().getName().startsWith("java.")) { |
||||
return o.toString(); |
||||
} |
||||
} |
||||
catch (Exception ex) { |
||||
// Ignore
|
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,429 @@
@@ -0,0 +1,429 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.configurationmetadata.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 source code.
|
||||
|
||||
/** |
||||
* 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(); |
||||
|
||||
/** |
||||
* Lexical scoping elements within this stringer, necessary to insert the appropriate |
||||
* separator characters (i.e. 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, |
||||
|
||||
/** |
||||
* An 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 |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Unlike the original implementation, this stack isn't limited to 20 levels of |
||||
* nesting. |
||||
*/ |
||||
private final List<Scope> stack = new ArrayList<>(); |
||||
|
||||
/** |
||||
* 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() { |
||||
this.indent = null; |
||||
} |
||||
|
||||
JSONStringer(int indentSpaces) { |
||||
char[] indentChars = new char[indentSpaces]; |
||||
Arrays.fill(indentChars, ' '); |
||||
this.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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer array() throws JSONException { |
||||
return open(Scope.EMPTY_ARRAY, "["); |
||||
} |
||||
|
||||
/** |
||||
* Ends encoding the current array. |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer object() throws JSONException { |
||||
return open(Scope.EMPTY_OBJECT, "{"); |
||||
} |
||||
|
||||
/** |
||||
* Ends encoding the current object. |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
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. |
||||
* @param empty any necessary whitespace |
||||
* @param openBracket the open bracket |
||||
* @return this object |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
JSONStringer open(Scope empty, String openBracket) throws JSONException { |
||||
if (this.stack.isEmpty() && !this.out.isEmpty()) { |
||||
throw new JSONException("Nesting problem: multiple top-level roots"); |
||||
} |
||||
beforeValue(); |
||||
this.stack.add(empty); |
||||
this.out.append(openBracket); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Closes the current scope by appending any necessary whitespace and the given |
||||
* bracket. |
||||
* @param empty any necessary whitespace |
||||
* @param nonempty the current scope |
||||
* @param closeBracket the close bracket |
||||
* @return the JSON stringer |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException { |
||||
Scope context = peek(); |
||||
if (context != nonempty && context != empty) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
|
||||
this.stack.remove(this.stack.size() - 1); |
||||
if (context == nonempty) { |
||||
newline(); |
||||
} |
||||
this.out.append(closeBracket); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value on the top of the stack. |
||||
* @return the scope |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private Scope peek() throws JSONException { |
||||
if (this.stack.isEmpty()) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
return this.stack.get(this.stack.size() - 1); |
||||
} |
||||
|
||||
/** |
||||
* Replace the value on the top of the stack with the given value. |
||||
* @param topOfStack the scope at the top of the stack |
||||
*/ |
||||
private void replaceTop(Scope topOfStack) { |
||||
this.stack.set(this.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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer value(Object value) throws JSONException { |
||||
if (this.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) { |
||||
this.out.append(value); |
||||
|
||||
} |
||||
else if (value instanceof Number) { |
||||
this.out.append(JSONObject.numberToString((Number) value)); |
||||
|
||||
} |
||||
else { |
||||
string(value.toString()); |
||||
} |
||||
|
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Encodes {@code value} to this stringer. |
||||
* @param value the value to encode |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer value(boolean value) throws JSONException { |
||||
if (this.stack.isEmpty()) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
beforeValue(); |
||||
this.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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer value(double value) throws JSONException { |
||||
if (this.stack.isEmpty()) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
beforeValue(); |
||||
this.out.append(JSONObject.numberToString(value)); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Encodes {@code value} to this stringer. |
||||
* @param value the value to encode |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public JSONStringer value(long value) throws JSONException { |
||||
if (this.stack.isEmpty()) { |
||||
throw new JSONException("Nesting problem"); |
||||
} |
||||
beforeValue(); |
||||
this.out.append(value); |
||||
return this; |
||||
} |
||||
|
||||
private void string(String value) { |
||||
this.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 '"', '\\', '/' -> this.out.append('\\').append(c); |
||||
case '\t' -> this.out.append("\\t"); |
||||
case '\b' -> this.out.append("\\b"); |
||||
case '\n' -> this.out.append("\\n"); |
||||
case '\r' -> this.out.append("\\r"); |
||||
case '\f' -> this.out.append("\\f"); |
||||
default -> { |
||||
if (c <= 0x1F) { |
||||
this.out.append(String.format("\\u%04x", (int) c)); |
||||
} |
||||
else { |
||||
this.out.append(c); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
this.out.append("\""); |
||||
} |
||||
|
||||
private void newline() { |
||||
if (this.indent == null) { |
||||
return; |
||||
} |
||||
|
||||
this.out.append("\n"); |
||||
this.out.append(this.indent.repeat(this.stack.size())); |
||||
} |
||||
|
||||
/** |
||||
* Encodes the key (property name) to this stringer. |
||||
* @param name the name of the forthcoming value. May not be null. |
||||
* @return this stringer. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private void beforeKey() throws JSONException { |
||||
Scope context = peek(); |
||||
if (context == Scope.NONEMPTY_OBJECT) { // first in object
|
||||
this.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. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private void beforeValue() throws JSONException { |
||||
if (this.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
|
||||
this.out.append(','); |
||||
newline(); |
||||
} |
||||
else if (context == Scope.DANGLING_KEY) { // value for key
|
||||
this.out.append(this.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. |
||||
* @return the encoded JSON string. |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
return this.out.isEmpty() ? null : this.out.toString(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,555 @@
@@ -0,0 +1,555 @@
|
||||
/* |
||||
* Copyright 2012-present the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.boot.configurationmetadata.json; |
||||
|
||||
// Note: this class was written without inspecting the non-free org.json source code.
|
||||
|
||||
/** |
||||
* Parses a JSON (<a href="https://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>) encoded |
||||
* string into the corresponding object. Most clients of this class will use only need the |
||||
* {@link #JSONTokener(String) constructor} and {@link #nextValue} method. Example usage: |
||||
* <pre> |
||||
* String json = "{" |
||||
* + " \"query\": \"Pizza\", " |
||||
* + " \"locations\": [ 94043, 90210 ] " |
||||
* + "}"; |
||||
* |
||||
* JSONObject object = (JSONObject) new JSONTokener(json).nextValue(); |
||||
* String query = object.getString("query"); |
||||
* JSONArray locations = object.getJSONArray("locations");</pre> |
||||
* <p> |
||||
* For best interoperability and performance use JSON that complies with RFC 4627, such as |
||||
* that generated by {@link JSONStringer}. For legacy reasons this parser is lenient, so a |
||||
* successful parse does not indicate that the input string was valid JSON. All the |
||||
* following syntax errors will be ignored: |
||||
* <ul> |
||||
* <li>End of line comments starting with {@code //} or {@code #} and ending with a
|
||||
* newline character. |
||||
* <li>C-style comments starting with {@code /*} and ending with {@code *}{@code /}. Such |
||||
* comments may not be nested. |
||||
* <li>Strings that are unquoted or {@code 'single quoted'}. |
||||
* <li>Hexadecimal integers prefixed with {@code 0x} or {@code 0X}. |
||||
* <li>Octal integers prefixed with {@code 0}. |
||||
* <li>Array elements separated by {@code ;}. |
||||
* <li>Unnecessary array separators. These are interpreted as if null was the omitted |
||||
* value. |
||||
* <li>Key-value pairs separated by {@code =} or {@code =>}. |
||||
* <li>Key-value pairs separated by {@code ;}. |
||||
* </ul> |
||||
* <p> |
||||
* Each tokener may be used to parse a single JSON string. 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 JSONTokener { |
||||
|
||||
/** |
||||
* The input JSON. |
||||
*/ |
||||
private final String in; |
||||
|
||||
/** |
||||
* The index of the next character to be returned by {@link #next}. When the input is |
||||
* exhausted, this equals the input's length. |
||||
*/ |
||||
private int pos; |
||||
|
||||
/** |
||||
* @param in JSON encoded string. Null is not permitted and will yield a tokener that |
||||
* throws {@code NullPointerExceptions} when methods are called. |
||||
*/ |
||||
public JSONTokener(String in) { |
||||
// consume an optional byte order mark (BOM) if it exists
|
||||
if (in != null && in.startsWith("\ufeff")) { |
||||
in = in.substring(1); |
||||
} |
||||
this.in = in; |
||||
} |
||||
|
||||
/** |
||||
* Returns the next value from the input. |
||||
* @return a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long, |
||||
* Double or {@link JSONObject#NULL}. |
||||
* @throws JSONException if the input is malformed. |
||||
*/ |
||||
public Object nextValue() throws JSONException { |
||||
int c = nextCleanInternal(); |
||||
switch (c) { |
||||
case -1: |
||||
throw syntaxError("End of input"); |
||||
|
||||
case '{': |
||||
return readObject(); |
||||
|
||||
case '[': |
||||
return readArray(); |
||||
|
||||
case '\'', '"': |
||||
return nextString((char) c); |
||||
|
||||
default: |
||||
this.pos--; |
||||
return readLiteral(); |
||||
} |
||||
} |
||||
|
||||
private int nextCleanInternal() throws JSONException { |
||||
while (this.pos < this.in.length()) { |
||||
int c = this.in.charAt(this.pos++); |
||||
switch (c) { |
||||
case '\t', ' ', '\n', '\r': |
||||
continue; |
||||
|
||||
case '/': |
||||
if (this.pos == this.in.length()) { |
||||
return c; |
||||
} |
||||
|
||||
char peek = this.in.charAt(this.pos); |
||||
switch (peek) { |
||||
case '*': |
||||
// skip a /* c-style comment */
|
||||
this.pos++; |
||||
int commentEnd = this.in.indexOf("*/", this.pos); |
||||
if (commentEnd == -1) { |
||||
throw syntaxError("Unterminated comment"); |
||||
} |
||||
this.pos = commentEnd + 2; |
||||
continue; |
||||
|
||||
case '/': |
||||
// skip a // end-of-line comment
|
||||
this.pos++; |
||||
skipToEndOfLine(); |
||||
continue; |
||||
|
||||
default: |
||||
return c; |
||||
} |
||||
|
||||
case '#': |
||||
/* |
||||
* Skip a # hash end-of-line comment. The JSON RFC doesn't specify |
||||
* this behavior, but it's required to parse existing documents. See |
||||
* https://b/2571423.
|
||||
*/ |
||||
skipToEndOfLine(); |
||||
continue; |
||||
|
||||
default: |
||||
return c; |
||||
} |
||||
} |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
/** |
||||
* Advances the position until after the next newline character. If the line is |
||||
* terminated by "\r\n", the '\n' must be consumed as whitespace by the caller. |
||||
*/ |
||||
private void skipToEndOfLine() { |
||||
for (; this.pos < this.in.length(); this.pos++) { |
||||
char c = this.in.charAt(this.pos); |
||||
if (c == '\r' || c == '\n') { |
||||
this.pos++; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the string up to but not including {@code quote}, unescaping any character |
||||
* escape sequences encountered along the way. The opening quote should have already |
||||
* been read. This consumes the closing quote, but does not include it in the returned |
||||
* string. |
||||
* @param quote either ' or ". |
||||
* @return the string up to but not including {@code quote} |
||||
* @throws NumberFormatException if any unicode escape sequences are malformed. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
public String nextString(char quote) throws JSONException { |
||||
/* |
||||
* For strings that are free of escape sequences, we can just extract the result |
||||
* as a substring of the input. But if we encounter an escape sequence, we need to |
||||
* use a StringBuilder to compose the result. |
||||
*/ |
||||
StringBuilder builder = null; |
||||
|
||||
/* the index of the first character not yet appended to the builder. */ |
||||
int start = this.pos; |
||||
|
||||
while (this.pos < this.in.length()) { |
||||
int c = this.in.charAt(this.pos++); |
||||
if (c == quote) { |
||||
if (builder == null) { |
||||
// a new string avoids leaking memory
|
||||
return new String(this.in.substring(start, this.pos - 1)); |
||||
} |
||||
else { |
||||
builder.append(this.in, start, this.pos - 1); |
||||
return builder.toString(); |
||||
} |
||||
} |
||||
|
||||
if (c == '\\') { |
||||
if (this.pos == this.in.length()) { |
||||
throw syntaxError("Unterminated escape sequence"); |
||||
} |
||||
if (builder == null) { |
||||
builder = new StringBuilder(); |
||||
} |
||||
builder.append(this.in, start, this.pos - 1); |
||||
builder.append(readEscapeCharacter()); |
||||
start = this.pos; |
||||
} |
||||
} |
||||
|
||||
throw syntaxError("Unterminated string"); |
||||
} |
||||
|
||||
/** |
||||
* Unescapes the character identified by the character or characters that immediately |
||||
* follow a backslash. The backslash '\' should have already been read. This supports |
||||
* both unicode escapes "u000A" and two-character escapes "\n". |
||||
* @return the unescaped char |
||||
* @throws NumberFormatException if any unicode escape sequences are malformed. |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private char readEscapeCharacter() throws JSONException { |
||||
char escaped = this.in.charAt(this.pos++); |
||||
switch (escaped) { |
||||
case 'u': |
||||
if (this.pos + 4 > this.in.length()) { |
||||
throw syntaxError("Unterminated escape sequence"); |
||||
} |
||||
String hex = this.in.substring(this.pos, this.pos + 4); |
||||
this.pos += 4; |
||||
return (char) Integer.parseInt(hex, 16); |
||||
|
||||
case 't': |
||||
return '\t'; |
||||
|
||||
case 'b': |
||||
return '\b'; |
||||
|
||||
case 'n': |
||||
return '\n'; |
||||
|
||||
case 'r': |
||||
return '\r'; |
||||
|
||||
case 'f': |
||||
return '\f'; |
||||
|
||||
case '\'', '"', '\\': |
||||
default: |
||||
return escaped; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Reads a null, boolean, numeric or unquoted string literal value. Numeric values |
||||
* will be returned as an Integer, Long, or Double, in that order of preference. |
||||
* @return a literal value |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private Object readLiteral() throws JSONException { |
||||
String literal = nextToInternal("{}[]/\\:,=;# \t\f"); |
||||
|
||||
if (literal.isEmpty()) { |
||||
throw syntaxError("Expected literal value"); |
||||
} |
||||
else if ("null".equalsIgnoreCase(literal)) { |
||||
return JSONObject.NULL; |
||||
} |
||||
else if ("true".equalsIgnoreCase(literal)) { |
||||
return Boolean.TRUE; |
||||
} |
||||
else if ("false".equalsIgnoreCase(literal)) { |
||||
return Boolean.FALSE; |
||||
} |
||||
|
||||
/* try to parse as an integral type... */ |
||||
if (literal.indexOf('.') == -1) { |
||||
int base = 10; |
||||
String number = literal; |
||||
if (number.startsWith("0x") || number.startsWith("0X")) { |
||||
number = number.substring(2); |
||||
base = 16; |
||||
} |
||||
else if (number.startsWith("0") && number.length() > 1) { |
||||
number = number.substring(1); |
||||
base = 8; |
||||
} |
||||
try { |
||||
long longValue = Long.parseLong(number, base); |
||||
if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { |
||||
return (int) longValue; |
||||
} |
||||
else { |
||||
return longValue; |
||||
} |
||||
} |
||||
catch (NumberFormatException e) { |
||||
/* |
||||
* This only happens for integral numbers greater than Long.MAX_VALUE, |
||||
* numbers in exponential form (5e-10) and unquoted strings. Fall through |
||||
* to try floating point. |
||||
*/ |
||||
} |
||||
} |
||||
|
||||
/* ...next try to parse as a floating point... */ |
||||
try { |
||||
return Double.valueOf(literal); |
||||
} |
||||
catch (NumberFormatException ex) { |
||||
// Ignore
|
||||
} |
||||
|
||||
/* ... finally give up. We have an unquoted string */ |
||||
return new String(literal); // a new string avoids leaking memory
|
||||
} |
||||
|
||||
/** |
||||
* Returns the string up to but not including any of the given characters or a newline |
||||
* character. This does not consume the excluded character. |
||||
* @return the string up to but not including any of the given characters or a newline |
||||
* character |
||||
*/ |
||||
private String nextToInternal(String excluded) { |
||||
int start = this.pos; |
||||
for (; this.pos < this.in.length(); this.pos++) { |
||||
char c = this.in.charAt(this.pos); |
||||
if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) { |
||||
return this.in.substring(start, this.pos); |
||||
} |
||||
} |
||||
return this.in.substring(start); |
||||
} |
||||
|
||||
/** |
||||
* Reads a sequence of key/value pairs and the trailing closing brace '}' of an |
||||
* object. The opening brace '{' should have already been read. |
||||
* @return an object |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private JSONObject readObject() throws JSONException { |
||||
JSONObject result = new JSONObject(); |
||||
|
||||
/* Peek to see if this is the empty object. */ |
||||
int first = nextCleanInternal(); |
||||
if (first == '}') { |
||||
return result; |
||||
} |
||||
else if (first != -1) { |
||||
this.pos--; |
||||
} |
||||
|
||||
while (true) { |
||||
Object name = nextValue(); |
||||
if (!(name instanceof String)) { |
||||
if (name == null) { |
||||
throw syntaxError("Names cannot be null"); |
||||
} |
||||
else { |
||||
throw syntaxError( |
||||
"Names must be strings, but " + name + " is of type " + name.getClass().getName()); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* Expect the name/value separator to be either a colon ':', an equals sign |
||||
* '=', or an arrow "=>". The last two are bogus but we include them because |
||||
* that's what the original implementation did. |
||||
*/ |
||||
int separator = nextCleanInternal(); |
||||
if (separator != ':' && separator != '=') { |
||||
throw syntaxError("Expected ':' after " + name); |
||||
} |
||||
if (this.pos < this.in.length() && this.in.charAt(this.pos) == '>') { |
||||
this.pos++; |
||||
} |
||||
|
||||
result.put((String) name, nextValue()); |
||||
|
||||
switch (nextCleanInternal()) { |
||||
case '}': |
||||
return result; |
||||
case ';', ',': |
||||
continue; |
||||
default: |
||||
throw syntaxError("Unterminated object"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Reads a sequence of values and the trailing closing brace ']' of an array. The |
||||
* opening brace '[' should have already been read. Note that "[]" yields an empty |
||||
* array, but "[,]" returns a two-element array equivalent to "[null,null]". |
||||
* @return an array |
||||
* @throws JSONException if processing of json failed |
||||
*/ |
||||
private JSONArray readArray() throws JSONException { |
||||
JSONArray result = new JSONArray(); |
||||
|
||||
/* to cover input that ends with ",]". */ |
||||
boolean hasTrailingSeparator = false; |
||||
|
||||
while (true) { |
||||
switch (nextCleanInternal()) { |
||||
case -1: |
||||
throw syntaxError("Unterminated array"); |
||||
case ']': |
||||
if (hasTrailingSeparator) { |
||||
result.put(null); |
||||
} |
||||
return result; |
||||
case ',', ';': |
||||
/* A separator without a value first means "null". */ |
||||
result.put(null); |
||||
hasTrailingSeparator = true; |
||||
continue; |
||||
default: |
||||
this.pos--; |
||||
} |
||||
|
||||
result.put(nextValue()); |
||||
|
||||
switch (nextCleanInternal()) { |
||||
case ']': |
||||
return result; |
||||
case ',', ';': |
||||
hasTrailingSeparator = true; |
||||
continue; |
||||
default: |
||||
throw syntaxError("Unterminated array"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns an exception containing the given message plus the current position and the |
||||
* entire input string. |
||||
* @param message the message |
||||
* @return an exception |
||||
*/ |
||||
public JSONException syntaxError(String message) { |
||||
return new JSONException(message + this); |
||||
} |
||||
|
||||
/** |
||||
* Returns the current position and the entire input string. |
||||
* @return the current position and the entire input string. |
||||
*/ |
||||
@Override |
||||
public String toString() { |
||||
// consistent with the original implementation
|
||||
return " at character " + this.pos + " of " + this.in; |
||||
} |
||||
|
||||
/* |
||||
* Legacy APIs. |
||||
* |
||||
* None of the methods below are on the critical path of parsing JSON documents. They |
||||
* exist only because they were exposed by the original implementation and may be used |
||||
* by some clients. |
||||
*/ |
||||
|
||||
public boolean more() { |
||||
return this.pos < this.in.length(); |
||||
} |
||||
|
||||
public char next() { |
||||
return this.pos < this.in.length() ? this.in.charAt(this.pos++) : '\0'; |
||||
} |
||||
|
||||
public char next(char c) throws JSONException { |
||||
char result = next(); |
||||
if (result != c) { |
||||
throw syntaxError("Expected " + c + " but was " + result); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public char nextClean() throws JSONException { |
||||
int nextCleanInt = nextCleanInternal(); |
||||
return nextCleanInt == -1 ? '\0' : (char) nextCleanInt; |
||||
} |
||||
|
||||
public String next(int length) throws JSONException { |
||||
if (this.pos + length > this.in.length()) { |
||||
throw syntaxError(length + " is out of bounds"); |
||||
} |
||||
String result = this.in.substring(this.pos, this.pos + length); |
||||
this.pos += length; |
||||
return result; |
||||
} |
||||
|
||||
public String nextTo(String excluded) { |
||||
if (excluded == null) { |
||||
throw new NullPointerException("excluded == null"); |
||||
} |
||||
return nextToInternal(excluded).trim(); |
||||
} |
||||
|
||||
public String nextTo(char excluded) { |
||||
return nextToInternal(String.valueOf(excluded)).trim(); |
||||
} |
||||
|
||||
public void skipPast(String thru) { |
||||
int thruStart = this.in.indexOf(thru, this.pos); |
||||
this.pos = thruStart == -1 ? this.in.length() : (thruStart + thru.length()); |
||||
} |
||||
|
||||
public char skipTo(char to) { |
||||
int index = this.in.indexOf(to, this.pos); |
||||
if (index != -1) { |
||||
this.pos = index; |
||||
return to; |
||||
} |
||||
else { |
||||
return '\0'; |
||||
} |
||||
} |
||||
|
||||
public void back() { |
||||
if (--this.pos == -1) { |
||||
this.pos = 0; |
||||
} |
||||
} |
||||
|
||||
public static int dehexchar(char hex) { |
||||
if (hex >= '0' && hex <= '9') { |
||||
return hex - '0'; |
||||
} |
||||
else if (hex >= 'A' && hex <= 'F') { |
||||
return hex - 'A' + 10; |
||||
} |
||||
else if (hex >= 'a' && hex <= 'f') { |
||||
return hex - 'a' + 10; |
||||
} |
||||
else { |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue