Statistics
| Branch: | Tag: | Revision:

root / src / org / json / JSONObject.java @ 4bf05cad

History | View | Annotate | Download (48.4 kB)

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.Writer;
29
import java.lang.reflect.Field;
30
import java.lang.reflect.Method;
31
import java.util.Collection;
32
import java.util.HashMap;
33
import java.util.Iterator;
34
import java.util.Map;
35
import java.util.TreeSet;
36

    
37
/**
38
 * A JSONObject is an unordered collection of name/value pairs. Its
39
 * external form is a string wrapped in curly braces with colons between the
40
 * names and values, and commas between the values and names. The internal form
41
 * is an object having <code>get</code> and <code>opt</code> methods for
42
 * accessing the values by name, and <code>put</code> methods for adding or
43
 * replacing values by name. The values can be any of these types:
44
 * <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
45
 * <code>Number</code>, <code>String</code>, or the <code>JSONObject.NULL</code>
46
 * object. A JSONObject constructor can be used to convert an external form
47
 * JSON text into an internal form whose values can be retrieved with the
48
 * <code>get</code> and <code>opt</code> methods, or to convert values into a
49
 * JSON text using the <code>put</code> and <code>toString</code> methods.
50
 * A <code>get</code> method returns a value if one can be found, and throws an
51
 * exception if one cannot be found. An <code>opt</code> method returns a
52
 * default value instead of throwing an exception, and so is useful for
53
 * obtaining optional values.
54
 * <p>
55
 * The generic <code>get()</code> and <code>opt()</code> methods return an
56
 * object, which you can cast or query for type. There are also typed
57
 * <code>get</code> and <code>opt</code> methods that do type checking and type
58
 * coercion for you.
59
 * <p>
60
 * The <code>put</code> methods adds values to an object. For example, <pre>
61
 *     myString = new JSONObject().put("JSON", "Hello, World!").toString();</pre>
62
 * produces the string <code>{"JSON": "Hello, World"}</code>.
63
 * <p>
64
 * The texts produced by the <code>toString</code> methods strictly conform to
65
 * the JSON syntax rules.
66
 * The constructors are more forgiving in the texts they will accept:
67
 * <ul>
68
 * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
69
 *     before the closing brace.</li>
70
 * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
71
 *     quote)</small>.</li>
72
 * <li>Strings do not need to be quoted at all if they do not begin with a quote
73
 *     or single quote, and if they do not contain leading or trailing spaces,
74
 *     and if they do not contain any of these characters:
75
 *     <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
76
 *     and if they are not the reserved words <code>true</code>,
77
 *     <code>false</code>, or <code>null</code>.</li>
78
 * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as
79
 *     by <code>:</code>.</li>
80
 * <li>Values can be followed by <code>;</code> <small>(semicolon)</small> as
81
 *     well as by <code>,</code> <small>(comma)</small>.</li>
82
 * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
83
 *     <code>0x-</code> <small>(hex)</small> prefix.</li>
84
 * </ul>
85
 * @author JSON.org
86
 * @version 2008-09-18
87
 */
88
public class JSONObject {
89

    
90
    /**
91
     * JSONObject.NULL is equivalent to the value that JavaScript calls null,
92
     * whilst Java's null is equivalent to the value that JavaScript calls
93
     * undefined.
94
     */
95
     private static final class Null {
96

    
97
        /**
98
         * There is only intended to be a single instance of the NULL object,
99
         * so the clone method returns itself.
100
         * @return     NULL.
101
         */
102
        @Override
103
                protected final Object clone() {
104
            return this;
105
        }
106

    
107

    
108
        /**
109
         * A Null object is equal to the null value and to itself.
110
         * @param object    An object to test for nullness.
111
         * @return true if the object parameter is the JSONObject.NULL object
112
         *  or null.
113
         */
114
        @Override
115
                public boolean equals(Object object) {
116
            return object == null || object == this;
117
        }
118

    
119

    
120
        /**
121
         * Get the "null" string value.
122
         * @return The string "null".
123
         */
124
        @Override
125
                public String toString() {
126
            return "null";
127
        }
128
    }
129

    
130

    
131
    /**
132
     * The map where the JSONObject's properties are kept.
133
     */
134
    private Map map;
135

    
136

    
137
    /**
138
     * It is sometimes more convenient and less ambiguous to have a
139
     * <code>NULL</code> object than to use Java's <code>null</code> value.
140
     * <code>JSONObject.NULL.equals(null)</code> returns <code>true</code>.
141
     * <code>JSONObject.NULL.toString()</code> returns <code>"null"</code>.
142
     */
143
    public static final Object NULL = new Null();
144

    
145

    
146
    /**
147
     * Construct an empty JSONObject.
148
     */
149
    public JSONObject() {
150
        map = new HashMap();
151
    }
152

    
153

    
154
    /**
155
     * Construct a JSONObject from a subset of another JSONObject.
156
     * An array of strings is used to identify the keys that should be copied.
157
     * Missing keys are ignored.
158
     * @param jo A JSONObject.
159
     * @param names An array of strings.
160
     * @exception JSONException If a value is a non-finite number or if a name is duplicated.
161
     */
162
    public JSONObject(JSONObject jo, String[] names) throws JSONException {
163
        this();
164
        for (int i = 0; i < names.length; i += 1)
165
                        putOnce(names[i], jo.opt(names[i]));
166
    }
167

    
168

    
169
    /**
170
     * Construct a JSONObject from a JSONTokener.
171
     * @param x A JSONTokener object containing the source string.
172
     * @throws JSONException If there is a syntax error in the source string
173
     *  or a duplicated key.
174
     */
175
    public JSONObject(JSONTokener x) throws JSONException {
176
        this();
177
        char c;
178
        String key;
179

    
180
        if (x.nextClean() != '{')
181
                        throw x.syntaxError("A JSONObject text must begin with '{'");
182
        for (;;) {
183
            c = x.nextClean();
184
            switch (c) {
185
            case 0:
186
                throw x.syntaxError("A JSONObject text must end with '}'");
187
            case '}':
188
                return;
189
            default:
190
                x.back();
191
                key = x.nextValue().toString();
192
            }
193

    
194
            /*
195
             * The key is followed by ':'. We will also tolerate '=' or '=>'.
196
             */
197

    
198
            c = x.nextClean();
199
            if (c == '=') {
200
                if (x.next() != '>')
201
                                        x.back();
202
            } else if (c != ':')
203
                                throw x.syntaxError("Expected a ':' after a key");
204
            putOnce(key, x.nextValue());
205

    
206
            /*
207
             * Pairs are separated by ','. We will also tolerate ';'.
208
             */
209

    
210
            switch (x.nextClean()) {
211
            case ';':
212
            case ',':
213
                if (x.nextClean() == '}')
214
                                        return;
215
                x.back();
216
                break;
217
            case '}':
218
                return;
219
            default:
220
                throw x.syntaxError("Expected a ',' or '}'");
221
            }
222
        }
223
    }
224

    
225

    
226
    /**
227
     * Construct a JSONObject from a Map.
228
     *
229
     * @param aMmap A map object that can be used to initialize the contents of
230
     *  the JSONObject.
231
     */
232
    public JSONObject(Map aMmap) {
233
        map = aMmap == null ? new HashMap() : aMmap;
234
    }
235

    
236
    /**
237
     * Construct a JSONObject from a Map.
238
     *
239
     * Note: Use this constructor when the map contains <key,bean>.
240
     *
241
     * @param aMap - A map with Key-Bean data.
242
     * @param includeSuperClass - Tell whether to include the super class properties.
243
     */
244
    public JSONObject(Map aMap, boolean includeSuperClass) {
245
               map = new HashMap();
246
               if (aMap != null)
247
                        for (Iterator i = aMap.entrySet().iterator(); i.hasNext(); ) {
248
                Map.Entry e = (Map.Entry)i.next();
249
                map.put(e.getKey(), new JSONObject(e.getValue(), includeSuperClass));
250
            }
251
    }
252

    
253

    
254
    /**
255
     * Construct a JSONObject from an Object using bean getters.
256
     * It reflects on all of the public methods of the object.
257
     * For each of the methods with no parameters and a name starting
258
     * with <code>"get"</code> or <code>"is"</code> followed by an uppercase letter,
259
     * the method is invoked, and a key and the value returned from the getter method
260
     * are put into the new JSONObject.
261
     *
262
     * The key is formed by removing the <code>"get"</code> or <code>"is"</code> prefix. If the second remaining
263
     * character is not upper case, then the first
264
     * 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 <code>"Larry Fine"</code>,
268
     * then the JSONObject will contain <code>"name": "Larry Fine"</code>.
269
     *
270
     * @param bean An object that has getter methods that should be used
271
     * to make a JSONObject.
272
     */
273
    public JSONObject(Object bean) {
274
            this();
275
        populateInternalMap(bean, false);
276
    }
277

    
278

    
279
    /**
280
     * Construct JSONObject from the given bean. This will also create JSONObject
281
     * for all internal object (List, Map, Inner Objects) of the provided bean.
282
     *
283
     * -- See Documentation of JSONObject(Object bean) also.
284
     *
285
     * @param bean An object that has getter methods that should be used
286
     * to make a JSONObject.
287
     * @param includeSuperClass - Tell whether to include the super class properties.
288
     */
289
    public JSONObject(Object bean, boolean includeSuperClass) {
290
            this();
291
        populateInternalMap(bean, includeSuperClass);
292
    }
293

    
294
    private void populateInternalMap(Object bean, boolean includeSuperClass){
295
            Class klass = bean.getClass();
296

    
297
        //If klass.getSuperClass is System class then includeSuperClass = false;
298

    
299
            if (klass.getClassLoader() == null)
300
                        includeSuperClass = false;
301

    
302
            Method[] methods = includeSuperClass ?
303
                            klass.getMethods() : klass.getDeclaredMethods();
304
        for (int i = 0; i < methods.length; i += 1)
305
                        try {
306
                Method method = methods[i];
307
                String name = method.getName();
308
                String key = "";
309
                if (name.startsWith("get"))
310
                                        key = name.substring(3);
311
                                else if (name.startsWith("is"))
312
                                        key = name.substring(2);
313
                if (key.length() > 0 &&
314
                        Character.isUpperCase(key.charAt(0)) &&
315
                        method.getParameterTypes().length == 0) {
316
                    if (key.length() == 1)
317
                                                key = key.toLowerCase();
318
                                        else if (!Character.isUpperCase(key.charAt(1)))
319
                                                key = key.substring(0, 1).toLowerCase() +
320
                            key.substring(1);
321

    
322
                    Object result = method.invoke(bean, (Object[])null);
323
                    if (result == null)
324
                                                map.put(key, NULL);
325
                                        else if (result.getClass().isArray())
326
                                                map.put(key, new JSONArray(result,includeSuperClass));
327
                                        else if (result instanceof Collection)
328
                                                map.put(key, new JSONArray((Collection)result,includeSuperClass));
329
                                        else if (result instanceof Map)
330
                                                map.put(key, new JSONObject((Map)result,includeSuperClass));
331
                                        else if (isStandardProperty(result.getClass()))
332
                                                map.put(key, result);
333
                                        else if (result.getClass().getPackage().getName().startsWith("java") ||
334
                                                        result.getClass().getClassLoader() == null)
335
                                                map.put(key, result.toString());
336
                                        else
337
                                                map.put(key, new JSONObject(result,includeSuperClass));
338
                }
339
            } catch (Exception e) {
340
                    throw new RuntimeException(e);
341
            }
342
    }
343

    
344
    private boolean isStandardProperty(Class clazz) {
345
            return clazz.isPrimitive()                  ||
346
                    clazz.isAssignableFrom(Byte.class)      ||
347
                    clazz.isAssignableFrom(Short.class)     ||
348
                    clazz.isAssignableFrom(Integer.class)   ||
349
                    clazz.isAssignableFrom(Long.class)      ||
350
                    clazz.isAssignableFrom(Float.class)     ||
351
                    clazz.isAssignableFrom(Double.class)    ||
352
                    clazz.isAssignableFrom(Character.class) ||
353
                    clazz.isAssignableFrom(String.class)    ||
354
                    clazz.isAssignableFrom(Boolean.class);
355
    }
356

    
357
         /**
358
     * Construct a JSONObject from an Object, using reflection to find the
359
     * public members. The resulting JSONObject's keys will be the strings
360
     * from the names array, and the values will be the field values associated
361
     * with those keys in the object. If a key is not found or not visible,
362
     * then it will not be copied into the new JSONObject.
363
     * @param object An object that has fields that should be used to make a
364
     * JSONObject.
365
     * @param names An array of strings, the names of the fields to be obtained
366
     * from the object.
367
     */
368
    public JSONObject(Object object, String names[]) {
369
        this();
370
        Class c = object.getClass();
371
        for (int i = 0; i < names.length; i += 1) {
372
            String name = names[i];
373
                try {
374
                putOpt(name, c.getField(name).get(object));
375
                } catch (Exception e) {
376
                /* forget about it */
377
            }
378
        }
379
    }
380

    
381

    
382
    /**
383
     * Construct a JSONObject from a source JSON text string.
384
     * This is the most commonly used JSONObject constructor.
385
     * @param source    A string beginning
386
     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
387
     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
388
     * @exception JSONException If there is a syntax error in the source
389
     *  string or a duplicated key.
390
     */
391
    public JSONObject(String source) throws JSONException {
392
        this(new JSONTokener(source));
393
    }
394

    
395

    
396
    /**
397
     * Accumulate values under a key. It is similar to the put method except
398
     * that if there is already an object stored under the key then a
399
     * JSONArray is stored under the key to hold all of the accumulated values.
400
     * If there is already a JSONArray, then the new value is appended to it.
401
     * In contrast, the put method replaces the previous value.
402
     * @param key   A key string.
403
     * @param value An object to be accumulated under the key.
404
     * @return this.
405
     * @throws JSONException If the value is an invalid number
406
     *  or if the key is null.
407
     */
408
    public JSONObject accumulate(String key, Object value)
409
            throws JSONException {
410
        testValidity(value);
411
        Object o = opt(key);
412
        if (o == null)
413
                        put(key, value instanceof JSONArray ?
414
                    new JSONArray().put(value) :
415
                    value);
416
                else if (o instanceof JSONArray)
417
                        ((JSONArray)o).put(value);
418
                else
419
                        put(key, new JSONArray().put(o).put(value));
420
        return this;
421
    }
422

    
423

    
424
    /**
425
     * Append values to the array under a key. If the key does not exist in the
426
     * JSONObject, then the key is put in the JSONObject with its value being a
427
     * JSONArray containing the value parameter. If the key was already
428
     * associated with a JSONArray, then the value parameter is appended to it.
429
     * @param key   A key string.
430
     * @param value An object to be accumulated under the key.
431
     * @return this.
432
     * @throws JSONException If the key is null or if the current value
433
     *  associated with the key is not a JSONArray.
434
     */
435
    public JSONObject append(String key, Object value)
436
            throws JSONException {
437
        testValidity(value);
438
        Object o = opt(key);
439
        if (o == null)
440
                        put(key, new JSONArray().put(value));
441
                else if (o instanceof JSONArray)
442
                        put(key, ((JSONArray)o).put(value));
443
                else
444
                        throw new JSONException("JSONObject[" + key +
445
                    "] is not a JSONArray.");
446
        return this;
447
    }
448

    
449

    
450
    /**
451
     * Produce a string from a double. The string "null" will be returned if
452
     * the number is not finite.
453
     * @param  d A double.
454
     * @return A String.
455
     */
456
    static public String doubleToString(double d) {
457
        if (Double.isInfinite(d) || Double.isNaN(d))
458
                        return "null";
459

    
460
// Shave off trailing zeros and decimal point, if possible.
461

    
462
        String s = Double.toString(d);
463
        if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
464
            while (s.endsWith("0"))
465
                                s = s.substring(0, s.length() - 1);
466
            if (s.endsWith("."))
467
                                s = s.substring(0, s.length() - 1);
468
        }
469
        return s;
470
    }
471

    
472

    
473
    /**
474
     * Get the value object associated with a key.
475
     *
476
     * @param key   A key string.
477
     * @return      The object associated with the key.
478
     * @throws   JSONException if the key is not found.
479
     */
480
    public Object get(String key) throws JSONException {
481
        Object o = opt(key);
482
        if (o == null)
483
                        throw new JSONException("JSONObject[" + quote(key) +
484
                    "] not found.");
485
        return o;
486
    }
487

    
488

    
489
    /**
490
     * Get the boolean value associated with a key.
491
     *
492
     * @param key   A key string.
493
     * @return      The truth.
494
     * @throws   JSONException
495
     *  if the value is not a Boolean or the String "true" or "false".
496
     */
497
    public boolean getBoolean(String key) throws JSONException {
498
        Object o = get(key);
499
        if (o.equals(Boolean.FALSE) ||
500
                o instanceof String &&
501
                ((String)o).equalsIgnoreCase("false"))
502
                        return false;
503
                else if (o.equals(Boolean.TRUE) ||
504
                o instanceof String &&
505
                ((String)o).equalsIgnoreCase("true"))
506
                        return true;
507
        throw new JSONException("JSONObject[" + quote(key) +
508
                "] is not a Boolean.");
509
    }
510

    
511

    
512
    /**
513
     * Get the double value associated with a key.
514
     * @param key   A key string.
515
     * @return      The numeric value.
516
     * @throws JSONException if the key is not found or
517
     *  if the value is not a Number object and cannot be converted to a number.
518
     */
519
    public double getDouble(String key) throws JSONException {
520
        Object o = get(key);
521
        try {
522
            return o instanceof Number ?
523
                ((Number)o).doubleValue() :
524
                Double.valueOf((String)o).doubleValue();
525
        } catch (Exception e) {
526
            throw new JSONException("JSONObject[" + quote(key) +
527
                "] is not a number.");
528
        }
529
    }
530

    
531

    
532
    /**
533
     * Get the int value associated with a key. If the number value is too
534
     * large for an int, it will be clipped.
535
     *
536
     * @param key   A key string.
537
     * @return      The integer value.
538
     * @throws   JSONException if the key is not found or if the value cannot
539
     *  be converted to an integer.
540
     */
541
    public int getInt(String key) throws JSONException {
542
        Object o = get(key);
543
        return o instanceof Number ?
544
                ((Number)o).intValue() : (int)getDouble(key);
545
    }
546

    
547

    
548
    /**
549
     * Get the JSONArray value associated with a key.
550
     *
551
     * @param key   A key string.
552
     * @return      A JSONArray which is the value.
553
     * @throws   JSONException if the key is not found or
554
     *  if the value is not a JSONArray.
555
     */
556
    public JSONArray getJSONArray(String key) throws JSONException {
557
        Object o = get(key);
558
        if (o instanceof JSONArray)
559
                        return (JSONArray)o;
560
        throw new JSONException("JSONObject[" + quote(key) +
561
                "] is not a JSONArray.");
562
    }
563

    
564

    
565
    /**
566
     * Get the JSONObject value associated with a key.
567
     *
568
     * @param key   A key string.
569
     * @return      A JSONObject which is the value.
570
     * @throws   JSONException if the key is not found or
571
     *  if the value is not a JSONObject.
572
     */
573
    public JSONObject getJSONObject(String key) throws JSONException {
574
        Object o = get(key);
575
        if (o instanceof JSONObject)
576
                        return (JSONObject)o;
577
        throw new JSONException("JSONObject[" + quote(key) +
578
                "] is not a JSONObject.");
579
    }
580

    
581

    
582
    /**
583
     * Get the long value associated with a key. If the number value is too
584
     * long for a long, it will be clipped.
585
     *
586
     * @param key   A key string.
587
     * @return      The long value.
588
     * @throws   JSONException if the key is not found or if the value cannot
589
     *  be converted to a long.
590
     */
591
    public long getLong(String key) throws JSONException {
592
        Object o = get(key);
593
        return o instanceof Number ?
594
                ((Number)o).longValue() : (long)getDouble(key);
595
    }
596

    
597

    
598
    /**
599
     * Get an array of field names from a JSONObject.
600
     *
601
     * @return An array of field names, or null if there are no names.
602
     */
603
    public static String[] getNames(JSONObject jo) {
604
            int length = jo.length();
605
            if (length == 0)
606
                        return null;
607
            Iterator i = jo.keys();
608
            String[] names = new String[length];
609
            int j = 0;
610
            while (i.hasNext()) {
611
                    names[j] = (String)i.next();
612
                    j += 1;
613
            }
614
        return names;
615
    }
616

    
617

    
618
    /**
619
     * Get an array of field names from an Object.
620
     *
621
     * @return An array of field names, or null if there are no names.
622
     */
623
    public static String[] getNames(Object object) {
624
            if (object == null)
625
                        return null;
626
            Class klass = object.getClass();
627
            Field[] fields = klass.getFields();
628
            int length = fields.length;
629
            if (length == 0)
630
                        return null;
631
            String[] names = new String[length];
632
            for (int i = 0; i < length; i += 1)
633
                        names[i] = fields[i].getName();
634
        return names;
635
    }
636

    
637

    
638
    /**
639
     * Get the string associated with a key.
640
     *
641
     * @param key   A key string.
642
     * @return      A string which is the value.
643
     * @throws   JSONException if the key is not found.
644
     */
645
    public String getString(String key) throws JSONException {
646
        return get(key).toString();
647
    }
648

    
649

    
650
    /**
651
     * Determine if the JSONObject contains a specific key.
652
     * @param key   A key string.
653
     * @return      true if the key exists in the JSONObject.
654
     */
655
    public boolean has(String key) {
656
        return map.containsKey(key);
657
    }
658

    
659

    
660
    /**
661
     * Determine if the value associated with the key is null or if there is
662
     *  no value.
663
     * @param key   A key string.
664
     * @return      true if there is no value associated with the key or if
665
     *  the value is the JSONObject.NULL object.
666
     */
667
    public boolean isNull(String key) {
668
        return JSONObject.NULL.equals(opt(key));
669
    }
670

    
671

    
672
    /**
673
     * Get an enumeration of the keys of the JSONObject.
674
     *
675
     * @return An iterator of the keys.
676
     */
677
    public Iterator keys() {
678
        return map.keySet().iterator();
679
    }
680

    
681

    
682
    /**
683
     * Get the number of keys stored in the JSONObject.
684
     *
685
     * @return The number of keys in the JSONObject.
686
     */
687
    public int length() {
688
        return map.size();
689
    }
690

    
691

    
692
    /**
693
     * Produce a JSONArray containing the names of the elements of this
694
     * JSONObject.
695
     * @return A JSONArray containing the key strings, or null if the JSONObject
696
     * is empty.
697
     */
698
    public JSONArray names() {
699
        JSONArray ja = new JSONArray();
700
        Iterator  keys = keys();
701
        while (keys.hasNext())
702
                        ja.put(keys.next());
703
        return ja.length() == 0 ? null : ja;
704
    }
705

    
706
    /**
707
     * Produce a string from a Number.
708
     * @param  n A Number
709
     * @return A String.
710
     * @throws JSONException If n is a non-finite number.
711
     */
712
    static public String numberToString(Number n)
713
            throws JSONException {
714
        if (n == null)
715
                        throw new JSONException("Null pointer");
716
        testValidity(n);
717

    
718
// Shave off trailing zeros and decimal point, if possible.
719

    
720
        String s = n.toString();
721
        if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
722
            while (s.endsWith("0"))
723
                                s = s.substring(0, s.length() - 1);
724
            if (s.endsWith("."))
725
                                s = s.substring(0, s.length() - 1);
726
        }
727
        return s;
728
    }
729

    
730

    
731
    /**
732
     * Get an optional value associated with a key.
733
     * @param key   A key string.
734
     * @return      An object which is the value, or null if there is no value.
735
     */
736
    public Object opt(String key) {
737
        return key == null ? null : map.get(key);
738
    }
739

    
740

    
741
    /**
742
     * Get an optional boolean associated with a key.
743
     * It returns false if there is no such key, or if the value is not
744
     * Boolean.TRUE or the String "true".
745
     *
746
     * @param key   A key string.
747
     * @return      The truth.
748
     */
749
    public boolean optBoolean(String key) {
750
        return optBoolean(key, false);
751
    }
752

    
753

    
754
    /**
755
     * Get an optional boolean associated with a key.
756
     * It returns the defaultValue if there is no such key, or if it is not
757
     * a Boolean or the String "true" or "false" (case insensitive).
758
     *
759
     * @param key              A key string.
760
     * @param defaultValue     The default.
761
     * @return      The truth.
762
     */
763
    public boolean optBoolean(String key, boolean defaultValue) {
764
        try {
765
            return getBoolean(key);
766
        } catch (Exception e) {
767
            return defaultValue;
768
        }
769
    }
770

    
771

    
772
    /**
773
     * Put a key/value pair in the JSONObject, where the value will be a
774
     * JSONArray which is produced from a Collection.
775
     * @param key   A key string.
776
     * @param value A Collection value.
777
     * @return      this.
778
     * @throws JSONException
779
     */
780
    public JSONObject put(String key, Collection value) throws JSONException {
781
        put(key, new JSONArray(value));
782
        return this;
783
    }
784

    
785

    
786
    /**
787
     * Get an optional double associated with a key,
788
     * or NaN if there is no such key or if its value is not a number.
789
     * If the value is a string, an attempt will be made to evaluate it as
790
     * a number.
791
     *
792
     * @param key   A string which is the key.
793
     * @return      An object which is the value.
794
     */
795
    public double optDouble(String key) {
796
        return optDouble(key, Double.NaN);
797
    }
798

    
799

    
800
    /**
801
     * Get an optional double associated with a key, or the
802
     * defaultValue if there is no such key or if its value is not a number.
803
     * If the value is a string, an attempt will be made to evaluate it as
804
     * a number.
805
     *
806
     * @param key   A key string.
807
     * @param defaultValue     The default.
808
     * @return      An object which is the value.
809
     */
810
    public double optDouble(String key, double defaultValue) {
811
        try {
812
            Object o = opt(key);
813
            return o instanceof Number ? ((Number)o).doubleValue() :
814
                new Double((String)o).doubleValue();
815
        } catch (Exception e) {
816
            return defaultValue;
817
        }
818
    }
819

    
820

    
821
    /**
822
     * Get an optional int value associated with a key,
823
     * or zero if there is no such key or if the value is not a number.
824
     * If the value is a string, an attempt will be made to evaluate it as
825
     * a number.
826
     *
827
     * @param key   A key string.
828
     * @return      An object which is the value.
829
     */
830
    public int optInt(String key) {
831
        return optInt(key, 0);
832
    }
833

    
834

    
835
    /**
836
     * Get an optional int value associated with a key,
837
     * or the default if there is no such key or if the value is not a number.
838
     * If the value is a string, an attempt will be made to evaluate it as
839
     * a number.
840
     *
841
     * @param key   A key string.
842
     * @param defaultValue     The default.
843
     * @return      An object which is the value.
844
     */
845
    public int optInt(String key, int defaultValue) {
846
        try {
847
            return getInt(key);
848
        } catch (Exception e) {
849
            return defaultValue;
850
        }
851
    }
852

    
853

    
854
    /**
855
     * Get an optional JSONArray associated with a key.
856
     * It returns null if there is no such key, or if its value is not a
857
     * JSONArray.
858
     *
859
     * @param key   A key string.
860
     * @return      A JSONArray which is the value.
861
     */
862
    public JSONArray optJSONArray(String key) {
863
        Object o = opt(key);
864
        return o instanceof JSONArray ? (JSONArray)o : null;
865
    }
866

    
867

    
868
    /**
869
     * Get an optional JSONObject associated with a key.
870
     * It returns null if there is no such key, or if its value is not a
871
     * JSONObject.
872
     *
873
     * @param key   A key string.
874
     * @return      A JSONObject which is the value.
875
     */
876
    public JSONObject optJSONObject(String key) {
877
        Object o = opt(key);
878
        return o instanceof JSONObject ? (JSONObject)o : null;
879
    }
880

    
881

    
882
    /**
883
     * Get an optional long value associated with a key,
884
     * or zero if there is no such key or if the value is not a number.
885
     * If the value is a string, an attempt will be made to evaluate it as
886
     * a number.
887
     *
888
     * @param key   A key string.
889
     * @return      An object which is the value.
890
     */
891
    public long optLong(String key) {
892
        return optLong(key, 0);
893
    }
894

    
895

    
896
    /**
897
     * Get an optional long value associated with a key,
898
     * or the default if there is no such key or if the value is not a number.
899
     * If the value is a string, an attempt will be made to evaluate it as
900
     * a number.
901
     *
902
     * @param key   A key string.
903
     * @param defaultValue     The default.
904
     * @return      An object which is the value.
905
     */
906
    public long optLong(String key, long defaultValue) {
907
        try {
908
            return getLong(key);
909
        } catch (Exception e) {
910
            return defaultValue;
911
        }
912
    }
913

    
914

    
915
    /**
916
     * Get an optional string associated with a key.
917
     * It returns an empty string if there is no such key. If the value is not
918
     * a string and is not null, then it is coverted to a string.
919
     *
920
     * @param key   A key string.
921
     * @return      A string which is the value.
922
     */
923
    public String optString(String key) {
924
        return optString(key, "");
925
    }
926

    
927

    
928
    /**
929
     * Get an optional string associated with a key.
930
     * It returns the defaultValue if there is no such key.
931
     *
932
     * @param key   A key string.
933
     * @param defaultValue     The default.
934
     * @return      A string which is the value.
935
     */
936
    public String optString(String key, String defaultValue) {
937
        Object o = opt(key);
938
        return o != null ? o.toString() : defaultValue;
939
    }
940

    
941

    
942
    /**
943
     * Put a key/boolean pair in the JSONObject.
944
     *
945
     * @param key   A key string.
946
     * @param value A boolean which is the value.
947
     * @return this.
948
     * @throws JSONException If the key is null.
949
     */
950
    public JSONObject put(String key, boolean value) throws JSONException {
951
        put(key, value ? Boolean.TRUE : Boolean.FALSE);
952
        return this;
953
    }
954

    
955

    
956
    /**
957
     * Put a key/double pair in the JSONObject.
958
     *
959
     * @param key   A key string.
960
     * @param value A double which is the value.
961
     * @return this.
962
     * @throws JSONException If the key is null or if the number is invalid.
963
     */
964
    public JSONObject put(String key, double value) throws JSONException {
965
        put(key, new Double(value));
966
        return this;
967
    }
968

    
969

    
970
    /**
971
     * Put a key/int pair in the JSONObject.
972
     *
973
     * @param key   A key string.
974
     * @param value An int which is the value.
975
     * @return this.
976
     * @throws JSONException If the key is null.
977
     */
978
    public JSONObject put(String key, int value) throws JSONException {
979
        put(key, new Integer(value));
980
        return this;
981
    }
982

    
983

    
984
    /**
985
     * Put a key/long pair in the JSONObject.
986
     *
987
     * @param key   A key string.
988
     * @param value A long which is the value.
989
     * @return this.
990
     * @throws JSONException If the key is null.
991
     */
992
    public JSONObject put(String key, long value) throws JSONException {
993
        put(key, new Long(value));
994
        return this;
995
    }
996

    
997

    
998
    /**
999
     * Put a key/value pair in the JSONObject, where the value will be a
1000
     * JSONObject which is produced from a Map.
1001
     * @param key   A key string.
1002
     * @param value A Map value.
1003
     * @return      this.
1004
     * @throws JSONException
1005
     */
1006
    public JSONObject put(String key, Map value) throws JSONException {
1007
        put(key, new JSONObject(value));
1008
        return this;
1009
    }
1010

    
1011

    
1012
    /**
1013
     * Put a key/value pair in the JSONObject. If the value is null,
1014
     * then the key will be removed from the JSONObject if it is present.
1015
     * @param key   A key string.
1016
     * @param value An object which is the value. It should be of one of these
1017
     *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1018
     *  or the JSONObject.NULL object.
1019
     * @return this.
1020
     * @throws JSONException If the value is non-finite number
1021
     *  or if the key is null.
1022
     */
1023
    public JSONObject put(String key, Object value) throws JSONException {
1024
        if (key == null)
1025
                        throw new JSONException("Null key.");
1026
        if (value != null) {
1027
            testValidity(value);
1028
            map.put(key, value);
1029
        } else
1030
                        remove(key);
1031
        return this;
1032
    }
1033

    
1034

    
1035
    /**
1036
     * Put a key/value pair in the JSONObject, but only if the key and the
1037
     * value are both non-null, and only if there is not already a member
1038
     * with that name.
1039
     * @param key
1040
     * @param value
1041
     * @return his.
1042
     * @throws JSONException if the key is a duplicate
1043
     */
1044
    public JSONObject putOnce(String key, Object value) throws JSONException {
1045
        if (key != null && value != null) {
1046
                if (opt(key) != null)
1047
                                throw new JSONException("Duplicate key \"" + key + "\"");
1048
            put(key, value);
1049
        }
1050
        return this;
1051
    }
1052

    
1053

    
1054
    /**
1055
     * Put a key/value pair in the JSONObject, but only if the
1056
     * key and the value are both non-null.
1057
     * @param key   A key string.
1058
     * @param value An object which is the value. It should be of one of these
1059
     *  types: Boolean, Double, Integer, JSONArray, JSONObject, Long, String,
1060
     *  or the JSONObject.NULL object.
1061
     * @return this.
1062
     * @throws JSONException If the value is a non-finite number.
1063
     */
1064
    public JSONObject putOpt(String key, Object value) throws JSONException {
1065
        if (key != null && value != null)
1066
                        put(key, value);
1067
        return this;
1068
    }
1069

    
1070

    
1071
    /**
1072
     * Produce a string in double quotes with backslash sequences in all the
1073
     * right places. A backslash will be inserted within </, allowing JSON
1074
     * text to be delivered in HTML. In JSON text, a string cannot contain a
1075
     * control character or an unescaped quote or backslash.
1076
     * @param string A String
1077
     * @return  A String correctly formatted for insertion in a JSON text.
1078
     */
1079
    public static String quote(String string) {
1080
        if (string == null || string.length() == 0)
1081
                        return "\"\"";
1082

    
1083
        char         b;
1084
        char         c = 0;
1085
        int          i;
1086
        int          len = string.length();
1087
        StringBuffer sb = new StringBuffer(len + 4);
1088
        String       t;
1089

    
1090
        sb.append('"');
1091
        for (i = 0; i < len; i += 1) {
1092
            b = c;
1093
            c = string.charAt(i);
1094
            switch (c) {
1095
            case '\\':
1096
            case '"':
1097
                sb.append('\\');
1098
                sb.append(c);
1099
                break;
1100
            case '/':
1101
                if (b == '<')
1102
                                        sb.append('\\');
1103
                sb.append(c);
1104
                break;
1105
            case '\b':
1106
                sb.append("\\b");
1107
                break;
1108
            case '\t':
1109
                sb.append("\\t");
1110
                break;
1111
            case '\n':
1112
                sb.append("\\n");
1113
                break;
1114
            case '\f':
1115
                sb.append("\\f");
1116
                break;
1117
            case '\r':
1118
                sb.append("\\r");
1119
                break;
1120
            default:
1121
                if (c < ' ' || c >= '\u0080' && c < '\u00a0' ||
1122
                               c >= '\u2000' && c < '\u2100') {
1123
                    t = "000" + Integer.toHexString(c);
1124
                    sb.append("\\u" + t.substring(t.length() - 4));
1125
                } else
1126
                                        sb.append(c);
1127
            }
1128
        }
1129
        sb.append('"');
1130
        return sb.toString();
1131
    }
1132

    
1133
    /**
1134
     * Remove a name and its value, if present.
1135
     * @param key The name to be removed.
1136
     * @return The value that was associated with the name,
1137
     * or null if there was no value.
1138
     */
1139
    public Object remove(String key) {
1140
        return map.remove(key);
1141
    }
1142

    
1143
    /**
1144
     * Get an enumeration of the keys of the JSONObject.
1145
     * The keys will be sorted alphabetically.
1146
     *
1147
     * @return An iterator of the keys.
1148
     */
1149
    public Iterator sortedKeys() {
1150
      return new TreeSet(map.keySet()).iterator();
1151
    }
1152

    
1153
    /**
1154
     * Try to convert a string into a number, boolean, or null. If the string
1155
     * can't be converted, return the string.
1156
     * @param s A String.
1157
     * @return A simple JSON value.
1158
     */
1159
    static public Object stringToValue(String s) {
1160
        if (s.equals(""))
1161
                        return s;
1162
        if (s.equalsIgnoreCase("true"))
1163
                        return Boolean.TRUE;
1164
        if (s.equalsIgnoreCase("false"))
1165
                        return Boolean.FALSE;
1166
        if (s.equalsIgnoreCase("null"))
1167
                        return JSONObject.NULL;
1168

    
1169
        /*
1170
         * If it might be a number, try converting it. We support the 0- and 0x-
1171
         * conventions. If a number cannot be produced, then the value will just
1172
         * be a string. Note that the 0-, 0x-, plus, and implied string
1173
         * conventions are non-standard. A JSON parser is free to accept
1174
         * non-JSON forms as long as it accepts all correct JSON forms.
1175
         */
1176

    
1177
        char b = s.charAt(0);
1178
        if (b >= '0' && b <= '9' || b == '.' || b == '-' || b == '+') {
1179
            if (b == '0')
1180
                                if (s.length() > 2 &&
1181
                        (s.charAt(1) == 'x' || s.charAt(1) == 'X'))
1182
                                        try {
1183
                        return new Integer(Integer.parseInt(s.substring(2),
1184
                                16));
1185
                    } catch (Exception e) {
1186
                        /* Ignore the error */
1187
                    }
1188
                                else
1189
                                        try {
1190
                        return new Integer(Integer.parseInt(s, 8));
1191
                    } catch (Exception e) {
1192
                        /* Ignore the error */
1193
                    }
1194
            try {
1195
                return new Integer(s);
1196
            } catch (Exception e) {
1197
                try {
1198
                    return new Long(s);
1199
                } catch (Exception f) {
1200
                    try {
1201
                        return new Double(s);
1202
                    }  catch (Exception g) {
1203
                            /* Ignore the error */
1204
                    }
1205
                }
1206
            }
1207
        }
1208
        return s;
1209
    }
1210

    
1211

    
1212
    /**
1213
     * Throw an exception if the object is an NaN or infinite number.
1214
     * @param o The object to test.
1215
     * @throws JSONException If o is a non-finite number.
1216
     */
1217
    static void testValidity(Object o) throws JSONException {
1218
        if (o != null)
1219
                        if (o instanceof Double) {
1220
                if (((Double)o).isInfinite() || ((Double)o).isNaN())
1221
                                        throw new JSONException(
1222
                        "JSON does not allow non-finite numbers.");
1223
            } else if (o instanceof Float)
1224
                                if (((Float)o).isInfinite() || ((Float)o).isNaN())
1225
                                        throw new JSONException(
1226
                        "JSON does not allow non-finite numbers.");
1227
    }
1228

    
1229

    
1230
    /**
1231
     * Produce a JSONArray containing the values of the members of this
1232
     * JSONObject.
1233
     * @param names A JSONArray containing a list of key strings. This
1234
     * determines the sequence of the values in the result.
1235
     * @return A JSONArray of values.
1236
     * @throws JSONException If any of the values are non-finite numbers.
1237
     */
1238
    public JSONArray toJSONArray(JSONArray names) throws JSONException {
1239
        if (names == null || names.length() == 0)
1240
                        return null;
1241
        JSONArray ja = new JSONArray();
1242
        for (int i = 0; i < names.length(); i += 1)
1243
                        ja.put(opt(names.getString(i)));
1244
        return ja;
1245
    }
1246

    
1247
    /**
1248
     * Make a JSON text of this JSONObject. For compactness, no whitespace
1249
     * is added. If this would not result in a syntactically correct JSON text,
1250
     * then null will be returned instead.
1251
     * <p>
1252
     * Warning: This method assumes that the data structure is acyclical.
1253
     *
1254
     * @return a printable, displayable, portable, transmittable
1255
     *  representation of the object, beginning
1256
     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1257
     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1258
     */
1259
    @Override
1260
        public String toString() {
1261
        try {
1262
            Iterator     keys = keys();
1263
            StringBuffer sb = new StringBuffer("{");
1264

    
1265
            while (keys.hasNext()) {
1266
                if (sb.length() > 1)
1267
                                        sb.append(',');
1268
                Object o = keys.next();
1269
                sb.append(quote(o.toString()));
1270
                sb.append(':');
1271
                sb.append(valueToString(map.get(o)));
1272
            }
1273
            sb.append('}');
1274
            return sb.toString();
1275
        } catch (Exception e) {
1276
            return null;
1277
        }
1278
    }
1279

    
1280

    
1281
    /**
1282
     * Make a prettyprinted JSON text of this JSONObject.
1283
     * <p>
1284
     * Warning: This method assumes that the data structure is acyclical.
1285
     * @param indentFactor The number of spaces to add to each level of
1286
     *  indentation.
1287
     * @return a printable, displayable, portable, transmittable
1288
     *  representation of the object, beginning
1289
     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1290
     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1291
     * @throws JSONException If the object contains an invalid number.
1292
     */
1293
    public String toString(int indentFactor) throws JSONException {
1294
        return toString(indentFactor, 0);
1295
    }
1296

    
1297

    
1298
    /**
1299
     * Make a prettyprinted JSON text of this JSONObject.
1300
     * <p>
1301
     * Warning: This method assumes that the data structure is acyclical.
1302
     * @param indentFactor The number of spaces to add to each level of
1303
     *  indentation.
1304
     * @param indent The indentation of the top level.
1305
     * @return a printable, displayable, transmittable
1306
     *  representation of the object, beginning
1307
     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1308
     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1309
     * @throws JSONException If the object contains an invalid number.
1310
     */
1311
    String toString(int indentFactor, int indent) throws JSONException {
1312
        int j;
1313
        int n = length();
1314
        if (n == 0)
1315
                        return "{}";
1316
        Iterator     keys = sortedKeys();
1317
        StringBuffer sb = new StringBuffer("{");
1318
        int          newindent = indent + indentFactor;
1319
        Object       o;
1320
        if (n == 1) {
1321
            o = keys.next();
1322
            sb.append(quote(o.toString()));
1323
            sb.append(": ");
1324
            sb.append(valueToString(map.get(o), indentFactor,
1325
                    indent));
1326
        } else {
1327
            while (keys.hasNext()) {
1328
                o = keys.next();
1329
                if (sb.length() > 1)
1330
                                        sb.append(",\n");
1331
                                else
1332
                                        sb.append('\n');
1333
                for (j = 0; j < newindent; j += 1)
1334
                                        sb.append(' ');
1335
                sb.append(quote(o.toString()));
1336
                sb.append(": ");
1337
                sb.append(valueToString(map.get(o), indentFactor,
1338
                        newindent));
1339
            }
1340
            if (sb.length() > 1) {
1341
                sb.append('\n');
1342
                for (j = 0; j < indent; j += 1)
1343
                                        sb.append(' ');
1344
            }
1345
        }
1346
        sb.append('}');
1347
        return sb.toString();
1348
    }
1349

    
1350

    
1351
    /**
1352
     * Make a JSON text of an Object value. If the object has an
1353
     * value.toJSONString() method, then that method will be used to produce
1354
     * the JSON text. The method is required to produce a strictly
1355
     * conforming text. If the object does not contain a toJSONString
1356
     * method (which is the most common case), then a text will be
1357
     * produced by other means. If the value is an array or Collection,
1358
     * then a JSONArray will be made from it and its toJSONString method
1359
     * will be called. If the value is a MAP, then a JSONObject will be made
1360
     * from it and its toJSONString method will be called. Otherwise, the
1361
     * value's toString method will be called, and the result will be quoted.
1362
     *
1363
     * <p>
1364
     * Warning: This method assumes that the data structure is acyclical.
1365
     * @param value The value to be serialized.
1366
     * @return a printable, displayable, transmittable
1367
     *  representation of the object, beginning
1368
     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1369
     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1370
     * @throws JSONException If the value is or contains an invalid number.
1371
     */
1372
    static String valueToString(Object value) throws JSONException {
1373
        if (value == null || value.equals(null))
1374
                        return "null";
1375
        if (value instanceof JSONString) {
1376
            Object o;
1377
            try {
1378
                o = ((JSONString)value).toJSONString();
1379
            } catch (Exception e) {
1380
                throw new JSONException(e);
1381
            }
1382
            if (o instanceof String)
1383
                                return (String)o;
1384
            throw new JSONException("Bad value from toJSONString: " + o);
1385
        }
1386
        if (value instanceof Number)
1387
                        return numberToString((Number) value);
1388
        if (value instanceof Boolean || value instanceof JSONObject ||
1389
                value instanceof JSONArray)
1390
                        return value.toString();
1391
        if (value instanceof Map)
1392
                        return new JSONObject((Map)value).toString();
1393
        if (value instanceof Collection)
1394
                        return new JSONArray((Collection)value).toString();
1395
        if (value.getClass().isArray())
1396
                        return new JSONArray(value).toString();
1397
        return quote(value.toString());
1398
    }
1399

    
1400

    
1401
    /**
1402
     * Make a prettyprinted JSON text of an object value.
1403
     * <p>
1404
     * Warning: This method assumes that the data structure is acyclical.
1405
     * @param value The value to be serialized.
1406
     * @param indentFactor The number of spaces to add to each level of
1407
     *  indentation.
1408
     * @param indent The indentation of the top level.
1409
     * @return a printable, displayable, transmittable
1410
     *  representation of the object, beginning
1411
     *  with <code>{</code>&nbsp;<small>(left brace)</small> and ending
1412
     *  with <code>}</code>&nbsp;<small>(right brace)</small>.
1413
     * @throws JSONException If the object contains an invalid number.
1414
     */
1415
     static String valueToString(Object value, int indentFactor, int indent)
1416
            throws JSONException {
1417
        if (value == null || value.equals(null))
1418
                        return "null";
1419
        try {
1420
            if (value instanceof JSONString) {
1421
                Object o = ((JSONString)value).toJSONString();
1422
                if (o instanceof String)
1423
                                        return (String)o;
1424
            }
1425
        } catch (Exception e) {
1426
            /* forget about it */
1427
        }
1428
        if (value instanceof Number)
1429
                        return numberToString((Number) value);
1430
        if (value instanceof Boolean)
1431
                        return value.toString();
1432
        if (value instanceof JSONObject)
1433
                        return ((JSONObject)value).toString(indentFactor, indent);
1434
        if (value instanceof JSONArray)
1435
                        return ((JSONArray)value).toString(indentFactor, indent);
1436
        if (value instanceof Map)
1437
                        return new JSONObject((Map)value).toString(indentFactor, indent);
1438
        if (value instanceof Collection)
1439
                        return new JSONArray((Collection)value).toString(indentFactor, indent);
1440
        if (value.getClass().isArray())
1441
                        return new JSONArray(value).toString(indentFactor, indent);
1442
        return quote(value.toString());
1443
    }
1444

    
1445

    
1446
     /**
1447
      * Write the contents of the JSONObject as JSON text to a writer.
1448
      * For compactness, no whitespace is added.
1449
      * <p>
1450
      * Warning: This method assumes that the data structure is acyclical.
1451
      *
1452
      * @return The writer.
1453
      * @throws JSONException
1454
      */
1455
     public Writer write(Writer writer) throws JSONException {
1456
        try {
1457
            boolean  b = false;
1458
            Iterator keys = keys();
1459
            writer.write('{');
1460

    
1461
            while (keys.hasNext()) {
1462
                if (b)
1463
                                        writer.write(',');
1464
                Object k = keys.next();
1465
                writer.write(quote(k.toString()));
1466
                writer.write(':');
1467
                Object v = map.get(k);
1468
                if (v instanceof JSONObject)
1469
                                        ((JSONObject)v).write(writer);
1470
                                else if (v instanceof JSONArray)
1471
                                        ((JSONArray)v).write(writer);
1472
                                else
1473
                                        writer.write(valueToString(v));
1474
                b = true;
1475
            }
1476
            writer.write('}');
1477
            return writer;
1478
        } catch (IOException e) {
1479
            throw new JSONException(e);
1480
        }
1481
     }
1482
}