1 | |
package org.apache.tapestry.json; |
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
|
23 | |
|
24 | |
|
25 | |
|
26 | |
|
27 | |
import java.text.ParseException; |
28 | |
import java.util.*; |
29 | |
|
30 | |
|
31 | |
|
32 | |
|
33 | |
|
34 | |
|
35 | |
|
36 | |
|
37 | |
|
38 | |
|
39 | |
|
40 | |
|
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | |
|
53 | |
|
54 | |
|
55 | |
|
56 | |
|
57 | |
|
58 | |
|
59 | |
|
60 | |
|
61 | |
|
62 | |
|
63 | |
|
64 | |
|
65 | |
|
66 | |
|
67 | |
|
68 | |
|
69 | |
|
70 | |
|
71 | |
|
72 | |
|
73 | |
public class JSONObject |
74 | |
{ |
75 | |
|
76 | |
|
77 | |
|
78 | |
|
79 | |
|
80 | |
|
81 | 0 | public static final Object NULL = new Null(); |
82 | |
|
83 | |
|
84 | |
|
85 | |
|
86 | |
|
87 | |
|
88 | 0 | private static final class Null |
89 | |
{ |
90 | |
|
91 | |
|
92 | |
|
93 | |
|
94 | |
|
95 | |
|
96 | |
|
97 | |
protected Object clone() |
98 | |
{ |
99 | 0 | return this; |
100 | |
} |
101 | |
|
102 | |
|
103 | |
|
104 | |
|
105 | |
|
106 | |
|
107 | |
|
108 | |
|
109 | |
|
110 | |
public boolean equals(Object object) |
111 | |
{ |
112 | 0 | return object == null || object == this; |
113 | |
} |
114 | |
|
115 | |
|
116 | |
|
117 | |
|
118 | |
|
119 | |
|
120 | |
public String toString() |
121 | |
{ |
122 | 0 | return "null"; |
123 | |
} |
124 | |
} |
125 | |
|
126 | |
|
127 | |
|
128 | |
|
129 | |
private HashMap myHashMap; |
130 | |
|
131 | |
|
132 | |
|
133 | |
|
134 | |
public JSONObject() |
135 | 0 | { |
136 | 0 | this.myHashMap = new LinkedHashMap(); |
137 | 0 | } |
138 | |
|
139 | |
|
140 | |
|
141 | |
|
142 | |
|
143 | |
|
144 | |
|
145 | |
|
146 | |
|
147 | |
|
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 | |
|
160 | |
|
161 | |
|
162 | |
|
163 | |
|
164 | |
|
165 | |
|
166 | |
public JSONObject(JSONTokener x) |
167 | |
throws ParseException |
168 | |
{ |
169 | 0 | this(); |
170 | |
char c; |
171 | |
String key; |
172 | |
|
173 | 0 | if (x.nextClean() != '{') { throw x |
174 | |
.syntaxError("A JSONObject must begin with '{'"); } |
175 | |
while(true) |
176 | |
{ |
177 | 0 | c = x.nextClean(); |
178 | 0 | switch(c) |
179 | |
{ |
180 | |
case 0: |
181 | 0 | throw x.syntaxError("A JSONObject must end with '}'"); |
182 | |
case '}': |
183 | 0 | return; |
184 | |
default: |
185 | 0 | x.back(); |
186 | 0 | key = x.nextValue().toString(); |
187 | |
} |
188 | |
|
189 | |
|
190 | |
|
191 | |
|
192 | |
|
193 | 0 | c = x.nextClean(); |
194 | 0 | if (c == '=') |
195 | |
{ |
196 | 0 | if (x.next() != '>') |
197 | |
{ |
198 | 0 | x.back(); |
199 | |
} |
200 | |
} |
201 | 0 | else if (c != ':') { throw x |
202 | |
.syntaxError("Expected a ':' after a key"); } |
203 | 0 | this.myHashMap.put(key, x.nextValue()); |
204 | |
|
205 | |
|
206 | |
|
207 | |
|
208 | |
|
209 | 0 | 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 | 0 | throw x.syntaxError("Expected a ',' or '}'"); |
220 | |
} |
221 | |
} |
222 | |
} |
223 | |
|
224 | |
|
225 | |
|
226 | |
|
227 | |
|
228 | |
|
229 | |
|
230 | |
|
231 | |
public JSONObject(Map map) |
232 | 0 | { |
233 | 0 | this.myHashMap = new HashMap(map); |
234 | 0 | } |
235 | |
|
236 | |
|
237 | |
|
238 | |
|
239 | |
|
240 | |
|
241 | |
|
242 | |
|
243 | |
|
244 | |
|
245 | |
|
246 | |
|
247 | |
public JSONObject(String string) |
248 | |
throws ParseException |
249 | |
{ |
250 | 0 | this(new JSONTokener(string)); |
251 | 0 | } |
252 | |
|
253 | |
public JSONObject accumulate(String key, Object value) |
254 | |
{ |
255 | |
JSONArray a; |
256 | 0 | Object o = opt(key); |
257 | 0 | if (o == null) |
258 | |
{ |
259 | 0 | a = new JSONArray(); |
260 | 0 | a.put(value); |
261 | 0 | put(key, a); |
262 | |
} |
263 | 0 | else if (o instanceof JSONArray) |
264 | |
{ |
265 | 0 | a = (JSONArray) o; |
266 | 0 | a.put(value); |
267 | |
} |
268 | |
|
269 | 0 | return this; |
270 | |
} |
271 | |
|
272 | |
public Object get(String key) |
273 | |
{ |
274 | 0 | Object o = opt(key); |
275 | 0 | if (o == null) { throw new NoSuchElementException("JSONObject[" |
276 | |
+ quote(key) + "] not found."); } |
277 | 0 | 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 | |
|
307 | |
|
308 | |
|
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 | 0 | Object o = get(key); |
333 | 0 | 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 | 0 | 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 | 0 | return this.myHashMap.keySet().iterator(); |
356 | |
} |
357 | |
|
358 | |
public int length() |
359 | |
{ |
360 | 0 | 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 | |
|
376 | |
|
377 | |
|
378 | |
|
379 | |
|
380 | |
|
381 | |
|
382 | |
public static String numberToString(Number n) |
383 | |
{ |
384 | 0 | 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 | |
|
390 | |
|
391 | 0 | String s = n.toString(); |
392 | 0 | 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 | 0 | return s; |
404 | |
} |
405 | |
|
406 | |
public Object opt(String key) |
407 | |
{ |
408 | 0 | if (key == null) { throw new NullPointerException("Null key"); } |
409 | 0 | 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 | 0 | put(key, Boolean.valueOf(value)); |
507 | 0 | 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 | 0 | put(key, new Long(value)); |
519 | 0 | return this; |
520 | |
} |
521 | |
|
522 | |
public JSONObject put(Object key, int value) |
523 | |
{ |
524 | 0 | put(key, new Integer(value)); |
525 | 0 | return this; |
526 | |
} |
527 | |
|
528 | |
public JSONObject put(Object key, Object value) |
529 | |
{ |
530 | 0 | if (key == null) |
531 | 0 | throw new NullPointerException("Null key."); |
532 | |
|
533 | 0 | if (value != null) |
534 | |
{ |
535 | 0 | this.myHashMap.put(key, value); |
536 | |
} |
537 | |
else |
538 | |
{ |
539 | 0 | remove(key.toString()); |
540 | |
} |
541 | |
|
542 | 0 | 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 | |
|
556 | |
|
557 | |
|
558 | |
|
559 | |
|
560 | |
public static String quote(char value) |
561 | |
{ |
562 | 0 | return quote(new String(new char[]{value})); |
563 | |
} |
564 | |
|
565 | |
|
566 | |
|
567 | |
|
568 | |
|
569 | |
|
570 | |
|
571 | |
|
572 | |
|
573 | |
public static String quote(String string) |
574 | |
{ |
575 | 0 | if (string == null || string.length() == 0) { return "\"\""; } |
576 | |
|
577 | |
char b; |
578 | 0 | char c = 0; |
579 | |
int i; |
580 | 0 | int len = string.length(); |
581 | 0 | StringBuffer sb = new StringBuffer(len + 4); |
582 | |
String t; |
583 | |
|
584 | 0 | sb.append('"'); |
585 | 0 | for(i = 0; i < len; i += 1) |
586 | |
{ |
587 | 0 | b = c; |
588 | 0 | c = string.charAt(i); |
589 | 0 | switch(c) |
590 | |
{ |
591 | |
case '\\': |
592 | |
case '"': |
593 | 0 | sb.append('\\'); |
594 | 0 | sb.append(c); |
595 | 0 | break; |
596 | |
case '/': |
597 | 0 | if (b == '<') |
598 | |
{ |
599 | 0 | sb.append('\\'); |
600 | |
} |
601 | 0 | sb.append(c); |
602 | 0 | 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 | 0 | 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 | 0 | sb.append(c); |
627 | |
} |
628 | |
} |
629 | |
} |
630 | 0 | sb.append('"'); |
631 | 0 | return sb.toString(); |
632 | |
} |
633 | |
|
634 | |
public Object remove(String key) |
635 | |
{ |
636 | 0 | 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 | |
|
652 | |
|
653 | |
public String toString() |
654 | |
{ |
655 | 0 | Iterator keys = keys(); |
656 | 0 | StringBuffer sb = new StringBuffer("{"); |
657 | |
|
658 | 0 | while(keys.hasNext()) |
659 | |
{ |
660 | 0 | if (sb.length() > 1) |
661 | |
{ |
662 | 0 | sb.append(','); |
663 | |
} |
664 | 0 | Object o = keys.next(); |
665 | 0 | sb.append(valueToString(o)); |
666 | 0 | sb.append(':'); |
667 | 0 | sb.append(valueToString(this.myHashMap.get(o))); |
668 | 0 | } |
669 | 0 | sb.append('}'); |
670 | 0 | return sb.toString(); |
671 | |
} |
672 | |
|
673 | |
public String toString(int indentFactor) |
674 | |
{ |
675 | 0 | return toString(indentFactor, 0); |
676 | |
} |
677 | |
|
678 | |
|
679 | |
|
680 | |
|
681 | |
|
682 | |
|
683 | |
|
684 | |
|
685 | |
|
686 | |
|
687 | |
|
688 | |
|
689 | |
|
690 | |
|
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 | |
|
747 | |
|
748 | |
|
749 | |
|
750 | |
|
751 | |
|
752 | |
|
753 | |
|
754 | |
|
755 | |
|
756 | |
|
757 | |
static String valueToString(Object value) |
758 | |
{ |
759 | 0 | if (value == null || value.equals(null)) { return "null"; } |
760 | 0 | if (value instanceof Number) { return numberToString((Number) value); } |
761 | 0 | if (value instanceof Boolean || value instanceof JSONObject |
762 | |
|| value instanceof JSONArray |
763 | 0 | || value instanceof JSONLiteral) { return value.toString(); } |
764 | 0 | return quote(value.toString()); |
765 | |
} |
766 | |
|
767 | |
|
768 | |
|
769 | |
|
770 | |
|
771 | |
|
772 | |
|
773 | |
|
774 | |
|
775 | |
|
776 | |
|
777 | |
|
778 | |
|
779 | |
|
780 | |
|
781 | |
|
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 | |
} |