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