001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.parser; 014 015import java.io.*; 016import java.lang.reflect.*; 017import java.util.*; 018 019import org.apache.juneau.*; 020import org.apache.juneau.annotation.*; 021import org.apache.juneau.html.*; 022import org.apache.juneau.http.*; 023import org.apache.juneau.json.*; 024import org.apache.juneau.msgpack.*; 025import org.apache.juneau.transform.*; 026import org.apache.juneau.transforms.*; 027import org.apache.juneau.uon.*; 028import org.apache.juneau.utils.*; 029import org.apache.juneau.xml.*; 030 031/** 032 * Parent class for all Juneau parsers. 033 * 034 * <h5 class='topic'>Valid data conversions</h5> 035 * 036 * Parsers can parse any parsable POJO types, as specified in the {@doc PojoCategories}. 037 * 038 * <p> 039 * Some examples of conversions are shown below... 040 * </p> 041 * <table class='styled'> 042 * <tr> 043 * <th>Data type</th> 044 * <th>Class type</th> 045 * <th>JSON example</th> 046 * <th>XML example</th> 047 * <th>Class examples</th> 048 * </tr> 049 * <tr> 050 * <td>object</td> 051 * <td>Maps, Java beans</td> 052 * <td class='code'>{name:<js>'John Smith'</js>,age:21}</td> 053 * <td class='code'><xt><object> 054 * <name</xt> <xa>type</xa>=<xs>'string'</xs><xt>></xt>John Smith<xt></name> 055 * <age</xt> <xa>type</xa>=<xs>'number'</xs><xt>></xt>21<xt></age> 056 * </object></xt></td> 057 * <td class='code'>HashMap, TreeMap<String,Integer></td> 058 * </tr> 059 * <tr> 060 * <td>array</td> 061 * <td>Collections, Java arrays</td> 062 * <td class='code'>[1,2,3]</td> 063 * <td class='code'><xt><array> 064 * <number></xt>1<xt></number> 065 * <number></xt>2<xt></number> 066 * <number></xt>3<xt></number> 067 * </array></xt></td> 068 * <td class='code'>List<Integer>, <jk>int</jk>[], Float[], Set<Person></td> 069 * </tr> 070 * <tr> 071 * <td>number</td> 072 * <td>Numbers</td> 073 * <td class='code'>123</td> 074 * <td class='code'><xt><number></xt>123<xt></number></xt></td> 075 * <td class='code'>Integer, Long, Float, <jk>int</jk></td> 076 * </tr> 077 * <tr> 078 * <td>boolean</td> 079 * <td>Booleans</td> 080 * <td class='code'><jk>true</jk></td> 081 * <td class='code'><xt><boolean></xt>true<xt></boolean></xt></td> 082 * <td class='code'>Boolean</td> 083 * </tr> 084 * <tr> 085 * <td>string</td> 086 * <td>CharSequences</td> 087 * <td class='code'><js>'foobar'</js></td> 088 * <td class='code'><xt><string></xt>foobar<xt></string></xt></td> 089 * <td class='code'>String, StringBuilder</td> 090 * </tr> 091 * </table> 092 * 093 * <p> 094 * In addition, any class types with {@link PojoSwap PojoSwaps} associated with them on the registered 095 * bean context can also be passed in. 096 * 097 * <p> 098 * For example, if the {@link TemporalCalendarSwap} transform is used to generalize {@code Calendar} objects to {@code String} 099 * objects. 100 * When registered with this parser, you can construct {@code Calendar} objects from {@code Strings} using the 101 * following syntax... 102 * <p class='bcode w800'> 103 * Calendar c = parser.parse(<js>"'Sun Mar 03 04:05:06 EST 2001'"</js>, GregorianCalendar.<jk>class</jk>); 104 * </p> 105 * 106 * <p> 107 * If <code>Object.<jk>class</jk></code> is specified as the target type, then the parser automatically determines the 108 * data types and generates the following object types... 109 * <table class='styled'> 110 * <tr><th>JSON type</th><th>Class type</th></tr> 111 * <tr><td>object</td><td>{@link ObjectMap}</td></tr> 112 * <tr><td>array</td><td>{@link ObjectList}</td></tr> 113 * <tr><td>number</td><td>{@link Number}<br>(depending on length and format, could be {@link Integer}, 114 * {@link Double}, {@link Float}, etc...)</td></tr> 115 * <tr><td>boolean</td><td>{@link Boolean}</td></tr> 116 * <tr><td>string</td><td>{@link String}</td></tr> 117 * </table> 118 */ 119@ConfigurableContext 120public abstract class Parser extends BeanContext { 121 122 //------------------------------------------------------------------------------------------------------------------- 123 // Configurable properties 124 //------------------------------------------------------------------------------------------------------------------- 125 126 static final String PREFIX = "Parser"; 127 128 /** 129 * Configuration property: Auto-close streams. 130 * 131 * <h5 class='section'>Property:</h5> 132 * <ul> 133 * <li><b>Name:</b> <js>"Parser.autoCloseStreams.b"</js> 134 * <li><b>Data type:</b> <c>Boolean</c> 135 * <li><b>Default:</b> <jk>false</jk> 136 * <li><b>Session property:</b> <jk>false</jk> 137 * <li><b>Methods:</b> 138 * <ul> 139 * <li class='jm'>{@link ParserBuilder#autoCloseStreams(boolean)} 140 * <li class='jm'>{@link ParserBuilder#autoCloseStreams()} 141 * </ul> 142 * </ul> 143 * 144 * <h5 class='section'>Description:</h5> 145 * <p> 146 * If <jk>true</jk>, <l>InputStreams</l> and <l>Readers</l> passed into parsers will be closed 147 * after parsing is complete. 148 * 149 * <h5 class='section'>Example:</h5> 150 * <p class='bcode w800'> 151 * <jc>// Create a parser using strict mode.</jc> 152 * ReaderParser p = JsonParser. 153 * .<jsm>create</jsm>() 154 * .autoCloseStreams() 155 * .build(); 156 * 157 * <jc>// Same, but use property.</jc> 158 * ReaderParser p = JsonParser. 159 * .<jsm>create</jsm>() 160 * .set(<jsf>PARSER_autoCloseStreams</jsf>, <jk>true</jk>) 161 * .build(); 162 * 163 * Reader r = <jk>new</jk> FileReader(<js>"/tmp/myfile.json"</js>); 164 * MyBean myBean = p.parse(r, MyBean.<jk>class</jk>); 165 * 166 * <jsm>assertTrue</jsm>(r.isClosed()); 167 * </p> 168 */ 169 public static final String PARSER_autoCloseStreams = PREFIX + ".autoCloseStreams.b"; 170 171 /** 172 * Configuration property: Debug output lines. 173 * 174 * <h5 class='section'>Property:</h5> 175 * <ul> 176 * <li><b>Name:</b> <js>"Parser.debugOutputLines.i"</js> 177 * <li><b>Data type:</b> <c>Integer</c> 178 * <li><b>Default:</b> <c>5</c> 179 * <li><b>Session property:</b> <jk>false</jk> 180 * <li><b>Methods:</b> 181 * <ul> 182 * <li class='jm'>{@link ParserBuilder#debugOutputLines(int)} 183 * </ul> 184 * </ul> 185 * 186 * <h5 class='section'>Description:</h5> 187 * <p> 188 * When parse errors occur, this specifies the number of lines of input before and after the 189 * error location to be printed as part of the exception message. 190 * 191 * <h5 class='section'>Example:</h5> 192 * <p class='bcode w800'> 193 * <jc>// Create a parser whose exceptions print out 100 lines before and after the parse error location.</jc> 194 * ReaderParser p = JsonParser. 195 * .<jsm>create</jsm>() 196 * .debug() <jc>// Enable debug mode to capture Reader contents as strings.</jc> 197 * .debugOuputLines(100) 198 * .build(); 199 * 200 * <jc>// Same, but use property.</jc> 201 * ReaderParser p = JsonParser. 202 * .<jsm>create</jsm>() 203 * .set(<jsf>BEAN_debug</jsf>, <jk>true</jk>) 204 * .set(<jsf>PARSER_debugOutputLines</jsf>, 100) 205 * .build(); 206 * 207 * Reader r = <jk>new</jk> FileReader(<js>"/tmp/mybadfile.json"</js>); 208 * <jk>try</jk> { 209 * p.parse(r, Object.<jk>class</jk>); 210 * } <jk>catch</jk> (ParseException e) { 211 * System.<jsf>err</jsf>.println(e.getMessage()); <jc>// Will display 200 lines of the output.</jc> 212 * } 213 * </p> 214 */ 215 public static final String PARSER_debugOutputLines = PREFIX + ".debugOutputLines.i"; 216 217 /** 218 * Configuration property: Parser listener. 219 * 220 * <h5 class='section'>Property:</h5> 221 * <ul> 222 * <li><b>Name:</b> <js>"Parser.listener.c"</js> 223 * <li><b>Data type:</b> <c>Class<? extends ParserListener></c> 224 * <li><b>Default:</b> <jk>null</jk> 225 * <li><b>Session property:</b> <jk>false</jk> 226 * <li><b>Methods:</b> 227 * <ul> 228 * <li class='jm'>{@link ParserBuilder#listener(Class)} 229 * </ul> 230 * </ul> 231 * 232 * <h5 class='section'>Description:</h5> 233 * <p> 234 * Class used to listen for errors and warnings that occur during parsing. 235 * 236 * <h5 class='section'>Example:</h5> 237 * <p class='bcode w800'> 238 * <jc>// Define our parser listener.</jc> 239 * <jc>// Simply captures all unknown bean property events.</jc> 240 * <jk>public class</jk> MyParserListener <jk>extends</jk> ParserListener { 241 * 242 * <jc>// A simple property to store our events.</jc> 243 * <jk>public</jk> List<String> <jf>events</jf> = <jk>new</jk> LinkedList<>(); 244 * 245 * <ja>@Override</ja> 246 * <jk>public</jk> <T> <jk>void</jk> onUnknownBeanProperty(ParserSession session, ParserPipe pipe, String propertyName, Class<T> beanClass, T bean, <jk>int</jk> line, <jk>int</jk> col) { 247 * <jf>events</jf>.add(propertyName + <js>","</js> + line + <js>","</js> + col); 248 * } 249 * } 250 * 251 * <jc>// Create a parser using our listener.</jc> 252 * ReaderParser p = JsonParser. 253 * .<jsm>create</jsm>() 254 * .listener(MyParserListener.<jk>class</jk>) 255 * .build(); 256 * 257 * <jc>// Same, but use property.</jc> 258 * ReaderParser p = JsonParser. 259 * .<jsm>create</jsm>() 260 * .set(<jsf>PARSER_listener</jsf>, MyParserListener.<jk>class</jk>) 261 * .build(); 262 * 263 * <jc>// Create a session object.</jc> 264 * <jc>// Needed because listeners are created per-session.</jc> 265 * <jk>try</jk> (ReaderParserSession s = p.createSession()) { 266 * 267 * <jc>// Parse some JSON object.</jc> 268 * MyBean myBean = s.parse(<js>"{...}"</js>, MyBean.<jk>class</jk>); 269 * 270 * <jc>// Get the listener.</jc> 271 * MyParserListener l = s.getListener(MyParserListener.<jk>class</jk>); 272 * 273 * <jc>// Dump the results to the console.</jc> 274 * SimpleJsonSerializer.<jsf>DEFAULT</jsf>.println(l.<jf>events</jf>); 275 * } 276 * </p> 277 */ 278 public static final String PARSER_listener = PREFIX + ".listener.c"; 279 280 /** 281 * Configuration property: Strict mode. 282 * 283 * <h5 class='section'>Property:</h5> 284 * <ul> 285 * <li><b>Name:</b> <js>"Parser.strict.b"</js> 286 * <li><b>Data type:</b> <c>Boolean</c> 287 * <li><b>Default:</b> <jk>false</jk> 288 * <li><b>Session property:</b> <jk>false</jk> 289 * <li><b>Methods:</b> 290 * <ul> 291 * <li class='jm'>{@link ParserBuilder#strict(boolean)} 292 * <li class='jm'>{@link ParserBuilder#strict()} 293 * </ul> 294 * </ul> 295 * 296 * <h5 class='section'>Description:</h5> 297 * <p> 298 * If <jk>true</jk>, strict mode for the parser is enabled. 299 * 300 * <p> 301 * Strict mode can mean different things for different parsers. 302 * 303 * <table class='styled'> 304 * <tr><th>Parser class</th><th>Strict behavior</th></tr> 305 * <tr> 306 * <td>All reader-based parsers</td> 307 * <td> 308 * When enabled, throws {@link ParseException ParseExceptions} on malformed charset input. 309 * Otherwise, malformed input is ignored. 310 * </td> 311 * </tr> 312 * <tr> 313 * <td>{@link JsonParser}</td> 314 * <td> 315 * When enabled, throws exceptions on the following invalid JSON syntax: 316 * <ul> 317 * <li>Unquoted attributes. 318 * <li>Missing attribute values. 319 * <li>Concatenated strings. 320 * <li>Javascript comments. 321 * <li>Numbers and booleans when Strings are expected. 322 * <li>Numbers valid in Java but not JSON (e.g. octal notation, etc...) 323 * </ul> 324 * </td> 325 * </tr> 326 * </table> 327 * 328 * <h5 class='section'>Example:</h5> 329 * <p class='bcode w800'> 330 * <jc>// Create a parser using strict mode.</jc> 331 * ReaderParser p = JsonParser. 332 * .<jsm>create</jsm>() 333 * .strict() 334 * .build(); 335 * 336 * <jc>// Same, but use property.</jc> 337 * ReaderParser p = JsonParser. 338 * .<jsm>create</jsm>() 339 * .set(<jsf>PARSER_strict</jsf>, <jk>true</jk>) 340 * .build(); 341 * 342 * <jc>// Use it.</jc> 343 * <jk>try</jk> { 344 * String json = <js>"{unquotedAttr:'value'}"</js>; 345 * MyBean myBean = p.parse(json, MyBean.<jk>class</jk>); 346 * } <jk>catch</jk> (ParseException e) { 347 * <jsm>assertTrue</jsm>(e.getMessage().contains(<js>"Unquoted attribute detected."</js>); 348 * } 349 * </p> 350 */ 351 public static final String PARSER_strict = PREFIX + ".strict.b"; 352 353 /** 354 * Configuration property: Trim parsed strings. 355 * 356 * <h5 class='section'>Property:</h5> 357 * <ul> 358 * <li><b>Name:</b> <js>"Parser.trimStrings.b"</js> 359 * <li><b>Data type:</b> <c>Boolean</c> 360 * <li><b>Default:</b> <jk>false</jk> 361 * <li><b>Session property:</b> <jk>false</jk> 362 * <li><b>Methods:</b> 363 * <ul> 364 * <li class='jm'>{@link ParserBuilder#trimStrings(boolean)} 365 * <li class='jm'>{@link ParserBuilder#trimStrings()} 366 * </ul> 367 * </ul> 368 * 369 * <h5 class='section'>Description:</h5> 370 * <p> 371 * If <jk>true</jk>, string values will be trimmed of whitespace using {@link String#trim()} before being added to 372 * the POJO. 373 * 374 * <h5 class='section'>Example:</h5> 375 * <p class='bcode w800'> 376 * <jc>// Create a parser with trim-strings enabled.</jc> 377 * ReaderParser p = JsonParser. 378 * .<jsm>create</jsm>() 379 * .trimStrings() 380 * .build(); 381 * 382 * <jc>// Same, but use property.</jc> 383 * ReaderParser p = JsonParser. 384 * .<jsm>create</jsm>() 385 * .set(<jsf>PARSER_trimStrings</jsf>, <jk>true</jk>) 386 * .build(); 387 * 388 * <jc>// Use it.</jc> 389 * String json = <js>"{foo:' bar '}"</js>; 390 * Map<String,String> m = p.parse(json, HashMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 391 * <jsm>assertEquals</jsm>(<js>"bar"</js>, m.get(<js>"foo"</js>)); 392 * </p> 393 */ 394 public static final String PARSER_trimStrings = PREFIX + ".trimStrings.b"; 395 396 /** 397 * Configuration property: Unbuffered. 398 * 399 * <h5 class='section'>Property:</h5> 400 * <ul> 401 * <li><b>Name:</b> <js>"Parser.unbuffered.b"</js> 402 * <li><b>Data type:</b> <c>Boolean</c> 403 * <li><b>Default:</b> <jk>false</jk> 404 * <li><b>Session property:</b> <jk>false</jk> 405 * <li><b>Methods:</b> 406 * <ul> 407 * <li class='jm'>{@link ParserBuilder#unbuffered(boolean)} 408 * <li class='jm'>{@link ParserBuilder#unbuffered()} 409 * </ul> 410 * </ul> 411 * 412 * <h5 class='section'>Description:</h5> 413 * <p> 414 * If <jk>true</jk>, don't use internal buffering during parsing. 415 * 416 * <p> 417 * This is useful in cases when you want to parse the same input stream or reader multiple times 418 * because it may contain multiple independent POJOs to parse. 419 * <br>Buffering would cause the parser to read past the current POJO in the stream. 420 * 421 * <h5 class='section'>Example:</h5> 422 * <p class='bcode w800'> 423 * <jc>// Create a parser using strict mode.</jc> 424 * ReaderParser p = JsonParser. 425 * .<jsm>create</jsm>() 426 * .unbuffered() 427 * .build(); 428 * 429 * <jc>// Same, but use property.</jc> 430 * ReaderParser p = JsonParser. 431 * .<jsm>create</jsm>() 432 * .set(<jsf>PARSER_unbuffered</jsf>, <jk>true</jk>) 433 * .build(); 434 * 435 * <jc>// If you're calling parse on the same input multiple times, use a session instead of the parser directly.</jc> 436 * <jc>// It's more efficient because we don't need to recalc the session settings again. </jc> 437 * ReaderParserSession s = p.createSession(); 438 * 439 * <jc>// Read input with multiple POJOs</jc> 440 * Reader json = <jk>new</jk> StringReader(<js>"{foo:'bar'}{foo:'baz'}"</js>); 441 * MyBean myBean1 = s.parse(json, MyBean.<jk>class</jk>); 442 * MyBean myBean2 = s.parse(json, MyBean.<jk>class</jk>); 443 * </p> 444 * 445 * <ul class='notes'> 446 * <li> 447 * This only allows for multi-input streams for the following parsers: 448 * <ul> 449 * <li class='jc'>{@link JsonParser} 450 * <li class='jc'>{@link UonParser} 451 * </ul> 452 * It has no effect on the following parsers: 453 * <ul> 454 * <li class='jc'>{@link MsgPackParser} - It already doesn't use buffering. 455 * <li class='jc'>{@link XmlParser}, {@link HtmlParser} - These use StAX which doesn't allow for more than one root element anyway. 456 * <li>RDF parsers - These read everything into an internal model before any parsing begins. 457 * </ul> 458 * </ul> 459 * 460 * If <jk>true</jk>, don't use internal buffering during parsing. 461 */ 462 public static final String PARSER_unbuffered = PREFIX + ".unbuffered.b"; 463 464 static Parser DEFAULT = new Parser(PropertyStore.create().build()) { 465 @Override 466 public ParserSession createSession(ParserSessionArgs args) { 467 throw new NoSuchMethodError(); 468 } 469 }; 470 471 //------------------------------------------------------------------------------------------------------------------- 472 // Instance 473 //------------------------------------------------------------------------------------------------------------------- 474 475 private final boolean trimStrings, strict, autoCloseStreams, unbuffered; 476 private final int debugOutputLines; 477 private final Class<? extends ParserListener> listener; 478 479 /** General parser properties currently set on this parser. */ 480 private final MediaType[] consumes; 481 482 /** 483 * Constructor. 484 * 485 * @param ps The property store containing all the settings for this object. 486 * @param consumes The list of media types that this parser consumes (e.g. <js>"application/json"</js>). 487 */ 488 protected Parser(PropertyStore ps, String...consumes) { 489 super(ps); 490 491 trimStrings = getBooleanProperty(PARSER_trimStrings, false); 492 strict = getBooleanProperty(PARSER_strict, false); 493 autoCloseStreams = getBooleanProperty(PARSER_autoCloseStreams, false); 494 debugOutputLines = getIntegerProperty(PARSER_debugOutputLines, 5); 495 unbuffered = getBooleanProperty(PARSER_unbuffered, false); 496 listener = getClassProperty(PARSER_listener, ParserListener.class, null); 497 this.consumes = new MediaType[consumes.length]; 498 for (int i = 0; i < consumes.length; i++) { 499 this.consumes[i] = MediaType.forString(consumes[i]); 500 } 501 } 502 503 @Override /* Context */ 504 public ParserBuilder builder() { 505 return new ParserBuilder(getPropertyStore()); 506 } 507 508 /** 509 * Instantiates a new clean-slate {@link ParserBuilder} object. 510 * 511 * <p> 512 * This is equivalent to simply calling <code><jk>new</jk> ParserBuilder()</code>. 513 * 514 * <p> 515 * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 516 * the settings of the object called on. 517 * 518 * @return A new {@link ParserBuilder} object. 519 */ 520 public static ParserBuilder create() { 521 return new ParserBuilder(PropertyStore.DEFAULT); 522 } 523 524 525 //----------------------------------------------------------------------------------------------------------------- 526 // Abstract methods 527 //----------------------------------------------------------------------------------------------------------------- 528 529 /** 530 * Returns <jk>true</jk> if this parser subclasses from {@link ReaderParser}. 531 * 532 * @return <jk>true</jk> if this parser subclasses from {@link ReaderParser}. 533 */ 534 public boolean isReaderParser() { 535 return true; 536 } 537 538 /** 539 * Create the session object that will be passed in to the parse method. 540 * 541 * <p> 542 * It's up to implementers to decide what the session object looks like, although typically it's going to be a 543 * subclass of {@link ParserSession}. 544 * 545 * @param args 546 * Runtime arguments. 547 * @return The new session. 548 */ 549 public abstract ParserSession createSession(ParserSessionArgs args); 550 551 552 //----------------------------------------------------------------------------------------------------------------- 553 // Other methods 554 //----------------------------------------------------------------------------------------------------------------- 555 556 /** 557 * Parses input into the specified object type. 558 * 559 * <p> 560 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 561 * 562 * <h5 class='section'>Examples:</h5> 563 * <p class='bcode w800'> 564 * ReaderParser p = JsonParser.<jsf>DEFAULT</jsf>; 565 * 566 * <jc>// Parse into a linked-list of strings.</jc> 567 * List l = p.parse(json, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 568 * 569 * <jc>// Parse into a linked-list of beans.</jc> 570 * List l = p.parse(json, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 571 * 572 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 573 * List l = p.parse(json, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 574 * 575 * <jc>// Parse into a map of string keys/values.</jc> 576 * Map m = p.parse(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 577 * 578 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 579 * Map m = p.parse(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 580 * </p> 581 * 582 * <p> 583 * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type. 584 * 585 * <p> 586 * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 587 * 588 * <p> 589 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 590 * 591 * <ul class='notes'> 592 * <li> 593 * Use the {@link #parse(Object, Class)} method instead if you don't need a parameterized map/collection. 594 * </ul> 595 * 596 * @param <T> The class type of the object to create. 597 * @param input 598 * The input. 599 * <br>Character-based parsers can handle the following input class types: 600 * <ul> 601 * <li><jk>null</jk> 602 * <li>{@link Reader} 603 * <li>{@link CharSequence} 604 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 605 * {@link ReaderParser#RPARSER_streamCharset} property value). 606 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 607 * {@link ReaderParser#RPARSER_streamCharset} property value). 608 * <li>{@link File} containing system encoded text (or charset defined by 609 * {@link ReaderParser#RPARSER_fileCharset} property value). 610 * </ul> 611 * <br>Stream-based parsers can handle the following input class types: 612 * <ul> 613 * <li><jk>null</jk> 614 * <li>{@link InputStream} 615 * <li><code><jk>byte</jk>[]</code> 616 * <li>{@link File} 617 * <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser#ISPARSER_binaryFormat} setting. 618 * </ul> 619 * @param type 620 * The object type to create. 621 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 622 * @param args 623 * The type arguments of the class if it's a collection or map. 624 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 625 * <br>Ignored if the main type is not a map or collection. 626 * @return The parsed object. 627 * @throws ParseException Malformed input encountered. 628 * @throws IOException Thrown by underlying stream. 629 * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. 630 */ 631 public final <T> T parse(Object input, Type type, Type...args) throws ParseException, IOException { 632 return createSession().parse(input, type, args); 633 } 634 635 /** 636 * Same as {@link #parse(Object, Type, Type...)} but since it's a {@link String} input doesn't throw an {@link IOException}. 637 * 638 * @param <T> The class type of the object being created. 639 * @param input 640 * The input. 641 * See {@link #parse(Object, Type, Type...)} for details. 642 * @param type 643 * The object type to create. 644 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 645 * @param args 646 * The type arguments of the class if it's a collection or map. 647 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 648 * <br>Ignored if the main type is not a map or collection. 649 * @return The parsed object. 650 * @throws ParseException Malformed input encountered. 651 */ 652 public final <T> T parse(String input, Type type, Type...args) throws ParseException { 653 return createSession().parse(input, type, args); 654 } 655 656 /** 657 * Same as {@link #parse(Object, Type, Type...)} except optimized for a non-parameterized class. 658 * 659 * <p> 660 * This is the preferred parse method for simple types since you don't need to cast the results. 661 * 662 * <h5 class='section'>Examples:</h5> 663 * <p class='bcode w800'> 664 * ReaderParser p = JsonParser.<jsf>DEFAULT</jsf>; 665 * 666 * <jc>// Parse into a string.</jc> 667 * String s = p.parse(json, String.<jk>class</jk>); 668 * 669 * <jc>// Parse into a bean.</jc> 670 * MyBean b = p.parse(json, MyBean.<jk>class</jk>); 671 * 672 * <jc>// Parse into a bean array.</jc> 673 * MyBean[] ba = p.parse(json, MyBean[].<jk>class</jk>); 674 * 675 * <jc>// Parse into a linked-list of objects.</jc> 676 * List l = p.parse(json, LinkedList.<jk>class</jk>); 677 * 678 * <jc>// Parse into a map of object keys/values.</jc> 679 * Map m = p.parse(json, TreeMap.<jk>class</jk>); 680 * </p> 681 * 682 * @param <T> The class type of the object being created. 683 * @param input 684 * The input. 685 * See {@link #parse(Object, Type, Type...)} for details. 686 * @param type The object type to create. 687 * @return The parsed object. 688 * @throws ParseException Malformed input encountered. 689 * @throws IOException Thrown by the underlying stream. 690 */ 691 public final <T> T parse(Object input, Class<T> type) throws ParseException, IOException { 692 return createSession().parse(input, type); 693 } 694 695 /** 696 * Same as {@link #parse(Object, Class)} but since it's a {@link String} input doesn't throw an {@link IOException}. 697 * 698 * @param <T> The class type of the object being created. 699 * @param input 700 * The input. 701 * See {@link #parse(Object, Type, Type...)} for details. 702 * @param type The object type to create. 703 * @return The parsed object. 704 * @throws ParseException Malformed input encountered. 705 */ 706 public final <T> T parse(String input, Class<T> type) throws ParseException { 707 return createSession().parse(input, type); 708 } 709 710 /** 711 * Same as {@link #parse(Object, Type, Type...)} except the type has already been converted into a {@link ClassMeta} 712 * object. 713 * 714 * <p> 715 * This is mostly an internal method used by the framework. 716 * 717 * @param <T> The class type of the object being created. 718 * @param input 719 * The input. 720 * See {@link #parse(Object, Type, Type...)} for details. 721 * @param type The object type to create. 722 * @return The parsed object. 723 * @throws ParseException Malformed input encountered. 724 * @throws IOException Thrown by the underlying stream. 725 */ 726 public final <T> T parse(Object input, ClassMeta<T> type) throws ParseException, IOException { 727 return createSession().parse(input, type); 728 } 729 730 /** 731 * Same as {@link #parse(Object, ClassMeta)} but since it's a {@link String} input doesn't throw an {@link IOException}. 732 * 733 * @param <T> The class type of the object being created. 734 * @param input 735 * The input. 736 * See {@link #parse(Object, Type, Type...)} for details. 737 * @param type The object type to create. 738 * @return The parsed object. 739 * @throws ParseException Malformed input encountered. 740 */ 741 public final <T> T parse(String input, ClassMeta<T> type) throws ParseException { 742 return createSession().parse(input, type); 743 } 744 745 @Override /* Context */ 746 public ParserSession createSession() { 747 return createSession(createDefaultSessionArgs()); 748 } 749 750 @Override /* Context */ 751 public final ParserSessionArgs createDefaultSessionArgs() { 752 return new ParserSessionArgs().mediaType(getPrimaryMediaType()); 753 } 754 755 //----------------------------------------------------------------------------------------------------------------- 756 // Optional methods 757 //----------------------------------------------------------------------------------------------------------------- 758 759 /** 760 * Parses the contents of the specified reader and loads the results into the specified map. 761 * 762 * <p> 763 * Reader must contain something that serializes to a map (such as text containing a JSON object). 764 * 765 * <p> 766 * Used in the following locations: 767 * <ul class='spaced-list'> 768 * <li> 769 * The various character-based constructors in {@link ObjectMap} (e.g. 770 * {@link ObjectMap#ObjectMap(CharSequence,Parser)}). 771 * </ul> 772 * 773 * @param <K> The key class type. 774 * @param <V> The value class type. 775 * @param input The input. See {@link #parse(Object, ClassMeta)} for supported input types. 776 * @param m The map being loaded. 777 * @param keyType The class type of the keys, or <jk>null</jk> to default to <code>String.<jk>class</jk></code>. 778 * @param valueType The class type of the values, or <jk>null</jk> to default to whatever is being parsed. 779 * @return The same map that was passed in to allow this method to be chained. 780 * @throws ParseException Malformed input encountered. 781 * @throws UnsupportedOperationException If not implemented. 782 */ 783 public final <K,V> Map<K,V> parseIntoMap(Object input, Map<K,V> m, Type keyType, Type valueType) throws ParseException { 784 return createSession().parseIntoMap(input, m, keyType, valueType); 785 } 786 787 /** 788 * Parses the contents of the specified reader and loads the results into the specified collection. 789 * 790 * <p> 791 * Used in the following locations: 792 * <ul class='spaced-list'> 793 * <li> 794 * The various character-based constructors in {@link ObjectList} (e.g. 795 * {@link ObjectList#ObjectList(CharSequence,Parser)}. 796 * </ul> 797 * 798 * @param <E> The element class type. 799 * @param input The input. See {@link #parse(Object, ClassMeta)} for supported input types. 800 * @param c The collection being loaded. 801 * @param elementType The class type of the elements, or <jk>null</jk> to default to whatever is being parsed. 802 * @return The same collection that was passed in to allow this method to be chained. 803 * @throws ParseException Malformed input encountered. 804 * @throws UnsupportedOperationException If not implemented. 805 */ 806 public final <E> Collection<E> parseIntoCollection(Object input, Collection<E> c, Type elementType) throws ParseException { 807 return createSession().parseIntoCollection(input, c, elementType); 808 } 809 810 /** 811 * Parses the specified array input with each entry in the object defined by the {@code argTypes} 812 * argument. 813 * 814 * <p> 815 * Used for converting arrays (e.g. <js>"[arg1,arg2,...]"</js>) into an {@code Object[]} that can be passed 816 * to the {@code Method.invoke(target, args)} method. 817 * 818 * <p> 819 * Used in the following locations: 820 * <ul class='spaced-list'> 821 * <li> 822 * Used to parse argument strings in the {@link PojoIntrospector#invokeMethod(Method, Reader)} method. 823 * </ul> 824 * 825 * @param input The input. Subclasses can support different input types. 826 * @param argTypes Specifies the type of objects to create for each entry in the array. 827 * @return An array of parsed objects. 828 * @throws ParseException Malformed input encountered. 829 */ 830 public final Object[] parseArgs(Object input, Type[] argTypes) throws ParseException { 831 if (argTypes == null || argTypes.length == 0) 832 return new Object[0]; 833 return createSession().parseArgs(input, argTypes); 834 } 835 836 837 //----------------------------------------------------------------------------------------------------------------- 838 // Other methods 839 //----------------------------------------------------------------------------------------------------------------- 840 841 /** 842 * Returns the media types handled based on the values passed to the <c>consumes</c> constructor parameter. 843 * 844 * @return The list of media types. Never <jk>null</jk>. 845 */ 846 public final MediaType[] getMediaTypes() { 847 return consumes; 848 } 849 850 /** 851 * Returns the first media type handled based on the values passed to the <c>consumes</c> constructor parameter. 852 * 853 * @return The media type. 854 */ 855 public final MediaType getPrimaryMediaType() { 856 return consumes == null || consumes.length == 0 ? null : consumes[0]; 857 } 858 859 /** 860 * Returns <jk>true</jk> if this parser can handle the specified content type. 861 * 862 * @param contentType The content type to test. 863 * @return <jk>true</jk> if this parser can handle the specified content type. 864 */ 865 public boolean canHandle(String contentType) { 866 if (contentType != null) 867 for (MediaType mt : getMediaTypes()) 868 if (contentType.equals(mt.toString())) 869 return true; 870 return false; 871 } 872 873 //----------------------------------------------------------------------------------------------------------------- 874 // Properties 875 //----------------------------------------------------------------------------------------------------------------- 876 877 /** 878 * Configuration property: Auto-close streams. 879 * 880 * @see #PARSER_autoCloseStreams 881 * @return 882 * <jk>true</jk> if <l>InputStreams</l> and <l>Readers</l> passed into parsers will be closed 883 * after parsing is complete. 884 */ 885 protected final boolean isAutoCloseStreams() { 886 return autoCloseStreams; 887 } 888 889 /** 890 * Configuration property: Debug output lines. 891 * 892 * @see #PARSER_debugOutputLines 893 * @return 894 * The number of lines of input before and after the error location to be printed as part of the exception message. 895 */ 896 protected final int getDebugOutputLines() { 897 return debugOutputLines; 898 } 899 900 /** 901 * Configuration property: Parser listener. 902 * 903 * @see #PARSER_listener 904 * @return 905 * Class used to listen for errors and warnings that occur during parsing. 906 */ 907 protected final Class<? extends ParserListener> getListener() { 908 return listener; 909 } 910 911 /** 912 * Configuration property: Strict mode. 913 * 914 * @see #PARSER_strict 915 * @return 916 * <jk>true</jk> if strict mode for the parser is enabled. 917 */ 918 protected final boolean isStrict() { 919 return strict; 920 } 921 922 /** 923 * Configuration property: Trim parsed strings. 924 * 925 * @see #PARSER_trimStrings 926 * @return 927 * <jk>true</jk> if string values will be trimmed of whitespace using {@link String#trim()} before being added to 928 * the POJO. 929 */ 930 protected final boolean isTrimStrings() { 931 return trimStrings; 932 } 933 934 /** 935 * Configuration property: Unbuffered. 936 * 937 * @see #PARSER_unbuffered 938 * @return 939 * <jk>true</jk> if parsers don't use internal buffering during parsing. 940 */ 941 protected final boolean isUnbuffered() { 942 return unbuffered; 943 } 944 945 //----------------------------------------------------------------------------------------------------------------- 946 // Other methods 947 //----------------------------------------------------------------------------------------------------------------- 948 949 @Override /* Context */ 950 public ObjectMap toMap() { 951 return super.toMap() 952 .append("Parser", new DefaultFilteringObjectMap() 953 .append("autoCloseStreams", autoCloseStreams) 954 .append("debugOutputLines", debugOutputLines) 955 .append("listener", listener) 956 .append("strict", strict) 957 .append("trimStrings", trimStrings) 958 .append("unbuffered", unbuffered) 959 ); 960 } 961}