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