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}