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