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