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