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