Commit 4568f14c authored by Stephane Nicoll's avatar Stephane Nicoll

Fix javadoc warnings

parent 966d4251
...@@ -17,100 +17,114 @@ ...@@ -17,100 +17,114 @@
package org.springframework.boot.configurationprocessor.json; package org.springframework.boot.configurationprocessor.json;
class JSON { class JSON {
/** /**
* Returns the input if it is a JSON-permissible value; throws otherwise. * Returns the input if it is a JSON-permissible value; throws otherwise.
*/ */
static double checkDouble(double d) throws JSONException { static double checkDouble(double d) throws JSONException {
if (Double.isInfinite(d) || Double.isNaN(d)) { if (Double.isInfinite(d) || Double.isNaN(d)) {
throw new JSONException("Forbidden numeric value: " + d); throw new JSONException("Forbidden numeric value: " + d);
} }
return d; return d;
} }
static Boolean toBoolean(Object value) { static Boolean toBoolean(Object value) {
if (value instanceof Boolean) { if (value instanceof Boolean) {
return (Boolean) value; return (Boolean) value;
} else if (value instanceof String) { }
String stringValue = (String) value; else if (value instanceof String) {
if ("true".equalsIgnoreCase(stringValue)) { String stringValue = (String) value;
return true; if ("true".equalsIgnoreCase(stringValue)) {
} else if ("false".equalsIgnoreCase(stringValue)) { return true;
return false; }
} else if ("false".equalsIgnoreCase(stringValue)) {
} return false;
return null; }
} }
return null;
}
static Double toDouble(Object value) { static Double toDouble(Object value) {
if (value instanceof Double) { if (value instanceof Double) {
return (Double) value; return (Double) value;
} else if (value instanceof Number) { }
return ((Number) value).doubleValue(); else if (value instanceof Number) {
} else if (value instanceof String) { return ((Number) value).doubleValue();
try { }
return Double.valueOf((String) value); else if (value instanceof String) {
} catch (NumberFormatException ignored) { try {
} return Double.valueOf((String) value);
} }
return null; catch (NumberFormatException ignored) {
} }
}
return null;
}
static Integer toInteger(Object value) { static Integer toInteger(Object value) {
if (value instanceof Integer) { if (value instanceof Integer) {
return (Integer) value; return (Integer) value;
} else if (value instanceof Number) { }
return ((Number) value).intValue(); else if (value instanceof Number) {
} else if (value instanceof String) { return ((Number) value).intValue();
try { }
return (int) Double.parseDouble((String) value); else if (value instanceof String) {
} catch (NumberFormatException ignored) { try {
} return (int) Double.parseDouble((String) value);
} }
return null; catch (NumberFormatException ignored) {
} }
}
return null;
}
static Long toLong(Object value) { static Long toLong(Object value) {
if (value instanceof Long) { if (value instanceof Long) {
return (Long) value; return (Long) value;
} else if (value instanceof Number) { }
return ((Number) value).longValue(); else if (value instanceof Number) {
} else if (value instanceof String) { return ((Number) value).longValue();
try { }
return (long) Double.parseDouble((String) value); else if (value instanceof String) {
} catch (NumberFormatException ignored) { try {
} return (long) Double.parseDouble((String) value);
} }
return null; catch (NumberFormatException ignored) {
} }
}
return null;
}
static String toString(Object value) { static String toString(Object value) {
if (value instanceof String) { if (value instanceof String) {
return (String) value; return (String) value;
} else if (value != null) { }
return String.valueOf(value); else if (value != null) {
} return String.valueOf(value);
return null; }
} return null;
}
public static JSONException typeMismatch(Object indexOrName, Object actual, public static JSONException typeMismatch(Object indexOrName, Object actual,
String requiredType) throws JSONException { String requiredType) throws JSONException {
if (actual == null) { if (actual == null) {
throw new JSONException("Value at " + indexOrName + " is null."); throw new JSONException("Value at " + indexOrName + " is null.");
} else { }
throw new JSONException("Value " + actual + " at " + indexOrName else {
+ " of type " + actual.getClass().getName() throw new JSONException("Value " + actual + " at " + indexOrName
+ " cannot be converted to " + requiredType); + " of type " + actual.getClass().getName()
} + " cannot be converted to " + requiredType);
} }
}
public static JSONException typeMismatch(Object actual, String requiredType) public static JSONException typeMismatch(Object actual, String requiredType)
throws JSONException { throws JSONException {
if (actual == null) { if (actual == null) {
throw new JSONException("Value is null."); throw new JSONException("Value is null.");
} else { }
throw new JSONException("Value " + actual else {
+ " of type " + actual.getClass().getName() throw new JSONException("Value " + actual
+ " cannot be converted to " + requiredType); + " of type " + actual.getClass().getName()
} + " cannot be converted to " + requiredType);
} }
}
} }
...@@ -48,568 +48,643 @@ import java.util.List; ...@@ -48,568 +48,643 @@ import java.util.List;
*/ */
public class JSONArray { public class JSONArray {
private final List<Object> values; private final List<Object> values;
/** /**
* Creates a {@code JSONArray} with no values. * Creates a {@code JSONArray} with no values.
*/ */
public JSONArray() { public JSONArray() {
values = new ArrayList<Object>(); this.values = new ArrayList<Object>();
} }
/** /**
* Creates a new {@code JSONArray} by copying all values from the given * Creates a new {@code JSONArray} by copying all values from the given
* collection. * collection.
* *
* @param copyFrom a collection whose values are of supported types. * @param copyFrom a collection whose values are of supported types.
* Unsupported values are not permitted and will yield an array in an * Unsupported values are not permitted and will yield an array in an
* inconsistent state. * inconsistent state.
*/ */
/* Accept a raw type for API compatibility */ /* Accept a raw type for API compatibility */
public JSONArray(Collection copyFrom) { public JSONArray(Collection copyFrom) {
this(); this();
if (copyFrom != null) { if (copyFrom != null) {
for (Iterator it = copyFrom.iterator(); it.hasNext();) { for (Iterator it = copyFrom.iterator(); it.hasNext(); ) {
put(JSONObject.wrap(it.next())); put(JSONObject.wrap(it.next()));
} }
} }
} }
/** /**
* Creates a new {@code JSONArray} with values from the next array in the * Creates a new {@code JSONArray} with values from the next array in the
* tokener. * tokener.
* *
* @param readFrom a tokener whose nextValue() method will yield a * @param readFrom a tokener whose nextValue() method will yield a
* {@code JSONArray}. * {@code JSONArray}.
* @throws JSONException if the parse fails or doesn't yield a * @throws JSONException if the parse fails or doesn't yield a
* {@code JSONArray}. * {@code JSONArray}.
*/ * @throws JSONException if processing of json failed
public JSONArray(JSONTokener readFrom) throws JSONException { */
/* 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. * 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) { Object object = readFrom.nextValue();
values = ((JSONArray) object).values; if (object instanceof JSONArray) {
} else { this.values = ((JSONArray) object).values;
throw JSON.typeMismatch(object, "JSONArray"); }
} 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. * Creates a new {@code JSONArray} with values from the JSON string.
* @throws JSONException if the parse fails or doesn't yield a {@code *
* JSONArray}. * @param json a JSON-encoded string containing an array.
*/ * @throws JSONException if the parse fails or doesn't yield a {@code
public JSONArray(String json) throws JSONException { * JSONArray}.
this(new JSONTokener(json)); */
} public JSONArray(String json) throws JSONException {
this(new JSONTokener(json));
/** }
* Creates a new {@code JSONArray} with values from the given primitive array.
*/ /**
public JSONArray(Object array) throws JSONException { * Creates a new {@code JSONArray} with values from the given primitive array.
if (!array.getClass().isArray()) { * @param array a primitive array
throw new JSONException("Not a primitive array: " + array.getClass()); * @throws JSONException if processing of json failed
} */
final int length = Array.getLength(array); public JSONArray(Object array) throws JSONException {
values = new ArrayList<Object>(length); if (!array.getClass().isArray()) {
for (int i = 0; i < length; ++i) { throw new JSONException("Not a primitive array: " + array.getClass());
put(JSONObject.wrap(Array.get(array, i))); }
} final int length = Array.getLength(array);
} this.values = new ArrayList<Object>(length);
for (int i = 0; i < length; ++i) {
/** put(JSONObject.wrap(Array.get(array, i)));
* Returns the number of values in this array. }
*/ }
public int length() {
return values.size(); /**
} * Returns the number of values in this array.
* @return the length of this array
/** */
* Appends {@code value} to the end of this array. public int length() {
* return this.values.size();
* @return this array. }
*/
public JSONArray put(boolean value) { /**
values.add(value); * Appends {@code value} to the end of this array.
return this; *
} * @param value the value
* @return this array.
/** */
* Appends {@code value} to the end of this array. public JSONArray put(boolean value) {
* this.values.add(value);
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or return this;
* {@link Double#isInfinite() infinities}. }
* @return this array.
*/ /**
public JSONArray put(double value) throws JSONException { * Appends {@code value} to the end of this array.
values.add(JSON.checkDouble(value)); *
return this; * @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
* Appends {@code value} to the end of this array. */
* public JSONArray put(double value) throws JSONException {
* @return this array. this.values.add(JSON.checkDouble(value));
*/ return this;
public JSONArray put(int value) { }
values.add(value);
return this; /**
} * Appends {@code value} to the end of this array.
* @param value the value
/** * @return this array.
* Appends {@code value} to the end of this array. */
* public JSONArray put(int value) {
* @return this array. this.values.add(value);
*/ return this;
public JSONArray put(long value) { }
values.add(value);
return this; /**
} * Appends {@code value} to the end of this array.
* @param value the value
/** * @return this array.
* Appends {@code value} to the end of this array. */
* public JSONArray put(long value) {
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, this.values.add(value);
* Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May return this;
* 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. * Appends {@code value} to the end of this array.
*/ *
public JSONArray put(Object value) { * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
values.add(value); * Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May
return this; * 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.
* 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 public JSONArray put(Object value) {
* index}, it will be replaced. this.values.add(value);
* return this;
* @return this array. }
*/
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
* Sets the value at {@code index} to {@code value}, null padding this array * @param value the value
* to the required length if necessary. If a value already exists at {@code * @return this array.
* index}, it will be replaced. * @throws JSONException if processing of json failed
* */
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or public JSONArray put(int index, boolean value) throws JSONException {
* {@link Double#isInfinite() infinities}. return put(index, (Boolean) value);
* @return this array. }
*/
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
* Sets the value at {@code index} to {@code value}, null padding this array * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
* to the required length if necessary. If a value already exists at {@code * {@link Double#isInfinite() infinities}.
* index}, it will be replaced. * @return this array.
* * @throws JSONException if processing of json failed
* @return this array. */
*/ public JSONArray put(int index, double value) throws JSONException {
public JSONArray put(int index, int value) throws JSONException { return put(index, (Double) value);
return put(index, (Integer) value); }
}
/**
/** * Sets the value at {@code index} to {@code value}, null padding this array
* 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
* to the required length if necessary. If a value already exists at {@code * index}, it will be replaced.
* index}, it will be replaced. * @param index the index to set the value to
* * @param value the value
* @return this array. * @return this array.
*/ * @throws JSONException if processing of json failed
public JSONArray put(int index, long value) throws JSONException { */
return put(index, (Long) value); 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 * Sets the value at {@code index} to {@code value}, null padding this array
* index}, it will be replaced. * to the required length if necessary. If a value already exists at {@code
* * index}, it will be replaced.
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * @param index the index to set the value to
* Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May * @param value the value
* not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() * @return this array.
* infinities}. * @throws JSONException if processing of json failed
* @return this array. */
*/ public JSONArray put(int index, long value) throws JSONException {
public JSONArray put(int index, Object value) throws JSONException { return put(index, (Long) value);
if (value instanceof Number) { }
// deviate from the original by checking all Numbers, not just floats & doubles
JSON.checkDouble(((Number) value).doubleValue()); /**
} * Sets the value at {@code index} to {@code value}, null padding this array
while (values.size() <= index) { * to the required length if necessary. If a value already exists at {@code
values.add(null); * index}, it will be replaced.
} * @param index the index to set the value to
values.set(index, value); * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
return this; * Integer, Long, Double, {@link JSONObject#NULL}, or {@code null}. May
} * not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
* infinities}.
/** * @return this array.
* Returns true if this array has no value at {@code index}, or if its value * @throws JSONException if processing of json failed
* is the {@code null} reference or {@link JSONObject#NULL}. */
*/ public JSONArray put(int index, Object value) throws JSONException {
public boolean isNull(int index) { if (value instanceof Number) {
Object value = opt(index); // deviate from the original by checking all Numbers, not just floats & doubles
return value == null || value == JSONObject.NULL; JSON.checkDouble(((Number) value).doubleValue());
} }
while (this.values.size() <= index) {
/** this.values.add(null);
* Returns the value at {@code index}. }
* this.values.set(index, value);
* @throws JSONException if this array has no value at {@code index}, or if return this;
* 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 { * Returns true if this array has no value at {@code index}, or if its value
try { * is the {@code null} reference or {@link JSONObject#NULL}.
Object value = values.get(index); * @param index the index to set the value to
if (value == null) { * @return true if this array has no value at {@code index}
throw new JSONException("Value at " + index + " is null."); */
} public boolean isNull(int index) {
return value; Object value = opt(index);
} catch (IndexOutOfBoundsException e) { return value == null || value == JSONObject.NULL;
throw new JSONException("Index " + index + " out of range [0.." + values.size() + ")"); }
}
} /**
* Returns the value at {@code index}.
/** * @param index the index to get the value from
* Returns the value at {@code index}, or null if the array has no value * @return the value at {@code index}.
* 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
public Object opt(int index) { * normally if the value is {@code JSONObject#NULL}.
if (index < 0 || index >= values.size()) { */
return null; public Object get(int index) throws JSONException {
} try {
return values.get(index); Object value = this.values.get(index);
} if (value == null) {
throw new JSONException("Value at " + index + " is null.");
/** }
* Removes and returns the value at {@code index}, or null if the array has no value return value;
* at {@code index}. }
*/ catch (IndexOutOfBoundsException e) {
public Object remove(int index) { throw new JSONException("Index " + index + " out of range [0.." + this.values.size() + ")");
if (index < 0 || index >= values.size()) { }
return null; }
}
return values.remove(index); /**
} * 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
* Returns the value at {@code index} if it exists and is a boolean or can * @return the value at {@code index} or {@code null}
* be coerced to a boolean. */
* public Object opt(int index) {
* @throws JSONException if the value at {@code index} doesn't exist or if (index < 0 || index >= this.values.size()) {
* cannot be coerced to a boolean. return null;
*/ }
public boolean getBoolean(int index) throws JSONException { return this.values.get(index);
Object object = get(index); }
Boolean result = JSON.toBoolean(object);
if (result == null) { /**
throw JSON.typeMismatch(index, object, "boolean"); * Removes and returns the value at {@code index}, or null if the array has no value
} * at {@code index}.
return result; * @param index the index of the value to remove
} * @return the previous value at {@code index}
*/
/** public Object remove(int index) {
* Returns the value at {@code index} if it exists and is a boolean or can if (index < 0 || index >= this.values.size()) {
* be coerced to a boolean. Returns false otherwise. return null;
*/ }
public boolean optBoolean(int index) { return this.values.remove(index);
return optBoolean(index, false); }
}
/**
/** * Returns the value at {@code index} if it exists and is a boolean or can
* Returns the value at {@code index} if it exists and is a boolean or can * be coerced to a boolean.
* be coerced to a boolean. Returns {@code fallback} otherwise. * @param index the index to get the value from
*/ * @return the value at {@code index}
public boolean optBoolean(int index, boolean fallback) { * @throws JSONException if the value at {@code index} doesn't exist or
Object object = opt(index); * cannot be coerced to a boolean.
Boolean result = JSON.toBoolean(object); */
return result != null ? result : fallback; public boolean getBoolean(int index) throws JSONException {
} Object object = get(index);
Boolean result = JSON.toBoolean(object);
/** if (result == null) {
* Returns the value at {@code index} if it exists and is a double or can throw JSON.typeMismatch(index, object, "boolean");
* be coerced to a double. }
* return result;
* @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 { * Returns the value at {@code index} if it exists and is a boolean or can
Object object = get(index); * be coerced to a boolean. Returns false otherwise.
Double result = JSON.toDouble(object); * @param index the index to get the value from
if (result == null) { * @return the {@code value} or {@code false}
throw JSON.typeMismatch(index, object, "double"); */
} public boolean optBoolean(int index) {
return result; return optBoolean(index, false);
} }
/** /**
* Returns the value at {@code index} if it exists and is a double or can * Returns the value at {@code index} if it exists and is a boolean or can
* be coerced to a double. Returns {@code NaN} otherwise. * be coerced to a boolean. Returns {@code fallback} otherwise.
*/ * @param index the index to get the value from
public double optDouble(int index) { * @param fallback the fallback value
return optDouble(index, Double.NaN); * @return the value at {@code index} of {@code fallback}
} */
public boolean optBoolean(int index, boolean fallback) {
/** Object object = opt(index);
* Returns the value at {@code index} if it exists and is a double or can Boolean result = JSON.toBoolean(object);
* be coerced to a double. Returns {@code fallback} otherwise. return result != null ? result : fallback;
*/ }
public double optDouble(int index, double fallback) {
Object object = opt(index); /**
Double result = JSON.toDouble(object); * Returns the value at {@code index} if it exists and is a double or can
return result != null ? result : fallback; * 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
* Returns the value at {@code index} if it exists and is an int or * cannot be coerced to a double.
* can be coerced to an int. */
* public double getDouble(int index) throws JSONException {
* @throws JSONException if the value at {@code index} doesn't exist or Object object = get(index);
* cannot be coerced to a int. Double result = JSON.toDouble(object);
*/ if (result == null) {
public int getInt(int index) throws JSONException { throw JSON.typeMismatch(index, object, "double");
Object object = get(index); }
Integer result = JSON.toInteger(object); return result;
if (result == null) { }
throw JSON.typeMismatch(index, object, "int");
} /**
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}
* Returns the value at {@code index} if it exists and is an int or */
* can be coerced to an int. Returns 0 otherwise. public double optDouble(int index) {
*/ return optDouble(index, Double.NaN);
public int optInt(int index) { }
return optInt(index, 0);
} /**
* Returns the value at {@code index} if it exists and is a double or can
/** * be coerced to a double. Returns {@code fallback} otherwise.
* Returns the value at {@code index} if it exists and is an int or * @param index the index to get the value from
* can be coerced to an int. Returns {@code fallback} otherwise. * @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); public double optDouble(int index, double fallback) {
Integer result = JSON.toInteger(object); Object object = opt(index);
return result != null ? result : fallback; Double result = JSON.toDouble(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. * Returns the value at {@code index} if it exists and is an int or
* * can be coerced to an int.
* @throws JSONException if the value at {@code index} doesn't exist or * @param index the index to get the value from
* cannot be coerced to a long. * @return the {@code value}
*/ * @throws JSONException if the value at {@code index} doesn't exist or
public long getLong(int index) throws JSONException { * cannot be coerced to a int.
Object object = get(index); */
Long result = JSON.toLong(object); public int getInt(int index) throws JSONException {
if (result == null) { Object object = get(index);
throw JSON.typeMismatch(index, object, "long"); Integer result = JSON.toInteger(object);
} if (result == null) {
return result; throw JSON.typeMismatch(index, object, "int");
} }
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. /**
*/ * Returns the value at {@code index} if it exists and is an int or
public long optLong(int index) { * can be coerced to an int. Returns 0 otherwise.
return optLong(index, 0L); * @param index the index to get the value from
} * @return the {@code value} or {@code 0}
*/
/** public int optInt(int index) {
* Returns the value at {@code index} if it exists and is a long or return optInt(index, 0);
* can be coerced to a long. Returns {@code fallback} otherwise. }
*/
public long optLong(int index, long fallback) { /**
Object object = opt(index); * Returns the value at {@code index} if it exists and is an int or
Long result = JSON.toLong(object); * can be coerced to an int. Returns {@code fallback} otherwise.
return result != null ? result : fallback; * @param index the index to get the value from
} * @param fallback the fallback value
* @return the value at {@code index} of {@code fallback}
/** */
* Returns the value at {@code index} if it exists, coercing it if public int optInt(int index, int fallback) {
* necessary. Object object = opt(index);
* Integer result = JSON.toInteger(object);
* @throws JSONException if no such value exists. return result != null ? result : fallback;
*/ }
public String getString(int index) throws JSONException {
Object object = get(index); /**
String result = JSON.toString(object); * Returns the value at {@code index} if it exists and is a long or
if (result == null) { * can be coerced to a long.
throw JSON.typeMismatch(index, object, "String"); * @param index the index to get the value from
} * @return the {@code value}
return result; *
} * @throws JSONException if the value at {@code index} doesn't exist or
* cannot be coerced to a long.
/** */
* Returns the value at {@code index} if it exists, coercing it if public long getLong(int index) throws JSONException {
* necessary. Returns the empty string if no such value exists. Object object = get(index);
*/ Long result = JSON.toLong(object);
public String optString(int index) { if (result == null) {
return optString(index, ""); throw JSON.typeMismatch(index, object, "long");
} }
return result;
/** }
* Returns the value at {@code index} if it exists, coercing it if
* necessary. Returns {@code fallback} if no such value exists. /**
*/ * Returns the value at {@code index} if it exists and is a long or
public String optString(int index, String fallback) { * can be coerced to a long. Returns 0 otherwise.
Object object = opt(index); * @param index the index to get the value from
String result = JSON.toString(object); * @return the {@code value} or {@code 0}
return result != null ? result : fallback; */
} public long optLong(int index) {
return optLong(index, 0L);
/** }
* Returns the value at {@code index} if it exists and is a {@code
* JSONArray}. /**
* * Returns the value at {@code index} if it exists and is a long or
* @throws JSONException if the value doesn't exist or is not a {@code * can be coerced to a long. Returns {@code fallback} otherwise.
* JSONArray}. * @param index the index to get the value from
*/ * @param fallback the fallback value
public JSONArray getJSONArray(int index) throws JSONException { * @return the value at {@code index} of {@code fallback}
Object object = get(index); */
if (object instanceof JSONArray) { public long optLong(int index, long fallback) {
return (JSONArray) object; Object object = opt(index);
} else { Long result = JSON.toLong(object);
throw JSON.typeMismatch(index, object, "JSONArray"); return result != null ? result : fallback;
} }
}
/**
/** * Returns the value at {@code index} if it exists, coercing it if
* Returns the value at {@code index} if it exists and is a {@code * necessary.
* JSONArray}. Returns null otherwise. * @param index the index to get the value from
*/ * @return the {@code value}
public JSONArray optJSONArray(int index) { * @throws JSONException if no such value exists.
Object object = opt(index); */
return object instanceof JSONArray ? (JSONArray) object : null; public String getString(int index) throws JSONException {
} Object object = get(index);
String result = JSON.toString(object);
/** if (result == null) {
* Returns the value at {@code index} if it exists and is a {@code throw JSON.typeMismatch(index, object, "String");
* JSONObject}. }
* return result;
* @throws JSONException if the value doesn't exist or is not a {@code }
* JSONObject}.
*/ /**
public JSONObject getJSONObject(int index) throws JSONException { * Returns the value at {@code index} if it exists, coercing it if
Object object = get(index); * necessary. Returns the empty string if no such value exists.
if (object instanceof JSONObject) { * @param index the index to get the value from
return (JSONObject) object; * @return the {@code value} or an empty string
} else { */
throw JSON.typeMismatch(index, object, "JSONObject"); public String optString(int index) {
} return optString(index, "");
} }
/** /**
* Returns the value at {@code index} if it exists and is a {@code * Returns the value at {@code index} if it exists, coercing it if
* JSONObject}. Returns null otherwise. * necessary. Returns {@code fallback} if no such value exists.
*/ * @param index the index to get the value from
public JSONObject optJSONObject(int index) { * @param fallback the fallback value
Object object = opt(index); * @return the value at {@code index} of {@code fallback}
return object instanceof JSONObject ? (JSONObject) object : null; */
} public String optString(int index, String fallback) {
Object object = opt(index);
/** String result = JSON.toString(object);
* Returns a new object whose values are the values in this array, and whose return result != null ? result : fallback;
* 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. * Returns the value at {@code index} if it exists and is a {@code
*/ * JSONArray}.
public JSONObject toJSONObject(JSONArray names) throws JSONException { * @param index the index to get the value from
JSONObject result = new JSONObject(); * @return the array at {@code index}
int length = Math.min(names.length(), values.size()); * @throws JSONException if the value doesn't exist or is not a {@code
if (length == 0) { * JSONArray}.
return null; */
} public JSONArray getJSONArray(int index) throws JSONException {
for (int i = 0; i < length; i++) { Object object = get(index);
String name = JSON.toString(names.opt(i)); if (object instanceof JSONArray) {
result.put(name, opt(i)); return (JSONArray) object;
} }
return result; else {
} throw JSON.typeMismatch(index, object, "JSONArray");
}
/** }
* 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" * Returns the value at {@code index} if it exists and is a {@code
* pizza', 'taco' and 'soda' joined on '+' returns this: * JSONArray}. Returns null otherwise.
* <pre>"12\" pizza"+"taco"+"soda"</pre> * @param index the index to get the value from
*/ * @return the array at {@code index} or {@code null}
public String join(String separator) throws JSONException { */
JSONStringer stringer = new JSONStringer(); public JSONArray optJSONArray(int index) {
stringer.open(JSONStringer.Scope.NULL, ""); Object object = opt(index);
for (int i = 0, size = values.size(); i < size; i++) { return object instanceof JSONArray ? (JSONArray) object : null;
if (i > 0) { }
stringer.out.append(separator);
} /**
stringer.value(values.get(i)); * Returns the value at {@code index} if it exists and is a {@code
} * JSONObject}.
stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); * @param index the index to get the value from
return stringer.out.toString(); * @return the object at {@code index}
} * @throws JSONException if the value doesn't exist or is not a {@code
* JSONObject}.
/** */
* Encodes this array as a compact JSON string, such as: public JSONObject getJSONObject(int index) throws JSONException {
* <pre>[94043,90210]</pre> Object object = get(index);
*/ if (object instanceof JSONObject) {
@Override public String toString() { return (JSONObject) object;
try { }
JSONStringer stringer = new JSONStringer(); else {
writeTo(stringer); throw JSON.typeMismatch(index, object, "JSONObject");
return stringer.toString(); }
} catch (JSONException e) { }
return null;
} /**
} * 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
* Encodes this array as a human readable JSON string for debugging, such * @return the object at {@code index} or {@code null}
* as: */
* <pre> public JSONObject optJSONObject(int index) {
* [ Object object = opt(index);
* 94043, return object instanceof JSONObject ? (JSONObject) object : null;
* 90210 }
* ]</pre>
* /**
* @param indentSpaces the number of spaces to indent for each level of * Returns a new object whose values are the values in this array, and whose
* nesting. * 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
public String toString(int indentSpaces) throws JSONException { * strings will be coerced to strings. This method returns null if either
JSONStringer stringer = new JSONStringer(indentSpaces); * array is empty.
writeTo(stringer); * @param names the property names
return stringer.toString(); * @return a json object
} * @throws JSONException if processing of json failed
*/
void writeTo(JSONStringer stringer) throws JSONException { public JSONObject toJSONObject(JSONArray names) throws JSONException {
stringer.array(); JSONObject result = new JSONObject();
for (Object value : values) { int length = Math.min(names.length(), this.values.size());
stringer.value(value); if (length == 0) {
} return null;
stringer.endArray(); }
} for (int i = 0; i < length; i++) {
String name = JSON.toString(names.opt(i));
@Override public boolean equals(Object o) { result.put(name, opt(i));
return o instanceof JSONArray && ((JSONArray) o).values.equals(values); }
} return result;
}
@Override public int hashCode() {
// diverge from the original, which doesn't implement hashCode /**
return values.hashCode(); * 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();
}
} }
...@@ -43,7 +43,7 @@ package org.springframework.boot.configurationprocessor.json; ...@@ -43,7 +43,7 @@ package org.springframework.boot.configurationprocessor.json;
*/ */
public class JSONException extends Exception { public class JSONException extends Exception {
public JSONException(String s) { public JSONException(String s) {
super(s); super(s);
} }
} }
...@@ -78,698 +78,792 @@ import java.util.Map; ...@@ -78,698 +78,792 @@ import java.util.Map;
*/ */
public class JSONObject { public class JSONObject {
private static final Double NEGATIVE_ZERO = -0d; private static final Double NEGATIVE_ZERO = -0d;
/** /**
* A sentinel value used to explicitly define a name with no value. Unlike * A sentinel value used to explicitly define a name with no value. Unlike
* {@code null}, names with this value: * {@code null}, names with this value:
* <ul> * <ul>
* <li>show up in the {@link #names} array * <li>show up in the {@link #names} array
* <li>show up in the {@link #keys} iterator * <li>show up in the {@link #keys} iterator
* <li>return {@code true} for {@link #has(String)} * <li>return {@code true} for {@link #has(String)}
* <li>do not throw on {@link #get(String)} * <li>do not throw on {@link #get(String)}
* <li>are included in the encoded JSON string. * <li>are included in the encoded JSON string.
* </ul> * </ul>
* *
* <p>This value violates the general contract of {@link Object#equals} by * <p>This value violates the general contract of {@link Object#equals} by
* returning true when compared to {@code null}. Its {@link #toString} * returning true when compared to {@code null}. Its {@link #toString}
* method returns "null". * method returns "null".
*/ */
public static final Object NULL = new Object() { public static final Object NULL = new Object() {
@Override public boolean equals(Object o) { @Override
return o == this || o == null; // API specifies this broken equals implementation public boolean equals(Object o) {
} return o == this || o == null; // API specifies this broken equals implementation
@Override public String toString() { }
return "null";
} @Override
}; public String toString() {
return "null";
private final Map<String, Object> nameValuePairs; }
};
/**
* Creates a {@code JSONObject} with no name/value mappings. private final Map<String, Object> nameValuePairs;
*/
public JSONObject() { /**
nameValuePairs = new HashMap<String, Object>(); * Creates a {@code JSONObject} with no name/value mappings.
} */
public JSONObject() {
/** this.nameValuePairs = new HashMap<String, Object>();
* 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 * Creates a new {@code JSONObject} by copying all name/value mappings from
* values are of supported types. * the given map.
* @throws NullPointerException if any of the map's keys are null. *
*/ * @param copyFrom a map whose keys are of type {@link String} and whose
/* (accept a raw type for API compatibility) */ * values are of supported types.
public JSONObject(Map copyFrom) { * @throws NullPointerException if any of the map's keys are null.
this(); */
Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom; /* (accept a raw type for API compatibility) */
for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) { public JSONObject(Map copyFrom) {
/* this();
* Deviate from the original by checking that keys are non-null and Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom;
* of the proper type. (We still defer validating the values). for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) {
*/ /*
String key = (String) entry.getKey(); * Deviate from the original by checking that keys are non-null and
if (key == null) { * of the proper type. (We still defer validating the values).
throw new NullPointerException("key == null"); */
} String key = (String) entry.getKey();
nameValuePairs.put(key, wrap(entry.getValue())); 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 * Creates a new {@code JSONObject} with name/value mappings from the next
* {@code JSONObject}. * object in the tokener.
* @throws JSONException if the parse fails or doesn't yield a *
* {@code JSONObject}. * @param readFrom a tokener whose nextValue() method will yield a
*/ * {@code JSONObject}.
public JSONObject(JSONTokener readFrom) throws JSONException { * @throws JSONException if the parse fails or doesn't yield a
/* * {@code JSONObject}.
* Getting the parser to populate this could get tricky. Instead, just */
* parse to temporary JSONObject and then steal the data from that. public JSONObject(JSONTokener readFrom) throws JSONException {
*/ /*
Object object = readFrom.nextValue(); * Getting the parser to populate this could get tricky. Instead, just
if (object instanceof JSONObject) { * parse to temporary JSONObject and then steal the data from that.
this.nameValuePairs = ((JSONObject) object).nameValuePairs; */
} else { Object object = readFrom.nextValue();
throw JSON.typeMismatch(object, "JSONObject"); 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 * Creates a new {@code JSONObject} with name/value mappings from the JSON
* JSONObject}. * string.
*/ *
public JSONObject(String json) throws JSONException { * @param json a JSON-encoded string containing an object.
this(new JSONTokener(json)); * @throws JSONException if the parse fails or doesn't yield a {@code
} * JSONObject}.
*/
/** public JSONObject(String json) throws JSONException {
* Creates a new {@code JSONObject} by copying mappings for the listed names this(new JSONTokener(json));
* from the given object. Names that aren't present in {@code copyFrom} will }
* be skipped.
*/ /**
public JSONObject(JSONObject copyFrom, String[] names) throws JSONException { * Creates a new {@code JSONObject} by copying mappings for the listed names
this(); * from the given object. Names that aren't present in {@code copyFrom} will
for (String name : names) { * be skipped.
Object value = copyFrom.opt(name); * @param copyFrom the source
if (value != null) { * @param names the property names
nameValuePairs.put(name, value); * @throws JSONException if an error occurs
} */
} public JSONObject(JSONObject copyFrom, String[] names) throws JSONException {
} this();
for (String name : names) {
/** Object value = copyFrom.opt(name);
* Returns the number of name/value mappings in this object. if (value != null) {
*/ this.nameValuePairs.put(name, value);
public int length() { }
return nameValuePairs.size(); }
} }
/** /**
* Maps {@code name} to {@code value}, clobbering any existing name/value * Returns the number of name/value mappings in this object.
* mapping with the same name. * @return the number of name/value mappings in this object
* */
* @return this object. public int length() {
*/ return this.nameValuePairs.size();
public JSONObject put(String name, boolean value) throws JSONException { }
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
* Maps {@code name} to {@code value}, clobbering any existing name/value * @param value the value of the property
* mapping with the same name. * @return this object.
* * @throws JSONException if an error occurs
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or */
* {@link Double#isInfinite() infinities}. public JSONObject put(String name, boolean value) throws JSONException {
* @return this object. this.nameValuePairs.put(checkName(name), value);
*/ return this;
public JSONObject put(String name, double value) throws JSONException { }
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.
/** *
* Maps {@code name} to {@code value}, clobbering any existing name/value * @param name the name of the property
* mapping with the same name. * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
* * {@link Double#isInfinite() infinities}.
* @return this object. * @return this object.
*/ * @throws JSONException if an error occurs
public JSONObject put(String name, int value) throws JSONException { */
nameValuePairs.put(checkName(name), value); public JSONObject put(String name, double value) throws JSONException {
return this; 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. * Maps {@code name} to {@code value}, clobbering any existing name/value
* * mapping with the same name.
* @return this object. *
*/ * @param name the name of the property
public JSONObject put(String name, long value) throws JSONException { * @param value the value of the property
nameValuePairs.put(checkName(name), value); * @return this object.
return this; * @throws JSONException if an error occurs
} */
public JSONObject put(String name, int value) throws JSONException {
/** this.nameValuePairs.put(checkName(name), value);
* Maps {@code name} to {@code value}, clobbering any existing name/value return this;
* mapping with the same name. If the value is {@code null}, any existing }
* mapping for {@code name} is removed.
* /**
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, * Maps {@code name} to {@code value}, clobbering any existing name/value
* Integer, Long, Double, {@link #NULL}, or {@code null}. May not be * mapping with the same name.
* {@link Double#isNaN() NaNs} or {@link Double#isInfinite() *
* infinities}. * @param name the name of the property
* @return this object. * @param value the value of the property
*/ * @return this object.
public JSONObject put(String name, Object value) throws JSONException { * @throws JSONException if an error occurs
if (value == null) { */
nameValuePairs.remove(name); public JSONObject put(String name, long value) throws JSONException {
return this; this.nameValuePairs.put(checkName(name), value);
} return this;
if (value instanceof Number) { }
// deviate from the original by checking all Numbers, not just floats & doubles
JSON.checkDouble(((Number) value).doubleValue()); /**
} * Maps {@code name} to {@code value}, clobbering any existing name/value
nameValuePairs.put(checkName(name), value); * mapping with the same name. If the value is {@code null}, any existing
return this; * mapping for {@code name} is removed.
} *
* @param name the name of the property
/** * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
* Equivalent to {@code put(name, value)} when both parameters are non-null; * Integer, Long, Double, {@link #NULL}, or {@code null}. May not be
* does nothing otherwise. * {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
*/ * infinities}.
public JSONObject putOpt(String name, Object value) throws JSONException { * @return this object.
if (name == null || value == null) { * @throws JSONException if an error occurs
return this; */
} public JSONObject put(String name, Object value) throws JSONException {
return put(name, value); if (value == null) {
} this.nameValuePairs.remove(name);
return this;
/** }
* Appends {@code value} to the array already mapped to {@code name}. If if (value instanceof Number) {
* this object has no mapping for {@code name}, this inserts a new mapping. // deviate from the original by checking all Numbers, not just floats & doubles
* If the mapping exists but its value is not an array, the existing JSON.checkDouble(((Number) value).doubleValue());
* 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 this.nameValuePairs.put(checkName(name), value);
* mapping one at a time. return this;
* }
* @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}. * Equivalent to {@code put(name, value)} when both parameters are non-null;
*/ * does nothing otherwise.
public JSONObject accumulate(String name, Object value) throws JSONException { * @param name the name of the property
Object current = nameValuePairs.get(checkName(name)); * @param value the value of the property
if (current == null) { * @return this object.
return put(name, value); * @throws JSONException if an error occurs
} */
public JSONObject putOpt(String name, Object value) throws JSONException {
// check in accumulate, since array.put(Object) doesn't do any checking if (name == null || value == null) {
if (value instanceof Number) { return this;
JSON.checkDouble(((Number) value).doubleValue()); }
} return put(name, value);
}
if (current instanceof JSONArray) {
JSONArray array = (JSONArray) current; /**
array.put(value); * Appends {@code value} to the array already mapped to {@code name}. If
} else { * this object has no mapping for {@code name}, this inserts a new mapping.
JSONArray array = new JSONArray(); * If the mapping exists but its value is not an array, the existing
array.put(current); * and new values are inserted in order into a new array which is itself
array.put(value); * mapped to {@code name}. In aggregate, this allows values to be added to a
nameValuePairs.put(name, array); * mapping one at a time.
} *
return this; * @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
String checkName(String name) throws JSONException { * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}.
if (name == null) { * @return this object.
throw new JSONException("Names must be non-null"); * @throws JSONException if an error occurs
} */
return name; public JSONObject accumulate(String name, Object value) throws JSONException {
} Object current = this.nameValuePairs.get(checkName(name));
if (current == null) {
/** return put(name, value);
* Removes the named mapping if it exists; does nothing otherwise. }
*
* @return the value previously mapped by {@code name}, or null if there was // check in accumulate, since array.put(Object) doesn't do any checking
* no such mapping. if (value instanceof Number) {
*/ JSON.checkDouble(((Number) value).doubleValue());
public Object remove(String name) { }
return nameValuePairs.remove(name);
} if (current instanceof JSONArray) {
JSONArray array = (JSONArray) current;
/** array.put(value);
* Returns true if this object has no mapping for {@code name} or if it has }
* a mapping whose value is {@link #NULL}. else {
*/ JSONArray array = new JSONArray();
public boolean isNull(String name) { array.put(current);
Object value = nameValuePairs.get(name); array.put(value);
return value == null || value == NULL; this.nameValuePairs.put(name, array);
} }
return this;
/** }
* Returns true if this object has a mapping for {@code name}. The mapping
* may be {@link #NULL}. String checkName(String name) throws JSONException {
*/ if (name == null) {
public boolean has(String name) { throw new JSONException("Names must be non-null");
return nameValuePairs.containsKey(name); }
} return name;
}
/**
* Returns the value mapped by {@code name}. /**
* * Removes the named mapping if it exists; does nothing otherwise.
* @throws JSONException if no such mapping exists. *
*/ * @param name the name of the property
public Object get(String name) throws JSONException { * @return the value previously mapped by {@code name}, or null if there was
Object result = nameValuePairs.get(name); * no such mapping.
if (result == null) { */
throw new JSONException("No value for " + name); public Object remove(String name) {
} return this.nameValuePairs.remove(name);
return result; }
}
/**
/** * Returns true if this object has no mapping for {@code name} or if it has
* Returns the value mapped by {@code name}, or null if no such mapping * a mapping whose value is {@link #NULL}.
* exists. * @param name the name of the property
*/ * @return true if this object has no mapping for {@code name}
public Object opt(String name) { */
return nameValuePairs.get(name); public boolean isNull(String name) {
} Object value = this.nameValuePairs.get(name);
return value == null || value == NULL;
/** }
* Returns the value mapped by {@code name} if it exists and is a boolean or
* can be coerced to a boolean. /**
* * Returns true if this object has a mapping for {@code name}. The mapping
* @throws JSONException if the mapping doesn't exist or cannot be coerced * may be {@link #NULL}.
* to a boolean. * @param name the name of the property
*/ * @return true if this object has a mapping for {@code name}
public boolean getBoolean(String name) throws JSONException { */
Object object = get(name); public boolean has(String name) {
Boolean result = JSON.toBoolean(object); return this.nameValuePairs.containsKey(name);
if (result == null) { }
throw JSON.typeMismatch(name, object, "boolean");
} /**
return result; * Returns the value mapped by {@code name}.
} * @param name the name of the property
* @return the value
/** * @throws JSONException if no such mapping exists.
* Returns the value mapped by {@code name} if it exists and is a boolean or */
* can be coerced to a boolean. Returns false otherwise. public Object get(String name) throws JSONException {
*/ Object result = this.nameValuePairs.get(name);
public boolean optBoolean(String name) { if (result == null) {
return optBoolean(name, false); throw new JSONException("No value for " + name);
} }
return result;
/** }
* 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. /**
*/ * Returns the value mapped by {@code name}, or null if no such mapping
public boolean optBoolean(String name, boolean fallback) { * exists.
Object object = opt(name); * @param name the name of the property
Boolean result = JSON.toBoolean(object); * @return the value or {@code null}
return result != null ? result : fallback; */
} public Object opt(String name) {
return this.nameValuePairs.get(name);
/** }
* Returns the value mapped by {@code name} if it exists and is a double or
* can be coerced to a double. /**
* * Returns the value mapped by {@code name} if it exists and is a boolean or
* @throws JSONException if the mapping doesn't exist or cannot be coerced * can be coerced to a boolean.
* to a double. *
*/ * @param name the name of the property
public double getDouble(String name) throws JSONException { * @return the value
Object object = get(name); * @throws JSONException if the mapping doesn't exist or cannot be coerced
Double result = JSON.toDouble(object); * to a boolean.
if (result == null) { */
throw JSON.typeMismatch(name, object, "double"); public boolean getBoolean(String name) throws JSONException {
} Object object = get(name);
return result; Boolean result = JSON.toBoolean(object);
} if (result == null) {
throw JSON.typeMismatch(name, object, "boolean");
/** }
* Returns the value mapped by {@code name} if it exists and is a double or return result;
* can be coerced to a double. Returns {@code NaN} otherwise. }
*/
public double optDouble(String name) { /**
return optDouble(name, Double.NaN); * 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}
* 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. public boolean optBoolean(String name) {
*/ return optBoolean(name, false);
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 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
* Returns the value mapped by {@code name} if it exists and is an int or * @return the value or {@code fallback}
* can be coerced to an int. */
* public boolean optBoolean(String name, boolean fallback) {
* @throws JSONException if the mapping doesn't exist or cannot be coerced Object object = opt(name);
* to an int. Boolean result = JSON.toBoolean(object);
*/ return result != null ? result : fallback;
public int getInt(String name) throws JSONException { }
Object object = get(name);
Integer result = JSON.toInteger(object); /**
if (result == null) { * Returns the value mapped by {@code name} if it exists and is a double or
throw JSON.typeMismatch(name, object, "int"); * can be coerced to a double.
} *
return result; * @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.
* Returns the value mapped by {@code name} if it exists and is an int or */
* can be coerced to an int. Returns 0 otherwise. public double getDouble(String name) throws JSONException {
*/ Object object = get(name);
public int optInt(String name) { Double result = JSON.toDouble(object);
return optInt(name, 0); if (result == null) {
} throw JSON.typeMismatch(name, object, "double");
}
/** return result;
* 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.
*/ /**
public int optInt(String name, int fallback) { * Returns the value mapped by {@code name} if it exists and is a double or
Object object = opt(name); * can be coerced to a double. Returns {@code NaN} otherwise.
Integer result = JSON.toInteger(object); * @param name the name of the property
return result != null ? result : fallback; * @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 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 via JSON. /**
* * Returns the value mapped by {@code name} if it exists and is a double or
* @throws JSONException if the mapping doesn't exist or cannot be coerced * can be coerced to a double. Returns {@code fallback} otherwise.
* to a long. * @param name the name of the property
*/ * @param fallback a fallback value
public long getLong(String name) throws JSONException { * @return the value or {@code fallback}
Object object = get(name); */
Long result = JSON.toLong(object); public double optDouble(String name, double fallback) {
if (result == null) { Object object = opt(name);
throw JSON.typeMismatch(name, object, "long"); Double result = JSON.toDouble(object);
} return result != null ? result : fallback;
return result; }
}
/**
/** * Returns the value mapped by {@code name} if it exists and is an int or
* Returns the value mapped by {@code name} if it exists and is a long or * can be coerced to an int.
* can be coerced to a long. Returns 0 otherwise. Note that JSON represents numbers as doubles, * @param name the name of the property
* so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via JSON. * @return the value
*/ * @throws JSONException if the mapping doesn't exist or cannot be coerced
public long optLong(String name) { * to an int.
return optLong(name, 0L); */
} public int getInt(String name) throws JSONException {
Object object = get(name);
/** Integer result = JSON.toInteger(object);
* Returns the value mapped by {@code name} if it exists and is a long or if (result == null) {
* can be coerced to a long. Returns {@code fallback} otherwise. Note that JSON represents throw JSON.typeMismatch(name, object, "int");
* numbers as doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer }
* numbers via JSON. return result;
*/ }
public long optLong(String name, long fallback) {
Object object = opt(name); /**
Long result = JSON.toLong(object); * Returns the value mapped by {@code name} if it exists and is an int or
return result != null ? result : fallback; * can be coerced to an int. Returns 0 otherwise.
} * @param name the name of the property
* @return the value of {@code 0}
/** */
* Returns the value mapped by {@code name} if it exists, coercing it if public int optInt(String name) {
* necessary. return optInt(name, 0);
* }
* @throws JSONException if no such mapping exists.
*/ /**
public String getString(String name) throws JSONException { * Returns the value mapped by {@code name} if it exists and is an int or
Object object = get(name); * can be coerced to an int. Returns {@code fallback} otherwise.
String result = JSON.toString(object); * @param name the name of the property
if (result == null) { * @param fallback a fallback value
throw JSON.typeMismatch(name, object, "String"); * @return the value or {@code fallback}
} */
return result; 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, coercing it if }
* necessary. Returns the empty string if no such mapping exists.
*/ /**
public String optString(String name) { * Returns the value mapped by {@code name} if it exists and is a long or
return optString(name, ""); * 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 via JSON.
*
/** * @param name the name of the property
* Returns the value mapped by {@code name} if it exists, coercing it if * @return the value
* necessary. Returns {@code fallback} if no such mapping exists. * @throws JSONException if the mapping doesn't exist or cannot be coerced
*/ * to a long.
public String optString(String name, String fallback) { */
Object object = opt(name); public long getLong(String name) throws JSONException {
String result = JSON.toString(object); Object object = get(name);
return result != null ? result : fallback; Long result = JSON.toLong(object);
} if (result == null) {
throw JSON.typeMismatch(name, object, "long");
/** }
* Returns the value mapped by {@code name} if it exists and is a {@code return result;
* JSONArray}. }
*
* @throws JSONException if the mapping doesn't exist or is not a {@code /**
* JSONArray}. * 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,
public JSONArray getJSONArray(String name) throws JSONException { * so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via JSON.
Object object = get(name); * @param name the name of the property
if (object instanceof JSONArray) { * @return the value or {@code 0L}
return (JSONArray) object; */
} else { public long optLong(String name) {
throw JSON.typeMismatch(name, object, "JSONArray"); return optLong(name, 0L);
} }
}
/**
/** * Returns the value mapped by {@code name} if it exists and is a long or
* Returns the value mapped by {@code name} if it exists and is a {@code * can be coerced to a long. Returns {@code fallback} otherwise. Note that JSON represents
* JSONArray}. Returns null otherwise. * numbers as doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer
*/ * numbers via JSON.
public JSONArray optJSONArray(String name) { * @param name the name of the property
Object object = opt(name); * @param fallback a fallback value
return object instanceof JSONArray ? (JSONArray) object : null; * @return the value or {@code fallback}
} */
public long optLong(String name, long fallback) {
/** Object object = opt(name);
* Returns the value mapped by {@code name} if it exists and is a {@code Long result = JSON.toLong(object);
* JSONObject}. return result != null ? result : fallback;
* }
* @throws JSONException if the mapping doesn't exist or is not a {@code
* JSONObject}. /**
*/ * Returns the value mapped by {@code name} if it exists, coercing it if
public JSONObject getJSONObject(String name) throws JSONException { * necessary.
Object object = get(name); * @param name the name of the property
if (object instanceof JSONObject) { * @return the value
return (JSONObject) object; * @throws JSONException if no such mapping exists.
} else { */
throw JSON.typeMismatch(name, object, "JSONObject"); 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");
* Returns the value mapped by {@code name} if it exists and is a {@code }
* JSONObject}. Returns null otherwise. return result;
*/ }
public JSONObject optJSONObject(String name) {
Object object = opt(name); /**
return object instanceof JSONObject ? (JSONObject) object : null; * 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
* Returns an array with the values corresponding to {@code names}. The */
* array contains null for names that aren't mapped. This method returns public String optString(String name) {
* null if {@code names} is either null or empty. return optString(name, "");
*/ }
public JSONArray toJSONArray(JSONArray names) throws JSONException {
JSONArray result = new JSONArray(); /**
if (names == null) { * Returns the value mapped by {@code name} if it exists, coercing it if
return null; * necessary. Returns {@code fallback} if no such mapping exists.
} * @param name the name of the property
int length = names.length(); * @param fallback a fallback value
if (length == 0) { * @return the value or {@code fallback}
return null; */
} public String optString(String name, String fallback) {
for (int i = 0; i < length; i++) { Object object = opt(name);
String name = JSON.toString(names.opt(i)); String result = JSON.toString(object);
result.put(opt(name)); return result != null ? result : fallback;
} }
return result;
} /**
* Returns the value mapped by {@code name} if it exists and is a {@code
/** * JSONArray}.
* Returns an iterator of the {@code String} names in this object. The * @param name the name of the property
* returned iterator supports {@link Iterator#remove() remove}, which will * @return the value
* remove the corresponding mapping from this object. If this object is * @throws JSONException if the mapping doesn't exist or is not a {@code
* modified after the iterator is returned, the iterator's behavior is * JSONArray}.
* undefined. The order of the keys is undefined. */
*/ public JSONArray getJSONArray(String name) throws JSONException {
/* Return a raw type for API compatibility */ Object object = get(name);
public Iterator keys() { if (object instanceof JSONArray) {
return nameValuePairs.keySet().iterator(); return (JSONArray) object;
} }
else {
/** throw JSON.typeMismatch(name, object, "JSONArray");
* Returns an array containing the string names in this object. This method }
* returns null if this object contains no mappings. }
*/
public JSONArray names() { /**
return nameValuePairs.isEmpty() * Returns the value mapped by {@code name} if it exists and is a {@code
? null * JSONArray}. Returns null otherwise.
: new JSONArray(new ArrayList<String>(nameValuePairs.keySet())); * @param name the name of the property
} * @return the value or {@code null}
*/
/** public JSONArray optJSONArray(String name) {
* Encodes this object as a compact JSON string, such as: Object object = opt(name);
* <pre>{"query":"Pizza","locations":[94043,90210]}</pre> return object instanceof JSONArray ? (JSONArray) object : null;
*/ }
@Override public String toString() {
try { /**
JSONStringer stringer = new JSONStringer(); * Returns the value mapped by {@code name} if it exists and is a {@code
writeTo(stringer); * JSONObject}.
return stringer.toString(); * @param name the name of the property
} catch (JSONException e) { * @return the value
return null; * @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);
* Encodes this object as a human readable JSON string for debugging, such if (object instanceof JSONObject) {
* as: return (JSONObject) object;
* <pre> }
* { else {
* "query": "Pizza", throw JSON.typeMismatch(name, object, "JSONObject");
* "locations": [ }
* 94043, }
* 90210
* ] /**
* }</pre> * Returns the value mapped by {@code name} if it exists and is a {@code
* * JSONObject}. Returns null otherwise.
* @param indentSpaces the number of spaces to indent for each level of * @param name the name of the property
* nesting. * @return the value or {@code null}
*/ */
public String toString(int indentSpaces) throws JSONException { public JSONObject optJSONObject(String name) {
JSONStringer stringer = new JSONStringer(indentSpaces); Object object = opt(name);
writeTo(stringer); return object instanceof JSONObject ? (JSONObject) object : null;
return stringer.toString(); }
}
/**
void writeTo(JSONStringer stringer) throws JSONException { * Returns an array with the values corresponding to {@code names}. The
stringer.object(); * array contains null for names that aren't mapped. This method returns
for (Map.Entry<String, Object> entry : nameValuePairs.entrySet()) { * null if {@code names} is either null or empty.
stringer.key(entry.getKey()).value(entry.getValue()); * @param names the names of the properties
} * @return the array
stringer.endObject(); */
} public JSONArray toJSONArray(JSONArray names) {
JSONArray result = new JSONArray();
/** if (names == null) {
* Encodes the number as a JSON string. return null;
* }
* @param number a finite value. May not be {@link Double#isNaN() NaNs} or int length = names.length();
* {@link Double#isInfinite() infinities}. if (length == 0) {
*/ return null;
public static String numberToString(Number number) throws JSONException { }
if (number == null) { for (int i = 0; i < length; i++) {
throw new JSONException("Number must be non-null"); String name = JSON.toString(names.opt(i));
} result.put(opt(name));
}
double doubleValue = number.doubleValue(); return result;
JSON.checkDouble(doubleValue); }
// the original returns "-0" instead of "-0.0" for negative zero /**
if (number.equals(NEGATIVE_ZERO)) { * Returns an iterator of the {@code String} names in this object. The
return "-0"; * 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
long longValue = number.longValue(); * undefined. The order of the keys is undefined.
if (doubleValue == (double) longValue) { * @return the keys
return Long.toString(longValue); */
} /* Return a raw type for API compatibility */
public Iterator keys() {
return number.toString(); return this.nameValuePairs.keySet().iterator();
} }
/** /**
* Encodes {@code data} as a JSON string. This applies quotes and any * Returns an array containing the string names in this object. This method
* necessary character escaping. * returns null if this object contains no mappings.
* * @return the array
* @param data the string to encode. Null will be interpreted as an empty */
* string. public JSONArray names() {
*/ return this.nameValuePairs.isEmpty()
public static String quote(String data) { ? null
if (data == null) { : new JSONArray(new ArrayList<String>(this.nameValuePairs.keySet()));
return "\"\""; }
}
try { /**
JSONStringer stringer = new JSONStringer(); * Encodes this object as a compact JSON string, such as:
stringer.open(JSONStringer.Scope.NULL, ""); * <pre>{"query":"Pizza","locations":[94043,90210]}</pre>
stringer.value(data); * @return a string representation of the object.
stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); */
return stringer.toString(); @Override
} catch (JSONException e) { public String toString() {
throw new AssertionError(); try {
} JSONStringer stringer = new JSONStringer();
} writeTo(stringer);
return stringer.toString();
/** }
* Wraps the given object if necessary. catch (JSONException e) {
* return null;
* <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}. * Encodes this object as a human readable JSON string for debugging, such
* If the object is a primitive wrapper type or {@code String}, returns the object. * as:
* Otherwise if the object is from a {@code java} package, returns the result of {@code toString}. * <pre>
* If wrapping fails, returns null. * {
*/ * "query": "Pizza",
public static Object wrap(Object o) { * "locations": [
if (o == null) { * 94043,
return NULL; * 90210
} * ]
if (o instanceof JSONArray || o instanceof JSONObject) { * }</pre>
return o; *
} * @param indentSpaces the number of spaces to indent for each level of
if (o.equals(NULL)) { * nesting.
return o; * @return a string representation of the object.
} * @throws JSONException if an error occurs
try { */
if (o instanceof Collection) { public String toString(int indentSpaces) throws JSONException {
return new JSONArray((Collection) o); JSONStringer stringer = new JSONStringer(indentSpaces);
} else if (o.getClass().isArray()) { writeTo(stringer);
return new JSONArray(o); return stringer.toString();
} }
if (o instanceof Map) {
return new JSONObject((Map) o); void writeTo(JSONStringer stringer) throws JSONException {
} stringer.object();
if (o instanceof Boolean || for (Map.Entry<String, Object> entry : this.nameValuePairs.entrySet()) {
o instanceof Byte || stringer.key(entry.getKey()).value(entry.getValue());
o instanceof Character || }
o instanceof Double || stringer.endObject();
o instanceof Float || }
o instanceof Integer ||
o instanceof Long || /**
o instanceof Short || * Encodes the number as a JSON string.
o instanceof String) { *
return o; * @param number a finite value. May not be {@link Double#isNaN() NaNs} or
} * {@link Double#isInfinite() infinities}.
if (o.getClass().getPackage().getName().startsWith("java.")) { * @return the encoded value
return o.toString(); * @throws JSONException if an error occurs
} */
} catch (Exception ignored) { public static String numberToString(Number number) throws JSONException {
} if (number == null) {
return 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 == (double) 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
*/
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 ignored) {
}
return null;
}
} }
...@@ -60,373 +60,407 @@ import java.util.List; ...@@ -60,373 +60,407 @@ import java.util.List;
*/ */
public class JSONStringer { public class JSONStringer {
/** The output data, containing at most one top-level array or object. */ /** The output data, containing at most one top-level array or object. */
final StringBuilder out = new StringBuilder(); final StringBuilder out = new StringBuilder();
/** /**
* Lexical scoping elements within this stringer, necessary to insert the * Lexical scoping elements within this stringer, necessary to insert the
* appropriate separator characters (ie. commas and colons) and to detect * appropriate separator characters (ie. commas and colons) and to detect
* nesting errors. * nesting errors.
*/ */
enum Scope { enum Scope {
/** /**
* An array with no elements requires no separators or newlines before * An array with no elements requires no separators or newlines before
* it is closed. * it is closed.
*/ */
EMPTY_ARRAY, EMPTY_ARRAY,
/** /**
* A array with at least one value requires a comma and newline before * A array with at least one value requires a comma and newline before
* the next element. * the next element.
*/ */
NONEMPTY_ARRAY, NONEMPTY_ARRAY,
/** /**
* An object with no keys or values requires no separators or newlines * An object with no keys or values requires no separators or newlines
* before it is closed. * before it is closed.
*/ */
EMPTY_OBJECT, EMPTY_OBJECT,
/** /**
* An object whose most recent element is a key. The next element must * An object whose most recent element is a key. The next element must
* be a value. * be a value.
*/ */
DANGLING_KEY, DANGLING_KEY,
/** /**
* An object with at least one name/value pair requires a comma and * An object with at least one name/value pair requires a comma and
* newline before the next element. * newline before the next element.
*/ */
NONEMPTY_OBJECT, NONEMPTY_OBJECT,
/** /**
* A special bracketless array needed by JSONStringer.join() and * A special bracketless array needed by JSONStringer.join() and
* JSONObject.quote() only. Not used for JSON encoding. * JSONObject.quote() only. Not used for JSON encoding.
*/ */
NULL, NULL,
} }
/** /**
* Unlike the original implementation, this stack isn't limited to 20 * Unlike the original implementation, this stack isn't limited to 20
* levels of nesting. * levels of nesting.
*/ */
private final List<Scope> stack = new ArrayList<Scope>(); private final List<Scope> stack = new ArrayList<Scope>();
/** /**
* A string containing a full set of spaces for a single level of * A string containing a full set of spaces for a single level of
* indentation, or null for no pretty printing. * indentation, or null for no pretty printing.
*/ */
private final String indent; private final String indent;
public JSONStringer() { public JSONStringer() {
indent = null; this.indent = null;
} }
JSONStringer(int indentSpaces) { JSONStringer(int indentSpaces) {
char[] indentChars = new char[indentSpaces]; char[] indentChars = new char[indentSpaces];
Arrays.fill(indentChars, ' '); Arrays.fill(indentChars, ' ');
indent = new String(indentChars); this.indent = new String(indentChars);
} }
/** /**
* Begins encoding a new array. Each call to this method must be paired with * Begins encoding a new array. Each call to this method must be paired with
* a call to {@link #endArray}. * a call to {@link #endArray}.
* *
* @return this stringer. * @return this stringer.
*/ * @throws JSONException if processing of json failed
public JSONStringer array() throws JSONException { */
return open(Scope.EMPTY_ARRAY, "["); public JSONStringer array() throws JSONException {
} return open(Scope.EMPTY_ARRAY, "[");
}
/**
* Ends encoding the current array. /**
* * Ends encoding the current array.
* @return this stringer. *
*/ * @return this stringer.
public JSONStringer endArray() throws JSONException { * @throws JSONException if processing of json failed
return close(Scope.EMPTY_ARRAY, Scope.NONEMPTY_ARRAY, "]"); */
} 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}. /**
* * Begins encoding a new object. Each call to this method must be paired
* @return this stringer. * with a call to {@link #endObject}.
*/ *
public JSONStringer object() throws JSONException { * @return this stringer.
return open(Scope.EMPTY_OBJECT, "{"); * @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. /**
*/ * Ends encoding the current object.
public JSONStringer endObject() throws JSONException { *
return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}"); * @return this stringer.
} * @throws JSONException if processing of json failed
*/
/** public JSONStringer endObject() throws JSONException {
* Enters a new scope by appending any necessary whitespace and the given return close(Scope.EMPTY_OBJECT, Scope.NONEMPTY_OBJECT, "}");
* bracket. }
*/
JSONStringer open(Scope empty, String openBracket) throws JSONException { /**
if (stack.isEmpty() && out.length() > 0) { * Enters a new scope by appending any necessary whitespace and the given
throw new JSONException("Nesting problem: multiple top-level roots"); * bracket.
} * @param empty any necessary whitespace
beforeValue(); * @param openBracket the open bracket
stack.add(empty); * @return this object
out.append(openBracket); * @throws JSONException if processing of json failed
return this; */
} JSONStringer open(Scope empty, String openBracket) throws JSONException {
if (this.stack.isEmpty() && this.out.length() > 0) {
/** throw new JSONException("Nesting problem: multiple top-level roots");
* Closes the current scope by appending any necessary whitespace and the }
* given bracket. beforeValue();
*/ this.stack.add(empty);
JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException { this.out.append(openBracket);
Scope context = peek(); return this;
if (context != nonempty && context != empty) { }
throw new JSONException("Nesting problem");
} /**
* Closes the current scope by appending any necessary whitespace and the
stack.remove(stack.size() - 1); * given bracket.
if (context == nonempty) { * @param empty any necessary whitespace
newline(); * @param nonempty the current scope
} * @param closeBracket the close bracket
out.append(closeBracket); * @throws JSONException if processing of json failed
return this; */
} JSONStringer close(Scope empty, Scope nonempty, String closeBracket) throws JSONException {
Scope context = peek();
/** if (context != nonempty && context != empty) {
* Returns the value on the top of the stack. throw new JSONException("Nesting problem");
*/ }
private Scope peek() throws JSONException {
if (stack.isEmpty()) { this.stack.remove(this.stack.size() - 1);
throw new JSONException("Nesting problem"); if (context == nonempty) {
} newline();
return stack.get(stack.size() - 1); }
} this.out.append(closeBracket);
return this;
/** }
* Replace the value on the top of the stack with the given value.
*/ /**
private void replaceTop(Scope topOfStack) { * Returns the value on the top of the stack.
stack.set(stack.size() - 1, topOfStack); * @return the scope
} * @throws JSONException if processing of json failed
*/
/** private Scope peek() throws JSONException {
* Encodes {@code value}. if (this.stack.isEmpty()) {
* throw new JSONException("Nesting problem");
* @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, }
* Integer, Long, Double or null. May not be {@link Double#isNaN() NaNs} return this.stack.get(this.stack.size() - 1);
* or {@link Double#isInfinite() infinities}. }
* @return this stringer.
*/ /**
public JSONStringer value(Object value) throws JSONException { * Replace the value on the top of the stack with the given value.
if (stack.isEmpty()) { * @param topOfStack the scope at the top of the stack
throw new JSONException("Nesting problem"); */
} private void replaceTop(Scope topOfStack) {
this.stack.set(this.stack.size() - 1, topOfStack);
if (value instanceof JSONArray) { }
((JSONArray) value).writeTo(this);
return this; /**
* Encodes {@code value}.
} else if (value instanceof JSONObject) { *
((JSONObject) value).writeTo(this); * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
return this; * Integer, Long, Double or null. May not be {@link Double#isNaN() NaNs}
} * or {@link Double#isInfinite() infinities}.
* @return this stringer.
beforeValue(); * @throws JSONException if processing of json failed
*/
if (value == null public JSONStringer value(Object value) throws JSONException {
|| value instanceof Boolean if (this.stack.isEmpty()) {
|| value == JSONObject.NULL) { throw new JSONException("Nesting problem");
out.append(value); }
} else if (value instanceof Number) { if (value instanceof JSONArray) {
out.append(JSONObject.numberToString((Number) value)); ((JSONArray) value).writeTo(this);
return this;
} else {
string(value.toString()); }
} else if (value instanceof JSONObject) {
((JSONObject) value).writeTo(this);
return this; return this;
} }
/** beforeValue();
* Encodes {@code value} to this stringer.
* if (value == null
* @return this stringer. || value instanceof Boolean
*/ || value == JSONObject.NULL) {
public JSONStringer value(boolean value) throws JSONException { this.out.append(value);
if (stack.isEmpty()) {
throw new JSONException("Nesting problem"); }
} else if (value instanceof Number) {
beforeValue(); this.out.append(JSONObject.numberToString((Number) value));
out.append(value);
return this; }
} else {
string(value.toString());
/** }
* Encodes {@code value} to this stringer.
* return this;
* @param value a finite value. May not be {@link Double#isNaN() NaNs} or }
* {@link Double#isInfinite() infinities}.
* @return this stringer. /**
*/ * Encodes {@code value} to this stringer.
public JSONStringer value(double value) throws JSONException { *
if (stack.isEmpty()) { * @param value the value to encode
throw new JSONException("Nesting problem"); * @return this stringer.
} * @throws JSONException if processing of json failed
beforeValue(); */
out.append(JSONObject.numberToString(value)); public JSONStringer value(boolean value) throws JSONException {
return this; if (this.stack.isEmpty()) {
} throw new JSONException("Nesting problem");
}
/** beforeValue();
* Encodes {@code value} to this stringer. this.out.append(value);
* return this;
* @return this stringer. }
*/
public JSONStringer value(long value) throws JSONException { /**
if (stack.isEmpty()) { * Encodes {@code value} to this stringer.
throw new JSONException("Nesting problem"); *
} * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
beforeValue(); * {@link Double#isInfinite() infinities}.
out.append(value); * @return this stringer.
return this; * @throws JSONException if processing of json failed
} */
public JSONStringer value(double value) throws JSONException {
private void string(String value) { if (this.stack.isEmpty()) {
out.append("\""); throw new JSONException("Nesting problem");
for (int i = 0, length = value.length(); i < length; i++) { }
char c = value.charAt(i); beforeValue();
this.out.append(JSONObject.numberToString(value));
/* return this;
* 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)." * Encodes {@code value} to this stringer.
*/ *
switch (c) { * @param value the value to encode
case '"': * @return this stringer.
case '\\': * @throws JSONException if processing of json failed
case '/': */
out.append('\\').append(c); public JSONStringer value(long value) throws JSONException {
break; if (this.stack.isEmpty()) {
throw new JSONException("Nesting problem");
case '\t': }
out.append("\\t"); beforeValue();
break; this.out.append(value);
return this;
case '\b': }
out.append("\\b");
break; private void string(String value) {
this.out.append("\"");
case '\n': for (int i = 0, length = value.length(); i < length; i++) {
out.append("\\n"); char c = value.charAt(i);
break;
/*
case '\r': * From RFC 4627, "All Unicode characters may be placed within the
out.append("\\r"); * quotation marks except for the characters that must be escaped:
break; * quotation mark, reverse solidus, and the control characters
* (U+0000 through U+001F)."
case '\f': */
out.append("\\f"); switch (c) {
break; case '"':
case '\\':
default: case '/':
if (c <= 0x1F) { this.out.append('\\').append(c);
out.append(String.format("\\u%04x", (int) c)); break;
} else {
out.append(c); case '\t':
} this.out.append("\\t");
break; break;
}
case '\b':
} this.out.append("\\b");
out.append("\""); break;
}
case '\n':
private void newline() { this.out.append("\\n");
if (indent == null) { break;
return;
} case '\r':
this.out.append("\\r");
out.append("\n"); break;
for (int i = 0; i < stack.size(); i++) {
out.append(indent); case '\f':
} this.out.append("\\f");
} break;
/** default:
* Encodes the key (property name) to this stringer. if (c <= 0x1F) {
* this.out.append(String.format("\\u%04x", (int) c));
* @param name the name of the forthcoming value. May not be null. }
* @return this stringer. else {
*/ this.out.append(c);
public JSONStringer key(String name) throws JSONException { }
if (name == null) { break;
throw new JSONException("Names must be non-null"); }
}
beforeKey(); }
string(name); this.out.append("\"");
return this; }
}
private void newline() {
/** if (this.indent == null) {
* Inserts any necessary separators and whitespace before a name. Also return;
* adjusts the stack to expect the key's value. }
*/
private void beforeKey() throws JSONException { this.out.append("\n");
Scope context = peek(); for (int i = 0; i < this.stack.size(); i++) {
if (context == Scope.NONEMPTY_OBJECT) { // first in object this.out.append(this.indent);
out.append(','); }
} else if (context != Scope.EMPTY_OBJECT) { // not in an object! }
throw new JSONException("Nesting problem");
} /**
newline(); * Encodes the key (property name) to this stringer.
replaceTop(Scope.DANGLING_KEY); *
} * @param name the name of the forthcoming value. May not be null.
* @return this stringer.
/** * @throws JSONException if processing of json failed
* Inserts any necessary separators and whitespace before a literal value, */
* inline array, or inline object. Also adjusts the stack to expect either a public JSONStringer key(String name) throws JSONException {
* closing bracket or another element. if (name == null) {
*/ throw new JSONException("Names must be non-null");
private void beforeValue() throws JSONException { }
if (stack.isEmpty()) { beforeKey();
return; string(name);
} return this;
}
Scope context = peek();
if (context == Scope.EMPTY_ARRAY) { // first in array /**
replaceTop(Scope.NONEMPTY_ARRAY); * Inserts any necessary separators and whitespace before a name. Also
newline(); * adjusts the stack to expect the key's value.
} else if (context == Scope.NONEMPTY_ARRAY) { // another in array * @throws JSONException if processing of json failed
out.append(','); */
newline(); private void beforeKey() throws JSONException {
} else if (context == Scope.DANGLING_KEY) { // value for key Scope context = peek();
out.append(indent == null ? ":" : ": "); if (context == Scope.NONEMPTY_OBJECT) { // first in object
replaceTop(Scope.NONEMPTY_OBJECT); this.out.append(',');
} else if (context != Scope.NULL) { }
throw new JSONException("Nesting problem"); else if (context != Scope.EMPTY_OBJECT) { // not in an object!
} throw new JSONException("Nesting problem");
} }
newline();
/** replaceTop(Scope.DANGLING_KEY);
* Returns the encoded JSON string. }
*
* <p>If invoked with unterminated arrays or unclosed objects, this method's /**
* return value is undefined. * Inserts any necessary separators and whitespace before a literal value,
* * inline array, or inline object. Also adjusts the stack to expect either a
* <p><strong>Warning:</strong> although it contradicts the general contract * closing bracket or another element.
* of {@link Object#toString}, this method returns null if the stringer * @throws JSONException if processing of json failed
* contains no data. */
*/ private void beforeValue() throws JSONException {
@Override public String toString() { if (this.stack.isEmpty()) {
return out.length() == 0 ? null : out.toString(); 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.length() == 0 ? null : this.out.toString();
}
} }
...@@ -61,551 +61,512 @@ package org.springframework.boot.configurationprocessor.json; ...@@ -61,551 +61,512 @@ package org.springframework.boot.configurationprocessor.json;
*/ */
public class JSONTokener { public class JSONTokener {
/** The input JSON. */ /** The input JSON. */
private final String in; private final String in;
/** /**
* The index of the next character to be returned by {@link #next}. When * The index of the next character to be returned by {@link #next}. When
* the input is exhausted, this equals the input's length. * the input is exhausted, this equals the input's length.
*/ */
private int pos; private int pos;
/** /**
* @param in JSON encoded string. Null is not permitted and will yield a * @param in JSON encoded string. Null is not permitted and will yield a
* tokener that throws {@code NullPointerExceptions} when methods are * tokener that throws {@code NullPointerExceptions} when methods are
* called. * called.
*/ */
public JSONTokener(String in) { public JSONTokener(String in) {
// consume an optional byte order mark (BOM) if it exists // consume an optional byte order mark (BOM) if it exists
if (in != null && in.startsWith("\ufeff")) { if (in != null && in.startsWith("\ufeff")) {
in = in.substring(1); in = in.substring(1);
} }
this.in = in; this.in = in;
} }
/** /**
* Returns the next value from the input. * Returns the next value from the input.
* *
* @return a {@link JSONObject}, {@link JSONArray}, String, Boolean, * @return a {@link JSONObject}, {@link JSONArray}, String, Boolean,
* Integer, Long, Double or {@link JSONObject#NULL}. * Integer, Long, Double or {@link JSONObject#NULL}.
* @throws JSONException if the input is malformed. * @throws JSONException if the input is malformed.
*/ */
public Object nextValue() throws JSONException { public Object nextValue() throws JSONException {
int c = nextCleanInternal(); int c = nextCleanInternal();
switch (c) { switch (c) {
case -1: case -1:
throw syntaxError("End of input"); throw syntaxError("End of input");
case '{': case '{':
return readObject(); return readObject();
case '[': case '[':
return readArray(); return readArray();
case '\'': case '\'':
case '"': case '"':
return nextString((char) c); return nextString((char) c);
default: default:
pos--; this.pos--;
return readLiteral(); return readLiteral();
} }
} }
private int nextCleanInternal() throws JSONException { private int nextCleanInternal() throws JSONException {
while (pos < in.length()) { while (this.pos < this.in.length()) {
int c = in.charAt(pos++); int c = this.in.charAt(this.pos++);
switch (c) { switch (c) {
case '\t': case '\t':
case ' ': case ' ':
case '\n': case '\n':
case '\r': case '\r':
continue; continue;
case '/': case '/':
if (pos == in.length()) { if (this.pos == this.in.length()) {
return c; return c;
} }
char peek = in.charAt(pos); char peek = this.in.charAt(this.pos);
switch (peek) { switch (peek) {
case '*': case '*':
// skip a /* c-style comment */ // skip a /* c-style comment */
pos++; this.pos++;
int commentEnd = in.indexOf("*/", pos); int commentEnd = this.in.indexOf("*/", this.pos);
if (commentEnd == -1) { if (commentEnd == -1) {
throw syntaxError("Unterminated comment"); throw syntaxError("Unterminated comment");
} }
pos = commentEnd + 2; this.pos = commentEnd + 2;
continue; continue;
case '/': case '/':
// skip a // end-of-line comment // skip a // end-of-line comment
pos++; this.pos++;
skipToEndOfLine(); skipToEndOfLine();
continue; continue;
default: default:
return c; return c;
} }
case '#': case '#':
/* /*
* Skip a # hash end-of-line comment. The JSON RFC doesn't * Skip a # hash end-of-line comment. The JSON RFC doesn't
* specify this behavior, but it's required to parse * specify this behavior, but it's required to parse
* existing documents. See http://b/2571423. * existing documents. See http://b/2571423.
*/ */
skipToEndOfLine(); skipToEndOfLine();
continue; continue;
default: default:
return c; return c;
} }
} }
return -1; return -1;
} }
/** /**
* Advances the position until after the next newline character. If the line * 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 * is terminated by "\r\n", the '\n' must be consumed as whitespace by the
* caller. * caller.
*/ */
private void skipToEndOfLine() { private void skipToEndOfLine() {
for (; pos < in.length(); pos++) { for (; this.pos < this.in.length(); this.pos++) {
char c = in.charAt(pos); char c = this.in.charAt(this.pos);
if (c == '\r' || c == '\n') { if (c == '\r' || c == '\n') {
pos++; this.pos++;
break; break;
} }
} }
} }
/** /**
* Returns the string up to but not including {@code quote}, unescaping any * Returns the string up to but not including {@code quote}, unescaping any
* character escape sequences encountered along the way. The opening quote * character escape sequences encountered along the way. The opening quote
* should have already been read. This consumes the closing quote, but does * should have already been read. This consumes the closing quote, but does
* not include it in the returned string. * not include it in the returned string.
* *
* @param quote either ' or ". * @param quote either ' or ".
* @throws NumberFormatException if any unicode escape sequences are * @return the string up to but not including {@code quote}
* malformed. * @throws NumberFormatException if any unicode escape sequences are
*/ * malformed.
public String nextString(char quote) throws JSONException { * @throws JSONException if processing of json failed
/* */
* For strings that are free of escape sequences, we can just extract public String nextString(char quote) throws JSONException {
* 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. * 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
StringBuilder builder = null; * sequence, we need to use a StringBuilder to compose the result.
*/
/* the index of the first character not yet appended to the builder. */ StringBuilder builder = null;
int start = pos;
/* the index of the first character not yet appended to the builder. */
while (pos < in.length()) { int start = this.pos;
int c = in.charAt(pos++);
if (c == quote) { while (this.pos < this.in.length()) {
if (builder == null) { int c = this.in.charAt(this.pos++);
// a new string avoids leaking memory if (c == quote) {
return new String(in.substring(start, pos - 1)); if (builder == null) {
} else { // a new string avoids leaking memory
builder.append(in, start, pos - 1); return new String(this.in.substring(start, this.pos - 1));
return builder.toString(); }
} else {
} builder.append(this.in, start, this.pos - 1);
return builder.toString();
if (c == '\\') { }
if (pos == in.length()) { }
throw syntaxError("Unterminated escape sequence");
} if (c == '\\') {
if (builder == null) { if (this.pos == this.in.length()) {
builder = new StringBuilder(); throw syntaxError("Unterminated escape sequence");
} }
builder.append(in, start, pos - 1); if (builder == null) {
builder.append(readEscapeCharacter()); builder = new StringBuilder();
start = pos; }
} builder.append(this.in, start, this.pos - 1);
} builder.append(readEscapeCharacter());
start = this.pos;
throw syntaxError("Unterminated string"); }
} }
/** 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". * Unescapes the character identified by the character or characters that
* * immediately follow a backslash. The backslash '\' should have already
* @throws NumberFormatException if any unicode escape sequences are * been read. This supports both unicode escapes "u000A" and two-character
* malformed. * escapes "\n".
*/ *
private char readEscapeCharacter() throws JSONException { * @return the unescaped char
char escaped = in.charAt(pos++); * @throws NumberFormatException if any unicode escape sequences are
switch (escaped) { * malformed.
case 'u': * @throws JSONException if processing of json failed
if (pos + 4 > in.length()) { */
throw syntaxError("Unterminated escape sequence"); private char readEscapeCharacter() throws JSONException {
} char escaped = this.in.charAt(this.pos++);
String hex = in.substring(pos, pos + 4); switch (escaped) {
pos += 4; case 'u':
return (char) Integer.parseInt(hex, 16); if (this.pos + 4 > this.in.length()) {
throw syntaxError("Unterminated escape sequence");
case 't': }
return '\t'; String hex = this.in.substring(this.pos, this.pos + 4);
this.pos += 4;
case 'b': return (char) Integer.parseInt(hex, 16);
return '\b';
case 't':
case 'n': return '\t';
return '\n';
case 'b':
case 'r': return '\b';
return '\r';
case 'n':
case 'f': return '\n';
return '\f';
case 'r':
case '\'': return '\r';
case '"':
case '\\': case 'f':
default: return '\f';
return escaped;
} case '\'':
} case '"':
case '\\':
/** default:
* Reads a null, boolean, numeric or unquoted string literal value. Numeric return escaped;
* values will be returned as an Integer, Long, or Double, in that order of }
* preference. }
*/
private Object readLiteral() throws JSONException { /**
String literal = nextToInternal("{}[]/\\:,=;# \t\f"); * 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
if (literal.length() == 0) { * preference.
throw syntaxError("Expected literal value"); * @return a literal value
} else if ("null".equalsIgnoreCase(literal)) { * @throws JSONException if processing of json failed
return JSONObject.NULL; */
} else if ("true".equalsIgnoreCase(literal)) { private Object readLiteral() throws JSONException {
return Boolean.TRUE; String literal = nextToInternal("{}[]/\\:,=;# \t\f");
} else if ("false".equalsIgnoreCase(literal)) {
return Boolean.FALSE; if (literal.length() == 0) {
} throw syntaxError("Expected literal value");
}
/* try to parse as an integral type... */ else if ("null".equalsIgnoreCase(literal)) {
if (literal.indexOf('.') == -1) { return JSONObject.NULL;
int base = 10; }
String number = literal; else if ("true".equalsIgnoreCase(literal)) {
if (number.startsWith("0x") || number.startsWith("0X")) { return Boolean.TRUE;
number = number.substring(2); }
base = 16; else if ("false".equalsIgnoreCase(literal)) {
} else if (number.startsWith("0") && number.length() > 1) { return Boolean.FALSE;
number = number.substring(1); }
base = 8;
} /* try to parse as an integral type... */
try { if (literal.indexOf('.') == -1) {
long longValue = Long.parseLong(number, base); int base = 10;
if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { String number = literal;
return (int) longValue; if (number.startsWith("0x") || number.startsWith("0X")) {
} else { number = number.substring(2);
return longValue; base = 16;
} }
} catch (NumberFormatException e) { else if (number.startsWith("0") && number.length() > 1) {
/* number = number.substring(1);
* This only happens for integral numbers greater than base = 8;
* Long.MAX_VALUE, numbers in exponential form (5e-10) and }
* unquoted strings. Fall through to try floating point. try {
*/ long longValue = Long.parseLong(number, base);
} if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) {
} return (int) longValue;
}
/* ...next try to parse as a floating point... */ else {
try { return longValue;
return Double.valueOf(literal); }
} catch (NumberFormatException ignored) { }
} catch (NumberFormatException e) {
/*
/* ... finally give up. We have an unquoted string */ * This only happens for integral numbers greater than
return new String(literal); // a new string avoids leaking memory * Long.MAX_VALUE, numbers in exponential form (5e-10) and
} * unquoted strings. Fall through to try floating point.
*/
/** }
* Returns the string up to but not including any of the given characters or }
* a newline character. This does not consume the excluded character.
*/ /* ...next try to parse as a floating point... */
private String nextToInternal(String excluded) { try {
int start = pos; return Double.valueOf(literal);
for (; pos < in.length(); pos++) { }
char c = in.charAt(pos); catch (NumberFormatException ignored) {
if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) { }
return in.substring(start, pos);
} /* ... finally give up. We have an unquoted string */
} return new String(literal); // a new string avoids leaking memory
return in.substring(start); }
}
/**
/** * Returns the string up to but not including any of the given characters or
* Reads a sequence of key/value pairs and the trailing closing brace '}' of * a newline character. This does not consume the excluded character.
* an object. The opening brace '{' should have already been read. * @return the string up to but not including any of the given characters or
*/ * a newline character
private JSONObject readObject() throws JSONException { */
JSONObject result = new JSONObject(); private String nextToInternal(String excluded) {
int start = this.pos;
/* Peek to see if this is the empty object. */ for (; this.pos < this.in.length(); this.pos++) {
int first = nextCleanInternal(); char c = this.in.charAt(this.pos);
if (first == '}') { if (c == '\r' || c == '\n' || excluded.indexOf(c) != -1) {
return result; return this.in.substring(start, this.pos);
} else if (first != -1) { }
pos--; }
} return this.in.substring(start);
}
while (true) {
Object name = nextValue(); /**
if (!(name instanceof String)) { * Reads a sequence of key/value pairs and the trailing closing brace '}' of
if (name == null) { * an object. The opening brace '{' should have already been read.
throw syntaxError("Names cannot be null"); * @return an object
} else { * @throws JSONException if processing of json failed
throw syntaxError("Names must be strings, but " + name */
+ " is of type " + name.getClass().getName()); private JSONObject readObject() throws JSONException {
} JSONObject result = new JSONObject();
}
/* Peek to see if this is the empty object. */
/* int first = nextCleanInternal();
* Expect the name/value separator to be either a colon ':', an if (first == '}') {
* equals sign '=', or an arrow "=>". The last two are bogus but we return result;
* include them because that's what the original implementation did. }
*/ else if (first != -1) {
int separator = nextCleanInternal(); this.pos--;
if (separator != ':' && separator != '=') { }
throw syntaxError("Expected ':' after " + name);
} while (true) {
if (pos < in.length() && in.charAt(pos) == '>') { Object name = nextValue();
pos++; if (!(name instanceof String)) {
} if (name == null) {
throw syntaxError("Names cannot be null");
result.put((String) name, nextValue()); }
else {
switch (nextCleanInternal()) { throw syntaxError("Names must be strings, but " + name
case '}': + " is of type " + name.getClass().getName());
return result; }
case ';': }
case ',':
continue; /*
default: * Expect the name/value separator to be either a colon ':', an
throw syntaxError("Unterminated object"); * 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);
* Reads a sequence of values and the trailing closing brace ']' of an }
* array. The opening brace '[' should have already been read. Note that if (this.pos < this.in.length() && this.in.charAt(this.pos) == '>') {
* "[]" yields an empty array, but "[,]" returns a two-element array this.pos++;
* equivalent to "[null,null]". }
*/
private JSONArray readArray() throws JSONException { result.put((String) name, nextValue());
JSONArray result = new JSONArray();
switch (nextCleanInternal()) {
/* to cover input that ends with ",]". */ case '}':
boolean hasTrailingSeparator = false; return result;
case ';':
while (true) { case ',':
switch (nextCleanInternal()) { continue;
case -1: default:
throw syntaxError("Unterminated array"); throw syntaxError("Unterminated object");
case ']': }
if (hasTrailingSeparator) { }
result.put(null); }
}
return result; /**
case ',': * Reads a sequence of values and the trailing closing brace ']' of an
case ';': * array. The opening brace '[' should have already been read. Note that
/* A separator without a value first means "null". */ * "[]" yields an empty array, but "[,]" returns a two-element array
result.put(null); * equivalent to "[null,null]".
hasTrailingSeparator = true; * @return an array
continue; * @throws JSONException if processing of json failed
default: */
pos--; private JSONArray readArray() throws JSONException {
} JSONArray result = new JSONArray();
result.put(nextValue()); /* to cover input that ends with ",]". */
boolean hasTrailingSeparator = false;
switch (nextCleanInternal()) {
case ']': while (true) {
return result; switch (nextCleanInternal()) {
case ',': case -1:
case ';': throw syntaxError("Unterminated array");
hasTrailingSeparator = true; case ']':
continue; if (hasTrailingSeparator) {
default: result.put(null);
throw syntaxError("Unterminated array"); }
} return result;
} case ',':
} case ';':
/* A separator without a value first means "null". */
/** result.put(null);
* Returns an exception containing the given message plus the current hasTrailingSeparator = true;
* position and the entire input string. continue;
*/ default:
public JSONException syntaxError(String message) { this.pos--;
return new JSONException(message + this); }
}
result.put(nextValue());
/**
* Returns the current position and the entire input string. switch (nextCleanInternal()) {
*/ case ']':
@Override public String toString() { return result;
// consistent with the original implementation case ',':
return " at character " + pos + " of " + in; case ';':
} hasTrailingSeparator = true;
continue;
/* default:
* Legacy APIs. throw syntaxError("Unterminated array");
* }
* 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.
*/ /**
* Returns an exception containing the given message plus the current
/** * position and the entire input string.
* Returns true until the input has been exhausted. * @param message the message
*/ * @return an exception
public boolean more() { */
return pos < in.length(); public JSONException syntaxError(String message) {
} return new JSONException(message + this);
}
/**
* Returns the next available character, or the null character '\0' if all /**
* input has been exhausted. The return value of this method is ambiguous * Returns the current position and the entire input string.
* for JSON strings that contain the character '\0'. * @return the current position and the entire input string.
*/ */
public char next() { @Override
return pos < in.length() ? in.charAt(pos++) : '\0'; public String toString() {
} // consistent with the original implementation
return " at character " + this.pos + " of " + this.in;
/** }
* Returns the next available character if it equals {@code c}. Otherwise an
* exception is thrown. /*
*/ * Legacy APIs.
public char next(char c) throws JSONException { *
char result = next(); * None of the methods below are on the critical path of parsing JSON
if (result != c) { * documents. They exist only because they were exposed by the original
throw syntaxError("Expected " + c + " but was " + result); * implementation and may be used by some clients.
} */
return result;
} public boolean more() {
return this.pos < this.in.length();
/** }
* Returns the next character that is not whitespace and does not belong to
* a comment. If the input is exhausted before such a character can be public char next() {
* found, the null character '\0' is returned. The return value of this return this.pos < this.in.length() ? this.in.charAt(this.pos++) : '\0';
* method is ambiguous for JSON strings that contain the character '\0'. }
*/
public char nextClean() throws JSONException { public char next(char c) throws JSONException {
int nextCleanInt = nextCleanInternal(); char result = next();
return nextCleanInt == -1 ? '\0' : (char) nextCleanInt; if (result != c) {
} throw syntaxError("Expected " + c + " but was " + result);
}
/** return result;
* Returns the next {@code length} characters of the input. }
*
* <p>The returned string shares its backing character array with this public char nextClean() throws JSONException {
* tokener's input string. If a reference to the returned string may be held int nextCleanInt = nextCleanInternal();
* indefinitely, you should use {@code new String(result)} to copy it first return nextCleanInt == -1 ? '\0' : (char) nextCleanInt;
* to avoid memory leaks. }
*
* @throws JSONException if the remaining input is not long enough to public String next(int length) throws JSONException {
* satisfy this request. if (this.pos + length > this.in.length()) {
*/ throw syntaxError(length + " is out of bounds");
public String next(int length) throws JSONException { }
if (pos + length > in.length()) { String result = this.in.substring(this.pos, this.pos + length);
throw syntaxError(length + " is out of bounds"); this.pos += length;
} return result;
String result = in.substring(pos, pos + length); }
pos += length;
return result; public String nextTo(String excluded) {
} if (excluded == null) {
throw new NullPointerException("excluded == null");
/** }
* Returns the {@link String#trim trimmed} string holding the characters up return nextToInternal(excluded).trim();
* to but not including the first of: }
* <ul>
* <li>any character in {@code excluded} public String nextTo(char excluded) {
* <li>a newline character '\n' return nextToInternal(String.valueOf(excluded)).trim();
* <li>a carriage return '\r' }
* </ul>
* public void skipPast(String thru) {
* <p>The returned string shares its backing character array with this int thruStart = this.in.indexOf(thru, this.pos);
* tokener's input string. If a reference to the returned string may be held this.pos = thruStart == -1 ? this.in.length() : (thruStart + thru.length());
* indefinitely, you should use {@code new String(result)} to copy it first }
* to avoid memory leaks.
* public char skipTo(char to) {
* @return a possibly-empty string int index = this.in.indexOf(to, this.pos);
*/ if (index != -1) {
public String nextTo(String excluded) { this.pos = index;
if (excluded == null) { return to;
throw new NullPointerException("excluded == null"); }
} else {
return nextToInternal(excluded).trim(); return '\0';
} }
}
/**
* Equivalent to {@code nextTo(String.valueOf(excluded))}. public void back() {
*/ if (--this.pos == -1) {
public String nextTo(char excluded) { this.pos = 0;
return nextToInternal(String.valueOf(excluded)).trim(); }
} }
/** public static int dehexchar(char hex) {
* Advances past all input up to and including the next occurrence of if (hex >= '0' && hex <= '9') {
* {@code thru}. If the remaining input doesn't contain {@code thru}, the return hex - '0';
* input is exhausted. }
*/ else if (hex >= 'A' && hex <= 'F') {
public void skipPast(String thru) { return hex - 'A' + 10;
int thruStart = in.indexOf(thru, pos); }
pos = thruStart == -1 ? in.length() : (thruStart + thru.length()); else if (hex >= 'a' && hex <= 'f') {
} return hex - 'a' + 10;
}
/** else {
* Advances past all input up to but not including the next occurrence of return -1;
* {@code to}. If the remaining input doesn't contain {@code to}, the input }
* is unchanged. }
*/
public char skipTo(char to) {
int index = in.indexOf(to, pos);
if (index != -1) {
pos = index;
return to;
} else {
return '\0';
}
}
/**
* Unreads the most recent character of input. If no input characters have
* been read, the input is unchanged.
*/
public void back() {
if (--pos == -1) {
pos = 0;
}
}
/**
* Returns the integer [0..15] value for the given hex character, or -1
* for non-hex input.
*
* @param hex a character in the ranges [0-9], [A-F] or [a-f]. Any other
* character will yield a -1 result.
*/
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;
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment