1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.util.ajax;
20
21 import java.io.Externalizable;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.Reader;
25 import java.lang.reflect.Array;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.Map;
31 import java.util.concurrent.ConcurrentHashMap;
32
33 import org.eclipse.jetty.util.IO;
34 import org.eclipse.jetty.util.Loader;
35 import org.eclipse.jetty.util.QuotedStringTokenizer;
36 import org.eclipse.jetty.util.TypeUtil;
37 import org.eclipse.jetty.util.log.Log;
38 import org.eclipse.jetty.util.log.Logger;
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 public class JSON
91 {
92 static final Logger LOG = Log.getLogger(JSON.class);
93 public final static JSON DEFAULT = new JSON();
94
95 private Map<String, Convertor> _convertors = new ConcurrentHashMap<String, Convertor>();
96 private int _stringBufferSize = 1024;
97
98 public JSON()
99 {
100 }
101
102
103
104
105
106 public int getStringBufferSize()
107 {
108 return _stringBufferSize;
109 }
110
111
112
113
114
115
116 public void setStringBufferSize(int stringBufferSize)
117 {
118 _stringBufferSize = stringBufferSize;
119 }
120
121
122
123
124
125
126
127
128
129 public static void registerConvertor(Class forClass, Convertor convertor)
130 {
131 DEFAULT.addConvertor(forClass,convertor);
132 }
133
134 public static JSON getDefault()
135 {
136 return DEFAULT;
137 }
138
139 @Deprecated
140 public static void setDefault(JSON json)
141 {
142 }
143
144 public static String toString(Object object)
145 {
146 StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
147 DEFAULT.append(buffer,object);
148 return buffer.toString();
149 }
150
151 public static String toString(Map object)
152 {
153 StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
154 DEFAULT.appendMap(buffer,object);
155 return buffer.toString();
156 }
157
158 public static String toString(Object[] array)
159 {
160 StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize());
161 DEFAULT.appendArray(buffer,array);
162 return buffer.toString();
163 }
164
165
166
167
168
169
170 public static Object parse(String s)
171 {
172 return DEFAULT.parse(new StringSource(s),false);
173 }
174
175
176
177
178
179
180
181
182 public static Object parse(String s, boolean stripOuterComment)
183 {
184 return DEFAULT.parse(new StringSource(s),stripOuterComment);
185 }
186
187
188
189
190
191
192 public static Object parse(Reader in) throws IOException
193 {
194 return DEFAULT.parse(new ReaderSource(in),false);
195 }
196
197
198
199
200
201
202
203
204 public static Object parse(Reader in, boolean stripOuterComment) throws IOException
205 {
206 return DEFAULT.parse(new ReaderSource(in),stripOuterComment);
207 }
208
209
210
211
212
213
214
215 @Deprecated
216 public static Object parse(InputStream in) throws IOException
217 {
218 return DEFAULT.parse(new StringSource(IO.toString(in)),false);
219 }
220
221
222
223
224
225
226
227
228
229 @Deprecated
230 public static Object parse(InputStream in, boolean stripOuterComment) throws IOException
231 {
232 return DEFAULT.parse(new StringSource(IO.toString(in)),stripOuterComment);
233 }
234
235
236
237
238
239
240
241
242 public String toJSON(Object object)
243 {
244 StringBuilder buffer = new StringBuilder(getStringBufferSize());
245 append(buffer,object);
246 return buffer.toString();
247 }
248
249
250
251
252
253
254
255
256 public Object fromJSON(String json)
257 {
258 Source source = new StringSource(json);
259 return parse(source);
260 }
261
262 @Deprecated
263 public void append(StringBuffer buffer, Object object)
264 {
265 append((Appendable)buffer,object);
266 }
267
268
269
270
271
272
273
274
275
276 public void append(Appendable buffer, Object object)
277 {
278 try
279 {
280 if (object == null)
281 {
282 buffer.append("null");
283 }
284
285 else if (object instanceof Map)
286 {
287 appendMap(buffer,(Map)object);
288 }
289 else if (object instanceof String)
290 {
291 appendString(buffer,(String)object);
292 }
293 else if (object instanceof Number)
294 {
295 appendNumber(buffer,(Number)object);
296 }
297 else if (object instanceof Boolean)
298 {
299 appendBoolean(buffer,(Boolean)object);
300 }
301 else if (object.getClass().isArray())
302 {
303 appendArray(buffer,object);
304 }
305 else if (object instanceof Character)
306 {
307 appendString(buffer,object.toString());
308 }
309 else if (object instanceof Convertible)
310 {
311 appendJSON(buffer,(Convertible)object);
312 }
313 else if (object instanceof Generator)
314 {
315 appendJSON(buffer,(Generator)object);
316 }
317 else
318 {
319
320 Convertor convertor = getConvertor(object.getClass());
321 if (convertor != null)
322 {
323 appendJSON(buffer,convertor,object);
324 }
325 else if (object instanceof Collection)
326 {
327 appendArray(buffer,(Collection)object);
328 }
329 else
330 {
331 appendString(buffer,object.toString());
332 }
333 }
334 }
335 catch (IOException e)
336 {
337 throw new RuntimeException(e);
338 }
339 }
340
341 @Deprecated
342 public void appendNull(StringBuffer buffer)
343 {
344 appendNull((Appendable)buffer);
345 }
346
347 public void appendNull(Appendable buffer)
348 {
349 try
350 {
351 buffer.append("null");
352 }
353 catch (IOException e)
354 {
355 throw new RuntimeException(e);
356 }
357 }
358
359 @Deprecated
360 public void appendJSON(final StringBuffer buffer, final Convertor convertor, final Object object)
361 {
362 appendJSON((Appendable)buffer,convertor,object);
363 }
364
365 public void appendJSON(final Appendable buffer, final Convertor convertor, final Object object)
366 {
367 appendJSON(buffer,new Convertible()
368 {
369 public void fromJSON(Map object)
370 {
371 }
372
373 public void toJSON(Output out)
374 {
375 convertor.toJSON(object,out);
376 }
377 });
378 }
379
380 @Deprecated
381 public void appendJSON(final StringBuffer buffer, Convertible converter)
382 {
383 appendJSON((Appendable)buffer,converter);
384 }
385
386 public void appendJSON(final Appendable buffer, Convertible converter)
387 {
388 ConvertableOutput out=new ConvertableOutput(buffer);
389 converter.toJSON(out);
390 out.complete();
391 }
392
393 @Deprecated
394 public void appendJSON(StringBuffer buffer, Generator generator)
395 {
396 generator.addJSON(buffer);
397 }
398
399 public void appendJSON(Appendable buffer, Generator generator)
400 {
401 generator.addJSON(buffer);
402 }
403
404 @Deprecated
405 public void appendMap(StringBuffer buffer, Map<?,?> map)
406 {
407 appendMap((Appendable)buffer,map);
408 }
409
410 public void appendMap(Appendable buffer, Map<?,?> map)
411 {
412 try
413 {
414 if (map == null)
415 {
416 appendNull(buffer);
417 return;
418 }
419
420 buffer.append('{');
421 Iterator<?> iter = map.entrySet().iterator();
422 while (iter.hasNext())
423 {
424 Map.Entry<?,?> entry = (Map.Entry<?,?>)iter.next();
425 QuotedStringTokenizer.quote(buffer,entry.getKey().toString());
426 buffer.append(':');
427 append(buffer,entry.getValue());
428 if (iter.hasNext())
429 buffer.append(',');
430 }
431
432 buffer.append('}');
433 }
434 catch (IOException e)
435 {
436 throw new RuntimeException(e);
437 }
438 }
439
440 @Deprecated
441 public void appendArray(StringBuffer buffer, Collection collection)
442 {
443 appendArray((Appendable)buffer,collection);
444 }
445
446 public void appendArray(Appendable buffer, Collection collection)
447 {
448 try
449 {
450 if (collection == null)
451 {
452 appendNull(buffer);
453 return;
454 }
455
456 buffer.append('[');
457 Iterator iter = collection.iterator();
458 boolean first = true;
459 while (iter.hasNext())
460 {
461 if (!first)
462 buffer.append(',');
463
464 first = false;
465 append(buffer,iter.next());
466 }
467
468 buffer.append(']');
469 }
470 catch (IOException e)
471 {
472 throw new RuntimeException(e);
473 }
474 }
475
476 @Deprecated
477 public void appendArray(StringBuffer buffer, Object array)
478 {
479 appendArray((Appendable)buffer,array);
480 }
481
482 public void appendArray(Appendable buffer, Object array)
483 {
484 try
485 {
486 if (array == null)
487 {
488 appendNull(buffer);
489 return;
490 }
491
492 buffer.append('[');
493 int length = Array.getLength(array);
494
495 for (int i = 0; i < length; i++)
496 {
497 if (i != 0)
498 buffer.append(',');
499 append(buffer,Array.get(array,i));
500 }
501
502 buffer.append(']');
503 }
504 catch (IOException e)
505 {
506 throw new RuntimeException(e);
507 }
508 }
509
510 @Deprecated
511 public void appendBoolean(StringBuffer buffer, Boolean b)
512 {
513 appendBoolean((Appendable)buffer,b);
514 }
515
516 public void appendBoolean(Appendable buffer, Boolean b)
517 {
518 try
519 {
520 if (b == null)
521 {
522 appendNull(buffer);
523 return;
524 }
525 buffer.append(b?"true":"false");
526 }
527 catch (IOException e)
528 {
529 throw new RuntimeException(e);
530 }
531 }
532
533 @Deprecated
534 public void appendNumber(StringBuffer buffer, Number number)
535 {
536 appendNumber((Appendable)buffer,number);
537 }
538
539 public void appendNumber(Appendable buffer, Number number)
540 {
541 try
542 {
543 if (number == null)
544 {
545 appendNull(buffer);
546 return;
547 }
548 buffer.append(String.valueOf(number));
549 }
550 catch (IOException e)
551 {
552 throw new RuntimeException(e);
553 }
554 }
555
556 @Deprecated
557 public void appendString(StringBuffer buffer, String string)
558 {
559 appendString((Appendable)buffer,string);
560 }
561
562 public void appendString(Appendable buffer, String string)
563 {
564 if (string == null)
565 {
566 appendNull(buffer);
567 return;
568 }
569
570 QuotedStringTokenizer.quote(buffer,string);
571 }
572
573
574
575 protected String toString(char[] buffer, int offset, int length)
576 {
577 return new String(buffer,offset,length);
578 }
579
580 protected Map<String, Object> newMap()
581 {
582 return new HashMap<String, Object>();
583 }
584
585 protected Object[] newArray(int size)
586 {
587 return new Object[size];
588 }
589
590 protected JSON contextForArray()
591 {
592 return this;
593 }
594
595 protected JSON contextFor(String field)
596 {
597 return this;
598 }
599
600 protected Object convertTo(Class type, Map map)
601 {
602 if (type != null && Convertible.class.isAssignableFrom(type))
603 {
604 try
605 {
606 Convertible conv = (Convertible)type.newInstance();
607 conv.fromJSON(map);
608 return conv;
609 }
610 catch (Exception e)
611 {
612 throw new RuntimeException(e);
613 }
614 }
615
616 Convertor convertor = getConvertor(type);
617 if (convertor != null)
618 {
619 return convertor.fromJSON(map);
620 }
621 return map;
622 }
623
624
625
626
627
628
629
630
631
632 public void addConvertor(Class forClass, Convertor convertor)
633 {
634 _convertors.put(forClass.getName(),convertor);
635 }
636
637
638
639
640
641
642
643
644
645
646
647
648 protected Convertor getConvertor(Class forClass)
649 {
650 Class cls = forClass;
651 Convertor convertor = _convertors.get(cls.getName());
652 if (convertor == null && this != DEFAULT)
653 convertor = DEFAULT.getConvertor(cls);
654
655 while (convertor == null && cls != Object.class)
656 {
657 Class[] ifs = cls.getInterfaces();
658 int i = 0;
659 while (convertor == null && ifs != null && i < ifs.length)
660 convertor = _convertors.get(ifs[i++].getName());
661 if (convertor == null)
662 {
663 cls = cls.getSuperclass();
664 convertor = _convertors.get(cls.getName());
665 }
666 }
667 return convertor;
668 }
669
670
671
672
673
674
675
676
677
678 public void addConvertorFor(String name, Convertor convertor)
679 {
680 _convertors.put(name,convertor);
681 }
682
683
684
685
686
687
688
689
690 public Convertor getConvertorFor(String name)
691 {
692 Convertor convertor = _convertors.get(name);
693 if (convertor == null && this != DEFAULT)
694 convertor = DEFAULT.getConvertorFor(name);
695 return convertor;
696 }
697
698 public Object parse(Source source, boolean stripOuterComment)
699 {
700 int comment_state = 0;
701 if (!stripOuterComment)
702 return parse(source);
703
704 int strip_state = 1;
705
706 Object o = null;
707 while (source.hasNext())
708 {
709 char c = source.peek();
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728 comment
729 else if (comment_state > 1)
730 {
731 switch (c)
732 {
733 case '*':
734 comment_state = 3;
735 break;
736 case '/':
737 if (comment_state == 3)
738 {
739 comment_state = 0;
740 if (strip_state == 2)
741 return o;
742 }
743 else
744 comment_state = 2;
745 break;
746 default:
747 comment_state = 2;
748 }
749 }
750
751 else if (comment_state < 0)
752 {
753 switch (c)
754 {
755 case '\r':
756 case '\n':
757 comment_state = 0;
758 default:
759 break;
760 }
761 }
762
763 else
764 {
765 if (!Character.isWhitespace(c))
766 {
767 if (c == '/')
768 comment_state = 1;
769 else if (c == '*')
770 comment_state = 3;
771 else if (o == null)
772 {
773 o = parse(source);
774 continue;
775 }
776 }
777 }
778
779 source.next();
780 }
781
782 return o;
783 }
784
785 public Object parse(Source source)
786 {
787 int comment_state = 0;
788
789 while (source.hasNext())
790 {
791 char c = source.peek();
792
793
794
795
796
797
798
799
800
801
802
803
804
805 comment
806 else if (comment_state > 1)
807 {
808 switch (c)
809 {
810 case '*':
811 comment_state = 3;
812 break;
813 case '/':
814 if (comment_state == 3)
815 comment_state = 0;
816 else
817 comment_state = 2;
818 break;
819 default:
820 comment_state = 2;
821 }
822 }
823
824 else if (comment_state < 0)
825 {
826 switch (c)
827 {
828 case '\r':
829 case '\n':
830 comment_state = 0;
831 break;
832 default:
833 break;
834 }
835 }
836
837 else
838 {
839 switch (c)
840 {
841 case '{':
842 return parseObject(source);
843 case '[':
844 return parseArray(source);
845 case '"':
846 return parseString(source);
847 case '-':
848 return parseNumber(source);
849
850 case 'n':
851 complete("null",source);
852 return null;
853 case 't':
854 complete("true",source);
855 return Boolean.TRUE;
856 case 'f':
857 complete("false",source);
858 return Boolean.FALSE;
859 case 'u':
860 complete("undefined",source);
861 return null;
862 case 'N':
863 complete("NaN",source);
864 return null;
865
866 case '/':
867 comment_state = 1;
868 break;
869
870 default:
871 if (Character.isDigit(c))
872 return parseNumber(source);
873 else if (Character.isWhitespace(c))
874 break;
875 return handleUnknown(source,c);
876 }
877 }
878 source.next();
879 }
880
881 return null;
882 }
883
884 protected Object handleUnknown(Source source, char c)
885 {
886 throw new IllegalStateException("unknown char '" + c + "'(" + (int)c + ") in " + source);
887 }
888
889 protected Object parseObject(Source source)
890 {
891 if (source.next() != '{')
892 throw new IllegalStateException();
893 Map<String, Object> map = newMap();
894
895 char next = seekTo("\"}",source);
896
897 while (source.hasNext())
898 {
899 if (next == '}')
900 {
901 source.next();
902 break;
903 }
904
905 String name = parseString(source);
906 seekTo(':',source);
907 source.next();
908
909 Object value = contextFor(name).parse(source);
910 map.put(name,value);
911
912 seekTo(",}",source);
913 next = source.next();
914 if (next == '}')
915 break;
916 else
917 next = seekTo("\"}",source);
918 }
919
920 String xclassname = (String)map.get("x-class");
921 if (xclassname != null)
922 {
923 Convertor c = getConvertorFor(xclassname);
924 if (c != null)
925 return c.fromJSON(map);
926 LOG.warn("No Convertor for x-class '{}'", xclassname);
927 }
928
929 String classname = (String)map.get("class");
930 if (classname != null)
931 {
932 try
933 {
934 Class c = Loader.loadClass(JSON.class,classname);
935 return convertTo(c,map);
936 }
937 catch (ClassNotFoundException e)
938 {
939 LOG.warn("No Class for '{}'", classname);
940 }
941 }
942
943 return map;
944 }
945
946 protected Object parseArray(Source source)
947 {
948 if (source.next() != '[')
949 throw new IllegalStateException();
950
951 int size = 0;
952 ArrayList list = null;
953 Object item = null;
954 boolean coma = true;
955
956 while (source.hasNext())
957 {
958 char c = source.peek();
959 switch (c)
960 {
961 case ']':
962 source.next();
963 switch (size)
964 {
965 case 0:
966 return newArray(0);
967 case 1:
968 Object array = newArray(1);
969 Array.set(array,0,item);
970 return array;
971 default:
972 return list.toArray(newArray(list.size()));
973 }
974
975 case ',':
976 if (coma)
977 throw new IllegalStateException();
978 coma = true;
979 source.next();
980 break;
981
982 default:
983 if (Character.isWhitespace(c))
984 source.next();
985 else
986 {
987 coma = false;
988 if (size++ == 0)
989 item = contextForArray().parse(source);
990 else if (list == null)
991 {
992 list = new ArrayList();
993 list.add(item);
994 item = contextForArray().parse(source);
995 list.add(item);
996 item = null;
997 }
998 else
999 {
1000 item = contextForArray().parse(source);
1001 list.add(item);
1002 item = null;
1003 }
1004 }
1005 }
1006
1007 }
1008
1009 throw new IllegalStateException("unexpected end of array");
1010 }
1011
1012 protected String parseString(Source source)
1013 {
1014 if (source.next() != '"')
1015 throw new IllegalStateException();
1016
1017 boolean escape = false;
1018
1019 StringBuilder b = null;
1020 final char[] scratch = source.scratchBuffer();
1021
1022 if (scratch != null)
1023 {
1024 int i = 0;
1025 while (source.hasNext())
1026 {
1027 if (i >= scratch.length)
1028 {
1029
1030
1031 b = new StringBuilder(scratch.length * 2);
1032 b.append(scratch,0,i);
1033 break;
1034 }
1035
1036 char c = source.next();
1037
1038 if (escape)
1039 {
1040 escape = false;
1041 switch (c)
1042 {
1043 case '"':
1044 scratch[i++] = '"';
1045 break;
1046 case '\\':
1047 scratch[i++] = '\\';
1048 break;
1049 case '/':
1050 scratch[i++] = '/';
1051 break;
1052 case 'b':
1053 scratch[i++] = '\b';
1054 break;
1055 case 'f':
1056 scratch[i++] = '\f';
1057 break;
1058 case 'n':
1059 scratch[i++] = '\n';
1060 break;
1061 case 'r':
1062 scratch[i++] = '\r';
1063 break;
1064 case 't':
1065 scratch[i++] = '\t';
1066 break;
1067 case 'u':
1068 char uc = (char)((TypeUtil.convertHexDigit((byte)source.next()) << 12) + (TypeUtil.convertHexDigit((byte)source.next()) << 8)
1069 + (TypeUtil.convertHexDigit((byte)source.next()) << 4) + (TypeUtil.convertHexDigit((byte)source.next())));
1070 scratch[i++] = uc;
1071 break;
1072 default:
1073 scratch[i++] = c;
1074 }
1075 }
1076 else if (c == '\\')
1077 {
1078 escape = true;
1079 }
1080 else if (c == '\"')
1081 {
1082
1083 return toString(scratch,0,i);
1084 }
1085 else
1086 {
1087 scratch[i++] = c;
1088 }
1089 }
1090
1091
1092 if (b == null)
1093 return toString(scratch,0,i);
1094 }
1095 else
1096 b = new StringBuilder(getStringBufferSize());
1097
1098
1099 final StringBuilder builder=b;
1100 while (source.hasNext())
1101 {
1102 char c = source.next();
1103
1104 if (escape)
1105 {
1106 escape = false;
1107 switch (c)
1108 {
1109 case '"':
1110 builder.append('"');
1111 break;
1112 case '\\':
1113 builder.append('\\');
1114 break;
1115 case '/':
1116 builder.append('/');
1117 break;
1118 case 'b':
1119 builder.append('\b');
1120 break;
1121 case 'f':
1122 builder.append('\f');
1123 break;
1124 case 'n':
1125 builder.append('\n');
1126 break;
1127 case 'r':
1128 builder.append('\r');
1129 break;
1130 case 't':
1131 builder.append('\t');
1132 break;
1133 case 'u':
1134 char uc = (char)((TypeUtil.convertHexDigit((byte)source.next()) << 12) + (TypeUtil.convertHexDigit((byte)source.next()) << 8)
1135 + (TypeUtil.convertHexDigit((byte)source.next()) << 4) + (TypeUtil.convertHexDigit((byte)source.next())));
1136 builder.append(uc);
1137 break;
1138 default:
1139 builder.append(c);
1140 }
1141 }
1142 else if (c == '\\')
1143 {
1144 escape = true;
1145 }
1146 else if (c == '\"')
1147 {
1148 break;
1149 }
1150 else
1151 {
1152 builder.append(c);
1153 }
1154 }
1155 return builder.toString();
1156 }
1157
1158 public Number parseNumber(Source source)
1159 {
1160 boolean minus = false;
1161 long number = 0;
1162 StringBuilder buffer = null;
1163
1164 longLoop: while (source.hasNext())
1165 {
1166 char c = source.peek();
1167 switch (c)
1168 {
1169 case '0':
1170 case '1':
1171 case '2':
1172 case '3':
1173 case '4':
1174 case '5':
1175 case '6':
1176 case '7':
1177 case '8':
1178 case '9':
1179 number = number * 10 + (c - '0');
1180 source.next();
1181 break;
1182
1183 case '-':
1184 case '+':
1185 if (number != 0)
1186 throw new IllegalStateException("bad number");
1187 minus = true;
1188 source.next();
1189 break;
1190
1191 case '.':
1192 case 'e':
1193 case 'E':
1194 buffer = new StringBuilder(16);
1195 if (minus)
1196 buffer.append('-');
1197 buffer.append(number);
1198 buffer.append(c);
1199 source.next();
1200 break longLoop;
1201
1202 default:
1203 break longLoop;
1204 }
1205 }
1206
1207 if (buffer == null)
1208 return minus ? -1 * number : number;
1209
1210 doubleLoop: while (source.hasNext())
1211 {
1212 char c = source.peek();
1213 switch (c)
1214 {
1215 case '0':
1216 case '1':
1217 case '2':
1218 case '3':
1219 case '4':
1220 case '5':
1221 case '6':
1222 case '7':
1223 case '8':
1224 case '9':
1225 case '-':
1226 case '.':
1227 case '+':
1228 case 'e':
1229 case 'E':
1230 buffer.append(c);
1231 source.next();
1232 break;
1233
1234 default:
1235 break doubleLoop;
1236 }
1237 }
1238 return new Double(buffer.toString());
1239
1240 }
1241
1242 protected void seekTo(char seek, Source source)
1243 {
1244 while (source.hasNext())
1245 {
1246 char c = source.peek();
1247 if (c == seek)
1248 return;
1249
1250 if (!Character.isWhitespace(c))
1251 throw new IllegalStateException("Unexpected '" + c + " while seeking '" + seek + "'");
1252 source.next();
1253 }
1254
1255 throw new IllegalStateException("Expected '" + seek + "'");
1256 }
1257
1258 protected char seekTo(String seek, Source source)
1259 {
1260 while (source.hasNext())
1261 {
1262 char c = source.peek();
1263 if (seek.indexOf(c) >= 0)
1264 {
1265 return c;
1266 }
1267
1268 if (!Character.isWhitespace(c))
1269 throw new IllegalStateException("Unexpected '" + c + "' while seeking one of '" + seek + "'");
1270 source.next();
1271 }
1272
1273 throw new IllegalStateException("Expected one of '" + seek + "'");
1274 }
1275
1276 protected static void complete(String seek, Source source)
1277 {
1278 int i = 0;
1279 while (source.hasNext() && i < seek.length())
1280 {
1281 char c = source.next();
1282 if (c != seek.charAt(i++))
1283 throw new IllegalStateException("Unexpected '" + c + " while seeking \"" + seek + "\"");
1284 }
1285
1286 if (i < seek.length())
1287 throw new IllegalStateException("Expected \"" + seek + "\"");
1288 }
1289
1290 private final class ConvertableOutput implements Output
1291 {
1292 private final Appendable _buffer;
1293 char c = '{';
1294
1295 private ConvertableOutput(Appendable buffer)
1296 {
1297 _buffer = buffer;
1298 }
1299
1300 public void complete()
1301 {
1302 try
1303 {
1304 if (c == '{')
1305 _buffer.append("{}");
1306 else if (c != 0)
1307 _buffer.append("}");
1308 }
1309 catch (IOException e)
1310 {
1311 throw new RuntimeException(e);
1312 }
1313 }
1314
1315 public void add(Object obj)
1316 {
1317 if (c == 0)
1318 throw new IllegalStateException();
1319 append(_buffer,obj);
1320 c = 0;
1321 }
1322
1323 public void addClass(Class type)
1324 {
1325 try
1326 {
1327 if (c == 0)
1328 throw new IllegalStateException();
1329 _buffer.append(c);
1330 _buffer.append("\"class\":");
1331 append(_buffer,type.getName());
1332 c = ',';
1333 }
1334 catch (IOException e)
1335 {
1336 throw new RuntimeException(e);
1337 }
1338 }
1339
1340 public void add(String name, Object value)
1341 {
1342 try
1343 {
1344 if (c == 0)
1345 throw new IllegalStateException();
1346 _buffer.append(c);
1347 QuotedStringTokenizer.quote(_buffer,name);
1348 _buffer.append(':');
1349 append(_buffer,value);
1350 c = ',';
1351 }
1352 catch (IOException e)
1353 {
1354 throw new RuntimeException(e);
1355 }
1356 }
1357
1358 public void add(String name, double value)
1359 {
1360 try
1361 {
1362 if (c == 0)
1363 throw new IllegalStateException();
1364 _buffer.append(c);
1365 QuotedStringTokenizer.quote(_buffer,name);
1366 _buffer.append(':');
1367 appendNumber(_buffer, value);
1368 c = ',';
1369 }
1370 catch (IOException e)
1371 {
1372 throw new RuntimeException(e);
1373 }
1374 }
1375
1376 public void add(String name, long value)
1377 {
1378 try
1379 {
1380 if (c == 0)
1381 throw new IllegalStateException();
1382 _buffer.append(c);
1383 QuotedStringTokenizer.quote(_buffer,name);
1384 _buffer.append(':');
1385 appendNumber(_buffer, value);
1386 c = ',';
1387 }
1388 catch (IOException e)
1389 {
1390 throw new RuntimeException(e);
1391 }
1392 }
1393
1394 public void add(String name, boolean value)
1395 {
1396 try
1397 {
1398 if (c == 0)
1399 throw new IllegalStateException();
1400 _buffer.append(c);
1401 QuotedStringTokenizer.quote(_buffer,name);
1402 _buffer.append(':');
1403 appendBoolean(_buffer,value?Boolean.TRUE:Boolean.FALSE);
1404 c = ',';
1405 }
1406 catch (IOException e)
1407 {
1408 throw new RuntimeException(e);
1409 }
1410 }
1411 }
1412
1413 public interface Source
1414 {
1415 boolean hasNext();
1416
1417 char next();
1418
1419 char peek();
1420
1421 char[] scratchBuffer();
1422 }
1423
1424 public static class StringSource implements Source
1425 {
1426 private final String string;
1427 private int index;
1428 private char[] scratch;
1429
1430 public StringSource(String s)
1431 {
1432 string = s;
1433 }
1434
1435 public boolean hasNext()
1436 {
1437 if (index < string.length())
1438 return true;
1439 scratch = null;
1440 return false;
1441 }
1442
1443 public char next()
1444 {
1445 return string.charAt(index++);
1446 }
1447
1448 public char peek()
1449 {
1450 return string.charAt(index);
1451 }
1452
1453 @Override
1454 public String toString()
1455 {
1456 return string.substring(0,index) + "|||" + string.substring(index);
1457 }
1458
1459 public char[] scratchBuffer()
1460 {
1461 if (scratch == null)
1462 scratch = new char[string.length()];
1463 return scratch;
1464 }
1465 }
1466
1467 public static class ReaderSource implements Source
1468 {
1469 private Reader _reader;
1470 private int _next = -1;
1471 private char[] scratch;
1472
1473 public ReaderSource(Reader r)
1474 {
1475 _reader = r;
1476 }
1477
1478 public void setReader(Reader reader)
1479 {
1480 _reader = reader;
1481 _next = -1;
1482 }
1483
1484 public boolean hasNext()
1485 {
1486 getNext();
1487 if (_next < 0)
1488 {
1489 scratch = null;
1490 return false;
1491 }
1492 return true;
1493 }
1494
1495 public char next()
1496 {
1497 getNext();
1498 char c = (char)_next;
1499 _next = -1;
1500 return c;
1501 }
1502
1503 public char peek()
1504 {
1505 getNext();
1506 return (char)_next;
1507 }
1508
1509 private void getNext()
1510 {
1511 if (_next < 0)
1512 {
1513 try
1514 {
1515 _next = _reader.read();
1516 }
1517 catch (IOException e)
1518 {
1519 throw new RuntimeException(e);
1520 }
1521 }
1522 }
1523
1524 public char[] scratchBuffer()
1525 {
1526 if (scratch == null)
1527 scratch = new char[1024];
1528 return scratch;
1529 }
1530
1531 }
1532
1533
1534
1535
1536 public interface Output
1537 {
1538 public void addClass(Class c);
1539
1540 public void add(Object obj);
1541
1542 public void add(String name, Object value);
1543
1544 public void add(String name, double value);
1545
1546 public void add(String name, long value);
1547
1548 public void add(String name, boolean value);
1549 }
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564 public interface Convertible
1565 {
1566 public void toJSON(Output out);
1567
1568 public void fromJSON(Map object);
1569 }
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582 public interface Convertor
1583 {
1584 public void toJSON(Object obj, Output out);
1585
1586 public Object fromJSON(Map object);
1587 }
1588
1589
1590
1591
1592
1593
1594 public interface Generator
1595 {
1596 public void addJSON(Appendable buffer);
1597 }
1598
1599
1600
1601
1602
1603 public static class Literal implements Generator
1604 {
1605 private String _json;
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615 public Literal(String json)
1616 {
1617 if (LOG.isDebugEnabled())
1618 parse(json);
1619 _json = json;
1620 }
1621
1622 @Override
1623 public String toString()
1624 {
1625 return _json;
1626 }
1627
1628 public void addJSON(Appendable buffer)
1629 {
1630 try
1631 {
1632 buffer.append(_json);
1633 }
1634 catch(IOException e)
1635 {
1636 throw new RuntimeException(e);
1637 }
1638 }
1639 }
1640 }