001
002
003/*
004Copyright (c) 2002 JSON.org
005
006Permission is hereby granted, free of charge, to any person obtaining a copy
007of this software and associated documentation files (the "Software"), to deal
008in the Software without restriction, including without limitation the rights
009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010copies of the Software, and to permit persons to whom the Software is
011furnished to do so, subject to the following conditions:
012
013The above copyright notice and this permission notice shall be included in all
014copies or substantial portions of the Software.
015
016The Software shall be used for Good, not Evil.
017
018THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
021AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
023OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
024SOFTWARE.
025*/
026package armyc2.c5isr.web.json.utilities;
027import java.io.IOException;
028import java.io.Writer;
029import java.lang.reflect.Field;
030import java.lang.reflect.Modifier;
031import java.lang.reflect.Method;
032import java.util.Collection;
033import java.util.Enumeration;
034import java.util.HashMap;
035import java.util.Iterator;
036import java.util.Locale;
037import java.util.Map;
038import java.util.ResourceBundle;
039
040/**
041 * A JSONObject is an unordered collection of name/value pairs. Its
042 * external form is a string wrapped in curly braces with colons between the
043 * names and values, and commas between the values and names. The internal form
044 * is an object having <code>get</code> and <code>opt</code> methods for
045 * accessing the values by name, and <code>put</code> methods for adding or
046 * replacing values by name. The values can be any of these types:
047 * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
048 * <code>Number</code>, <code>String</code>, or the <code>JSONObject.NULL</code>
049 * object. A JSONObject constructor can be used to convert an external form
050 * JSON text into an internal form whose values can be retrieved with the
051 * <code>get</code> and <code>opt</code> methods, or to convert values into a
052 * JSON text using the <code>put</code> and <code>toString</code> methods.
053 * A <code>get</code> method returns a value if one can be found, and throws an
054 * exception if one cannot be found. An <code>opt</code> method returns a
055 * default value instead of throwing an exception, and so is useful for
056 * obtaining optional values.
057 * <p>
058 * The generic <code>get()</code> and <code>opt()</code> methods return an
059 * object, which you can cast or query for type. There are also typed
060 * <code>get</code> and <code>opt</code> methods that do type checking and type
061 * coercion for you. The opt methods differ from the get methods in that they
062 * do not throw. Instead, they return a specified value, such as null.
063 * <p>
064 * The <code>put</code> methods add or replace values in an object. For example, 
065 * <pre>myString = new JSONObject().put("JSON", "Hello, World!").toString();</pre>
066 * produces the string <code>{"JSON": "Hello, World"}</code>.
067 * <p>
068 * The texts produced by the <code>toString</code> methods strictly conform to
069 * the JSON syntax rules.
070 * The constructors are more forgiving in the texts they will accept:
071 * <ul>
072 * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
073 *     before the closing brace.</li>
074 * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
075 *     quote)</small>.</li>
076 * <li>Strings do not need to be quoted at all if they do not begin with a quote
077 *     or single quote, and if they do not contain leading or trailing spaces,
078 *     and if they do not contain any of these characters:
079 *     <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
080 *     and if they are not the reserved words <code>true</code>,
081 *     <code>false</code>, or <code>null</code>.</li>
082 * <li>Keys can be followed by <code>=</code> or <code>=&gt;</code> as well as
083 *     by <code>:</code>.</li>
084 * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
085 *     well as by <code>,</code> <small>(comma)</small>.</li>
086 * <li>Numbers may have the <code>0x-</code> <small>(hex)</small> prefix.</li>
087 * </ul>
088 * @author JSON.org
089 * @version 2011-04-05
090 */
091public class JSONObject {
092
093    /**
094     * JSONObject.NULL is equivalent to the value that JavaScript calls null,
095     * whilst Java's null is equivalent to the value that JavaScript calls
096     * undefined.
097     */
098     private static final class Null {
099
100        /**
101         * There is only intended to be a single instance of the NULL object,
102         * so the clone method returns itself.
103         * @return     NULL.
104         */
105        protected final Object clone() {
106            return this;
107        }
108
109        /**
110         * A Null object is equal to the null value and to itself.
111         * @param object    An object to test for nullness.
112         * @return true if the object parameter is the JSONObject.NULL object
113         *  or null.
114         */
115        public boolean equals(Object object) {
116            return object == null || object == this;
117        }
118
119        /**
120         * Get the "null" string value.
121         * @return The string "null".
122         */
123        public String toString() {
124            return "null";
125        }
126    }
127
128
129    /**
130     * The map where the JSONObject's properties are kept.
131     */
132    private Map map;
133
134
135    /**
136     * It is sometimes more convenient and less ambiguous to have a
137     * <code>NULL</code> object than to use Java's <code>null</code> value.
138     * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
139     * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
140     */
141    public static final Object NULL = new Null();
142
143
144    /**
145     * Construct an empty JSONObject.
146     */
147    public JSONObject() {
148        this.map = new HashMap();
149    }
150
151
152    /**
153     * Construct a JSONObject from a subset of another JSONObject.
154     * An array of strings is used to identify the keys that should be copied.
155     * Missing keys are ignored.
156     * @param jo A JSONObject.
157     * @param names An array of strings.
158     */
159    public JSONObject(JSONObject jo, String[] names) {
160        this();
161        for (int i = 0; i < names.length; i += 1) {
162            try {
163                putOnce(names[i], jo.opt(names[i]));
164            } catch (Exception ignore) {
165            }
166        }
167    }
168
169
170    /**
171     * Construct a JSONObject from a JSONTokener.
172     * @param x A JSONTokener object containing the source string.
173     * @throws JSONException If there is a syntax error in the source string
174     *  or a duplicated key.
175     */
176    public JSONObject(JSONTokener x) throws JSONException {
177        this();
178        char c;
179        String key;
180
181        if (x.nextClean() != '{') {
182            throw x.syntaxError("A JSONObject text must begin with '{'");
183        }
184        for (;;) {
185            c = x.nextClean();
186            switch (c) {
187            case 0:
188                throw x.syntaxError("A JSONObject text must end with '}'");
189            case '}':
190                return;
191            default:
192                x.back();
193                key = x.nextValue().toString();
194            }
195
196// The key is followed by ':'. We will also tolerate '=' or '=>'.
197
198            c = x.nextClean();
199            if (c == '=') {
200                if (x.next() != '>') {
201                    x.back();
202                }
203            } else if (c != ':') {
204                throw x.syntaxError("Expected a ':' after a key");
205            }
206            putOnce(key, x.nextValue());
207
208// Pairs are separated by ','. We will also tolerate ';'.
209
210            switch (x.nextClean()) {
211            case ';':
212            case ',':
213                if (x.nextClean() == '}') {
214                    return;
215                }
216                x.back();
217                break;
218            case '}':
219                return;
220            default:
221                throw x.syntaxError("Expected a ',' or '}'");
222            }
223        }
224    }
225
226
227    /**
228     * Construct a JSONObject from a Map.
229     *
230     * @param map A map object that can be used to initialize the contents of
231     *  the JSONObject.
232     */
233    public JSONObject(Map map) {
234        this.map = new HashMap();
235        if (map != null) {
236            Iterator i = map.entrySet().iterator();
237            while (i.hasNext()) {
238                Map.Entry e = (Map.Entry)i.next();
239                Object value = e.getValue();
240                if (value != null) {
241                    this.map.put(e.getKey(), wrap(value));
242                }
243            }
244        }
245    }
246
247
248    /**
249     * Construct a JSONObject from an Object using bean getters.
250     * It reflects on all of the public methods of the object.
251     * For each of the methods with no parameters and a name starting
252     * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
253     * the method is invoked, and a key and the value returned from the getter method
254     * are put into the new JSONObject.
255     *
256     * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix.
257     * If the second remaining character is not upper case, then the first
258     * character is converted to lower case.
259     *
260     * For example, if an object has a method named <code>"getName"</code>, and
261     * if the result of calling <code>object.getName()</code> is <code>"Larry Fine"</code>,
262     * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
263     *
264     * @param bean An object that has getter methods that should be used
265     * to make a JSONObject.
266     */
267    public JSONObject(Object bean) {
268        this();
269        populateMap(bean);
270    }
271
272
273    /**
274     * Construct a JSONObject from an Object, using reflection to find the
275     * public members. The resulting JSONObject's keys will be the strings
276     * from the names array, and the values will be the field values associated
277     * with those keys in the object. If a key is not found or not visible,
278     * then it will not be copied into the new JSONObject.
279     * @param object An object that has fields that should be used to make a
280     * JSONObject.
281     * @param names An array of strings, the names of the fields to be obtained
282     * from the object.
283     */
284    public JSONObject(Object object, String names[]) {
285        this();
286        Class c = object.getClass();
287        for (int i = 0; i < names.length; i += 1) {
288            String name = names[i];
289            try {
290                putOpt(name, c.getField(name).get(object));
291            } catch (Exception ignore) {
292            }
293        }
294    }
295
296
297    /**
298     * Construct a JSONObject from a source JSON text string.
299     * This is the most commonly used JSONObject constructor.
300     * @param source    A string beginning
301     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
302     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
303     * @exception JSONException If there is a syntax error in the source
304     *  string or a duplicated key.
305     */
306    public JSONObject(String source) throws JSONException {
307        this(new JSONTokener(source));
308    }
309
310
311    /**
312     * Construct a JSONObject from a ResourceBundle.
313     * @param baseName The ResourceBundle base name.
314     * @param locale The Locale to load the ResourceBundle for.
315     * @throws JSONException If any JSONExceptions are detected.
316     */
317    public JSONObject(String baseName, Locale locale) throws JSONException {
318        this();
319        ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, 
320                Thread.currentThread().getContextClassLoader());
321
322// Iterate through the keys in the bundle.
323        
324        Enumeration keys = bundle.getKeys();
325        while (keys.hasMoreElements()) {
326            Object key = keys.nextElement();
327            if (key instanceof String) {
328    
329// Go through the path, ensuring that there is a nested JSONObject for each 
330// segment except the last. Add the value using the last segment's name into
331// the deepest nested JSONObject.
332                
333                String[] path = ((String)key).split("\\.");
334                int last = path.length - 1;
335                JSONObject target = this;
336                for (int i = 0; i < last; i += 1) {
337                    String segment = path[i];
338                    JSONObject nextTarget = target.optJSONObject(segment);
339                    if (nextTarget == null) {
340                        nextTarget = new JSONObject();
341                        target.put(segment, nextTarget);
342                    }
343                    target = nextTarget;
344                }
345                target.put(path[last], bundle.getString((String)key));
346            }
347        }
348    }
349
350    
351    /**
352     * Accumulate values under a key. It is similar to the put method except
353     * that if there is already an object stored under the key then a
354     * JSONArray is stored under the key to hold all of the accumulated values.
355     * If there is already a JSONArray, then the new value is appended to it.
356     * In contrast, the put method replaces the previous value.
357     * 
358     * If only one value is accumulated that is not a JSONArray, then the
359     * result will be the same as using put. But if multiple values are 
360     * accumulated, then the result will be like append.
361     * @param key   A key string.
362     * @param value An object to be accumulated under the key.
363     * @return this.
364     * @throws JSONException If the value is an invalid number
365     *  or if the key is null.
366     */
367    public JSONObject accumulate(
368        String key, 
369        Object value
370    ) throws JSONException {
371        testValidity(value);
372        Object object = opt(key);
373        if (object == null) {
374            put(key, value instanceof JSONArray ?
375                    new JSONArray().put(value) : value);
376        } else if (object instanceof JSONArray) {
377            ((JSONArray)object).put(value);
378        } else {
379            put(key, new JSONArray().put(object).put(value));
380        }
381        return this;
382    }
383
384
385    /**
386     * Append values to the array under a key. If the key does not exist in the
387     * JSONObject, then the key is put in the JSONObject with its value being a
388     * JSONArray containing the value parameter. If the key was already
389     * associated with a JSONArray, then the value parameter is appended to it.
390     * @param key   A key string.
391     * @param value An object to be accumulated under the key.
392     * @return this.
393     * @throws JSONException If the key is null or if the current value
394     *  associated with the key is not a JSONArray.
395     */
396    public JSONObject append(String key, Object value) throws JSONException {
397        testValidity(value);
398        Object object = opt(key);
399        if (object == null) {
400            put(key, new JSONArray().put(value));
401        } else if (object instanceof JSONArray) {
402            put(key, ((JSONArray)object).put(value));
403        } else {
404            throw new JSONException("JSONObject[" + key +
405                    "] is not a JSONArray.");
406        }
407        return this;
408    }
409
410
411    /**
412     * Produce a string from a double. The string "null" will be returned if
413     * the number is not finite.
414     * @param  d A double.
415     * @return A String.
416     */
417    public static String doubleToString(double d) {
418        if (Double.isInfinite(d) || Double.isNaN(d)) {
419            return "null";
420        }
421
422// Shave off trailing zeros and decimal point, if possible.
423
424        String string = Double.toString(d);
425        if (string.indexOf('.') > 0 && string.indexOf('e') < 0 && 
426                        string.indexOf('E') < 0) {
427            while (string.endsWith("0")) {
428                string = string.substring(0, string.length() - 1);
429            }
430            if (string.endsWith(".")) {
431                string = string.substring(0, string.length() - 1);
432            }
433        }
434        return string;
435    }
436
437
438    /**
439     * Get the value object associated with a key.
440     *
441     * @param key   A key string.
442     * @return      The object associated with the key.
443     * @throws      JSONException if the key is not found.
444     */
445    public Object get(String key) throws JSONException {
446        if (key == null) {
447            throw new JSONException("Null key.");
448        }
449        Object object = opt(key);
450        if (object == null) {
451            throw new JSONException("JSONObject[" + quote(key) +
452                    "] not found.");
453        }
454        return object;
455    }
456
457
458    /**
459     * Get the boolean value associated with a key.
460     *
461     * @param key   A key string.
462     * @return      The truth.
463     * @throws      JSONException
464     *  if the value is not a Boolean or the String "true" or "false".
465     */
466    public boolean getBoolean(String key) throws JSONException {
467        Object object = get(key);
468        if (object.equals(Boolean.FALSE) ||
469                (object instanceof String &&
470                ((String)object).equalsIgnoreCase("false"))) {
471            return false;
472        } else if (object.equals(Boolean.TRUE) ||
473                (object instanceof String &&
474                ((String)object).equalsIgnoreCase("true"))) {
475            return true;
476        }
477        throw new JSONException("JSONObject[" + quote(key) +
478                "] is not a Boolean.");
479    }
480
481
482    /**
483     * Get the double value associated with a key.
484     * @param key   A key string.
485     * @return      The numeric value.
486     * @throws JSONException if the key is not found or
487     *  if the value is not a Number object and cannot be converted to a number.
488     */
489    public double getDouble(String key) throws JSONException {
490        Object object = get(key);
491        try {
492            return object instanceof Number ?
493                ((Number)object).doubleValue() :
494                Double.parseDouble((String)object);
495        } catch (Exception e) {
496            throw new JSONException("JSONObject[" + quote(key) +
497                "] is not a number.");
498        }
499    }
500
501
502    /**
503     * Get the int value associated with a key. 
504     *
505     * @param key   A key string.
506     * @return      The integer value.
507     * @throws   JSONException if the key is not found or if the value cannot
508     *  be converted to an integer.
509     */
510    public int getInt(String key) throws JSONException {
511        Object object = get(key);
512        try {
513            return object instanceof Number ?
514                ((Number)object).intValue() :
515                Integer.parseInt((String)object);
516        } catch (Exception e) {
517            throw new JSONException("JSONObject[" + quote(key) +
518                "] is not an int.");
519        }
520    }
521
522
523    /**
524     * Get the JSONArray value associated with a key.
525     *
526     * @param key   A key string.
527     * @return      A JSONArray which is the value.
528     * @throws      JSONException if the key is not found or
529     *  if the value is not a JSONArray.
530     */
531    public JSONArray getJSONArray(String key) throws JSONException {
532        Object object = get(key);
533        if (object instanceof JSONArray) {
534            return (JSONArray)object;
535        }
536        throw new JSONException("JSONObject[" + quote(key) +
537                "] is not a JSONArray.");
538    }
539
540
541    /**
542     * Get the JSONObject value associated with a key.
543     *
544     * @param key   A key string.
545     * @return      A JSONObject which is the value.
546     * @throws      JSONException if the key is not found or
547     *  if the value is not a JSONObject.
548     */
549    public JSONObject getJSONObject(String key) throws JSONException {
550        Object object = get(key);
551        if (object instanceof JSONObject) {
552            return (JSONObject)object;
553        }
554        throw new JSONException("JSONObject[" + quote(key) +
555                "] is not a JSONObject.");
556    }
557
558
559    /**
560     * Get the long value associated with a key. 
561     *
562     * @param key   A key string.
563     * @return      The long value.
564     * @throws   JSONException if the key is not found or if the value cannot
565     *  be converted to a long.
566     */
567    public long getLong(String key) throws JSONException {
568        Object object = get(key);
569        try {
570            return object instanceof Number ?
571                ((Number)object).longValue() :
572                Long.parseLong((String)object);
573        } catch (Exception e) {
574            throw new JSONException("JSONObject[" + quote(key) +
575                "] is not a long.");
576        }
577    }
578
579
580    /**
581     * Get an array of field names from a JSONObject.
582     *
583     * @return An array of field names, or null if there are no names.
584     */
585    public static String[] getNames(JSONObject jo) {
586        int length = jo.length();
587        if (length == 0) {
588            return null;
589        }
590        Iterator iterator = jo.keys();
591        String[] names = new String[length];
592        int i = 0;
593        while (iterator.hasNext()) {
594            names[i] = (String)iterator.next();
595            i += 1;
596        }
597        return names;
598    }
599
600
601    /**
602     * Get an array of field names from an Object.
603     *
604     * @return An array of field names, or null if there are no names.
605     */
606    public static String[] getNames(Object object) {
607        if (object == null) {
608            return null;
609        }
610        Class klass = object.getClass();
611        Field[] fields = klass.getFields();
612        int length = fields.length;
613        if (length == 0) {
614            return null;
615        }
616        String[] names = new String[length];
617        for (int i = 0; i < length; i += 1) {
618            names[i] = fields[i].getName();
619        }
620        return names;
621    }
622
623
624    /**
625     * Get the string associated with a key.
626     *
627     * @param key   A key string.
628     * @return      A string which is the value.
629     * @throws   JSONException if there is no string value for the key.
630     */
631    public String getString(String key) throws JSONException {
632        Object object = get(key);
633        if (object instanceof String) {
634            return (String)object;
635        }
636        throw new JSONException("JSONObject[" + quote(key) +
637            "] not a string.");
638    }
639
640
641    /**
642     * Determine if the JSONObject contains a specific key.
643     * @param key   A key string.
644     * @return      true if the key exists in the JSONObject.
645     */
646    public boolean has(String key) {
647        return this.map.containsKey(key);
648    }
649    
650    
651    /**
652     * Increment a property of a JSONObject. If there is no such property,
653     * create one with a value of 1. If there is such a property, and if
654     * it is an Integer, Long, Double, or Float, then add one to it.
655     * @param key  A key string.
656     * @return this.
657     * @throws JSONException If there is already a property with this name
658     * that is not an Integer, Long, Double, or Float.
659     */
660    public JSONObject increment(String key) throws JSONException {
661        Object value = opt(key);
662        if (value == null) {
663            put(key, 1);
664        } else if (value instanceof Integer) {
665            put(key, ((Integer)value).intValue() + 1);
666        } else if (value instanceof Long) {
667            put(key, ((Long)value).longValue() + 1);                
668        } else if (value instanceof Double) {
669            put(key, ((Double)value).doubleValue() + 1);                
670        } else if (value instanceof Float) {
671            put(key, ((Float)value).floatValue() + 1);                
672        } else {
673            throw new JSONException("Unable to increment [" + quote(key) + "].");
674        }
675        return this;
676    }
677
678
679    /**
680     * Determine if the value associated with the key is null or if there is
681     *  no value.
682     * @param key   A key string.
683     * @return      true if there is no value associated with the key or if
684     *  the value is the JSONObject.NULL object.
685     */
686    public boolean isNull(String key) {
687        return JSONObject.NULL.equals(opt(key));
688    }
689
690
691    /**
692     * Get an enumeration of the keys of the JSONObject.
693     *
694     * @return An iterator of the keys.
695     */
696    public Iterator keys() {
697        return this.map.keySet().iterator();
698    }
699
700
701    /**
702     * Get the number of keys stored in the JSONObject.
703     *
704     * @return The number of keys in the JSONObject.
705     */
706    public int length() {
707        return this.map.size();
708    }
709
710
711    /**
712     * Produce a JSONArray containing the names of the elements of this
713     * JSONObject.
714     * @return A JSONArray containing the key strings, or null if the JSONObject
715     * is empty.
716     */
717    public JSONArray names() {
718        JSONArray ja = new JSONArray();
719        Iterator  keys = this.keys();
720        while (keys.hasNext()) {
721            ja.put(keys.next());
722        }
723        return ja.length() == 0 ? null : ja;
724    }
725
726    /**
727     * Produce a string from a Number.
728     * @param  number A Number
729     * @return A String.
730     * @throws JSONException If n is a non-finite number.
731     */
732    public static String numberToString(Number number)
733            throws JSONException {
734        if (number == null) {
735            throw new JSONException("Null pointer");
736        }
737        testValidity(number);
738
739// Shave off trailing zeros and decimal point, if possible.
740
741        String string = number.toString();
742        if (string.indexOf('.') > 0 && string.indexOf('e') < 0 && 
743                        string.indexOf('E') < 0) {
744            while (string.endsWith("0")) {
745                string = string.substring(0, string.length() - 1);
746            }
747            if (string.endsWith(".")) {
748                string = string.substring(0, string.length() - 1);
749            }
750        }
751        return string;
752    }
753
754
755    /**
756     * Get an optional value associated with a key.
757     * @param key   A key string.
758     * @return      An object which is the value, or null if there is no value.
759     */
760    public Object opt(String key) {
761        return key == null ? null : this.map.get(key);
762    }
763
764
765    /**
766     * Get an optional boolean associated with a key.
767     * It returns false if there is no such key, or if the value is not
768     * Boolean.TRUE or the String "true".
769     *
770     * @param key   A key string.
771     * @return      The truth.
772     */
773    public boolean optBoolean(String key) {
774        return optBoolean(key, false);
775    }
776
777
778    /**
779     * Get an optional boolean associated with a key.
780     * It returns the defaultValue if there is no such key, or if it is not
781     * a Boolean or the String "true" or "false" (case insensitive).
782     *
783     * @param key              A key string.
784     * @param defaultValue     The default.
785     * @return      The truth.
786     */
787    public boolean optBoolean(String key, boolean defaultValue) {
788        try {
789            return getBoolean(key);
790        } catch (Exception e) {
791            return defaultValue;
792        }
793    }
794
795
796    /**
797     * Get an optional double associated with a key,
798     * or NaN if there is no such key or if its value is not a number.
799     * If the value is a string, an attempt will be made to evaluate it as
800     * a number.
801     *
802     * @param key   A string which is the key.
803     * @return      An object which is the value.
804     */
805    public double optDouble(String key) {
806        return optDouble(key, Double.NaN);
807    }
808
809
810    /**
811     * Get an optional double associated with a key, or the
812     * defaultValue if there is no such key or if its value is not a number.
813     * If the value is a string, an attempt will be made to evaluate it as
814     * a number.
815     *
816     * @param key   A key string.
817     * @param defaultValue     The default.
818     * @return      An object which is the value.
819     */
820    public double optDouble(String key, double defaultValue) {
821        try {
822            return getDouble(key);
823        } catch (Exception e) {
824            return defaultValue;
825        }
826    }
827
828
829    /**
830     * Get an optional int value associated with a key,
831     * or zero if there is no such key or if the value is not a number.
832     * If the value is a string, an attempt will be made to evaluate it as
833     * a number.
834     *
835     * @param key   A key string.
836     * @return      An object which is the value.
837     */
838    public int optInt(String key) {
839        return optInt(key, 0);
840    }
841
842
843    /**
844     * Get an optional int value associated with a key,
845     * or the default if there is no such key or if the value is not a number.
846     * If the value is a string, an attempt will be made to evaluate it as
847     * a number.
848     *
849     * @param key   A key string.
850     * @param defaultValue     The default.
851     * @return      An object which is the value.
852     */
853    public int optInt(String key, int defaultValue) {
854        try {
855            return getInt(key);
856        } catch (Exception e) {
857            return defaultValue;
858        }
859    }
860
861
862    /**
863     * Get an optional JSONArray associated with a key.
864     * It returns null if there is no such key, or if its value is not a
865     * JSONArray.
866     *
867     * @param key   A key string.
868     * @return      A JSONArray which is the value.
869     */
870    public JSONArray optJSONArray(String key) {
871        Object o = opt(key);
872        return o instanceof JSONArray ? (JSONArray)o : null;
873    }
874
875
876    /**
877     * Get an optional JSONObject associated with a key.
878     * It returns null if there is no such key, or if its value is not a
879     * JSONObject.
880     *
881     * @param key   A key string.
882     * @return      A JSONObject which is the value.
883     */
884    public JSONObject optJSONObject(String key) {
885        Object object = opt(key);
886        return object instanceof JSONObject ? (JSONObject)object : null;
887    }
888
889
890    /**
891     * Get an optional long value associated with a key,
892     * or zero if there is no such key or if the value is not a number.
893     * If the value is a string, an attempt will be made to evaluate it as
894     * a number.
895     *
896     * @param key   A key string.
897     * @return      An object which is the value.
898     */
899    public long optLong(String key) {
900        return optLong(key, 0);
901    }
902
903
904    /**
905     * Get an optional long value associated with a key,
906     * or the default if there is no such key or if the value is not a number.
907     * If the value is a string, an attempt will be made to evaluate it as
908     * a number.
909     *
910     * @param key          A key string.
911     * @param defaultValue The default.
912     * @return             An object which is the value.
913     */
914    public long optLong(String key, long defaultValue) {
915        try {
916            return getLong(key);
917        } catch (Exception e) {
918            return defaultValue;
919        }
920    }
921
922
923    /**
924     * Get an optional string associated with a key.
925     * It returns an empty string if there is no such key. If the value is not
926     * a string and is not null, then it is converted to a string.
927     *
928     * @param key   A key string.
929     * @return      A string which is the value.
930     */
931    public String optString(String key) {
932        return optString(key, "");
933    }
934
935
936    /**
937     * Get an optional string associated with a key.
938     * It returns the defaultValue if there is no such key.
939     *
940     * @param key   A key string.
941     * @param defaultValue     The default.
942     * @return      A string which is the value.
943     */
944    public String optString(String key, String defaultValue) {
945        Object object = opt(key);
946        return NULL.equals(object) ? defaultValue : object.toString();        
947    }
948
949
950    private void populateMap(Object bean) {
951        Class klass = bean.getClass();
952
953// If klass is a System class then set includeSuperClass to false. 
954
955        boolean includeSuperClass = klass.getClassLoader() != null;
956
957        Method[] methods = (includeSuperClass) ?
958                klass.getMethods() : klass.getDeclaredMethods();
959        for (int i = 0; i < methods.length; i += 1) {
960            try {
961                Method method = methods[i];
962                if (Modifier.isPublic(method.getModifiers())) {
963                    String name = method.getName();
964                    String key = "";
965                    if (name.startsWith("get")) {
966                        if (name.equals("getClass") || 
967                                name.equals("getDeclaringClass")) {
968                            key = "";
969                        } else {
970                            key = name.substring(3);
971                        }
972                    } else if (name.startsWith("is")) {
973                        key = name.substring(2);
974                    }
975                    if (key.length() > 0 &&
976                            Character.isUpperCase(key.charAt(0)) &&
977                            method.getParameterTypes().length == 0) {
978                        if (key.length() == 1) {
979                            key = key.toLowerCase();
980                        } else if (!Character.isUpperCase(key.charAt(1))) {
981                            key = key.substring(0, 1).toLowerCase() +
982                                key.substring(1);
983                        }
984
985                        Object result = method.invoke(bean, (Object[])null);
986                        if (result != null) {
987                            map.put(key, wrap(result));
988                        }
989                    }
990                }
991            } catch (Exception ignore) {
992            }
993        }
994    }
995
996
997    /**
998     * Put a key/boolean pair in the JSONObject.
999     *
1000     * @param key   A key string.
1001     * @param value A boolean which is the value.
1002     * @return this.
1003     * @throws JSONException If the key is null.
1004     */
1005    public JSONObject put(String key, boolean value) throws JSONException {
1006        put(key, value ? Boolean.TRUE : Boolean.FALSE);
1007        return this;
1008    }
1009
1010
1011    /**
1012     * Put a key/value pair in the JSONObject, where the value will be a
1013     * JSONArray which is produced from a Collection.
1014     * @param key   A key string.
1015     * @param value A Collection value.
1016     * @return      this.
1017     * @throws JSONException
1018     */
1019    public JSONObject put(String key, Collection value) throws JSONException {
1020        put(key, new JSONArray(value));
1021        return this;
1022    }
1023
1024
1025    /**
1026     * Put a key/double pair in the JSONObject.
1027     *
1028     * @param key   A key string.
1029     * @param value A double which is the value.
1030     * @return this.
1031     * @throws JSONException If the key is null or if the number is invalid.
1032     */
1033    public JSONObject put(String key, double value) throws JSONException {
1034        put(key, new Double(value));
1035        return this;
1036    }
1037
1038
1039    /**
1040     * Put a key/int pair in the JSONObject.
1041     *
1042     * @param key   A key string.
1043     * @param value An int which is the value.
1044     * @return this.
1045     * @throws JSONException If the key is null.
1046     */
1047    public JSONObject put(String key, int value) throws JSONException {
1048        put(key, new Integer(value));
1049        return this;
1050    }
1051
1052
1053    /**
1054     * Put a key/long pair in the JSONObject.
1055     *
1056     * @param key   A key string.
1057     * @param value A long which is the value.
1058     * @return this.
1059     * @throws JSONException If the key is null.
1060     */
1061    public JSONObject put(String key, long value) throws JSONException {
1062        put(key, new Long(value));
1063        return this;
1064    }
1065
1066
1067    /**
1068     * Put a key/value pair in the JSONObject, where the value will be a
1069     * JSONObject which is produced from a Map.
1070     * @param key   A key string.
1071     * @param value A Map value.
1072     * @return      this.
1073     * @throws JSONException
1074     */
1075    public JSONObject put(String key, Map value) throws JSONException {
1076        put(key, new JSONObject(value));
1077        return this;
1078    }
1079
1080
1081    /**
1082     * Put a key/value pair in the JSONObject. If the value is null,
1083     * then the key will be removed from the JSONObject if it is present.
1084     * @param key   A key string.
1085     * @param value An object which is the value. It should be of one of these
1086     *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1087     *  or the JSONObject.NULL object.
1088     * @return this.
1089     * @throws JSONException If the value is non-finite number
1090     *  or if the key is null.
1091     */
1092    public JSONObject put(String key, Object value) throws JSONException {
1093        if (key == null) {
1094            throw new JSONException("Null key.");
1095        }
1096        if (value != null) {
1097            testValidity(value);
1098            this.map.put(key, value);
1099        } else {
1100            remove(key);
1101        }
1102        return this;
1103    }
1104
1105
1106    /**
1107     * Put a key/value pair in the JSONObject, but only if the key and the
1108     * value are both non-null, and only if there is not already a member
1109     * with that name.
1110     * @param key
1111     * @param value
1112     * @return his.
1113     * @throws JSONException if the key is a duplicate
1114     */
1115    public JSONObject putOnce(String key, Object value) throws JSONException {
1116        if (key != null && value != null) {
1117            if (opt(key) != null) {
1118                throw new JSONException("Duplicate key \"" + key + "\"");
1119            }
1120            put(key, value);
1121        }
1122        return this;
1123    }
1124
1125
1126    /**
1127     * Put a key/value pair in the JSONObject, but only if the
1128     * key and the value are both non-null.
1129     * @param key   A key string.
1130     * @param value An object which is the value. It should be of one of these
1131     *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1132     *  or the JSONObject.NULL object.
1133     * @return this.
1134     * @throws JSONException If the value is a non-finite number.
1135     */
1136    public JSONObject putOpt(String key, Object value) throws JSONException {
1137        if (key != null && value != null) {
1138            put(key, value);
1139        }
1140        return this;
1141    }
1142
1143
1144    /**
1145     * Produce a string in double quotes with backslash sequences in all the
1146     * right places. A backslash will be inserted within &lt;/, producing &lt;\/,
1147     * allowing JSON text to be delivered in HTML. In JSON text, a string 
1148     * cannot contain a control character or an unescaped quote or backslash.
1149     * @param string A String
1150     * @return  A String correctly formatted for insertion in a JSON text.
1151     */
1152    public static String quote(String string) {
1153        if (string == null || string.length() == 0) {
1154            return "\"\"";
1155        }
1156
1157        char         b;
1158        char         c = 0;
1159        String       hhhh;
1160        int          i;
1161        int          len = string.length();
1162        StringBuffer sb = new StringBuffer(len + 4);
1163
1164        sb.append('"');
1165        for (i = 0; i < len; i += 1) {
1166            b = c;
1167            c = string.charAt(i);
1168            switch (c) {
1169            case '\\':
1170            case '"':
1171                sb.append('\\');
1172                sb.append(c);
1173                break;
1174            case '/':
1175                if (b == '<') {
1176                    sb.append('\\');
1177                }
1178                sb.append(c);
1179                break;
1180            case '\b':
1181                sb.append("\\b");
1182                break;
1183            case '\t':
1184                sb.append("\\t");
1185                break;
1186            case '\n':
1187                sb.append("\\n");
1188                break;
1189            case '\f':
1190                sb.append("\\f");
1191                break;
1192            case '\r':
1193                sb.append("\\r");
1194                break;
1195            default:
1196                if (c < ' ' || (c >= '\u0080' && c < '\u00a0') ||
1197                               (c >= '\u2000' && c < '\u2100')) {
1198                    hhhh = "000" + Integer.toHexString(c);
1199                    sb.append("\\u" + hhhh.substring(hhhh.length() - 4));
1200                } else {
1201                    sb.append(c);
1202                }
1203            }
1204        }
1205        sb.append('"');
1206        return sb.toString();
1207    }
1208
1209    /**
1210     * Remove a name and its value, if present.
1211     * @param key The name to be removed.
1212     * @return The value that was associated with the name,
1213     * or null if there was no value.
1214     */
1215    public Object remove(String key) {
1216        return this.map.remove(key);
1217    }
1218
1219    /**
1220     * Try to convert a string into a number, boolean, or null. If the string
1221     * can't be converted, return the string.
1222     * @param string A String.
1223     * @return A simple JSON value.
1224     */
1225    public static Object stringToValue(String string) {
1226        if (string.equals("")) {
1227            return string;
1228        }
1229        if (string.equalsIgnoreCase("true")) {
1230            return Boolean.TRUE;
1231        }
1232        if (string.equalsIgnoreCase("false")) {
1233            return Boolean.FALSE;
1234        }
1235        if (string.equalsIgnoreCase("null")) {
1236            return JSONObject.NULL;
1237        }
1238
1239        /*
1240         * If it might be a number, try converting it. 
1241         * We support the non-standard 0x- convention. 
1242         * If a number cannot be produced, then the value will just
1243         * be a string. Note that the 0x-, plus, and implied string
1244         * conventions are non-standard. A JSON parser may accept
1245         * non-JSON forms as long as it accepts all correct JSON forms.
1246         */
1247
1248        char b = string.charAt(0);
1249        if ((b >= '0' && b <= '9') || b == '.' || b == '-' || b == '+') {
1250            if (b == '0' && string.length() > 2 &&
1251                        (string.charAt(1) == 'x' || string.charAt(1) == 'X')) {
1252                try {
1253                    return new Integer(Integer.parseInt(string.substring(2), 16));
1254                } catch (Exception ignore) {
1255                }
1256            }
1257            try {
1258                if (string.indexOf('.') > -1 || 
1259                        string.indexOf('e') > -1 || string.indexOf('E') > -1) {
1260                    return Double.valueOf(string);
1261                } else {
1262                    Long myLong = new Long(string);
1263                    if (myLong.longValue() == myLong.intValue()) {
1264                        return new Integer(myLong.intValue());
1265                    } else {
1266                        return myLong;
1267                    }
1268                }
1269            }  catch (Exception ignore) {
1270            }
1271        }
1272        return string;
1273    }
1274
1275
1276    /**
1277     * Throw an exception if the object is a NaN or infinite number.
1278     * @param o The object to test.
1279     * @throws JSONException If o is a non-finite number.
1280     */
1281    public static void testValidity(Object o) throws JSONException {
1282        if (o != null) {
1283            if (o instanceof Double) {
1284                if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
1285                    throw new JSONException(
1286                        "JSON does not allow non-finite numbers.");
1287                }
1288            } else if (o instanceof Float) {
1289                if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
1290                    throw new JSONException(
1291                        "JSON does not allow non-finite numbers.");
1292                }
1293            }
1294        }
1295    }
1296
1297
1298    /**
1299     * Produce a JSONArray containing the values of the members of this
1300     * JSONObject.
1301     * @param names A JSONArray containing a list of key strings. This
1302     * determines the sequence of the values in the result.
1303     * @return A JSONArray of values.
1304     * @throws JSONException If any of the values are non-finite numbers.
1305     */
1306    public JSONArray toJSONArray(JSONArray names) throws JSONException {
1307        if (names == null || names.length() == 0) {
1308            return null;
1309        }
1310        JSONArray ja = new JSONArray();
1311        for (int i = 0; i < names.length(); i += 1) {
1312            ja.put(this.opt(names.getString(i)));
1313        }
1314        return ja;
1315    }
1316
1317    /**
1318     * Make a JSON text of this JSONObject. For compactness, no whitespace
1319     * is added. If this would not result in a syntactically correct JSON text,
1320     * then null will be returned instead.
1321     * <p>
1322     * Warning: This method assumes that the data structure is acyclical.
1323     *
1324     * @return a printable, displayable, portable, transmittable
1325     *  representation of the object, beginning
1326     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1327     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1328     */
1329    public String toString() {
1330        try {
1331            Iterator     keys = this.keys();
1332            StringBuffer sb = new StringBuffer("{");
1333
1334            while (keys.hasNext()) {
1335                if (sb.length() > 1) {
1336                    sb.append(',');
1337                }
1338                Object o = keys.next();
1339                sb.append(quote(o.toString()));
1340                sb.append(':');
1341                sb.append(valueToString(this.map.get(o)));
1342            }
1343            sb.append('}');
1344            return sb.toString();
1345        } catch (Exception e) {
1346            return null;
1347        }
1348    }
1349
1350
1351    /**
1352     * Make a prettyprinted JSON text of this JSONObject.
1353     * <p>
1354     * Warning: This method assumes that the data structure is acyclical.
1355     * @param indentFactor The number of spaces to add to each level of
1356     *  indentation.
1357     * @return a printable, displayable, portable, transmittable
1358     *  representation of the object, beginning
1359     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1360     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1361     * @throws JSONException If the object contains an invalid number.
1362     */
1363    public String toString(int indentFactor) throws JSONException {
1364        return toString(indentFactor, 0);
1365    }
1366
1367
1368    /**
1369     * Make a prettyprinted JSON text of this JSONObject.
1370     * <p>
1371     * Warning: This method assumes that the data structure is acyclical.
1372     * @param indentFactor The number of spaces to add to each level of
1373     *  indentation.
1374     * @param indent The indentation of the top level.
1375     * @return a printable, displayable, transmittable
1376     *  representation of the object, beginning
1377     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1378     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1379     * @throws JSONException If the object contains an invalid number.
1380     */
1381    String toString(int indentFactor, int indent) throws JSONException {
1382        int i;
1383        int length = this.length();
1384        if (length == 0) {
1385            return "{}";
1386        }
1387        Iterator     keys = this.keys();
1388        int          newindent = indent + indentFactor;
1389        Object       object;
1390        StringBuffer sb = new StringBuffer("{");
1391        if (length == 1) {
1392            object = keys.next();
1393            sb.append(quote(object.toString()));
1394            sb.append(": ");
1395            sb.append(valueToString(this.map.get(object), indentFactor,
1396                    indent));
1397        } else {
1398            while (keys.hasNext()) {
1399                object = keys.next();
1400                if (sb.length() > 1) {
1401                    sb.append(",\n");
1402                } else {
1403                    sb.append('\n');
1404                }
1405                for (i = 0; i < newindent; i += 1) {
1406                    sb.append(' ');
1407                }
1408                sb.append(quote(object.toString()));
1409                sb.append(": ");
1410                sb.append(valueToString(this.map.get(object), indentFactor,
1411                        newindent));
1412            }
1413            if (sb.length() > 1) {
1414                sb.append('\n');
1415                for (i = 0; i < indent; i += 1) {
1416                    sb.append(' ');
1417                }
1418            }
1419        }
1420        sb.append('}');
1421        return sb.toString();
1422    }
1423
1424
1425    /**
1426     * Make a JSON text of an Object value. If the object has an
1427     * value.toJSONString() method, then that method will be used to produce
1428     * the JSON text. The method is required to produce a strictly
1429     * conforming text. If the object does not contain a toJSONString
1430     * method (which is the most common case), then a text will be
1431     * produced by other means. If the value is an array or Collection,
1432     * then a JSONArray will be made from it and its toJSONString method
1433     * will be called. If the value is a MAP, then a JSONObject will be made
1434     * from it and its toJSONString method will be called. Otherwise, the
1435     * value's toString method will be called, and the result will be quoted.
1436     *
1437     * <p>
1438     * Warning: This method assumes that the data structure is acyclical.
1439     * @param value The value to be serialized.
1440     * @return a printable, displayable, transmittable
1441     *  representation of the object, beginning
1442     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1443     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1444     * @throws JSONException If the value is or contains an invalid number.
1445     */
1446    public static String valueToString(Object value) throws JSONException {
1447        if (value == null || value.equals(null)) {
1448            return "null";
1449        }
1450        if (value instanceof JSONString) {
1451            Object object;
1452            try {
1453                object = ((JSONString)value).toJSONString();
1454            } catch (Exception e) {
1455                throw new JSONException(e);
1456            }
1457            if (object instanceof String) {
1458                return (String)object;
1459            }
1460            throw new JSONException("Bad value from toJSONString: " + object);
1461        }
1462        if (value instanceof Number) {
1463            return numberToString((Number) value);
1464        }
1465        if (value instanceof Boolean || value instanceof JSONObject ||
1466                value instanceof JSONArray) {
1467            return value.toString();
1468        }
1469        if (value instanceof Map) {
1470            return new JSONObject((Map)value).toString();
1471        }
1472        if (value instanceof Collection) {
1473            return new JSONArray((Collection)value).toString();
1474        }
1475        if (value.getClass().isArray()) {
1476            return new JSONArray(value).toString();
1477        }
1478        return quote(value.toString());
1479    }
1480
1481
1482    /**
1483     * Make a prettyprinted JSON text of an object value.
1484     * <p>
1485     * Warning: This method assumes that the data structure is acyclical.
1486     * @param value The value to be serialized.
1487     * @param indentFactor The number of spaces to add to each level of
1488     *  indentation.
1489     * @param indent The indentation of the top level.
1490     * @return a printable, displayable, transmittable
1491     *  representation of the object, beginning
1492     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1493     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1494     * @throws JSONException If the object contains an invalid number.
1495     */
1496     static String valueToString(
1497         Object value, 
1498         int    indentFactor, 
1499         int    indent
1500     ) throws JSONException {
1501        if (value == null || value.equals(null)) {
1502            return "null";
1503        }
1504        try {
1505            if (value instanceof JSONString) {
1506                Object o = ((JSONString)value).toJSONString();
1507                if (o instanceof String) {
1508                    return (String)o;
1509                }
1510            }
1511        } catch (Exception ignore) {
1512        }
1513        if (value instanceof Number) {
1514            return numberToString((Number) value);
1515        }
1516        if (value instanceof Boolean) {
1517            return value.toString();
1518        }
1519        if (value instanceof JSONObject) {
1520            return ((JSONObject)value).toString(indentFactor, indent);
1521        }
1522        if (value instanceof JSONArray) {
1523            return ((JSONArray)value).toString(indentFactor, indent);
1524        }
1525        if (value instanceof Map) {
1526            return new JSONObject((Map)value).toString(indentFactor, indent);
1527        }
1528        if (value instanceof Collection) {
1529            return new JSONArray((Collection)value).toString(indentFactor, indent);
1530        }
1531        if (value.getClass().isArray()) {
1532            return new JSONArray(value).toString(indentFactor, indent);
1533        }
1534        return quote(value.toString());
1535    }
1536
1537
1538     /**
1539      * Wrap an object, if necessary. If the object is null, return the NULL 
1540      * object. If it is an array or collection, wrap it in a JSONArray. If 
1541      * it is a map, wrap it in a JSONObject. If it is a standard property 
1542      * (Double, String, et al) then it is already wrapped. Otherwise, if it 
1543      * comes from one of the java packages, turn it into a string. And if 
1544      * it doesn't, try to wrap it in a JSONObject. If the wrapping fails,
1545      * then null is returned.
1546      *
1547      * @param object The object to wrap
1548      * @return The wrapped value
1549      */
1550     public static Object wrap(Object object) {
1551         try {
1552             if (object == null) {
1553                 return NULL;
1554             }
1555             if (object instanceof JSONObject || object instanceof JSONArray  || 
1556                     NULL.equals(object)      || object instanceof JSONString || 
1557                     object instanceof Byte   || object instanceof Character  ||
1558                     object instanceof Short  || object instanceof Integer    ||
1559                     object instanceof Long   || object instanceof Boolean    || 
1560                     object instanceof Float  || object instanceof Double     ||
1561                     object instanceof String) {
1562                 return object;
1563             }
1564             
1565             if (object instanceof Collection) {
1566                 return new JSONArray((Collection)object);
1567             }
1568             if (object.getClass().isArray()) {
1569                 return new JSONArray(object);
1570             }
1571             if (object instanceof Map) {
1572                 return new JSONObject((Map)object);
1573             }
1574             Package objectPackage = object.getClass().getPackage();
1575             String objectPackageName = objectPackage != null ? 
1576                 objectPackage.getName() : "";
1577             if (
1578                 objectPackageName.startsWith("java.") ||
1579                 objectPackageName.startsWith("javax.") ||
1580                 object.getClass().getClassLoader() == null
1581             ) {
1582                 return object.toString();
1583             }
1584             return new JSONObject(object);
1585         } catch(Exception exception) {
1586             return null;
1587         }
1588     }
1589
1590     
1591     /**
1592      * Write the contents of the JSONObject as JSON text to a writer.
1593      * For compactness, no whitespace is added.
1594      * <p>
1595      * Warning: This method assumes that the data structure is acyclical.
1596      *
1597      * @return The writer.
1598      * @throws JSONException
1599      */
1600     public Writer write(Writer writer) throws JSONException {
1601        try {
1602            boolean  commanate = false;
1603            Iterator keys = this.keys();
1604            writer.write('{');
1605
1606            while (keys.hasNext()) {
1607                if (commanate) {
1608                    writer.write(',');
1609                }
1610                Object key = keys.next();
1611                writer.write(quote(key.toString()));
1612                writer.write(':');
1613                Object value = this.map.get(key);
1614                if (value instanceof JSONObject) {
1615                    ((JSONObject)value).write(writer);
1616                } else if (value instanceof JSONArray) {
1617                    ((JSONArray)value).write(writer);
1618                } else {
1619                    writer.write(valueToString(value));
1620                }
1621                commanate = true;
1622            }
1623            writer.write('}');
1624            return writer;
1625        } catch (IOException exception) {
1626            throw new JSONException(exception);
1627        }
1628     }
1629}