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