    001/*
    002 * Copyright (C) 2010 The Android Open Source Project
    003 *
    004 * Licensed under the Apache License, Version 2.0 (the "License");
    005 * you may not use this file except in compliance with the License.
    006 * You may obtain a copy of the License at
    007 *
    008 *      http://www.apache.org/licenses/LICENSE-2.0
    009 *
    010 * Unless required by applicable law or agreed to in writing, software
    011 * distributed under the License is distributed on an "AS IS" BASIS,
    012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    013 * See the License for the specific language governing permissions and
    014 * limitations under the License.
    015 */
    016
    017package org.apache.tapestry5.json;
    018
    019import java.io.ObjectStreamException;
    020import java.io.Serializable;
    021import java.util.Collection;
    022import java.util.Collections;
    023import java.util.LinkedHashMap;
    024import java.util.Map;
    025import java.util.Objects;
    026import java.util.Set;
    027import java.util.function.BiFunction;
    028
    029import org.apache.tapestry5.json.exceptions.JSONTypeMismatchException;
    030import org.apache.tapestry5.json.exceptions.JSONValueNotFoundException;
    031
    032// Note: this class was written without inspecting the non-free org.json sourcecode.
    033
    034/**
    035 * A modifiable set of name/value mappings. Names are unique, non-null strings.
    036 * Values may be any mix of {@link JSONObject JSONObjects}, {@link JSONArray
    037 * JSONArrays}, Strings, Booleans, Integers, Longs, Doubles or {@link #NULL}.
    038 * Values may not be {@code null}, {@link Double#isNaN() NaNs}, {@link
    039 * Double#isInfinite() infinities}, or of any type not listed here.
    040 *
    041 * <p>This class can coerce values to another type when requested.
    042 * <ul>
    043 * <li>When the requested type is a boolean, strings will be coerced using a
    044 * case-insensitive comparison to "true" and "false".
    045 * <li>When the requested type is a double, other {@link Number} types will
    046 * be coerced using {@link Number#doubleValue() doubleValue}. Strings
    047 * that can be coerced using {@link Double#valueOf(String)} will be.
    048 * <li>When the requested type is an int, other {@link Number} types will
    049 * be coerced using {@link Number#intValue() intValue}. Strings
    050 * that can be coerced using {@link Double#valueOf(String)} will be,
    051 * and then cast to int.
    052 * <li><a href="#lossy">When the requested type is a long, other {@link Number} types will
    053 * be coerced using {@link Number#longValue() longValue}. Strings
    054 * that can be coerced using {@link Double#valueOf(String)} will be,
    055 * and then cast to long. This two-step conversion is lossy for very
    056 * large values. For example, the string "9223372036854775806" yields the
    057 * long 9223372036854775807.</a>
    058 * <li>When the requested type is a String, other non-null values will be
    059 * coerced using {@link String#valueOf(Object)}. Although null cannot be
    060 * coerced, the sentinel value {@link JSONObject#NULL} is coerced to the
    061 * string "null".
    062 * </ul>
    063 *
    064 * <p>This class can look up both mandatory and optional values:
    065 * <ul>
    066 * <li>Use <code>get<i>Type</i>()</code> to retrieve a mandatory value. This
    067 * fails with a {@code RuntimeException} if the requested name has no value
    068 * or if the value cannot be coerced to the requested type.
    069 * <li>Use <code>opt()</code> to retrieve an optional value.
    070 * </ul>
    071 *
    072 * <p><strong>Warning:</strong> this class represents null in two incompatible
    073 * ways: the standard Java {@code null} reference, and the sentinel value {@link
    074 * JSONObject#NULL}. In particular, calling {@code put(name, null)} removes the
    075 * named entry from the object but {@code put(name, JSONObject.NULL)} stores an
    076 * entry whose value is {@code JSONObject.NULL}.
    077 *
    078 * <p>Instances of this class are not thread safe.
    079 */
    080public final class JSONObject extends JSONCollection implements Map<String, Object> {
    081
    082    private static final long serialVersionUID = 1L;
    083
    084    private static final Double NEGATIVE_ZERO = -0d;
    085
    086    /**
    087     * A sentinel value used to explicitly define a name with no value. Unlike
    088     * {@code null}, names with this value:
    089     * <ul>
    090     * <li>show up in the {@link #names} array
    091     * <li>show up in the {@link #keys} iterator
    092     * <li>return {@code true} for {@link #has(String)}
    093     * <li>do not throw on {@link #get}
    094     * <li>are included in the encoded JSON string.
    095     * </ul>
    096     *
    097     * <p>This value violates the general contract of {@link Object#equals} by
    098     * returning true when compared to {@code null}. Its {@link #toString}
    099     * method returns "null".
    100     */
    101    public static final Object NULL = new Serializable() {
    102
    103      private static final long serialVersionUID = 1L;
    104
    105        @Override
    106        public boolean equals(Object o) {
    107            return o == this || o == null; // API specifies this broken equals implementation
    108        }
    109
    110        // at least make the broken equals(null) consistent with Objects.hashCode(null).
    111        @Override
    112        public int hashCode() {
    113            return 0;
    114        }
    115
    116        @Override
    117        public String toString() {
    118            return "null";
    119        }
    120
    121        // Serialization magic: after de-serializing, it will be back to the singleton instance of NULL.
    122        private Object readResolve() throws ObjectStreamException
    123        {
    124            return NULL;
    125        }
    126
    127    };
    128
    129    private final LinkedHashMap<String, Object> nameValuePairs;
    130
    131    /**
    132     * Creates a {@code JSONObject} with no name/value mappings.
    133     */
    134    public JSONObject() {
    135        nameValuePairs = new LinkedHashMap<String, Object>();
    136    }
    137
    138    /**
    139     * Creates a new {@code JSONObject} with name/value mappings from the next
    140     * object in the tokener.
    141     *
    142     * @param readFrom a tokener whose nextValue() method will yield a
    143     *                 {@code JSONObject}.
    144     * @throws RuntimeException if the parse fails or doesn't yield a
    145     *                       {@code JSONObject}.
    146     */
    147    JSONObject(JSONTokener readFrom) {
    148        /*
    149         * Getting the parser to populate this could get tricky. Instead, just
    150         * parse to temporary JSONObject and then steal the data from that.
    151         */
    152        Object object = readFrom.nextValue(JSONObject.class);
    153        if (object instanceof JSONObject) {
    154            this.nameValuePairs = ((JSONObject) object).nameValuePairs;
    155        } else {
    156            throw JSONExceptionBuilder.tokenerTypeMismatch(object, JSONType.OBJECT);
    157        }
    158    }
    159
    160    /**
    161     * Creates a new {@code JSONObject} with name/value mappings from the JSON
    162     * string.
    163     *
    164     * @param json a JSON-encoded string containing an object.
    165     * @throws RuntimeException if the parse fails or doesn't yield a {@code
    166     *                       JSONObject}.
    167     */
    168    public JSONObject(String json) {
    169        this(new JSONTokener(json));
    170    }
    171
    172    /**
    173     * Creates a new {@code JSONObject} by copying mappings for the listed names
    174     * from the given object. Names that aren't present in {@code copyFrom} will
    175     * be skipped.
    176     *
    177     * @param copyFrom The source object.
    178     * @param names    The names of the fields to copy.
    179     * @throws RuntimeException On internal errors. Shouldn't happen.
    180     */
    181    public JSONObject(JSONObject copyFrom, String... names) {
    182        this();
    183        for (String name : names) {
    184            Object value = copyFrom.opt(name);
    185            if (value != null) {
    186                nameValuePairs.put(name, value);
    187            }
    188        }
    189    }
    190
    191
    192    /**
    193     * Returns a new JSONObject that is a shallow copy of this JSONObject.
    194     *
    195     * @since 5.4
    196     */
    197    public JSONObject copy()
    198    {
    199        JSONObject dupe = new JSONObject();
    200        dupe.nameValuePairs.putAll(nameValuePairs);
    201
    202        return dupe;
    203    }
    204
    205    /**
    206     * Constructs a new JSONObject using a series of String keys and object values.
    207     * Object values should be compatible with {@link #put(String, Object)}. Keys must be strings
    208     * (toString() will be invoked on each key).
    209     *
    210     * Prior to release 5.4, keysAndValues was type String...; changing it to Object... makes
    211     * it much easier to initialize a JSONObject in a single statement, which is more readable.
    212     *
    213     * @since 5.2.0
    214     */
    215    public JSONObject(Object... keysAndValues)
    216    {
    217        this();
    218
    219        int i = 0;
    220
    221        while (i < keysAndValues.length)
    222        {
    223            put(keysAndValues[i++].toString(), keysAndValues[i++]);
    224        }
    225    }
    226
    227    /**
    228     * Returns the number of name/value mappings in this object.
    229     *
    230     * @deprecated Use {@link #size()} instead.
    231     * @return the length of this.
    232     */
    233    @Deprecated
    234    public int length() {
    235        return nameValuePairs.size();
    236    }
    237
    238    /**
    239     * Maps {@code name} to {@code value}, clobbering any existing name/value
    240     * mapping with the same name. If the value is {@code null}, any existing
    241     * mapping for {@code name} is removed.
    242     *
    243     * @param name  The name of the new value.
    244     * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
    245     *              Integer, Long, Double, {@link #NULL}, or {@code null}. May not be
    246     *              {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
    247     *              infinities}.
    248     * @return this object.
    249     * @throws IllegalArgumentException if the value is an invalid double (infinite or NaN).
    250     */
    251    @Override
    252    public JSONObject put(String name, Object value) {
    253        if (value == null) {
    254            nameValuePairs.remove(name);
    255            return this;
    256        }
    257        JSON.testValidity(value);
    258        if (value instanceof Number) {
    259            // deviate from the original by checking all Numbers, not just floats & doubles
    260            JSON.checkDouble(((Number) value).doubleValue());
    261        }
    262        nameValuePairs.put(checkName(name), value);
    263        return this;
    264    }
    265
    266    /**
    267     * Appends {@code value} to the array already mapped to {@code name}. If
    268     * this object has no mapping for {@code name}, this inserts a new mapping.
    269     * If the mapping exists but its value is not an array, the existing
    270     * and new values are inserted in order into a new array which is itself
    271     * mapped to {@code name}. In aggregate, this allows values to be added to a
    272     * mapping one at a time.
    273     *
    274     * Note that {@code append(String, Object)} provides better semantics.
    275     * In particular, the mapping for {@code name} will <b>always</b> be a
    276     * {@link JSONArray}. Using {@code accumulate} will result in either a
    277     * {@link JSONArray} or a mapping whose type is the type of {@code value}
    278     * depending on the number of calls to it.
    279     *
    280     * @param name  The name of the field to change.
    281     * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
    282     *              Integer, Long, Double, {@link #NULL} or null. May not be {@link
    283     *              Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}.
    284     * @return this object after mutation.
    285     * @throws RuntimeException If the object being added is an invalid number.
    286     */
    287    // TODO: Change {@code append) to {@link #append} when append is
    288    // unhidden.
    289    public JSONObject accumulate(String name, Object value) {
    290        Object current = nameValuePairs.get(checkName(name));
    291        if (current == null) {
    292            return put(name, value);
    293        }
    294
    295        if (current instanceof JSONArray) {
    296            JSONArray array = (JSONArray) current;
    297            array.checkedPut(value);
    298        } else {
    299            JSONArray array = new JSONArray();
    300            array.checkedPut(current);
    301            array.checkedPut(value);
    302            nameValuePairs.put(name, array);
    303        }
    304        return this;
    305    }
    306
    307    /**
    308     * Appends values to the array mapped to {@code name}. A new {@link JSONArray}
    309     * mapping for {@code name} will be inserted if no mapping exists. If the existing
    310     * mapping for {@code name} is not a {@link JSONArray}, a {@link RuntimeException}
    311     * will be thrown.
    312     *
    313     * @param name  The name of the array to which the value should be appended.
    314     * @param value The value to append.
    315     * @return this object.
    316     * @throws JSONTypeMismatchException if {@code name} is {@code null} or if the mapping for
    317     *                       {@code name} is non-null and is not a {@link JSONArray}.
    318     */
    319    public JSONObject append(String name, Object value) {
    320        JSON.testValidity(value);
    321        Object current = nameValuePairs.get(checkName(name));
    322
    323        final JSONArray array;
    324        if (current instanceof JSONArray) {
    325            array = (JSONArray) current;
    326        } else if (current == null) {
    327            JSONArray newArray = new JSONArray();
    328            nameValuePairs.put(name, newArray);
    329            array = newArray;
    330        } else {
    331            throw new JSONTypeMismatchException("JSONObject[\"" + name + "\"]", JSONType.ARRAY, current.getClass());
    332        }
    333
    334        array.checkedPut(value);
    335
    336        return this;
    337    }
    338
    339    String checkName(String name) {
    340        if (name == null) {
    341            throw new RuntimeException("Names must be non-null");
    342        }
    343        return name;
    344    }
    345
    346    /**
    347     * Returns true if this object has no mapping for {@code name} or if it has
    348     * a mapping whose value is {@link #NULL}.
    349     *
    350     * @param name The name of the value to check on.
    351     * @return true if the field doesn't exist or is null.
    352     */
    353    public boolean isNull(String name) {
    354        Object value = nameValuePairs.get(name);
    355        return value == null || value == NULL;
    356    }
    357
    358    /**
    359     * Returns true if this object has a mapping for {@code name}. The mapping
    360     * may be {@link #NULL}.
    361     *
    362     * @deprecated use {@link #containsKey(Object)} instead
    363     * @param name
    364     *            The name of the value to check on.
    365     * @return true if this object has a field named {@code name}
    366     */
    367    @Deprecated
    368    public boolean has(String name) {
    369        return containsKey(name);
    370    }
    371
    372    /**
    373     * Returns the value mapped by {@code name}, or null if no such mapping
    374     * exists.
    375     *
    376     * @param name The name of the value to get.
    377     * @return The value.
    378     */
    379    public Object opt(Object name) {
    380        return nameValuePairs.get(name);
    381    }
    382
    383    /**
    384     * Returns the value mapped by {@code name} if it exists and is a boolean or
    385     * can be coerced to a boolean, or throws otherwise.
    386     *
    387     * @param name The name of the field we want.
    388     * @return The selected value if it exists.
    389     * @throws JSONValueNotFoundException if the mapping doesn't exist
    390     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    391     *                       to a boolean.
    392     */
    393    public boolean getBoolean(String name) {
    394        Object object = opt(name);
    395        if (object == null) {
    396            throw JSONExceptionBuilder.valueNotFound(false, name, JSONType.BOOLEAN);
    397        }
    398        Boolean result = JSON.toBoolean(object);
    399        if (result == null) {
    400            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.BOOLEAN);
    401        }
    402        return result;
    403    }
    404
    405    /**
    406     * Returns the value to which the specified key is mapped and a boolean, or
    407     * {@code defaultValue} if this JSONObject contains no mapping for the key.
    408     *
    409     * @param name the key whose associated value is to be returned
    410     * @param defaultValue the default mapping of the key
    411     * @return the value to which the specified key is mapped, or
    412     * {@code defaultValue} if this JSONObject contains no mapping for the key
    413     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    414     *                       to a boolean.
    415     * @since 5.7
    416     */
    417    public boolean getBooleanOrDefault(String name, boolean defaultValue)
    418    {
    419        Object object = opt(name);
    420        if (object == null)
    421        {
    422            return defaultValue;
    423        }
    424        Boolean result = JSON.toBoolean(object);
    425        if (result == null)
    426        {
    427            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.BOOLEAN);
    428        }
    429        return result;
    430    }
    431
    432    /**
    433     * Returns the value mapped by {@code name} if it exists and is a double or
    434     * can be coerced to a double, or throws otherwise.
    435     *
    436     * @param name The name of the field we want.
    437     * @return The selected value if it exists.
    438     * @throws JSONValueNotFoundException if the mapping doesn't exist
    439     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    440     *                       to a double.
    441     */
    442    public double getDouble(String name) {
    443        Object object = opt(name);
    444        if (object == null) {
    445            throw JSONExceptionBuilder.valueNotFound(false, name, JSONType.NUMBER);
    446        }
    447        Double result = JSON.toDouble(object);
    448        if (result == null) {
    449            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.NUMBER);
    450        }
    451        return result;
    452    }
    453
    454    /**
    455     * Returns the value mapped by {@code name} if it exists and is an int or
    456     * can be coerced to an int, or throws otherwise.
    457     *
    458     * @param name The name of the field we want.
    459     * @return The selected value if it exists.
    460     * @throws JSONValueNotFoundException if the mapping doesn't exist
    461     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    462     *                       to an int.
    463     */
    464    public int getInt(String name) {
    465        Object object = opt(name);
    466        if (object == null) {
    467            throw JSONExceptionBuilder.valueNotFound(false, name, JSONType.NUMBER);
    468        }
    469        Integer result = JSON.toInteger(object);
    470        if (result == null) {
    471            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.NUMBER);
    472        }
    473        return result;
    474    }
    475
    476    /**
    477     * Returns the value to which the specified key is mapped and an int, or
    478     * {@code defaultValue} if this JSONObject contains no mapping for the key.
    479     *
    480     * @param name the key whose associated value is to be returned
    481     * @param defaultValue the default mapping of the key
    482     * @return the value to which the specified key is mapped, or
    483     * {@code defaultValue} if this JSONObject contains no mapping for the key
    484     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    485     *                       to an int.
    486     * @since 5.7
    487     */
    488    public int getIntOrDefault(String name, int defaultValue)
    489    {
    490        Object object = opt(name);
    491        if (object == null)
    492        {
    493            return defaultValue;
    494        }
    495        Integer result = JSON.toInteger(object);
    496        if (result == null)
    497        {
    498            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.NUMBER);
    499        }
    500        return result;
    501    }
    502
    503    /**
    504     * Returns the value mapped by {@code name} if it exists and is a long or
    505     * can be coerced to a long, or throws otherwise.
    506     * Note that JSON represents numbers as doubles,
    507     *
    508     * so this is <a href="#lossy">lossy</a>; use strings to transfer numbers
    509     * via JSON without loss.
    510     *
    511     * @param name The name of the field that we want.
    512     * @return The value of the field.
    513     * @throws JSONValueNotFoundException if the mapping doesn't exist
    514     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    515     *                       to a long.
    516     */
    517    public long getLong(String name) {
    518        Object object = opt(name);
    519        if (object == null) {
    520            throw JSONExceptionBuilder.valueNotFound(false, name, JSONType.NUMBER);
    521        }
    522        Long result = JSON.toLong(object);
    523        if (result == null) {
    524            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.NUMBER);
    525        }
    526        return result;
    527    }
    528
    529    /**
    530     * Returns the value to which the specified key is mapped and a long, or
    531     * {@code defaultValue} if this JSONObject contains no mapping for the key.
    532     *
    533     * @param name the key whose associated value is to be returned
    534     * @param defaultValue the default mapping of the key
    535     * @return the value to which the specified key is mapped, or
    536     * {@code defaultValue} if this JSONObject contains no mapping for the key
    537     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    538     *                       to a long.
    539     * @since 5.7
    540     */
    541    public long getLongOrDefault(String name, long defaultValue)
    542    {
    543        Object object = opt(name);
    544        if (object == null)
    545        {
    546            return defaultValue;
    547        }
    548        Long result = JSON.toLong(object);
    549        if (result == null)
    550        {
    551            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.NUMBER);
    552        }
    553        return result;
    554    }
    555
    556    /**
    557     * Returns the value mapped by {@code name} if it exists, coercing it if
    558     * necessary, or throws if no such mapping exists.
    559     *
    560     * @param name The name of the field we want.
    561     * @return The value of the field.
    562     * @throws JSONValueNotFoundException if the mapping doesn't exist
    563     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    564     *                               to String
    565     */
    566    public String getString(String name) {
    567        Object object = opt(name);
    568        if (object == null) {
    569            throw JSONExceptionBuilder.valueNotFound(false, name, JSONType.STRING);
    570        }
    571        String result = JSON.toString(object);
    572        if (result == null) {
    573            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.STRING);
    574        }
    575        return result;
    576    }
    577
    578    /**
    579     * Returns the value to which the specified key is mapped and a string, or
    580     * {@code defaultValue} if this JSONObject contains no mapping for the key.
    581     *
    582     * @param name the key whose associated value is to be returned
    583     * @param defaultValue the default mapping of the key
    584     * @return the value to which the specified key is mapped, or
    585     * {@code defaultValue} if this JSONObject contains no mapping for the key
    586     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    587     *                       to a string.
    588     * @since 5.7
    589     */
    590    public String getStringOrDefault(String name, String defaultValue)
    591    {
    592        Object object = opt(name);
    593        if (object == null)
    594        {
    595            return defaultValue;
    596        }
    597        String result = JSON.toString(object);
    598        if (result == null)
    599        {
    600            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.STRING);
    601        }
    602        return result;
    603    }
    604
    605    /**
    606     * Returns the value mapped by {@code name} if it exists and is a {@code
    607     * JSONArray}, or throws otherwise.
    608     *
    609     * @param name The field we want to get.
    610     * @return The value of the field (if it is a JSONArray.
    611     * @throws JSONValueNotFoundException if the mapping doesn't exist
    612     * @throws JSONTypeMismatchException if the mapping is not a {@code
    613     *                       JSONArray}.
    614     */
    615    public JSONArray getJSONArray(String name) {
    616        Object object = opt(name);
    617        if (object == null) {
    618            throw JSONExceptionBuilder.valueNotFound(false, name, JSONType.ARRAY);
    619        }
    620        if (object instanceof JSONArray) {
    621            return (JSONArray) object;
    622        } else {
    623            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.ARRAY);
    624        }
    625    }
    626
    627    /**
    628     * Returns the value to which the specified key is mapped and a JSONArray, or
    629     * {@code defaultValue} if this JSONObject contains no mapping for the key.
    630     *
    631     * @param name the key whose associated value is to be returned
    632     * @param defaultValue the default mapping of the key
    633     * @return the value to which the specified key is mapped, or
    634     * {@code defaultValue} if this JSONObject contains no mapping for the key
    635     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    636     *                       to a JSONArray.
    637     * @since 5.7
    638     */
    639    public JSONArray getJSONArrayOrDefault(String name, JSONArray defaultValue)
    640    {
    641        Object object = opt(name);
    642        if (object == null)
    643        {
    644            return defaultValue;
    645        }
    646        if (object instanceof JSONArray) {
    647            return (JSONArray) object;
    648        } else {
    649            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.ARRAY);
    650        }
    651    }
    652
    653    /**
    654     * Returns the value mapped by {@code name} if it exists and is a {@code
    655     * JSONObject}, or throws otherwise.
    656     *
    657     * @param name The name of the field that we want.
    658     * @return a specified field value (if it is a JSONObject)
    659     * @throws JSONValueNotFoundException if the mapping doesn't exist
    660     * @throws JSONTypeMismatchException if the mapping is not a {@code
    661     *                       JSONObject}.
    662     */
    663    public JSONObject getJSONObject(String name) {
    664        Object object = opt(name);
    665        if (object == null) {
    666            throw JSONExceptionBuilder.valueNotFound(false, name, JSONType.OBJECT);
    667        }
    668        if (object instanceof JSONObject) {
    669            return (JSONObject) object;
    670        } else {
    671            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.OBJECT);
    672        }
    673    }
    674
    675
    676    /**
    677     * Returns the value to which the specified key is mapped and a JSONObject, or
    678     * {@code defaultValue} if this map contains no mapping for the key.
    679     *
    680     * @param name the key whose associated value is to be returned
    681     * @param defaultValue the default mapping of the key
    682     * @return the value to which the specified key is mapped, or
    683     * {@code defaultValue} if this map contains no mapping for the key
    684     * @throws JSONTypeMismatchException if the mapping cannot be coerced
    685     *                       to a JSONObject.
    686     * @since 5.7
    687     */
    688    public JSONObject getJSONObjectOrDefault(String name, JSONObject defaultValue)
    689    {
    690        Object object = opt(name);
    691        if (object == null)
    692        {
    693            return defaultValue;
    694        }
    695        if (object instanceof JSONObject) {
    696            return (JSONObject) object;
    697        } else {
    698            throw JSONExceptionBuilder.typeMismatch(false, name, object, JSONType.OBJECT);
    699        }
    700    }
    701    /**
    702     * Returns the set of {@code String} names in this object. The returned set
    703     * is a view of the keys in this object. {@link Set#remove(Object)} will remove
    704     * the corresponding mapping from this object and set iterator behaviour
    705     * is undefined if this object is modified after it is returned.
    706     *
    707     * See {@link #keys()}.
    708     *
    709     * @return The names in this object.
    710     */
    711    public Set<String> keys() {
    712        return nameValuePairs.keySet();
    713    }
    714
    715    /**
    716     * Returns an array containing the string names in this object. This method
    717     * returns null if this object contains no mappings.
    718     *
    719     * @return the names.
    720     */
    721    public JSONArray names() {
    722        return nameValuePairs.isEmpty()
    723                ? null
    724                : JSONArray.from(nameValuePairs.keySet());
    725    }
    726
    727    /**
    728     * Encodes the number as a JSON string.
    729     *
    730     * @param number a finite value. May not be {@link Double#isNaN() NaNs} or
    731     *               {@link Double#isInfinite() infinities}.
    732     * @return The encoded number in string form.
    733     * @throws RuntimeException On internal errors. Shouldn't happen.
    734     */
    735    public static String numberToString(Number number) {
    736        if (number == null) {
    737            throw new RuntimeException("Number must be non-null");
    738        }
    739
    740        double doubleValue = number.doubleValue();
    741        JSON.checkDouble(doubleValue);
    742
    743        // the original returns "-0" instead of "-0.0" for negative zero
    744        if (number.equals(NEGATIVE_ZERO)) {
    745            return "-0";
    746        }
    747
    748        long longValue = number.longValue();
    749        if (doubleValue == (double) longValue) {
    750            return Long.toString(longValue);
    751        }
    752
    753        return number.toString();
    754    }
    755
    756    static String doubleToString(double d)
    757    {
    758        if (Double.isInfinite(d) || Double.isNaN(d))
    759        {
    760            return "null";
    761        }
    762
    763        return numberToString(d);
    764    }
    765
    766    /**
    767     * Encodes {@code data} as a JSON string. This applies quotes and any
    768     * necessary character escaping.
    769     *
    770     * @param data the string to encode. Null will be interpreted as an empty
    771     *             string.
    772     * @return the quoted string.
    773     */
    774    public static String quote(String data) {
    775        if (data == null) {
    776            return "\"\"";
    777        }
    778        try {
    779            JSONStringer stringer = new JSONStringer();
    780            stringer.open(JSONStringer.Scope.NULL, "");
    781            stringer.string(data);
    782            stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
    783            return stringer.toString();
    784        } catch (RuntimeException e) {
    785            throw new AssertionError();
    786        }
    787    }
    788
    789
    790
    791    /**
    792     * Prints the JSONObject using the session.
    793     *
    794     * @since 5.2.0
    795     */
    796    @Override
    797    void print(JSONPrintSession session)
    798    {
    799        session.printSymbol('{');
    800
    801        session.indent();
    802
    803        boolean comma = false;
    804
    805        for (String key : keys())
    806        {
    807            if (comma)
    808                session.printSymbol(',');
    809
    810            session.newline();
    811
    812            session.printQuoted(key);
    813
    814            session.printSymbol(':');
    815
    816            printValue(session, nameValuePairs.get(key));
    817
    818            comma = true;
    819        }
    820
    821        session.outdent();
    822
    823        if (comma)
    824            session.newline();
    825
    826        session.printSymbol('}');
    827    }
    828
    829
    830    /**
    831     * Prints a value (a JSONArray or JSONObject, or a value stored in an array or object) using
    832     * the session.
    833     *
    834     * @since 5.2.0
    835     */
    836    static void printValue(JSONPrintSession session, Object value)
    837    {
    838
    839        if (value == null || value == NULL)
    840        {
    841            session.print("null");
    842            return;
    843        }
    844        if (value instanceof JSONObject)
    845        {
    846            ((JSONObject) value).print(session);
    847            return;
    848        }
    849
    850        if (value instanceof JSONArray)
    851        {
    852            ((JSONArray) value).print(session);
    853            return;
    854        }
    855
    856        if (value instanceof JSONString)
    857        {
    858            String printValue = ((JSONString) value).toJSONString();
    859
    860            session.print(printValue);
    861
    862            return;
    863        }
    864
    865        if (value instanceof Number)
    866        {
    867            String printValue = numberToString((Number) value);
    868            session.print(printValue);
    869            return;
    870        }
    871
    872        if (value instanceof Boolean)
    873        {
    874            session.print(value.toString());
    875
    876            return;
    877        }
    878
    879        // Otherwise it really should just be a string. Nothing else can go in.
    880        session.printQuoted(value.toString());
    881    }
    882
    883    public boolean equals(Object obj)
    884    {
    885        if (obj == null)
    886            return false;
    887
    888        if (!(obj instanceof JSONObject))
    889            return false;
    890
    891        JSONObject other = (JSONObject) obj;
    892
    893        return nameValuePairs.equals(other.nameValuePairs);
    894    }
    895
    896    /**
    897     * Returns a Map of the keys and values of the JSONObject. The returned map is unmodifiable.
    898     * Note that changes to the JSONObject will be reflected in the map. In addition, null values in the JSONObject
    899     * are represented as {@link JSONObject#NULL} in the map.
    900     *
    901     * @return unmodifiable map of properties and values
    902     * @since 5.4
    903     */
    904    public Map<String, Object> toMap()
    905    {
    906        return Collections.unmodifiableMap(nameValuePairs);
    907    }
    908
    909    /**
    910     * Navigates into a nested JSONObject, creating the JSONObject if necessary. They key must not exist,
    911     * or must be a JSONObject.
    912     *
    913     * @param key
    914     * @return the nested JSONObject
    915     * @throws IllegalStateException
    916     *         if the current value for the key is not null and not JSONObject
    917     */
    918    public JSONObject in(String key)
    919    {
    920        assert key != null;
    921
    922        Object nested = nameValuePairs.get(key);
    923
    924        if (nested != null && !(nested instanceof JSONObject))
    925        {
    926            throw new IllegalStateException(String.format("JSONObject[%s] is not a JSONObject.", quote(key)));
    927        }
    928
    929        if (nested == null)
    930        {
    931            nested = new JSONObject();
    932            nameValuePairs.put(key, nested);
    933        }
    934
    935        return (JSONObject) nested;
    936    }
    937
    938    /**
    939     * Returns the number of key-value mappings in this JSONObject.
    940     * If it contains more than {@code Integer.MAX_VALUE} elements, returns
    941     * {@code Integer.MAX_VALUE}.
    942     *
    943     * @return the number of key-value mappings in this JSONObject
    944     * @since 5.7
    945     */
    946    @Override
    947    public int size()
    948    {
    949        return nameValuePairs.size();
    950    }
    951
    952    /**
    953     * Returns {@code true} if this JSONObject contains no key-value mappings.
    954     *
    955     * @return {@code true} if this JSONObject contains no key-value mappings
    956     * @since 5.7
    957     */
    958    @Override
    959    public boolean isEmpty()
    960    {
    961        return nameValuePairs.isEmpty();
    962    }
    963
    964    /**
    965     * Returns {@code true} if this JSONObject contains a mapping for the specified
    966     * key.
    967     *
    968     * @param key
    969     *            key whose presence in this map is to be tested
    970     * @return {@code true} if this map contains a mapping for the specified
    971     *         key
    972     * @since 5.7
    973     */
    974    @Override
    975    public boolean containsKey(Object key)
    976    {
    977        return nameValuePairs.containsKey(key);
    978    }
    979
    980    /**
    981     * Returns {@code true} if this JSONObject maps one or more keys to the
    982     * specified value.
    983     *
    984     * @param value value whose presence in this map is to be tested
    985     * @return {@code true} if this JSONObject maps one or more keys to the
    986     *         specified value
    987     * @since 5.7
    988     */
    989    @Override
    990    public boolean containsValue(Object value)
    991    {
    992        return nameValuePairs.containsValue(value);
    993    }
    994
    995    /**
    996     * Returns the value mapped by {@code name}, or throws if no such mapping exists.
    997     *
    998     * @param name The name of the value to get.
    999     * @return The value.
    1000     * @throws JSONValueNotFoundException if no such mapping exists.
    1001     */ @Override
    1002    public Object get(Object name)
    1003    {
    1004        Object result = nameValuePairs.get(name);
    1005         if (result == null) {
    1006            throw JSONExceptionBuilder.valueNotFound(false, name, JSONType.ANY);
    1007         }
    1008         return result;
    1009    }
    1010
    1011    /**
    1012      * Returns the value to which the specified key is mapped, or
    1013      * {@code defaultValue} if this JSONObject contains no mapping for the key.
    1014      *
    1015      * @param key the key whose associated value is to be returned
    1016      * @param defaultValue the default mapping of the key
    1017      * @return the value to which the specified key is mapped, or
    1018      *         {@code defaultValue} if this JSONObject contains no mapping for the key
    1019      * @since 5.7
    1020      */
    1021    @Override
    1022    public Object getOrDefault(Object key, Object defaultValue)
    1023    {
    1024        Object value = opt(key);
    1025        return value == null ? defaultValue : value;
    1026    }
    1027
    1028    /**
    1029     * Removes the named mapping if it exists; does nothing otherwise.
    1030     *
    1031     * @param name The name of the mapping to remove.
    1032     * @return the value previously mapped by {@code name}, or null if there was
    1033     *         no such mapping.
    1034     */
    1035    @Override
    1036    public Object remove(Object name)
    1037    {
    1038        return nameValuePairs.remove(name);
    1039    }
    1040
    1041    /**
    1042     * Invokes {@link #put(String, Object)} for each value from the map.
    1043     *
    1044     * @param newProperties
    1045     *            to add to this JSONObject
    1046     * @since 5.7
    1047     */
    1048    @Override
    1049    public void putAll(Map<? extends String, ? extends Object> newProperties)
    1050    {
    1051        assert newProperties != null;
    1052
    1053        for (Map.Entry<? extends String, ? extends Object> e : newProperties.entrySet())
    1054        {
    1055            put(e.getKey(), e.getValue());
    1056        }
    1057    }
    1058
    1059    /**
    1060     * Removes all of the mappings from this JSONObject.
    1061     *
    1062     * @since 5.7
    1063     */
    1064    @Override
    1065    public void clear()
    1066    {
    1067        nameValuePairs.clear();
    1068    }
    1069
    1070    /**
    1071     * Returns a {@link Set} view of the keys contained in this JSONObject.
    1072     * The set is backed by the JSONObject, so changes to the map are
    1073     * reflected in the set, and vice-versa.
    1074     *
    1075     * @return a set view of the keys contained in this JSONObject
    1076     * @since 5.7
    1077     */
    1078    @Override
    1079    public Set<String> keySet()
    1080    {
    1081        return nameValuePairs.keySet();
    1082    }
    1083
    1084    /**
    1085     * Returns a {@link Collection} view of the values contained in this JSONObject.
    1086     * The collection is backed by the JSONObject, so changes to the map are
    1087     * reflected in the collection, and vice-versa.
    1088     *
    1089     * @return a collection view of the values contained in this JSONObject
    1090     * @since 5.7
    1091     */
    1092    @Override
    1093    public Collection<Object> values()
    1094    {
    1095        return nameValuePairs.values();
    1096    }
    1097
    1098    /**
    1099     * Returns a {@link Set} view of the mappings contained in this JSONObject.
    1100     * The set is backed by the JSONObject, so changes to the map are
    1101     * reflected in the set, and vice-versa.
    1102     *
    1103     * @return a set view of the mappings contained in this JSONObject
    1104     * @since 5.7
    1105     */
    1106    @Override
    1107    public Set<Entry<String, Object>> entrySet()
    1108    {
    1109        return nameValuePairs.entrySet();
    1110    }
    1111
    1112    /**
    1113     * If the specified key is not already associated with a value or is
    1114     * associated with null, associates it with the given non-null value.
    1115     *
    1116     * Otherwise, replaces the associated value with the results of the given
    1117     * remapping function, or removes if the result is {@code null}.
    1118     *
    1119     * @param key
    1120     *            key with which the resulting value is to be associated
    1121     * @param value
    1122     *            the non-null value to be merged with the existing value
    1123     *            associated with the key or, if no existing value or a null value
    1124     *            is associated with the key, to be associated with the key
    1125     * @param remappingFunction
    1126     *            the function to recompute a value if present
    1127     * @return the new value associated with the specified key, or null if no
    1128     *         value is associated with the key
    1129     * @throws NullPointerException
    1130     *             if the specified key is null or the value or remappingFunction
    1131     *             is null
    1132     * @since 5.7
    1133     */
    1134    @Override
    1135    public Object merge(String key, Object value,
    1136            BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction)
    1137    {
    1138        // We need to override the merge method due to the default implementation using
    1139        // #get(String) to check for the key, which will throw a {@code JSONValueNotFoundException}
    1140        // if not found.
    1141
    1142        Objects.requireNonNull(remappingFunction);
    1143        Objects.requireNonNull(value);
    1144
    1145        Object oldValue = opt(key);
    1146        Object newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
    1147
    1148        put(key, newValue);
    1149
    1150        return newValue;
    1151    }
    1152
    1153}