Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||||||
JSONObject |
|
| 3.2857142857142856;3.286 |
1 | package org.apache.tapestry.json; |
|
2 | ||
3 | /* |
|
4 | Copyright (c) 2002 JSON.org |
|
5 | ||
6 | Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 | of this software and associated documentation files (the "Software"), to deal |
|
8 | in the Software without restriction, including without limitation the rights |
|
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 | copies of the Software, and to permit persons to whom the Software is |
|
11 | furnished to do so, subject to the following conditions: |
|
12 | ||
13 | The above copyright notice and this permission notice shall be included in all |
|
14 | copies or substantial portions of the Software. |
|
15 | ||
16 | The Software shall be used for Good, not Evil. |
|
17 | ||
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
24 | SOFTWARE. |
|
25 | */ |
|
26 | ||
27 | import java.text.ParseException; |
|
28 | import java.util.*; |
|
29 | ||
30 | /** |
|
31 | * A JSONObject is an unordered collection of name/value pairs. Its external |
|
32 | * form is a string wrapped in curly braces with colons between the names and |
|
33 | * values, and commas between the values and names. The internal form is an |
|
34 | * object having get() and opt() methods for accessing the values by name, and |
|
35 | * put() methods for adding or replacing values by name. The values can be any |
|
36 | * of these types: Boolean, JSONArray, JSONObject, Number, String, or the |
|
37 | * JSONObject.NULL object. |
|
38 | * <p> |
|
39 | * The constructor can convert an external form string into an internal form |
|
40 | * Java object. The toString() method creates an external form string. |
|
41 | * <p> |
|
42 | * A get() method returns a value if one can be found, and throws an exception |
|
43 | * if one cannot be found. An opt() method returns a default value instead of |
|
44 | * throwing an exception, and so is useful for obtaining optional values. |
|
45 | * <p> |
|
46 | * The generic get() and opt() methods return an object, which you can cast or |
|
47 | * query for type. There are also typed get() and opt() methods that do type |
|
48 | * checking and type coersion for you. |
|
49 | * <p> |
|
50 | * The texts produced by the toString() methods are very strict. The |
|
51 | * constructors are more forgiving in the texts they will accept: |
|
52 | * <ul> |
|
53 | * <li>An extra <code>,</code> <small>(comma)</small> may appear just |
|
54 | * before the closing brace.</li> |
|
55 | * <li>Strings may be quoted with <code>'</code> <small>(single quote)</small>.</li> |
|
56 | * <li>Strings do not need to be quoted at all if they do not begin with a |
|
57 | * quote or single quote, and if they do not contain leading or trailing spaces, |
|
58 | * and if they do not contain any of these characters: |
|
59 | * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and |
|
60 | * if they are not the reserved words <code>true</code>, <code>false</code>, |
|
61 | * or <code>null</code>.</li> |
|
62 | * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as |
|
63 | * by <code>:</code></li> |
|
64 | * <li>Values can be followed by <code>;</code> as well as by <code>,</code></li> |
|
65 | * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or |
|
66 | * <code>0x-</code> <small>(hex)</small> prefix.</li> |
|
67 | * <li>Line comments can begin with <code>#</code></li> |
|
68 | * </ul> |
|
69 | * |
|
70 | * @author JSON.org |
|
71 | * @version 1 |
|
72 | */ |
|
73 | public class JSONObject |
|
74 | { |
|
75 | ||
76 | /** |
|
77 | * It is sometimes more convenient and less ambiguous to have a NULL object |
|
78 | * than to use Java's null value. JSONObject.NULL.equals(null) returns true. |
|
79 | * JSONObject.NULL.toString() returns "null". |
|
80 | */ |
|
81 | 1 | public static final Object NULL = new Null(); |
82 | ||
83 | /** |
|
84 | * JSONObject.NULL is equivalent to the value that JavaScript calls null, |
|
85 | * whilst Java's null is equivalent to the value that JavaScript calls |
|
86 | * undefined. |
|
87 | */ |
|
88 | 2 | private static final class Null |
89 | { |
|
90 | ||
91 | /** |
|
92 | * There is only intended to be a single instance of the NULL object, so |
|
93 | * the clone method returns itself. |
|
94 | * CHECKSTYLE:OFF |
|
95 | * @return NULL. |
|
96 | */ |
|
97 | protected Object clone() |
|
98 | { |
|
99 | 0 | return this; |
100 | } |
|
101 | ||
102 | /** |
|
103 | * A Null object is equal to the null value and to itself. |
|
104 | * |
|
105 | * @param object |
|
106 | * An object to test for nullness. |
|
107 | * @return true if the object parameter is the JSONObject.NULL object or |
|
108 | * null. |
|
109 | */ |
|
110 | public boolean equals(Object object) |
|
111 | { |
|
112 | 0 | return object == null || object == this; |
113 | } |
|
114 | ||
115 | /** |
|
116 | * Get the "null" string value. |
|
117 | * |
|
118 | * @return The string "null". |
|
119 | */ |
|
120 | public String toString() |
|
121 | { |
|
122 | 0 | return "null"; |
123 | } |
|
124 | } |
|
125 | ||
126 | /** |
|
127 | * The hash map where the JSONObject's properties are kept. |
|
128 | */ |
|
129 | private HashMap myHashMap; |
|
130 | ||
131 | /** |
|
132 | * Construct an empty JSONObject. |
|
133 | */ |
|
134 | public JSONObject() |
|
135 | 148 | { |
136 | 148 | this.myHashMap = new LinkedHashMap(); |
137 | 148 | } |
138 | ||
139 | /** |
|
140 | * Construct a JSONObject from a subset of another JSONObject. An array of |
|
141 | * strings is used to identify the keys that should be copied. Missing keys |
|
142 | * are ignored. |
|
143 | * |
|
144 | * @param jo |
|
145 | * A JSONObject. |
|
146 | * @param sa |
|
147 | * An array of strings. |
|
148 | */ |
|
149 | public JSONObject(JSONObject jo, String[] sa) |
|
150 | { |
|
151 | 0 | this(); |
152 | 0 | for(int i = 0; i < sa.length; i += 1) |
153 | { |
|
154 | 0 | putOpt(sa[i], jo.opt(sa[i])); |
155 | } |
|
156 | 0 | } |
157 | ||
158 | /** |
|
159 | * Construct a JSONObject from a JSONTokener. |
|
160 | * |
|
161 | * @param x |
|
162 | * A JSONTokener object containing the source string. |
|
163 | * @throws ParseException |
|
164 | * if there is a syntax error in the source string. |
|
165 | */ |
|
166 | public JSONObject(JSONTokener x) |
|
167 | throws ParseException |
|
168 | { |
|
169 | 1 | this(); |
170 | char c; |
|
171 | String key; |
|
172 | ||
173 | 1 | if (x.nextClean() != '{') { throw x |
174 | .syntaxError("A JSONObject must begin with '{'"); } |
|
175 | while(true) |
|
176 | { |
|
177 | 1 | c = x.nextClean(); |
178 | 1 | switch(c) |
179 | { |
|
180 | case 0: |
|
181 | 0 | throw x.syntaxError("A JSONObject must end with '}'"); |
182 | case '}': |
|
183 | 0 | return; |
184 | default: |
|
185 | 1 | x.back(); |
186 | 1 | key = x.nextValue().toString(); |
187 | } |
|
188 | ||
189 | /* |
|
190 | * The key is followed by ':'. We will also tolerate '=' or '=>'. |
|
191 | */ |
|
192 | ||
193 | 1 | c = x.nextClean(); |
194 | 1 | if (c == '=') |
195 | { |
|
196 | 0 | if (x.next() != '>') |
197 | { |
|
198 | 0 | x.back(); |
199 | } |
|
200 | } |
|
201 | 1 | else if (c != ':') { throw x |
202 | .syntaxError("Expected a ':' after a key"); } |
|
203 | 1 | this.myHashMap.put(key, x.nextValue()); |
204 | ||
205 | /* |
|
206 | * Pairs are separated by ','. We will also tolerate ';'. |
|
207 | */ |
|
208 | ||
209 | 1 | switch(x.nextClean()) |
210 | { |
|
211 | case ';': |
|
212 | case ',': |
|
213 | 0 | if (x.nextClean() == '}') { return; } |
214 | 0 | x.back(); |
215 | 0 | break; |
216 | case '}': |
|
217 | 0 | return; |
218 | default: |
|
219 | 1 | throw x.syntaxError("Expected a ',' or '}'"); |
220 | } |
|
221 | } |
|
222 | } |
|
223 | ||
224 | /** |
|
225 | * Construct a JSONObject from a Map. |
|
226 | * |
|
227 | * @param map |
|
228 | * A map object that can be used to initialize the contents of |
|
229 | * the JSONObject. |
|
230 | */ |
|
231 | public JSONObject(Map map) |
|
232 | 0 | { |
233 | 0 | this.myHashMap = new HashMap(map); |
234 | 0 | } |
235 | ||
236 | /** |
|
237 | * Construct a JSONObject from a string. This is the most commonly used |
|
238 | * JSONObject constructor. |
|
239 | * |
|
240 | * @param string |
|
241 | * A string beginning with <code>{</code> <small>(left |
|
242 | * brace)</small> and ending with <code>}</code> <small>(right |
|
243 | * brace)</small>. |
|
244 | * @exception ParseException |
|
245 | * The string must be properly formatted. |
|
246 | */ |
|
247 | public JSONObject(String string) |
|
248 | throws ParseException |
|
249 | { |
|
250 | 1 | this(new JSONTokener(string)); |
251 | 0 | } |
252 | ||
253 | public JSONObject accumulate(String key, Object value) |
|
254 | { |
|
255 | JSONArray a; |
|
256 | 58 | Object o = opt(key); |
257 | 58 | if (o == null) |
258 | { |
|
259 | 52 | a = new JSONArray(); |
260 | 52 | a.put(value); |
261 | 52 | put(key, a); |
262 | } |
|
263 | 6 | else if (o instanceof JSONArray) |
264 | { |
|
265 | 6 | a = (JSONArray) o; |
266 | 6 | a.put(value); |
267 | } |
|
268 | ||
269 | 58 | return this; |
270 | } |
|
271 | ||
272 | public Object get(String key) |
|
273 | { |
|
274 | 52 | Object o = opt(key); |
275 | 52 | if (o == null) { throw new NoSuchElementException("JSONObject[" |
276 | + quote(key) + "] not found."); } |
|
277 | 52 | return o; |
278 | } |
|
279 | ||
280 | public boolean getBoolean(String key) |
|
281 | { |
|
282 | 0 | Object o = get(key); |
283 | 0 | if (o.equals(Boolean.FALSE) |
284 | || (o instanceof String && ((String) o) |
|
285 | .equalsIgnoreCase("false"))) |
|
286 | { |
|
287 | 0 | return false; |
288 | } |
|
289 | 0 | else if (o.equals(Boolean.TRUE) |
290 | || (o instanceof String && ((String) o) |
|
291 | 0 | .equalsIgnoreCase("true"))) { return true; } |
292 | 0 | throw new ClassCastException("JSONObject[" + quote(key) |
293 | + "] is not a Boolean."); |
|
294 | } |
|
295 | ||
296 | public double getDouble(String key) |
|
297 | { |
|
298 | 0 | Object o = get(key); |
299 | 0 | if (o instanceof Number) { return ((Number) o).doubleValue(); } |
300 | 0 | if (o instanceof String) { return new Double((String) o).doubleValue(); } |
301 | 0 | throw new NumberFormatException("JSONObject[" + quote(key) |
302 | + "] is not a number."); |
|
303 | } |
|
304 | ||
305 | /** |
|
306 | * Get the Map the holds that contents of the JSONObject. |
|
307 | * |
|
308 | * @return The getHashMap. |
|
309 | */ |
|
310 | Map getMap() |
|
311 | { |
|
312 | 0 | return this.myHashMap; |
313 | } |
|
314 | ||
315 | public int getInt(String key) |
|
316 | { |
|
317 | 0 | Object o = get(key); |
318 | 0 | return o instanceof Number ? ((Number) o).intValue() |
319 | : (int) getDouble(key); |
|
320 | } |
|
321 | ||
322 | public JSONArray getJSONArray(String key) |
|
323 | { |
|
324 | 0 | Object o = get(key); |
325 | 0 | if (o instanceof JSONArray) { return (JSONArray) o; } |
326 | 0 | throw new NoSuchElementException("JSONObject[" + quote(key) |
327 | + "] is not a JSONArray."); |
|
328 | } |
|
329 | ||
330 | public JSONObject getJSONObject(String key) |
|
331 | { |
|
332 | 46 | Object o = get(key); |
333 | 46 | if (o instanceof JSONObject) { return (JSONObject) o; } |
334 | 0 | throw new NoSuchElementException("JSONObject[" + quote(key) |
335 | + "] is not a JSONObject."); |
|
336 | } |
|
337 | ||
338 | public String getString(String key) |
|
339 | { |
|
340 | 0 | return get(key).toString(); |
341 | } |
|
342 | ||
343 | public boolean has(String key) |
|
344 | { |
|
345 | 50 | return this.myHashMap.containsKey(key); |
346 | } |
|
347 | ||
348 | public boolean isNull(String key) |
|
349 | { |
|
350 | 0 | return JSONObject.NULL.equals(opt(key)); |
351 | } |
|
352 | ||
353 | public Iterator keys() |
|
354 | { |
|
355 | 87 | return this.myHashMap.keySet().iterator(); |
356 | } |
|
357 | ||
358 | public int length() |
|
359 | { |
|
360 | 1 | return this.myHashMap.size(); |
361 | } |
|
362 | ||
363 | public JSONArray names() |
|
364 | { |
|
365 | 0 | JSONArray ja = new JSONArray(); |
366 | 0 | Iterator keys = keys(); |
367 | 0 | while(keys.hasNext()) |
368 | { |
|
369 | 0 | ja.put(keys.next()); |
370 | } |
|
371 | 0 | return ja.length() == 0 ? null : ja; |
372 | } |
|
373 | ||
374 | /** |
|
375 | * Produce a string from a number. |
|
376 | * |
|
377 | * @param n A Number |
|
378 | * @return A String. |
|
379 | * @exception ArithmeticException |
|
380 | * JSON can only serialize finite numbers. |
|
381 | */ |
|
382 | public static String numberToString(Number n) |
|
383 | { |
|
384 | 7 | if ((n instanceof Float && (((Float) n).isInfinite() || ((Float) n).isNaN())) |
385 | || (n instanceof Double && (((Double) n).isInfinite() || ((Double) n).isNaN()))) { |
|
386 | 0 | throw new ArithmeticException("JSON can only serialize finite numbers."); |
387 | } |
|
388 | ||
389 | // Shave off trailing zeros and decimal point, if possible. |
|
390 | ||
391 | 7 | String s = n.toString(); |
392 | 7 | if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) |
393 | { |
|
394 | 0 | while(s.endsWith("0")) |
395 | { |
|
396 | 0 | s = s.substring(0, s.length() - 1); |
397 | } |
|
398 | 0 | if (s.endsWith(".")) |
399 | { |
|
400 | 0 | s = s.substring(0, s.length() - 1); |
401 | } |
|
402 | } |
|
403 | 7 | return s; |
404 | } |
|
405 | ||
406 | public Object opt(String key) |
|
407 | { |
|
408 | 110 | if (key == null) { throw new NullPointerException("Null key"); } |
409 | 110 | return this.myHashMap.get(key); |
410 | } |
|
411 | ||
412 | public boolean optBoolean(String key) |
|
413 | { |
|
414 | 0 | return optBoolean(key, false); |
415 | } |
|
416 | ||
417 | public boolean optBoolean(String key, boolean defaultValue) |
|
418 | { |
|
419 | 0 | Object o = opt(key); |
420 | 0 | if (o != null) |
421 | { |
|
422 | 0 | if (o.equals(Boolean.FALSE) |
423 | || (o instanceof String && ((String) o) |
|
424 | .equalsIgnoreCase("false"))) |
|
425 | { |
|
426 | 0 | return false; |
427 | } |
|
428 | 0 | else if (o.equals(Boolean.TRUE) |
429 | || (o instanceof String && ((String) o) |
|
430 | 0 | .equalsIgnoreCase("true"))) { return true; } |
431 | } |
|
432 | 0 | return defaultValue; |
433 | } |
|
434 | ||
435 | public double optDouble(String key) |
|
436 | { |
|
437 | 0 | return optDouble(key, Double.NaN); |
438 | } |
|
439 | ||
440 | public double optDouble(String key, double defaultValue) |
|
441 | { |
|
442 | 0 | Object o = opt(key); |
443 | 0 | if (o != null) |
444 | { |
|
445 | 0 | if (o instanceof Number) { return ((Number) o).doubleValue(); } |
446 | try |
|
447 | { |
|
448 | 0 | return new Double((String) o).doubleValue(); |
449 | } |
|
450 | 0 | catch (Exception e) |
451 | { |
|
452 | 0 | return defaultValue; |
453 | } |
|
454 | } |
|
455 | 0 | return defaultValue; |
456 | } |
|
457 | ||
458 | public int optInt(String key) |
|
459 | { |
|
460 | 0 | return optInt(key, 0); |
461 | } |
|
462 | ||
463 | public int optInt(String key, int defaultValue) |
|
464 | { |
|
465 | 0 | Object o = opt(key); |
466 | 0 | if (o != null) |
467 | { |
|
468 | 0 | if (o instanceof Number) { return ((Number) o).intValue(); } |
469 | try |
|
470 | { |
|
471 | 0 | return Integer.parseInt((String) o); |
472 | } |
|
473 | 0 | catch (Exception e) |
474 | { |
|
475 | 0 | return defaultValue; |
476 | } |
|
477 | } |
|
478 | 0 | return defaultValue; |
479 | } |
|
480 | ||
481 | public JSONArray optJSONArray(String key) |
|
482 | { |
|
483 | 0 | Object o = opt(key); |
484 | 0 | return o instanceof JSONArray ? (JSONArray) o : null; |
485 | } |
|
486 | ||
487 | public JSONObject optJSONObject(String key) |
|
488 | { |
|
489 | 0 | Object o = opt(key); |
490 | 0 | return o instanceof JSONObject ? (JSONObject) o : null; |
491 | } |
|
492 | ||
493 | public String optString(String key) |
|
494 | { |
|
495 | 0 | return optString(key, ""); |
496 | } |
|
497 | ||
498 | public String optString(String key, String defaultValue) |
|
499 | { |
|
500 | 0 | Object o = opt(key); |
501 | 0 | return o != null ? o.toString() : defaultValue; |
502 | } |
|
503 | ||
504 | public JSONObject put(Object key, boolean value) |
|
505 | { |
|
506 | 11 | put(key, Boolean.valueOf(value)); |
507 | 11 | return this; |
508 | } |
|
509 | ||
510 | public JSONObject put(Object key, double value) |
|
511 | { |
|
512 | 0 | put(key, new Double(value)); |
513 | 0 | return this; |
514 | } |
|
515 | ||
516 | public JSONObject put(Object key, long value) |
|
517 | { |
|
518 | 1 | put(key, new Long(value)); |
519 | 1 | return this; |
520 | } |
|
521 | ||
522 | public JSONObject put(Object key, int value) |
|
523 | { |
|
524 | 6 | put(key, new Integer(value)); |
525 | 6 | return this; |
526 | } |
|
527 | ||
528 | public JSONObject put(Object key, Object value) |
|
529 | { |
|
530 | 153 | if (key == null) |
531 | 0 | throw new NullPointerException("Null key."); |
532 | ||
533 | 153 | if (value != null) |
534 | { |
|
535 | 152 | this.myHashMap.put(key, value); |
536 | } |
|
537 | else |
|
538 | { |
|
539 | 1 | remove(key.toString()); |
540 | } |
|
541 | ||
542 | 153 | return this; |
543 | } |
|
544 | ||
545 | public JSONObject putOpt(String key, Object value) |
|
546 | { |
|
547 | 0 | if (value != null) |
548 | { |
|
549 | 0 | put(key, value); |
550 | } |
|
551 | 0 | return this; |
552 | } |
|
553 | ||
554 | /** |
|
555 | * @see #quote(String) . |
|
556 | * @param value |
|
557 | * |
|
558 | * @return The character quoted. |
|
559 | */ |
|
560 | public static String quote(char value) |
|
561 | { |
|
562 | 10 | return quote(new String(new char[]{value})); |
563 | } |
|
564 | ||
565 | /** |
|
566 | * Produce a string in double quotes with backslash sequences in all the |
|
567 | * right places. |
|
568 | * |
|
569 | * @param string |
|
570 | * A String |
|
571 | * @return A String correctly formatted for insertion in a JSON message. |
|
572 | */ |
|
573 | public static String quote(String string) |
|
574 | { |
|
575 | 227 | if (string == null || string.length() == 0) { return "\"\""; } |
576 | ||
577 | char b; |
|
578 | 227 | char c = 0; |
579 | int i; |
|
580 | 227 | int len = string.length(); |
581 | 227 | StringBuffer sb = new StringBuffer(len + 4); |
582 | String t; |
|
583 | ||
584 | 227 | sb.append('"'); |
585 | 2271 | for(i = 0; i < len; i += 1) |
586 | { |
|
587 | 2044 | b = c; |
588 | 2044 | c = string.charAt(i); |
589 | 2044 | switch(c) |
590 | { |
|
591 | case '\\': |
|
592 | case '"': |
|
593 | 10 | sb.append('\\'); |
594 | 10 | sb.append(c); |
595 | 10 | break; |
596 | case '/': |
|
597 | 38 | if (b == '<') |
598 | { |
|
599 | 0 | sb.append('\\'); |
600 | } |
|
601 | 38 | sb.append(c); |
602 | 38 | break; |
603 | case '\b': |
|
604 | 0 | sb.append("\\b"); |
605 | 0 | break; |
606 | case '\t': |
|
607 | 0 | sb.append("\\t"); |
608 | 0 | break; |
609 | case '\n': |
|
610 | 0 | sb.append("\\n"); |
611 | 0 | break; |
612 | case '\f': |
|
613 | 0 | sb.append("\\f"); |
614 | 0 | break; |
615 | case '\r': |
|
616 | 0 | sb.append("\\r"); |
617 | 0 | break; |
618 | default: |
|
619 | 1996 | if (c < ' ') |
620 | { |
|
621 | 0 | t = "000" + Integer.toHexString(c); |
622 | 0 | sb.append("\\u" + t.substring(t.length() - 4)); |
623 | } |
|
624 | else |
|
625 | { |
|
626 | 1996 | sb.append(c); |
627 | } |
|
628 | } |
|
629 | } |
|
630 | 227 | sb.append('"'); |
631 | 227 | return sb.toString(); |
632 | } |
|
633 | ||
634 | public Object remove(String key) |
|
635 | { |
|
636 | 1 | return this.myHashMap.remove(key); |
637 | } |
|
638 | ||
639 | public JSONArray toJSONArray(JSONArray names) |
|
640 | { |
|
641 | 0 | if (names == null || names.length() == 0) { return null; } |
642 | 0 | JSONArray ja = new JSONArray(); |
643 | 0 | for(int i = 0; i < names.length(); i += 1) |
644 | { |
|
645 | 0 | ja.put(this.opt(names.getString(i))); |
646 | } |
|
647 | 0 | return ja; |
648 | } |
|
649 | ||
650 | /** |
|
651 | * {@inheritDoc} |
|
652 | */ |
|
653 | public String toString() |
|
654 | { |
|
655 | 87 | Iterator keys = keys(); |
656 | 87 | StringBuffer sb = new StringBuffer("{"); |
657 | ||
658 | 234 | while(keys.hasNext()) |
659 | { |
|
660 | 147 | if (sb.length() > 1) |
661 | { |
|
662 | 61 | sb.append(','); |
663 | } |
|
664 | 147 | Object o = keys.next(); |
665 | 147 | sb.append(valueToString(o)); |
666 | 147 | sb.append(':'); |
667 | 147 | sb.append(valueToString(this.myHashMap.get(o))); |
668 | 147 | } |
669 | 87 | sb.append('}'); |
670 | 87 | return sb.toString(); |
671 | } |
|
672 | ||
673 | public String toString(int indentFactor) |
|
674 | { |
|
675 | 0 | return toString(indentFactor, 0); |
676 | } |
|
677 | ||
678 | /** |
|
679 | * Make a prettyprinted JSON string of this JSONObject. |
|
680 | * <p> |
|
681 | * Warning: This method assumes that the data structure is acyclical. |
|
682 | * |
|
683 | * @param indentFactor |
|
684 | * The number of spaces to add to each level of indentation. |
|
685 | * @param indent |
|
686 | * The indentation of the top level. |
|
687 | * @return a printable, displayable, transmittable representation of the |
|
688 | * object, beginning with <code>{</code> <small>(left |
|
689 | * brace)</small> and ending with <code>}</code> <small>(right |
|
690 | * brace)</small>. |
|
691 | */ |
|
692 | String toString(int indentFactor, int indent) |
|
693 | { |
|
694 | int i; |
|
695 | 0 | int n = length(); |
696 | 0 | if (n == 0) { return "{}"; } |
697 | 0 | Iterator keys = keys(); |
698 | 0 | StringBuffer sb = new StringBuffer("{"); |
699 | 0 | int newindent = indent + indentFactor; |
700 | Object o; |
|
701 | 0 | if (n == 1) |
702 | { |
|
703 | 0 | o = keys.next(); |
704 | 0 | sb.append(quote(o.toString())); |
705 | 0 | sb.append(": "); |
706 | 0 | sb |
707 | .append(valueToString(this.myHashMap.get(o), indentFactor, |
|
708 | indent)); |
|
709 | } |
|
710 | else |
|
711 | { |
|
712 | 0 | while(keys.hasNext()) |
713 | { |
|
714 | 0 | o = keys.next(); |
715 | 0 | if (sb.length() > 1) |
716 | { |
|
717 | 0 | sb.append(",\n"); |
718 | } |
|
719 | else |
|
720 | { |
|
721 | 0 | sb.append('\n'); |
722 | } |
|
723 | 0 | for(i = 0; i < newindent; i += 1) |
724 | { |
|
725 | 0 | sb.append(' '); |
726 | } |
|
727 | 0 | sb.append(quote(o.toString())); |
728 | 0 | sb.append(": "); |
729 | 0 | sb.append(valueToString(this.myHashMap.get(o), indentFactor, |
730 | newindent)); |
|
731 | } |
|
732 | 0 | if (sb.length() > 1) |
733 | { |
|
734 | 0 | sb.append('\n'); |
735 | 0 | for(i = 0; i < indent; i += 1) |
736 | { |
|
737 | 0 | sb.append(' '); |
738 | } |
|
739 | } |
|
740 | } |
|
741 | 0 | sb.append('}'); |
742 | 0 | return sb.toString(); |
743 | } |
|
744 | ||
745 | /** |
|
746 | * Make JSON string of an object value. |
|
747 | * <p> |
|
748 | * Warning: This method assumes that the data structure is acyclical. |
|
749 | * |
|
750 | * @param value |
|
751 | * The value to be serialized. |
|
752 | * @return a printable, displayable, transmittable representation of the |
|
753 | * object, beginning with <code>{</code> <small>(left |
|
754 | * brace)</small> and ending with <code>}</code> <small>(right |
|
755 | * brace)</small>. |
|
756 | */ |
|
757 | static String valueToString(Object value) |
|
758 | { |
|
759 | 355 | if (value == null || value.equals(null)) { return "null"; } |
760 | 355 | if (value instanceof Number) { return numberToString((Number) value); } |
761 | 348 | if (value instanceof Boolean || value instanceof JSONObject |
762 | || value instanceof JSONArray |
|
763 | 149 | || value instanceof JSONLiteral) { return value.toString(); } |
764 | 199 | return quote(value.toString()); |
765 | } |
|
766 | ||
767 | /** |
|
768 | * Make a prettyprinted JSON string of an object value. |
|
769 | * <p> |
|
770 | * Warning: This method assumes that the data structure is acyclical. |
|
771 | * |
|
772 | * @param value |
|
773 | * The value to be serialized. |
|
774 | * @param indentFactor |
|
775 | * The number of spaces to add to each level of indentation. |
|
776 | * @param indent |
|
777 | * The indentation of the top level. |
|
778 | * @return a printable, displayable, transmittable representation of the |
|
779 | * object, beginning with <code>{</code> <small>(left |
|
780 | * brace)</small> and ending with <code>}</code> <small>(right |
|
781 | * brace)</small>. |
|
782 | */ |
|
783 | static String valueToString(Object value, int indentFactor, int indent) |
|
784 | { |
|
785 | 0 | if (value == null || value.equals(null)) { return "null"; } |
786 | 0 | if (value instanceof Number) { return numberToString((Number) value); } |
787 | 0 | if (value instanceof Boolean) { return value.toString(); } |
788 | 0 | if (value instanceof JSONObject) { return (((JSONObject) value) |
789 | .toString(indentFactor, indent)); } |
|
790 | 0 | if (value instanceof JSONArray) { return (((JSONArray) value).toString( |
791 | indentFactor, indent)); } |
|
792 | 0 | if (JSONLiteral.class.isAssignableFrom(value.getClass())) |
793 | 0 | return value.toString(); |
794 | 0 | return quote(value.toString()); |
795 | } |
|
796 | } |