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