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