Mercurial Hosting > luan
comparison src/org/eclipse/jetty/util/ajax/JSON.java @ 802:3428c60d7cfc
replace jetty jars with source
| author | Franklin Schmidt <fschmidt@gmail.com> |
|---|---|
| date | Wed, 07 Sep 2016 21:15:48 -0600 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 801:6a21393191c1 | 802:3428c60d7cfc |
|---|---|
| 1 // | |
| 2 // ======================================================================== | |
| 3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. | |
| 4 // ------------------------------------------------------------------------ | |
| 5 // All rights reserved. This program and the accompanying materials | |
| 6 // are made available under the terms of the Eclipse Public License v1.0 | |
| 7 // and Apache License v2.0 which accompanies this distribution. | |
| 8 // | |
| 9 // The Eclipse Public License is available at | |
| 10 // http://www.eclipse.org/legal/epl-v10.html | |
| 11 // | |
| 12 // The Apache License v2.0 is available at | |
| 13 // http://www.opensource.org/licenses/apache2.0.php | |
| 14 // | |
| 15 // You may elect to redistribute this code under either of these licenses. | |
| 16 // ======================================================================== | |
| 17 // | |
| 18 | |
| 19 package org.eclipse.jetty.util.ajax; | |
| 20 | |
| 21 import java.io.Externalizable; | |
| 22 import java.io.IOException; | |
| 23 import java.io.InputStream; | |
| 24 import java.io.Reader; | |
| 25 import java.lang.reflect.Array; | |
| 26 import java.util.ArrayList; | |
| 27 import java.util.Collection; | |
| 28 import java.util.HashMap; | |
| 29 import java.util.Iterator; | |
| 30 import java.util.Map; | |
| 31 import java.util.concurrent.ConcurrentHashMap; | |
| 32 | |
| 33 import org.eclipse.jetty.util.IO; | |
| 34 import org.eclipse.jetty.util.Loader; | |
| 35 import org.eclipse.jetty.util.QuotedStringTokenizer; | |
| 36 import org.eclipse.jetty.util.TypeUtil; | |
| 37 import org.eclipse.jetty.util.log.Log; | |
| 38 import org.eclipse.jetty.util.log.Logger; | |
| 39 | |
| 40 /** | |
| 41 * JSON Parser and Generator. | |
| 42 * <p /> | |
| 43 * This class provides some static methods to convert POJOs to and from JSON | |
| 44 * notation. The mapping from JSON to java is: | |
| 45 * | |
| 46 * <pre> | |
| 47 * object ==> Map | |
| 48 * array ==> Object[] | |
| 49 * number ==> Double or Long | |
| 50 * string ==> String | |
| 51 * null ==> null | |
| 52 * bool ==> Boolean | |
| 53 * </pre> | |
| 54 | |
| 55 * The java to JSON mapping is: | |
| 56 * | |
| 57 * <pre> | |
| 58 * String --> string | |
| 59 * Number --> number | |
| 60 * Map --> object | |
| 61 * List --> array | |
| 62 * Array --> array | |
| 63 * null --> null | |
| 64 * Boolean--> boolean | |
| 65 * Object --> string (dubious!) | |
| 66 * </pre> | |
| 67 * | |
| 68 * The interface {@link JSON.Convertible} may be implemented by classes that | |
| 69 * wish to externalize and initialize specific fields to and from JSON objects. | |
| 70 * Only directed acyclic graphs of objects are supported. | |
| 71 * <p /> | |
| 72 * The interface {@link JSON.Generator} may be implemented by classes that know | |
| 73 * how to render themselves as JSON and the {@link #toString(Object)} method | |
| 74 * will use {@link JSON.Generator#addJSON(Appendable)} to generate the JSON. | |
| 75 * The class {@link JSON.Literal} may be used to hold pre-generated JSON object. | |
| 76 * <p /> | |
| 77 * The interface {@link JSON.Convertor} may be implemented to provide static | |
| 78 * converters for objects that may be registered with | |
| 79 * {@link #registerConvertor(Class, Convertor)}. | |
| 80 * These converters are looked up by class, interface and super class by | |
| 81 * {@link #getConvertor(Class)}. | |
| 82 * <p /> | |
| 83 * If a JSON object has a "class" field, then a java class for that name is | |
| 84 * loaded and the method {@link #convertTo(Class,Map)} is used to find a | |
| 85 * {@link JSON.Convertor} for that class. | |
| 86 * <p /> | |
| 87 * If a JSON object has a "x-class" field then a direct lookup for a | |
| 88 * {@link JSON.Convertor} for that class name is done (without loading the class). | |
| 89 */ | |
| 90 public class JSON | |
| 91 { | |
| 92 static final Logger LOG = Log.getLogger(JSON.class); | |
| 93 public final static JSON DEFAULT = new JSON(); | |
| 94 | |
| 95 private Map<String, Convertor> _convertors = new ConcurrentHashMap<String, Convertor>(); | |
| 96 private int _stringBufferSize = 1024; | |
| 97 | |
| 98 public JSON() | |
| 99 { | |
| 100 } | |
| 101 | |
| 102 /** | |
| 103 * @return the initial stringBuffer size to use when creating JSON strings | |
| 104 * (default 1024) | |
| 105 */ | |
| 106 public int getStringBufferSize() | |
| 107 { | |
| 108 return _stringBufferSize; | |
| 109 } | |
| 110 | |
| 111 /** | |
| 112 * @param stringBufferSize | |
| 113 * the initial stringBuffer size to use when creating JSON | |
| 114 * strings (default 1024) | |
| 115 */ | |
| 116 public void setStringBufferSize(int stringBufferSize) | |
| 117 { | |
| 118 _stringBufferSize = stringBufferSize; | |
| 119 } | |
| 120 | |
| 121 /** | |
| 122 * Register a {@link Convertor} for a class or interface. | |
| 123 * | |
| 124 * @param forClass | |
| 125 * The class or interface that the convertor applies to | |
| 126 * @param convertor | |
| 127 * the convertor | |
| 128 */ | |
| 129 public static void registerConvertor(Class forClass, Convertor convertor) | |
| 130 { | |
| 131 DEFAULT.addConvertor(forClass,convertor); | |
| 132 } | |
| 133 | |
| 134 public static JSON getDefault() | |
| 135 { | |
| 136 return DEFAULT; | |
| 137 } | |
| 138 | |
| 139 @Deprecated | |
| 140 public static void setDefault(JSON json) | |
| 141 { | |
| 142 } | |
| 143 | |
| 144 public static String toString(Object object) | |
| 145 { | |
| 146 StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize()); | |
| 147 DEFAULT.append(buffer,object); | |
| 148 return buffer.toString(); | |
| 149 } | |
| 150 | |
| 151 public static String toString(Map object) | |
| 152 { | |
| 153 StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize()); | |
| 154 DEFAULT.appendMap(buffer,object); | |
| 155 return buffer.toString(); | |
| 156 } | |
| 157 | |
| 158 public static String toString(Object[] array) | |
| 159 { | |
| 160 StringBuilder buffer = new StringBuilder(DEFAULT.getStringBufferSize()); | |
| 161 DEFAULT.appendArray(buffer,array); | |
| 162 return buffer.toString(); | |
| 163 } | |
| 164 | |
| 165 /** | |
| 166 * @param s | |
| 167 * String containing JSON object or array. | |
| 168 * @return A Map, Object array or primitive array parsed from the JSON. | |
| 169 */ | |
| 170 public static Object parse(String s) | |
| 171 { | |
| 172 return DEFAULT.parse(new StringSource(s),false); | |
| 173 } | |
| 174 | |
| 175 /** | |
| 176 * @param s | |
| 177 * String containing JSON object or array. | |
| 178 * @param stripOuterComment | |
| 179 * If true, an outer comment around the JSON is ignored. | |
| 180 * @return A Map, Object array or primitive array parsed from the JSON. | |
| 181 */ | |
| 182 public static Object parse(String s, boolean stripOuterComment) | |
| 183 { | |
| 184 return DEFAULT.parse(new StringSource(s),stripOuterComment); | |
| 185 } | |
| 186 | |
| 187 /** | |
| 188 * @param in | |
| 189 * Reader containing JSON object or array. | |
| 190 * @return A Map, Object array or primitive array parsed from the JSON. | |
| 191 */ | |
| 192 public static Object parse(Reader in) throws IOException | |
| 193 { | |
| 194 return DEFAULT.parse(new ReaderSource(in),false); | |
| 195 } | |
| 196 | |
| 197 /** | |
| 198 * @param in | |
| 199 * Reader containing JSON object or array. | |
| 200 * @param stripOuterComment | |
| 201 * If true, an outer comment around the JSON is ignored. | |
| 202 * @return A Map, Object array or primitive array parsed from the JSON. | |
| 203 */ | |
| 204 public static Object parse(Reader in, boolean stripOuterComment) throws IOException | |
| 205 { | |
| 206 return DEFAULT.parse(new ReaderSource(in),stripOuterComment); | |
| 207 } | |
| 208 | |
| 209 /** | |
| 210 * @deprecated use {@link #parse(Reader)} | |
| 211 * @param in | |
| 212 * Reader containing JSON object or array. | |
| 213 * @return A Map, Object array or primitive array parsed from the JSON. | |
| 214 */ | |
| 215 @Deprecated | |
| 216 public static Object parse(InputStream in) throws IOException | |
| 217 { | |
| 218 return DEFAULT.parse(new StringSource(IO.toString(in)),false); | |
| 219 } | |
| 220 | |
| 221 /** | |
| 222 * @deprecated use {@link #parse(Reader, boolean)} | |
| 223 * @param in | |
| 224 * Stream containing JSON object or array. | |
| 225 * @param stripOuterComment | |
| 226 * If true, an outer comment around the JSON is ignored. | |
| 227 * @return A Map, Object array or primitive array parsed from the JSON. | |
| 228 */ | |
| 229 @Deprecated | |
| 230 public static Object parse(InputStream in, boolean stripOuterComment) throws IOException | |
| 231 { | |
| 232 return DEFAULT.parse(new StringSource(IO.toString(in)),stripOuterComment); | |
| 233 } | |
| 234 | |
| 235 /** | |
| 236 * Convert Object to JSON | |
| 237 * | |
| 238 * @param object | |
| 239 * The object to convert | |
| 240 * @return The JSON String | |
| 241 */ | |
| 242 public String toJSON(Object object) | |
| 243 { | |
| 244 StringBuilder buffer = new StringBuilder(getStringBufferSize()); | |
| 245 append(buffer,object); | |
| 246 return buffer.toString(); | |
| 247 } | |
| 248 | |
| 249 /** | |
| 250 * Convert JSON to Object | |
| 251 * | |
| 252 * @param json | |
| 253 * The json to convert | |
| 254 * @return The object | |
| 255 */ | |
| 256 public Object fromJSON(String json) | |
| 257 { | |
| 258 Source source = new StringSource(json); | |
| 259 return parse(source); | |
| 260 } | |
| 261 | |
| 262 @Deprecated | |
| 263 public void append(StringBuffer buffer, Object object) | |
| 264 { | |
| 265 append((Appendable)buffer,object); | |
| 266 } | |
| 267 | |
| 268 /** | |
| 269 * Append object as JSON to string buffer. | |
| 270 * | |
| 271 * @param buffer | |
| 272 * the buffer to append to | |
| 273 * @param object | |
| 274 * the object to append | |
| 275 */ | |
| 276 public void append(Appendable buffer, Object object) | |
| 277 { | |
| 278 try | |
| 279 { | |
| 280 if (object == null) | |
| 281 { | |
| 282 buffer.append("null"); | |
| 283 } | |
| 284 // Most likely first | |
| 285 else if (object instanceof Map) | |
| 286 { | |
| 287 appendMap(buffer,(Map)object); | |
| 288 } | |
| 289 else if (object instanceof String) | |
| 290 { | |
| 291 appendString(buffer,(String)object); | |
| 292 } | |
| 293 else if (object instanceof Number) | |
| 294 { | |
| 295 appendNumber(buffer,(Number)object); | |
| 296 } | |
| 297 else if (object instanceof Boolean) | |
| 298 { | |
| 299 appendBoolean(buffer,(Boolean)object); | |
| 300 } | |
| 301 else if (object.getClass().isArray()) | |
| 302 { | |
| 303 appendArray(buffer,object); | |
| 304 } | |
| 305 else if (object instanceof Character) | |
| 306 { | |
| 307 appendString(buffer,object.toString()); | |
| 308 } | |
| 309 else if (object instanceof Convertible) | |
| 310 { | |
| 311 appendJSON(buffer,(Convertible)object); | |
| 312 } | |
| 313 else if (object instanceof Generator) | |
| 314 { | |
| 315 appendJSON(buffer,(Generator)object); | |
| 316 } | |
| 317 else | |
| 318 { | |
| 319 // Check Convertor before Collection to support JSONCollectionConvertor | |
| 320 Convertor convertor = getConvertor(object.getClass()); | |
| 321 if (convertor != null) | |
| 322 { | |
| 323 appendJSON(buffer,convertor,object); | |
| 324 } | |
| 325 else if (object instanceof Collection) | |
| 326 { | |
| 327 appendArray(buffer,(Collection)object); | |
| 328 } | |
| 329 else | |
| 330 { | |
| 331 appendString(buffer,object.toString()); | |
| 332 } | |
| 333 } | |
| 334 } | |
| 335 catch (IOException e) | |
| 336 { | |
| 337 throw new RuntimeException(e); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 @Deprecated | |
| 342 public void appendNull(StringBuffer buffer) | |
| 343 { | |
| 344 appendNull((Appendable)buffer); | |
| 345 } | |
| 346 | |
| 347 public void appendNull(Appendable buffer) | |
| 348 { | |
| 349 try | |
| 350 { | |
| 351 buffer.append("null"); | |
| 352 } | |
| 353 catch (IOException e) | |
| 354 { | |
| 355 throw new RuntimeException(e); | |
| 356 } | |
| 357 } | |
| 358 | |
| 359 @Deprecated | |
| 360 public void appendJSON(final StringBuffer buffer, final Convertor convertor, final Object object) | |
| 361 { | |
| 362 appendJSON((Appendable)buffer,convertor,object); | |
| 363 } | |
| 364 | |
| 365 public void appendJSON(final Appendable buffer, final Convertor convertor, final Object object) | |
| 366 { | |
| 367 appendJSON(buffer,new Convertible() | |
| 368 { | |
| 369 public void fromJSON(Map object) | |
| 370 { | |
| 371 } | |
| 372 | |
| 373 public void toJSON(Output out) | |
| 374 { | |
| 375 convertor.toJSON(object,out); | |
| 376 } | |
| 377 }); | |
| 378 } | |
| 379 | |
| 380 @Deprecated | |
| 381 public void appendJSON(final StringBuffer buffer, Convertible converter) | |
| 382 { | |
| 383 appendJSON((Appendable)buffer,converter); | |
| 384 } | |
| 385 | |
| 386 public void appendJSON(final Appendable buffer, Convertible converter) | |
| 387 { | |
| 388 ConvertableOutput out=new ConvertableOutput(buffer); | |
| 389 converter.toJSON(out); | |
| 390 out.complete(); | |
| 391 } | |
| 392 | |
| 393 @Deprecated | |
| 394 public void appendJSON(StringBuffer buffer, Generator generator) | |
| 395 { | |
| 396 generator.addJSON(buffer); | |
| 397 } | |
| 398 | |
| 399 public void appendJSON(Appendable buffer, Generator generator) | |
| 400 { | |
| 401 generator.addJSON(buffer); | |
| 402 } | |
| 403 | |
| 404 @Deprecated | |
| 405 public void appendMap(StringBuffer buffer, Map<?,?> map) | |
| 406 { | |
| 407 appendMap((Appendable)buffer,map); | |
| 408 } | |
| 409 | |
| 410 public void appendMap(Appendable buffer, Map<?,?> map) | |
| 411 { | |
| 412 try | |
| 413 { | |
| 414 if (map == null) | |
| 415 { | |
| 416 appendNull(buffer); | |
| 417 return; | |
| 418 } | |
| 419 | |
| 420 buffer.append('{'); | |
| 421 Iterator<?> iter = map.entrySet().iterator(); | |
| 422 while (iter.hasNext()) | |
| 423 { | |
| 424 Map.Entry<?,?> entry = (Map.Entry<?,?>)iter.next(); | |
| 425 QuotedStringTokenizer.quote(buffer,entry.getKey().toString()); | |
| 426 buffer.append(':'); | |
| 427 append(buffer,entry.getValue()); | |
| 428 if (iter.hasNext()) | |
| 429 buffer.append(','); | |
| 430 } | |
| 431 | |
| 432 buffer.append('}'); | |
| 433 } | |
| 434 catch (IOException e) | |
| 435 { | |
| 436 throw new RuntimeException(e); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 @Deprecated | |
| 441 public void appendArray(StringBuffer buffer, Collection collection) | |
| 442 { | |
| 443 appendArray((Appendable)buffer,collection); | |
| 444 } | |
| 445 | |
| 446 public void appendArray(Appendable buffer, Collection collection) | |
| 447 { | |
| 448 try | |
| 449 { | |
| 450 if (collection == null) | |
| 451 { | |
| 452 appendNull(buffer); | |
| 453 return; | |
| 454 } | |
| 455 | |
| 456 buffer.append('['); | |
| 457 Iterator iter = collection.iterator(); | |
| 458 boolean first = true; | |
| 459 while (iter.hasNext()) | |
| 460 { | |
| 461 if (!first) | |
| 462 buffer.append(','); | |
| 463 | |
| 464 first = false; | |
| 465 append(buffer,iter.next()); | |
| 466 } | |
| 467 | |
| 468 buffer.append(']'); | |
| 469 } | |
| 470 catch (IOException e) | |
| 471 { | |
| 472 throw new RuntimeException(e); | |
| 473 } | |
| 474 } | |
| 475 | |
| 476 @Deprecated | |
| 477 public void appendArray(StringBuffer buffer, Object array) | |
| 478 { | |
| 479 appendArray((Appendable)buffer,array); | |
| 480 } | |
| 481 | |
| 482 public void appendArray(Appendable buffer, Object array) | |
| 483 { | |
| 484 try | |
| 485 { | |
| 486 if (array == null) | |
| 487 { | |
| 488 appendNull(buffer); | |
| 489 return; | |
| 490 } | |
| 491 | |
| 492 buffer.append('['); | |
| 493 int length = Array.getLength(array); | |
| 494 | |
| 495 for (int i = 0; i < length; i++) | |
| 496 { | |
| 497 if (i != 0) | |
| 498 buffer.append(','); | |
| 499 append(buffer,Array.get(array,i)); | |
| 500 } | |
| 501 | |
| 502 buffer.append(']'); | |
| 503 } | |
| 504 catch (IOException e) | |
| 505 { | |
| 506 throw new RuntimeException(e); | |
| 507 } | |
| 508 } | |
| 509 | |
| 510 @Deprecated | |
| 511 public void appendBoolean(StringBuffer buffer, Boolean b) | |
| 512 { | |
| 513 appendBoolean((Appendable)buffer,b); | |
| 514 } | |
| 515 | |
| 516 public void appendBoolean(Appendable buffer, Boolean b) | |
| 517 { | |
| 518 try | |
| 519 { | |
| 520 if (b == null) | |
| 521 { | |
| 522 appendNull(buffer); | |
| 523 return; | |
| 524 } | |
| 525 buffer.append(b?"true":"false"); | |
| 526 } | |
| 527 catch (IOException e) | |
| 528 { | |
| 529 throw new RuntimeException(e); | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 @Deprecated | |
| 534 public void appendNumber(StringBuffer buffer, Number number) | |
| 535 { | |
| 536 appendNumber((Appendable)buffer,number); | |
| 537 } | |
| 538 | |
| 539 public void appendNumber(Appendable buffer, Number number) | |
| 540 { | |
| 541 try | |
| 542 { | |
| 543 if (number == null) | |
| 544 { | |
| 545 appendNull(buffer); | |
| 546 return; | |
| 547 } | |
| 548 buffer.append(String.valueOf(number)); | |
| 549 } | |
| 550 catch (IOException e) | |
| 551 { | |
| 552 throw new RuntimeException(e); | |
| 553 } | |
| 554 } | |
| 555 | |
| 556 @Deprecated | |
| 557 public void appendString(StringBuffer buffer, String string) | |
| 558 { | |
| 559 appendString((Appendable)buffer,string); | |
| 560 } | |
| 561 | |
| 562 public void appendString(Appendable buffer, String string) | |
| 563 { | |
| 564 if (string == null) | |
| 565 { | |
| 566 appendNull(buffer); | |
| 567 return; | |
| 568 } | |
| 569 | |
| 570 QuotedStringTokenizer.quote(buffer,string); | |
| 571 } | |
| 572 | |
| 573 // Parsing utilities | |
| 574 | |
| 575 protected String toString(char[] buffer, int offset, int length) | |
| 576 { | |
| 577 return new String(buffer,offset,length); | |
| 578 } | |
| 579 | |
| 580 protected Map<String, Object> newMap() | |
| 581 { | |
| 582 return new HashMap<String, Object>(); | |
| 583 } | |
| 584 | |
| 585 protected Object[] newArray(int size) | |
| 586 { | |
| 587 return new Object[size]; | |
| 588 } | |
| 589 | |
| 590 protected JSON contextForArray() | |
| 591 { | |
| 592 return this; | |
| 593 } | |
| 594 | |
| 595 protected JSON contextFor(String field) | |
| 596 { | |
| 597 return this; | |
| 598 } | |
| 599 | |
| 600 protected Object convertTo(Class type, Map map) | |
| 601 { | |
| 602 if (type != null && Convertible.class.isAssignableFrom(type)) | |
| 603 { | |
| 604 try | |
| 605 { | |
| 606 Convertible conv = (Convertible)type.newInstance(); | |
| 607 conv.fromJSON(map); | |
| 608 return conv; | |
| 609 } | |
| 610 catch (Exception e) | |
| 611 { | |
| 612 throw new RuntimeException(e); | |
| 613 } | |
| 614 } | |
| 615 | |
| 616 Convertor convertor = getConvertor(type); | |
| 617 if (convertor != null) | |
| 618 { | |
| 619 return convertor.fromJSON(map); | |
| 620 } | |
| 621 return map; | |
| 622 } | |
| 623 | |
| 624 /** | |
| 625 * Register a {@link Convertor} for a class or interface. | |
| 626 * | |
| 627 * @param forClass | |
| 628 * The class or interface that the convertor applies to | |
| 629 * @param convertor | |
| 630 * the convertor | |
| 631 */ | |
| 632 public void addConvertor(Class forClass, Convertor convertor) | |
| 633 { | |
| 634 _convertors.put(forClass.getName(),convertor); | |
| 635 } | |
| 636 | |
| 637 /** | |
| 638 * Lookup a convertor for a class. | |
| 639 * <p> | |
| 640 * If no match is found for the class, then the interfaces for the class are | |
| 641 * tried. If still no match is found, then the super class and it's | |
| 642 * interfaces are tried recursively. | |
| 643 * | |
| 644 * @param forClass | |
| 645 * The class | |
| 646 * @return a {@link JSON.Convertor} or null if none were found. | |
| 647 */ | |
| 648 protected Convertor getConvertor(Class forClass) | |
| 649 { | |
| 650 Class cls = forClass; | |
| 651 Convertor convertor = _convertors.get(cls.getName()); | |
| 652 if (convertor == null && this != DEFAULT) | |
| 653 convertor = DEFAULT.getConvertor(cls); | |
| 654 | |
| 655 while (convertor == null && cls != Object.class) | |
| 656 { | |
| 657 Class[] ifs = cls.getInterfaces(); | |
| 658 int i = 0; | |
| 659 while (convertor == null && ifs != null && i < ifs.length) | |
| 660 convertor = _convertors.get(ifs[i++].getName()); | |
| 661 if (convertor == null) | |
| 662 { | |
| 663 cls = cls.getSuperclass(); | |
| 664 convertor = _convertors.get(cls.getName()); | |
| 665 } | |
| 666 } | |
| 667 return convertor; | |
| 668 } | |
| 669 | |
| 670 /** | |
| 671 * Register a {@link JSON.Convertor} for a named class or interface. | |
| 672 * | |
| 673 * @param name | |
| 674 * name of a class or an interface that the convertor applies to | |
| 675 * @param convertor | |
| 676 * the convertor | |
| 677 */ | |
| 678 public void addConvertorFor(String name, Convertor convertor) | |
| 679 { | |
| 680 _convertors.put(name,convertor); | |
| 681 } | |
| 682 | |
| 683 /** | |
| 684 * Lookup a convertor for a named class. | |
| 685 * | |
| 686 * @param name | |
| 687 * name of the class | |
| 688 * @return a {@link JSON.Convertor} or null if none were found. | |
| 689 */ | |
| 690 public Convertor getConvertorFor(String name) | |
| 691 { | |
| 692 Convertor convertor = _convertors.get(name); | |
| 693 if (convertor == null && this != DEFAULT) | |
| 694 convertor = DEFAULT.getConvertorFor(name); | |
| 695 return convertor; | |
| 696 } | |
| 697 | |
| 698 public Object parse(Source source, boolean stripOuterComment) | |
| 699 { | |
| 700 int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//" | |
| 701 if (!stripOuterComment) | |
| 702 return parse(source); | |
| 703 | |
| 704 int strip_state = 1; // 0=no strip, 1=wait for /*, 2= wait for */ | |
| 705 | |
| 706 Object o = null; | |
| 707 while (source.hasNext()) | |
| 708 { | |
| 709 char c = source.peek(); | |
| 710 | |
| 711 // handle // or /* comment | |
| 712 if (comment_state == 1) | |
| 713 { | |
| 714 switch (c) | |
| 715 { | |
| 716 case '/': | |
| 717 comment_state = -1; | |
| 718 break; | |
| 719 case '*': | |
| 720 comment_state = 2; | |
| 721 if (strip_state == 1) | |
| 722 { | |
| 723 comment_state = 0; | |
| 724 strip_state = 2; | |
| 725 } | |
| 726 } | |
| 727 } | |
| 728 // handle /* */ 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 { | |
| 739 comment_state = 0; | |
| 740 if (strip_state == 2) | |
| 741 return o; | |
| 742 } | |
| 743 else | |
| 744 comment_state = 2; | |
| 745 break; | |
| 746 default: | |
| 747 comment_state = 2; | |
| 748 } | |
| 749 } | |
| 750 // handle // comment | |
| 751 else if (comment_state < 0) | |
| 752 { | |
| 753 switch (c) | |
| 754 { | |
| 755 case '\r': | |
| 756 case '\n': | |
| 757 comment_state = 0; | |
| 758 default: | |
| 759 break; | |
| 760 } | |
| 761 } | |
| 762 // handle unknown | |
| 763 else | |
| 764 { | |
| 765 if (!Character.isWhitespace(c)) | |
| 766 { | |
| 767 if (c == '/') | |
| 768 comment_state = 1; | |
| 769 else if (c == '*') | |
| 770 comment_state = 3; | |
| 771 else if (o == null) | |
| 772 { | |
| 773 o = parse(source); | |
| 774 continue; | |
| 775 } | |
| 776 } | |
| 777 } | |
| 778 | |
| 779 source.next(); | |
| 780 } | |
| 781 | |
| 782 return o; | |
| 783 } | |
| 784 | |
| 785 public Object parse(Source source) | |
| 786 { | |
| 787 int comment_state = 0; // 0=no comment, 1="/", 2="/*", 3="/* *" -1="//" | |
| 788 | |
| 789 while (source.hasNext()) | |
| 790 { | |
| 791 char c = source.peek(); | |
| 792 | |
| 793 // handle // or /* comment | |
| 794 if (comment_state == 1) | |
| 795 { | |
| 796 switch (c) | |
| 797 { | |
| 798 case '/': | |
| 799 comment_state = -1; | |
| 800 break; | |
| 801 case '*': | |
| 802 comment_state = 2; | |
| 803 } | |
| 804 } | |
| 805 // handle /* */ comment | |
| 806 else if (comment_state > 1) | |
| 807 { | |
| 808 switch (c) | |
| 809 { | |
| 810 case '*': | |
| 811 comment_state = 3; | |
| 812 break; | |
| 813 case '/': | |
| 814 if (comment_state == 3) | |
| 815 comment_state = 0; | |
| 816 else | |
| 817 comment_state = 2; | |
| 818 break; | |
| 819 default: | |
| 820 comment_state = 2; | |
| 821 } | |
| 822 } | |
| 823 // handle // comment | |
| 824 else if (comment_state < 0) | |
| 825 { | |
| 826 switch (c) | |
| 827 { | |
| 828 case '\r': | |
| 829 case '\n': | |
| 830 comment_state = 0; | |
| 831 break; | |
| 832 default: | |
| 833 break; | |
| 834 } | |
| 835 } | |
| 836 // handle unknown | |
| 837 else | |
| 838 { | |
| 839 switch (c) | |
| 840 { | |
| 841 case '{': | |
| 842 return parseObject(source); | |
| 843 case '[': | |
| 844 return parseArray(source); | |
| 845 case '"': | |
| 846 return parseString(source); | |
| 847 case '-': | |
| 848 return parseNumber(source); | |
| 849 | |
| 850 case 'n': | |
| 851 complete("null",source); | |
| 852 return null; | |
| 853 case 't': | |
| 854 complete("true",source); | |
| 855 return Boolean.TRUE; | |
| 856 case 'f': | |
| 857 complete("false",source); | |
| 858 return Boolean.FALSE; | |
| 859 case 'u': | |
| 860 complete("undefined",source); | |
| 861 return null; | |
| 862 case 'N': | |
| 863 complete("NaN",source); | |
| 864 return null; | |
| 865 | |
| 866 case '/': | |
| 867 comment_state = 1; | |
| 868 break; | |
| 869 | |
| 870 default: | |
| 871 if (Character.isDigit(c)) | |
| 872 return parseNumber(source); | |
| 873 else if (Character.isWhitespace(c)) | |
| 874 break; | |
| 875 return handleUnknown(source,c); | |
| 876 } | |
| 877 } | |
| 878 source.next(); | |
| 879 } | |
| 880 | |
| 881 return null; | |
| 882 } | |
| 883 | |
| 884 protected Object handleUnknown(Source source, char c) | |
| 885 { | |
| 886 throw new IllegalStateException("unknown char '" + c + "'(" + (int)c + ") in " + source); | |
| 887 } | |
| 888 | |
| 889 protected Object parseObject(Source source) | |
| 890 { | |
| 891 if (source.next() != '{') | |
| 892 throw new IllegalStateException(); | |
| 893 Map<String, Object> map = newMap(); | |
| 894 | |
| 895 char next = seekTo("\"}",source); | |
| 896 | |
| 897 while (source.hasNext()) | |
| 898 { | |
| 899 if (next == '}') | |
| 900 { | |
| 901 source.next(); | |
| 902 break; | |
| 903 } | |
| 904 | |
| 905 String name = parseString(source); | |
| 906 seekTo(':',source); | |
| 907 source.next(); | |
| 908 | |
| 909 Object value = contextFor(name).parse(source); | |
| 910 map.put(name,value); | |
| 911 | |
| 912 seekTo(",}",source); | |
| 913 next = source.next(); | |
| 914 if (next == '}') | |
| 915 break; | |
| 916 else | |
| 917 next = seekTo("\"}",source); | |
| 918 } | |
| 919 | |
| 920 String xclassname = (String)map.get("x-class"); | |
| 921 if (xclassname != null) | |
| 922 { | |
| 923 Convertor c = getConvertorFor(xclassname); | |
| 924 if (c != null) | |
| 925 return c.fromJSON(map); | |
| 926 LOG.warn("No Convertor for x-class '{}'", xclassname); | |
| 927 } | |
| 928 | |
| 929 String classname = (String)map.get("class"); | |
| 930 if (classname != null) | |
| 931 { | |
| 932 try | |
| 933 { | |
| 934 Class c = Loader.loadClass(JSON.class,classname); | |
| 935 return convertTo(c,map); | |
| 936 } | |
| 937 catch (ClassNotFoundException e) | |
| 938 { | |
| 939 LOG.warn("No Class for '{}'", classname); | |
| 940 } | |
| 941 } | |
| 942 | |
| 943 return map; | |
| 944 } | |
| 945 | |
| 946 protected Object parseArray(Source source) | |
| 947 { | |
| 948 if (source.next() != '[') | |
| 949 throw new IllegalStateException(); | |
| 950 | |
| 951 int size = 0; | |
| 952 ArrayList list = null; | |
| 953 Object item = null; | |
| 954 boolean coma = true; | |
| 955 | |
| 956 while (source.hasNext()) | |
| 957 { | |
| 958 char c = source.peek(); | |
| 959 switch (c) | |
| 960 { | |
| 961 case ']': | |
| 962 source.next(); | |
| 963 switch (size) | |
| 964 { | |
| 965 case 0: | |
| 966 return newArray(0); | |
| 967 case 1: | |
| 968 Object array = newArray(1); | |
| 969 Array.set(array,0,item); | |
| 970 return array; | |
| 971 default: | |
| 972 return list.toArray(newArray(list.size())); | |
| 973 } | |
| 974 | |
| 975 case ',': | |
| 976 if (coma) | |
| 977 throw new IllegalStateException(); | |
| 978 coma = true; | |
| 979 source.next(); | |
| 980 break; | |
| 981 | |
| 982 default: | |
| 983 if (Character.isWhitespace(c)) | |
| 984 source.next(); | |
| 985 else | |
| 986 { | |
| 987 coma = false; | |
| 988 if (size++ == 0) | |
| 989 item = contextForArray().parse(source); | |
| 990 else if (list == null) | |
| 991 { | |
| 992 list = new ArrayList(); | |
| 993 list.add(item); | |
| 994 item = contextForArray().parse(source); | |
| 995 list.add(item); | |
| 996 item = null; | |
| 997 } | |
| 998 else | |
| 999 { | |
| 1000 item = contextForArray().parse(source); | |
| 1001 list.add(item); | |
| 1002 item = null; | |
| 1003 } | |
| 1004 } | |
| 1005 } | |
| 1006 | |
| 1007 } | |
| 1008 | |
| 1009 throw new IllegalStateException("unexpected end of array"); | |
| 1010 } | |
| 1011 | |
| 1012 protected String parseString(Source source) | |
| 1013 { | |
| 1014 if (source.next() != '"') | |
| 1015 throw new IllegalStateException(); | |
| 1016 | |
| 1017 boolean escape = false; | |
| 1018 | |
| 1019 StringBuilder b = null; | |
| 1020 final char[] scratch = source.scratchBuffer(); | |
| 1021 | |
| 1022 if (scratch != null) | |
| 1023 { | |
| 1024 int i = 0; | |
| 1025 while (source.hasNext()) | |
| 1026 { | |
| 1027 if (i >= scratch.length) | |
| 1028 { | |
| 1029 // we have filled the scratch buffer, so we must | |
| 1030 // use the StringBuffer for a large string | |
| 1031 b = new StringBuilder(scratch.length * 2); | |
| 1032 b.append(scratch,0,i); | |
| 1033 break; | |
| 1034 } | |
| 1035 | |
| 1036 char c = source.next(); | |
| 1037 | |
| 1038 if (escape) | |
| 1039 { | |
| 1040 escape = false; | |
| 1041 switch (c) | |
| 1042 { | |
| 1043 case '"': | |
| 1044 scratch[i++] = '"'; | |
| 1045 break; | |
| 1046 case '\\': | |
| 1047 scratch[i++] = '\\'; | |
| 1048 break; | |
| 1049 case '/': | |
| 1050 scratch[i++] = '/'; | |
| 1051 break; | |
| 1052 case 'b': | |
| 1053 scratch[i++] = '\b'; | |
| 1054 break; | |
| 1055 case 'f': | |
| 1056 scratch[i++] = '\f'; | |
| 1057 break; | |
| 1058 case 'n': | |
| 1059 scratch[i++] = '\n'; | |
| 1060 break; | |
| 1061 case 'r': | |
| 1062 scratch[i++] = '\r'; | |
| 1063 break; | |
| 1064 case 't': | |
| 1065 scratch[i++] = '\t'; | |
| 1066 break; | |
| 1067 case 'u': | |
| 1068 char uc = (char)((TypeUtil.convertHexDigit((byte)source.next()) << 12) + (TypeUtil.convertHexDigit((byte)source.next()) << 8) | |
| 1069 + (TypeUtil.convertHexDigit((byte)source.next()) << 4) + (TypeUtil.convertHexDigit((byte)source.next()))); | |
| 1070 scratch[i++] = uc; | |
| 1071 break; | |
| 1072 default: | |
| 1073 scratch[i++] = c; | |
| 1074 } | |
| 1075 } | |
| 1076 else if (c == '\\') | |
| 1077 { | |
| 1078 escape = true; | |
| 1079 } | |
| 1080 else if (c == '\"') | |
| 1081 { | |
| 1082 // Return string that fits within scratch buffer | |
| 1083 return toString(scratch,0,i); | |
| 1084 } | |
| 1085 else | |
| 1086 { | |
| 1087 scratch[i++] = c; | |
| 1088 } | |
| 1089 } | |
| 1090 | |
| 1091 // Missing end quote, but return string anyway ? | |
| 1092 if (b == null) | |
| 1093 return toString(scratch,0,i); | |
| 1094 } | |
| 1095 else | |
| 1096 b = new StringBuilder(getStringBufferSize()); | |
| 1097 | |
| 1098 // parse large string into string buffer | |
| 1099 final StringBuilder builder=b; | |
| 1100 while (source.hasNext()) | |
| 1101 { | |
| 1102 char c = source.next(); | |
| 1103 | |
| 1104 if (escape) | |
| 1105 { | |
| 1106 escape = false; | |
| 1107 switch (c) | |
| 1108 { | |
| 1109 case '"': | |
| 1110 builder.append('"'); | |
| 1111 break; | |
| 1112 case '\\': | |
| 1113 builder.append('\\'); | |
| 1114 break; | |
| 1115 case '/': | |
| 1116 builder.append('/'); | |
| 1117 break; | |
| 1118 case 'b': | |
| 1119 builder.append('\b'); | |
| 1120 break; | |
| 1121 case 'f': | |
| 1122 builder.append('\f'); | |
| 1123 break; | |
| 1124 case 'n': | |
| 1125 builder.append('\n'); | |
| 1126 break; | |
| 1127 case 'r': | |
| 1128 builder.append('\r'); | |
| 1129 break; | |
| 1130 case 't': | |
| 1131 builder.append('\t'); | |
| 1132 break; | |
| 1133 case 'u': | |
| 1134 char uc = (char)((TypeUtil.convertHexDigit((byte)source.next()) << 12) + (TypeUtil.convertHexDigit((byte)source.next()) << 8) | |
| 1135 + (TypeUtil.convertHexDigit((byte)source.next()) << 4) + (TypeUtil.convertHexDigit((byte)source.next()))); | |
| 1136 builder.append(uc); | |
| 1137 break; | |
| 1138 default: | |
| 1139 builder.append(c); | |
| 1140 } | |
| 1141 } | |
| 1142 else if (c == '\\') | |
| 1143 { | |
| 1144 escape = true; | |
| 1145 } | |
| 1146 else if (c == '\"') | |
| 1147 { | |
| 1148 break; | |
| 1149 } | |
| 1150 else | |
| 1151 { | |
| 1152 builder.append(c); | |
| 1153 } | |
| 1154 } | |
| 1155 return builder.toString(); | |
| 1156 } | |
| 1157 | |
| 1158 public Number parseNumber(Source source) | |
| 1159 { | |
| 1160 boolean minus = false; | |
| 1161 long number = 0; | |
| 1162 StringBuilder buffer = null; | |
| 1163 | |
| 1164 longLoop: while (source.hasNext()) | |
| 1165 { | |
| 1166 char c = source.peek(); | |
| 1167 switch (c) | |
| 1168 { | |
| 1169 case '0': | |
| 1170 case '1': | |
| 1171 case '2': | |
| 1172 case '3': | |
| 1173 case '4': | |
| 1174 case '5': | |
| 1175 case '6': | |
| 1176 case '7': | |
| 1177 case '8': | |
| 1178 case '9': | |
| 1179 number = number * 10 + (c - '0'); | |
| 1180 source.next(); | |
| 1181 break; | |
| 1182 | |
| 1183 case '-': | |
| 1184 case '+': | |
| 1185 if (number != 0) | |
| 1186 throw new IllegalStateException("bad number"); | |
| 1187 minus = true; | |
| 1188 source.next(); | |
| 1189 break; | |
| 1190 | |
| 1191 case '.': | |
| 1192 case 'e': | |
| 1193 case 'E': | |
| 1194 buffer = new StringBuilder(16); | |
| 1195 if (minus) | |
| 1196 buffer.append('-'); | |
| 1197 buffer.append(number); | |
| 1198 buffer.append(c); | |
| 1199 source.next(); | |
| 1200 break longLoop; | |
| 1201 | |
| 1202 default: | |
| 1203 break longLoop; | |
| 1204 } | |
| 1205 } | |
| 1206 | |
| 1207 if (buffer == null) | |
| 1208 return minus ? -1 * number : number; | |
| 1209 | |
| 1210 doubleLoop: while (source.hasNext()) | |
| 1211 { | |
| 1212 char c = source.peek(); | |
| 1213 switch (c) | |
| 1214 { | |
| 1215 case '0': | |
| 1216 case '1': | |
| 1217 case '2': | |
| 1218 case '3': | |
| 1219 case '4': | |
| 1220 case '5': | |
| 1221 case '6': | |
| 1222 case '7': | |
| 1223 case '8': | |
| 1224 case '9': | |
| 1225 case '-': | |
| 1226 case '.': | |
| 1227 case '+': | |
| 1228 case 'e': | |
| 1229 case 'E': | |
| 1230 buffer.append(c); | |
| 1231 source.next(); | |
| 1232 break; | |
| 1233 | |
| 1234 default: | |
| 1235 break doubleLoop; | |
| 1236 } | |
| 1237 } | |
| 1238 return new Double(buffer.toString()); | |
| 1239 | |
| 1240 } | |
| 1241 | |
| 1242 protected void seekTo(char seek, Source source) | |
| 1243 { | |
| 1244 while (source.hasNext()) | |
| 1245 { | |
| 1246 char c = source.peek(); | |
| 1247 if (c == seek) | |
| 1248 return; | |
| 1249 | |
| 1250 if (!Character.isWhitespace(c)) | |
| 1251 throw new IllegalStateException("Unexpected '" + c + " while seeking '" + seek + "'"); | |
| 1252 source.next(); | |
| 1253 } | |
| 1254 | |
| 1255 throw new IllegalStateException("Expected '" + seek + "'"); | |
| 1256 } | |
| 1257 | |
| 1258 protected char seekTo(String seek, Source source) | |
| 1259 { | |
| 1260 while (source.hasNext()) | |
| 1261 { | |
| 1262 char c = source.peek(); | |
| 1263 if (seek.indexOf(c) >= 0) | |
| 1264 { | |
| 1265 return c; | |
| 1266 } | |
| 1267 | |
| 1268 if (!Character.isWhitespace(c)) | |
| 1269 throw new IllegalStateException("Unexpected '" + c + "' while seeking one of '" + seek + "'"); | |
| 1270 source.next(); | |
| 1271 } | |
| 1272 | |
| 1273 throw new IllegalStateException("Expected one of '" + seek + "'"); | |
| 1274 } | |
| 1275 | |
| 1276 protected static void complete(String seek, Source source) | |
| 1277 { | |
| 1278 int i = 0; | |
| 1279 while (source.hasNext() && i < seek.length()) | |
| 1280 { | |
| 1281 char c = source.next(); | |
| 1282 if (c != seek.charAt(i++)) | |
| 1283 throw new IllegalStateException("Unexpected '" + c + " while seeking \"" + seek + "\""); | |
| 1284 } | |
| 1285 | |
| 1286 if (i < seek.length()) | |
| 1287 throw new IllegalStateException("Expected \"" + seek + "\""); | |
| 1288 } | |
| 1289 | |
| 1290 private final class ConvertableOutput implements Output | |
| 1291 { | |
| 1292 private final Appendable _buffer; | |
| 1293 char c = '{'; | |
| 1294 | |
| 1295 private ConvertableOutput(Appendable buffer) | |
| 1296 { | |
| 1297 _buffer = buffer; | |
| 1298 } | |
| 1299 | |
| 1300 public void complete() | |
| 1301 { | |
| 1302 try | |
| 1303 { | |
| 1304 if (c == '{') | |
| 1305 _buffer.append("{}"); | |
| 1306 else if (c != 0) | |
| 1307 _buffer.append("}"); | |
| 1308 } | |
| 1309 catch (IOException e) | |
| 1310 { | |
| 1311 throw new RuntimeException(e); | |
| 1312 } | |
| 1313 } | |
| 1314 | |
| 1315 public void add(Object obj) | |
| 1316 { | |
| 1317 if (c == 0) | |
| 1318 throw new IllegalStateException(); | |
| 1319 append(_buffer,obj); | |
| 1320 c = 0; | |
| 1321 } | |
| 1322 | |
| 1323 public void addClass(Class type) | |
| 1324 { | |
| 1325 try | |
| 1326 { | |
| 1327 if (c == 0) | |
| 1328 throw new IllegalStateException(); | |
| 1329 _buffer.append(c); | |
| 1330 _buffer.append("\"class\":"); | |
| 1331 append(_buffer,type.getName()); | |
| 1332 c = ','; | |
| 1333 } | |
| 1334 catch (IOException e) | |
| 1335 { | |
| 1336 throw new RuntimeException(e); | |
| 1337 } | |
| 1338 } | |
| 1339 | |
| 1340 public void add(String name, Object value) | |
| 1341 { | |
| 1342 try | |
| 1343 { | |
| 1344 if (c == 0) | |
| 1345 throw new IllegalStateException(); | |
| 1346 _buffer.append(c); | |
| 1347 QuotedStringTokenizer.quote(_buffer,name); | |
| 1348 _buffer.append(':'); | |
| 1349 append(_buffer,value); | |
| 1350 c = ','; | |
| 1351 } | |
| 1352 catch (IOException e) | |
| 1353 { | |
| 1354 throw new RuntimeException(e); | |
| 1355 } | |
| 1356 } | |
| 1357 | |
| 1358 public void add(String name, double value) | |
| 1359 { | |
| 1360 try | |
| 1361 { | |
| 1362 if (c == 0) | |
| 1363 throw new IllegalStateException(); | |
| 1364 _buffer.append(c); | |
| 1365 QuotedStringTokenizer.quote(_buffer,name); | |
| 1366 _buffer.append(':'); | |
| 1367 appendNumber(_buffer, value); | |
| 1368 c = ','; | |
| 1369 } | |
| 1370 catch (IOException e) | |
| 1371 { | |
| 1372 throw new RuntimeException(e); | |
| 1373 } | |
| 1374 } | |
| 1375 | |
| 1376 public void add(String name, long value) | |
| 1377 { | |
| 1378 try | |
| 1379 { | |
| 1380 if (c == 0) | |
| 1381 throw new IllegalStateException(); | |
| 1382 _buffer.append(c); | |
| 1383 QuotedStringTokenizer.quote(_buffer,name); | |
| 1384 _buffer.append(':'); | |
| 1385 appendNumber(_buffer, value); | |
| 1386 c = ','; | |
| 1387 } | |
| 1388 catch (IOException e) | |
| 1389 { | |
| 1390 throw new RuntimeException(e); | |
| 1391 } | |
| 1392 } | |
| 1393 | |
| 1394 public void add(String name, boolean value) | |
| 1395 { | |
| 1396 try | |
| 1397 { | |
| 1398 if (c == 0) | |
| 1399 throw new IllegalStateException(); | |
| 1400 _buffer.append(c); | |
| 1401 QuotedStringTokenizer.quote(_buffer,name); | |
| 1402 _buffer.append(':'); | |
| 1403 appendBoolean(_buffer,value?Boolean.TRUE:Boolean.FALSE); | |
| 1404 c = ','; | |
| 1405 } | |
| 1406 catch (IOException e) | |
| 1407 { | |
| 1408 throw new RuntimeException(e); | |
| 1409 } | |
| 1410 } | |
| 1411 } | |
| 1412 | |
| 1413 public interface Source | |
| 1414 { | |
| 1415 boolean hasNext(); | |
| 1416 | |
| 1417 char next(); | |
| 1418 | |
| 1419 char peek(); | |
| 1420 | |
| 1421 char[] scratchBuffer(); | |
| 1422 } | |
| 1423 | |
| 1424 public static class StringSource implements Source | |
| 1425 { | |
| 1426 private final String string; | |
| 1427 private int index; | |
| 1428 private char[] scratch; | |
| 1429 | |
| 1430 public StringSource(String s) | |
| 1431 { | |
| 1432 string = s; | |
| 1433 } | |
| 1434 | |
| 1435 public boolean hasNext() | |
| 1436 { | |
| 1437 if (index < string.length()) | |
| 1438 return true; | |
| 1439 scratch = null; | |
| 1440 return false; | |
| 1441 } | |
| 1442 | |
| 1443 public char next() | |
| 1444 { | |
| 1445 return string.charAt(index++); | |
| 1446 } | |
| 1447 | |
| 1448 public char peek() | |
| 1449 { | |
| 1450 return string.charAt(index); | |
| 1451 } | |
| 1452 | |
| 1453 @Override | |
| 1454 public String toString() | |
| 1455 { | |
| 1456 return string.substring(0,index) + "|||" + string.substring(index); | |
| 1457 } | |
| 1458 | |
| 1459 public char[] scratchBuffer() | |
| 1460 { | |
| 1461 if (scratch == null) | |
| 1462 scratch = new char[string.length()]; | |
| 1463 return scratch; | |
| 1464 } | |
| 1465 } | |
| 1466 | |
| 1467 public static class ReaderSource implements Source | |
| 1468 { | |
| 1469 private Reader _reader; | |
| 1470 private int _next = -1; | |
| 1471 private char[] scratch; | |
| 1472 | |
| 1473 public ReaderSource(Reader r) | |
| 1474 { | |
| 1475 _reader = r; | |
| 1476 } | |
| 1477 | |
| 1478 public void setReader(Reader reader) | |
| 1479 { | |
| 1480 _reader = reader; | |
| 1481 _next = -1; | |
| 1482 } | |
| 1483 | |
| 1484 public boolean hasNext() | |
| 1485 { | |
| 1486 getNext(); | |
| 1487 if (_next < 0) | |
| 1488 { | |
| 1489 scratch = null; | |
| 1490 return false; | |
| 1491 } | |
| 1492 return true; | |
| 1493 } | |
| 1494 | |
| 1495 public char next() | |
| 1496 { | |
| 1497 getNext(); | |
| 1498 char c = (char)_next; | |
| 1499 _next = -1; | |
| 1500 return c; | |
| 1501 } | |
| 1502 | |
| 1503 public char peek() | |
| 1504 { | |
| 1505 getNext(); | |
| 1506 return (char)_next; | |
| 1507 } | |
| 1508 | |
| 1509 private void getNext() | |
| 1510 { | |
| 1511 if (_next < 0) | |
| 1512 { | |
| 1513 try | |
| 1514 { | |
| 1515 _next = _reader.read(); | |
| 1516 } | |
| 1517 catch (IOException e) | |
| 1518 { | |
| 1519 throw new RuntimeException(e); | |
| 1520 } | |
| 1521 } | |
| 1522 } | |
| 1523 | |
| 1524 public char[] scratchBuffer() | |
| 1525 { | |
| 1526 if (scratch == null) | |
| 1527 scratch = new char[1024]; | |
| 1528 return scratch; | |
| 1529 } | |
| 1530 | |
| 1531 } | |
| 1532 | |
| 1533 /** | |
| 1534 * JSON Output class for use by {@link Convertible}. | |
| 1535 */ | |
| 1536 public interface Output | |
| 1537 { | |
| 1538 public void addClass(Class c); | |
| 1539 | |
| 1540 public void add(Object obj); | |
| 1541 | |
| 1542 public void add(String name, Object value); | |
| 1543 | |
| 1544 public void add(String name, double value); | |
| 1545 | |
| 1546 public void add(String name, long value); | |
| 1547 | |
| 1548 public void add(String name, boolean value); | |
| 1549 } | |
| 1550 | |
| 1551 /* ------------------------------------------------------------ */ | |
| 1552 /** | |
| 1553 * JSON Convertible object. Object can implement this interface in a similar | |
| 1554 * way to the {@link Externalizable} interface is used to allow classes to | |
| 1555 * provide their own serialization mechanism. | |
| 1556 * <p> | |
| 1557 * A JSON.Convertible object may be written to a JSONObject or initialized | |
| 1558 * from a Map of field names to values. | |
| 1559 * <p> | |
| 1560 * If the JSON is to be convertible back to an Object, then the method | |
| 1561 * {@link Output#addClass(Class)} must be called from within toJSON() | |
| 1562 * | |
| 1563 */ | |
| 1564 public interface Convertible | |
| 1565 { | |
| 1566 public void toJSON(Output out); | |
| 1567 | |
| 1568 public void fromJSON(Map object); | |
| 1569 } | |
| 1570 | |
| 1571 /** | |
| 1572 * Static JSON Convertor. | |
| 1573 * <p> | |
| 1574 * may be implemented to provide static convertors for objects that may be | |
| 1575 * registered with | |
| 1576 * {@link JSON#registerConvertor(Class, org.eclipse.jetty.util.ajax.JSON.Convertor)} | |
| 1577 * . These convertors are looked up by class, interface and super class by | |
| 1578 * {@link JSON#getConvertor(Class)}. Convertors should be used when the | |
| 1579 * classes to be converted cannot implement {@link Convertible} or | |
| 1580 * {@link Generator}. | |
| 1581 */ | |
| 1582 public interface Convertor | |
| 1583 { | |
| 1584 public void toJSON(Object obj, Output out); | |
| 1585 | |
| 1586 public Object fromJSON(Map object); | |
| 1587 } | |
| 1588 | |
| 1589 /** | |
| 1590 * JSON Generator. A class that can add it's JSON representation directly to | |
| 1591 * a StringBuffer. This is useful for object instances that are frequently | |
| 1592 * converted and wish to avoid multiple Conversions | |
| 1593 */ | |
| 1594 public interface Generator | |
| 1595 { | |
| 1596 public void addJSON(Appendable buffer); | |
| 1597 } | |
| 1598 | |
| 1599 /** | |
| 1600 * A Literal JSON generator A utility instance of {@link JSON.Generator} | |
| 1601 * that holds a pre-generated string on JSON text. | |
| 1602 */ | |
| 1603 public static class Literal implements Generator | |
| 1604 { | |
| 1605 private String _json; | |
| 1606 | |
| 1607 /** | |
| 1608 * Construct a literal JSON instance for use by | |
| 1609 * {@link JSON#toString(Object)}. If {@link Log#isDebugEnabled()} is | |
| 1610 * true, the JSON will be parsed to check validity | |
| 1611 * | |
| 1612 * @param json | |
| 1613 * A literal JSON string. | |
| 1614 */ | |
| 1615 public Literal(String json) | |
| 1616 { | |
| 1617 if (LOG.isDebugEnabled()) // TODO: Make this a configurable option on JSON instead! | |
| 1618 parse(json); | |
| 1619 _json = json; | |
| 1620 } | |
| 1621 | |
| 1622 @Override | |
| 1623 public String toString() | |
| 1624 { | |
| 1625 return _json; | |
| 1626 } | |
| 1627 | |
| 1628 public void addJSON(Appendable buffer) | |
| 1629 { | |
| 1630 try | |
| 1631 { | |
| 1632 buffer.append(_json); | |
| 1633 } | |
| 1634 catch(IOException e) | |
| 1635 { | |
| 1636 throw new RuntimeException(e); | |
| 1637 } | |
| 1638 } | |
| 1639 } | |
| 1640 } |
