Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 0333E200C45 for ; Sat, 18 Feb 2017 00:13:15 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 01F52160B6D; Fri, 17 Feb 2017 23:13:15 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id E2D58160B7A for ; Sat, 18 Feb 2017 00:13:12 +0100 (CET) Received: (qmail 33902 invoked by uid 500); 17 Feb 2017 23:13:12 -0000 Mailing-List: contact commits-help@geode.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@geode.apache.org Delivered-To: mailing list commits@geode.apache.org Received: (qmail 33698 invoked by uid 99); 17 Feb 2017 23:13:12 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 17 Feb 2017 23:13:12 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id C8D81E04F3; Fri, 17 Feb 2017 23:13:11 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: udo@apache.org To: commits@geode.apache.org Date: Fri, 17 Feb 2017 23:13:19 -0000 Message-Id: <68462706ea1f4462a4d07345e64335fb@git.apache.org> In-Reply-To: <8e529c75318946a5aa29a44f7de143d2@git.apache.org> References: <8e529c75318946a5aa29a44f7de143d2@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [09/10] geode git commit: GEODE-2142: Amending JSONObject.java with cyclicalDependency management Refactoring some code in GfJsonObject.java archived-at: Fri, 17 Feb 2017 23:13:15 -0000 GEODE-2142: Amending JSONObject.java with cyclicalDependency management Refactoring some code in GfJsonObject.java Project: http://git-wip-us.apache.org/repos/asf/geode/repo Commit: http://git-wip-us.apache.org/repos/asf/geode/commit/00f1d3d7 Tree: http://git-wip-us.apache.org/repos/asf/geode/tree/00f1d3d7 Diff: http://git-wip-us.apache.org/repos/asf/geode/diff/00f1d3d7 Branch: refs/heads/feature/GEODE-2142 Commit: 00f1d3d7bd9053b85b2fb2258299cdf6a79acd86 Parents: 7e78537 Author: Udo Kohlmeyer Authored: Fri Feb 17 15:11:49 2017 -0800 Committer: Udo Kohlmeyer Committed: Fri Feb 17 15:11:49 2017 -0800 ---------------------------------------------------------------------- .../internal/cli/json/GfJsonObject.java | 36 - .../src/main/java/org/json/JSONObject.java | 1765 +++++++++--------- 2 files changed, 872 insertions(+), 929 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/geode/blob/00f1d3d7/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/GfJsonObject.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/GfJsonObject.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/GfJsonObject.java index 69666ff..66e9f79 100644 --- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/GfJsonObject.java +++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/json/GfJsonObject.java @@ -49,22 +49,6 @@ public class GfJsonObject { this.jsonObject = (JSONObject) bean; } else { this.jsonObject = new JSONObject(bean); - // If we want to print out the values of the primitive arrays and report back - // Class klass = bean.getClass(); - // if(klass.isArray() && klass.getComponentType().isPrimitive()){ - // String str = ""; - // int length = Array.getLength(bean); - // for (int i = 0; i < length; i++) { - // if(i==0) - // str += (Array.get(bean, i)); - // else - // str +=(","+Array.get(bean, i)); - // } - // try { - // this.jsonObject.put("Value", str); - // } catch (JSONException ignore) { - // } - // } } if (checkCyclicDep) { JSONObject.cyclicDepChkEnabled.set(false); @@ -88,10 +72,6 @@ public class GfJsonObject { } } - public GfJsonObject(Object bean, String[] paramNames) { - this.jsonObject = new JSONObject(bean, paramNames); - } - /** * * @param source A string beginning with { (left brace) and ending with } (right brace). @@ -258,22 +238,6 @@ public class GfJsonObject { * @param key * @param value * @return this GfJsonObject - * @throws GfJsonException if the key is a duplicate - */ - public GfJsonObject putOnce(String key, Object value) throws GfJsonException { - try { - jsonObject.putOnce(key, value); - } catch (JSONException e) { - throw new GfJsonException(e.getMessage()); - } - return this; - } - - /** - * - * @param key - * @param value - * @return this GfJsonObject * @throws GfJsonException If the value is a non-finite number. */ public GfJsonObject putOpt(String key, Object value) throws GfJsonException { http://git-wip-us.apache.org/repos/asf/geode/blob/00f1d3d7/geode-json/src/main/java/org/json/JSONObject.java ---------------------------------------------------------------------- diff --git a/geode-json/src/main/java/org/json/JSONObject.java b/geode-json/src/main/java/org/json/JSONObject.java index d2bc126..ce15d1b 100755 --- a/geode-json/src/main/java/org/json/JSONObject.java +++ b/geode-json/src/main/java/org/json/JSONObject.java @@ -16,10 +16,8 @@ package org.json; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -84,910 +82,891 @@ import java.util.TreeMap; */ public class JSONObject { - private static final Double NEGATIVE_ZERO = -0d; - - /** - * A sentinel value used to explicitly define a name with no value. Unlike - * {@code null}, names with this value: - *
    - *
  • show up in the {@link #names} array - *
  • show up in the {@link #keys} iterator - *
  • return {@code true} for {@link #has(String)} - *
  • do not throw on {@link #get(String)} - *
  • are included in the encoded JSON string. - *
- * - *

This value violates the general contract of {@link Object#equals} by - * returning true when compared to {@code null}. Its {@link #toString} - * method returns "null". - */ - public static final Object NULL = new Object() { - @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") - @Override - public boolean equals(Object o) { - return o == this || o == null; // API specifies this broken equals implementation - } - - // at least make the broken equals(null) consistent with Objects.hashCode(null). - @Override - public int hashCode() { - return 0; - } - - @Override - public String toString() { - return "null"; - } - }; - - private final LinkedHashMap nameValuePairs; + private static final Double NEGATIVE_ZERO = -0d; + + public static ThreadLocal cyclicDependencySet = new ThreadLocal(); + public static ThreadLocal cyclicDepChkEnabled = new ThreadLocal(); + + /** + * A sentinel value used to explicitly define a name with no value. Unlike + * {@code null}, names with this value: + *

    + *
  • show up in the {@link #names} array + *
  • show up in the {@link #keys} iterator + *
  • return {@code true} for {@link #has(String)} + *
  • do not throw on {@link #get(String)} + *
  • are included in the encoded JSON string. + *
+ * + *

This value violates the general contract of {@link Object#equals} by + * returning true when compared to {@code null}. Its {@link #toString} + * method returns "null". + */ + public static final Object NULL = new Object() { + @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") + @Override + public boolean equals(Object o) { + return o == this || o == null; // API specifies this broken equals implementation + } - /** - * Creates a {@code JSONObject} with no name/value mappings. - */ - public JSONObject() { - nameValuePairs = new LinkedHashMap(); + // at least make the broken equals(null) consistent with Objects.hashCode(null). + @Override + public int hashCode() { + return 0; } - /** - * 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. - */ + @Override + public String toString() { + return "null"; + } + }; + + private final LinkedHashMap nameValuePairs; + + /** + * Creates a {@code JSONObject} with no name/value mappings. + */ + public JSONObject() { + nameValuePairs = new LinkedHashMap(); + } + + /** + * Creates a new {@code JSONObject} by copying all name/value mappings from + * the given map. + * @param copyFrom a map whose keys are of type {@link String} and whose values are of supported + * types. + * @throws NullPointerException if any of the map's keys are null. + */ /* (accept a raw type for API compatibility) */ - public JSONObject(Map copyFrom) { - this(); - Map contentsTyped = (Map) copyFrom; - for (Map.Entry entry : contentsTyped.entrySet()) { + public JSONObject(Map copyFrom) { + 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())); - } - } - - /** - * Creates a new {@code JSONObject} with name/value mappings from the next - * object in the tokener. - * - * @param readFrom a tokener whose nextValue() method will yield a - * {@code JSONObject}. - * @throws JSONException if the parse fails or doesn't yield a - * {@code JSONObject}. - */ - public JSONObject(JSONTokener readFrom) throws JSONException { + String key = (String) entry.getKey(); + if (key == null) { + throw new NullPointerException("key == null"); + } + nameValuePairs.put(key, wrap(entry.getValue())); + } + } + + /** + * Creates a new {@code JSONObject} with name/value mappings from the next + * object in the tokener. + * @param readFrom a tokener whose nextValue() method will yield a {@code JSONObject}. + * @throws JSONException if the parse fails or doesn't yield a {@code JSONObject}. + */ + public JSONObject(JSONTokener readFrom) throws JSONException { /* * Getting the parser to populate this could get tricky. Instead, just * parse to temporary JSONObject and then steal the data from that. */ - Object object = readFrom.nextValue(); - if (object instanceof JSONObject) { - this.nameValuePairs = ((JSONObject) object).nameValuePairs; - } else { - throw JSON.typeMismatch(object, "JSONObject"); - } - } - - /** - * Creates a new {@code JSONObject} with name/value mappings from the JSON - * string. - * - * @param json a JSON-encoded string containing an object. - * @throws JSONException if the parse fails or doesn't yield a {@code - * JSONObject}. - */ - public JSONObject(String json) throws JSONException { - this(new JSONTokener(json)); - } - - /** - * Creates a new {@code JSONObject} by copying mappings for the listed names - * from the given object. Names that aren't present in {@code copyFrom} will - * be skipped. - * - * @param copyFrom The source object. - * @param names The names of the fields to copy. - * @throws JSONException On internal errors. Shouldn't happen. - */ - public JSONObject(JSONObject copyFrom, String[] names) throws JSONException { - this(); - for (String name : names) { - Object value = copyFrom.opt(name); - if (value != null) { - nameValuePairs.put(name, value); - } - } - } - - /** - * Creates a json object from a bean - * @param bean the bean to create the json object from - * @throws JSONException If there is an exception while reading the bean - */ - public JSONObject(Object bean) throws JSONException { - this(propertiesAsMap(bean)); - } - - private static Map propertiesAsMap(Object bean) throws JSONException { - Map props = new TreeMap(); - try { - PropertyDescriptor[] properties = Introspector.getBeanInfo(bean.getClass(), Object.class) - .getPropertyDescriptors(); - for (PropertyDescriptor prop : properties) { - Object v = prop.getReadMethod().invoke(bean); - props.put(prop.getDisplayName(), wrap(v)); - } - } catch (IllegalAccessException e) { - throw new JSONException(e); - } catch (IntrospectionException e) { - throw new JSONException(e); - } catch (InvocationTargetException e) { - throw new JSONException(e); - } - return props; - } - - public static String[] getNames(JSONObject x) { - Set names = x.keySet(); - String[] r = new String[names.size()]; - int i = 0; - for (String name : names) { - r[i++] = name; - } - return r; - } - - /** - * Returns the number of name/value mappings in this object. - * - * @return the length of this. - */ - public int length() { - return nameValuePairs.size(); - } - - /** - * Maps {@code name} to {@code value}, clobbering any existing name/value - * mapping with the same name. - * - * @param name The name of the value to insert. - * @param value The value to insert. - * @return this object. - * @throws JSONException Should not be possible. - */ - public JSONObject put(String name, boolean value) throws JSONException { - nameValuePairs.put(checkName(name), value); - return this; - } - - /** - * Maps {@code name} to {@code value}, clobbering any existing name/value - * mapping with the same name. - * - * @param name The name for the new value. - * @param value a finite value. May not be {@link Double#isNaN() NaNs} or - * {@link Double#isInfinite() infinities}. - * @return this object. - * @throws JSONException if value is NaN or infinite. - */ - public JSONObject put(String name, double value) throws JSONException { - nameValuePairs.put(checkName(name), JSON.checkDouble(value)); - return this; - } - - /** - * Maps {@code name} to {@code value}, clobbering any existing name/value - * mapping with the same name. - * - * @param name The name for the new value. - * @param value The new value. - * @return this object. - * @throws JSONException Should not be possible. - */ - public JSONObject put(String name, int value) throws JSONException { - nameValuePairs.put(checkName(name), value); - return this; - } - - /** - * Maps {@code name} to {@code value}, clobbering any existing name/value - * mapping with the same name. - * - * @param name The name of the new value. - * @param value The new value to insert. - * @return this object. - * @throws JSONException Should not be possible. - */ - public JSONObject put(String name, long value) throws JSONException { - nameValuePairs.put(checkName(name), value); - return this; - } - - /** - * Maps {@code name} to {@code value}, clobbering any existing name/value - * mapping with the same name. If the value is {@code null}, any existing - * mapping for {@code name} is removed. - * - * @param name The name of the new value. - * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, - * Integer, Long, Double, {@link #NULL}, or {@code null}. May not be - * {@link Double#isNaN() NaNs} or {@link Double#isInfinite() - * infinities}. - * @return this object. - * @throws JSONException if the value is an invalid double (infinite or NaN). - */ - public JSONObject put(String name, Object value) throws JSONException { - if (value == null) { - nameValuePairs.remove(name); - return this; - } - if (value instanceof Number) { - // deviate from the original by checking all Numbers, not just floats & doubles - JSON.checkDouble(((Number) value).doubleValue()); - } - nameValuePairs.put(checkName(name), value); - return this; - } - - /** - * Equivalent to {@code put(name, value)} when both parameters are non-null; - * does nothing otherwise. - * - * @param name The name of the value to insert. - * @param value The value to insert. - * @return this object. - * @throws JSONException if the value is an invalid double (infinite or NaN). - */ - 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 name The name of the field to change. - * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, - * Integer, Long, Double, {@link #NULL} or null. May not be {@link - * Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}. - * @return this object after mutation. - * @throws JSONException If the object being added is an invalid number. - */ - // TODO: Change {@code append) to {@link #append} when append is - // unhidden. - public JSONObject accumulate(String name, Object value) throws JSONException { - Object current = nameValuePairs.get(checkName(name)); - if (current == null) { - return put(name, value); - } - - if (current instanceof JSONArray) { - JSONArray array = (JSONArray) current; - array.checkedPut(value); - } else { - JSONArray array = new JSONArray(); - array.checkedPut(current); - array.checkedPut(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. - * - * @param name The name of the array to which the value should be appended. - * @param value The value to append. - * @return this object. - * @throws JSONException if {@code name} is {@code null} or if the mapping for - * {@code name} is non-null and is not a {@link JSONArray}. - */ - public JSONObject append(String name, Object value) throws JSONException { - 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.checkedPut(value); - - return this; - } - - String checkName(String name) throws JSONException { - if (name == null) { - throw new JSONException("Names must be non-null"); - } - return name; - } - - /** - * Removes the named mapping if it exists; does nothing otherwise. - * - * @param name The name of the mapping to remove. - * @return the value previously mapped by {@code name}, or null if there was - * no such mapping. - */ - public Object remove(String name) { - 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 #NULL}. - * - * @param name The name of the value to check on. - * @return true if the field doesn't exist or is null. - */ - public boolean isNull(String name) { - Object value = nameValuePairs.get(name); - return value == null || value == NULL; - } - - /** - * Returns true if this object has a mapping for {@code name}. The mapping - * may be {@link #NULL}. - * - * @param name The name of the value to check on. - * @return true if this object has a field named {@code name} - */ - public boolean has(String name) { - return nameValuePairs.containsKey(name); - } - - /** - * Returns the value mapped by {@code name}, or throws if no such mapping exists. - * - * @param name The name of the value to get. - * @return The value. - * @throws JSONException if no such mapping exists. - */ - public Object get(String name) throws JSONException { - Object result = nameValuePairs.get(name); - if (result == null) { - throw new JSONException("No value for " + name); - } - return result; - } - - /** - * Returns the value mapped by {@code name}, or null if no such mapping - * exists. - * - * @param name The name of the value to get. - * @return The value. - */ - public Object 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. - * - * @param name The name of the field we want. - * @return The selected value if it exists. - * @throws JSONException if the mapping doesn't exist or cannot be coerced - * to a boolean. - */ - public boolean getBoolean(String name) throws JSONException { - Object object = get(name); - Boolean result = JSON.toBoolean(object); - if (result == null) { - throw JSON.typeMismatch(name, object, "boolean"); - } - return result; - } - - /** - * Returns the value mapped by {@code name} if it exists and is a boolean or - * can be coerced to a boolean, or false otherwise. - * - * @param name The name of the field we want. - * @return The selected value if it exists. - */ - public boolean optBoolean(String name) { - return optBoolean(name, false); - } - - /** - * Returns the value mapped by {@code name} if it exists and is a boolean or - * can be coerced to a boolean, or {@code fallback} otherwise. - * - * @param name The name of the field we want. - * @param fallback The value to return if the field isn't there. - * @return The selected value or the fallback. - */ - public boolean optBoolean(String name, boolean fallback) { - Object object = opt(name); - Boolean result = JSON.toBoolean(object); - return result != null ? result : fallback; - } - - /** - * Returns the value mapped by {@code name} if it exists and is a double or - * can be coerced to a double, or throws otherwise. - * - * @param name The name of the field we want. - * @return The selected value if it exists. - * @throws JSONException if the mapping doesn't exist or cannot be coerced - * to a double. - */ - public double getDouble(String name) throws JSONException { - Object object = get(name); - Double result = JSON.toDouble(object); - if (result == null) { - throw JSON.typeMismatch(name, object, "double"); - } - return result; - } - - /** - * Returns the value mapped by {@code name} if it exists and is a double or - * can be coerced to a double, or {@code NaN} otherwise. - * - * @param name The name of the field we want. - * @return The selected value if it exists. - */ - public double optDouble(String name) { - return optDouble(name, Double.NaN); - } - - /** - * Returns the value mapped by {@code name} if it exists and is a double or - * can be coerced to a double, or {@code fallback} otherwise. - * - * @param name The name of the field we want. - * @param fallback The value to return if the field isn't there. - * @return The selected value or the fallback. - */ - public double optDouble(String name, double fallback) { - Object object = opt(name); - Double result = JSON.toDouble(object); - return result != null ? result : fallback; - } - - /** - * Returns the value mapped by {@code name} if it exists and is an int or - * can be coerced to an int, or throws otherwise. - * - * @param name The name of the field we want. - * @return The selected value if it exists. - * @throws JSONException if the mapping doesn't exist or cannot be coerced - * to an int. - */ - public int getInt(String name) throws JSONException { - Object object = get(name); - Integer result = JSON.toInteger(object); - if (result == null) { - throw JSON.typeMismatch(name, object, "int"); - } - return result; - } - - /** - * Returns the value mapped by {@code name} if it exists and is an int or - * can be coerced to an int, or 0 otherwise. - * - * @param name The name of the field we want. - * @return The selected value if it exists. - */ - public int optInt(String name) { - return optInt(name, 0); - } - - /** - * Returns the value mapped by {@code name} if it exists and is an int or - * can be coerced to an int, or {@code fallback} otherwise. - * - * @param name The name of the field we want. - * @param fallback The value to return if the field isn't there. - * @return The selected value or the fallback. - */ - public int optInt(String name, int fallback) { - Object object = opt(name); - Integer result = JSON.toInteger(object); - return result != null ? result : fallback; - } - - /** - * Returns the value mapped by {@code name} if it exists and is a long or - * can be coerced to a long, or throws otherwise. - * Note that JSON represents numbers as doubles, - * - * so this is lossy; use strings to transfer numbers - * via JSON without loss. - * - * @param name The name of the field that we want. - * @return The value of the field. - * @throws JSONException if the mapping doesn't exist or cannot be coerced - * to a long. - */ - public long getLong(String name) throws JSONException { - Object object = get(name); - Long result = JSON.toLong(object); - if (result == null) { - throw JSON.typeMismatch(name, object, "long"); - } - return result; - } - - /** - * Returns the value mapped by {@code name} if it exists and is a long or - * can be coerced to a long, or 0 otherwise. Note that JSON represents numbers as doubles, - * so this is lossy; use strings to transfer numbers via JSON. - * - * @param name The name of the field we want. - * @return The selected value. - */ - public long optLong(String name) { - return optLong(name, 0L); - } - - /** - * Returns the value mapped by {@code name} if it exists and is a long or - * can be coerced to a long, or {@code fallback} otherwise. Note that JSON represents - * numbers as doubles, so this is lossy; use strings to transfer - * numbers via JSON. - * - * @param name The name of the field we want. - * @param fallback The value to return if the field isn't there. - * @return The selected value or the fallback. - */ - public long optLong(String name, long fallback) { - Object object = opt(name); - Long result = JSON.toLong(object); - return result != null ? result : fallback; - } - - /** - * Returns the value mapped by {@code name} if it exists, coercing it if - * necessary, or throws if no such mapping exists. - * - * @param name The name of the field we want. - * @return The value of the field. - * @throws JSONException if no such mapping exists. - */ - public String getString(String name) throws JSONException { - Object object = get(name); - String result = JSON.toString(object); - if (result == null) { - throw JSON.typeMismatch(name, object, "String"); - } - return result; - } - - /** - * Returns the value mapped by {@code name} if it exists, coercing it if - * necessary, or the empty string if no such mapping exists. - * - * @param name The name of the field we want. - * @return The value of the field. - */ - public String optString(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. - * - * @param name The name of the field that we want. - * @param fallback The value to return if the field doesn't exist. - * @return The value of the field or fallback. - */ - public String optString(String name, String fallback) { - Object object = opt(name); - String result = JSON.toString(object); - return result != null ? result : fallback; - } - - /** - * Returns the value mapped by {@code name} if it exists and is a {@code - * JSONArray}, or throws otherwise. - * - * @param name The field we want to get. - * @return The value of the field (if it is a JSONArray. - * @throws JSONException if the mapping doesn't exist or is not a {@code - * JSONArray}. - */ - public JSONArray getJSONArray(String name) throws JSONException { - Object object = get(name); - if (object instanceof JSONArray) { - return (JSONArray) object; - } else { - throw JSON.typeMismatch(name, object, "JSONArray"); - } - } - - /** - * Returns the value mapped by {@code name} if it exists and is a {@code - * JSONArray}, or null otherwise. - * - * @param name The name of the field we want. - * @return The value of the specified field (assuming it is a JSNOArray - */ - public JSONArray optJSONArray(String name) { - Object object = opt(name); - return object instanceof JSONArray ? (JSONArray) object : null; - } - - /** - * Returns the value mapped by {@code name} if it exists and is a {@code - * JSONObject}, or throws otherwise. - * - * @param name The name of the field that we want. - * @return a specified field value (if it is a JSONObject) - * @throws JSONException if the mapping doesn't exist or is not a {@code - * JSONObject}. - */ - public JSONObject getJSONObject(String name) throws JSONException { - Object object = get(name); - if (object instanceof JSONObject) { - return (JSONObject) object; - } else { - throw JSON.typeMismatch(name, object, "JSONObject"); - } - } - - /** - * Returns the value mapped by {@code name} if it exists and is a {@code - * JSONObject}, or null otherwise. - * - * @param name The name of the value we want. - * @return The specified value. - */ - public JSONObject optJSONObject(String name) { - Object object = opt(name); - return object instanceof JSONObject ? (JSONObject) object : null; - } - - /** - * Returns an array with the values corresponding to {@code names}. The - * array contains null for names that aren't mapped. This method returns - * null if {@code names} is either null or empty. - * - * @param names The names of the fields that we want the values for. - * @return The selected values. - * @throws JSONException On internal errors. Shouldn't happen. - */ - 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 = JSON.toString(names.opt(i)); - result.put(opt(name)); - } - return result; - } - - /** - * Returns an iterator of the {@code String} names in this object. The - * returned iterator supports {@link Iterator#remove() remove}, which will - * remove the corresponding mapping from this object. If this object is - * modified after the iterator is returned, the iterator's behavior is - * undefined. The order of the keys is undefined. - * - * @return an iterator over the keys. - */ - 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()}. - * - * @return The names in this object. - */ - public Set keySet() { - return nameValuePairs.keySet(); - } - - /** - * Returns an array containing the string names in this object. This method - * returns null if this object contains no mappings. - * - * @return the names. - */ - public JSONArray names() { - return nameValuePairs.isEmpty() - ? null - : new JSONArray(new ArrayList(nameValuePairs.keySet())); - } - - /** - * Encodes this object as a compact JSON string, such as: - *

{"query":"Pizza","locations":[94043,90210]}
- */ - @Override - public String toString() { - try { - JSONStringer stringer = new JSONStringer(); - writeTo(stringer); - return stringer.toString(); - } catch (JSONException e) { - return null; - } - } - - /** - * Encodes this object as a human readable JSON string for debugging, such - * as: - *
-     * {
-     *     "query": "Pizza",
-     *     "locations": [
-     *         94043,
-     *         90210
-     *     ]
-     * }
- * - * @param indentSpaces the number of spaces to indent for each level of - * nesting. - * @return The string containing the pretty form of this. - * @throws JSONException On internal errors. Shouldn't happen. - */ - public String toString(int indentSpaces) throws JSONException { - JSONStringer stringer = new JSONStringer(indentSpaces); - writeTo(stringer); - return stringer.toString(); - } - - void writeTo(JSONStringer stringer) throws JSONException { - stringer.object(); - for (Map.Entry entry : nameValuePairs.entrySet()) { - stringer.key(entry.getKey()).value(entry.getValue()); - } - stringer.endObject(); - } - - /** - * Encodes the number as a JSON string. - * - * @param number a finite value. May not be {@link Double#isNaN() NaNs} or - * {@link Double#isInfinite() infinities}. - * @return The encoded number in string form. - * @throws JSONException On internal errors. Shouldn't happen. - */ - public static String numberToString(Number number) throws JSONException { - if (number == null) { - throw new JSONException("Number must be non-null"); - } - - double doubleValue = number.doubleValue(); - JSON.checkDouble(doubleValue); - - // the original returns "-0" instead of "-0.0" for negative zero - if (number.equals(NEGATIVE_ZERO)) { - return "-0"; - } - - long longValue = number.longValue(); - if (doubleValue == (double) longValue) { - return Long.toString(longValue); - } - - return number.toString(); - } - - /** - * Encodes {@code data} as a JSON string. This applies quotes and any - * necessary character escaping. - * - * @param data the string to encode. Null will be interpreted as an empty - * string. - * @return the quoted string. - */ - public static String quote(String data) { - if (data == null) { - return "\"\""; - } - try { - JSONStringer stringer = new JSONStringer(); - stringer.open(JSONStringer.Scope.NULL, ""); - stringer.value(data); - stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); - return stringer.toString(); - } catch (JSONException e) { - throw new AssertionError(); - } - } - - /** - * Wraps the given object if necessary. - * - *

If the object is null or , returns {@link #NULL}. - * If the object is a {@code JSONArray} or {@code JSONObject}, no wrapping is necessary. - * If the object is {@code NULL}, no wrapping is necessary. - * If the object is an array or {@code Collection}, returns an equivalent {@code JSONArray}. - * If the object is a {@code Map}, returns an equivalent {@code JSONObject}. - * If the object is a primitive wrapper type or {@code String}, returns the object. - * If the object is from a {@code java} package, returns the result of {@code toString}. - * If the object is some other kind of object then it is assumed to be a bean and is converted to a JSONObject. - * If wrapping fails, returns null. - * - * @param o The object to wrap. - * @return The wrapped (if necessary) form of the object {$code o} - */ - public static Object wrap(Object o) { - if (o == null) { - return NULL; - } - if (o instanceof JSONArray || o instanceof JSONObject) { - return o; - } - if (o.equals(NULL)) { - return o; - } - try { - if (o instanceof Collection) { - return new JSONArray((Collection) o); - } else if (o.getClass().isArray()) { - return new JSONArray(o); + Object object = readFrom.nextValue(); + if (object instanceof JSONObject) { + this.nameValuePairs = ((JSONObject) object).nameValuePairs; + } else { + throw JSON.typeMismatch(object, "JSONObject"); + } + } + + /** + * Creates a new {@code JSONObject} with name/value mappings from the JSON + * string. + * @param json a JSON-encoded string containing an object. + * @throws JSONException if the parse fails or doesn't yield a {@code JSONObject}. + */ + public JSONObject(String json) throws JSONException { + this(new JSONTokener(json)); + } + + /** + * Creates a new {@code JSONObject} by copying mappings for the listed names + * from the given object. Names that aren't present in {@code copyFrom} will + * be skipped. + * @param copyFrom The source object. + * @param names The names of the fields to copy. + * @throws JSONException On internal errors. Shouldn't happen. + */ + public JSONObject(JSONObject copyFrom, String[] names) throws JSONException { + this(); + for (String name : names) { + Object value = copyFrom.opt(name); + if (value != null) { + nameValuePairs.put(name, value); + } + } + } + + /** + * Creates a json object from a bean + * @param bean the bean to create the json object from + * @throws JSONException If there is an exception while reading the bean + */ + public JSONObject(Object bean) throws JSONException { + this(propertiesAsMap(bean)); + } + + // This is custom properties mapping specific for GEODE + private static Map propertiesAsMap(Object bean) { + Map props = new TreeMap(); + Class klass = bean.getClass(); + + // If klass is a System class then set includeSuperClass to false. + + boolean includeSuperClass = klass.getClassLoader() != null; + + Method[] methods = includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods(); + for (int i = 0; i < methods.length; i += 1) { + try { + Method method = methods[i]; + if (Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) { + String name = method.getName(); + String key = ""; + if (name.startsWith("get")) { + if ("getClass".equals(name) || "getDeclaringClass".equals(name)) { + key = ""; + } else { + key = name.substring(3); } - if (o instanceof Map) { - return new JSONObject((Map) o); + } else if (name.startsWith("is")) { + key = name.substring(2); + } + if (key.length() > 0 && Character.isUpperCase(key.charAt(0)) + && method.getParameterTypes().length == 0) { + if (key.length() == 1) { + key = key.toLowerCase(); + } else if (!Character.isUpperCase(key.charAt(1))) { + key = key.substring(0, 1).toLowerCase() + key.substring(1); } - if (o instanceof Boolean || - o instanceof Byte || - o instanceof Character || - o instanceof Double || - o instanceof Float || - o instanceof Integer || - o instanceof Long || - o instanceof Short || - o instanceof String) { - return o; + Object result = method.invoke(bean, (Object[]) null); + if (result != null) { + props.put(key, wrap(result)); + } else if (!method.getReturnType().isArray()) { + props.put(key, JSONObject.NULL); } - if (o.getClass().getPackage().getName().startsWith("java.") || o instanceof Enum) { - return o.toString(); - } else { - return new JSONObject(o); - } - } catch (Exception ignored) { + } + } + } catch (Exception ignore) { + } + } + props.put("type-class", klass.getCanonicalName()); + return props; + } + + public static String[] getNames(JSONObject x) { + Set names = x.keySet(); + String[] r = new String[names.size()]; + int i = 0; + for (String name : names) { + r[i++] = name; + } + return r; + } + + /** + * Returns the number of name/value mappings in this object. + * @return the length of this. + */ + public int length() { + return nameValuePairs.size(); + } + + /** + * Maps {@code name} to {@code value}, clobbering any existing name/value + * mapping with the same name. + * @param name The name of the value to insert. + * @param value The value to insert. + * @return this object. + * @throws JSONException Should not be possible. + */ + public JSONObject put(String name, boolean value) throws JSONException { + nameValuePairs.put(checkName(name), value); + return this; + } + + /** + * Maps {@code name} to {@code value}, clobbering any existing name/value + * mapping with the same name. + * @param name The name for the new value. + * @param value a finite value. May not be {@link Double#isNaN() NaNs} or {@link + * Double#isInfinite() infinities}. + * @return this object. + * @throws JSONException if value is NaN or infinite. + */ + public JSONObject put(String name, double value) throws JSONException { + nameValuePairs.put(checkName(name), JSON.checkDouble(value)); + return this; + } + + /** + * Maps {@code name} to {@code value}, clobbering any existing name/value + * mapping with the same name. + * @param name The name for the new value. + * @param value The new value. + * @return this object. + * @throws JSONException Should not be possible. + */ + public JSONObject put(String name, int value) throws JSONException { + nameValuePairs.put(checkName(name), value); + return this; + } + + /** + * Maps {@code name} to {@code value}, clobbering any existing name/value + * mapping with the same name. + * @param name The name of the new value. + * @param value The new value to insert. + * @return this object. + * @throws JSONException Should not be possible. + */ + public JSONObject put(String name, long value) throws JSONException { + nameValuePairs.put(checkName(name), value); + return this; + } + + /** + * Maps {@code name} to {@code value}, clobbering any existing name/value + * mapping with the same name. If the value is {@code null}, any existing + * mapping for {@code name} is removed. + * @param name The name of the new value. + * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long, Double, + * {@link #NULL}, or {@code null}. May not be {@link Double#isNaN() NaNs} or {@link + * Double#isInfinite() infinities}. + * @return this object. + * @throws JSONException if the value is an invalid double (infinite or NaN). + */ + public JSONObject put(String name, Object value) throws JSONException { + if (value == null) { + nameValuePairs.remove(name); + return this; + } + if (value instanceof Number) { + // deviate from the original by checking all Numbers, not just floats & doubles + JSON.checkDouble(((Number) value).doubleValue()); + } + nameValuePairs.put(checkName(name), value); + return this; + } + + /** + * Equivalent to {@code put(name, value)} when both parameters are non-null; + * does nothing otherwise. + * @param name The name of the value to insert. + * @param value The value to insert. + * @return this object. + * @throws JSONException if the value is an invalid double (infinite or NaN). + */ + 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 name The name of the field to change. + * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean, Integer, Long, Double, + * {@link #NULL} or null. May not be {@link Double#isNaN() NaNs} or {@link Double#isInfinite() + * infinities}. + * @return this object after mutation. + * @throws JSONException If the object being added is an invalid number. + */ + // TODO: Change {@code append) to {@link #append} when append is + // unhidden. + public JSONObject accumulate(String name, Object value) throws JSONException { + Object current = nameValuePairs.get(checkName(name)); + if (current == null) { + return put(name, value); + } + + if (current instanceof JSONArray) { + JSONArray array = (JSONArray) current; + array.checkedPut(value); + } else { + JSONArray array = new JSONArray(); + array.checkedPut(current); + array.checkedPut(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. + * @param name The name of the array to which the value should be appended. + * @param value The value to append. + * @return this object. + * @throws JSONException if {@code name} is {@code null} or if the mapping for {@code name} is + * non-null and is not a {@link JSONArray}. + */ + public JSONObject append(String name, Object value) throws JSONException { + 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.checkedPut(value); + + return this; + } + + String checkName(String name) throws JSONException { + if (name == null) { + throw new JSONException("Names must be non-null"); + } + return name; + } + + /** + * Removes the named mapping if it exists; does nothing otherwise. + * @param name The name of the mapping to remove. + * @return the value previously mapped by {@code name}, or null if there was no such mapping. + */ + public Object remove(String name) { + 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 #NULL}. + * @param name The name of the value to check on. + * @return true if the field doesn't exist or is null. + */ + public boolean isNull(String name) { + Object value = nameValuePairs.get(name); + return value == null || value == NULL; + } + + /** + * Returns true if this object has a mapping for {@code name}. The mapping + * may be {@link #NULL}. + * @param name The name of the value to check on. + * @return true if this object has a field named {@code name} + */ + public boolean has(String name) { + return nameValuePairs.containsKey(name); + } + + /** + * Returns the value mapped by {@code name}, or throws if no such mapping exists. + * @param name The name of the value to get. + * @return The value. + * @throws JSONException if no such mapping exists. + */ + public Object get(String name) throws JSONException { + Object result = nameValuePairs.get(name); + if (result == null) { + throw new JSONException("No value for " + name); + } + return result; + } + + /** + * Returns the value mapped by {@code name}, or null if no such mapping + * exists. + * @param name The name of the value to get. + * @return The value. + */ + public Object 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. + * @param name The name of the field we want. + * @return The selected value if it exists. + * @throws JSONException if the mapping doesn't exist or cannot be coerced to a boolean. + */ + public boolean getBoolean(String name) throws JSONException { + Object object = get(name); + Boolean result = JSON.toBoolean(object); + if (result == null) { + throw JSON.typeMismatch(name, object, "boolean"); + } + return result; + } + + /** + * Returns the value mapped by {@code name} if it exists and is a boolean or + * can be coerced to a boolean, or false otherwise. + * @param name The name of the field we want. + * @return The selected value if it exists. + */ + public boolean optBoolean(String name) { + return optBoolean(name, false); + } + + /** + * Returns the value mapped by {@code name} if it exists and is a boolean or + * can be coerced to a boolean, or {@code fallback} otherwise. + * @param name The name of the field we want. + * @param fallback The value to return if the field isn't there. + * @return The selected value or the fallback. + */ + public boolean optBoolean(String name, boolean fallback) { + Object object = opt(name); + Boolean result = JSON.toBoolean(object); + return result != null ? result : fallback; + } + + /** + * Returns the value mapped by {@code name} if it exists and is a double or + * can be coerced to a double, or throws otherwise. + * @param name The name of the field we want. + * @return The selected value if it exists. + * @throws JSONException if the mapping doesn't exist or cannot be coerced to a double. + */ + public double getDouble(String name) throws JSONException { + Object object = get(name); + Double result = JSON.toDouble(object); + if (result == null) { + throw JSON.typeMismatch(name, object, "double"); + } + return result; + } + + /** + * Returns the value mapped by {@code name} if it exists and is a double or + * can be coerced to a double, or {@code NaN} otherwise. + * @param name The name of the field we want. + * @return The selected value if it exists. + */ + public double optDouble(String name) { + return optDouble(name, Double.NaN); + } + + /** + * Returns the value mapped by {@code name} if it exists and is a double or + * can be coerced to a double, or {@code fallback} otherwise. + * @param name The name of the field we want. + * @param fallback The value to return if the field isn't there. + * @return The selected value or the fallback. + */ + public double optDouble(String name, double fallback) { + Object object = opt(name); + Double result = JSON.toDouble(object); + return result != null ? result : fallback; + } + + /** + * Returns the value mapped by {@code name} if it exists and is an int or + * can be coerced to an int, or throws otherwise. + * @param name The name of the field we want. + * @return The selected value if it exists. + * @throws JSONException if the mapping doesn't exist or cannot be coerced to an int. + */ + public int getInt(String name) throws JSONException { + Object object = get(name); + Integer result = JSON.toInteger(object); + if (result == null) { + throw JSON.typeMismatch(name, object, "int"); + } + return result; + } + + /** + * Returns the value mapped by {@code name} if it exists and is an int or + * can be coerced to an int, or 0 otherwise. + * @param name The name of the field we want. + * @return The selected value if it exists. + */ + public int optInt(String name) { + return optInt(name, 0); + } + + /** + * Returns the value mapped by {@code name} if it exists and is an int or + * can be coerced to an int, or {@code fallback} otherwise. + * @param name The name of the field we want. + * @param fallback The value to return if the field isn't there. + * @return The selected value or the fallback. + */ + public int optInt(String name, int fallback) { + Object object = opt(name); + Integer result = JSON.toInteger(object); + return result != null ? result : fallback; + } + + /** + * Returns the value mapped by {@code name} if it exists and is a long or + * can be coerced to a long, or throws otherwise. + * Note that JSON represents numbers as doubles, + * + * so this is lossy; use strings to transfer numbers + * via JSON without loss. + * @param name The name of the field that we want. + * @return The value of the field. + * @throws JSONException if the mapping doesn't exist or cannot be coerced to a long. + */ + public long getLong(String name) throws JSONException { + Object object = get(name); + Long result = JSON.toLong(object); + if (result == null) { + throw JSON.typeMismatch(name, object, "long"); + } + return result; + } + + /** + * Returns the value mapped by {@code name} if it exists and is a long or + * can be coerced to a long, or 0 otherwise. Note that JSON represents numbers as doubles, + * so this is lossy; use strings to transfer numbers via JSON. + * @param name The name of the field we want. + * @return The selected value. + */ + public long optLong(String name) { + return optLong(name, 0L); + } + + /** + * Returns the value mapped by {@code name} if it exists and is a long or + * can be coerced to a long, or {@code fallback} otherwise. Note that JSON represents + * numbers as doubles, so this is lossy; use strings to transfer + * numbers via JSON. + * @param name The name of the field we want. + * @param fallback The value to return if the field isn't there. + * @return The selected value or the fallback. + */ + public long optLong(String name, long fallback) { + Object object = opt(name); + Long result = JSON.toLong(object); + return result != null ? result : fallback; + } + + /** + * Returns the value mapped by {@code name} if it exists, coercing it if + * necessary, or throws if no such mapping exists. + * @param name The name of the field we want. + * @return The value of the field. + * @throws JSONException if no such mapping exists. + */ + public String getString(String name) throws JSONException { + Object object = get(name); + String result = JSON.toString(object); + if (result == null) { + throw JSON.typeMismatch(name, object, "String"); + } + return result; + } + + /** + * Returns the value mapped by {@code name} if it exists, coercing it if + * necessary, or the empty string if no such mapping exists. + * @param name The name of the field we want. + * @return The value of the field. + */ + public String optString(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. + * @param name The name of the field that we want. + * @param fallback The value to return if the field doesn't exist. + * @return The value of the field or fallback. + */ + public String optString(String name, String fallback) { + Object object = opt(name); + String result = JSON.toString(object); + return result != null ? result : fallback; + } + + /** + * Returns the value mapped by {@code name} if it exists and is a {@code + * JSONArray}, or throws otherwise. + * @param name The field we want to get. + * @return The value of the field (if it is a JSONArray. + * @throws JSONException if the mapping doesn't exist or is not a {@code JSONArray}. + */ + public JSONArray getJSONArray(String name) throws JSONException { + Object object = get(name); + if (object instanceof JSONArray) { + return (JSONArray) object; + } else { + throw JSON.typeMismatch(name, object, "JSONArray"); + } + } + + /** + * Returns the value mapped by {@code name} if it exists and is a {@code + * JSONArray}, or null otherwise. + * @param name The name of the field we want. + * @return The value of the specified field (assuming it is a JSNOArray + */ + public JSONArray optJSONArray(String name) { + Object object = opt(name); + return object instanceof JSONArray ? (JSONArray) object : null; + } + + /** + * Returns the value mapped by {@code name} if it exists and is a {@code + * JSONObject}, or throws otherwise. + * @param name The name of the field that we want. + * @return a specified field value (if it is a JSONObject) + * @throws JSONException if the mapping doesn't exist or is not a {@code JSONObject}. + */ + public JSONObject getJSONObject(String name) throws JSONException { + Object object = get(name); + if (object instanceof JSONObject) { + return (JSONObject) object; + } else { + throw JSON.typeMismatch(name, object, "JSONObject"); + } + } + + /** + * Returns the value mapped by {@code name} if it exists and is a {@code + * JSONObject}, or null otherwise. + * @param name The name of the value we want. + * @return The specified value. + */ + public JSONObject optJSONObject(String name) { + Object object = opt(name); + return object instanceof JSONObject ? (JSONObject) object : null; + } + + /** + * Returns an array with the values corresponding to {@code names}. The + * array contains null for names that aren't mapped. This method returns + * null if {@code names} is either null or empty. + * @param names The names of the fields that we want the values for. + * @return The selected values. + * @throws JSONException On internal errors. Shouldn't happen. + */ + 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 = JSON.toString(names.opt(i)); + result.put(opt(name)); + } + return result; + } + + /** + * Returns an iterator of the {@code String} names in this object. The + * returned iterator supports {@link Iterator#remove() remove}, which will + * remove the corresponding mapping from this object. If this object is + * modified after the iterator is returned, the iterator's behavior is + * undefined. The order of the keys is undefined. + * @return an iterator over the keys. + */ + 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()}. + * @return The names in this object. + */ + public Set keySet() { + return nameValuePairs.keySet(); + } + + /** + * Returns an array containing the string names in this object. This method + * returns null if this object contains no mappings. + * @return the names. + */ + public JSONArray names() { + return nameValuePairs.isEmpty() + ? null + : new JSONArray(new ArrayList(nameValuePairs.keySet())); + } + + /** + * Encodes this object as a compact JSON string, such as: + *

{"query":"Pizza","locations":[94043,90210]}
+ */ + @Override + public String toString() { + try { + JSONStringer stringer = new JSONStringer(); + writeTo(stringer); + return stringer.toString(); + } catch (JSONException e) { + return null; + } + } + + /** + * Encodes this object as a human readable JSON string for debugging, such + * as: + *
+   * {
+   *     "query": "Pizza",
+   *     "locations": [
+   *         94043,
+   *         90210
+   *     ]
+   * }
+ * @param indentSpaces the number of spaces to indent for each level of nesting. + * @return The string containing the pretty form of this. + * @throws JSONException On internal errors. Shouldn't happen. + */ + public String toString(int indentSpaces) throws JSONException { + JSONStringer stringer = new JSONStringer(indentSpaces); + writeTo(stringer); + return stringer.toString(); + } + + void writeTo(JSONStringer stringer) throws JSONException { + stringer.object(); + for (Map.Entry entry : nameValuePairs.entrySet()) { + stringer.key(entry.getKey()).value(entry.getValue()); + } + stringer.endObject(); + } + + /** + * Encodes the number as a JSON string. + * @param number a finite value. May not be {@link Double#isNaN() NaNs} or {@link + * Double#isInfinite() infinities}. + * @return The encoded number in string form. + * @throws JSONException On internal errors. Shouldn't happen. + */ + public static String numberToString(Number number) throws JSONException { + if (number == null) { + throw new JSONException("Number must be non-null"); + } + + double doubleValue = number.doubleValue(); + JSON.checkDouble(doubleValue); + + // the original returns "-0" instead of "-0.0" for negative zero + if (number.equals(NEGATIVE_ZERO)) { + return "-0"; + } + + long longValue = number.longValue(); + if (doubleValue == (double) longValue) { + return Long.toString(longValue); + } + + return number.toString(); + } + + /** + * Encodes {@code data} as a JSON string. This applies quotes and any + * necessary character escaping. + * @param data the string to encode. Null will be interpreted as an empty string. + * @return the quoted string. + */ + public static String quote(String data) { + if (data == null) { + return "\"\""; + } + try { + JSONStringer stringer = new JSONStringer(); + stringer.open(JSONStringer.Scope.NULL, ""); + stringer.value(data); + stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, ""); + return stringer.toString(); + } catch (JSONException e) { + throw new AssertionError(); + } + } + + /** + * Wraps the given object if necessary. + * + *

If the object is null or , returns {@link #NULL}. If the object is a {@code JSONArray} or + * {@code JSONObject}, no wrapping is necessary. If the object is {@code NULL}, no wrapping is + * necessary. If the object is an array or {@code Collection}, returns an equivalent {@code + * JSONArray}. If the object is a {@code Map}, returns an equivalent {@code JSONObject}. If the + * object is a primitive wrapper type or {@code String}, returns the object. If the object is from + * a {@code java} package, returns the result of {@code toString}. If the object is some other + * kind of object then it is assumed to be a bean and is converted to a JSONObject. If wrapping + * fails, returns null. + * @param o The object to wrap. + * @return The wrapped (if necessary) form of the object {$code o} + */ + public static Object wrap(Object o) { + if (o == null) { + return NULL; + } + if (o instanceof JSONArray || o instanceof JSONObject) { + return o; + } + if (o.equals(NULL)) { + return o; + } + try { + if (o instanceof Collection) { + return new JSONArray((Collection) o); + } else if (o.getClass().isArray()) { + return new JSONArray(o); + } + if (o instanceof Map) { + return new JSONObject((Map) o); + } + if (o instanceof Boolean || o instanceof Byte || o instanceof Character || o instanceof Double + || o instanceof Float || o instanceof Integer || o instanceof Long || o instanceof Short + || o instanceof String) { + return o; + } + // GEODE-2142: Added check to ignore GemFireCacheImpl + Package objectPackage = o.getClass().getPackage(); + String objectPackageName = objectPackage != null ? objectPackage.getName() : ""; + if (objectPackageName.startsWith("java.") || objectPackageName.startsWith("javax.") + || o instanceof Enum || o.getClass().getClassLoader() == null + || o.getClass().getName().contains("GemFireCacheImpl")) { + return o.toString(); + } + // GEODE-2142: Added the cyclicalDepCheck + if (cyclicDepChkEnabled.get() != null && cyclicDependencySet.get() != null) { + if (cyclicDepChkEnabled.get() && cyclicDependencySet.get().contains(o)) { + // break cyclic reference + return o.getClass().getCanonicalName(); + } else { + cyclicDependencySet.get().add(o); + return new JSONObject(o); } - return null; + } else { + return new JSONObject(o); + } + } catch (Exception ignored) { } + return null; + } }