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.nio.charset.*; 018import java.util.*; 019 020import org.apache.juneau.*; 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 <a class="doclink" 037 * href="../../../../overview-summary.html#juneau-marshall.PojoCategories">POJO Categories</a>. 038 * 039 * <p> 040 * Some examples of conversions are shown below... 041 * </p> 042 * <table class='styled'> 043 * <tr> 044 * <th>Data type</th> 045 * <th>Class type</th> 046 * <th>JSON example</th> 047 * <th>XML example</th> 048 * <th>Class examples</th> 049 * </tr> 050 * <tr> 051 * <td>object</td> 052 * <td>Maps, Java beans</td> 053 * <td class='code'>{name:<js>'John Smith'</js>,age:21}</td> 054 * <td class='code'><xt><object> 055 * <name</xt> <xa>type</xa>=<xs>'string'</xs><xt>></xt>John Smith<xt></name> 056 * <age</xt> <xa>type</xa>=<xs>'number'</xs><xt>></xt>21<xt></age> 057 * </object></xt></td> 058 * <td class='code'>HashMap, TreeMap<String,Integer></td> 059 * </tr> 060 * <tr> 061 * <td>array</td> 062 * <td>Collections, Java arrays</td> 063 * <td class='code'>[1,2,3]</td> 064 * <td class='code'><xt><array> 065 * <number></xt>1<xt></number> 066 * <number></xt>2<xt></number> 067 * <number></xt>3<xt></number> 068 * </array></xt></td> 069 * <td class='code'>List<Integer>, <jk>int</jk>[], Float[], Set<Person></td> 070 * </tr> 071 * <tr> 072 * <td>number</td> 073 * <td>Numbers</td> 074 * <td class='code'>123</td> 075 * <td class='code'><xt><number></xt>123<xt></number></xt></td> 076 * <td class='code'>Integer, Long, Float, <jk>int</jk></td> 077 * </tr> 078 * <tr> 079 * <td>boolean</td> 080 * <td>Booleans</td> 081 * <td class='code'><jk>true</jk></td> 082 * <td class='code'><xt><boolean></xt>true<xt></boolean></xt></td> 083 * <td class='code'>Boolean</td> 084 * </tr> 085 * <tr> 086 * <td>string</td> 087 * <td>CharSequences</td> 088 * <td class='code'><js>'foobar'</js></td> 089 * <td class='code'><xt><string></xt>foobar<xt></string></xt></td> 090 * <td class='code'>String, StringBuilder</td> 091 * </tr> 092 * </table> 093 * 094 * <p> 095 * In addition, any class types with {@link PojoSwap PojoSwaps} associated with them on the registered 096 * bean context can also be passed in. 097 * 098 * <p> 099 * For example, if the {@link CalendarSwap} transform is used to generalize {@code Calendar} objects to {@code String} 100 * objects. 101 * When registered with this parser, you can construct {@code Calendar} objects from {@code Strings} using the 102 * following syntax... 103 * <p class='bcode'> 104 * Calendar c = parser.parse(<js>"'Sun Mar 03 04:05:06 EST 2001'"</js>, GregorianCalendar.<jk>class</jk>); 105 * </p> 106 * 107 * <p> 108 * If <code>Object.<jk>class</jk></code> is specified as the target type, then the parser automatically determines the 109 * data types and generates the following object types... 110 * <table class='styled'> 111 * <tr><th>JSON type</th><th>Class type</th></tr> 112 * <tr><td>object</td><td>{@link ObjectMap}</td></tr> 113 * <tr><td>array</td><td>{@link ObjectList}</td></tr> 114 * <tr><td>number</td><td>{@link Number}<br>(depending on length and format, could be {@link Integer}, 115 * {@link Double}, {@link Float}, etc...)</td></tr> 116 * <tr><td>boolean</td><td>{@link Boolean}</td></tr> 117 * <tr><td>string</td><td>{@link String}</td></tr> 118 * </table> 119 */ 120public abstract class Parser extends BeanContext { 121 122 //------------------------------------------------------------------------------------------------------------------- 123 // Configurable properties 124 //------------------------------------------------------------------------------------------------------------------- 125 126 private 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> <code>Boolean</code> 135 * <li><b>Default:</b> <jk>false</jk> 136 * <li><b>Session-overridable:</b> <jk>true</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'> 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: File charset. 173 * 174 * <h5 class='section'>Property:</h5> 175 * <ul> 176 * <li><b>Name:</b> <js>"Parser.fileCharset.s"</js> 177 * <li><b>Data type:</b> <code>String</code> 178 * <li><b>Default:</b> <js>"DEFAULT"</js> 179 * <li><b>Session-overridable:</b> <jk>true</jk> 180 * <li><b>Methods:</b> 181 * <ul> 182 * <li class='jm'>{@link ParserBuilder#fileCharset(String)} 183 * <li class='jm'>{@link ParserBuilder#fileCharset(Charset)} 184 * </ul> 185 * </ul> 186 * 187 * <h5 class='section'>Description:</h5> 188 * <p> 189 * The character set to use for reading <code>Files</code> from the file system. 190 * 191 * <p> 192 * Used when passing in files to {@link Parser#parse(Object, Class)}. 193 * 194 * <p> 195 * <js>"DEFAULT"</js> can be used to indicate the JVM default file system charset. 196 * 197 * <h5 class='section'>Example:</h5> 198 * <p class='bcode'> 199 * <jc>// Create a parser that reads UTF-8 files.</jc> 200 * ReaderParser p = JsonParser. 201 * .<jsm>create</jsm>() 202 * .fileCharset(<js>"UTF-8"</js>) 203 * .build(); 204 * 205 * <jc>// Same, but use property.</jc> 206 * ReaderParser p = JsonParser. 207 * .<jsm>create</jsm>() 208 * .set(<jsf>PARSER_fileCharset</jsf>, <js>"UTF-8"</js>) 209 * .build(); 210 * 211 * <jc>// Use it to read a UTF-8 encoded file.</jc> 212 * MyBean myBean = p.parse(<jk>new</jk> File(<js>"MyBean.txt"</js>), MyBean.<jk>class</jk>); 213 * </p> 214 */ 215 public static final String PARSER_fileCharset = PREFIX + "fileCharset.s"; 216 217 /** 218 * Configuration property: Input stream charset. 219 * 220 * <h5 class='section'>Property:</h5> 221 * <ul> 222 * <li><b>Name:</b> <js>"Parser.inputStreamCharset.s"</js> 223 * <li><b>Data type:</b> <code>String</code> 224 * <li><b>Default:</b> <js>"UTF-8"</js> 225 * <li><b>Session-overridable:</b> <jk>true</jk> 226 * <li><b>Methods:</b> 227 * <ul> 228 * <li class='jm'>{@link ParserBuilder#inputStreamCharset(String)} 229 * <li class='jm'>{@link ParserBuilder#inputStreamCharset(Charset)} 230 * </ul> 231 * </ul> 232 * 233 * <h5 class='section'>Description:</h5> 234 * <p> 235 * The character set to use for converting <code>InputStreams</code> and byte arrays to readers. 236 * 237 * <p> 238 * Used when passing in input streams and byte arrays to {@link Parser#parse(Object, Class)}. 239 * 240 * <h5 class='section'>Example:</h5> 241 * <p class='bcode'> 242 * <jc>// Create a parser that reads UTF-8 files.</jc> 243 * ReaderParser p = JsonParser. 244 * .<jsm>create</jsm>() 245 * .inputStreamCharset(<js>"UTF-8"</js>) 246 * .build(); 247 * 248 * <jc>// Same, but use property.</jc> 249 * ReaderParser p = JsonParser. 250 * .<jsm>create</jsm>() 251 * .set(<jsf>PARSER_inputStreamCharset</jsf>, <js>"UTF-8"</js>) 252 * .build(); 253 * 254 * <jc>// Use it to read a UTF-8 encoded input stream.</jc> 255 * MyBean myBean = p.parse(<jk>new</jk> FileInputStream(<js>"MyBean.txt"</js>), MyBean.<jk>class</jk>); 256 * </p> 257 */ 258 public static final String PARSER_inputStreamCharset = PREFIX + "inputStreamCharset.s"; 259 260 /** 261 * Configuration property: Parser listener. 262 * 263 * <h5 class='section'>Property:</h5> 264 * <ul> 265 * <li><b>Name:</b> <js>"Parser.listener.c"</js> 266 * <li><b>Data type:</b> <code>Class<? extends ParserListener></code> 267 * <li><b>Default:</b> <jk>null</jk> 268 * <li><b>Session-overridable:</b> <jk>true</jk> 269 * <li><b>Methods:</b> 270 * <ul> 271 * <li class='jm'>{@link ParserBuilder#listener(Class)} 272 * </ul> 273 * </ul> 274 * 275 * <h5 class='section'>Description:</h5> 276 * <p> 277 * Class used to listen for errors and warnings that occur during parsing. 278 * 279 * <h5 class='section'>Example:</h5> 280 * <p class='bcode'> 281 * <jc>// Define our parser listener.</jc> 282 * <jc>// Simply captures all unknown bean property events.</jc> 283 * <jk>public class</jk> MyParserListener <jk>extends</jk> ParserListener { 284 * 285 * <jc>// A simple property to store our events.</jc> 286 * <jk>public</jk> List<String> <jf>events</jf> = <jk>new</jk> LinkedList<>(); 287 * 288 * <ja>@Override</ja> 289 * <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) { 290 * <jf>events</jf>.add(propertyName + <js>","</js> + line + <js>","</js> + col); 291 * } 292 * } 293 * 294 * <jc>// Create a parser using our listener.</jc> 295 * ReaderParser p = JsonParser. 296 * .<jsm>create</jsm>() 297 * .listener(MyParserListener.<jk>class</jk>) 298 * .build(); 299 * 300 * <jc>// Same, but use property.</jc> 301 * ReaderParser p = JsonParser. 302 * .<jsm>create</jsm>() 303 * .set(<jsf>PARSER_listener</jsf>, MyParserListener.<jk>class</jk>) 304 * .build(); 305 * 306 * <jc>// Create a session object.</jc> 307 * <jc>// Needed because listeners are created per-session.</jc> 308 * <jk>try</jk> (ReaderParserSession s = p.createSession()) { 309 * 310 * <jc>// Parse some JSON object.</jc> 311 * MyBean myBean = s.parse(<js>"{...}"</js>, MyBean.<jk>class</jk>); 312 * 313 * <jc>// Get the listener.</jc> 314 * MyParserListener l = s.getListener(MyParserListener.<jk>class</jk>); 315 * 316 * <jc>// Dump the results to the console.</jc> 317 * JsonSerializer.<jsf>DEFAULT_LAX</jsf>.println(l.<jf>events</jf>); 318 * } 319 * </p> 320 */ 321 public static final String PARSER_listener = PREFIX + "listener.c"; 322 323 /** 324 * Configuration property: Strict mode. 325 * 326 * <h5 class='section'>Property:</h5> 327 * <ul> 328 * <li><b>Name:</b> <js>"Parser.strict.b"</js> 329 * <li><b>Data type:</b> <code>Boolean</code> 330 * <li><b>Default:</b> <jk>false</jk> 331 * <li><b>Session-overridable:</b> <jk>true</jk> 332 * <li><b>Methods:</b> 333 * <ul> 334 * <li class='jm'>{@link ParserBuilder#strict(boolean)} 335 * <li class='jm'>{@link ParserBuilder#strict()} 336 * </ul> 337 * </ul> 338 * 339 * <h5 class='section'>Description:</h5> 340 * <p> 341 * If <jk>true</jk>, strict mode for the parser is enabled. 342 * 343 * <p> 344 * Strict mode can mean different things for different parsers. 345 * 346 * <table class='styled'> 347 * <tr><th>Parser class</th><th>Strict behavior</th></tr> 348 * <tr> 349 * <td>All reader-based parsers</td> 350 * <td> 351 * When enabled, throws {@link ParseException ParseExceptions} on malformed charset input. 352 * Otherwise, malformed input is ignored. 353 * </td> 354 * </tr> 355 * <tr> 356 * <td>{@link JsonParser}</td> 357 * <td> 358 * When enabled, throws exceptions on the following invalid JSON syntax: 359 * <ul> 360 * <li>Unquoted attributes. 361 * <li>Missing attribute values. 362 * <li>Concatenated strings. 363 * <li>Javascript comments. 364 * <li>Numbers and booleans when Strings are expected. 365 * <li>Numbers valid in Java but not JSON (e.g. octal notation, etc...) 366 * </ul> 367 * </td> 368 * </tr> 369 * </table> 370 * 371 * <h5 class='section'>Example:</h5> 372 * <p class='bcode'> 373 * <jc>// Create a parser using strict mode.</jc> 374 * ReaderParser p = JsonParser. 375 * .<jsm>create</jsm>() 376 * .strict() 377 * .build(); 378 * 379 * <jc>// Same, but use property.</jc> 380 * ReaderParser p = JsonParser. 381 * .<jsm>create</jsm>() 382 * .set(<jsf>PARSER_strict</jsf>, <jk>true</jk>) 383 * .build(); 384 * 385 * <jc>// Use it.</jc> 386 * <jk>try</jk> { 387 * String json = <js>"{unquotedAttr:'value'}"</js>; 388 * MyBean myBean = p.parse(json, MyBean.<jk>class</jk>); 389 * } <jk>catch</jk> (ParseException e) { 390 * <jsm>assertTrue</jsm>(e.getMessage().contains(<js>"Unquoted attribute detected."</js>); 391 * } 392 * </p> 393 */ 394 public static final String PARSER_strict = PREFIX + "strict.b"; 395 396 /** 397 * Configuration property: Trim parsed strings. 398 * 399 * <h5 class='section'>Property:</h5> 400 * <ul> 401 * <li><b>Name:</b> <js>"Parser.trimStrings.b"</js> 402 * <li><b>Data type:</b> <code>Boolean</code> 403 * <li><b>Default:</b> <jk>false</jk> 404 * <li><b>Session-overridable:</b> <jk>true</jk> 405 * <li><b>Methods:</b> 406 * <ul> 407 * <li class='jm'>{@link ParserBuilder#trimStrings(boolean)} 408 * <li class='jm'>{@link ParserBuilder#trimStrings()} 409 * </ul> 410 * </ul> 411 * 412 * <h5 class='section'>Description:</h5> 413 * <p> 414 * If <jk>true</jk>, string values will be trimmed of whitespace using {@link String#trim()} before being added to 415 * the POJO. 416 * 417 * <h5 class='section'>Example:</h5> 418 * <p class='bcode'> 419 * <jc>// Create a parser with trim-strings enabled.</jc> 420 * ReaderParser p = JsonParser. 421 * .<jsm>create</jsm>() 422 * .trimStrings() 423 * .build(); 424 * 425 * <jc>// Same, but use property.</jc> 426 * ReaderParser p = JsonParser. 427 * .<jsm>create</jsm>() 428 * .set(<jsf>PARSER_trimStrings</jsf>, <jk>true</jk>) 429 * .build(); 430 * 431 * <jc>// Use it.</jc> 432 * String json = <js>"{foo:' bar '}"</js>; 433 * Map<String,String> m = p.parse(json, HashMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 434 * <jsm>assertEquals</jsm>(<js>"bar"</js>, m.get(<js>"foo"</js>)); 435 * </p> 436 */ 437 public static final String PARSER_trimStrings = PREFIX + "trimStrings.b"; 438 439 /** 440 * Configuration property: Unbuffered. 441 * 442 * <h5 class='section'>Property:</h5> 443 * <ul> 444 * <li><b>Name:</b> <js>"Parser.unbuffered.b"</js> 445 * <li><b>Data type:</b> <code>Boolean</code> 446 * <li><b>Default:</b> <jk>false</jk> 447 * <li><b>Session-overridable:</b> <jk>true</jk> 448 * <li><b>Methods:</b> 449 * <ul> 450 * <li class='jm'>{@link ParserBuilder#unbuffered(boolean)} 451 * <li class='jm'>{@link ParserBuilder#unbuffered()} 452 * </ul> 453 * </ul> 454 * 455 * <h5 class='section'>Description:</h5> 456 * <p> 457 * If <jk>true</jk>, don't use internal buffering during parsing. 458 * 459 * <p> 460 * This is useful in cases when you want to parse the same input stream or reader multiple times 461 * because it may contain multiple independent POJOs to parse. 462 * <br>Buffering would cause the parser to read past the current POJO in the stream. 463 * 464 * <h5 class='section'>Example:</h5> 465 * <p class='bcode'> 466 * <jc>// Create a parser using strict mode.</jc> 467 * ReaderParser p = JsonParser. 468 * .<jsm>create</jsm>() 469 * .unbuffered() 470 * .build(); 471 * 472 * <jc>// Same, but use property.</jc> 473 * ReaderParser p = JsonParser. 474 * .<jsm>create</jsm>() 475 * .set(<jsf>PARSER_unbuffered</jsf>, <jk>true</jk>) 476 * .build(); 477 * 478 * <jc>// If you're calling parse on the same input multiple times, use a session instead of the parser directly.</jc> 479 * <jc>// It's more efficient because we don't need to recalc the session settings again. </jc> 480 * ReaderParserSession s = p.createSession(); 481 * 482 * <jc>// Read input with multiple POJOs</jc> 483 * Reader json = <jk>new</jk> StringReader(<js>"{foo:'bar'}{foo:'baz'}"</js>); 484 * MyBean myBean1 = s.parse(json, MyBean.<jk>class</jk>); 485 * MyBean myBean2 = s.parse(json, MyBean.<jk>class</jk>); 486 * </p> 487 * 488 * <h5 class='section'>Notes:</h5> 489 * <ul class='spaced-list'> 490 * <li> 491 * This only allows for multi-input streams for the following parsers: 492 * <ul> 493 * <li class='jc'>{@link JsonParser} 494 * <li class='jc'>{@link UonParser} 495 * </ul> 496 * It has no effect on the following parsers: 497 * <ul> 498 * <li class='jc'>{@link MsgPackParser} - It already doesn't use buffering. 499 * <li class='jc'>{@link XmlParser}, {@link HtmlParser} - These use StAX which doesn't allow for more than one root element anyway. 500 * <li>RDF parsers - These read everything into an internal model before any parsing begins. 501 * </ul> 502 * </ul> 503 * 504 * If <jk>true</jk>, don't use internal buffering during parsing. 505 */ 506 public static final String PARSER_unbuffered = PREFIX + "unbuffered.b"; 507 508 static Parser DEFAULT = new Parser(PropertyStore.create().build()) { 509 @Override 510 public ParserSession createSession(ParserSessionArgs args) { 511 throw new NoSuchMethodError(); 512 } 513 }; 514 515 //------------------------------------------------------------------------------------------------------------------- 516 // Instance 517 //------------------------------------------------------------------------------------------------------------------- 518 519 final boolean trimStrings, strict, autoCloseStreams, unbuffered; 520 final String inputStreamCharset, fileCharset; 521 final Class<? extends ParserListener> listener; 522 523 /** General parser properties currently set on this parser. */ 524 private final MediaType[] consumes; 525 526 // Hidden constructor to force subclass from InputStreamParser or ReaderParser. 527 Parser(PropertyStore ps, String...consumes) { 528 super(ps); 529 530 trimStrings = getBooleanProperty(PARSER_trimStrings, false); 531 strict = getBooleanProperty(PARSER_strict, false); 532 autoCloseStreams = getBooleanProperty(PARSER_autoCloseStreams, false); 533 unbuffered = getBooleanProperty(PARSER_unbuffered, false); 534 inputStreamCharset = getStringProperty(PARSER_inputStreamCharset, "UTF-8"); 535 fileCharset = getStringProperty(PARSER_fileCharset, "DEFAULT"); 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'> 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 * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type. 624 * 625 * <p> 626 * <code>Map</code> 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 * <h5 class='section'>Notes:</h5> 632 * <ul class='spaced-list'> 633 * <li> 634 * Use the {@link #parse(Object, Class)} method instead if you don't need a parameterized map/collection. 635 * </ul> 636 * 637 * @param <T> The class type of the object to create. 638 * @param input 639 * The input. 640 * <br>Character-based parsers can handle the following input class types: 641 * <ul> 642 * <li><jk>null</jk> 643 * <li>{@link Reader} 644 * <li>{@link CharSequence} 645 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 646 * {@link #PARSER_inputStreamCharset} property value). 647 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 648 * {@link #PARSER_inputStreamCharset} property value). 649 * <li>{@link File} containing system encoded text (or charset defined by 650 * {@link #PARSER_fileCharset} property value). 651 * </ul> 652 * <br>Stream-based parsers can handle the following input class types: 653 * <ul> 654 * <li><jk>null</jk> 655 * <li>{@link InputStream} 656 * <li><code><jk>byte</jk>[]</code> 657 * <li>{@link File} 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 668 * If the input contains a syntax error or is malformed, or is not valid for the specified type. 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 { 672 return createSession().parse(input, type, args); 673 } 674 675 /** 676 * Same as {@link #parse(Object, Type, Type...)} except optimized for a non-parameterized class. 677 * 678 * <p> 679 * This is the preferred parse method for simple types since you don't need to cast the results. 680 * 681 * <h5 class='section'>Examples:</h5> 682 * <p class='bcode'> 683 * ReaderParser p = JsonParser.<jsf>DEFAULT</jsf>; 684 * 685 * <jc>// Parse into a string.</jc> 686 * String s = p.parse(json, String.<jk>class</jk>); 687 * 688 * <jc>// Parse into a bean.</jc> 689 * MyBean b = p.parse(json, MyBean.<jk>class</jk>); 690 * 691 * <jc>// Parse into a bean array.</jc> 692 * MyBean[] ba = p.parse(json, MyBean[].<jk>class</jk>); 693 * 694 * <jc>// Parse into a linked-list of objects.</jc> 695 * List l = p.parse(json, LinkedList.<jk>class</jk>); 696 * 697 * <jc>// Parse into a map of object keys/values.</jc> 698 * Map m = p.parse(json, TreeMap.<jk>class</jk>); 699 * </p> 700 * 701 * @param <T> The class type of the object being created. 702 * @param input 703 * The input. 704 * See {@link #parse(Object, Type, Type...)} for details. 705 * @param type The object type to create. 706 * @return The parsed object. 707 * @throws ParseException 708 * If the input contains a syntax error or is malformed, or is not valid for the specified type. 709 */ 710 public final <T> T parse(Object input, Class<T> type) throws ParseException { 711 return createSession().parse(input, type); 712 } 713 714 /** 715 * Same as {@link #parse(Object, Type, Type...)} except the type has already been converted into a {@link ClassMeta} 716 * object. 717 * 718 * <p> 719 * This is mostly an internal method used by the framework. 720 * 721 * @param <T> The class type of the object being created. 722 * @param input 723 * The input. 724 * See {@link #parse(Object, Type, Type...)} for details. 725 * @param type The object type to create. 726 * @return The parsed object. 727 * @throws ParseException 728 * If the input contains a syntax error or is malformed, or is not valid for the specified type. 729 */ 730 public final <T> T parse(Object input, ClassMeta<T> type) throws ParseException { 731 return createSession().parse(input, type); 732 } 733 734 @Override /* Context */ 735 public final ParserSession createSession() { 736 return createSession(createDefaultSessionArgs()); 737 } 738 739 @Override /* Context */ 740 public final ParserSessionArgs createDefaultSessionArgs() { 741 return new ParserSessionArgs(ObjectMap.EMPTY_MAP, null, null, null, getPrimaryMediaType(), null); 742 } 743 744 //-------------------------------------------------------------------------------- 745 // Optional methods 746 //-------------------------------------------------------------------------------- 747 748 /** 749 * Parses the contents of the specified reader and loads the results into the specified map. 750 * 751 * <p> 752 * Reader must contain something that serializes to a map (such as text containing a JSON object). 753 * 754 * <p> 755 * Used in the following locations: 756 * <ul class='spaced-list'> 757 * <li> 758 * The various character-based constructors in {@link ObjectMap} (e.g. 759 * {@link ObjectMap#ObjectMap(CharSequence,Parser)}). 760 * </ul> 761 * 762 * @param <K> The key class type. 763 * @param <V> The value class type. 764 * @param input The input. See {@link #parse(Object, ClassMeta)} for supported input types. 765 * @param m The map being loaded. 766 * @param keyType The class type of the keys, or <jk>null</jk> to default to <code>String.<jk>class</jk></code>. 767 * @param valueType The class type of the values, or <jk>null</jk> to default to whatever is being parsed. 768 * @return The same map that was passed in to allow this method to be chained. 769 * @throws ParseException If the input contains a syntax error or is malformed, or is not valid for the specified type. 770 * @throws UnsupportedOperationException If not implemented. 771 */ 772 public final <K,V> Map<K,V> parseIntoMap(Object input, Map<K,V> m, Type keyType, Type valueType) throws ParseException { 773 return createSession().parseIntoMap(input, m, keyType, valueType); 774 } 775 776 /** 777 * Parses the contents of the specified reader and loads the results into the specified collection. 778 * 779 * <p> 780 * Used in the following locations: 781 * <ul class='spaced-list'> 782 * <li> 783 * The various character-based constructors in {@link ObjectList} (e.g. 784 * {@link ObjectList#ObjectList(CharSequence,Parser)}. 785 * </ul> 786 * 787 * @param <E> The element class type. 788 * @param input The input. See {@link #parse(Object, ClassMeta)} for supported input types. 789 * @param c The collection being loaded. 790 * @param elementType The class type of the elements, or <jk>null</jk> to default to whatever is being parsed. 791 * @return The same collection that was passed in to allow this method to be chained. 792 * @throws ParseException 793 * If the input contains a syntax error or is malformed, or is not valid for the specified type. 794 * @throws UnsupportedOperationException If not implemented. 795 */ 796 public final <E> Collection<E> parseIntoCollection(Object input, Collection<E> c, Type elementType) throws ParseException { 797 return createSession().parseIntoCollection(input, c, elementType); 798 } 799 800 /** 801 * Parses the specified array input with each entry in the object defined by the {@code argTypes} 802 * argument. 803 * 804 * <p> 805 * Used for converting arrays (e.g. <js>"[arg1,arg2,...]"</js>) into an {@code Object[]} that can be passed 806 * to the {@code Method.invoke(target, args)} method. 807 * 808 * <p> 809 * Used in the following locations: 810 * <ul class='spaced-list'> 811 * <li> 812 * Used to parse argument strings in the {@link PojoIntrospector#invokeMethod(Method, Reader)} method. 813 * </ul> 814 * 815 * @param input The input. Subclasses can support different input types. 816 * @param argTypes Specifies the type of objects to create for each entry in the array. 817 * @return An array of parsed objects. 818 * @throws ParseException 819 * If the input contains a syntax error or is malformed, or is not valid for the specified type. 820 */ 821 public final Object[] parseArgs(Object input, Type[] argTypes) throws ParseException { 822 if (argTypes == null || argTypes.length == 0) 823 return new Object[0]; 824 return createSession().parseArgs(input, argTypes); 825 } 826 827 828 //-------------------------------------------------------------------------------- 829 // Other methods 830 //-------------------------------------------------------------------------------- 831 832 /** 833 * Returns the media types handled based on the values passed to the <code>consumes</code> constructor parameter. 834 * 835 * @return The list of media types. Never <jk>null</jk>. 836 */ 837 public final MediaType[] getMediaTypes() { 838 return consumes; 839 } 840 841 /** 842 * Returns the first media type handled based on the values passed to the <code>consumes</code> constructor parameter. 843 * 844 * @return The media type. 845 */ 846 public final MediaType getPrimaryMediaType() { 847 return consumes == null || consumes.length == 0 ? null : consumes[0]; 848 } 849 850 @Override /* Context */ 851 public ObjectMap asMap() { 852 return super.asMap() 853 .append("Parser", new ObjectMap() 854 .append("trimStrings", trimStrings) 855 .append("strict", strict) 856 .append("inputStreamCharset", inputStreamCharset) 857 .append("fileCharset", fileCharset) 858 .append("listener", listener) 859 ); 860 } 861}