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