1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.json;
18
19import java.util.ArrayList;
20import java.util.Collection;
21import java.util.Iterator;
22import java.util.LinkedHashMap;
23import java.util.Map;
24import java.util.Set;
25
26// Note: this class was written without inspecting the non-free org.json sourcecode.
27
28/**
29 * A modifiable set of name/value mappings. Names are unique, non-null strings.
30 * Values may be any mix of {@link JSONObject JSONObjects}, {@link JSONArray
31 * JSONArrays}, Strings, Booleans, Integers, Longs, Doubles or {@link #NULL}.
32 * Values may not be {@code null}, {@link Double#isNaN() NaNs}, {@link
33 * Double#isInfinite() infinities}, or of any type not listed here.
34 *
35 * <p>This class can coerce values to another type when requested.
36 * <ul>
37 *   <li>When the requested type is a boolean, strings will be coerced using a
38 *       case-insensitive comparison to "true" and "false".
39 *   <li>When the requested type is a double, other {@link Number} types will
40 *       be coerced using {@link Number#doubleValue() doubleValue}. Strings
41 *       that can be coerced using {@link Double#valueOf(String)} will be.
42 *   <li>When the requested type is an int, other {@link Number} types will
43 *       be coerced using {@link Number#intValue() intValue}. Strings
44 *       that can be coerced using {@link Double#valueOf(String)} will be,
45 *       and then cast to int.
46 *   <li><a name="lossy">When the requested type is a long, other {@link Number} types will
47 *       be coerced using {@link Number#longValue() longValue}. Strings
48 *       that can be coerced using {@link Double#valueOf(String)} will be,
49 *       and then cast to long. This two-step conversion is lossy for very
50 *       large values. For example, the string "9223372036854775806" yields the
51 *       long 9223372036854775807.</a>
52 *   <li>When the requested type is a String, other non-null values will be
53 *       coerced using {@link String#valueOf(Object)}. Although null cannot be
54 *       coerced, the sentinel value {@link JSONObject#NULL} is coerced to the
55 *       string "null".
56 * </ul>
57 *
58 * <p>This class can look up both mandatory and optional values:
59 * <ul>
60 *   <li>Use <code>get<i>Type</i>()</code> to retrieve a mandatory value. This
61 *       fails with a {@code JSONException} if the requested name has no value
62 *       or if the value cannot be coerced to the requested type.
63 *   <li>Use <code>opt<i>Type</i>()</code> to retrieve an optional value. This
64 *       returns a system- or user-supplied default if the requested name has no
65 *       value or if the value cannot be coerced to the requested type.
66 * </ul>
67 *
68 * <p><strong>Warning:</strong> this class represents null in two incompatible
69 * ways: the standard Java {@code null} reference, and the sentinel value {@link
70 * JSONObject#NULL}. In particular, calling {@code put(name, null)} removes the
71 * named entry from the object but {@code put(name, JSONObject.NULL)} stores an
72 * entry whose value is {@code JSONObject.NULL}.
73 *
74 * <p>Instances of this class are not thread safe. Although this class is
75 * nonfinal, it was not designed for inheritance and should not be subclassed.
76 * In particular, self-use by overrideable methods is not specified. See
77 * <i>Effective Java</i> Item 17, "Design and Document or inheritance or else
78 * prohibit it" for further information.
79 */
80public class JSONObject {
81
82    private static final Double NEGATIVE_ZERO = -0d;
83
84    /**
85     * A sentinel value used to explicitly define a name with no value. Unlike
86     * {@code null}, names with this value:
87     * <ul>
88     *   <li>show up in the {@link #names} array
89     *   <li>show up in the {@link #keys} iterator
90     *   <li>return {@code true} for {@link #has(String)}
91     *   <li>do not throw on {@link #get(String)}
92     *   <li>are included in the encoded JSON string.
93     * </ul>
94     *
95     * <p>This value violates the general contract of {@link Object#equals} by
96     * returning true when compared to {@code null}. Its {@link #toString}
97     * method returns "null".
98     */
99    public static final Object NULL = new Object() {
100        @Override public boolean equals(Object o) {
101            return o == this || o == null; // API specifies this broken equals implementation
102        }
103        @Override public String toString() {
104            return "null";
105        }
106    };
107
108    private final LinkedHashMap<String, Object> nameValuePairs;
109
110    /**
111     * Creates a {@code JSONObject} with no name/value mappings.
112     */
113    public JSONObject() {
114        nameValuePairs = new LinkedHashMap<String, Object>();
115    }
116
117    /**
118     * Creates a new {@code JSONObject} by copying all name/value mappings from
119     * the given map.
120     *
121     * @param copyFrom a map whose keys are of type {@link String} and whose
122     *     values are of supported types.
123     * @throws NullPointerException if any of the map's keys are null.
124     */
125    /* (accept a raw type for API compatibility) */
126    public JSONObject(Map copyFrom) {
127        this();
128        Map<?, ?> contentsTyped = (Map<?, ?>) copyFrom;
129        for (Map.Entry<?, ?> entry : contentsTyped.entrySet()) {
130            /*
131             * Deviate from the original by checking that keys are non-null and
132             * of the proper type. (We still defer validating the values).
133             */
134            String key = (String) entry.getKey();
135            if (key == null) {
136                throw new NullPointerException("key == null");
137            }
138            nameValuePairs.put(key, wrap(entry.getValue()));
139        }
140    }
141
142    /**
143     * Creates a new {@code JSONObject} with name/value mappings from the next
144     * object in the tokener.
145     *
146     * @param readFrom a tokener whose nextValue() method will yield a
147     *     {@code JSONObject}.
148     * @throws JSONException if the parse fails or doesn't yield a
149     *     {@code JSONObject}.
150     */
151    public JSONObject(JSONTokener readFrom) throws JSONException {
152        /*
153         * Getting the parser to populate this could get tricky. Instead, just
154         * parse to temporary JSONObject and then steal the data from that.
155         */
156        Object object = readFrom.nextValue();
157        if (object instanceof JSONObject) {
158            this.nameValuePairs = ((JSONObject) object).nameValuePairs;
159        } else {
160            throw JSON.typeMismatch(object, "JSONObject");
161        }
162    }
163
164    /**
165     * Creates a new {@code JSONObject} with name/value mappings from the JSON
166     * string.
167     *
168     * @param json a JSON-encoded string containing an object.
169     * @throws JSONException if the parse fails or doesn't yield a {@code
170     *     JSONObject}.
171     */
172    public JSONObject(String json) throws JSONException {
173        this(new JSONTokener(json));
174    }
175
176    /**
177     * Creates a new {@code JSONObject} by copying mappings for the listed names
178     * from the given object. Names that aren't present in {@code copyFrom} will
179     * be skipped.
180     */
181    public JSONObject(JSONObject copyFrom, String[] names) throws JSONException {
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     * Returns the number of name/value mappings in this object.
193     */
194    public int length() {
195        return nameValuePairs.size();
196    }
197
198    /**
199     * Maps {@code name} to {@code value}, clobbering any existing name/value
200     * mapping with the same name.
201     *
202     * @return this object.
203     */
204    public JSONObject put(String name, boolean value) throws JSONException {
205        nameValuePairs.put(checkName(name), value);
206        return this;
207    }
208
209    /**
210     * Maps {@code name} to {@code value}, clobbering any existing name/value
211     * mapping with the same name.
212     *
213     * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
214     *     {@link Double#isInfinite() infinities}.
215     * @return this object.
216     */
217    public JSONObject put(String name, double value) throws JSONException {
218        nameValuePairs.put(checkName(name), JSON.checkDouble(value));
219        return this;
220    }
221
222    /**
223     * Maps {@code name} to {@code value}, clobbering any existing name/value
224     * mapping with the same name.
225     *
226     * @return this object.
227     */
228    public JSONObject put(String name, int value) throws JSONException {
229        nameValuePairs.put(checkName(name), value);
230        return this;
231    }
232
233    /**
234     * Maps {@code name} to {@code value}, clobbering any existing name/value
235     * mapping with the same name.
236     *
237     * @return this object.
238     */
239    public JSONObject put(String name, long value) throws JSONException {
240        nameValuePairs.put(checkName(name), value);
241        return this;
242    }
243
244    /**
245     * Maps {@code name} to {@code value}, clobbering any existing name/value
246     * mapping with the same name. If the value is {@code null}, any existing
247     * mapping for {@code name} is removed.
248     *
249     * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
250     *     Integer, Long, Double, {@link #NULL}, or {@code null}. May not be
251     *     {@link Double#isNaN() NaNs} or {@link Double#isInfinite()
252     *     infinities}.
253     * @return this object.
254     */
255    public JSONObject put(String name, Object value) throws JSONException {
256        if (value == null) {
257            nameValuePairs.remove(name);
258            return this;
259        }
260        if (value instanceof Number) {
261            // deviate from the original by checking all Numbers, not just floats & doubles
262            JSON.checkDouble(((Number) value).doubleValue());
263        }
264        nameValuePairs.put(checkName(name), value);
265        return this;
266    }
267
268    /**
269     * Equivalent to {@code put(name, value)} when both parameters are non-null;
270     * does nothing otherwise.
271     */
272    public JSONObject putOpt(String name, Object value) throws JSONException {
273        if (name == null || value == null) {
274            return this;
275        }
276        return put(name, value);
277    }
278
279    /**
280     * Appends {@code value} to the array already mapped to {@code name}. If
281     * this object has no mapping for {@code name}, this inserts a new mapping.
282     * If the mapping exists but its value is not an array, the existing
283     * and new values are inserted in order into a new array which is itself
284     * mapped to {@code name}. In aggregate, this allows values to be added to a
285     * mapping one at a time.
286     *
287     * <p> Note that {@code append(String, Object)} provides better semantics.
288     * In particular, the mapping for {@code name} will <b>always</b> be a
289     * {@link JSONArray}. Using {@code accumulate} will result in either a
290     * {@link JSONArray} or a mapping whose type is the type of {@code value}
291     * depending on the number of calls to it.
292     *
293     * @param value a {@link JSONObject}, {@link JSONArray}, String, Boolean,
294     *     Integer, Long, Double, {@link #NULL} or null. May not be {@link
295     *     Double#isNaN() NaNs} or {@link Double#isInfinite() infinities}.
296     */
297    // TODO: Change {@code append) to {@link #append} when append is
298    // unhidden.
299    public JSONObject accumulate(String name, Object value) throws JSONException {
300        Object current = nameValuePairs.get(checkName(name));
301        if (current == null) {
302            return put(name, value);
303        }
304
305        if (current instanceof JSONArray) {
306            JSONArray array = (JSONArray) current;
307            array.checkedPut(value);
308        } else {
309            JSONArray array = new JSONArray();
310            array.checkedPut(current);
311            array.checkedPut(value);
312            nameValuePairs.put(name, array);
313        }
314        return this;
315    }
316
317    /**
318     * Appends values to the array mapped to {@code name}. A new {@link JSONArray}
319     * mapping for {@code name} will be inserted if no mapping exists. If the existing
320     * mapping for {@code name} is not a {@link JSONArray}, a {@link JSONException}
321     * will be thrown.
322     *
323     * @throws JSONException if {@code name} is {@code null} or if the mapping for
324     *         {@code name} is non-null and is not a {@link JSONArray}.
325     *
326     * @hide
327     */
328    public JSONObject append(String name, Object value) throws JSONException {
329        Object current = nameValuePairs.get(checkName(name));
330
331        final JSONArray array;
332        if (current instanceof JSONArray) {
333            array = (JSONArray) current;
334        } else if (current == null) {
335            JSONArray newArray = new JSONArray();
336            nameValuePairs.put(name, newArray);
337            array = newArray;
338        } else {
339            throw new JSONException("Key " + name + " is not a JSONArray");
340        }
341
342        array.checkedPut(value);
343
344        return this;
345    }
346
347    String checkName(String name) throws JSONException {
348        if (name == null) {
349            throw new JSONException("Names must be non-null");
350        }
351        return name;
352    }
353
354    /**
355     * Removes the named mapping if it exists; does nothing otherwise.
356     *
357     * @return the value previously mapped by {@code name}, or null if there was
358     *     no such mapping.
359     */
360    public Object remove(String name) {
361        return nameValuePairs.remove(name);
362    }
363
364    /**
365     * Returns true if this object has no mapping for {@code name} or if it has
366     * a mapping whose value is {@link #NULL}.
367     */
368    public boolean isNull(String name) {
369        Object value = nameValuePairs.get(name);
370        return value == null || value == NULL;
371    }
372
373    /**
374     * Returns true if this object has a mapping for {@code name}. The mapping
375     * may be {@link #NULL}.
376     */
377    public boolean has(String name) {
378        return nameValuePairs.containsKey(name);
379    }
380
381    /**
382     * Returns the value mapped by {@code name}, or throws if no such mapping exists.
383     *
384     * @throws JSONException if no such mapping exists.
385     */
386    public Object get(String name) throws JSONException {
387        Object result = nameValuePairs.get(name);
388        if (result == null) {
389            throw new JSONException("No value for " + name);
390        }
391        return result;
392    }
393
394    /**
395     * Returns the value mapped by {@code name}, or null if no such mapping
396     * exists.
397     */
398    public Object opt(String name) {
399        return nameValuePairs.get(name);
400    }
401
402    /**
403     * Returns the value mapped by {@code name} if it exists and is a boolean or
404     * can be coerced to a boolean, or throws otherwise.
405     *
406     * @throws JSONException if the mapping doesn't exist or cannot be coerced
407     *     to a boolean.
408     */
409    public boolean getBoolean(String name) throws JSONException {
410        Object object = get(name);
411        Boolean result = JSON.toBoolean(object);
412        if (result == null) {
413            throw JSON.typeMismatch(name, object, "boolean");
414        }
415        return result;
416    }
417
418    /**
419     * Returns the value mapped by {@code name} if it exists and is a boolean or
420     * can be coerced to a boolean, or false otherwise.
421     */
422    public boolean optBoolean(String name) {
423        return optBoolean(name, false);
424    }
425
426    /**
427     * Returns the value mapped by {@code name} if it exists and is a boolean or
428     * can be coerced to a boolean, or {@code fallback} otherwise.
429     */
430    public boolean optBoolean(String name, boolean fallback) {
431        Object object = opt(name);
432        Boolean result = JSON.toBoolean(object);
433        return result != null ? result : fallback;
434    }
435
436    /**
437     * Returns the value mapped by {@code name} if it exists and is a double or
438     * can be coerced to a double, or throws otherwise.
439     *
440     * @throws JSONException if the mapping doesn't exist or cannot be coerced
441     *     to a double.
442     */
443    public double getDouble(String name) throws JSONException {
444        Object object = get(name);
445        Double result = JSON.toDouble(object);
446        if (result == null) {
447            throw JSON.typeMismatch(name, object, "double");
448        }
449        return result;
450    }
451
452    /**
453     * Returns the value mapped by {@code name} if it exists and is a double or
454     * can be coerced to a double, or {@code NaN} otherwise.
455     */
456    public double optDouble(String name) {
457        return optDouble(name, Double.NaN);
458    }
459
460    /**
461     * Returns the value mapped by {@code name} if it exists and is a double or
462     * can be coerced to a double, or {@code fallback} otherwise.
463     */
464    public double optDouble(String name, double fallback) {
465        Object object = opt(name);
466        Double result = JSON.toDouble(object);
467        return result != null ? result : fallback;
468    }
469
470    /**
471     * Returns the value mapped by {@code name} if it exists and is an int or
472     * can be coerced to an int, or throws otherwise.
473     *
474     * @throws JSONException if the mapping doesn't exist or cannot be coerced
475     *     to an int.
476     */
477    public int getInt(String name) throws JSONException {
478        Object object = get(name);
479        Integer result = JSON.toInteger(object);
480        if (result == null) {
481            throw JSON.typeMismatch(name, object, "int");
482        }
483        return result;
484    }
485
486    /**
487     * Returns the value mapped by {@code name} if it exists and is an int or
488     * can be coerced to an int, or 0 otherwise.
489     */
490    public int optInt(String name) {
491        return optInt(name, 0);
492    }
493
494    /**
495     * Returns the value mapped by {@code name} if it exists and is an int or
496     * can be coerced to an int, or {@code fallback} otherwise.
497     */
498    public int optInt(String name, int fallback) {
499        Object object = opt(name);
500        Integer result = JSON.toInteger(object);
501        return result != null ? result : fallback;
502    }
503
504    /**
505     * Returns the value mapped by {@code name} if it exists and is a long or
506     * can be coerced to a long, or throws otherwise.
507     * Note that JSON represents numbers as doubles,
508     * so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via JSON.
509     *
510     * @throws JSONException if the mapping doesn't exist or cannot be coerced
511     *     to a long.
512     */
513    public long getLong(String name) throws JSONException {
514        Object object = get(name);
515        Long result = JSON.toLong(object);
516        if (result == null) {
517            throw JSON.typeMismatch(name, object, "long");
518        }
519        return result;
520    }
521
522    /**
523     * Returns the value mapped by {@code name} if it exists and is a long or
524     * can be coerced to a long, or 0 otherwise. Note that JSON represents numbers as doubles,
525     * so this is <a href="#lossy">lossy</a>; use strings to transfer numbers via JSON.
526     */
527    public long optLong(String name) {
528        return optLong(name, 0L);
529    }
530
531    /**
532     * Returns the value mapped by {@code name} if it exists and is a long or
533     * can be coerced to a long, or {@code fallback} otherwise. Note that JSON represents
534     * numbers as doubles, so this is <a href="#lossy">lossy</a>; use strings to transfer
535     * numbers via JSON.
536     */
537    public long optLong(String name, long fallback) {
538        Object object = opt(name);
539        Long result = JSON.toLong(object);
540        return result != null ? result : fallback;
541    }
542
543    /**
544     * Returns the value mapped by {@code name} if it exists, coercing it if
545     * necessary, or throws if no such mapping exists.
546     *
547     * @throws JSONException if no such mapping exists.
548     */
549    public String getString(String name) throws JSONException {
550        Object object = get(name);
551        String result = JSON.toString(object);
552        if (result == null) {
553            throw JSON.typeMismatch(name, object, "String");
554        }
555        return result;
556    }
557
558    /**
559     * Returns the value mapped by {@code name} if it exists, coercing it if
560     * necessary, or the empty string if no such mapping exists.
561     */
562    public String optString(String name) {
563        return optString(name, "");
564    }
565
566    /**
567     * Returns the value mapped by {@code name} if it exists, coercing it if
568     * necessary, or {@code fallback} if no such mapping exists.
569     */
570    public String optString(String name, String fallback) {
571        Object object = opt(name);
572        String result = JSON.toString(object);
573        return result != null ? result : fallback;
574    }
575
576    /**
577     * Returns the value mapped by {@code name} if it exists and is a {@code
578     * JSONArray}, or throws otherwise.
579     *
580     * @throws JSONException if the mapping doesn't exist or is not a {@code
581     *     JSONArray}.
582     */
583    public JSONArray getJSONArray(String name) throws JSONException {
584        Object object = get(name);
585        if (object instanceof JSONArray) {
586            return (JSONArray) object;
587        } else {
588            throw JSON.typeMismatch(name, object, "JSONArray");
589        }
590    }
591
592    /**
593     * Returns the value mapped by {@code name} if it exists and is a {@code
594     * JSONArray}, or null otherwise.
595     */
596    public JSONArray optJSONArray(String name) {
597        Object object = opt(name);
598        return object instanceof JSONArray ? (JSONArray) object : null;
599    }
600
601    /**
602     * Returns the value mapped by {@code name} if it exists and is a {@code
603     * JSONObject}, or throws otherwise.
604     *
605     * @throws JSONException if the mapping doesn't exist or is not a {@code
606     *     JSONObject}.
607     */
608    public JSONObject getJSONObject(String name) throws JSONException {
609        Object object = get(name);
610        if (object instanceof JSONObject) {
611            return (JSONObject) object;
612        } else {
613            throw JSON.typeMismatch(name, object, "JSONObject");
614        }
615    }
616
617    /**
618     * Returns the value mapped by {@code name} if it exists and is a {@code
619     * JSONObject}, or null otherwise.
620     */
621    public JSONObject optJSONObject(String name) {
622        Object object = opt(name);
623        return object instanceof JSONObject ? (JSONObject) object : null;
624    }
625
626    /**
627     * Returns an array with the values corresponding to {@code names}. The
628     * array contains null for names that aren't mapped. This method returns
629     * null if {@code names} is either null or empty.
630     */
631    public JSONArray toJSONArray(JSONArray names) throws JSONException {
632        JSONArray result = new JSONArray();
633        if (names == null) {
634            return null;
635        }
636        int length = names.length();
637        if (length == 0) {
638            return null;
639        }
640        for (int i = 0; i < length; i++) {
641            String name = JSON.toString(names.opt(i));
642            result.put(opt(name));
643        }
644        return result;
645    }
646
647    /**
648     * Returns an iterator of the {@code String} names in this object. The
649     * returned iterator supports {@link Iterator#remove() remove}, which will
650     * remove the corresponding mapping from this object. If this object is
651     * modified after the iterator is returned, the iterator's behavior is
652     * undefined. The order of the keys is undefined.
653     */
654    public Iterator<String> keys() {
655        return nameValuePairs.keySet().iterator();
656    }
657
658    /**
659     * Returns the set of {@code String} names in this object. The returned set
660     * is a view of the keys in this object. {@link Set#remove(Object)} will remove
661     * the corresponding mapping from this object and set iterator behaviour
662     * is undefined if this object is modified after it is returned.
663     *
664     * See {@link #keys()}.
665     *
666     * @hide.
667     */
668    public Set<String> keySet() {
669        return nameValuePairs.keySet();
670    }
671
672    /**
673     * Returns an array containing the string names in this object. This method
674     * returns null if this object contains no mappings.
675     */
676    public JSONArray names() {
677        return nameValuePairs.isEmpty()
678                ? null
679                : new JSONArray(new ArrayList<String>(nameValuePairs.keySet()));
680    }
681
682    /**
683     * Encodes this object as a compact JSON string, such as:
684     * <pre>{"query":"Pizza","locations":[94043,90210]}</pre>
685     */
686    @Override public String toString() {
687        try {
688            JSONStringer stringer = new JSONStringer();
689            writeTo(stringer);
690            return stringer.toString();
691        } catch (JSONException e) {
692            return null;
693        }
694    }
695
696    /**
697     * Encodes this object as a human readable JSON string for debugging, such
698     * as:
699     * <pre>
700     * {
701     *     "query": "Pizza",
702     *     "locations": [
703     *         94043,
704     *         90210
705     *     ]
706     * }</pre>
707     *
708     * @param indentSpaces the number of spaces to indent for each level of
709     *     nesting.
710     */
711    public String toString(int indentSpaces) throws JSONException {
712        JSONStringer stringer = new JSONStringer(indentSpaces);
713        writeTo(stringer);
714        return stringer.toString();
715    }
716
717    void writeTo(JSONStringer stringer) throws JSONException {
718        stringer.object();
719        for (Map.Entry<String, Object> entry : nameValuePairs.entrySet()) {
720            stringer.key(entry.getKey()).value(entry.getValue());
721        }
722        stringer.endObject();
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     */
731    public static String numberToString(Number number) throws JSONException {
732        if (number == null) {
733            throw new JSONException("Number must be non-null");
734        }
735
736        double doubleValue = number.doubleValue();
737        JSON.checkDouble(doubleValue);
738
739        // the original returns "-0" instead of "-0.0" for negative zero
740        if (number.equals(NEGATIVE_ZERO)) {
741            return "-0";
742        }
743
744        long longValue = number.longValue();
745        if (doubleValue == (double) longValue) {
746            return Long.toString(longValue);
747        }
748
749        return number.toString();
750    }
751
752    /**
753     * Encodes {@code data} as a JSON string. This applies quotes and any
754     * necessary character escaping.
755     *
756     * @param data the string to encode. Null will be interpreted as an empty
757     *     string.
758     */
759    public static String quote(String data) {
760        if (data == null) {
761            return "\"\"";
762        }
763        try {
764            JSONStringer stringer = new JSONStringer();
765            stringer.open(JSONStringer.Scope.NULL, "");
766            stringer.value(data);
767            stringer.close(JSONStringer.Scope.NULL, JSONStringer.Scope.NULL, "");
768            return stringer.toString();
769        } catch (JSONException e) {
770            throw new AssertionError();
771        }
772    }
773
774    /**
775     * Wraps the given object if necessary.
776     *
777     * <p>If the object is null or , returns {@link #NULL}.
778     * If the object is a {@code JSONArray} or {@code JSONObject}, no wrapping is necessary.
779     * If the object is {@code NULL}, no wrapping is necessary.
780     * If the object is an array or {@code Collection}, returns an equivalent {@code JSONArray}.
781     * If the object is a {@code Map}, returns an equivalent {@code JSONObject}.
782     * If the object is a primitive wrapper type or {@code String}, returns the object.
783     * Otherwise if the object is from a {@code java} package, returns the result of {@code toString}.
784     * If wrapping fails, returns null.
785     */
786    public static Object wrap(Object o) {
787        if (o == null) {
788            return NULL;
789        }
790        if (o instanceof JSONArray || o instanceof JSONObject) {
791            return o;
792        }
793        if (o.equals(NULL)) {
794            return o;
795        }
796        try {
797            if (o instanceof Collection) {
798                return new JSONArray((Collection) o);
799            } else if (o.getClass().isArray()) {
800                return new JSONArray(o);
801            }
802            if (o instanceof Map) {
803                return new JSONObject((Map) o);
804            }
805            if (o instanceof Boolean ||
806                o instanceof Byte ||
807                o instanceof Character ||
808                o instanceof Double ||
809                o instanceof Float ||
810                o instanceof Integer ||
811                o instanceof Long ||
812                o instanceof Short ||
813                o instanceof String) {
814                return o;
815            }
816            if (o.getClass().getPackage().getName().startsWith("java.")) {
817                return o.toString();
818            }
819        } catch (Exception ignored) {
820        }
821        return null;
822    }
823}
824