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
779        StringBuilder out = new StringBuilder(data.length() + 8);
780        out.append('"');
781        escapeInto(out, data);
782        out.append('"');
783        return out.toString();
784    }
785
786    private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray();
787
788    /**
789     * Appends the escaped (but not quoted) form of {@code value} to {@code out}: the same
790     * character escaping rules as {@link #quote(String)}, without allocating an intermediate
791     * String or wrapping quotes.
792     * 
793     * <p>This is the direct-to-{@link StringBuilder} fast path used by {@code quote()} itself,
794     * which always targets a StringBuilder and is called often enough (once per printed string/key)
795     * that the {@link JSONPrintSink} indirection used by {@link #escapeInto(JSONPrintSink, String)}
796     * measurably costs more than it's worth here. See {@link #escapeInto(JSONPrintSink, String)}
797     * for the print-session variant.
798     */
799    static void escapeInto(StringBuilder out, String value) {
800        char currentChar = 0;
801
802        for (int i = 0, length = value.length(); i < length; i++) {
803            char previousChar = currentChar;
804            currentChar = value.charAt(i);
805
806            /*
807             * From RFC 4627, "All Unicode characters may be placed within the
808             * quotation marks except for the characters that must be escaped:
809             * quotation mark, reverse solidus, and the control characters
810             * (U+0000 through U+001F)."
811             */
812            switch (currentChar) {
813                case '"':
814                case '\\':
815                    out.append('\\').append(currentChar);
816                    break;
817
818                case '/':
819                    // it makes life easier for HTML embedding of javascript if we escape </ sequences
820                    if (previousChar == '<') {
821                        out.append('\\');
822                    }
823                    out.append(currentChar);
824                    break;
825
826                case '\t':
827                    out.append("\\t");
828                    break;
829
830                case '\b':
831                    out.append("\\b");
832                    break;
833
834                case '\n':
835                    out.append("\\n");
836                    break;
837
838                case '\r':
839                    out.append("\\r");
840                    break;
841
842                case '\f':
843                    out.append("\\f");
844                    break;
845
846                default:
847                    if (currentChar <= 0x1F || (currentChar >= 0x0080 && currentChar < 0x00a0) || (currentChar >= 0x2000 && currentChar < 0x2100)) {
848                        out.append('\\').append('u')
849                           .append(HEX_DIGITS[(currentChar >> 12) & 0xF])
850                           .append(HEX_DIGITS[(currentChar >> 8) & 0xF])
851                           .append(HEX_DIGITS[(currentChar >> 4) & 0xF])
852                           .append(HEX_DIGITS[currentChar & 0xF]);
853                    } else {
854                        out.append(currentChar);
855                    }
856                    break;
857            }
858        }
859    }
860
861    /**
862     * Same escaping rules as {@link #escapeInto(StringBuilder, String)}, targeting a
863     * {@link JSONPrintSink} instead.
864     * 
865     * <p>Used by the print sessions, whose destination varies between an in-memory
866     * {@link StringBuilder} ({@code toString()}/{@code toCompactString()}) and a
867     * caller-supplied {@link java.io.PrintWriter} ({@code print(PrintWriter, boolean)}),
868     * so they can escape directly into whichever one they were constructed with.
869     * 
870     * <p>Note: Having dedicated methods for the types instead of a classifying mechanism
871     * keeps the call site monomorphic for the JVM, allowing for greater optimization
872     * potential.
873     */
874    static void escapeInto(JSONPrintSink out, String value) {
875        char currentChar = 0;
876
877        for (int i = 0, length = value.length(); i < length; i++) {
878            char previousChar = currentChar;
879            currentChar = value.charAt(i);
880
881            switch (currentChar) {
882                case '"':
883                case '\\':
884                    out.append('\\').append(currentChar);
885                    break;
886
887                case '/':
888                    if (previousChar == '<') {
889                        out.append('\\');
890                    }
891                    out.append(currentChar);
892                    break;
893
894                case '\t':
895                    out.append("\\t");
896                    break;
897
898                case '\b':
899                    out.append("\\b");
900                    break;
901
902                case '\n':
903                    out.append("\\n");
904                    break;
905
906                case '\r':
907                    out.append("\\r");
908                    break;
909
910                case '\f':
911                    out.append("\\f");
912                    break;
913
914                default:
915                    if (currentChar <= 0x1F || (currentChar >= 0x0080 && currentChar < 0x00a0) || (currentChar >= 0x2000 && currentChar < 0x2100)) {
916                        out.append('\\').append('u')
917                           .append(HEX_DIGITS[(currentChar >> 12) & 0xF])
918                           .append(HEX_DIGITS[(currentChar >> 8) & 0xF])
919                           .append(HEX_DIGITS[(currentChar >> 4) & 0xF])
920                           .append(HEX_DIGITS[currentChar & 0xF]);
921                    } else {
922                        out.append(currentChar);
923                    }
924                    break;
925            }
926        }
927    }
928
929
930
931    /**
932     * Prints the JSONObject using the session.
933     *
934     * @since 5.2.0
935     */
936    @Override
937    void print(JSONPrintSession session)
938    {
939        session.printSymbol('{');
940
941        session.indent();
942
943        boolean comma = false;
944
945        for (String key : keys())
946        {
947            if (comma)
948                session.printSymbol(',');
949
950            session.newline();
951
952            session.printQuoted(key);
953
954            session.printSymbol(':');
955
956            printValue(session, nameValuePairs.get(key));
957
958            comma = true;
959        }
960
961        session.outdent();
962
963        if (comma)
964            session.newline();
965
966        session.printSymbol('}');
967    }
968
969    /**
970     * Prints a value (a JSONArray or JSONObject, or a value stored in an array or object) using
971     * the session.
972     *
973     * @since 5.2.0
974     */
975    static void printValue(JSONPrintSession session, Object value)
976    {
977        // Orderd by (assumed) most common frequency in JSON
978
979        // Null Check: Must be first.
980        if (value == null || value == NULL)
981        {
982            session.print("null");
983            return;
984        }
985
986        // String: Explicitly checking here prevents the most common 
987        // JSON type from falling through all other checks.
988        if (value instanceof String)
989        {
990            session.printQuoted((String) value);
991            return;
992        }
993
994        // Number: Highly common in JSON payloads.
995        if (value instanceof Number)
996        {
997            String printValue = numberToString((Number) value);
998            session.print(printValue);
999            return;
1000        }
1001
1002        // JSONObject: The primary structural container.
1003        if (value instanceof JSONObject)
1004        {
1005            ((JSONObject) value).print(session);
1006            return;
1007        }
1008
1009        // Boolean: Common, but usually fewer instances per payload than numbers.
1010        if (value instanceof Boolean)
1011        {
1012            session.print(value.toString());
1013            return;
1014        }
1015
1016        // JSONArray: Generally fewer arrays than objects in nested structures.
1017        if (value instanceof JSONArray)
1018        {
1019            ((JSONArray) value).print(session);
1020            return;
1021        }
1022
1023        // JSONString: Kept near the bottom assuming it is a custom wrapper.
1024        if (value instanceof JSONString)
1025        {
1026            String printValue = ((JSONString) value).toJSONString();
1027            session.print(printValue);
1028            return;
1029        }
1030
1031        // Fallback for any other unexpected objects
1032        session.printQuoted(value.toString());
1033    }
1034
1035    public boolean equals(Object obj)
1036    {
1037        if (obj == null)
1038            return false;
1039
1040        if (!(obj instanceof JSONObject))
1041            return false;
1042
1043        JSONObject other = (JSONObject) obj;
1044
1045        return nameValuePairs.equals(other.nameValuePairs);
1046    }
1047
1048    @Override
1049    public int hashCode() {
1050        return nameValuePairs.hashCode();
1051    }
1052
1053    /**
1054     * Returns a Map of the keys and values of the JSONObject. The returned map is unmodifiable.
1055     * Note that changes to the JSONObject will be reflected in the map. In addition, null values in the JSONObject
1056     * are represented as {@link JSONObject#NULL} in the map.
1057     *
1058     * @return unmodifiable map of properties and values
1059     * @since 5.4
1060     */
1061    public Map<String, Object> toMap()
1062    {
1063        return Collections.unmodifiableMap(nameValuePairs);
1064    }
1065
1066    /**
1067     * Navigates into a nested JSONObject, creating the JSONObject if necessary. They key must not exist,
1068     * or must be a JSONObject.
1069     *
1070     * @param key
1071     * @return the nested JSONObject
1072     * @throws IllegalStateException
1073     *         if the current value for the key is not null and not JSONObject
1074     */
1075    public JSONObject in(String key)
1076    {
1077        assert key != null;
1078
1079        Object nested = nameValuePairs.get(key);
1080
1081        if (nested != null && !(nested instanceof JSONObject))
1082        {
1083            throw new IllegalStateException(String.format("JSONObject[%s] is not a JSONObject.", quote(key)));
1084        }
1085
1086        if (nested == null)
1087        {
1088            nested = new JSONObject();
1089            nameValuePairs.put(key, nested);
1090        }
1091
1092        return (JSONObject) nested;
1093    }
1094
1095    /**
1096     * Returns the number of key-value mappings in this JSONObject.
1097     * If it contains more than {@code Integer.MAX_VALUE} elements, returns
1098     * {@code Integer.MAX_VALUE}.
1099     *
1100     * @return the number of key-value mappings in this JSONObject
1101     * @since 5.7
1102     */
1103    @Override
1104    public int size()
1105    {
1106        return nameValuePairs.size();
1107    }
1108
1109    /**
1110     * Returns {@code true} if this JSONObject contains no key-value mappings.
1111     *
1112     * @return {@code true} if this JSONObject contains no key-value mappings
1113     * @since 5.7
1114     */
1115    @Override
1116    public boolean isEmpty()
1117    {
1118        return nameValuePairs.isEmpty();
1119    }
1120
1121    /**
1122     * Returns {@code true} if this JSONObject contains a mapping for the specified
1123     * key.
1124     *
1125     * @param key
1126     *            key whose presence in this map is to be tested
1127     * @return {@code true} if this map contains a mapping for the specified
1128     *         key
1129     * @since 5.7
1130     */
1131    @Override
1132    public boolean containsKey(Object key)
1133    {
1134        return nameValuePairs.containsKey(key);
1135    }
1136
1137    /**
1138     * Returns {@code true} if this JSONObject maps one or more keys to the
1139     * specified value.
1140     *
1141     * @param value value whose presence in this map is to be tested
1142     * @return {@code true} if this JSONObject maps one or more keys to the
1143     *         specified value
1144     * @since 5.7
1145     */
1146    @Override
1147    public boolean containsValue(Object value)
1148    {
1149        return nameValuePairs.containsValue(value);
1150    }
1151
1152    /**
1153     * Returns the value mapped by {@code name}, or throws if no such mapping exists.
1154     *
1155     * @param name The name of the value to get.
1156     * @return The value.
1157     * @throws JSONValueNotFoundException if no such mapping exists.
1158     */ @Override
1159    public Object get(Object name)
1160    {
1161        Object result = nameValuePairs.get(name);
1162         if (result == null) {
1163            throw JSONExceptionBuilder.valueNotFound(false, name, JSONType.ANY);
1164         }
1165         return result;
1166    }
1167
1168    /**
1169      * Returns the value to which the specified key is mapped, or
1170      * {@code defaultValue} if this JSONObject contains no mapping for the key.
1171      *
1172      * @param key the key whose associated value is to be returned
1173      * @param defaultValue the default mapping of the key
1174      * @return the value to which the specified key is mapped, or
1175      *         {@code defaultValue} if this JSONObject contains no mapping for the key
1176      * @since 5.7
1177      */
1178    @Override
1179    public Object getOrDefault(Object key, Object defaultValue)
1180    {
1181        Object value = opt(key);
1182        return value == null ? defaultValue : value;
1183    }
1184
1185    /**
1186     * Removes the named mapping if it exists; does nothing otherwise.
1187     *
1188     * @param name The name of the mapping to remove.
1189     * @return the value previously mapped by {@code name}, or null if there was
1190     *         no such mapping.
1191     */
1192    @Override
1193    public Object remove(Object name)
1194    {
1195        return nameValuePairs.remove(name);
1196    }
1197
1198    /**
1199     * Invokes {@link #put(String, Object)} for each value from the map.
1200     *
1201     * @param newProperties
1202     *            to add to this JSONObject
1203     * @since 5.7
1204     */
1205    @Override
1206    public void putAll(Map<? extends String, ? extends Object> newProperties)
1207    {
1208        assert newProperties != null;
1209
1210        for (Map.Entry<? extends String, ? extends Object> e : newProperties.entrySet())
1211        {
1212            put(e.getKey(), e.getValue());
1213        }
1214    }
1215
1216    /**
1217     * Removes all of the mappings from this JSONObject.
1218     * 
1219     * @since 5.7
1220     */
1221    @Override
1222    public void clear()
1223    {
1224        nameValuePairs.clear();
1225    }
1226
1227    /**
1228     * Returns a {@link Set} view of the keys contained in this JSONObject.
1229     * The set is backed by the JSONObject, so changes to the map are
1230     * reflected in the set, and vice-versa.
1231     *
1232     * @return a set view of the keys contained in this JSONObject
1233     * @since 5.7
1234     */
1235    @Override
1236    public Set<String> keySet()
1237    {
1238        return nameValuePairs.keySet();
1239    }
1240
1241    /**
1242     * Returns a {@link Collection} view of the values contained in this JSONObject.
1243     * The collection is backed by the JSONObject, so changes to the map are
1244     * reflected in the collection, and vice-versa.
1245     *
1246     * @return a collection view of the values contained in this JSONObject
1247     * @since 5.7
1248     */
1249    @Override
1250    public Collection<Object> values()
1251    {
1252        return nameValuePairs.values();
1253    }
1254
1255    /**
1256     * Returns a {@link Set} view of the mappings contained in this JSONObject.
1257     * The set is backed by the JSONObject, so changes to the map are
1258     * reflected in the set, and vice-versa.
1259     *
1260     * @return a set view of the mappings contained in this JSONObject
1261     * @since 5.7
1262     */
1263    @Override
1264    public Set<Entry<String, Object>> entrySet()
1265    {
1266        return nameValuePairs.entrySet();
1267    }
1268
1269    /**
1270     * If the specified key is not already associated with a value or is
1271     * associated with null, associates it with the given non-null value.
1272     *
1273     * Otherwise, replaces the associated value with the results of the given
1274     * remapping function, or removes if the result is {@code null}.
1275     *
1276     * @param key
1277     *            key with which the resulting value is to be associated
1278     * @param value
1279     *            the non-null value to be merged with the existing value
1280     *            associated with the key or, if no existing value or a null value
1281     *            is associated with the key, to be associated with the key
1282     * @param remappingFunction
1283     *            the function to recompute a value if present
1284     * @return the new value associated with the specified key, or null if no
1285     *         value is associated with the key
1286     * @throws NullPointerException
1287     *             if the specified key is null or the value or remappingFunction
1288     *             is null
1289     * @since 5.7
1290     */
1291    @Override
1292    public Object merge(String key, Object value,
1293            BiFunction<? super Object, ? super Object, ? extends Object> remappingFunction)
1294    {
1295        // We need to override the merge method due to the default implementation using
1296        // #get(String) to check for the key, which will throw a {@code JSONValueNotFoundException}
1297        // if not found.
1298
1299        Objects.requireNonNull(remappingFunction);
1300        Objects.requireNonNull(value);
1301
1302        Object oldValue = opt(key);
1303        Object newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value);
1304
1305        put(key, newValue);
1306
1307        return newValue;
1308    }
1309
1310}