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