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 return handleUnknown(source, c);
752 }
753 }
754 source.next();
755 }
756
757 return null;
758 }
759
760 protected Object handleUnknown(Source source, char c)
761 {
762 throw new IllegalStateException("unknown char '"+c+"'("+(int)c+") in "+source);
763 }
764
765 protected Object parseObject(Source source)
766 {
767 if (source.next()!='{')
768 throw new IllegalStateException();
769 Map map=newMap();
770
771 char next=seekTo("\"}",source);
772
773 while (source.hasNext())
774 {
775 if (next=='}')
776 {
777 source.next();
778 break;
779 }
780
781 String name=parseString(source);
782 seekTo(':',source);
783 source.next();
784
785 Object value=contextFor(name).parse(source);
786 map.put(name,value);
787
788 seekTo(",}",source);
789 next=source.next();
790 if (next=='}')
791 break;
792 else
793 next=seekTo("\"}",source);
794 }
795
796 String classname=(String)map.get("class");
797 if (classname!=null)
798 {
799 try
800 {
801 Class c=Loader.loadClass(JSON.class,classname);
802 return convertTo(c,map);
803 }
804 catch (ClassNotFoundException e)
805 {
806 e.printStackTrace();
807 }
808 }
809 return map;
810 }
811
812
813 protected Object parseArray(Source source)
814 {
815 if (source.next()!='[')
816 throw new IllegalStateException();
817
818 int size=0;
819 ArrayList list=null;
820 Object item=null;
821 boolean coma=true;
822
823 while (source.hasNext())
824 {
825 char c=source.peek();
826 switch (c)
827 {
828 case ']':
829 source.next();
830 switch(size)
831 {
832 case 0:
833 return newArray(0);
834 case 1:
835 Object array = newArray(1);
836 Array.set(array,0,item);
837 return array;
838 default:
839 return list.toArray(newArray(list.size()));
840 }
841
842 case ',':
843 if (coma)
844 throw new IllegalStateException();
845 coma=true;
846 source.next();
847 break;
848
849 default:
850 if (Character.isWhitespace(c))
851 source.next();
852 else
853 {
854 coma=false;
855 if (size++==0)
856 item=contextForArray().parse(source);
857 else if (list==null)
858 {
859 list=new ArrayList();
860 list.add(item);
861 item=contextForArray().parse(source);
862 list.add(item);
863 item=null;
864 }
865 else
866 {
867 item=contextForArray().parse(source);
868 list.add(item);
869 item=null;
870 }
871 }
872 }
873
874 }
875
876 throw new IllegalStateException("unexpected end of array");
877 }
878
879
880 protected String parseString(Source source)
881 {
882 if (source.next()!='"')
883 throw new IllegalStateException();
884
885 boolean escape=false;
886
887 StringBuffer b=null;
888 final char[] scratch=source.scratchBuffer();
889
890 if (scratch!=null)
891 {
892 int i=0;
893 while (source.hasNext())
894 {
895 if(i>=scratch.length)
896 {
897
898
899 b=new StringBuffer(scratch.length*2);
900 b.append(scratch,0,i);
901 break;
902 }
903
904 char c=source.next();
905
906 if (escape)
907 {
908 escape=false;
909 switch (c)
910 {
911 case '"':
912 scratch[i++]='"';
913 break;
914 case '\\':
915 scratch[i++]='\\';
916 break;
917 case '/':
918 scratch[i++]='/';
919 break;
920 case 'b':
921 scratch[i++]='\b';
922 break;
923 case 'f':
924 scratch[i++]='\f';
925 break;
926 case 'n':
927 scratch[i++]='\n';
928 break;
929 case 'r':
930 scratch[i++]='\r';
931 break;
932 case 't':
933 scratch[i++]='\t';
934 break;
935 case 'u':
936 char uc=(char)((TypeUtil.convertHexDigit((byte)source.next())<<12)+
937 (TypeUtil.convertHexDigit((byte)source.next())<<8)+
938 (TypeUtil.convertHexDigit((byte)source.next())<<4)+
939 (TypeUtil.convertHexDigit((byte)source.next())));
940 scratch[i++]=uc;
941 break;
942 default:
943 scratch[i++]=c;
944 }
945 }
946 else if (c=='\\')
947 {
948 escape=true;
949 continue;
950 }
951 else if (c=='\"')
952 {
953
954 return toString(scratch,0,i);
955 }
956 else
957 scratch[i++]=c;
958 }
959
960
961 if (b==null)
962 return toString(scratch,0,i);
963 }
964 else
965 b=new StringBuffer(getStringBufferSize());
966
967
968
969 synchronized (b)
970 {
971 while (source.hasNext())
972 {
973 char c=source.next();
974
975 if (escape)
976 {
977 escape=false;
978 switch (c)
979 {
980 case '"':
981 b.append('"');
982 break;
983 case '\\':
984 b.append('\\');
985 break;
986 case '/':
987 b.append('/');
988 break;
989 case 'b':
990 b.append('\b');
991 break;
992 case 'f':
993 b.append('\f');
994 break;
995 case 'n':
996 b.append('\n');
997 break;
998 case 'r':
999 b.append('\r');
1000 break;
1001 case 't':
1002 b.append('\t');
1003 break;
1004 case 'u':
1005 char uc=(char)((TypeUtil.convertHexDigit((byte)source.next())<<12)+
1006 (TypeUtil.convertHexDigit((byte)source.next())<<8)+
1007 (TypeUtil.convertHexDigit((byte)source.next())<<4)+
1008 (TypeUtil.convertHexDigit((byte)source.next())));
1009 b.append(uc);
1010 break;
1011 default:
1012 b.append(c);
1013 }
1014 }
1015 else if (c=='\\')
1016 {
1017 escape=true;
1018 continue;
1019 }
1020 else if (c=='\"')
1021 break;
1022 else
1023 b.append(c);
1024 }
1025
1026 return b.toString();
1027 }
1028 }
1029
1030 public Number parseNumber(Source source)
1031 {
1032 boolean minus=false;
1033 long number=0;
1034 StringBuffer buffer=null;
1035
1036 longLoop: while (source.hasNext())
1037 {
1038 char c=source.peek();
1039 switch (c)
1040 {
1041 case '0':
1042 case '1':
1043 case '2':
1044 case '3':
1045 case '4':
1046 case '5':
1047 case '6':
1048 case '7':
1049 case '8':
1050 case '9':
1051 number=number*10+(c-'0');
1052 source.next();
1053 break;
1054
1055 case '-':
1056 case '+':
1057 if (number!=0)
1058 throw new IllegalStateException("bad number");
1059 minus=true;
1060 source.next();
1061 break;
1062
1063 case '.':
1064 case 'e':
1065 case 'E':
1066 buffer=new StringBuffer(16);
1067 if(minus)
1068 buffer.append('-');
1069 buffer.append(number);
1070 buffer.append(c);
1071 source.next();
1072 break longLoop;
1073
1074 default:
1075 break longLoop;
1076 }
1077 }
1078
1079 if (buffer==null)
1080 return TypeUtil.newLong(minus?-1*number:number);
1081
1082 synchronized (buffer)
1083 {
1084 doubleLoop: while (source.hasNext())
1085 {
1086 char c=source.peek();
1087 switch (c)
1088 {
1089 case '0':
1090 case '1':
1091 case '2':
1092 case '3':
1093 case '4':
1094 case '5':
1095 case '6':
1096 case '7':
1097 case '8':
1098 case '9':
1099 case '-':
1100 case '.':
1101 case '+':
1102 case 'e':
1103 case 'E':
1104 buffer.append(c);
1105 source.next();
1106 break;
1107
1108 default:
1109 break doubleLoop;
1110 }
1111 }
1112 return new Double(buffer.toString());
1113 }
1114 }
1115
1116 protected void seekTo(char seek, Source source)
1117 {
1118 while (source.hasNext())
1119 {
1120 char c=source.peek();
1121 if (c==seek)
1122 return;
1123
1124 if (!Character.isWhitespace(c))
1125 throw new IllegalStateException("Unexpected '"+c+" while seeking '"+seek+"'");
1126 source.next();
1127 }
1128
1129 throw new IllegalStateException("Expected '"+seek+"'");
1130 }
1131
1132 protected char seekTo(String seek, Source source)
1133 {
1134 while (source.hasNext())
1135 {
1136 char c=source.peek();
1137 if (seek.indexOf(c)>=0)
1138 {
1139 return c;
1140 }
1141
1142 if (!Character.isWhitespace(c))
1143 throw new IllegalStateException("Unexpected '"+c+"' while seeking one of '"+seek+"'");
1144 source.next();
1145 }
1146
1147 throw new IllegalStateException("Expected one of '"+seek+"'");
1148 }
1149
1150 protected static void complete(String seek, Source source)
1151 {
1152 int i=0;
1153 while (source.hasNext()&&i<seek.length())
1154 {
1155 char c=source.next();
1156 if (c!=seek.charAt(i++))
1157 throw new IllegalStateException("Unexpected '"+c+" while seeking \""+seek+"\"");
1158 }
1159
1160 if (i<seek.length())
1161 throw new IllegalStateException("Expected \""+seek+"\"");
1162 }
1163
1164
1165 public interface Source
1166 {
1167 boolean hasNext();
1168
1169 char next();
1170
1171 char peek();
1172
1173 char[] scratchBuffer();
1174 }
1175
1176 public static class StringSource implements Source
1177 {
1178 private final String string;
1179 private int index;
1180 private char[] scratch;
1181
1182 public StringSource(String s)
1183 {
1184 string=s;
1185 }
1186
1187 public boolean hasNext()
1188 {
1189 if (index<string.length())
1190 return true;
1191 scratch=null;
1192 return false;
1193 }
1194
1195 public char next()
1196 {
1197 return string.charAt(index++);
1198 }
1199
1200 public char peek()
1201 {
1202 return string.charAt(index);
1203 }
1204
1205 public String toString()
1206 {
1207 return string.substring(0,index)+"|||"+string.substring(index);
1208 }
1209
1210 public char[] scratchBuffer()
1211 {
1212 if (scratch==null)
1213 scratch=new char[string.length()];
1214 return scratch;
1215 }
1216 }
1217
1218 public static class ReaderSource implements Source
1219 {
1220 private Reader _reader;
1221 private int _next=-1;
1222 private char[] scratch;
1223
1224 public ReaderSource(Reader r)
1225 {
1226 _reader=r;
1227 }
1228
1229 public void setReader(Reader reader)
1230 {
1231 _reader=reader;
1232 _next=-1;
1233 }
1234
1235 public boolean hasNext()
1236 {
1237 getNext();
1238 if (_next<0)
1239 {
1240 scratch=null;
1241 return false;
1242 }
1243 return true;
1244 }
1245
1246 public char next()
1247 {
1248 getNext();
1249 char c=(char)_next;
1250 _next=-1;
1251 return c;
1252 }
1253
1254 public char peek()
1255 {
1256 getNext();
1257 return (char)_next;
1258 }
1259
1260 private void getNext()
1261 {
1262 if (_next<0)
1263 {
1264 try
1265 {
1266 _next=_reader.read();
1267 }
1268 catch (IOException e)
1269 {
1270 throw new RuntimeException(e);
1271 }
1272 }
1273 }
1274
1275 public char[] scratchBuffer()
1276 {
1277 if (scratch==null)
1278 scratch=new char[1024];
1279 return scratch;
1280 }
1281
1282 }
1283
1284
1285
1286
1287
1288 public interface Output
1289 {
1290 public void addClass(Class c);
1291
1292 public void add(Object obj);
1293
1294 public void add(String name, Object value);
1295
1296 public void add(String name, double value);
1297
1298 public void add(String name, long value);
1299
1300 public void add(String name, boolean value);
1301 }
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317 public interface Convertible
1318 {
1319 public void toJSON(Output out);
1320
1321 public void fromJSON(Map object);
1322 }
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333 public interface Convertor
1334 {
1335 public void toJSON(Object obj, Output out);
1336
1337 public Object fromJSON(Map object);
1338 }
1339
1340
1341
1342
1343
1344
1345
1346 public interface Generator
1347 {
1348 public void addJSON(StringBuffer buffer);
1349 }
1350
1351
1352
1353
1354
1355 public static class Literal implements Generator
1356 {
1357 private String _json;
1358
1359
1360
1361
1362
1363
1364 public Literal(String json)
1365 {
1366 if (Log.isDebugEnabled())
1367 parse(json);
1368 _json=json;
1369 }
1370
1371 public String toString()
1372 {
1373 return _json;
1374 }
1375
1376 public void addJSON(StringBuffer buffer)
1377 {
1378 buffer.append(_json);
1379 }
1380 }
1381 }