]> WPIA git - gigi.git/blob - lib/json/org/json/JSONObject.java
aa227ff0969b93413eaf89bc44d66724c26f59be
[gigi.git] / lib / json / org / json / JSONObject.java
1 package org.json;
2
3 /*
4  Copyright (c) 2002 JSON.org
5
6  Permission is hereby granted, free of charge, to any person obtaining a copy
7  of this software and associated documentation files (the "Software"), to deal
8  in the Software without restriction, including without limitation the rights
9  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  copies of the Software, and to permit persons to whom the Software is
11  furnished to do so, subject to the following conditions:
12
13  The above copyright notice and this permission notice shall be included in all
14  copies or substantial portions of the Software.
15
16  The Software shall be used for Good, not Evil.
17
18  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  SOFTWARE.
25  */
26
27 import java.io.IOException;
28 import java.io.StringWriter;
29 import java.io.Writer;
30 import java.lang.reflect.Field;
31 import java.lang.reflect.Method;
32 import java.lang.reflect.Modifier;
33 import java.math.BigDecimal;
34 import java.math.BigInteger;
35 import java.util.Collection;
36 import java.util.Enumeration;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.Locale;
40 import java.util.Map;
41 import java.util.Map.Entry;
42 import java.util.ResourceBundle;
43 import java.util.Set;
44
45 /**
46  * A JSONObject is an unordered collection of name/value pairs. Its external
47  * form is a string wrapped in curly braces with colons between the names and
48  * values, and commas between the values and names. The internal form is an
49  * object having <code>get</code> and <code>opt</code> methods for accessing
50  * the values by name, and <code>put</code> methods for adding or replacing
51  * values by name. The values can be any of these types: <code>Boolean</code>,
52  * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
53  * <code>String</code>, or the <code>JSONObject.NULL</code> object. A
54  * JSONObject constructor can be used to convert an external form JSON text
55  * into an internal form whose values can be retrieved with the
56  * <code>get</code> and <code>opt</code> methods, or to convert values into a
57  * JSON text using the <code>put</code> and <code>toString</code> methods. A
58  * <code>get</code> method returns a value if one can be found, and throws an
59  * exception if one cannot be found. An <code>opt</code> method returns a
60  * default value instead of throwing an exception, and so is useful for
61  * obtaining optional values.
62  * <p>
63  * The generic <code>get()</code> and <code>opt()</code> methods return an
64  * object, which you can cast or query for type. There are also typed
65  * <code>get</code> and <code>opt</code> methods that do type checking and type
66  * coercion for you. The opt methods differ from the get methods in that they
67  * do not throw. Instead, they return a specified value, such as null.
68  * <p>
69  * The <code>put</code> methods add or replace values in an object. For
70  * example,
71  *
72  * <pre>
73  * myString = new JSONObject()
74  *         .put(&quot;JSON&quot;, &quot;Hello, World!&quot;).toString();
75  * </pre>
76  *
77  * produces the string <code>{"JSON": "Hello, World"}</code>.
78  * <p>
79  * The texts produced by the <code>toString</code> methods strictly conform to
80  * the JSON syntax rules. The constructors are more forgiving in the texts they
81  * will accept:
82  * <ul>
83  * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
84  * before the closing brace.</li>
85  * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
86  * quote)</small>.</li>
87  * <li>Strings do not need to be quoted at all if they do not begin with a
88  * quote or single quote, and if they do not contain leading or trailing
89  * spaces, and if they do not contain any of these characters:
90  * <code>{ } [ ] / \ : , #</code> and if they do not look like numbers and
91  * if they are not the reserved words <code>true</code>, <code>false</code>,
92  * or <code>null</code>.</li>
93  * </ul>
94  *
95  * @author JSON.org
96  * @version 2016-05-20
97  */
98 public class JSONObject {
99     /**
100      * JSONObject.NULL is equivalent to the value that JavaScript calls null,
101      * whilst Java's null is equivalent to the value that JavaScript calls
102      * undefined.
103      */
104     private static final class Null {
105
106         /**
107          * There is only intended to be a single instance of the NULL object,
108          * so the clone method returns itself.
109          *
110          * @return NULL.
111          */
112         @Override
113         protected final Object clone() {
114             return this;
115         }
116
117         /**
118          * A Null object is equal to the null value and to itself.
119          *
120          * @param object
121          *            An object to test for nullness.
122          * @return true if the object parameter is the JSONObject.NULL object or
123          *         null.
124          */
125         @Override
126         public boolean equals(Object object) {
127             return object == null || object == this;
128         }
129
130         /**
131          * Get the "null" string value.
132          *
133          * @return The string "null".
134          */
135         public String toString() {
136             return "null";
137         }
138     }
139
140     /**
141      * The map where the JSONObject's properties are kept.
142      */
143     private final Map<String, Object> map;
144
145     /**
146      * It is sometimes more convenient and less ambiguous to have a
147      * <code>NULL</code> object than to use Java's <code>null</code> value.
148      * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
149      * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
150      */
151     public static final Object NULL = new Null();
152
153     /**
154      * Construct an empty JSONObject.
155      */
156     public JSONObject() {
157         this.map = new HashMap<String, Object>();
158     }
159
160     /**
161      * Construct a JSONObject from a subset of another JSONObject. An array of
162      * strings is used to identify the keys that should be copied. Missing keys
163      * are ignored.
164      *
165      * @param jo
166      *            A JSONObject.
167      * @param names
168      *            An array of strings.
169      */
170     public JSONObject(JSONObject jo, String[] names) {
171         this();
172         for (int i = 0; i < names.length; i += 1) {
173             try {
174                 this.putOnce(names[i], jo.opt(names[i]));
175             } catch (Exception ignore) {
176             }
177         }
178     }
179
180     /**
181      * Construct a JSONObject from a JSONTokener.
182      *
183      * @param x
184      *            A JSONTokener object containing the source string.
185      * @throws JSONException
186      *             If there is a syntax error in the source string or a
187      *             duplicated key.
188      */
189     public JSONObject(JSONTokener x) throws JSONException {
190         this();
191         char c;
192         String key;
193
194         if (x.nextClean() != '{') {
195             throw x.syntaxError("A JSONObject text must begin with '{'");
196         }
197         for (;;) {
198             c = x.nextClean();
199             switch (c) {
200             case 0:
201                 throw x.syntaxError("A JSONObject text must end with '}'");
202             case '}':
203                 return;
204             default:
205                 x.back();
206                 key = x.nextValue().toString();
207             }
208
209 // The key is followed by ':'.
210
211             c = x.nextClean();
212             if (c != ':') {
213                 throw x.syntaxError("Expected a ':' after a key");
214             }
215             this.putOnce(key, x.nextValue());
216
217 // Pairs are separated by ','.
218
219             switch (x.nextClean()) {
220             case ';':
221             case ',':
222                 if (x.nextClean() == '}') {
223                     return;
224                 }
225                 x.back();
226                 break;
227             case '}':
228                 return;
229             default:
230                 throw x.syntaxError("Expected a ',' or '}'");
231             }
232         }
233     }
234
235     /**
236      * Construct a JSONObject from a Map.
237      *
238      * @param map
239      *            A map object that can be used to initialize the contents of
240      *            the JSONObject.
241      */
242     public JSONObject(Map<?, ?> map) {
243         this.map = new HashMap<String, Object>();
244         if (map != null) {
245                 for (final Entry<?, ?> e : map.entrySet()) {
246                 final Object value = e.getValue();
247                 if (value != null) {
248                     this.map.put(String.valueOf(e.getKey()), wrap(value));
249                 }
250             }
251         }
252     }
253
254     /**
255      * Construct a JSONObject from an Object using bean getters. It reflects on
256      * all of the public methods of the object. For each of the methods with no
257      * parameters and a name starting with <code>"get"</code> or
258      * <code>"is"</code> followed by an uppercase letter, the method is invoked,
259      * and a key and the value returned from the getter method are put into the
260      * new JSONObject.
261      *
262      * The key is formed by removing the <code>"get"</code> or <code>"is"</code>
263      * prefix. If the second remaining character is not upper case, then the
264      * first character is converted to lower case.
265      *
266      * For example, if an object has a method named <code>"getName"</code>, and
267      * if the result of calling <code>object.getName()</code> is
268      * <code>"Larry Fine"</code>, then the JSONObject will contain
269      * <code>"name": "Larry Fine"</code>.
270      *
271      * @param bean
272      *            An object that has getter methods that should be used to make
273      *            a JSONObject.
274      */
275     public JSONObject(Object bean) {
276         this();
277         this.populateMap(bean);
278     }
279
280     /**
281      * Construct a JSONObject from an Object, using reflection to find the
282      * public members. The resulting JSONObject's keys will be the strings from
283      * the names array, and the values will be the field values associated with
284      * those keys in the object. If a key is not found or not visible, then it
285      * will not be copied into the new JSONObject.
286      *
287      * @param object
288      *            An object that has fields that should be used to make a
289      *            JSONObject.
290      * @param names
291      *            An array of strings, the names of the fields to be obtained
292      *            from the object.
293      */
294     public JSONObject(Object object, String names[]) {
295         this();
296         Class<?> c = object.getClass();
297         for (int i = 0; i < names.length; i += 1) {
298             String name = names[i];
299             try {
300                 this.putOpt(name, c.getField(name).get(object));
301             } catch (Exception ignore) {
302             }
303         }
304     }
305
306     /**
307      * Construct a JSONObject from a source JSON text string. This is the most
308      * commonly used JSONObject constructor.
309      *
310      * @param source
311      *            A string beginning with <code>{</code>&nbsp;<small>(left
312      *            brace)</small> and ending with <code>}</code>
313      *            &nbsp;<small>(right brace)</small>.
314      * @exception JSONException
315      *                If there is a syntax error in the source string or a
316      *                duplicated key.
317      */
318     public JSONObject(String source) throws JSONException {
319         this(new JSONTokener(source));
320     }
321
322     /**
323      * Construct a JSONObject from a ResourceBundle.
324      *
325      * @param baseName
326      *            The ResourceBundle base name.
327      * @param locale
328      *            The Locale to load the ResourceBundle for.
329      * @throws JSONException
330      *             If any JSONExceptions are detected.
331      */
332     public JSONObject(String baseName, Locale locale) throws JSONException {
333         this();
334         ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale,
335                 Thread.currentThread().getContextClassLoader());
336
337 // Iterate through the keys in the bundle.
338
339         Enumeration<String> keys = bundle.getKeys();
340         while (keys.hasMoreElements()) {
341             Object key = keys.nextElement();
342             if (key != null) {
343
344 // Go through the path, ensuring that there is a nested JSONObject for each
345 // segment except the last. Add the value using the last segment's name into
346 // the deepest nested JSONObject.
347
348                 String[] path = ((String) key).split("\\.");
349                 int last = path.length - 1;
350                 JSONObject target = this;
351                 for (int i = 0; i < last; i += 1) {
352                     String segment = path[i];
353                     JSONObject nextTarget = target.optJSONObject(segment);
354                     if (nextTarget == null) {
355                         nextTarget = new JSONObject();
356                         target.put(segment, nextTarget);
357                     }
358                     target = nextTarget;
359                 }
360                 target.put(path[last], bundle.getString((String) key));
361             }
362         }
363     }
364
365     /**
366      * Accumulate values under a key. It is similar to the put method except
367      * that if there is already an object stored under the key then a JSONArray
368      * is stored under the key to hold all of the accumulated values. If there
369      * is already a JSONArray, then the new value is appended to it. In
370      * contrast, the put method replaces the previous value.
371      *
372      * If only one value is accumulated that is not a JSONArray, then the result
373      * will be the same as using put. But if multiple values are accumulated,
374      * then the result will be like append.
375      *
376      * @param key
377      *            A key string.
378      * @param value
379      *            An object to be accumulated under the key.
380      * @return this.
381      * @throws JSONException
382      *             If the value is an invalid number or if the key is null.
383      */
384     public JSONObject accumulate(String key, Object value) throws JSONException {
385         testValidity(value);
386         Object object = this.opt(key);
387         if (object == null) {
388             this.put(key,
389                     value instanceof JSONArray ? new JSONArray().put(value)
390                             : value);
391         } else if (object instanceof JSONArray) {
392             ((JSONArray) object).put(value);
393         } else {
394             this.put(key, new JSONArray().put(object).put(value));
395         }
396         return this;
397     }
398
399     /**
400      * Append values to the array under a key. If the key does not exist in the
401      * JSONObject, then the key is put in the JSONObject with its value being a
402      * JSONArray containing the value parameter. If the key was already
403      * associated with a JSONArray, then the value parameter is appended to it.
404      *
405      * @param key
406      *            A key string.
407      * @param value
408      *            An object to be accumulated under the key.
409      * @return this.
410      * @throws JSONException
411      *             If the key is null or if the current value associated with
412      *             the key is not a JSONArray.
413      */
414     public JSONObject append(String key, Object value) throws JSONException {
415         testValidity(value);
416         Object object = this.opt(key);
417         if (object == null) {
418             this.put(key, new JSONArray().put(value));
419         } else if (object instanceof JSONArray) {
420             this.put(key, ((JSONArray) object).put(value));
421         } else {
422             throw new JSONException("JSONObject[" + key
423                     + "] is not a JSONArray.");
424         }
425         return this;
426     }
427
428     /**
429      * Produce a string from a double. The string "null" will be returned if the
430      * number is not finite.
431      *
432      * @param d
433      *            A double.
434      * @return A String.
435      */
436     public static String doubleToString(double d) {
437         if (Double.isInfinite(d) || Double.isNaN(d)) {
438             return "null";
439         }
440
441 // Shave off trailing zeros and decimal point, if possible.
442
443         String string = Double.toString(d);
444         if (string.indexOf('.') > 0 && string.indexOf('e') < 0
445                 && string.indexOf('E') < 0) {
446             while (string.endsWith("0")) {
447                 string = string.substring(0, string.length() - 1);
448             }
449             if (string.endsWith(".")) {
450                 string = string.substring(0, string.length() - 1);
451             }
452         }
453         return string;
454     }
455
456     /**
457      * Get the value object associated with a key.
458      *
459      * @param key
460      *            A key string.
461      * @return The object associated with the key.
462      * @throws JSONException
463      *             if the key is not found.
464      */
465     public Object get(String key) throws JSONException {
466         if (key == null) {
467             throw new JSONException("Null key.");
468         }
469         Object object = this.opt(key);
470         if (object == null) {
471             throw new JSONException("JSONObject[" + quote(key) + "] not found.");
472         }
473         return object;
474     }
475
476     /**
477     * Get the enum value associated with a key.
478     * 
479     * @param clazz
480     *           The type of enum to retrieve.
481     * @param key
482     *           A key string.
483     * @return The enum value associated with the key
484     * @throws JSONException
485     *             if the key is not found or if the value cannot be converted
486     *             to an enum.
487     */
488     public <E extends Enum<E>> E getEnum(Class<E> clazz, String key) throws JSONException {
489         E val = optEnum(clazz, key);
490         if(val==null) {
491             // JSONException should really take a throwable argument.
492             // If it did, I would re-implement this with the Enum.valueOf
493             // method and place any thrown exception in the JSONException
494             throw new JSONException("JSONObject[" + quote(key)
495                     + "] is not an enum of type " + quote(clazz.getSimpleName())
496                     + ".");
497         }
498         return val;
499     }
500
501     /**
502      * Get the boolean value associated with a key.
503      *
504      * @param key
505      *            A key string.
506      * @return The truth.
507      * @throws JSONException
508      *             if the value is not a Boolean or the String "true" or
509      *             "false".
510      */
511     public boolean getBoolean(String key) throws JSONException {
512         Object object = this.get(key);
513         if (object.equals(Boolean.FALSE)
514                 || (object instanceof String && ((String) object)
515                         .equalsIgnoreCase("false"))) {
516             return false;
517         } else if (object.equals(Boolean.TRUE)
518                 || (object instanceof String && ((String) object)
519                         .equalsIgnoreCase("true"))) {
520             return true;
521         }
522         throw new JSONException("JSONObject[" + quote(key)
523                 + "] is not a Boolean.");
524     }
525
526     /**
527      * Get the BigInteger value associated with a key.
528      *
529      * @param key
530      *            A key string.
531      * @return The numeric value.
532      * @throws JSONException
533      *             if the key is not found or if the value cannot 
534      *             be converted to BigInteger.
535      */
536     public BigInteger getBigInteger(String key) throws JSONException {
537         Object object = this.get(key);
538         try {
539             return new BigInteger(object.toString());
540         } catch (Exception e) {
541             throw new JSONException("JSONObject[" + quote(key)
542                     + "] could not be converted to BigInteger.");
543         }
544     }
545
546     /**
547      * Get the BigDecimal value associated with a key.
548      *
549      * @param key
550      *            A key string.
551      * @return The numeric value.
552      * @throws JSONException
553      *             if the key is not found or if the value
554      *             cannot be converted to BigDecimal.
555      */
556     public BigDecimal getBigDecimal(String key) throws JSONException {
557         Object object = this.get(key);
558         try {
559             return new BigDecimal(object.toString());
560         } catch (Exception e) {
561             throw new JSONException("JSONObject[" + quote(key)
562                     + "] could not be converted to BigDecimal.");
563         }
564     }
565
566     /**
567      * Get the double value associated with a key.
568      *
569      * @param key
570      *            A key string.
571      * @return The numeric value.
572      * @throws JSONException
573      *             if the key is not found or if the value is not a Number
574      *             object and cannot be converted to a number.
575      */
576     public double getDouble(String key) throws JSONException {
577         Object object = this.get(key);
578         try {
579             return object instanceof Number ? ((Number) object).doubleValue()
580                     : Double.parseDouble((String) object);
581         } catch (Exception e) {
582             throw new JSONException("JSONObject[" + quote(key)
583                     + "] is not a number.");
584         }
585     }
586
587     /**
588      * Get the int value associated with a key.
589      *
590      * @param key
591      *            A key string.
592      * @return The integer value.
593      * @throws JSONException
594      *             if the key is not found or if the value cannot be converted
595      *             to an integer.
596      */
597     public int getInt(String key) throws JSONException {
598         Object object = this.get(key);
599         try {
600             return object instanceof Number ? ((Number) object).intValue()
601                     : Integer.parseInt((String) object);
602         } catch (Exception e) {
603             throw new JSONException("JSONObject[" + quote(key)
604                     + "] is not an int.");
605         }
606     }
607
608     /**
609      * Get the JSONArray value associated with a key.
610      *
611      * @param key
612      *            A key string.
613      * @return A JSONArray which is the value.
614      * @throws JSONException
615      *             if the key is not found or if the value is not a JSONArray.
616      */
617     public JSONArray getJSONArray(String key) throws JSONException {
618         Object object = this.get(key);
619         if (object instanceof JSONArray) {
620             return (JSONArray) object;
621         }
622         throw new JSONException("JSONObject[" + quote(key)
623                 + "] is not a JSONArray.");
624     }
625
626     /**
627      * Get the JSONObject value associated with a key.
628      *
629      * @param key
630      *            A key string.
631      * @return A JSONObject which is the value.
632      * @throws JSONException
633      *             if the key is not found or if the value is not a JSONObject.
634      */
635     public JSONObject getJSONObject(String key) throws JSONException {
636         Object object = this.get(key);
637         if (object instanceof JSONObject) {
638             return (JSONObject) object;
639         }
640         throw new JSONException("JSONObject[" + quote(key)
641                 + "] is not a JSONObject.");
642     }
643
644     /**
645      * Get the long value associated with a key.
646      *
647      * @param key
648      *            A key string.
649      * @return The long value.
650      * @throws JSONException
651      *             if the key is not found or if the value cannot be converted
652      *             to a long.
653      */
654     public long getLong(String key) throws JSONException {
655         Object object = this.get(key);
656         try {
657             return object instanceof Number ? ((Number) object).longValue()
658                     : Long.parseLong((String) object);
659         } catch (Exception e) {
660             throw new JSONException("JSONObject[" + quote(key)
661                     + "] is not a long.");
662         }
663     }
664
665     /**
666      * Get an array of field names from a JSONObject.
667      *
668      * @return An array of field names, or null if there are no names.
669      */
670     public static String[] getNames(JSONObject jo) {
671         int length = jo.length();
672         if (length == 0) {
673             return null;
674         }
675         Iterator<String> iterator = jo.keys();
676         String[] names = new String[length];
677         int i = 0;
678         while (iterator.hasNext()) {
679             names[i] = iterator.next();
680             i += 1;
681         }
682         return names;
683     }
684
685     /**
686      * Get an array of field names from an Object.
687      *
688      * @return An array of field names, or null if there are no names.
689      */
690     public static String[] getNames(Object object) {
691         if (object == null) {
692             return null;
693         }
694         Class<?> klass = object.getClass();
695         Field[] fields = klass.getFields();
696         int length = fields.length;
697         if (length == 0) {
698             return null;
699         }
700         String[] names = new String[length];
701         for (int i = 0; i < length; i += 1) {
702             names[i] = fields[i].getName();
703         }
704         return names;
705     }
706
707     /**
708      * Get the string associated with a key.
709      *
710      * @param key
711      *            A key string.
712      * @return A string which is the value.
713      * @throws JSONException
714      *             if there is no string value for the key.
715      */
716     public String getString(String key) throws JSONException {
717         Object object = this.get(key);
718         if (object instanceof String) {
719             return (String) object;
720         }
721         throw new JSONException("JSONObject[" + quote(key) + "] not a string.");
722     }
723
724     /**
725      * Determine if the JSONObject contains a specific key.
726      *
727      * @param key
728      *            A key string.
729      * @return true if the key exists in the JSONObject.
730      */
731     public boolean has(String key) {
732         return this.map.containsKey(key);
733     }
734
735     /**
736      * Increment a property of a JSONObject. If there is no such property,
737      * create one with a value of 1. If there is such a property, and if it is
738      * an Integer, Long, Double, or Float, then add one to it.
739      *
740      * @param key
741      *            A key string.
742      * @return this.
743      * @throws JSONException
744      *             If there is already a property with this name that is not an
745      *             Integer, Long, Double, or Float.
746      */
747     public JSONObject increment(String key) throws JSONException {
748         Object value = this.opt(key);
749         if (value == null) {
750             this.put(key, 1);
751         } else if (value instanceof BigInteger) {
752             this.put(key, ((BigInteger)value).add(BigInteger.ONE));
753         } else if (value instanceof BigDecimal) {
754             this.put(key, ((BigDecimal)value).add(BigDecimal.ONE));
755         } else if (value instanceof Integer) {
756             this.put(key, (Integer) value + 1);
757         } else if (value instanceof Long) {
758             this.put(key, (Long) value + 1);
759         } else if (value instanceof Double) {
760             this.put(key, (Double) value + 1);
761         } else if (value instanceof Float) {
762             this.put(key, (Float) value + 1);
763         } else {
764             throw new JSONException("Unable to increment [" + quote(key) + "].");
765         }
766         return this;
767     }
768
769     /**
770      * Determine if the value associated with the key is null or if there is no
771      * value.
772      *
773      * @param key
774      *            A key string.
775      * @return true if there is no value associated with the key or if the value
776      *         is the JSONObject.NULL object.
777      */
778     public boolean isNull(String key) {
779         return JSONObject.NULL.equals(this.opt(key));
780     }
781
782     /**
783      * Get an enumeration of the keys of the JSONObject.
784      *
785      * @return An iterator of the keys.
786      */
787     public Iterator<String> keys() {
788         return this.keySet().iterator();
789     }
790
791     /**
792      * Get a set of keys of the JSONObject.
793      *
794      * @return A keySet.
795      */
796     public Set<String> keySet() {
797         return this.map.keySet();
798     }
799
800     /**
801      * Get the number of keys stored in the JSONObject.
802      *
803      * @return The number of keys in the JSONObject.
804      */
805     public int length() {
806         return this.map.size();
807     }
808
809     /**
810      * Produce a JSONArray containing the names of the elements of this
811      * JSONObject.
812      *
813      * @return A JSONArray containing the key strings, or null if the JSONObject
814      *         is empty.
815      */
816     public JSONArray names() {
817         JSONArray ja = new JSONArray();
818         Iterator<String> keys = this.keys();
819         while (keys.hasNext()) {
820             ja.put(keys.next());
821         }
822         return ja.length() == 0 ? null : ja;
823     }
824
825     /**
826      * Produce a string from a Number.
827      *
828      * @param number
829      *            A Number
830      * @return A String.
831      * @throws JSONException
832      *             If n is a non-finite number.
833      */
834     public static String numberToString(Number number) throws JSONException {
835         if (number == null) {
836             throw new JSONException("Null pointer");
837         }
838         testValidity(number);
839
840 // Shave off trailing zeros and decimal point, if possible.
841
842         String string = number.toString();
843         if (string.indexOf('.') > 0 && string.indexOf('e') < 0
844                 && string.indexOf('E') < 0) {
845             while (string.endsWith("0")) {
846                 string = string.substring(0, string.length() - 1);
847             }
848             if (string.endsWith(".")) {
849                 string = string.substring(0, string.length() - 1);
850             }
851         }
852         return string;
853     }
854
855     /**
856      * Get an optional value associated with a key.
857      *
858      * @param key
859      *            A key string.
860      * @return An object which is the value, or null if there is no value.
861      */
862     public Object opt(String key) {
863         return key == null ? null : this.map.get(key);
864     }
865
866     /**
867      * Get the enum value associated with a key.
868      * 
869      * @param clazz
870      *            The type of enum to retrieve.
871      * @param key
872      *            A key string.
873      * @return The enum value associated with the key or null if not found
874      */
875     public <E extends Enum<E>> E optEnum(Class<E> clazz, String key) {
876         return this.optEnum(clazz, key, null);
877     }
878
879     /**
880      * Get the enum value associated with a key.
881      * 
882      * @param clazz
883      *            The type of enum to retrieve.
884      * @param key
885      *            A key string.
886      * @param defaultValue
887      *            The default in case the value is not found
888      * @return The enum value associated with the key or defaultValue
889      *            if the value is not found or cannot be assigned to clazz
890      */
891     public <E extends Enum<E>> E optEnum(Class<E> clazz, String key, E defaultValue) {
892         try {
893             Object val = this.opt(key);
894             if (NULL.equals(val)) {
895                 return defaultValue;
896             }
897             if (clazz.isAssignableFrom(val.getClass())) {
898                 // we just checked it!
899                 @SuppressWarnings("unchecked")
900                 E myE = (E) val;
901                 return myE;
902             }
903             return Enum.valueOf(clazz, val.toString());
904         } catch (IllegalArgumentException e) {
905             return defaultValue;
906         } catch (NullPointerException e) {
907             return defaultValue;
908         }
909     }
910
911     /**
912      * Get an optional boolean associated with a key. It returns false if there
913      * is no such key, or if the value is not Boolean.TRUE or the String "true".
914      *
915      * @param key
916      *            A key string.
917      * @return The truth.
918      */
919     public boolean optBoolean(String key) {
920         return this.optBoolean(key, false);
921     }
922
923     /**
924      * Get an optional boolean associated with a key. It returns the
925      * defaultValue if there is no such key, or if it is not a Boolean or the
926      * String "true" or "false" (case insensitive).
927      *
928      * @param key
929      *            A key string.
930      * @param defaultValue
931      *            The default.
932      * @return The truth.
933      */
934     public boolean optBoolean(String key, boolean defaultValue) {
935         try {
936             return this.getBoolean(key);
937         } catch (Exception e) {
938             return defaultValue;
939         }
940     }
941
942     /**
943      * Get an optional double associated with a key, or NaN if there is no such
944      * key or if its value is not a number. If the value is a string, an attempt
945      * will be made to evaluate it as a number.
946      *
947      * @param key
948      *            A string which is the key.
949      * @return An object which is the value.
950      */
951     public double optDouble(String key) {
952         return this.optDouble(key, Double.NaN);
953     }
954
955     /**
956      * Get an optional BigInteger associated with a key, or the defaultValue if
957      * there is no such key or if its value is not a number. If the value is a
958      * string, an attempt will be made to evaluate it as a number.
959      *
960      * @param key
961      *            A key string.
962      * @param defaultValue
963      *            The default.
964      * @return An object which is the value.
965      */
966     public BigInteger optBigInteger(String key, BigInteger defaultValue) {
967         try {
968             return this.getBigInteger(key);
969         } catch (Exception e) {
970             return defaultValue;
971         }
972     }
973
974     /**
975      * Get an optional BigDecimal associated with a key, or the defaultValue if
976      * there is no such key or if its value is not a number. If the value is a
977      * string, an attempt will be made to evaluate it as a number.
978      *
979      * @param key
980      *            A key string.
981      * @param defaultValue
982      *            The default.
983      * @return An object which is the value.
984      */
985     public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) {
986         try {
987             return this.getBigDecimal(key);
988         } catch (Exception e) {
989             return defaultValue;
990         }
991     }
992
993     /**
994      * Get an optional double associated with a key, or the defaultValue if
995      * there is no such key or if its value is not a number. If the value is a
996      * string, an attempt will be made to evaluate it as a number.
997      *
998      * @param key
999      *            A key string.
1000      * @param defaultValue
1001      *            The default.
1002      * @return An object which is the value.
1003      */
1004     public double optDouble(String key, double defaultValue) {
1005         try {
1006             return this.getDouble(key);
1007         } catch (Exception e) {
1008             return defaultValue;
1009         }
1010     }
1011
1012     /**
1013      * Get an optional int value associated with a key, or zero if there is no
1014      * such key or if the value is not a number. If the value is a string, an
1015      * attempt will be made to evaluate it as a number.
1016      *
1017      * @param key
1018      *            A key string.
1019      * @return An object which is the value.
1020      */
1021     public int optInt(String key) {
1022         return this.optInt(key, 0);
1023     }
1024
1025     /**
1026      * Get an optional int value associated with a key, or the default if there
1027      * is no such key or if the value is not a number. If the value is a string,
1028      * an attempt will be made to evaluate it as a number.
1029      *
1030      * @param key
1031      *            A key string.
1032      * @param defaultValue
1033      *            The default.
1034      * @return An object which is the value.
1035      */
1036     public int optInt(String key, int defaultValue) {
1037         try {
1038             return this.getInt(key);
1039         } catch (Exception e) {
1040             return defaultValue;
1041         }
1042     }
1043
1044     /**
1045      * Get an optional JSONArray associated with a key. It returns null if there
1046      * is no such key, or if its value is not a JSONArray.
1047      *
1048      * @param key
1049      *            A key string.
1050      * @return A JSONArray which is the value.
1051      */
1052     public JSONArray optJSONArray(String key) {
1053         Object o = this.opt(key);
1054         return o instanceof JSONArray ? (JSONArray) o : null;
1055     }
1056
1057     /**
1058      * Get an optional JSONObject associated with a key. It returns null if
1059      * there is no such key, or if its value is not a JSONObject.
1060      *
1061      * @param key
1062      *            A key string.
1063      * @return A JSONObject which is the value.
1064      */
1065     public JSONObject optJSONObject(String key) {
1066         Object object = this.opt(key);
1067         return object instanceof JSONObject ? (JSONObject) object : null;
1068     }
1069
1070     /**
1071      * Get an optional long value associated with a key, or zero if there is no
1072      * such key or if the value is not a number. If the value is a string, an
1073      * attempt will be made to evaluate it as a number.
1074      *
1075      * @param key
1076      *            A key string.
1077      * @return An object which is the value.
1078      */
1079     public long optLong(String key) {
1080         return this.optLong(key, 0);
1081     }
1082
1083     /**
1084      * Get an optional long value associated with a key, or the default if there
1085      * is no such key or if the value is not a number. If the value is a string,
1086      * an attempt will be made to evaluate it as a number.
1087      *
1088      * @param key
1089      *            A key string.
1090      * @param defaultValue
1091      *            The default.
1092      * @return An object which is the value.
1093      */
1094     public long optLong(String key, long defaultValue) {
1095         try {
1096             return this.getLong(key);
1097         } catch (Exception e) {
1098             return defaultValue;
1099         }
1100     }
1101
1102     /**
1103      * Get an optional string associated with a key. It returns an empty string
1104      * if there is no such key. If the value is not a string and is not null,
1105      * then it is converted to a string.
1106      *
1107      * @param key
1108      *            A key string.
1109      * @return A string which is the value.
1110      */
1111     public String optString(String key) {
1112         return this.optString(key, "");
1113     }
1114
1115     /**
1116      * Get an optional string associated with a key. It returns the defaultValue
1117      * if there is no such key.
1118      *
1119      * @param key
1120      *            A key string.
1121      * @param defaultValue
1122      *            The default.
1123      * @return A string which is the value.
1124      */
1125     public String optString(String key, String defaultValue) {
1126         Object object = this.opt(key);
1127         return NULL.equals(object) ? defaultValue : object.toString();
1128     }
1129
1130     private void populateMap(Object bean) {
1131         Class<?> klass = bean.getClass();
1132
1133 // If klass is a System class then set includeSuperClass to false.
1134
1135         boolean includeSuperClass = klass.getClassLoader() != null;
1136
1137         Method[] methods = includeSuperClass ? klass.getMethods() : klass
1138                 .getDeclaredMethods();
1139         for (int i = 0; i < methods.length; i += 1) {
1140             try {
1141                 Method method = methods[i];
1142                 if (Modifier.isPublic(method.getModifiers())) {
1143                     String name = method.getName();
1144                     String key = "";
1145                     if (name.startsWith("get")) {
1146                         if ("getClass".equals(name)
1147                                 || "getDeclaringClass".equals(name)) {
1148                             key = "";
1149                         } else {
1150                             key = name.substring(3);
1151                         }
1152                     } else if (name.startsWith("is")) {
1153                         key = name.substring(2);
1154                     }
1155                     if (key.length() > 0
1156                             && Character.isUpperCase(key.charAt(0))
1157                             && method.getParameterTypes().length == 0) {
1158                         if (key.length() == 1) {
1159                             key = key.toLowerCase();
1160                         } else if (!Character.isUpperCase(key.charAt(1))) {
1161                             key = key.substring(0, 1).toLowerCase()
1162                                     + key.substring(1);
1163                         }
1164
1165                         Object result = method.invoke(bean, (Object[]) null);
1166                         if (result != null) {
1167                             this.map.put(key, wrap(result));
1168                         }
1169                     }
1170                 }
1171             } catch (Exception ignore) {
1172             }
1173         }
1174     }
1175
1176     /**
1177      * Put a key/boolean pair in the JSONObject.
1178      *
1179      * @param key
1180      *            A key string.
1181      * @param value
1182      *            A boolean which is the value.
1183      * @return this.
1184      * @throws JSONException
1185      *             If the key is null.
1186      */
1187     public JSONObject put(String key, boolean value) throws JSONException {
1188         this.put(key, value ? Boolean.TRUE : Boolean.FALSE);
1189         return this;
1190     }
1191
1192     /**
1193      * Put a key/value pair in the JSONObject, where the value will be a
1194      * JSONArray which is produced from a Collection.
1195      *
1196      * @param key
1197      *            A key string.
1198      * @param value
1199      *            A Collection value.
1200      * @return this.
1201      * @throws JSONException
1202      */
1203     public JSONObject put(String key, Collection<?> value) throws JSONException {
1204         this.put(key, new JSONArray(value));
1205         return this;
1206     }
1207
1208     /**
1209      * Put a key/double pair in the JSONObject.
1210      *
1211      * @param key
1212      *            A key string.
1213      * @param value
1214      *            A double which is the value.
1215      * @return this.
1216      * @throws JSONException
1217      *             If the key is null or if the number is invalid.
1218      */
1219     public JSONObject put(String key, double value) throws JSONException {
1220         this.put(key, new Double(value));
1221         return this;
1222     }
1223
1224     /**
1225      * Put a key/int pair in the JSONObject.
1226      *
1227      * @param key
1228      *            A key string.
1229      * @param value
1230      *            An int which is the value.
1231      * @return this.
1232      * @throws JSONException
1233      *             If the key is null.
1234      */
1235     public JSONObject put(String key, int value) throws JSONException {
1236         this.put(key, new Integer(value));
1237         return this;
1238     }
1239
1240     /**
1241      * Put a key/long pair in the JSONObject.
1242      *
1243      * @param key
1244      *            A key string.
1245      * @param value
1246      *            A long which is the value.
1247      * @return this.
1248      * @throws JSONException
1249      *             If the key is null.
1250      */
1251     public JSONObject put(String key, long value) throws JSONException {
1252         this.put(key, new Long(value));
1253         return this;
1254     }
1255
1256     /**
1257      * Put a key/value pair in the JSONObject, where the value will be a
1258      * JSONObject which is produced from a Map.
1259      *
1260      * @param key
1261      *            A key string.
1262      * @param value
1263      *            A Map value.
1264      * @return this.
1265      * @throws JSONException
1266      */
1267     public JSONObject put(String key, Map<?, ?> value) throws JSONException {
1268         this.put(key, new JSONObject(value));
1269         return this;
1270     }
1271
1272     /**
1273      * Put a key/value pair in the JSONObject. If the value is null, then the
1274      * key will be removed from the JSONObject if it is present.
1275      *
1276      * @param key
1277      *            A key string.
1278      * @param value
1279      *            An object which is the value. It should be of one of these
1280      *            types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
1281      *            String, or the JSONObject.NULL object.
1282      * @return this.
1283      * @throws JSONException
1284      *             If the value is non-finite number or if the key is null.
1285      */
1286     public JSONObject put(String key, Object value) throws JSONException {
1287         if (key == null) {
1288             throw new NullPointerException("Null key.");
1289         }
1290         if (value != null) {
1291             testValidity(value);
1292             this.map.put(key, value);
1293         } else {
1294             this.remove(key);
1295         }
1296         return this;
1297     }
1298
1299     /**
1300      * Put a key/value pair in the JSONObject, but only if the key and the value
1301      * are both non-null, and only if there is not already a member with that
1302      * name.
1303      *
1304      * @param key string
1305      * @param value object
1306      * @return this.
1307      * @throws JSONException
1308      *             if the key is a duplicate
1309      */
1310     public JSONObject putOnce(String key, Object value) throws JSONException {
1311         if (key != null && value != null) {
1312             if (this.opt(key) != null) {
1313                 throw new JSONException("Duplicate key \"" + key + "\"");
1314             }
1315             this.put(key, value);
1316         }
1317         return this;
1318     }
1319
1320     /**
1321      * Put a key/value pair in the JSONObject, but only if the key and the value
1322      * are both non-null.
1323      *
1324      * @param key
1325      *            A key string.
1326      * @param value
1327      *            An object which is the value. It should be of one of these
1328      *            types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
1329      *            String, or the JSONObject.NULL object.
1330      * @return this.
1331      * @throws JSONException
1332      *             If the value is a non-finite number.
1333      */
1334     public JSONObject putOpt(String key, Object value) throws JSONException {
1335         if (key != null && value != null) {
1336             this.put(key, value);
1337         }
1338         return this;
1339     }
1340
1341     /**
1342      * Creates a JSONPointer using an intialization string and tries to 
1343      * match it to an item within this JSONObject. For example, given a
1344      * JSONObject initialized with this document:
1345      * <pre>
1346      * {
1347      *     "a":{"b":"c"}
1348      * }
1349      * </pre>
1350      * and this JSONPointer string: 
1351      * <pre>
1352      * "/a/b"
1353      * </pre>
1354      * Then this method will return the String "c".
1355      * A JSONPointerException may be thrown from code called by this method.
1356      *   
1357      * @param jsonPointer string that can be used to create a JSONPointer
1358      * @return the item matched by the JSONPointer, otherwise null
1359      */
1360     public Object query(String jsonPointer) {
1361         return new JSONPointer(jsonPointer).queryFrom(this);
1362     }
1363     
1364     /**
1365      * Queries and returns a value from this object using {@code jsonPointer}, or
1366      * returns null if the query fails due to a missing key.
1367      * 
1368      * @param jsonPointer the string representation of the JSON pointer
1369      * @return the queried value or {@code null}
1370      * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax
1371      */
1372     public Object optQuery(String jsonPointer) {
1373         JSONPointer pointer = new JSONPointer(jsonPointer);
1374         try {
1375             return pointer.queryFrom(this);
1376         } catch (JSONPointerException e) {
1377             return null;
1378         }
1379     }
1380
1381     /**
1382      * Produce a string in double quotes with backslash sequences in all the
1383      * right places. A backslash will be inserted within </, producing <\/,
1384      * allowing JSON text to be delivered in HTML. In JSON text, a string cannot
1385      * contain a control character or an unescaped quote or backslash.
1386      *
1387      * @param string
1388      *            A String
1389      * @return A String correctly formatted for insertion in a JSON text.
1390      */
1391     public static String quote(String string) {
1392         StringWriter sw = new StringWriter();
1393         synchronized (sw.getBuffer()) {
1394             try {
1395                 return quote(string, sw).toString();
1396             } catch (IOException ignored) {
1397                 // will never happen - we are writing to a string writer
1398                 return "";
1399             }
1400         }
1401     }
1402
1403     public static Writer quote(String string, Writer w) throws IOException {
1404         if (string == null || string.length() == 0) {
1405             w.write("\"\"");
1406             return w;
1407         }
1408
1409         char b;
1410         char c = 0;
1411         String hhhh;
1412         int i;
1413         int len = string.length();
1414
1415         w.write('"');
1416         for (i = 0; i < len; i += 1) {
1417             b = c;
1418             c = string.charAt(i);
1419             switch (c) {
1420             case '\\':
1421             case '"':
1422                 w.write('\\');
1423                 w.write(c);
1424                 break;
1425             case '/':
1426                 if (b == '<') {
1427                     w.write('\\');
1428                 }
1429                 w.write(c);
1430                 break;
1431             case '\b':
1432                 w.write("\\b");
1433                 break;
1434             case '\t':
1435                 w.write("\\t");
1436                 break;
1437             case '\n':
1438                 w.write("\\n");
1439                 break;
1440             case '\f':
1441                 w.write("\\f");
1442                 break;
1443             case '\r':
1444                 w.write("\\r");
1445                 break;
1446             default:
1447                 if (c < ' ' || (c >= '\u0080' && c < '\u00a0')
1448                         || (c >= '\u2000' && c < '\u2100')) {
1449                     w.write("\\u");
1450                     hhhh = Integer.toHexString(c);
1451                     w.write("0000", 0, 4 - hhhh.length());
1452                     w.write(hhhh);
1453                 } else {
1454                     w.write(c);
1455                 }
1456             }
1457         }
1458         w.write('"');
1459         return w;
1460     }
1461
1462     /**
1463      * Remove a name and its value, if present.
1464      *
1465      * @param key
1466      *            The name to be removed.
1467      * @return The value that was associated with the name, or null if there was
1468      *         no value.
1469      */
1470     public Object remove(String key) {
1471         return this.map.remove(key);
1472     }
1473
1474     /**
1475      * Determine if two JSONObjects are similar.
1476      * They must contain the same set of names which must be associated with
1477      * similar values.
1478      *
1479      * @param other The other JSONObject
1480      * @return true if they are equal
1481      */
1482     public boolean similar(Object other) {
1483         try {
1484             if (!(other instanceof JSONObject)) {
1485                 return false;
1486             }
1487             Set<String> set = this.keySet();
1488             if (!set.equals(((JSONObject)other).keySet())) {
1489                 return false;
1490             }
1491             Iterator<String> iterator = set.iterator();
1492             while (iterator.hasNext()) {
1493                 String name = iterator.next();
1494                 Object valueThis = this.get(name);
1495                 Object valueOther = ((JSONObject)other).get(name);
1496                 if (valueThis instanceof JSONObject) {
1497                     if (!((JSONObject)valueThis).similar(valueOther)) {
1498                         return false;
1499                     }
1500                 } else if (valueThis instanceof JSONArray) {
1501                     if (!((JSONArray)valueThis).similar(valueOther)) {
1502                         return false;
1503                     }
1504                 } else if (!valueThis.equals(valueOther)) {
1505                     return false;
1506                 }
1507             }
1508             return true;
1509         } catch (Throwable exception) {
1510             return false;
1511         }
1512     }
1513
1514     /**
1515      * Try to convert a string into a number, boolean, or null. If the string
1516      * can't be converted, return the string.
1517      *
1518      * @param string
1519      *            A String.
1520      * @return A simple JSON value.
1521      */
1522     public static Object stringToValue(String string) {
1523         if (string.equals("")) {
1524             return string;
1525         }
1526         if (string.equalsIgnoreCase("true")) {
1527             return Boolean.TRUE;
1528         }
1529         if (string.equalsIgnoreCase("false")) {
1530             return Boolean.FALSE;
1531         }
1532         if (string.equalsIgnoreCase("null")) {
1533             return JSONObject.NULL;
1534         }
1535
1536         /*
1537          * If it might be a number, try converting it. If a number cannot be
1538          * produced, then the value will just be a string.
1539          */
1540
1541         char initial = string.charAt(0);
1542         if ((initial >= '0' && initial <= '9') || initial == '-') {
1543             try {
1544                 if (string.indexOf('.') > -1 || string.indexOf('e') > -1
1545                         || string.indexOf('E') > -1
1546                         || "-0".equals(string)) {
1547                     Double d = Double.valueOf(string);
1548                     if (!d.isInfinite() && !d.isNaN()) {
1549                         return d;
1550                     }
1551                 } else {
1552                     Long myLong = new Long(string);
1553                     if (string.equals(myLong.toString())) {
1554                         if (myLong.longValue() == myLong.intValue()) {
1555                             return Integer.valueOf(myLong.intValue());
1556                         }
1557                         return myLong;
1558                     }
1559                 }
1560             } catch (Exception ignore) {
1561             }
1562         }
1563         return string;
1564     }
1565
1566     /**
1567      * Throw an exception if the object is a NaN or infinite number.
1568      *
1569      * @param o
1570      *            The object to test.
1571      * @throws JSONException
1572      *             If o is a non-finite number.
1573      */
1574     public static void testValidity(Object o) throws JSONException {
1575         if (o != null) {
1576             if (o instanceof Double) {
1577                 if (((Double) o).isInfinite() || ((Double) o).isNaN()) {
1578                     throw new JSONException(
1579                             "JSON does not allow non-finite numbers.");
1580                 }
1581             } else if (o instanceof Float) {
1582                 if (((Float) o).isInfinite() || ((Float) o).isNaN()) {
1583                     throw new JSONException(
1584                             "JSON does not allow non-finite numbers.");
1585                 }
1586             }
1587         }
1588     }
1589
1590     /**
1591      * Produce a JSONArray containing the values of the members of this
1592      * JSONObject.
1593      *
1594      * @param names
1595      *            A JSONArray containing a list of key strings. This determines
1596      *            the sequence of the values in the result.
1597      * @return A JSONArray of values.
1598      * @throws JSONException
1599      *             If any of the values are non-finite numbers.
1600      */
1601     public JSONArray toJSONArray(JSONArray names) throws JSONException {
1602         if (names == null || names.length() == 0) {
1603             return null;
1604         }
1605         JSONArray ja = new JSONArray();
1606         for (int i = 0; i < names.length(); i += 1) {
1607             ja.put(this.opt(names.getString(i)));
1608         }
1609         return ja;
1610     }
1611
1612     /**
1613      * Make a JSON text of this JSONObject. For compactness, no whitespace is
1614      * added. If this would not result in a syntactically correct JSON text,
1615      * then null will be returned instead.
1616      * <p>
1617      * Warning: This method assumes that the data structure is acyclical.
1618      *
1619      * @return a printable, displayable, portable, transmittable representation
1620      *         of the object, beginning with <code>{</code>&nbsp;<small>(left
1621      *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
1622      *         brace)</small>.
1623      */
1624     public String toString() {
1625         try {
1626             return this.toString(0);
1627         } catch (Exception e) {
1628             return null;
1629         }
1630     }
1631
1632     /**
1633      * Make a prettyprinted JSON text of this JSONObject.
1634      * <p>
1635      * Warning: This method assumes that the data structure is acyclical.
1636      *
1637      * @param indentFactor
1638      *            The number of spaces to add to each level of indentation.
1639      * @return a printable, displayable, portable, transmittable representation
1640      *         of the object, beginning with <code>{</code>&nbsp;<small>(left
1641      *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
1642      *         brace)</small>.
1643      * @throws JSONException
1644      *             If the object contains an invalid number.
1645      */
1646     public String toString(int indentFactor) throws JSONException {
1647         StringWriter w = new StringWriter();
1648         synchronized (w.getBuffer()) {
1649             return this.write(w, indentFactor, 0).toString();
1650         }
1651     }
1652
1653     /**
1654      * Make a JSON text of an Object value. If the object has an
1655      * value.toJSONString() method, then that method will be used to produce the
1656      * JSON text. The method is required to produce a strictly conforming text.
1657      * If the object does not contain a toJSONString method (which is the most
1658      * common case), then a text will be produced by other means. If the value
1659      * is an array or Collection, then a JSONArray will be made from it and its
1660      * toJSONString method will be called. If the value is a MAP, then a
1661      * JSONObject will be made from it and its toJSONString method will be
1662      * called. Otherwise, the value's toString method will be called, and the
1663      * result will be quoted.
1664      *
1665      * <p>
1666      * Warning: This method assumes that the data structure is acyclical.
1667      *
1668      * @param value
1669      *            The value to be serialized.
1670      * @return a printable, displayable, transmittable representation of the
1671      *         object, beginning with <code>{</code>&nbsp;<small>(left
1672      *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
1673      *         brace)</small>.
1674      * @throws JSONException
1675      *             If the value is or contains an invalid number.
1676      */
1677     public static String valueToString(Object value) throws JSONException {
1678         if (value == null || value.equals(null)) {
1679             return "null";
1680         }
1681         if (value instanceof JSONString) {
1682             Object object;
1683             try {
1684                 object = ((JSONString) value).toJSONString();
1685             } catch (Exception e) {
1686                 throw new JSONException(e);
1687             }
1688             if (object instanceof String) {
1689                 return (String) object;
1690             }
1691             throw new JSONException("Bad value from toJSONString: " + object);
1692         }
1693         if (value instanceof Number) {
1694             return numberToString((Number) value);
1695         }
1696         if (value instanceof Boolean || value instanceof JSONObject
1697                 || value instanceof JSONArray) {
1698             return value.toString();
1699         }
1700         if (value instanceof Map) {
1701             Map<?, ?> map = (Map<?, ?>) value;
1702             return new JSONObject(map).toString();
1703         }
1704         if (value instanceof Collection) {
1705             Collection<?> coll = (Collection<?>) value;
1706             return new JSONArray(coll).toString();
1707         }
1708         if (value.getClass().isArray()) {
1709             return new JSONArray(value).toString();
1710         }
1711         return quote(value.toString());
1712     }
1713
1714     /**
1715      * Wrap an object, if necessary. If the object is null, return the NULL
1716      * object. If it is an array or collection, wrap it in a JSONArray. If it is
1717      * a map, wrap it in a JSONObject. If it is a standard property (Double,
1718      * String, et al) then it is already wrapped. Otherwise, if it comes from
1719      * one of the java packages, turn it into a string. And if it doesn't, try
1720      * to wrap it in a JSONObject. If the wrapping fails, then null is returned.
1721      *
1722      * @param object
1723      *            The object to wrap
1724      * @return The wrapped value
1725      */
1726     public static Object wrap(Object object) {
1727         try {
1728             if (object == null) {
1729                 return NULL;
1730             }
1731             if (object instanceof JSONObject || object instanceof JSONArray
1732                     || NULL.equals(object) || object instanceof JSONString
1733                     || object instanceof Byte || object instanceof Character
1734                     || object instanceof Short || object instanceof Integer
1735                     || object instanceof Long || object instanceof Boolean
1736                     || object instanceof Float || object instanceof Double
1737                     || object instanceof String || object instanceof BigInteger
1738                     || object instanceof BigDecimal) {
1739                 return object;
1740             }
1741
1742             if (object instanceof Collection) {
1743                 Collection<?> coll = (Collection<?>) object;
1744                 return new JSONArray(coll);
1745             }
1746             if (object.getClass().isArray()) {
1747                 return new JSONArray(object);
1748             }
1749             if (object instanceof Map) {
1750                 Map<?, ?> map = (Map<?, ?>) object;
1751                 return new JSONObject(map);
1752             }
1753             Package objectPackage = object.getClass().getPackage();
1754             String objectPackageName = objectPackage != null ? objectPackage
1755                     .getName() : "";
1756             if (objectPackageName.startsWith("java.")
1757                     || objectPackageName.startsWith("javax.")
1758                     || object.getClass().getClassLoader() == null) {
1759                 return object.toString();
1760             }
1761             return new JSONObject(object);
1762         } catch (Exception exception) {
1763             return null;
1764         }
1765     }
1766
1767     /**
1768      * Write the contents of the JSONObject as JSON text to a writer. For
1769      * compactness, no whitespace is added.
1770      * <p>
1771      * Warning: This method assumes that the data structure is acyclical.
1772      *
1773      * @return The writer.
1774      * @throws JSONException
1775      */
1776     public Writer write(Writer writer) throws JSONException {
1777         return this.write(writer, 0, 0);
1778     }
1779
1780     static final Writer writeValue(Writer writer, Object value,
1781             int indentFactor, int indent) throws JSONException, IOException {
1782         if (value == null || value.equals(null)) {
1783             writer.write("null");
1784         } else if (value instanceof JSONObject) {
1785             ((JSONObject) value).write(writer, indentFactor, indent);
1786         } else if (value instanceof JSONArray) {
1787             ((JSONArray) value).write(writer, indentFactor, indent);
1788         } else if (value instanceof Map) {
1789             Map<?, ?> map = (Map<?, ?>) value;
1790             new JSONObject(map).write(writer, indentFactor, indent);
1791         } else if (value instanceof Collection) {
1792             Collection<?> coll = (Collection<?>) value;
1793             new JSONArray(coll).write(writer, indentFactor, indent);
1794         } else if (value.getClass().isArray()) {
1795             new JSONArray(value).write(writer, indentFactor, indent);
1796         } else if (value instanceof Number) {
1797             writer.write(numberToString((Number) value));
1798         } else if (value instanceof Boolean) {
1799             writer.write(value.toString());
1800         } else if (value instanceof JSONString) {
1801             Object o;
1802             try {
1803                 o = ((JSONString) value).toJSONString();
1804             } catch (Exception e) {
1805                 throw new JSONException(e);
1806             }
1807             writer.write(o != null ? o.toString() : quote(value.toString()));
1808         } else {
1809             quote(value.toString(), writer);
1810         }
1811         return writer;
1812     }
1813
1814     static final void indent(Writer writer, int indent) throws IOException {
1815         for (int i = 0; i < indent; i += 1) {
1816             writer.write(' ');
1817         }
1818     }
1819
1820     /**
1821      * Write the contents of the JSONObject as JSON text to a writer. For
1822      * compactness, no whitespace is added.
1823      * <p>
1824      * Warning: This method assumes that the data structure is acyclical.
1825      *
1826      * @param writer
1827      *            Writes the serialized JSON
1828      * @param indentFactor
1829      *            The number of spaces to add to each level of indentation.
1830      * @param indent
1831      *            The indention of the top level.
1832      * @return The writer.
1833      * @throws JSONException
1834      */
1835     public Writer write(Writer writer, int indentFactor, int indent)
1836             throws JSONException {
1837         try {
1838             boolean commanate = false;
1839             final int length = this.length();
1840             Iterator<String> keys = this.keys();
1841             writer.write('{');
1842
1843             if (length == 1) {
1844                 Object key = keys.next();
1845                 writer.write(quote(key.toString()));
1846                 writer.write(':');
1847                 if (indentFactor > 0) {
1848                     writer.write(' ');
1849                 }
1850                 writeValue(writer, this.map.get(key), indentFactor, indent);
1851             } else if (length != 0) {
1852                 final int newindent = indent + indentFactor;
1853                 while (keys.hasNext()) {
1854                     Object key = keys.next();
1855                     if (commanate) {
1856                         writer.write(',');
1857                     }
1858                     if (indentFactor > 0) {
1859                         writer.write('\n');
1860                     }
1861                     indent(writer, newindent);
1862                     writer.write(quote(key.toString()));
1863                     writer.write(':');
1864                     if (indentFactor > 0) {
1865                         writer.write(' ');
1866                     }
1867                     writeValue(writer, this.map.get(key), indentFactor, newindent);
1868                     commanate = true;
1869                 }
1870                 if (indentFactor > 0) {
1871                     writer.write('\n');
1872                 }
1873                 indent(writer, indent);
1874             }
1875             writer.write('}');
1876             return writer;
1877         } catch (IOException exception) {
1878             throw new JSONException(exception);
1879         }
1880     }
1881
1882     /**
1883      * Returns a java.util.Map containing all of the entrys in this object.
1884      * If an entry in the object is a JSONArray or JSONObject it will also
1885      * be converted.
1886      * <p>
1887      * Warning: This method assumes that the data structure is acyclical.
1888      *
1889      * @return a java.util.Map containing the entrys of this object
1890      */
1891     public Map<String, Object> toMap() {
1892         Map<String, Object> results = new HashMap<String, Object>();
1893         for (Entry<String, Object> entry : this.map.entrySet()) {
1894             Object value;
1895             if (entry.getValue() == null || NULL.equals(entry.getValue())) {
1896                 value = null;
1897             } else if (entry.getValue() instanceof JSONObject) {
1898                 value = ((JSONObject) entry.getValue()).toMap();
1899             } else if (entry.getValue() instanceof JSONArray) {
1900                 value = ((JSONArray) entry.getValue()).toList();
1901             } else {
1902                 value = entry.getValue();
1903             }
1904             results.put(entry.getKey(), value);
1905         }
1906         return results;
1907     }
1908 }