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