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