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