diff --git a/json-core/.gitignore b/json-core/.gitignore
index 796b96d..c4dfdc5 100644
--- a/json-core/.gitignore
+++ b/json-core/.gitignore
@@ -1 +1,2 @@
/build
+/target/
diff --git a/json-core/src/main/java/io/apptik/json/AbstractValidator.java b/json-core/src/main/java/io/apptik/json/AbstractValidator.java
index 1ed2cb3..dffc620 100644
--- a/json-core/src/main/java/io/apptik/json/AbstractValidator.java
+++ b/json-core/src/main/java/io/apptik/json/AbstractValidator.java
@@ -1,33 +1,30 @@
package io.apptik.json;
-
public abstract class AbstractValidator implements Validator {
- @Override
- public boolean isValid(JsonElement el) {
- //System.out.println("Started simple JsonSchema Validation using: " + this.getTitle());
- return doValidate(el, null);
- }
-
- @Override
- public String validate(JsonElement el) {
- //System.out.println("Started JsonSchema Validation using: " + this.getTitle());
- StringBuilder sb = new StringBuilder();
- doValidate(el, sb);
- return sb.toString();
- }
-
- @Override
- public boolean validate(JsonElement el, StringBuilder sb) {
- //System.out.println("Started full JsonSchema Validation using: " + this.getTitle());
- return doValidate(el, sb);
- }
-
- protected abstract boolean doValidate(JsonElement el, StringBuilder sb);
-
- @Override
- public String getTitle() {
- return getClass().getCanonicalName();
- }
+ protected abstract boolean doValidate(JsonElement el, StringBuilder sb);
+
+ public String getTitle() {
+ return getClass().getCanonicalName();
+ }
+
+ public boolean isValid(final JsonElement el) {
+ // System.out.println("Started simple JsonSchema Validation using: " +
+ // this.getTitle());
+ return doValidate(el, null);
+ }
+
+ public String validate(final JsonElement el) {
+ // System.out.println("Started JsonSchema Validation using: " +
+ // this.getTitle());
+ StringBuilder sb = new StringBuilder();
+ doValidate(el, sb);
+ return sb.toString();
+ }
+
+ public boolean validate(final JsonElement el, final StringBuilder sb) {
+ // System.out.println("Started full JsonSchema Validation using: " +
+ // this.getTitle());
+ return doValidate(el, sb);
+ }
}
-
diff --git a/json-core/src/main/java/io/apptik/json/JsonArray.java b/json-core/src/main/java/io/apptik/json/JsonArray.java
index 3be5723..73fb98c 100644
--- a/json-core/src/main/java/io/apptik/json/JsonArray.java
+++ b/json-core/src/main/java/io/apptik/json/JsonArray.java
@@ -17,6 +17,11 @@
package io.apptik.json;
+import static io.apptik.json.JsonNull.JSON_NULL;
+import io.apptik.json.exception.JsonException;
+import io.apptik.json.util.Freezable;
+import io.apptik.json.util.Util;
+
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
@@ -25,834 +30,848 @@
import java.util.List;
import java.util.ListIterator;
-import io.apptik.json.exception.JsonException;
-import io.apptik.json.util.Freezable;
-import io.apptik.json.util.Util;
-
-import static io.apptik.json.JsonNull.JSON_NULL;
-
// Note: this class was written without inspecting the non-free org.json sourcecode.
/**
* A dense indexed sequence of values. Values may be any mix of
* {@link JsonObject JsonObjects}, other {@link JsonArray JsonArrays}, Strings,
- * Booleans, Integers, Longs, Doubles, {@code null} or {@link JsonNull}.
- * Values may not be {@link Double#isNaN() NaNs}, {@link Double#isInfinite()
+ * Booleans, Integers, Longs, Doubles, {@code null} or {@link JsonNull}. Values
+ * may not be {@link Double#isNaN() NaNs}, {@link Double#isInfinite()
* infinities}, or of any type not listed here.
*
- *
{@code JsonArray} has the same type coercion behavior and
- * optional/mandatory accessors as {@link JsonObject}. See that class'
- * documentation for details.
*
- *
Warning: this class represents null in two incompatible
- * ways: the standard Java {@code null} reference, and the sentinel value {@link
- * JsonNull}. In particular, {@code get} fails if the requested index
+ * {@code JsonArray} has the same type coercion behavior and optional/mandatory
+ * accessors as {@link JsonObject}. See that class' documentation for details.
+ *
+ *
+ * Warning: this class represents null in two incompatible
+ * ways: the standard Java {@code null} reference, and the sentinel value
+ * {@link JsonNull}. In particular, {@code get} fails if the requested index
* holds the null reference, but succeeds if it holds {@code JsonNull}.
*
- *
Instances of this class are not thread safe.
+ *
+ * Instances of this class are not thread safe.
*/
-public final class JsonArray extends JsonElement implements List, Freezable {
-
- private volatile boolean frozen = false;
- private final List values;
-
- /**
- * Creates a {@code JsonArray} with no values.
- */
- public JsonArray() {
- values = new ArrayList();
- }
-
- /**
- * Creates a new {@code JsonArray} by copying all values from the given
- * collection.
- *
- * @param copyFrom a collection whose values are of supported types.
- * Unsupported values are not permitted and will yield an array in an
- * inconsistent state.
- */
- /* Accept a raw type for API compatibility */
- public JsonArray(Collection copyFrom) throws JsonException {
- this();
- if (copyFrom != null) {
- for (Object aCopyFrom : copyFrom) {
- put(JsonElement.wrap(aCopyFrom));
- }
- }
- }
-
-
- /**
- * Creates a new {@code JsonArray} with values from the given primitive array.
- */
- public JsonArray(Object array) throws JsonException {
- if (!array.getClass().isArray()) {
- throw new JsonException("Not a primitive array: " + array.getClass());
- }
- final int length = Array.getLength(array);
- values = new ArrayList(length);
- for (int i = 0; i < length; ++i) {
- put(JsonElement.wrap(Array.get(array, i)));
- }
- }
-
- /**
- * Returns the number of values in this array.
- */
- public int length() {
- return values.size();
- }
-
- /**
- * Appends {@code value} to the end of this array.
- *
- * @return this array.
- */
- public JsonArray put(boolean value) {
- return put(new JsonBoolean(value));
- }
-
- /**
- * Appends {@code value} to the end of this array.
- *
- * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
- * {@link Double#isInfinite() infinities}.
- * @return this array.
- */
- public JsonArray put(double value) throws JsonException {
- return put(new JsonNumber(value));
- }
-
- /**
- * Appends {@code value} to the end of this array.
- *
- * @return this array.
- */
- public JsonArray put(int value) {
- return put(new JsonNumber(value));
- }
-
- /**
- * Appends {@code value} to the end of this array.
- *
- * @return this array.
- */
- public JsonArray put(long value) {
- return put(new JsonNumber(value));
- }
-
- /**
- * Appends {@code value} to the end of this array.
- *
- * @param value a {@link JsonObject}, {@link JsonArray}, String, Boolean,
- * Integer, Long, Double, or {@code null}. May
- * not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
- * infinities}. Unsupported values are not permitted and will cause the
- * array to be in an inconsistent state.
- * @return this array.
- */
- public JsonArray put(Object value) throws JsonException {
- return put(wrap(value));
- }
-
- public JsonArray put(JsonElement value) {
- checkIfFrozen();
- putInternal(value);
-
- return this;
- }
-
- /**
- * Sets the value at {@code index} to {@code value}, null padding this array
- * to the required length if necessary. If a value already exists at {@code
- * index}, it will be replaced.
- *
- * @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 value a finite value. May not be {@link Double#isNaN() NaNs} or
- * {@link Double#isInfinite() infinities}.
- * @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.
- *
- * @return this array.
- */
- public JsonArray put(int index, int value) throws JsonException {
- return put(index, (Integer) value);
- }
-
- /**
- * Sets the value at {@code index} to {@code value}, null padding this array
- * to the required length if necessary. If a value already exists at {@code
- * index}, it will be replaced.
- *
- * @return this array.
- */
- public JsonArray put(int index, long value) throws JsonException {
- return put(index, (Long) value);
- }
-
- /**
- * Sets the value at {@code index} to {@code value}, null padding this array
- * to the required length if necessary. If a value already exists at {@code
- * index}, it will be replaced.
- *
- * @param value a {@link JsonObject}, {@link JsonArray}, String, Boolean,
- * Integer, Long, Double, or {@code null}. May
- * not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
- * infinities}.
- * @return this array.
- */
- public JsonArray put(int index, Object value) throws JsonException {
- checkIfFrozen();
- while (values.size() <= index) {
- values.add(JSON_NULL);
- }
- values.set(index, wrap(value));
- return this;
- }
-
- /**
- * Returns true if this array has no value at {@code index}, or if its value
- * is the {@code null} reference .
- */
- public boolean isNull(int index) {
- Object value = opt(index);
- return value == null || value.equals(JSON_NULL);
- }
-
- /**
- * Returns the value at {@code index}.
- *
- * @throws JsonException if this array has no value at {@code index}, or if
- * that value is the {@code null} reference. This method returns
- * normally if the value is {@code JsonObject#NULL}.
- */
- public JsonElement get(int index) throws JsonException {
- try {
- JsonElement value = values.get(index);
- if (value == null) {
- throw new JsonException("Value at " + index + " is null.");
- }
- return value;
- } catch (IndexOutOfBoundsException e) {
- throw new JsonException("Index " + index + " out of range [0.." + values.size() + ")");
- }
- }
-
- @Override
- public JsonElement set(int i, JsonElement jsonElement) {
- checkIfFrozen();
- return values.set(i, jsonElement);
- }
-
- @Override
- public void add(int i, JsonElement jsonElement) {
- checkIfFrozen();
- put(i, jsonElement);
- }
-
- /**
- * Returns the value at {@code index}, or null if the array has no value
- * at {@code index}.
- */
- public JsonElement opt(int index) {
- if (index < 0 || index >= values.size()) {
- return null;
- }
- return values.get(index);
- }
-
- /**
- * Removes and returns the value at {@code index}, or null if the array has no value
- * at {@code index}.
- */
- public JsonElement remove(int index) {
- checkIfFrozen();
- if (index < 0 || index >= values.size()) {
- return null;
- }
- return values.remove(index);
- }
-
- @Override
- public int indexOf(Object o) {
- return values.indexOf(o);
- }
-
- @Override
- public int lastIndexOf(Object o) {
- return values.lastIndexOf(o);
- }
-
- @Override
- public ListIterator listIterator() {
- return values.listIterator();
- }
-
- @Override
- public ListIterator listIterator(int i) {
- return values.listIterator(i);
- }
-
- @Override
- public List subList(int i, int i2) {
- return values.subList(i, i2);
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a boolean or can
- * be coerced to a boolean.
- *
- * @throws JsonException if the value at {@code index} doesn't exist or
- * cannot be coerced to a boolean.
- */
- public Boolean getBoolean(int index, boolean strict) throws JsonException {
- JsonElement el = get(index);
- Boolean res = null;
- if (strict && !el.isBoolean()) {
- throw Util.typeMismatch(index, el, "boolean", true);
- }
- if (el.isBoolean()) {
- res = el.asBoolean();
- }
- if (el.isString()) {
- res = Util.toBoolean(el.asString());
- }
- if (res == null)
- throw Util.typeMismatch(index, el, "boolean", strict);
- return res;
- }
-
- public Boolean getBoolean(int index) throws JsonException {
- return getBoolean(index, false);
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a boolean or can
- * be coerced to a boolean. Returns false otherwise.
- */
- public Boolean optBoolean(int index) {
- return optBoolean(index, null);
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a boolean or can
- * be coerced to a boolean. Returns {@code fallback} otherwise.
- */
- public Boolean optBoolean(int index, Boolean fallback) {
- return optBoolean(index, fallback, false);
- }
-
- public Boolean optBoolean(int index, Boolean fallback, boolean strict) {
- try {
- return getBoolean(index, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a double or can
- * be coerced to a double.
- *
- * @throws JsonException if the value at {@code index} doesn't exist or
- * cannot be coerced to a double.
- */
- public Double getDouble(int index, boolean strict) throws JsonException {
- JsonElement el = get(index);
- Double res = null;
- if (strict && !el.isNumber()) {
- throw Util.typeMismatch(index, el, "double", true);
- }
- if (el.isNumber()) {
- res = el.asDouble();
- }
- if (el.isString()) {
- res = Util.toDouble(el.asString());
- }
- if (res == null)
- throw Util.typeMismatch(index, el, "double", strict);
- return res;
- }
-
- public Double getDouble(int index) throws JsonException {
- return getDouble(index, false);
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a double or can
- * be coerced to a double. Returns {@code NaN} otherwise.
- */
- public Double optDouble(int index) {
- return optDouble(index, null);
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a double or can
- * be coerced to a double. Returns {@code fallback} otherwise.
- */
- public Double optDouble(int index, Double fallback) {
- return optDouble(index, fallback, false);
- }
-
- public Double optDouble(int index, Double fallback, boolean strict) {
- try {
- return getDouble(index, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
- /**
- * 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
- * cannot be coerced to a int.
- */
- public Integer getInt(int index) throws JsonException {
- return getInt(index, false);
- }
-
- /**
- * Returns the value at {@code index} if it exists and is an int or
- * can be coerced to an int. Returns 0 otherwise.
- */
- public Integer getInt(int index, boolean strict) throws JsonException {
- JsonElement el = get(index);
- Integer res = null;
- if (strict && !el.isNumber()) {
- throw Util.typeMismatch(index, el, "int", true);
- }
- if (el.isNumber()) {
- res = el.asInt();
- }
- if (el.isString()) {
- res = Util.toInteger(el.asString());
- }
- if (res == null)
- throw Util.typeMismatch(index, el, "int", strict);
- return res;
- }
-
-
- public Integer optInt(int index) {
- return optInt(index, null);
- }
-
- /**
- * Returns the value at {@code index} if it exists and is an int or
- * can be coerced to an int. Returns {@code fallback} otherwise.
- */
- public Integer optInt(int index, Integer fallback) {
- return optInt(index, fallback, false);
- }
-
- public Integer optInt(int index, Integer fallback, boolean strict) {
- try {
- return getInt(index, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a long or
- * can be coerced to a long.
- *
- * @throws JsonException if the value at {@code index} doesn't exist or
- * cannot be coerced to a long.
- */
-
- public Long getLong(int index, boolean strict) throws JsonException {
- JsonElement el = get(index);
- Long res = null;
- if (strict && !el.isNumber()) {
- throw Util.typeMismatch(index, el, "long", true);
- }
- if (el.isNumber()) {
- res = el.asLong();
- }
- if (el.isString()) {
- res = Util.toLong(el.asString());
- }
- if (res == null)
- throw Util.typeMismatch(index, el, "long", strict);
- return res;
- }
-
- public Long getLong(int index) throws JsonException {
- return getLong(index, false);
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a long or
- * can be coerced to a long. Returns 0 otherwise.
- */
- public Long optLong(int index) {
- return optLong(index, null);
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a long or
- * can be coerced to a long. Returns {@code fallback} otherwise.
- */
- public Long optLong(int index, Long fallback) {
- return optLong(index, fallback, false);
- }
-
-
- public Long optLong(int index, Long fallback, boolean strict) {
- try {
- return getLong(index, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
- /**
- * Returns the value at {@code index} if it exists, coercing it if
- * necessary.
- *
- * @throws JsonException if no such value exists.
- */
- public String getString(int index, boolean strict) throws JsonException {
- JsonElement el = get(index);
- String res = null;
- if (strict && !el.isString()) {
- throw Util.typeMismatch(index, el, "string", true);
- }
- res = el.toString();
- if (res == null)
- throw Util.typeMismatch(index, el, "string", strict);
- return res;
- }
-
- public String getString(int index) throws JsonException {
- return getString(index, false);
- }
-
- /**
- * Returns the value at {@code index} if it exists, coercing it if
- * necessary. Returns the empty string if no such value exists.
- */
- public String optString(int index) {
- return optString(index, null);
- }
-
- /**
- * Returns the value at {@code index} if it exists, coercing it if
- * necessary. Returns {@code fallback} if no such value exists.
- */
- public String optString(int index, String fallback) {
- return optString(index, fallback, false);
- }
-
- public String optString(int index, String fallback, boolean strict) {
- try {
- return getString(index, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
-
- /**
- * Returns the value at {@code index} if it exists and is a {@code
- * JsonArray}.
- *
- * @throws JsonException if the value doesn't exist or is not a {@code
- * JsonArray}.
- */
- public JsonArray getJsonArray(int index) throws JsonException {
- JsonElement el = get(index);
- if (!el.isJsonArray()) {
- throw Util.typeMismatch(index, el, "JsonArray");
- }
- return el.asJsonArray();
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a {@code
- * JsonArray}. Returns null otherwise.
- */
- public JsonArray optJsonArray(int index) {
- JsonElement el;
- try {
- el = get(index);
- } catch (JsonException e) {
- return null;
- }
- if (!el.isJsonArray()) {
- return null;
- }
- return el.asJsonArray();
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a {@code
- * JsonObject}.
- *
- * @throws JsonException if the value doesn't exist or is not a {@code
- * JsonObject}.
- */
- public JsonObject getJsonObject(int index) throws JsonException {
- JsonElement el = get(index);
- if (!el.isJsonObject()) {
- throw Util.typeMismatch(index, el, "JsonObject");
- }
- return el.asJsonObject();
- }
-
- /**
- * Returns the value at {@code index} if it exists and is a {@code
- * JsonObject}. Returns null otherwise.
- */
- public JsonObject optJsonObject(int index) {
- JsonElement el;
- try {
- el = get(index);
- } catch (JsonException e) {
- return null;
- }
- if (!el.isJsonObject()) {
- return null;
- }
- return el.asJsonObject();
- }
-
- /**
- * Returns a new object whose values are the values in this array, and whose
- * names are the values in {@code names}. Names and values are paired up by
- * index from 0 through to the shorter array's length. Names that are not
- * strings will be coerced to strings. This method returns null if either
- * array is empty.
- */
- public JsonObject toJsonObject(JsonArray names) throws JsonException {
- JsonObject result = new JsonObject();
- int length = Math.min(names.length(), values.size());
- if (length == 0) {
- return null;
- }
- for (int i = 0; i < length; i++) {
- String name = names.opt(i).toString();
- if (opt(i) != null)
- result.put(name, opt(i));
- }
- return result;
- }
-
- public ArrayList toArrayList() {
- ArrayList res = new ArrayList();
- for (JsonElement el : values) {
- res.add(el.toString());
- }
- return res;
- }
-
- public boolean isOnlyStrings() {
- for (JsonElement el : values) {
- if (!el.isString()) return false;
- }
- return true;
- }
-
- public boolean isOnlyNumbers() {
- for (JsonElement el : values) {
- if (!el.isNumber()) return false;
- }
- return true;
- }
-
- public boolean isOnlyBooleans() {
- for (JsonElement el : values) {
- if (!el.isBoolean()) return false;
- }
- return true;
- }
-
- public boolean isOnlyObjects() {
- for (JsonElement el : values) {
- if (!el.isJsonObject()) return false;
- }
- return true;
- }
-
- public boolean isOnlyArrays() {
- for (JsonElement el : values) {
- if (!el.isJsonArray()) return false;
- }
- return true;
- }
-
- @Override
- public void write(JsonWriter writer) throws IOException {
- writer.beginArray();
- for (JsonElement e : this) {
- e.write(writer);
- }
- writer.endArray();
- }
-
- @Override
- public boolean isJsonArray() {
- return true;
- }
-
- @Override
- public JsonArray asJsonArray() {
- return this;
- }
-
- @Override
- public boolean equals(Object o) {
- return o instanceof JsonArray && ((JsonArray) o).values.equals(values);
- }
-
- @Override
- public int hashCode() {
- // diverge from the original, which doesn't implement hashCode
- return values.hashCode();
- }
-
- @Override
- public int size() {
- return values.size();
- }
-
- @Override
- public boolean isEmpty() {
- return values.isEmpty();
- }
-
- @Override
- public boolean contains(Object o) {
- return values.contains(o);
- }
-
- @Override
- public Iterator iterator() {
- return values.iterator();
- }
-
- @Override
- public Object[] toArray() {
- return values.toArray();
- }
-
- @Override
- public T[] toArray(T[] ts) {
- return values.toArray(ts);
- }
-
- @Override
- public boolean add(JsonElement jsonElement) {
- checkIfFrozen();
- return values.add(jsonElement);
- }
-
- @Override
- public boolean remove(Object o) {
- checkIfFrozen();
- return values.remove(o);
- }
-
- @Override
- public boolean containsAll(Collection> objects) {
- return values.containsAll(objects);
- }
-
- @Override
- public boolean addAll(Collection extends JsonElement> jsonElements) {
- checkIfFrozen();
- return values.addAll(jsonElements);
- }
-
- @Override
- public boolean addAll(int i, Collection extends JsonElement> jsonElements) {
- checkIfFrozen();
- return addAll(jsonElements);
- }
-
- @Override
- public boolean removeAll(Collection> objects) {
- checkIfFrozen();
- return values.removeAll(objects);
- }
-
- @Override
- public boolean retainAll(Collection> objects) {
- checkIfFrozen();
- return values.retainAll(objects);
- }
-
- @Override
- public void clear() {
- checkIfFrozen();
- values.clear();
- }
-
- @Override
- public String getJsonType() {
- return TYPE_ARRAY;
- }
-
- @Override
- public boolean isFrozen() {
- return frozen;
- }
-
- @Override
- public JsonArray freeze() {
- frozen = true;
- for (JsonElement el : values) {
- if (el.isJsonArray()) {
- el.asJsonArray().freeze();
- }
- if (el.isJsonObject()) {
- el.asJsonObject().freeze();
- }
- }
- return this;
- }
-
- @Override
- public JsonArray cloneAsThawed() {
- try {
- return JsonElement.readFrom(this.toString()).asJsonArray();
- } catch (IOException e) {
- e.printStackTrace();
- throw new JsonException("Cannot Recreate Json Array", e);
- }
- }
-
- public void checkIfFrozen() {
- if (isFrozen()) {
- throw new IllegalStateException(
- "Attempt to modify a frozen JsonArray instance.");
- }
- }
-
- void putInternal(JsonElement value) {
- if (value != null) {
- values.add(value);
- }
- }
+public final class JsonArray extends JsonElement implements List,
+ Freezable {
+
+ private volatile boolean frozen = false;
+ private final List values;
+
+ /**
+ * Creates a {@code JsonArray} with no values.
+ */
+ public JsonArray() {
+ values = new ArrayList();
+ }
+
+ /**
+ * Creates a new {@code JsonArray} by copying all values from the given
+ * collection.
+ *
+ * @param copyFrom
+ * a collection whose values are of supported types. Unsupported
+ * values are not permitted and will yield an array in an
+ * inconsistent state.
+ */
+ /* Accept a raw type for API compatibility */
+ public JsonArray(final Collection copyFrom) throws JsonException {
+ this();
+ if (copyFrom != null) {
+ for (Object aCopyFrom : copyFrom) {
+ put(JsonElement.wrap(aCopyFrom));
+ }
+ }
+ }
+
+ /**
+ * Creates a new {@code JsonArray} with values from the given primitive
+ * array.
+ */
+ public JsonArray(final Object array) throws JsonException {
+ if (!array.getClass().isArray()) {
+ throw new JsonException("Not a primitive array: "
+ + array.getClass());
+ }
+ final int length = Array.getLength(array);
+ values = new ArrayList(length);
+ for (int i = 0; i < length; ++i) {
+ put(JsonElement.wrap(Array.get(array, i)));
+ }
+ }
+
+ public void add(final int i, final JsonElement jsonElement) {
+ checkIfFrozen();
+ put(i, jsonElement);
+ }
+
+ public boolean add(final JsonElement jsonElement) {
+ checkIfFrozen();
+ return values.add(jsonElement);
+ }
+
+ public boolean addAll(final Collection extends JsonElement> jsonElements) {
+ checkIfFrozen();
+ return values.addAll(jsonElements);
+ }
+
+ public boolean addAll(final int i,
+ final Collection extends JsonElement> jsonElements) {
+ checkIfFrozen();
+ return addAll(jsonElements);
+ }
+
+ @Override
+ public JsonArray asJsonArray() {
+ return this;
+ }
+
+ public void checkIfFrozen() {
+ if (isFrozen()) {
+ throw new IllegalStateException(
+ "Attempt to modify a frozen JsonArray instance.");
+ }
+ }
+
+ public void clear() {
+ checkIfFrozen();
+ values.clear();
+ }
+
+ public JsonArray cloneAsThawed() {
+ try {
+ return JsonElement.readFrom(this.toString()).asJsonArray();
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new JsonException("Cannot Recreate Json Array", e);
+ }
+ }
+
+ public boolean contains(final Object o) {
+ return values.contains(o);
+ }
+
+ public boolean containsAll(final Collection> objects) {
+ return values.containsAll(objects);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ return o instanceof JsonArray && ((JsonArray) o).values.equals(values);
+ }
+
+ public JsonArray freeze() {
+ frozen = true;
+ for (JsonElement el : values) {
+ if (el.isJsonArray()) {
+ el.asJsonArray().freeze();
+ }
+ if (el.isJsonObject()) {
+ el.asJsonObject().freeze();
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Returns the value at {@code index}.
+ *
+ * @throws JsonException
+ * if this array has no value at {@code index}, or if that value
+ * is the {@code null} reference. This method returns normally
+ * if the value is {@code JsonObject#NULL}.
+ */
+ public JsonElement get(final int index) throws JsonException {
+ try {
+ JsonElement value = values.get(index);
+ if (value == null) {
+ throw new JsonException("Value at " + index + " is null.");
+ }
+ return value;
+ } catch (IndexOutOfBoundsException e) {
+ throw new JsonException("Index " + index + " out of range [0.."
+ + values.size() + ")");
+ }
+ }
+
+ public Boolean getBoolean(final int index) throws JsonException {
+ return getBoolean(index, false);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a boolean or can
+ * be coerced to a boolean.
+ *
+ * @throws JsonException
+ * if the value at {@code index} doesn't exist or cannot be
+ * coerced to a boolean.
+ */
+ public Boolean getBoolean(final int index, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(index);
+ Boolean res = null;
+ if (strict && !el.isBoolean()) {
+ throw Util.typeMismatch(index, el, "boolean", true);
+ }
+ if (el.isBoolean()) {
+ res = el.asBoolean();
+ }
+ if (el.isString()) {
+ res = Util.toBoolean(el.asString());
+ }
+ if (res == null) {
+ throw Util.typeMismatch(index, el, "boolean", strict);
+ }
+ return res;
+ }
+
+ public Double getDouble(final int index) throws JsonException {
+ return getDouble(index, false);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a double or can be
+ * coerced to a double.
+ *
+ * @throws JsonException
+ * if the value at {@code index} doesn't exist or cannot be
+ * coerced to a double.
+ */
+ public Double getDouble(final int index, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(index);
+ Double res = null;
+ if (strict && !el.isNumber()) {
+ throw Util.typeMismatch(index, el, "double", true);
+ }
+ if (el.isNumber()) {
+ res = el.asDouble();
+ }
+ if (el.isString()) {
+ res = Util.toDouble(el.asString());
+ }
+ if (res == null) {
+ throw Util.typeMismatch(index, el, "double", strict);
+ }
+ return res;
+ }
+
+ /**
+ * 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 cannot be
+ * coerced to a int.
+ */
+ public Integer getInt(final int index) throws JsonException {
+ return getInt(index, false);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is an int or can be
+ * coerced to an int. Returns 0 otherwise.
+ */
+ public Integer getInt(final int index, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(index);
+ Integer res = null;
+ if (strict && !el.isNumber()) {
+ throw Util.typeMismatch(index, el, "int", true);
+ }
+ if (el.isNumber()) {
+ res = el.asInt();
+ }
+ if (el.isString()) {
+ res = Util.toInteger(el.asString());
+ }
+ if (res == null) {
+ throw Util.typeMismatch(index, el, "int", strict);
+ }
+ return res;
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a
+ * {@code JsonArray}.
+ *
+ * @throws JsonException
+ * if the value doesn't exist or is not a {@code JsonArray}.
+ */
+ public JsonArray getJsonArray(final int index) throws JsonException {
+ JsonElement el = get(index);
+ if (!el.isJsonArray()) {
+ throw Util.typeMismatch(index, el, "JsonArray");
+ }
+ return el.asJsonArray();
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a
+ * {@code JsonObject}.
+ *
+ * @throws JsonException
+ * if the value doesn't exist or is not a {@code JsonObject}.
+ */
+ public JsonObject getJsonObject(final int index) throws JsonException {
+ JsonElement el = get(index);
+ if (!el.isJsonObject()) {
+ throw Util.typeMismatch(index, el, "JsonObject");
+ }
+ return el.asJsonObject();
+ }
+
+ @Override
+ public String getJsonType() {
+ return TYPE_ARRAY;
+ }
+
+ public Long getLong(final int index) throws JsonException {
+ return getLong(index, false);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a long or can be
+ * coerced to a long.
+ *
+ * @throws JsonException
+ * if the value at {@code index} doesn't exist or cannot be
+ * coerced to a long.
+ */
+
+ public Long getLong(final int index, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(index);
+ Long res = null;
+ if (strict && !el.isNumber()) {
+ throw Util.typeMismatch(index, el, "long", true);
+ }
+ if (el.isNumber()) {
+ res = el.asLong();
+ }
+ if (el.isString()) {
+ res = Util.toLong(el.asString());
+ }
+ if (res == null) {
+ throw Util.typeMismatch(index, el, "long", strict);
+ }
+ return res;
+ }
+
+ public String getString(final int index) throws JsonException {
+ return getString(index, false);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists, coercing it if
+ * necessary.
+ *
+ * @throws JsonException
+ * if no such value exists.
+ */
+ public String getString(final int index, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(index);
+ String res = null;
+ if (strict && !el.isString()) {
+ throw Util.typeMismatch(index, el, "string", true);
+ }
+ res = el.toString();
+ if (res == null) {
+ throw Util.typeMismatch(index, el, "string", strict);
+ }
+ return res;
+ }
+
+ @Override
+ public int hashCode() {
+ // diverge from the original, which doesn't implement hashCode
+ return values.hashCode();
+ }
+
+ public int indexOf(final Object o) {
+ return values.indexOf(o);
+ }
+
+ public boolean isEmpty() {
+ return values.isEmpty();
+ }
+
+ public boolean isFrozen() {
+ return frozen;
+ }
+
+ @Override
+ public boolean isJsonArray() {
+ return true;
+ }
+
+ /**
+ * Returns true if this array has no value at {@code index}, or if its value
+ * is the {@code null} reference .
+ */
+ public boolean isNull(final int index) {
+ Object value = opt(index);
+ return value == null || value.equals(JSON_NULL);
+ }
+
+ public boolean isOnlyArrays() {
+ for (JsonElement el : values) {
+ if (!el.isJsonArray()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isOnlyBooleans() {
+ for (JsonElement el : values) {
+ if (!el.isBoolean()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isOnlyNumbers() {
+ for (JsonElement el : values) {
+ if (!el.isNumber()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isOnlyObjects() {
+ for (JsonElement el : values) {
+ if (!el.isJsonObject()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isOnlyStrings() {
+ for (JsonElement el : values) {
+ if (!el.isString()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public Iterator iterator() {
+ return values.iterator();
+ }
+
+ public int lastIndexOf(final Object o) {
+ return values.lastIndexOf(o);
+ }
+
+ /**
+ * Returns the number of values in this array.
+ */
+ public int length() {
+ return values.size();
+ }
+
+ public ListIterator listIterator() {
+ return values.listIterator();
+ }
+
+ public ListIterator listIterator(final int i) {
+ return values.listIterator(i);
+ }
+
+ /**
+ * Returns the value at {@code index}, or null if the array has no value at
+ * {@code index}.
+ */
+ public JsonElement opt(final int index) {
+ if (index < 0 || index >= values.size()) {
+ return null;
+ }
+ return values.get(index);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a boolean or can
+ * be coerced to a boolean. Returns false otherwise.
+ */
+ public Boolean optBoolean(final int index) {
+ return optBoolean(index, null);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a boolean or can
+ * be coerced to a boolean. Returns {@code fallback} otherwise.
+ */
+ public Boolean optBoolean(final int index, final Boolean fallback) {
+ return optBoolean(index, fallback, false);
+ }
+
+ public Boolean optBoolean(final int index, final Boolean fallback,
+ final boolean strict) {
+ try {
+ return getBoolean(index, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a double or can be
+ * coerced to a double. Returns {@code NaN} otherwise.
+ */
+ public Double optDouble(final int index) {
+ return optDouble(index, null);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a double or can be
+ * coerced to a double. Returns {@code fallback} otherwise.
+ */
+ public Double optDouble(final int index, final Double fallback) {
+ return optDouble(index, fallback, false);
+ }
+
+ public Double optDouble(final int index, final Double fallback,
+ final boolean strict) {
+ try {
+ return getDouble(index, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ public Integer optInt(final int index) {
+ return optInt(index, null);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is an int or can be
+ * coerced to an int. Returns {@code fallback} otherwise.
+ */
+ public Integer optInt(final int index, final Integer fallback) {
+ return optInt(index, fallback, false);
+ }
+
+ public Integer optInt(final int index, final Integer fallback,
+ final boolean strict) {
+ try {
+ return getInt(index, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a
+ * {@code JsonArray}. Returns null otherwise.
+ */
+ public JsonArray optJsonArray(final int index) {
+ JsonElement el;
+ try {
+ el = get(index);
+ } catch (JsonException e) {
+ return null;
+ }
+ if (!el.isJsonArray()) {
+ return null;
+ }
+ return el.asJsonArray();
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a
+ * {@code JsonObject}. Returns null otherwise.
+ */
+ public JsonObject optJsonObject(final int index) {
+ JsonElement el;
+ try {
+ el = get(index);
+ } catch (JsonException e) {
+ return null;
+ }
+ if (!el.isJsonObject()) {
+ return null;
+ }
+ return el.asJsonObject();
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a long or can be
+ * coerced to a long. Returns 0 otherwise.
+ */
+ public Long optLong(final int index) {
+ return optLong(index, null);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists and is a long or can be
+ * coerced to a long. Returns {@code fallback} otherwise.
+ */
+ public Long optLong(final int index, final Long fallback) {
+ return optLong(index, fallback, false);
+ }
+
+ public Long optLong(final int index, final Long fallback,
+ final boolean strict) {
+ try {
+ return getLong(index, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists, coercing it if
+ * necessary. Returns the empty string if no such value exists.
+ */
+ public String optString(final int index) {
+ return optString(index, null);
+ }
+
+ /**
+ * Returns the value at {@code index} if it exists, coercing it if
+ * necessary. Returns {@code fallback} if no such value exists.
+ */
+ public String optString(final int index, final String fallback) {
+ return optString(index, fallback, false);
+ }
+
+ public String optString(final int index, final String fallback,
+ final boolean strict) {
+ try {
+ return getString(index, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Appends {@code value} to the end of this array.
+ *
+ * @return this array.
+ */
+ public JsonArray put(final boolean value) {
+ return put(new JsonBoolean(value));
+ }
+
+ /**
+ * Appends {@code value} to the end of this array.
+ *
+ * @param value
+ * a finite value. May not be {@link Double#isNaN() NaNs} or
+ * {@link Double#isInfinite() infinities}.
+ * @return this array.
+ */
+ public JsonArray put(final double value) throws JsonException {
+ return put(new JsonNumber(value));
+ }
+
+ /**
+ * Appends {@code value} to the end of this array.
+ *
+ * @return this array.
+ */
+ public JsonArray put(final int value) {
+ return put(new JsonNumber(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.
+ *
+ * @return this array.
+ */
+ public JsonArray put(final int index, final 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 value
+ * a finite value. May not be {@link Double#isNaN() NaNs} or
+ * {@link Double#isInfinite() infinities}.
+ * @return this array.
+ */
+ public JsonArray put(final int index, final 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.
+ *
+ * @return this array.
+ */
+ public JsonArray put(final int index, final int value) throws JsonException {
+ return put(index, (Integer) value);
+ }
+
+ /**
+ * Sets the value at {@code index} to {@code value}, null padding this array
+ * to the required length if necessary. If a value already exists at
+ * {@code index}, it will be replaced.
+ *
+ * @return this array.
+ */
+ public JsonArray put(final int index, final long value)
+ throws JsonException {
+ return put(index, (Long) value);
+ }
+
+ /**
+ * Sets the value at {@code index} to {@code value}, null padding this array
+ * to the required length if necessary. If a value already exists at
+ * {@code index}, it will be replaced.
+ *
+ * @param value
+ * a {@link JsonObject}, {@link JsonArray}, String, Boolean,
+ * Integer, Long, Double, or {@code null}. May not be
+ * {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
+ * infinities}.
+ * @return this array.
+ */
+ public JsonArray put(final int index, final Object value)
+ throws JsonException {
+ checkIfFrozen();
+ while (values.size() <= index) {
+ values.add(JSON_NULL);
+ }
+ values.set(index, wrap(value));
+ return this;
+ }
+
+ public JsonArray put(final JsonElement value) {
+ checkIfFrozen();
+ putInternal(value);
+
+ return this;
+ }
+
+ /**
+ * Appends {@code value} to the end of this array.
+ *
+ * @return this array.
+ */
+ public JsonArray put(final long value) {
+ return put(new JsonNumber(value));
+ }
+
+ /**
+ * Appends {@code value} to the end of this array.
+ *
+ * @param value
+ * a {@link JsonObject}, {@link JsonArray}, String, Boolean,
+ * Integer, Long, Double, or {@code null}. May not be
+ * {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
+ * infinities}. Unsupported values are not permitted and will
+ * cause the array to be in an inconsistent state.
+ * @return this array.
+ */
+ public JsonArray put(final Object value) throws JsonException {
+ return put(wrap(value));
+ }
+
+ void putInternal(final JsonElement value) {
+ if (value != null) {
+ values.add(value);
+ }
+ }
+
+ /**
+ * Removes and returns the value at {@code index}, or null if the array has
+ * no value at {@code index}.
+ */
+ public JsonElement remove(final int index) {
+ checkIfFrozen();
+ if (index < 0 || index >= values.size()) {
+ return null;
+ }
+ return values.remove(index);
+ }
+
+ public boolean remove(final Object o) {
+ checkIfFrozen();
+ return values.remove(o);
+ }
+
+ public boolean removeAll(final Collection> objects) {
+ checkIfFrozen();
+ return values.removeAll(objects);
+ }
+
+ public boolean retainAll(final Collection> objects) {
+ checkIfFrozen();
+ return values.retainAll(objects);
+ }
+
+ public JsonElement set(final int i, final JsonElement jsonElement) {
+ checkIfFrozen();
+ return values.set(i, jsonElement);
+ }
+
+ public int size() {
+ return values.size();
+ }
+
+ public List subList(final int i, final int i2) {
+ return values.subList(i, i2);
+ }
+
+ public Object[] toArray() {
+ return values.toArray();
+ }
+
+ public T[] toArray(final T[] ts) {
+ return values.toArray(ts);
+ }
+
+ public ArrayList toArrayList() {
+ ArrayList res = new ArrayList();
+ for (JsonElement el : values) {
+ res.add(el.toString());
+ }
+ return res;
+ }
+
+ /**
+ * Returns a new object whose values are the values in this array, and whose
+ * names are the values in {@code names}. Names and values are paired up by
+ * index from 0 through to the shorter array's length. Names that are not
+ * strings will be coerced to strings. This method returns null if either
+ * array is empty.
+ */
+ public JsonObject toJsonObject(final JsonArray names) throws JsonException {
+ JsonObject result = new JsonObject();
+ int length = Math.min(names.length(), values.size());
+ if (length == 0) {
+ return null;
+ }
+ for (int i = 0; i < length; i++) {
+ String name = names.opt(i).toString();
+ if (opt(i) != null) {
+ result.put(name, opt(i));
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void write(final JsonWriter writer) throws IOException {
+ writer.beginArray();
+ for (JsonElement e : this) {
+ e.write(writer);
+ }
+ writer.endArray();
+ }
}
diff --git a/json-core/src/main/java/io/apptik/json/JsonElement.java b/json-core/src/main/java/io/apptik/json/JsonElement.java
index 778d244..a4661b8 100644
--- a/json-core/src/main/java/io/apptik/json/JsonElement.java
+++ b/json-core/src/main/java/io/apptik/json/JsonElement.java
@@ -1,5 +1,8 @@
package io.apptik.json;
+import static io.apptik.json.JsonNull.JSON_NULL;
+import io.apptik.json.exception.JsonException;
+
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
@@ -9,235 +12,249 @@
import java.util.Collection;
import java.util.Map;
-import io.apptik.json.exception.JsonException;
-
-import static io.apptik.json.JsonNull.JSON_NULL;
-
public abstract class JsonElement {
- public static final String TYPE_OBJECT = "object";
- public static final String TYPE_ARRAY = "array";
- public static final String TYPE_STRING = "string";
- public static final String TYPE_NUMBER = "number";
- public static final String TYPE_INTEGER = "integer";
- public static final String TYPE_BOOLEAN = "boolean";
- public static final String TYPE_NULL = "null";
-
- public static JsonElement readFrom(JsonReader reader) throws JsonException, IOException {
- return Adapter.fromJson(reader);
- }
-
- public static JsonElement readFrom(Reader reader) throws JsonException, IOException {
- return Adapter.fromJson(reader);
- }
-
- public static JsonElement readFrom(String text) throws JsonException, IOException {
- return JsonElement.readFrom(new StringReader(text));
-
- }
-
- public boolean isNull() {
- return false;
- }
-
- public boolean isBoolean() {
- return false;
- }
-
- public boolean isNumber() {
- return false;
- }
-
- public boolean isString() {
- return false;
- }
-
- public boolean isJsonObject() {
- return false;
- }
-
- public boolean isJsonArray() {
- return false;
- }
-
- public boolean asBoolean() {
- throw new UnsupportedOperationException(toString() + " is not a boolean");
- }
-
- public double asDouble() {
- throw new UnsupportedOperationException(toString() + " is not a double number");
- }
-
- public float asFloat() {
- throw new UnsupportedOperationException(toString() + " is not a float number");
- }
-
- public int asInt() {
- throw new UnsupportedOperationException(toString() + " is not an integer number");
- }
-
- public long asLong() {
- throw new UnsupportedOperationException(toString() + " is not a long integer number");
- }
-
- public byte asByte() {
- throw new UnsupportedOperationException(toString() + " is not a byte number");
- }
-
- public String asString() {
- throw new UnsupportedOperationException(toString() + " is not a string");
- }
-
- public JsonObject asJsonObject() {
- throw new UnsupportedOperationException(toString() + " is not a json object");
- }
-
- public JsonArray asJsonArray() {
- throw new UnsupportedOperationException(toString() + " is not an json array");
- }
-
- public void writeTo(Writer writer) throws IOException {
- write(new JsonWriter(writer));
- }
-
- @Override
- public String toString() {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- try {
- write(jsonWriter);
- } catch (IOException exception) {
- // StringWriter does not throw IOExceptions
- throw new RuntimeException(exception);
- }
- return stringWriter.toString();
- }
-
- @Override
- public boolean equals(Object object) {
- return super.equals(object);
- }
-
- @Override
- public int hashCode() {
- return super.hashCode();
- }
-
- /**
- * Wraps the given object if to JsonXXX object.
- */
- public static JsonElement wrap(Object o) throws JsonException {
- if (o == null) {
- //null value means not specified i.e.-> no valued will be mapped
- //Json.null is specific value
- return null;
- }
- if (o instanceof JsonElement) {
- return (JsonElement) o;
- }
- if (o instanceof ElementWrapper) {
- return ((ElementWrapper) o).getJson();
- }
- 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) {
- return new JsonBoolean((Boolean) o);
- }
- if (o instanceof Number) {
- return new JsonNumber((Number) o);
- }
- if (o instanceof String) {
- return new JsonString((String) o);
- }
- if (o instanceof Character) {
- return new JsonString(Character.toString((Character) o));
- }
- if (o instanceof ByteBuffer) {
- return new JsonString(((ByteBuffer) o).asCharBuffer().toString());
- }
-
- return new JsonString(o.toString());
- }
-
- public abstract void write(JsonWriter writer) throws IOException;
-
- public abstract String getJsonType();
-
- private static class Adapter {
- static public void write(JsonWriter out, JsonElement value) throws IOException {
- //TODO should this actually happen??
- if (value == null) {
- out.nullValue();
- } else {
- value.write(out);
- }
- }
-
- static public void toJson(Writer out, JsonElement value) throws IOException {
- JsonWriter writer = new JsonWriter(out);
- write(writer, value);
- }
-
- static public String toJson(JsonElement value) throws IOException {
- StringWriter stringWriter = new StringWriter();
- toJson(stringWriter, value);
- return stringWriter.toString();
- }
-
- static public JsonElement read(JsonReader in) throws IOException, JsonException {
- switch (in.peek()) {
- case STRING:
- return new JsonString(in.nextString());
- case NUMBER:
- return new JsonNumber(in.nextString());
- case BOOLEAN:
- return new JsonBoolean(in.nextBoolean());
- case NULL:
- in.nextNull();
- return JSON_NULL;
- case BEGIN_ARRAY:
- JsonArray array = new JsonArray();
- in.beginArray();
- while (in.hasNext()) {
- array.putInternal(read(in));
- }
- in.endArray();
- return array;
- case BEGIN_OBJECT:
- JsonObject object = new JsonObject();
- in.beginObject();
- while (in.hasNext()) {
- object.putInternal(in.nextName(), read(in));
- }
- in.endObject();
- return object;
- case END_DOCUMENT:
- case NAME:
- case END_OBJECT:
- case END_ARRAY:
- default:
- throw new IllegalArgumentException();
- }
- }
-
- static public JsonElement fromJson(Reader in) throws IOException, JsonException {
- JsonReader reader = new JsonReader(in);
- return read(reader);
- }
-
- static public JsonElement fromJson(JsonReader in) throws IOException, JsonException {
- return read(in);
- }
-
- static public JsonElement fromJson(String json) throws IOException, JsonException {
- return fromJson(new StringReader(json));
- }
- }
+ private static class Adapter {
+ static public JsonElement fromJson(final JsonReader in)
+ throws IOException, JsonException {
+ return read(in);
+ }
+
+ static public JsonElement fromJson(final Reader in) throws IOException,
+ JsonException {
+ JsonReader reader = new JsonReader(in);
+ return read(reader);
+ }
+
+ static public JsonElement fromJson(final String json)
+ throws IOException, JsonException {
+ return fromJson(new StringReader(json));
+ }
+
+ static public JsonElement read(final JsonReader in) throws IOException,
+ JsonException {
+ switch (in.peek()) {
+ case STRING:
+ return new JsonString(in.nextString());
+ case NUMBER:
+ return new JsonNumber(in.nextString());
+ case BOOLEAN:
+ return new JsonBoolean(in.nextBoolean());
+ case NULL:
+ in.nextNull();
+ return JSON_NULL;
+ case BEGIN_ARRAY:
+ JsonArray array = new JsonArray();
+ in.beginArray();
+ while (in.hasNext()) {
+ array.putInternal(read(in));
+ }
+ in.endArray();
+ return array;
+ case BEGIN_OBJECT:
+ JsonObject object = new JsonObject();
+ in.beginObject();
+ while (in.hasNext()) {
+ object.putInternal(in.nextName(), read(in));
+ }
+ in.endObject();
+ return object;
+ case END_DOCUMENT:
+ case NAME:
+ case END_OBJECT:
+ case END_ARRAY:
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ static public String toJson(final JsonElement value) throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ toJson(stringWriter, value);
+ return stringWriter.toString();
+ }
+
+ static public void toJson(final Writer out, final JsonElement value)
+ throws IOException {
+ JsonWriter writer = new JsonWriter(out);
+ write(writer, value);
+ }
+
+ static public void write(final JsonWriter out, final JsonElement value)
+ throws IOException {
+ // TODO should this actually happen??
+ if (value == null) {
+ out.nullValue();
+ } else {
+ value.write(out);
+ }
+ }
+ }
+
+ public static final String TYPE_ARRAY = "array";
+ public static final String TYPE_BOOLEAN = "boolean";
+ public static final String TYPE_INTEGER = "integer";
+ public static final String TYPE_NULL = "null";
+ public static final String TYPE_NUMBER = "number";
+ public static final String TYPE_OBJECT = "object";
+
+ public static final String TYPE_STRING = "string";
+
+ public static JsonElement readFrom(final JsonReader reader)
+ throws JsonException, IOException {
+ return Adapter.fromJson(reader);
+ }
+
+ public static JsonElement readFrom(final Reader reader)
+ throws JsonException, IOException {
+ return Adapter.fromJson(reader);
+ }
+
+ public static JsonElement readFrom(final String text) throws JsonException,
+ IOException {
+ return JsonElement.readFrom(new StringReader(text));
+
+ }
+
+ /**
+ * Wraps the given object if to JsonXXX object.
+ */
+ public static JsonElement wrap(final Object o) throws JsonException {
+ if (o == null) {
+ // null value means not specified i.e.-> no valued will be mapped
+ // Json.null is specific value
+ return null;
+ }
+ if (o instanceof JsonElement) {
+ return (JsonElement) o;
+ }
+ if (o instanceof ElementWrapper) {
+ return ((ElementWrapper) o).getJson();
+ }
+ 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) {
+ return new JsonBoolean((Boolean) o);
+ }
+ if (o instanceof Number) {
+ return new JsonNumber((Number) o);
+ }
+ if (o instanceof String) {
+ return new JsonString((String) o);
+ }
+ if (o instanceof Character) {
+ return new JsonString(Character.toString((Character) o));
+ }
+ if (o instanceof ByteBuffer) {
+ return new JsonString(((ByteBuffer) o).asCharBuffer().toString());
+ }
+
+ return new JsonString(o.toString());
+ }
+
+ public boolean asBoolean() {
+ throw new UnsupportedOperationException(toString()
+ + " is not a boolean");
+ }
+
+ public byte asByte() {
+ throw new UnsupportedOperationException(toString()
+ + " is not a byte number");
+ }
+
+ public double asDouble() {
+ throw new UnsupportedOperationException(toString()
+ + " is not a double number");
+ }
+
+ public float asFloat() {
+ throw new UnsupportedOperationException(toString()
+ + " is not a float number");
+ }
+
+ public int asInt() {
+ throw new UnsupportedOperationException(toString()
+ + " is not an integer number");
+ }
+
+ public JsonArray asJsonArray() {
+ throw new UnsupportedOperationException(toString()
+ + " is not an json array");
+ }
+
+ public JsonObject asJsonObject() {
+ throw new UnsupportedOperationException(toString()
+ + " is not a json object");
+ }
+
+ public long asLong() {
+ throw new UnsupportedOperationException(toString()
+ + " is not a long integer number");
+ }
+
+ public String asString() {
+ throw new UnsupportedOperationException(toString() + " is not a string");
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ return super.equals(object);
+ }
+
+ public abstract String getJsonType();
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ public boolean isBoolean() {
+ return false;
+ }
+
+ public boolean isJsonArray() {
+ return false;
+ }
+
+ public boolean isJsonObject() {
+ return false;
+ }
+
+ public boolean isNull() {
+ return false;
+ }
+
+ public boolean isNumber() {
+ return false;
+ }
+
+ public boolean isString() {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ try {
+ write(jsonWriter);
+ } catch (IOException exception) {
+ // StringWriter does not throw IOExceptions
+ throw new RuntimeException(exception);
+ }
+ return stringWriter.toString();
+ }
+
+ public abstract void write(JsonWriter writer) throws IOException;
+
+ public void writeTo(final Writer writer) throws IOException {
+ write(new JsonWriter(writer));
+ }
}
diff --git a/json-core/src/main/java/io/apptik/json/JsonObject.java b/json-core/src/main/java/io/apptik/json/JsonObject.java
index 73b99ea..0e20404 100644
--- a/json-core/src/main/java/io/apptik/json/JsonObject.java
+++ b/json-core/src/main/java/io/apptik/json/JsonObject.java
@@ -17,6 +17,11 @@
package io.apptik.json;
+import static io.apptik.json.JsonNull.JSON_NULL;
+import io.apptik.json.exception.JsonException;
+import io.apptik.json.util.Freezable;
+import io.apptik.json.util.LinkedTreeMap;
+import io.apptik.json.util.Util;
import java.io.IOException;
import java.util.ArrayList;
@@ -26,823 +31,858 @@
import java.util.Map;
import java.util.Set;
-import io.apptik.json.exception.JsonException;
-import io.apptik.json.util.Freezable;
-import io.apptik.json.util.LinkedTreeMap;
-import io.apptik.json.util.Util;
-
-import static io.apptik.json.JsonNull.JSON_NULL;
-
// Note: this class was written without inspecting the non-free org.json sourcecode.
/**
* A modifiable set of name/value mappings. Names are unique, non-null strings.
* Values may be any mix of {@link JsonObject JsonObjects}, {@link JsonArray
* JsonArrays}, Strings, Booleans, Integers, Longs, Doubles or {@link JsonNull}.
- * Values may not be {@code null}, {@link Double#isNaN() NaNs}, {@link
- * Double#isInfinite() infinities}, or of any type not listed here.
+ * Values may not be {@code null}, {@link Double#isNaN() NaNs},
+ * {@link Double#isInfinite() infinities}, or of any type not listed here.
+ *
*
- *
This class can coerce values to another type when requested.
+ * This class can coerce values to another type when requested.
*
*
When the requested type is a boolean, strings will be coerced using a
* case-insensitive comparison to "true" and "false".
- *
When the requested type is a double, other {@link Number} types will
- * be coerced using {@link Number#doubleValue() doubleValue}. Strings
- * that can be coerced using {@link Double#valueOf(String)} will be.
- *
When the requested type is an int, other {@link Number} types will
- * be coerced using {@link Number#intValue() intValue}. Strings
- * that can be coerced using {@link Double#valueOf(String)} will be,
- * and then cast to int.
- *
When the requested type is a double, other {@link Number} types will be
+ * coerced using {@link Number#doubleValue() doubleValue}. Strings that can be
+ * coerced using {@link Double#valueOf(String)} will be.
+ *
When the requested type is an int, other {@link Number} types will be
+ * coerced using {@link Number#intValue() intValue}. Strings that can be coerced
+ * using {@link Double#valueOf(String)} will be, and then cast to int.
+ *
When the requested type is a String, other non-null values will be
* coerced using {@link String#valueOf(Object)}. Although null cannot be
- * coerced, the sentinel value {@link JsonNull} is coerced to the
- * string "null".
+ * coerced, the sentinel value {@link JsonNull} is coerced to the string "null".
*
*
- *
This class can look up both mandatory and optional values:
+ *
+ * This class can look up both mandatory and optional values:
*
*
Use getType() to retrieve a mandatory value. This
- * fails with a {@code JsonException} if the requested name has no value
- * or if the value cannot be coerced to the requested type.
+ * fails with a {@code JsonException} if the requested name has no value or if
+ * the value cannot be coerced to the requested type.
*
Use optType() to retrieve an optional value. This
- * returns a system- or user-supplied default if the requested name has no
- * value or if the value cannot be coerced to the requested type.
+ * returns a system- or user-supplied default if the requested name has no value
+ * or if the value cannot be coerced to the requested type.
*
*
- *
Warning: this class represents null in two incompatible
- * ways: the standard Java {@code null} reference, and the sentinel value {@link
- * JsonNull}. In particular, calling {@code put(name, null)} removes the
+ *
+ * Warning: this class represents null in two incompatible
+ * ways: the standard Java {@code null} reference, and the sentinel value
+ * {@link JsonNull}. In particular, calling {@code put(name, null)} removes the
* named entry from the object but {@code put(name, JsonObject.NULL)} stores an
* entry whose value is {@code JsonObject.NULL}.
*
- *
Instances of this class are not thread safe. This class is
- * not designed for inheritance and should not be subclassed.
- * In particular, self-use by overrideable methods is not specified. See
- * Effective Java Item 17, "Design and Document or inheritance or else
- * prohibit it" for further information.
+ *
+ * Instances of this class are not thread safe. This class is not designed for
+ * inheritance and should not be subclassed. In particular, self-use by
+ * overrideable methods is not specified. See Effective Java Item 17,
+ * "Design and Document or inheritance or else prohibit it" for further
+ * information.
*/
-public final class JsonObject extends JsonElement implements Iterable>, Freezable {
-
- private volatile boolean frozen = false;
- private final LinkedTreeMap nameValuePairs = new LinkedTreeMap();
-
- /**
- * Creates a {@code JsonObject} with no name/value mappings.
- */
- public JsonObject() {
-
- }
-
-
- /**
- * Creates a new {@code JSONObject} by copying all name/value mappings from
- * the given map.
- *
- * @param copyFrom a map whose keys are of type {@link String} and whose
- * values are of supported types.
- * @throws NullPointerException if any of the map's keys are null.
- */
- /* (accept a raw type for API compatibility) */
- public JsonObject(Map copyFrom) throws JsonException {
- this();
- Map, ?> contentsTyped = (Map, ?>) copyFrom;
- for (Map.Entry, ?> entry : contentsTyped.entrySet()) {
- /*
- * Deviate from the original by checking that keys are non-null and
- * of the proper type. (We still defer validating the values).
- */
- String key = (String) entry.getKey();
- if (key == null) {
- throw new NullPointerException("key == null");
- }
- nameValuePairs.put(key, wrap(entry.getValue()));
- }
- }
-
-
- /**
- * Returns the number of name/value mappings in this object.
- */
- public int length() {
- return nameValuePairs.size();
- }
-
-
- /**
- * Maps {@code name} to {@code value}, clobbering any existing name/value
- * mapping with the same name.
- *
- * @return this object.
- */
- public JsonObject put(String name, boolean value) throws JsonException {
- return put(name, new JsonBoolean(value));
- }
-
- /**
- * Maps {@code name} to {@code value}, clobbering any existing name/value
- * 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.
- */
- public JsonObject put(String name, double value) throws JsonException {
- return put(name, new JsonNumber(value));
- }
-
- /**
- * Maps {@code name} to {@code value}, clobbering any existing name/value
- * mapping with the same name.
- *
- * @return this object.
- */
- public JsonObject put(String name, int value) throws JsonException {
- return put(name, new JsonNumber(value));
- }
-
- /**
- * Maps {@code name} to {@code value}, clobbering any existing name/value
- * mapping with the same name.
- *
- * @return this object.
- */
- public JsonObject put(String name, long value) throws JsonException {
- return put(name, new JsonNumber(value));
- }
-
- /**
- * Maps {@code name} to {@code value}, clobbering any existing name/value
- * mapping with the same name. If the value is {@code null}, any existing
- * mapping for {@code name} is removed.
- *
- * @param value a {@link JsonObject}, {@link JsonArray}, String, Boolean,
- * Integer, Long, Double, {@code null}. May not be
- * {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
- * infinities}.
- * @return this object.
- */
- public JsonObject put(String name, Object value) throws JsonException {
- return put(name, wrap(value));
- }
-
- /**
- * Maps {@code name} to {@code value}, clobbering any existing name/value
- * mapping with the same name. If the value is {@code null}, any existing
- * mapping for {@code name} is removed.
- *
- * @param value a {@link JsonObject}, {@link JsonArray}, {@link JsonString}, {@link JsonBoolean},
- * {@link JsonNumber}, {@link JsonNull}. May not be
- * {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
- * infinities}.
- * @return this object.
- */
- public JsonObject put(String name, JsonElement value) throws JsonException {
- checkIfFrozen();
- putInternal(name, value);
- return this;
- }
-
-
- /**
- * Equivalent to {@code put(name, value)} when both parameters are non-null;
- * does nothing otherwise.
- */
- public JsonObject putOpt(String name, Object value) throws JsonException {
- if (name == null || value == null) {
- return this;
- }
- return put(name, value);
- }
-
- /**
- * Appends {@code value} to the array already mapped to {@code name}. If
- * this object has no mapping for {@code name}, this inserts a new mapping.
- * If the mapping exists but its value is not an array, the existing
- * and new values are inserted in order into a new array which is itself
- * mapped to {@code name}. In aggregate, this allows values to be added to a
- * mapping one at a time.
- *
- *
Note that {@code append(String, Object)} provides better semantics.
- * In particular, the mapping for {@code name} will always be a
- * {@link JsonArray}. Using {@code accumulate} will result in either a
- * {@link JsonArray} or a mapping whose type is the type of {@code value}
- * depending on the number of calls to it.
- *
- * @param value a {@link JsonObject}, {@link JsonArray}, String, Boolean,
- * Integer, Long, Double or null. May not be {@link
- * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}.
- */
- // TODO: Change {@code append) to {@link #append} when append is
- // unhidden.
- public JsonObject accumulate(String name, Object value) throws JsonException {
- checkIfFrozen();
- Object current = nameValuePairs.get(checkName(name));
- if (current == null) {
- return put(name, value);
- }
-
- if (current instanceof JsonArray) {
- JsonArray array = (JsonArray) current;
- array.put(value);
- } else {
- JsonArray array = new JsonArray();
- array.put(current);
- array.put(value);
- nameValuePairs.put(name, array);
- }
- return this;
- }
-
- /**
- * Appends values to the array mapped to {@code name}. A new {@link JsonArray}
- * mapping for {@code name} will be inserted if no mapping exists. If the existing
- * mapping for {@code name} is not a {@link JsonArray}, a {@link JsonException}
- * will be thrown.
- *
- * @throws JsonException if {@code name} is {@code null} or if the mapping for
- * {@code name} is non-null and is not a {@link JsonArray}.
- * @hide
- */
- public JsonObject append(String name, Object value) throws JsonException {
- checkIfFrozen();
- Object current = nameValuePairs.get(checkName(name));
-
- final JsonArray array;
- if (current instanceof JsonArray) {
- array = (JsonArray) current;
- } else if (current == null) {
- JsonArray newArray = new JsonArray();
- nameValuePairs.put(name, newArray);
- array = newArray;
- } else {
- throw new JsonException("Key " + name + " is not a JsonArray");
- }
-
- array.put(value);
-
- return this;
- }
-
- String checkName(String name) {
- if (name == null) {
- throw new JsonException("Names must be non-null");
- }
- return name;
- }
-
- /**
- * Removes the named mapping if it exists; does nothing otherwise.
- *
- * @return the value previously mapped by {@code name}, or null if there was
- * no such mapping.
- */
- public Object remove(String name) {
- checkIfFrozen();
- return nameValuePairs.remove(name);
- }
-
- /**
- * Returns true if this object has no mapping for {@code name} or if it has
- * a mapping whose value is {@link JsonNull}.
- */
- public boolean isNull(String name) {
- JsonElement value = nameValuePairs.get(name);
- return value.isNull();
- }
-
- /**
- * Returns true if this object has a mapping for {@code name}. The mapping
- * may be {@link JsonNull}.
- */
- public boolean has(String name) {
- return nameValuePairs.containsKey(name);
- }
-
- /**
- * Returns the value mapped by {@code name}, or throws if no such mapping exists.
- *
- * @throws JsonException if no such mapping exists.
- */
- public JsonElement get(String name) throws JsonException {
- JsonElement result = nameValuePairs.get(name);
- if (result == null) {
- throw new JsonException("No value for " + name + ", in: " + this.toString());
- }
- return result;
- }
-
- /**
- * Returns the value mapped by {@code name}, or null if no such mapping
- * exists.
- */
- public JsonElement opt(String name) {
- return nameValuePairs.get(name);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a boolean or
- * can be coerced to a boolean, or throws otherwise.
- *
- * @throws JsonException if the mapping doesn't exist or cannot be coerced
- * to a boolean.
- */
- public Boolean getBoolean(String name, boolean strict) throws JsonException {
- JsonElement el = get(name);
- Boolean res = null;
- if (strict && !el.isBoolean()) {
- throw Util.typeMismatch(name, el, "boolean", true);
- }
- if (el.isBoolean()) {
- res = el.asBoolean();
- }
- if (el.isString()) {
- res = Util.toBoolean(el.asString());
- }
- if (res == null)
- throw Util.typeMismatch(name, el, "boolean", strict);
- return res;
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a boolean or
- * can be coerced to a boolean, or throws otherwise.
- *
- * @throws JsonException if the mapping doesn't exist or cannot be coerced
- * to a boolean.
- */
- public Boolean getBoolean(String name) throws JsonException {
- return getBoolean(name, true);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a boolean or
- * can be coerced to a boolean, or false otherwise.
- */
- public Boolean optBoolean(String name) {
- return optBoolean(name, null);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a boolean
- * or {@code fallback} otherwise.
- */
- public Boolean optBoolean(String name, Boolean fallback) {
- return optBoolean(name, fallback, true);
- }
-
- public Boolean optBoolean(String name, Boolean fallback, boolean strict) {
- try {
- return getBoolean(name, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a double or
- * can be coerced to a double, or throws otherwise.
- *
- * @throws JsonException if the mapping doesn't exist or cannot be coerced
- * to a double.
- */
- public Double getDouble(String name, boolean strict) throws JsonException {
- JsonElement el = get(name);
- Double res = null;
- if (strict && !el.isNumber()) {
- throw Util.typeMismatch(name, el, "double", true);
- }
- if (el.isNumber()) {
- res = el.asDouble();
- }
- if (el.isString()) {
- res = Util.toDouble(el.asString());
- }
- if (res == null)
- throw Util.typeMismatch(name, el, "double", strict);
- return res;
- }
-
- public Double getDouble(String name) throws JsonException {
- return getDouble(name, true);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a double or
- * can be coerced to a double, or {@code NaN} otherwise.
- */
- public Double optDouble(String name) {
- return optDouble(name, null);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a double or
- * can be coerced to a double, or {@code fallback} otherwise.
- */
- public Double optDouble(String name, Double fallback) {
- return optDouble(name, fallback, true);
- }
-
- public Double optDouble(String name, Double fallback, boolean strict) {
- try {
- return getDouble(name, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is an int or
- * can be coerced to an int, or throws otherwise.
- *
- * @throws JsonException if the mapping doesn't exist or cannot be coerced
- * to an int.
- */
- public Integer getInt(String name, boolean strict) throws JsonException {
- JsonElement el = get(name);
- Integer res = null;
- if (strict && !el.isNumber()) {
- throw Util.typeMismatch(name, el, "int", true);
- }
- if (el.isNumber()) {
- res = el.asInt();
- }
- if (el.isString()) {
- res = Util.toInteger(el.asString());
- }
- if (res == null)
- throw Util.typeMismatch(name, el, "int", strict);
- return res;
- }
-
- public Integer getInt(String name) throws JsonException {
- return getInt(name, true);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is an int or
- * can be coerced to an int, or 0 otherwise.
- */
- public Integer optInt(String name) {
- return optInt(name, null);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is an int or
- * can be coerced to an int, or {@code fallback} otherwise.
- */
- public Integer optInt(String name, Integer fallback) {
- return optInt(name, fallback, true);
- }
-
- public Integer optInt(String name, Integer fallback, boolean strict) {
- try {
- return getInt(name, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a long or
- * can be coerced to a long, or throws otherwise.
- * Note that Util represents numbers as doubles,
- * so this is lossy; use strings to transfer numbers via Util.
- *
- * @throws JsonException if the mapping doesn't exist or cannot be coerced
- * to a long.
- */
- public Long getLong(String name, boolean strict) throws JsonException {
- JsonElement el = get(name);
- Long res = null;
- if (strict && !el.isNumber()) {
- throw Util.typeMismatch(name, el, "long", true);
- }
- if (el.isNumber()) {
- res = el.asLong();
- }
- if (el.isString()) {
- res = Util.toLong(el.asString());
- }
- if (res == null)
- throw Util.typeMismatch(name, el, "long", strict);
- return res;
- }
-
- public Long getLong(String name) throws JsonException {
- return getLong(name, true);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a long or
- * can be coerced to a long, or 0 otherwise. Note that Util represents numbers as doubles,
- * so this is lossy; use strings to transfer numbers via Util.
- */
- public Long optLong(String name) {
- return optLong(name, null);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a long or
- * can be coerced to a long, or {@code fallback} otherwise. Note that Util represents
- * numbers as doubles, so this is lossy; use strings to transfer
- * numbers via Util.
- */
- public Long optLong(String name, Long fallback) {
- return optLong(name, fallback, true);
- }
-
- public Long optLong(String name, Long fallback, boolean strict) {
- try {
- return getLong(name, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists, coercing it if
- * necessary, or throws if no such mapping exists.
- *
- * @throws JsonException if no such mapping exists.
- */
- public String getString(String name, boolean strict) throws JsonException {
- JsonElement el = get(name);
- String res = null;
- if (strict && !el.isString()) {
- throw Util.typeMismatch(name, el, "string", true);
- }
- res = el.toString();
- if (res == null)
- throw Util.typeMismatch(name, el, "string", strict);
- return res;
- }
-
- public String getString(String name) throws JsonException {
- return getString(name, true);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists, coercing it if
- * necessary, or the empty string if no such mapping exists.
- */
- public String optString(String name) {
- return optString(name, null);
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists, coercing it if
- * necessary, or {@code fallback} if no such mapping exists.
- */
- public String optString(String name, String fallback) {
- return optString(name, fallback, true);
- }
-
- public String optString(String name, String fallback, boolean strict) {
- try {
- return getString(name, strict);
- } catch (JsonException e) {
- return fallback;
- }
- }
-
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a {@code
- * JsonArray}, or throws otherwise.
- *
- * @throws JsonException if the mapping doesn't exist or is not a {@code
- * JsonArray}.
- */
- public JsonArray getJsonArray(String name) throws JsonException {
- JsonElement el = get(name);
- if (!el.isJsonArray()) {
- throw Util.typeMismatch(name, el, "JsonArray");
- }
- return el.asJsonArray();
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a {@code
- * JsonArray}, or null otherwise.
- */
- public JsonArray optJsonArray(String name) {
- JsonElement el = null;
- try {
- el = get(name);
- } catch (JsonException e) {
- return null;
- }
- if (!el.isJsonArray()) {
- return null;
- }
- return el.asJsonArray();
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a {@code
- * JsonObject}, or throws otherwise.
- *
- * @throws JsonException if the mapping doesn't exist or is not a {@code
- * JsonObject}.
- */
- public JsonObject getJsonObject(String name) throws JsonException {
- JsonElement el = get(name);
- if (!el.isJsonObject()) {
- throw Util.typeMismatch(name, el, "JsonObject");
- }
- return el.asJsonObject();
- }
-
- /**
- * Returns the value mapped by {@code name} if it exists and is a {@code
- * JsonObject}, or null otherwise.
- */
- public JsonObject optJsonObject(String name) {
- JsonElement el = null;
- try {
- el = get(name);
- } catch (JsonException e) {
- return null;
- }
- if (!el.isJsonObject()) {
- return null;
- }
- return el.asJsonObject();
- }
-
- /**
- * Returns an array with the values corresponding to {@code names}. The
- * array contains null for names that aren't mapped. This method returns
- * null if {@code names} is either null or empty.
- */
- public JsonArray toJsonArray(JsonArray names) throws JsonException {
- JsonArray result = new JsonArray();
- if (names == null) {
- return null;
- }
- int length = names.length();
- if (length == 0) {
- return null;
- }
- for (int i = 0; i < length; i++) {
- String name = Util.toString(names.opt(i));
- result.put(opt(name));
- }
- return result;
- }
-
- /**
- * Returns an iterator of the {@code String} names in this object. The
- * returned iterator supports {@link Iterator#remove() remove}, which will
- * remove the corresponding mapping from this object. If this object is
- * modified after the iterator is returned, the iterator's behavior is
- * undefined. The order of the keys is undefined.
- */
- public Iterator keys() {
- return nameValuePairs.keySet().iterator();
- }
-
- /**
- * Returns the set of {@code String} names in this object. The returned set
- * is a view of the keys in this object. {@link Set#remove(Object)} will remove
- * the corresponding mapping from this object and set iterator behaviour
- * is undefined if this object is modified after it is returned.
- *
- * See {@link #keys()}.
- *
- * @hide.
- */
- public Set keySet() {
- return nameValuePairs.keySet();
- }
-
- public Collection valuesSet() {
- if (isFrozen()) Collections.unmodifiableCollection(nameValuePairs.values());
- return nameValuePairs.values();
- }
-
- /**
- * Returns an array containing the string names in this object. This method
- * returns null if this object contains no mappings.
- */
- public JsonArray names() throws JsonException {
- return nameValuePairs.isEmpty()
- ? null
- : new JsonArray(new ArrayList(nameValuePairs.keySet()));
- }
-
- @Override
- public void write(JsonWriter writer) throws IOException {
- writer.beginObject();
- for (Map.Entry e : nameValuePairs.entrySet()) {
- writer.name(e.getKey());
- e.getValue().write(writer);
- }
- writer.endObject();
- }
-
- @Override
- public boolean isJsonObject() {
- return true;
- }
-
- @Override
- public JsonObject asJsonObject() {
- return this;
- }
-
- @Override
- public boolean equals(Object o) {
- return o instanceof JsonObject && ((JsonObject) o).nameValuePairs.equals(nameValuePairs);
- }
-
- @Override
- public String getJsonType() {
- return TYPE_OBJECT;
- }
-
- @Override
- public Iterator> iterator() {
- return nameValuePairs.entrySet().iterator();
- }
-
- /**
- * Merge Json Object with another Json Object.
- * It does not change element of another with the same name exists.
- * However if the element is Json Object then it will go down and merge that object.
- *
- * @param another
- * @return
- */
- public JsonObject merge(JsonObject another) {
- for (Map.Entry anotherEntry : another) {
- JsonElement curr = this.opt(anotherEntry.getKey());
- if (curr == null) {
- try {
- this.put(anotherEntry.getKey(), anotherEntry.getValue());
- } catch (JsonException e) {
- e.printStackTrace();
- }
- } else if (curr.isJsonObject() && anotherEntry.getValue().isJsonObject()) {
- curr.asJsonObject().merge(anotherEntry.getValue().asJsonObject());
- }
- }
- return this;
- }
-
- public JsonObject clear() {
- checkIfFrozen();
- nameValuePairs.clear();
- return this;
- }
-
-
- @Override
- public boolean isFrozen() {
- return frozen;
- }
-
- @Override
- public JsonObject freeze() {
- frozen = true;
- for (JsonElement el : nameValuePairs.values()) {
- if (el.isJsonArray()) {
- el.asJsonArray().freeze();
- }
- if (el.isJsonObject()) {
- el.asJsonObject().freeze();
- }
- }
- return this;
- }
-
- @Override
- public JsonObject cloneAsThawed() {
- try {
- return JsonElement.readFrom(this.toString()).asJsonObject();
- } catch (IOException e) {
- e.printStackTrace();
- throw new JsonException("Cannot Recreate Json Object", e);
- }
- }
-
- public void checkIfFrozen() {
- if (isFrozen()) {
- throw new IllegalStateException(
- "Attempt to modify a frozen JsonObject instance.");
- }
- }
-
- void putInternal(String name, JsonElement value) {
- if (value == null) {
- value = JSON_NULL;
- }
- nameValuePairs.put(checkName(name), value);
- }
+public final class JsonObject extends JsonElement implements
+ Iterable>, Freezable {
+
+ private volatile boolean frozen = false;
+ private final LinkedTreeMap nameValuePairs = new LinkedTreeMap();
+
+ /**
+ * Creates a {@code JsonObject} with no name/value mappings.
+ */
+ public JsonObject() {
+
+ }
+
+ /**
+ * Creates a new {@code JSONObject} by copying all name/value mappings from
+ * the given map.
+ *
+ * @param copyFrom
+ * a map whose keys are of type {@link String} and whose values
+ * are of supported types.
+ * @throws NullPointerException
+ * if any of the map's keys are null.
+ */
+ /* (accept a raw type for API compatibility) */
+ public JsonObject(final Map copyFrom) throws JsonException {
+ this();
+ Map, ?> contentsTyped = copyFrom;
+ for (Map.Entry, ?> entry : contentsTyped.entrySet()) {
+ /*
+ * Deviate from the original by checking that keys are non-null and
+ * of the proper type. (We still defer validating the values).
+ */
+ String key = (String) entry.getKey();
+ if (key == null) {
+ throw new NullPointerException("key == null");
+ }
+ nameValuePairs.put(key, wrap(entry.getValue()));
+ }
+ }
+
+ /**
+ * Appends {@code value} to the array already mapped to {@code name}. If
+ * this object has no mapping for {@code name}, this inserts a new mapping.
+ * If the mapping exists but its value is not an array, the existing and new
+ * values are inserted in order into a new array which is itself mapped to
+ * {@code name}. In aggregate, this allows values to be added to a mapping
+ * one at a time.
+ *
+ *
+ * Note that {@code append(String, Object)} provides better semantics. In
+ * particular, the mapping for {@code name} will always be a
+ * {@link JsonArray}. Using {@code accumulate} will result in either a
+ * {@link JsonArray} or a mapping whose type is the type of {@code value}
+ * depending on the number of calls to it.
+ *
+ * @param value
+ * a {@link JsonObject}, {@link JsonArray}, String, Boolean,
+ * Integer, Long, Double or null. May not be
+ * {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
+ * infinities}.
+ */
+ // TODO: Change {@code append) to {@link #append} when append is
+ // unhidden.
+ public JsonObject accumulate(final String name, final Object value)
+ throws JsonException {
+ checkIfFrozen();
+ Object current = nameValuePairs.get(checkName(name));
+ if (current == null) {
+ return put(name, value);
+ }
+
+ if (current instanceof JsonArray) {
+ JsonArray array = (JsonArray) current;
+ array.put(value);
+ } else {
+ JsonArray array = new JsonArray();
+ array.put(current);
+ array.put(value);
+ nameValuePairs.put(name, array);
+ }
+ return this;
+ }
+
+ /**
+ * Appends values to the array mapped to {@code name}. A new
+ * {@link JsonArray} mapping for {@code name} will be inserted if no mapping
+ * exists. If the existing mapping for {@code name} is not a
+ * {@link JsonArray}, a {@link JsonException} will be thrown.
+ *
+ * @throws JsonException
+ * if {@code name} is {@code null} or if the mapping for
+ * {@code name} is non-null and is not a {@link JsonArray}.
+ * @hide
+ */
+ public JsonObject append(final String name, final Object value)
+ throws JsonException {
+ checkIfFrozen();
+ Object current = nameValuePairs.get(checkName(name));
+
+ final JsonArray array;
+ if (current instanceof JsonArray) {
+ array = (JsonArray) current;
+ } else if (current == null) {
+ JsonArray newArray = new JsonArray();
+ nameValuePairs.put(name, newArray);
+ array = newArray;
+ } else {
+ throw new JsonException("Key " + name + " is not a JsonArray");
+ }
+
+ array.put(value);
+
+ return this;
+ }
+
+ @Override
+ public JsonObject asJsonObject() {
+ return this;
+ }
+
+ public void checkIfFrozen() {
+ if (isFrozen()) {
+ throw new IllegalStateException(
+ "Attempt to modify a frozen JsonObject instance.");
+ }
+ }
+
+ String checkName(final String name) {
+ if (name == null) {
+ throw new JsonException("Names must be non-null");
+ }
+ return name;
+ }
+
+ public JsonObject clear() {
+ checkIfFrozen();
+ nameValuePairs.clear();
+ return this;
+ }
+
+ public JsonObject cloneAsThawed() {
+ try {
+ return JsonElement.readFrom(this.toString()).asJsonObject();
+ } catch (IOException e) {
+ e.printStackTrace();
+ throw new JsonException("Cannot Recreate Json Object", e);
+ }
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ return o instanceof JsonObject
+ && ((JsonObject) o).nameValuePairs.equals(nameValuePairs);
+ }
+
+ public JsonObject freeze() {
+ frozen = true;
+ for (JsonElement el : nameValuePairs.values()) {
+ if (el.isJsonArray()) {
+ el.asJsonArray().freeze();
+ }
+ if (el.isJsonObject()) {
+ el.asJsonObject().freeze();
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Returns the value mapped by {@code name}, or throws if no such mapping
+ * exists.
+ *
+ * @throws JsonException
+ * if no such mapping exists.
+ */
+ public JsonElement get(final String name) throws JsonException {
+ JsonElement result = nameValuePairs.get(name);
+ if (result == null) {
+ throw new JsonException("No value for " + name + ", in: "
+ + this.toString());
+ }
+ return result;
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a boolean or
+ * can be coerced to a boolean, or throws otherwise.
+ *
+ * @throws JsonException
+ * if the mapping doesn't exist or cannot be coerced to a
+ * boolean.
+ */
+ public Boolean getBoolean(final String name) throws JsonException {
+ return getBoolean(name, true);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a boolean or
+ * can be coerced to a boolean, or throws otherwise.
+ *
+ * @throws JsonException
+ * if the mapping doesn't exist or cannot be coerced to a
+ * boolean.
+ */
+ public Boolean getBoolean(final String name, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(name);
+ Boolean res = null;
+ if (strict && !el.isBoolean()) {
+ throw Util.typeMismatch(name, el, "boolean", true);
+ }
+ if (el.isBoolean()) {
+ res = el.asBoolean();
+ }
+ if (el.isString()) {
+ res = Util.toBoolean(el.asString());
+ }
+ if (res == null) {
+ throw Util.typeMismatch(name, el, "boolean", strict);
+ }
+ return res;
+ }
+
+ public Double getDouble(final String name) throws JsonException {
+ return getDouble(name, true);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a double or
+ * can be coerced to a double, or throws otherwise.
+ *
+ * @throws JsonException
+ * if the mapping doesn't exist or cannot be coerced to a
+ * double.
+ */
+ public Double getDouble(final String name, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(name);
+ Double res = null;
+ if (strict && !el.isNumber()) {
+ throw Util.typeMismatch(name, el, "double", true);
+ }
+ if (el.isNumber()) {
+ res = el.asDouble();
+ }
+ if (el.isString()) {
+ res = Util.toDouble(el.asString());
+ }
+ if (res == null) {
+ throw Util.typeMismatch(name, el, "double", strict);
+ }
+ return res;
+ }
+
+ public Integer getInt(final String name) throws JsonException {
+ return getInt(name, true);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is an int or
+ * can be coerced to an int, or throws otherwise.
+ *
+ * @throws JsonException
+ * if the mapping doesn't exist or cannot be coerced to an int.
+ */
+ public Integer getInt(final String name, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(name);
+ Integer res = null;
+ if (strict && !el.isNumber()) {
+ throw Util.typeMismatch(name, el, "int", true);
+ }
+ if (el.isNumber()) {
+ res = el.asInt();
+ }
+ if (el.isString()) {
+ res = Util.toInteger(el.asString());
+ }
+ if (res == null) {
+ throw Util.typeMismatch(name, el, "int", strict);
+ }
+ return res;
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a
+ * {@code JsonArray}, or throws otherwise.
+ *
+ * @throws JsonException
+ * if the mapping doesn't exist or is not a {@code JsonArray}.
+ */
+ public JsonArray getJsonArray(final String name) throws JsonException {
+ JsonElement el = get(name);
+ if (!el.isJsonArray()) {
+ throw Util.typeMismatch(name, el, "JsonArray");
+ }
+ return el.asJsonArray();
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a
+ * {@code JsonObject}, or throws otherwise.
+ *
+ * @throws JsonException
+ * if the mapping doesn't exist or is not a {@code JsonObject}.
+ */
+ public JsonObject getJsonObject(final String name) throws JsonException {
+ JsonElement el = get(name);
+ if (!el.isJsonObject()) {
+ throw Util.typeMismatch(name, el, "JsonObject");
+ }
+ return el.asJsonObject();
+ }
+
+ @Override
+ public String getJsonType() {
+ return TYPE_OBJECT;
+ }
+
+ public Long getLong(final String name) throws JsonException {
+ return getLong(name, true);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a long or
+ * can be coerced to a long, or throws otherwise. Note that Util represents
+ * numbers as doubles, so this is lossy; use strings to
+ * transfer numbers via Util.
+ *
+ * @throws JsonException
+ * if the mapping doesn't exist or cannot be coerced to a long.
+ */
+ public Long getLong(final String name, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(name);
+ Long res = null;
+ if (strict && !el.isNumber()) {
+ throw Util.typeMismatch(name, el, "long", true);
+ }
+ if (el.isNumber()) {
+ res = el.asLong();
+ }
+ if (el.isString()) {
+ res = Util.toLong(el.asString());
+ }
+ if (res == null) {
+ throw Util.typeMismatch(name, el, "long", strict);
+ }
+ return res;
+ }
+
+ public String getString(final String name) throws JsonException {
+ return getString(name, true);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists, coercing it if
+ * necessary, or throws if no such mapping exists.
+ *
+ * @throws JsonException
+ * if no such mapping exists.
+ */
+ public String getString(final String name, final boolean strict)
+ throws JsonException {
+ JsonElement el = get(name);
+ String res = null;
+ if (strict && !el.isString()) {
+ throw Util.typeMismatch(name, el, "string", true);
+ }
+ res = el.toString();
+ if (res == null) {
+ throw Util.typeMismatch(name, el, "string", strict);
+ }
+ return res;
+ }
+
+ /**
+ * Returns true if this object has a mapping for {@code name}. The mapping
+ * may be {@link JsonNull}.
+ */
+ public boolean has(final String name) {
+ return nameValuePairs.containsKey(name);
+ }
+
+ public boolean isFrozen() {
+ return frozen;
+ }
+
+ @Override
+ public boolean isJsonObject() {
+ return true;
+ }
+
+ /**
+ * Returns true if this object has no mapping for {@code name} or if it has
+ * a mapping whose value is {@link JsonNull}.
+ */
+ public boolean isNull(final String name) {
+ JsonElement value = nameValuePairs.get(name);
+ return value.isNull();
+ }
+
+ public Iterator> iterator() {
+ return nameValuePairs.entrySet().iterator();
+ }
+
+ /**
+ * Returns an iterator of the {@code String} names in this object. The
+ * returned iterator supports {@link Iterator#remove() remove}, which will
+ * remove the corresponding mapping from this object. If this object is
+ * modified after the iterator is returned, the iterator's behavior is
+ * undefined. The order of the keys is undefined.
+ */
+ public Iterator keys() {
+ return nameValuePairs.keySet().iterator();
+ }
+
+ /**
+ * Returns the set of {@code String} names in this object. The returned set
+ * is a view of the keys in this object. {@link Set#remove(Object)} will
+ * remove the corresponding mapping from this object and set iterator
+ * behaviour is undefined if this object is modified after it is returned.
+ *
+ * See {@link #keys()}.
+ *
+ * @hide.
+ */
+ public Set keySet() {
+ return nameValuePairs.keySet();
+ }
+
+ /**
+ * Returns the number of name/value mappings in this object.
+ */
+ public int length() {
+ return nameValuePairs.size();
+ }
+
+ /**
+ * Merge Json Object with another Json Object. It does not change element of
+ * another with the same name exists. However if the element is Json Object
+ * then it will go down and merge that object.
+ *
+ * @param another
+ * @return
+ */
+ public JsonObject merge(final JsonObject another) {
+ for (Map.Entry anotherEntry : another) {
+ JsonElement curr = this.opt(anotherEntry.getKey());
+ if (curr == null) {
+ try {
+ this.put(anotherEntry.getKey(), anotherEntry.getValue());
+ } catch (JsonException e) {
+ e.printStackTrace();
+ }
+ } else if (curr.isJsonObject()
+ && anotherEntry.getValue().isJsonObject()) {
+ curr.asJsonObject().merge(
+ anotherEntry.getValue().asJsonObject());
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Returns an array containing the string names in this object. This method
+ * returns null if this object contains no mappings.
+ */
+ public JsonArray names() throws JsonException {
+ return nameValuePairs.isEmpty() ? null : new JsonArray(
+ new ArrayList(nameValuePairs.keySet()));
+ }
+
+ /**
+ * Returns the value mapped by {@code name}, or null if no such mapping
+ * exists.
+ */
+ public JsonElement opt(final String name) {
+ return nameValuePairs.get(name);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a boolean or
+ * can be coerced to a boolean, or false otherwise.
+ */
+ public Boolean optBoolean(final String name) {
+ return optBoolean(name, false);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a boolean or
+ * {@code fallback} otherwise.
+ */
+ public Boolean optBoolean(final String name, final Boolean fallback) {
+ return optBoolean(name, fallback, true);
+ }
+
+ public Boolean optBoolean(final String name, final Boolean fallback,
+ final boolean strict) {
+ try {
+ return getBoolean(name, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a double or
+ * can be coerced to a double, or {@code NaN} otherwise.
+ */
+ public Double optDouble(final String name) {
+ return optDouble(name, null);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a double or
+ * can be coerced to a double, or {@code fallback} otherwise.
+ */
+ public Double optDouble(final String name, final Double fallback) {
+ return optDouble(name, fallback, true);
+ }
+
+ public Double optDouble(final String name, final Double fallback,
+ final boolean strict) {
+ try {
+ return getDouble(name, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is an int or
+ * can be coerced to an int, or 0 otherwise.
+ */
+ public Integer optInt(final String name) {
+ return optInt(name, 0);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is an int or
+ * can be coerced to an int, or {@code fallback} otherwise.
+ */
+ public Integer optInt(final String name, final Integer fallback) {
+ return optInt(name, fallback, true);
+ }
+
+ public Integer optInt(final String name, final Integer fallback,
+ final boolean strict) {
+ try {
+ return getInt(name, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a
+ * {@code JsonArray}, or null otherwise.
+ */
+ public JsonArray optJsonArray(final String name) {
+ JsonElement el = null;
+ try {
+ el = get(name);
+ } catch (JsonException e) {
+ return null;
+ }
+ if (!el.isJsonArray()) {
+ return null;
+ }
+ return el.asJsonArray();
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a
+ * {@code JsonObject}, or null otherwise.
+ */
+ public JsonObject optJsonObject(final String name) {
+ JsonElement el = null;
+ try {
+ el = get(name);
+ } catch (JsonException e) {
+ return null;
+ }
+ if (!el.isJsonObject()) {
+ return null;
+ }
+ return el.asJsonObject();
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a long or
+ * can be coerced to a long, or 0 otherwise. Note that Util represents
+ * numbers as doubles, so this is lossy; use strings to
+ * transfer numbers via Util.
+ */
+ public Long optLong(final String name) {
+ return optLong(name, (long) 0);
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists and is a long or
+ * can be coerced to a long, or {@code fallback} otherwise. Note that Util
+ * represents numbers as doubles, so this is lossy; use
+ * strings to transfer numbers via Util.
+ */
+ public Long optLong(final String name, final Long fallback) {
+ return optLong(name, fallback, true);
+ }
+
+ public Long optLong(final String name, final Long fallback,
+ final boolean strict) {
+ try {
+ return getLong(name, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists, coercing it if
+ * necessary, or the empty string if no such mapping exists.
+ */
+ public String optString(final String name) {
+ return optString(name, "");
+ }
+
+ /**
+ * Returns the value mapped by {@code name} if it exists, coercing it if
+ * necessary, or {@code fallback} if no such mapping exists.
+ */
+ public String optString(final String name, final String fallback) {
+ return optString(name, fallback, true);
+ }
+
+ public String optString(final String name, final String fallback,
+ final boolean strict) {
+ try {
+ return getString(name, strict);
+ } catch (JsonException e) {
+ return fallback;
+ }
+ }
+
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name.
+ *
+ * @return this object.
+ */
+ public JsonObject put(final String name, final boolean value)
+ throws JsonException {
+ return put(name, new JsonBoolean(value));
+ }
+
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * 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.
+ */
+ public JsonObject put(final String name, final double value)
+ throws JsonException {
+ return put(name, new JsonNumber(value));
+ }
+
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name.
+ *
+ * @return this object.
+ */
+ public JsonObject put(final String name, final int value)
+ throws JsonException {
+ return put(name, new JsonNumber(value));
+ }
+
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name. If the value is {@code null}, any existing
+ * mapping for {@code name} is removed.
+ *
+ * @param value
+ * a {@link JsonObject}, {@link JsonArray}, {@link JsonString},
+ * {@link JsonBoolean}, {@link JsonNumber}, {@link JsonNull}. May
+ * not be {@link Double#isNaN() NaNs} or
+ * {@link Double#isInfinite() infinities}.
+ * @return this object.
+ */
+ public JsonObject put(final String name, final JsonElement value)
+ throws JsonException {
+ checkIfFrozen();
+ putInternal(name, value);
+ return this;
+ }
+
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name.
+ *
+ * @return this object.
+ */
+ public JsonObject put(final String name, final long value)
+ throws JsonException {
+ return put(name, new JsonNumber(value));
+ }
+
+ /**
+ * Maps {@code name} to {@code value}, clobbering any existing name/value
+ * mapping with the same name. If the value is {@code null}, any existing
+ * mapping for {@code name} is removed.
+ *
+ * @param value
+ * a {@link JsonObject}, {@link JsonArray}, String, Boolean,
+ * Integer, Long, Double, {@code null}. May not be
+ * {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
+ * infinities}.
+ * @return this object.
+ */
+ public JsonObject put(final String name, final Object value)
+ throws JsonException {
+ return put(name, wrap(value));
+ }
+
+ void putInternal(final String name, JsonElement value) {
+ if (value == null) {
+ value = JSON_NULL;
+ }
+ nameValuePairs.put(checkName(name), value);
+ }
+
+ /**
+ * Equivalent to {@code put(name, value)} when both parameters are non-null;
+ * does nothing otherwise.
+ */
+ public JsonObject putOpt(final String name, final Object value)
+ throws JsonException {
+ if (name == null || value == null) {
+ return this;
+ }
+ 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
+ * no such mapping.
+ */
+ public Object remove(final String name) {
+ checkIfFrozen();
+ return nameValuePairs.remove(name);
+ }
+
+ /**
+ * Returns an array with the values corresponding to {@code names}. The
+ * array contains null for names that aren't mapped. This method returns
+ * null if {@code names} is either null or empty.
+ */
+ public JsonArray toJsonArray(final JsonArray names) throws JsonException {
+ JsonArray result = new JsonArray();
+ if (names == null) {
+ return null;
+ }
+ int length = names.length();
+ if (length == 0) {
+ return null;
+ }
+ for (int i = 0; i < length; i++) {
+ String name = Util.toString(names.opt(i));
+ result.put(opt(name));
+ }
+ return result;
+ }
+
+ public Collection valuesSet() {
+ if (isFrozen()) {
+ Collections.unmodifiableCollection(nameValuePairs.values());
+ }
+ return nameValuePairs.values();
+ }
+
+ @Override
+ public void write(final JsonWriter writer) throws IOException {
+ writer.beginObject();
+ StringBuilder indent = new StringBuilder();
+
+ for (Map.Entry e : nameValuePairs.entrySet()) {
+
+ writer.name(indent + e.getKey());
+ e.getValue().write(writer);
+ }
+ writer.endObject();
+ }
+
}
diff --git a/json-core/src/test/java/io/apptik/json/test/FreezingTest.java b/json-core/src/test/java/io/apptik/json/test/FreezingTest.java
index 35e0700..598058b 100644
--- a/json-core/src/test/java/io/apptik/json/test/FreezingTest.java
+++ b/json-core/src/test/java/io/apptik/json/test/FreezingTest.java
@@ -1,319 +1,313 @@
package io.apptik.json.test;
-
+import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import io.apptik.json.JsonArray;
import io.apptik.json.JsonElement;
import io.apptik.json.JsonObject;
import io.apptik.json.JsonString;
-import org.junit.Test;
import java.util.ArrayList;
-import static junit.framework.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import org.junit.Test;
public class FreezingTest {
- @Test
- public void objectChildrenFreezing() {
- JsonObject object = new JsonObject();
- JsonArray childArray = new JsonArray();
- JsonObject childObject = new JsonObject();
- object.put("foo", childArray);
- object.put("bar", childObject);
- object.freeze();
- try {
- childArray.put(true);
- fail();
- } catch (IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
-
- try {
- childObject.put("foochild",true);
- fail();
- } catch (IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
-
- }
-
- @Test
- public void arrayChildrenFreezing() {
- JsonArray array = new JsonArray();
- JsonArray childArray = new JsonArray();
- JsonObject childObject = new JsonObject();
- array.put(childArray);
- array.put(childObject);
- array.freeze();
- try {
- childArray.put(true);
- fail();
- } catch (IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
-
- try {
- childObject.put("foochild",true);
- fail();
- } catch (IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
-
- }
+ @Test
+ public void arrayChildrenFreezing() {
+ JsonArray array = new JsonArray();
+ JsonArray childArray = new JsonArray();
+ JsonObject childObject = new JsonObject();
+ array.put(childArray);
+ array.put(childObject);
+ array.freeze();
+ try {
+ childArray.put(true);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ childObject.put("foochild", true);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
- @Test
- public void objectFreezing() {
- JsonObject object = new JsonObject();
- object.put("foo", true);
- assertTrue(object.getBoolean("foo"));
- object.freeze();
- try {
- object.put("bar", 20);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
+ }
- try {
- object.put("bar", 5.5);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
+ @Test
+ public void arrayFreezing() {
+ JsonArray array = new JsonArray();
+ array.put(true);
+ assertTrue(array.getBoolean(0));
+ array.freeze();
+ // Adding
+ try {
+ array.add(new JsonString("xx"));
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.add(0, new JsonString("xx"));
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
- try {
- object.put("bar", null);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
+ ArrayList els = new ArrayList();
+ els.add(new JsonString("xx"));
+ els.add(new JsonString("xx"));
+ try {
+ array.addAll(els);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.addAll(0, els);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ // Removing
+ try {
+ array.remove(0);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.removeAll(els);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.retainAll(els);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.clear();
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
- try {
- object.put("bar", "xx");
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
+ // putting
+ try {
+ array.put(0, true);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(true);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
- try {
- object.put("bar", new JsonObject());
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
+ try {
+ array.put(0, 5);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(5);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(0, 5.5);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(5.5);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(0, null);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(null);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(0, "xx");
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put("xx");
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(0, new JsonObject());
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(new JsonObject());
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
- try {
- object.put("bar", new JsonArray());
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
+ try {
+ array.put(0, new JsonArray());
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ try {
+ array.put(new JsonArray());
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
+ }
+ @Test
+ public void objectChildrenFreezing() {
+ JsonObject object = new JsonObject();
+ JsonArray childArray = new JsonArray();
+ JsonObject childObject = new JsonObject();
+ object.put("foo", childArray);
+ object.put("bar", childObject);
+ object.freeze();
+ try {
+ childArray.put(true);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonArray instance.",
+ ex.getMessage());
+ }
- try {
- object.clear();
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
+ try {
+ childObject.put("foochild", true);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
+ }
- try {
- object.remove("bar");
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonObject instance.",
- ex.getMessage());
- }
- }
+ @Test
+ public void objectFreezing() {
+ JsonObject object = new JsonObject();
+ object.put("foo", true);
+ assertTrue(object.getBoolean("foo"));
+ object.freeze();
+ try {
+ object.put("bar", 20);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
+ try {
+ object.put("bar", 5.5);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
- @Test
- public void arrayFreezing() {
- JsonArray array = new JsonArray();
- array.put(true);
- assertTrue(array.getBoolean(0));
- array.freeze();
- //Adding
- try {
- array.add(new JsonString("xx"));
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.add(0,new JsonString("xx"));
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
+ try {
+ object.put("bar", null);
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
- ArrayList els = new ArrayList<>();
- els.add(new JsonString("xx"));
- els.add(new JsonString("xx"));
- try {
- array.addAll(els);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.addAll(0,els);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- //Removing
- try {
- array.remove(0);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.removeAll(els);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.retainAll(els);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.clear();
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
+ try {
+ object.put("bar", "xx");
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
- //putting
+ try {
+ object.put("bar", new JsonObject());
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
- try {
- array.put(0,true);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(true);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
+ try {
+ object.put("bar", new JsonArray());
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
- try {
- array.put(0,5);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(5);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(0,5.5);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(5.5);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(0,null);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(null);
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(0,"xx");
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put("xx");
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(0,new JsonObject());
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(new JsonObject());
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
+ try {
+ object.clear();
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
- try {
- array.put(0,new JsonArray());
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- try {
- array.put(new JsonArray());
- fail();
- } catch(IllegalStateException ex) {
- assertEquals("Attempt to modify a frozen JsonArray instance.",
- ex.getMessage());
- }
- }
+ try {
+ object.remove("bar");
+ fail();
+ } catch (IllegalStateException ex) {
+ assertEquals("Attempt to modify a frozen JsonObject instance.",
+ ex.getMessage());
+ }
+ }
}
diff --git a/json-core/src/test/java/io/apptik/json/test/JsonObjectTest.java b/json-core/src/test/java/io/apptik/json/test/JsonObjectTest.java
index d49da69..e4d76d2 100644
--- a/json-core/src/test/java/io/apptik/json/test/JsonObjectTest.java
+++ b/json-core/src/test/java/io/apptik/json/test/JsonObjectTest.java
@@ -17,11 +17,13 @@
package io.apptik.json.test;
-import junit.framework.TestCase;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import static io.apptik.json.JsonNull.JSON_NULL;
+import io.apptik.json.JsonArray;
+import io.apptik.json.JsonElement;
+import io.apptik.json.JsonNumber;
+import io.apptik.json.JsonObject;
+import io.apptik.json.JsonString;
+import io.apptik.json.exception.JsonException;
import java.net.ConnectException;
import java.util.ArrayList;
@@ -36,896 +38,926 @@
import java.util.Set;
import java.util.TreeMap;
-import io.apptik.json.JsonArray;
-import io.apptik.json.JsonNumber;
-import io.apptik.json.JsonObject;
-import io.apptik.json.JsonString;
-import io.apptik.json.exception.JsonException;
-
-import static io.apptik.json.JsonNull.JSON_NULL;
+import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
/**
- * This black box test was written without inspecting the non-free org.json sourcecode.
+ * This black box test was written without inspecting the non-free org.json
+ * sourcecode.
*/
@RunWith(JUnit4.class)
public class JsonObjectTest extends TestCase {
- @Test
- public void testEmptyObject() throws JsonException {
- JsonObject object = new JsonObject();
- assertEquals(0, object.length());
-
- // bogus (but documented) behaviour: returns null rather than the empty object!
- assertNull(object.names());
-
- // returns null rather than an empty array!
- assertNull(object.toJsonArray(new JsonArray()));
- assertEquals("{}", object.toString());
- try {
- object.get("foo");
- fail();
- } catch (JsonException e) {
- }
- try {
- object.getBoolean("foo");
- fail();
- } catch (JsonException e) {
- }
- try {
- object.getDouble("foo");
- fail();
- } catch (JsonException e) {
- }
- try {
- object.getInt("foo");
- fail();
- } catch (JsonException e) {
- }
- try {
- object.getJsonArray("foo");
- fail();
- } catch (JsonException e) {
- }
- try {
- object.getJsonObject("foo");
- fail();
- } catch (JsonException e) {
- }
- try {
- object.getLong("foo");
- fail();
- } catch (JsonException e) {
- }
- try {
- object.getString("foo");
- fail();
- } catch (JsonException e) {
- }
- assertFalse(object.has("foo"));
- assertNull(object.opt("foo"));
- assertNull(object.optBoolean("foo"));
- assertEquals(Boolean.TRUE, object.optBoolean("foo", Boolean.TRUE));
- assertNull(object.optDouble("foo"));
- assertEquals(5.0, object.optDouble("foo", 5.0));
- assertNull(object.optInt("foo"));
- assertEquals((Integer)5, object.optInt("foo", 5));
- assertEquals(null, object.optJsonArray("foo"));
- assertEquals(null, object.optJsonObject("foo"));
- assertNull(object.optLong("foo"));
- assertEquals(Long.valueOf(Long.MAX_VALUE-1), object.optLong("foo", Long.MAX_VALUE-1));
- assertNull(object.optString("foo")); // empty string is default!
- assertEquals("bar", object.optString("foo", "bar"));
- assertNull(object.remove("foo"));
- }
-
- @Test
- public void testEqualsAndHashCode() throws JsonException {
- JsonObject a = new JsonObject();
- JsonObject b = new JsonObject();
-
- // Json object override equals
- assertTrue(a.equals(b));
- assertEquals(a.hashCode(), System.identityHashCode(a));
- }
-
- @Test
- public void testGet() throws JsonException {
- JsonObject object = new JsonObject();
- Object value = new Object();
- object.put("foo", value);
- object.put("bar", new Object());
- object.put("baz", new Object());
- assertEquals(object.get("foo"), value.toString());
- try {
- object.get("FOO");
- fail();
- } catch (JsonException e) {
- }
- try {
- object.put(null, value);
- fail();
- } catch (JsonException e) {
- }
- try {
- object.get(null);
- fail();
- } catch (JsonException e) {
- }
- }
-
- @Test
- public void testPut() throws JsonException {
- JsonObject object = new JsonObject();
- assertSame(object, object.put("foo", Boolean.TRUE));
- object.put("foo", Boolean.FALSE);
- assertEquals(Boolean.FALSE, object.getBoolean("foo"));
-
- object.put("foo", 5.0d);
- assertEquals(5.0d, object.getDouble("foo"));
- object.put("foo", 0);
- assertEquals((Integer)0, object.getInt("foo"));
- object.put("bar", Long.MAX_VALUE - 1);
- assertEquals((Long)(Long.MAX_VALUE - 1), object.getLong("bar"));
- object.put("baz", "x");
- assertEquals("x", object.get("baz").toString());
- object.put("bar", null);
- assertEquals(object.get("bar"), null);
- }
-
- @Test
- public void testPutNullRemoves() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", "bar");
- object.put("foo", (Collection) null);
- assertEquals(1, object.length());
- assertTrue(object.has("foo"));
- assertEquals(object.get("foo"), null);
- }
-
- @Test
- public void testPutOpt() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", "bar");
- object.putOpt("foo", null);
- assertEquals(object.get("foo"), "bar");
- object.putOpt(null, null);
- assertEquals(1, object.length());
- object.putOpt(null, "bar");
- assertEquals(1, object.length());
- }
-
- @Test
- public void testPutOptUnsupportedNumbers() throws JsonException {
- JsonObject object = new JsonObject();
- try {
- object.putOpt("foo", Double.NaN);
- fail();
- } catch (IllegalArgumentException e) {
- }
- try {
- object.putOpt("foo", Double.NEGATIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException e) {
- }
- try {
- object.putOpt("foo", Double.POSITIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException e) {
- }
- }
-
- @Test
- public void testRemove() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", "bar");
- assertEquals(null, object.remove(null));
- assertEquals(null, object.remove(""));
- assertEquals(null, object.remove("bar"));
- assertEquals(object.remove("foo"), "bar");
- assertEquals(null, object.remove("foo"));
- }
-
- @Test
- public void testBooleans() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", Boolean.TRUE);
- object.put("bar", Boolean.FALSE);
- object.put("baz", "true");
- object.put("quux", "false");
- assertEquals(4, object.length());
- assertEquals(Boolean.TRUE, object.getBoolean("foo"));
- assertEquals(Boolean.FALSE, object.getBoolean("bar"));
- assertEquals(Boolean.TRUE, object.getBoolean("baz", Boolean.FALSE));
- assertEquals(Boolean.FALSE, object.getBoolean("quux", Boolean.FALSE));
- assertFalse(object.isNull("foo"));
- assertFalse(object.isNull("quux"));
- assertTrue(object.has("foo"));
- assertTrue(object.has("quux"));
- assertFalse(object.has("missing"));
- assertEquals(Boolean.TRUE, object.optBoolean("foo"));
- assertEquals(Boolean.FALSE, object.optBoolean("bar"));
- assertEquals(Boolean.TRUE, object.optBoolean("baz", Boolean.FALSE, Boolean.FALSE));
- assertNull(object.optBoolean("quux"));
- assertNull(object.optBoolean("missing"));
- assertEquals(Boolean.TRUE, object.optBoolean("foo", Boolean.TRUE));
- assertEquals(Boolean.FALSE, object.optBoolean("bar", Boolean.TRUE));
- assertEquals(Boolean.TRUE, object.optBoolean("baz", Boolean.TRUE));
- assertEquals(Boolean.FALSE, object.optBoolean("quux", Boolean.TRUE, Boolean.FALSE));
- assertEquals(Boolean.TRUE, object.optBoolean("missing", Boolean.TRUE));
-
- object.put("foo", "truE");
- object.put("bar", "FALSE");
- assertEquals(Boolean.TRUE, object.getBoolean("foo", Boolean.FALSE));
- assertEquals(Boolean.FALSE, object.getBoolean("bar", Boolean.FALSE));
- assertEquals(Boolean.TRUE, object.optBoolean("foo", Boolean.FALSE, Boolean.FALSE));
- assertEquals(Boolean.FALSE, object.optBoolean("bar", Boolean.TRUE, Boolean.FALSE));
- }
-
- @Test
- public void testCoerceStringToBoolean() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", "maybe");
- try {
- object.getBoolean("foo");
- fail();
- } catch (JsonException expected) {
- }
- assertNull(object.optBoolean("foo"));
- assertEquals(Boolean.TRUE, object.optBoolean("foo", Boolean.TRUE));
- }
-
- @Test
- public void testNumbers() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", Double.MIN_VALUE);
- object.put("bar", 9223372036854775806L);
- object.put("baz", Double.MAX_VALUE);
- object.put("quux", -0d);
- assertEquals(4, object.length());
-
- String toString = object.toString();
- assertTrue(toString, toString.contains("\"foo\":4.9E-324"));
- assertTrue(toString, toString.contains("\"bar\":9223372036854775806"));
- assertTrue(toString, toString.contains("\"baz\":1.7976931348623157E308"));
-
- assertTrue(toString, toString.contains("\"quux\":-0.0}") // no trailing decimal point
- || toString.contains("\"quux\":-0.0,"));
-
- assertEquals(object.get("foo"), Double.MIN_VALUE);
- assertEquals(object.get("bar"),9223372036854775806L);
- assertEquals(object.get("baz"),Double.MAX_VALUE);
- assertEquals(object.get("quux"),-0d);
- assertEquals(Double.MIN_VALUE, object.getDouble("foo"));
- assertEquals(9.223372036854776E18, object.getDouble("bar"));
- assertEquals(Double.MAX_VALUE, object.getDouble("baz"));
- assertEquals(-0d, object.getDouble("quux"));
- assertEquals((Long)0l, object.getLong("foo"));
- assertEquals((Long)9223372036854775806L, object.getLong("bar"));
- assertEquals((Long)Long.MAX_VALUE, object.getLong("baz"));
- assertEquals((Long)0l, object.getLong("quux"));
- assertEquals((Integer)0, object.getInt("foo"));
- assertEquals((Integer)(-2), object.getInt("bar"));
- assertEquals((Integer)Integer.MAX_VALUE, object.getInt("baz"));
- assertEquals((Integer)0, object.getInt("quux"));
- assertEquals(object.opt("foo"), Double.MIN_VALUE);
- assertEquals((Long)9223372036854775806L, object.optLong("bar"));
- assertEquals(Double.MAX_VALUE, object.optDouble("baz"));
- assertEquals((Integer)0, object.optInt("quux"));
- assertEquals(object.opt("foo"),Double.MIN_VALUE);
- assertEquals((Long)9223372036854775806L, object.optLong("bar"));
- assertEquals(Double.MAX_VALUE, object.optDouble("baz"));
- assertEquals((Integer)0, object.optInt("quux"));
- assertEquals(Double.MIN_VALUE, object.optDouble("foo", 5.0d));
- assertEquals((Long)9223372036854775806L, object.optLong("bar", 1L));
- assertEquals((Long)Long.MAX_VALUE, object.optLong("baz", 1L));
- assertEquals((Integer)0, object.optInt("quux", -1));
- assertEquals("4.9E-324", object.getString("foo", Boolean.FALSE));
- assertEquals("9223372036854775806", object.getString("bar", Boolean.FALSE));
- assertEquals("1.7976931348623157E308", object.getString("baz", Boolean.FALSE));
- assertEquals("-0.0", object.getString("quux", Boolean.FALSE));
- }
-
- @Test
- public void testFloats() throws JsonException {
- JsonObject object = new JsonObject();
- try {
- object.put("foo", (Float) Float.NaN);
- fail();
- } catch (IllegalArgumentException e) {
- }
- try {
- object.put("foo", (Float) Float.NEGATIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException e) {
- }
- try {
- object.put("foo", (Float) Float.POSITIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException e) {
- }
- }
-
- @Test
- public void testOtherNumbers() throws JsonException {
- Number nan = new Number() {
- public int intValue() {
- throw new UnsupportedOperationException();
- }
- public long longValue() {
- throw new UnsupportedOperationException();
- }
- public float floatValue() {
- throw new UnsupportedOperationException();
- }
- public double doubleValue() {
- return Double.NaN;
- }
- @Override public String toString() {
- return "x";
- }
- };
-
- JsonObject object = new JsonObject();
- try {
- object.put("foo", nan);
- fail("Object.put() accepted a NaN (via a custom Number class)");
- } catch (IllegalArgumentException e) {
- }
- }
-
- @Test
- public void testForeignObjects() throws JsonException {
- Object foreign = new Object() {
- @Override public String toString() {
- return "x";
- }
- };
-
- // foreign object types are accepted and treated as Strings!
- JsonObject object = new JsonObject();
- object.put("foo", foreign);
- assertEquals("{\"foo\":\"x\"}", object.toString());
- }
-
- @Test
- public void testNullKeys() {
- try {
- new JsonObject().put(null, Boolean.FALSE);
- fail();
- } catch (JsonException e) {
- }
- try {
- new JsonObject().put(null, 0.0d);
- fail();
- } catch (JsonException e) {
- }
- try {
- new JsonObject().put(null, 5);
- fail();
- } catch (JsonException e) {
- }
- try {
- new JsonObject().put(null, 5L);
- fail();
- } catch (JsonException e) {
- }
- try {
- new JsonObject().put(null, "foo");
- fail();
- } catch (JsonException e) {
- }
- }
-
- @Test
- public void testStrings() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", "true");
- object.put("bar", "5.5");
- object.put("baz", "9223372036854775806");
- object.put("quux", "null");
- object.put("height", "5\"8' tall");
-
- assertTrue(object.toString().contains("\"foo\":\"true\""));
- assertTrue(object.toString().contains("\"bar\":\"5.5\""));
- assertTrue(object.toString().contains("\"baz\":\"9223372036854775806\""));
- assertTrue(object.toString().contains("\"quux\":\"null\""));
- assertTrue(object.toString().contains("\"height\":\"5\\\"8' tall\""));
-
- assertEquals("true", object.get("foo").toString());
- assertEquals("null", object.getString("quux"));
- assertEquals("5\"8' tall", object.getString("height"));
- assertEquals("true", object.opt("foo").toString());
- assertEquals("5.5", object.optString("bar"));
- assertEquals("true", object.optString("foo", "x"));
- assertFalse(object.isNull("foo"));
-
- assertEquals(Boolean.TRUE, object.optBoolean("foo", Boolean.TRUE, Boolean.FALSE));
- assertNull(object.optInt("foo"));
- assertEquals((Integer)(-2), object.optInt("foo", -2));
-
- assertEquals(5.5d, object.getDouble("bar", Boolean.FALSE));
- assertEquals((Long)5L, object.getLong("bar", Boolean.FALSE));
- assertEquals((Integer)5, object.getInt("bar", Boolean.FALSE));
- assertEquals((Integer)5, object.optInt("bar", 3, Boolean.FALSE));
-
- // The last digit of the string is a 6 but getLong returns a 7. It's probably parsing as a
- // double and then converting that to a long. This is consistent with JavaScript.
- assertEquals((Long)9223372036854775807L, object.getLong("baz", Boolean.FALSE));
- assertEquals(9.223372036854776E18, object.getDouble("baz", Boolean.FALSE));
- assertEquals((Integer)Integer.MAX_VALUE, object.getInt("baz", Boolean.FALSE));
-
- assertFalse(object.isNull("quux"));
- try {
- object.getDouble("quux");
- fail();
- } catch (JsonException e) {
- }
- assertNull(object.optDouble("quux"));
- assertEquals(-1.0d, object.optDouble("quux", -1.0d));
-
- object.put("foo", "TRUE");
- assertEquals(Boolean.TRUE, object.getBoolean("foo", Boolean.FALSE));
- }
-
- @Test
- public void testJsonObjects() throws JsonException {
- JsonObject object = new JsonObject();
-
- JsonArray a = new JsonArray();
- JsonObject b = new JsonObject();
- object.put("foo", a);
- object.put("bar", b);
-
- assertSame(a, object.getJsonArray("foo"));
- assertSame(b, object.getJsonObject("bar"));
- try {
- object.getJsonObject("foo");
- fail();
- } catch (JsonException e) {
- }
- try {
- object.getJsonArray("bar");
- fail();
- } catch (JsonException e) {
- }
- assertEquals(a, object.optJsonArray("foo"));
- assertEquals(b, object.optJsonObject("bar"));
- assertEquals(null, object.optJsonArray("bar"));
- assertEquals(null, object.optJsonObject("foo"));
- }
-
- @Test
- public void testNullCoercionToString() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", JSON_NULL);
- assertEquals("null", object.getString("foo", Boolean.FALSE));
- }
-
- @Test
- public void testArrayCoercion() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", "[Boolean.TRUE]");
- try {
- object.getJsonArray("foo");
- fail();
- } catch (JsonException e) {
- }
- }
-
- @Test
- public void testObjectCoercion() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", "{}");
- try {
- object.getJsonObject("foo");
- fail();
- } catch (JsonException e) {
- }
- }
-
- @Test
- public void testAccumulateValueChecking() throws JsonException {
- JsonObject object = new JsonObject();
- try {
- object.accumulate("foo", Double.NaN);
- fail();
- } catch (IllegalArgumentException e) {
- }
- object.accumulate("foo", 1);
- try {
- object.accumulate("foo", Double.NaN);
- fail();
- } catch (IllegalArgumentException e) {
- }
- object.accumulate("foo", 2);
- try {
- object.accumulate("foo", Double.NaN);
- fail();
- } catch (IllegalArgumentException e) {
- }
- }
-
- @Test
- public void testToJsonArray() throws JsonException {
- JsonObject object = new JsonObject();
- Object value = new Object();
- object.put("foo", Boolean.TRUE);
- object.put("bar", 5.0d);
- object.put("baz", -0.0d);
- object.put("quux", value);
-
- JsonArray names = new JsonArray();
- names.put("baz");
- names.put("quux");
- names.put("foo");
-
- JsonArray array = object.toJsonArray(names);
- assertEquals(array.get(0), -0.0d);
- assertEquals(array.get(1), value.toString());
- assertEquals(array.get(2), Boolean.TRUE);
-
- object.put("foo", Boolean.FALSE);
- assertEquals(array.get(2), Boolean.TRUE);
- }
-
- @Test
- public void testToJsonArrayMissingNames() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", Boolean.TRUE);
- object.put("bar", 5.0d);
- object.put("baz", JSON_NULL);
-
- JsonArray names = new JsonArray();
- names.put("bar");
- names.put("foo");
- names.put("quux");
- names.put("baz");
-
- JsonArray array = object.toJsonArray(names);
- assertEquals(3, array.length());
-
- assertEquals(array.get(0), 5.0d);
- assertEquals(array.get(1), Boolean.TRUE);
- assertEquals(array.get(2), null);
- }
-
- @Test
- public void testToJsonArrayNull() throws JsonException {
- JsonObject object = new JsonObject();
- assertEquals(null, object.toJsonArray(null));
- object.put("foo", 5);
- try {
- object.toJsonArray(null);
- } catch (JsonException e) {
- }
- }
-
- @Test
- public void testToJsonArrayEndsUpEmpty() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", 5);
- JsonArray array = new JsonArray();
- array.put("bar");
- assertEquals(0, object.toJsonArray(array).length());
- }
-
- @Test
- public void testToJsonArrayNonString() throws JsonException {
- JsonObject object = new JsonObject();
- object.put("foo", 5);
- object.put("null", 10);
- object.put("false", 15);
-
- JsonArray names = new JsonArray();
- names.put(JSON_NULL);
- names.put(Boolean.FALSE);
- names.put("foo");
-
- // array elements are converted to strings to do name lookups on the map!
- JsonArray array = object.toJsonArray(names);
- assertEquals(3, array.length());
- assertEquals(array.get(0), 10);
- assertEquals(array.get(1), 15);
- assertEquals(array.get(2), 5);
- }
-
- @Test
- public void testPutUnsupportedNumbers() throws JsonException {
- JsonObject object = new JsonObject();
- try {
- object.put("foo", Double.NaN);
- fail();
- } catch (IllegalArgumentException e) {
- }
- try {
- object.put("foo", Double.NEGATIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException e) {
- }
- try {
- object.put("foo", Double.POSITIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException e) {
- }
- }
-
- @Test
- public void testPutUnsupportedNumbersAsObjects() throws JsonException {
- JsonObject object = new JsonObject();
- try {
- object.put("foo", (Double) Double.NaN);
- fail();
- } catch (IllegalArgumentException e) {
- }
- try {
- object.put("foo", (Double) Double.NEGATIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException e) {
- }
- try {
- object.put("foo", (Double) Double.POSITIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException e) {
- }
- }
-
- /**
- * JsonObject constructor fails
- */
- @Test(expected=IllegalArgumentException.class)
- public void testCreateWithUnsupportedNumbers() throws JsonException {
- Map contents = new HashMap();
- contents.put("foo", Double.NaN);
- contents.put("bar", Double.NEGATIVE_INFINITY);
- contents.put("baz", Double.POSITIVE_INFINITY);
-
- JsonObject object = new JsonObject(contents);
- }
-
- @Test(expected=IllegalArgumentException.class)
- public void testToStringWithUnsupportedNumbers() throws JsonException{
- // when the object contains an unsupported number JsonObject constructor fails
- JsonObject object = new JsonObject(Collections.singletonMap("foo", Double.NaN));
- }
-
- @Test
- public void testMapConstructorCopiesContents() throws JsonException {
- Map contents = new HashMap();
- contents.put("foo", 5);
- JsonObject object = new JsonObject(contents);
- contents.put("foo", 10);
- assertEquals(object.get("foo"), 5);
- }
-
- @Test
- public void testMapConstructorWithBogusEntries() {
- Map