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