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.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=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,convertor);
542 }
543
544
545
546
547
548
549
550
551
552 protected Convertor getConvertor(Class forClass)
553 {
554 Class c=forClass;
555 Convertor convertor=(Convertor)_convertors.get(c);
556 if (convertor==null && this!=__default)
557 convertor=__default.getConvertor(forClass);
558
559 while (convertor==null&&c!=null&&c!=Object.class)
560 {
561 Class[] ifs=c.getInterfaces();
562 int i=0;
563 while (convertor==null&&ifs!=null&&i<ifs.length)
564 convertor=(Convertor)_convertors.get(ifs[i++]);
565 if (convertor==null)
566 {
567 c=c.getSuperclass();
568 convertor=(Convertor)_convertors.get(c);
569 }
570 }
571 return convertor;
572 }
573
574
575
576 public Object parse(Source source, boolean stripOuterComment)
577 {
578 int comment_state=0;
579 if (!stripOuterComment)
580 return parse(source);
581
582 int strip_state=1;
583
584 Object o=null;
585 while (source.hasNext())
586 {
587 char c=source.peek();
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606 comment
607 else if (comment_state>1)
608 {
609 switch (c)
610 {
611 case '*':
612 comment_state=3;
613 break;
614 case '/':
615 if (comment_state==3)
616 {
617 comment_state=0;
618 if (strip_state==2)
619 return o;
620 }
621 else
622 comment_state=2;
623 break;
624 default:
625 comment_state=2;
626 }
627 }
628
629 else if (comment_state<0)
630 {
631 switch (c)
632 {
633 case '\r':
634 case '\n':
635 comment_state=0;
636 default:
637 break;
638 }
639 }
640
641 else
642 {
643 if (!Character.isWhitespace(c))
644 {
645 if (c=='/')
646 comment_state=1;
647 else if (c=='*')
648 comment_state=3;
649 else if (o==null)
650 {
651 o=parse(source);
652 continue;
653 }
654 }
655 }
656
657 source.next();
658 }
659
660 return o;
661 }
662
663
664 public Object parse(Source source)
665 {
666 int comment_state=0;
667
668 while (source.hasNext())
669 {
670 char c=source.peek();
671
672
673
674
675
676
677
678
679
680
681
682
683
684 comment
685 else if (comment_state>1)
686 {
687 switch (c)
688 {
689 case '*':
690 comment_state=3;
691 break;
692 case '/':
693 if (comment_state==3)
694 comment_state=0;
695 else
696 comment_state=2;
697 break;
698 default:
699 comment_state=2;
700 }
701 }
702
703 else if (comment_state<0)
704 {
705 switch (c)
706 {
707 case '\r':
708 case '\n':
709 comment_state=0;
710 break;
711 default:
712 break;
713 }
714 }
715
716 else
717 {
718 switch (c)
719 {
720 case '{':
721 return parseObject(source);
722 case '[':
723 return parseArray(source);
724 case '"':
725 return parseString(source);
726 case '-':
727 return parseNumber(source);
728
729 case 'n':
730 complete("null",source);
731 return null;
732 case 't':
733 complete("true",source);
734 return Boolean.TRUE;
735 case 'f':
736 complete("false",source);
737 return Boolean.FALSE;
738 case 'u':
739 complete("undefined",source);
740 return null;
741
742 case '/':
743 comment_state=1;
744 break;
745
746 default:
747 if (Character.isDigit(c))
748 return parseNumber(source);
749 else if (Character.isWhitespace(c))
750 break;
751 throw new IllegalStateException("unknown char '"+c+"'("+(int)c+") in "+source);
752 }
753 }
754 source.next();
755 }
756
757 return null;
758 }
759
760 protected Object parseObject(Source source)
761 {
762 if (source.next()!='{')
763 throw new IllegalStateException();
764 Map map=newMap();
765
766 char next=seekTo("\"}",source);
767
768 while (source.hasNext())
769 {
770 if (next=='}')
771 {
772 source.next();
773 break;
774 }
775
776 String name=parseString(source);
777 seekTo(':',source);
778 source.next();
779
780 Object value=contextFor(name).parse(source);
781 map.put(name,value);
782
783 seekTo(",}",source);
784 next=source.next();
785 if (next=='}')
786 break;
787 else
788 next=seekTo("\"}",source);
789 }
790
791 String classname=(String)map.get("class");
792 if (classname!=null)
793 {
794 try
795 {
796 Class c=Loader.loadClass(JSON.class,classname);
797 return convertTo(c,map);
798 }
799 catch (ClassNotFoundException e)
800 {
801 e.printStackTrace();
802 }
803 }
804 return map;
805 }
806
807
808 private Object parseArray(Source source)
809 {
810 if (source.next()!='[')
811 throw new IllegalStateException();
812
813 int size=0;
814 ArrayList list=null;
815 Object item=null;
816 boolean coma=true;
817
818 while (source.hasNext())
819 {
820 char c=source.peek();
821 switch (c)
822 {
823 case ']':
824 source.next();
825 switch(size)
826 {
827 case 0:
828 return newArray(0);
829 case 1:
830 Object array = newArray(1);
831 Array.set(array,0,item);
832 return array;
833 default:
834 return list.toArray(newArray(list.size()));
835 }
836
837 case ',':
838 if (coma)
839 throw new IllegalStateException();
840 coma=true;
841 source.next();
842 break;
843
844 default:
845 if (Character.isWhitespace(c))
846 source.next();
847 else
848 {
849 coma=false;
850 if (size++==0)
851 item=contextForArray().parse(source);
852 else if (list==null)
853 {
854 list=new ArrayList();
855 list.add(item);
856 item=contextForArray().parse(source);
857 list.add(item);
858 item=null;
859 }
860 else
861 {
862 item=contextForArray().parse(source);
863 list.add(item);
864 item=null;
865 }
866 }
867 }
868
869 }
870
871 throw new IllegalStateException("unexpected end of array");
872 }
873
874
875 private String parseString(Source source)
876 {
877 if (source.next()!='"')
878 throw new IllegalStateException();
879
880 boolean escape=false;
881
882 StringBuffer b=null;
883 final char[] scratch=source.scratchBuffer();
884
885 if (scratch!=null)
886 {
887 int i=0;
888 while (source.hasNext())
889 {
890 if(i>=scratch.length)
891 {
892
893
894 b=new StringBuffer(scratch.length*2);
895 b.append(scratch,0,i);
896 break;
897 }
898
899 char c=source.next();
900
901 if (escape)
902 {
903 escape=false;
904 switch (c)
905 {
906 case '"':
907 scratch[i++]='"';
908 break;
909 case '\\':
910 scratch[i++]='\\';
911 break;
912 case '/':
913 scratch[i++]='/';
914 break;
915 case 'b':
916 scratch[i++]='\b';
917 break;
918 case 'f':
919 scratch[i++]='\f';
920 break;
921 case 'n':
922 scratch[i++]='\n';
923 break;
924 case 'r':
925 scratch[i++]='\r';
926 break;
927 case 't':
928 scratch[i++]='\t';
929 break;
930 case 'u':
931 char uc=(char)((TypeUtil.convertHexDigit((byte)source.next())<<12)+
932 (TypeUtil.convertHexDigit((byte)source.next())<<8)+
933 (TypeUtil.convertHexDigit((byte)source.next())<<4)+
934 (TypeUtil.convertHexDigit((byte)source.next())));
935 scratch[i++]=uc;
936 break;
937 default:
938 scratch[i++]=c;
939 }
940 }
941 else if (c=='\\')
942 {
943 escape=true;
944 continue;
945 }
946 else if (c=='\"')
947 {
948
949 return toString(scratch,0,i);
950 }
951 else
952 scratch[i++]=c;
953 }
954
955
956 if (b==null)
957 return toString(scratch,0,i);
958 }
959 else
960 b=new StringBuffer(getStringBufferSize());
961
962
963
964 synchronized (b)
965 {
966 while (source.hasNext())
967 {
968 char c=source.next();
969
970 if (escape)
971 {
972 escape=false;
973 switch (c)
974 {
975 case '"':
976 b.append('"');
977 break;
978 case '\\':
979 b.append('\\');
980 break;
981 case '/':
982 b.append('/');
983 break;
984 case 'b':
985 b.append('\b');
986 break;
987 case 'f':
988 b.append('\f');
989 break;
990 case 'n':
991 b.append('\n');
992 break;
993 case 'r':
994 b.append('\r');
995 break;
996 case 't':
997 b.append('\t');
998 break;
999 case 'u':
1000 char uc=(char)((TypeUtil.convertHexDigit((byte)source.next())<<12)+
1001 (TypeUtil.convertHexDigit((byte)source.next())<<8)+
1002 (TypeUtil.convertHexDigit((byte)source.next())<<4)+
1003 (TypeUtil.convertHexDigit((byte)source.next())));
1004 b.append(uc);
1005 break;
1006 default:
1007 b.append(c);
1008 }
1009 }
1010 else if (c=='\\')
1011 {
1012 escape=true;
1013 continue;
1014 }
1015 else if (c=='\"')
1016 break;
1017 else
1018 b.append(c);
1019 }
1020
1021 return b.toString();
1022 }
1023 }
1024
1025 public Number parseNumber(Source source)
1026 {
1027 boolean minus=false;
1028 long number=0;
1029 StringBuffer buffer=null;
1030
1031 longLoop: while (source.hasNext())
1032 {
1033 char c=source.peek();
1034 switch (c)
1035 {
1036 case '0':
1037 case '1':
1038 case '2':
1039 case '3':
1040 case '4':
1041 case '5':
1042 case '6':
1043 case '7':
1044 case '8':
1045 case '9':
1046 number=number*10+(c-'0');
1047 source.next();
1048 break;
1049
1050 case '-':
1051 case '+':
1052 if (number!=0)
1053 throw new IllegalStateException("bad number");
1054 minus=true;
1055 source.next();
1056 break;
1057
1058 case '.':
1059 case 'e':
1060 case 'E':
1061 buffer=new StringBuffer(16);
1062 if(minus)
1063 buffer.append('-');
1064 buffer.append(number);
1065 buffer.append(c);
1066 source.next();
1067 break longLoop;
1068
1069 default:
1070 break longLoop;
1071 }
1072 }
1073
1074 if (buffer==null)
1075 return TypeUtil.newLong(minus?-1*number:number);
1076
1077 synchronized (buffer)
1078 {
1079 doubleLoop: while (source.hasNext())
1080 {
1081 char c=source.peek();
1082 switch (c)
1083 {
1084 case '0':
1085 case '1':
1086 case '2':
1087 case '3':
1088 case '4':
1089 case '5':
1090 case '6':
1091 case '7':
1092 case '8':
1093 case '9':
1094 case '-':
1095 case '.':
1096 case '+':
1097 case 'e':
1098 case 'E':
1099 buffer.append(c);
1100 source.next();
1101 break;
1102
1103 default:
1104 break doubleLoop;
1105 }
1106 }
1107 return new Double(buffer.toString());
1108 }
1109 }
1110
1111 protected void seekTo(char seek, Source source)
1112 {
1113 while (source.hasNext())
1114 {
1115 char c=source.peek();
1116 if (c==seek)
1117 return;
1118
1119 if (!Character.isWhitespace(c))
1120 throw new IllegalStateException("Unexpected '"+c+" while seeking '"+seek+"'");
1121 source.next();
1122 }
1123
1124 throw new IllegalStateException("Expected '"+seek+"'");
1125 }
1126
1127 protected char seekTo(String seek, Source source)
1128 {
1129 while (source.hasNext())
1130 {
1131 char c=source.peek();
1132 if (seek.indexOf(c)>=0)
1133 {
1134 return c;
1135 }
1136
1137 if (!Character.isWhitespace(c))
1138 throw new IllegalStateException("Unexpected '"+c+"' while seeking one of '"+seek+"'");
1139 source.next();
1140 }
1141
1142 throw new IllegalStateException("Expected one of '"+seek+"'");
1143 }
1144
1145 protected static void complete(String seek, Source source)
1146 {
1147 int i=0;
1148 while (source.hasNext()&&i<seek.length())
1149 {
1150 char c=source.next();
1151 if (c!=seek.charAt(i++))
1152 throw new IllegalStateException("Unexpected '"+c+" while seeking \""+seek+"\"");
1153 }
1154
1155 if (i<seek.length())
1156 throw new IllegalStateException("Expected \""+seek+"\"");
1157 }
1158
1159
1160 public interface Source
1161 {
1162 boolean hasNext();
1163
1164 char next();
1165
1166 char peek();
1167
1168 char[] scratchBuffer();
1169 }
1170
1171 public static class StringSource implements Source
1172 {
1173 private final String string;
1174 private int index;
1175 private char[] scratch;
1176
1177 public StringSource(String s)
1178 {
1179 string=s;
1180 }
1181
1182 public boolean hasNext()
1183 {
1184 if (index<string.length())
1185 return true;
1186 scratch=null;
1187 return false;
1188 }
1189
1190 public char next()
1191 {
1192 return string.charAt(index++);
1193 }
1194
1195 public char peek()
1196 {
1197 return string.charAt(index);
1198 }
1199
1200 public String toString()
1201 {
1202 return string.substring(0,index)+"|||"+string.substring(index);
1203 }
1204
1205 public char[] scratchBuffer()
1206 {
1207 if (scratch==null)
1208 scratch=new char[string.length()];
1209 return scratch;
1210 }
1211 }
1212
1213 public static class ReaderSource implements Source
1214 {
1215 private Reader _reader;
1216 private int _next=-1;
1217 private char[] scratch;
1218
1219 public ReaderSource(Reader r)
1220 {
1221 _reader=r;
1222 }
1223
1224 public void setReader(Reader reader)
1225 {
1226 _reader=reader;
1227 _next=-1;
1228 }
1229
1230 public boolean hasNext()
1231 {
1232 getNext();
1233 if (_next<0)
1234 {
1235 scratch=null;
1236 return false;
1237 }
1238 return true;
1239 }
1240
1241 public char next()
1242 {
1243 getNext();
1244 char c=(char)_next;
1245 _next=-1;
1246 return c;
1247 }
1248
1249 public char peek()
1250 {
1251 getNext();
1252 return (char)_next;
1253 }
1254
1255 private void getNext()
1256 {
1257 if (_next<0)
1258 {
1259 try
1260 {
1261 _next=_reader.read();
1262 }
1263 catch (IOException e)
1264 {
1265 throw new RuntimeException(e);
1266 }
1267 }
1268 }
1269
1270 public char[] scratchBuffer()
1271 {
1272 if (scratch==null)
1273 scratch=new char[1024];
1274 return scratch;
1275 }
1276
1277 }
1278
1279
1280
1281
1282
1283 public interface Output
1284 {
1285 public void addClass(Class c);
1286
1287 public void add(Object obj);
1288
1289 public void add(String name, Object value);
1290
1291 public void add(String name, double value);
1292
1293 public void add(String name, long value);
1294
1295 public void add(String name, boolean value);
1296 }
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312 public interface Convertible
1313 {
1314 public void toJSON(Output out);
1315
1316 public void fromJSON(Map object);
1317 }
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328 public interface Convertor
1329 {
1330 public void toJSON(Object obj, Output out);
1331
1332 public Object fromJSON(Map object);
1333 }
1334
1335
1336
1337
1338
1339
1340
1341 public interface Generator
1342 {
1343 public void addJSON(StringBuffer buffer);
1344 }
1345
1346
1347
1348
1349
1350 public static class Literal implements Generator
1351 {
1352 private String _json;
1353
1354
1355
1356
1357
1358
1359 public Literal(String json)
1360 {
1361 if (Log.isDebugEnabled())
1362 parse(json);
1363 _json=json;
1364 }
1365
1366 public String toString()
1367 {
1368 return _json;
1369 }
1370
1371 public void addJSON(StringBuffer buffer)
1372 {
1373 buffer.append(_json);
1374 }
1375 }
1376 }