001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.parser; 018 019import static org.apache.juneau.collections.JsonMap.*; 020import static org.apache.juneau.common.utils.StringUtils.*; 021import static org.apache.juneau.internal.ClassUtils.*; 022 023import java.io.*; 024import java.lang.reflect.*; 025import java.nio.charset.*; 026import java.util.*; 027import java.util.function.*; 028 029import org.apache.juneau.*; 030import org.apache.juneau.annotation.*; 031import org.apache.juneau.collections.*; 032import org.apache.juneau.cp.*; 033import org.apache.juneau.httppart.*; 034import org.apache.juneau.internal.*; 035import org.apache.juneau.objecttools.*; 036import org.apache.juneau.swap.*; 037 038/** 039 * Session object that lives for the duration of a single use of {@link Parser}. 040 * 041 * <h5 class='section'>Notes:</h5><ul> 042 * <li class='warn'>This class is not thread safe and is typically discarded after one use. 043 * </ul> 044 * 045 * <h5 class='section'>See Also:</h5><ul> 046 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a> 047 048 * </ul> 049 */ 050public class ParserSession extends BeanSession { 051 052 //------------------------------------------------------------------------------------------------------------------- 053 // Static 054 //------------------------------------------------------------------------------------------------------------------- 055 056 /** 057 * Creates a new builder for this object. 058 * 059 * @param ctx The context creating this session. 060 * @return A new builder. 061 */ 062 public static Builder create(Parser ctx) { 063 return new Builder(ctx); 064 } 065 066 //------------------------------------------------------------------------------------------------------------------- 067 // Builder 068 //------------------------------------------------------------------------------------------------------------------- 069 070 /** 071 * Builder class. 072 */ 073 public static class Builder extends BeanSession.Builder { 074 075 Parser ctx; 076 Method javaMethod; 077 Object outer; 078 HttpPartSchema schema; 079 080 /** 081 * Constructor 082 * 083 * @param ctx The context creating this session. 084 */ 085 protected Builder(Parser ctx) { 086 super(ctx.getBeanContext()); 087 this.ctx = ctx; 088 mediaTypeDefault(ctx.getPrimaryMediaType()); 089 } 090 091 @Override 092 public ParserSession build() { 093 return new ParserSession(this); 094 } 095 096 /** 097 * The java method that called this serializer, usually the method in a REST servlet. 098 * 099 * @param value 100 * The new property value. 101 * <br>Can be <jk>null</jk>. 102 * @return This object. 103 */ 104 public Builder javaMethod(Method value) { 105 this.javaMethod = value; 106 return this; 107 } 108 109 /** 110 * The outer object for instantiating top-level non-static inner classes. 111 * 112 * @param value 113 * The new property value. 114 * <br>Can be <jk>null</jk>. 115 * @return This object. 116 */ 117 public Builder outer(Object value) { 118 this.outer = value; 119 return this; 120 } 121 122 /** 123 * HTTP-part schema. 124 * 125 * <p> 126 * Used for schema-based serializers and parsers to define additional formatting. 127 * 128 * @param value 129 * The new value for this property. 130 * <br>Can be <jk>null</jk>. 131 * @return This object. 132 */ 133 public Builder schema(HttpPartSchema value) { 134 if (value != null) 135 this.schema = value; 136 return this; 137 } 138 139 /** 140 * Same as {@link #schema(HttpPartSchema)} but doesn't overwrite the value if it is already set. 141 * 142 * @param value 143 * The new value for this property. 144 * <br>If <jk>null</jk>, then the locale defined on the context is used. 145 * @return This object. 146 */ 147 public Builder schemaDefault(HttpPartSchema value) { 148 if (value != null && schema == null) 149 this.schema = value; 150 return this; 151 } 152 @Override /* Overridden from Builder */ 153 public <T> Builder apply(Class<T> type, Consumer<T> apply) { 154 super.apply(type, apply); 155 return this; 156 } 157 158 @Override /* Overridden from Builder */ 159 public Builder debug(Boolean value) { 160 super.debug(value); 161 return this; 162 } 163 164 @Override /* Overridden from Builder */ 165 public Builder properties(Map<String,Object> value) { 166 super.properties(value); 167 return this; 168 } 169 170 @Override /* Overridden from Builder */ 171 public Builder property(String key, Object value) { 172 super.property(key, value); 173 return this; 174 } 175 176 @Override /* Overridden from Builder */ 177 public Builder unmodifiable() { 178 super.unmodifiable(); 179 return this; 180 } 181 182 @Override /* Overridden from Builder */ 183 public Builder locale(Locale value) { 184 super.locale(value); 185 return this; 186 } 187 188 @Override /* Overridden from Builder */ 189 public Builder localeDefault(Locale value) { 190 super.localeDefault(value); 191 return this; 192 } 193 194 @Override /* Overridden from Builder */ 195 public Builder mediaType(MediaType value) { 196 super.mediaType(value); 197 return this; 198 } 199 200 @Override /* Overridden from Builder */ 201 public Builder mediaTypeDefault(MediaType value) { 202 super.mediaTypeDefault(value); 203 return this; 204 } 205 206 @Override /* Overridden from Builder */ 207 public Builder timeZone(TimeZone value) { 208 super.timeZone(value); 209 return this; 210 } 211 212 @Override /* Overridden from Builder */ 213 public Builder timeZoneDefault(TimeZone value) { 214 super.timeZoneDefault(value); 215 return this; 216 } 217 } 218 219 //------------------------------------------------------------------------------------------------------------------- 220 // Instance 221 //------------------------------------------------------------------------------------------------------------------- 222 223 private final Parser ctx; 224 private final Method javaMethod; 225 private final Object outer; 226 private final Stack<StringBuilder> sbStack; 227 private final HttpPartSchema schema; 228 229 // Writable properties. 230 private BeanPropertyMeta currentProperty; 231 private ClassMeta<?> currentClass; 232 private final ParserListener listener; 233 234 private Position mark = new Position(-1); 235 236 private ParserPipe pipe; 237 238 /** 239 * Constructor. 240 * 241 * @param builder The builder for this object. 242 */ 243 protected ParserSession(Builder builder) { 244 super(builder); 245 ctx = builder.ctx; 246 javaMethod = builder.javaMethod; 247 outer = builder.outer; 248 schema = builder.schema; 249 listener = BeanCreator.of(ParserListener.class).type(ctx.getListener()).orElse(null); 250 sbStack = new Stack<>(); 251 } 252 253 //----------------------------------------------------------------------------------------------------------------- 254 // Abstract methods 255 //----------------------------------------------------------------------------------------------------------------- 256 257 /** 258 * Workhorse method. 259 * 260 * <p> 261 * Subclasses are expected to implement this method or {@link Parser#doParse(ParserSession,ParserPipe,ClassMeta)}. 262 * 263 * <p> 264 * The default implementation of this method simply calls {@link Parser#doParse(ParserSession,ParserPipe,ClassMeta)}. 265 * 266 * @param pipe Where to get the input from. 267 * @param type 268 * The class type of the object to create. 269 * If <jk>null</jk> or <code>Object.<jk>class</jk></code>, object type is based on what's being parsed. 270 * For example, when parsing JSON text, it may return a <c>String</c>, <c>Number</c>, 271 * <c>JsonMap</c>, etc... 272 * @param <T> The class type of the object to create. 273 * @return The parsed object. 274 * @throws IOException Thrown by underlying stream. 275 * @throws ParseException Malformed input encountered. 276 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 277 */ 278 protected <T> T doParse(ParserPipe pipe, ClassMeta<T> type) throws IOException, ParseException, ExecutableException { 279 return ctx.doParse(this, pipe, type); 280 } 281 282 /** 283 * Returns <jk>true</jk> if this parser subclasses from {@link ReaderParser}. 284 * 285 * @return <jk>true</jk> if this parser subclasses from {@link ReaderParser}. 286 */ 287 public boolean isReaderParser() { 288 return false; 289 } 290 291 292 //----------------------------------------------------------------------------------------------------------------- 293 // Other methods 294 //----------------------------------------------------------------------------------------------------------------- 295 296 /** 297 * Wraps the specified input object into a {@link ParserPipe} object so that it can be easily converted into 298 * a stream or reader. 299 * 300 * @param input 301 * The input. 302 * <br>For character-based parsers, this can be any of the following types: 303 * <ul> 304 * <li><jk>null</jk> 305 * <li>{@link Reader} 306 * <li>{@link CharSequence} 307 * <li>{@link InputStream} containing UTF-8 encoded text (or whatever the encoding specified by 308 * {@link ReaderParser.Builder#streamCharset(Charset)}). 309 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or whatever the encoding specified by 310 * {@link ReaderParser.Builder#streamCharset(Charset)}). 311 * <li>{@link File} containing system encoded text (or whatever the encoding specified by 312 * {@link ReaderParser.Builder#fileCharset(Charset)}). 313 * </ul> 314 * <br>For byte-based parsers, this can be any of the following types: 315 * <ul> 316 * <li><jk>null</jk> 317 * <li>{@link InputStream} 318 * <li><code><jk>byte</jk>[]</code> 319 * <li>{@link File} 320 * <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser.Builder#binaryFormat(BinaryFormat)} setting. 321 * </ul> 322 * @return 323 * A new {@link ParserPipe} wrapper around the specified input object. 324 */ 325 protected ParserPipe createPipe(Object input) { 326 return null; 327 } 328 329 /** 330 * Returns information used to determine at what location in the parse a failure occurred. 331 * 332 * @return A map, typically containing something like <c>{line:123,column:456,currentProperty:"foobar"}</c> 333 */ 334 public final JsonMap getLastLocation() { 335 JsonMap m = new JsonMap(); 336 if (currentClass != null) 337 m.put("currentClass", currentClass.toString(true)); 338 if (currentProperty != null) 339 m.put("currentProperty", currentProperty); 340 return m; 341 } 342 343 /** 344 * Returns the Java method that invoked this parser. 345 * 346 * <p> 347 * When using the REST API, this is the Java method invoked by the REST call. 348 * Can be used to access annotations defined on the method or class. 349 * 350 * @return The Java method that invoked this parser. 351 */ 352 protected final Method getJavaMethod() { 353 return javaMethod; 354 } 355 356 /** 357 * Returns the outer object used for instantiating top-level non-static member classes. 358 * 359 * <p> 360 * When using the REST API, this is the servlet object. 361 * 362 * @return The outer object. 363 */ 364 protected final Object getOuter() { 365 return outer; 366 } 367 368 /** 369 * Sets the current bean property being parsed for proper error messages. 370 * 371 * @param currentProperty The current property being parsed. 372 */ 373 protected final void setCurrentProperty(BeanPropertyMeta currentProperty) { 374 this.currentProperty = currentProperty; 375 } 376 377 /** 378 * Sets the current class being parsed for proper error messages. 379 * 380 * @param currentClass The current class being parsed. 381 */ 382 protected final void setCurrentClass(ClassMeta<?> currentClass) { 383 this.currentClass = currentClass; 384 } 385 386 /** 387 * Trims the specified object if it's a <c>String</c> and {@link #isTrimStrings()} returns <jk>true</jk>. 388 * 389 * @param <K> The object type. 390 * @param o The object to trim. 391 * @return The trimmed string if it's a string. 392 */ 393 @SuppressWarnings("unchecked") 394 protected final <K> K trim(K o) { 395 if (isTrimStrings() && o instanceof String) 396 return (K)o.toString().trim(); 397 return o; 398 399 } 400 401 /** 402 * Trims the specified string if {@link ParserSession#isTrimStrings()} returns <jk>true</jk>. 403 * 404 * @param s The input string to trim. 405 * @return The trimmed string, or <jk>null</jk> if the input was <jk>null</jk>. 406 */ 407 protected final String trim(String s) { 408 if (isTrimStrings() && s != null) 409 return s.trim(); 410 return s; 411 } 412 413 /** 414 * Converts the specified <c>JsonMap</c> into a bean identified by the <js>"_type"</js> property in the map. 415 * 416 * @param m The map to convert to a bean. 417 * @param pMeta The current bean property being parsed. 418 * @param eType The current expected type being parsed. 419 * @return 420 * The converted bean, or the same map if the <js>"_type"</js> entry wasn't found or didn't resolve to a bean. 421 */ 422 protected final Object cast(JsonMap m, BeanPropertyMeta pMeta, ClassMeta<?> eType) { 423 424 String btpn = getBeanTypePropertyName(eType); 425 426 Object o = m.get(btpn); 427 if (o == null) 428 return m; 429 String typeName = o.toString(); 430 431 ClassMeta<?> cm = getClassMeta(typeName, pMeta, eType); 432 433 if (cm != null) { 434 BeanMap<?> bm = m.getBeanSession().newBeanMap(cm.getInnerClass()); 435 436 // Iterate through all the entries in the map and set the individual field values. 437 m.forEach((k,v) -> { 438 if (! k.equals(btpn)) { 439 // Attempt to recursively cast child maps. 440 if (v instanceof JsonMap) 441 v = cast((JsonMap)v, pMeta, eType); 442 bm.put(k, v); 443 } 444 }); 445 return bm.getBean(); 446 } 447 448 return m; 449 } 450 451 /** 452 * Give the specified dictionary name, resolve it to a class. 453 * 454 * @param typeName The dictionary name to resolve. 455 * @param pMeta The bean property we're currently parsing. 456 * @param eType The expected type we're currently parsing. 457 * @return The resolved class, or <jk>null</jk> if the type name could not be resolved. 458 */ 459 protected final ClassMeta<?> getClassMeta(String typeName, BeanPropertyMeta pMeta, ClassMeta<?> eType) { 460 BeanRegistry br = null; 461 462 // Resolve via @Beanp(dictionary={}) 463 if (pMeta != null) { 464 br = pMeta.getBeanRegistry(); 465 if (br != null && br.hasName(typeName)) 466 return br.getClassMeta(typeName); 467 } 468 469 // Resolve via @Bean(dictionary={}) on the expected type where the 470 // expected type is an interface with subclasses. 471 if (eType != null) { 472 br = eType.getBeanRegistry(); 473 if (br != null && br.hasName(typeName)) 474 return br.getClassMeta(typeName); 475 } 476 477 // Last resort, resolve using the session registry. 478 return getBeanRegistry().getClassMeta(typeName); 479 } 480 481 /** 482 * Specialized warning when an exception is thrown while executing a bean setter. 483 * 484 * @param p The bean map entry representing the bean property. 485 * @param t The throwable that the bean setter threw. 486 */ 487 protected final void onBeanSetterException(BeanPropertyMeta p, Throwable t) { 488 if (listener != null) 489 listener.onBeanSetterException(this, t, p); 490 String prefix = ""; 491 addWarning("{0}Could not call setValue() on property ''{1}'' of class ''{2}'', exception = {3}", prefix, 492 p.getName(), p.getBeanMeta().getClassMeta(), t.getLocalizedMessage()); 493 } 494 495 /** 496 * Method that gets called when an unknown bean property name is encountered. 497 * 498 * @param propertyName The unknown bean property name. 499 * @param beanMap The bean that doesn't have the expected property. 500 * @param value The parsed value. 501 * @throws ParseException 502 * Automatically thrown if {@link org.apache.juneau.BeanContext.Builder#ignoreUnknownBeanProperties()} setting on this parser is 503 * <jk>false</jk> 504 * @param <T> The class type of the bean map that doesn't have the expected property. 505 */ 506 protected final <T> void onUnknownProperty(String propertyName, BeanMap<T> beanMap, Object value) throws ParseException { 507 if (propertyName.equals(getBeanTypePropertyName(beanMap.getClassMeta()))) 508 return; 509 if (! isIgnoreUnknownBeanProperties()) 510 if (value != null || ! isIgnoreUnknownNullBeanProperties()) 511 throw new ParseException(this, 512 "Unknown property ''{0}'' encountered while trying to parse into class ''{1}''", propertyName, 513 beanMap.getClassMeta()); 514 if (listener != null) 515 listener.onUnknownBeanProperty(this, propertyName, beanMap.getClassMeta().getInnerClass(), beanMap.getBean()); 516 } 517 518 /** 519 * Parses input into the specified object type. 520 * 521 * <p> 522 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 523 * 524 * <h5 class='section'>Examples:</h5> 525 * <p class='bjava'> 526 * ReaderParser <jv>parser</jv> = JsonParser.<jsf>DEFAULT</jsf>; 527 * 528 * <jc>// Parse into a linked-list of strings.</jc> 529 * List <jv>list1</jv> = <jv>parser</jv>.parse(<jv>json</jv>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 530 * 531 * <jc>// Parse into a linked-list of beans.</jc> 532 * List <jv>list2</jv> = <jv>parser</jv>.parse(<jv>json</jv>, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 533 * 534 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 535 * List <jv>list3</jv> = <jv>parser</jv>.parse(<jv>json</jv>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 536 * 537 * <jc>// Parse into a map of string keys/values.</jc> 538 * Map <jv>map1</jv> = <jv>parser</jv>.parse(<jv>json</jv>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 539 * 540 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 541 * Map <jv>map2</jv> = <jv>parser</jv>.parse(<jv>json</jv>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 542 * </p> 543 * 544 * <p> 545 * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type. 546 * 547 * <p> 548 * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 549 * 550 * <p> 551 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 552 * 553 * <h5 class='section'>Notes:</h5><ul> 554 * <li class='note'> 555 * Use the {@link #parse(Object, Class)} method instead if you don't need a parameterized map/collection. 556 * </ul> 557 * 558 * @param <T> The class type of the object to create. 559 * @param input 560 * The input. 561 * <br>Character-based parsers can handle the following input class types: 562 * <ul> 563 * <li><jk>null</jk> 564 * <li>{@link Reader} 565 * <li>{@link CharSequence} 566 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 567 * {@link ReaderParser.Builder#streamCharset(Charset)} property value). 568 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 569 * {@link ReaderParser.Builder#streamCharset(Charset)} property value). 570 * <li>{@link File} containing system encoded text (or charset defined by 571 * {@link ReaderParser.Builder#fileCharset(Charset)} property value). 572 * </ul> 573 * <br>Stream-based parsers can handle the following input class types: 574 * <ul> 575 * <li><jk>null</jk> 576 * <li>{@link InputStream} 577 * <li><code><jk>byte</jk>[]</code> 578 * <li>{@link File} 579 * </ul> 580 * @param type 581 * The object type to create. 582 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 583 * @param args 584 * The type arguments of the class if it's a collection or map. 585 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 586 * <br>Ignored if the main type is not a map or collection. 587 * @return The parsed object. 588 * @throws ParseException Malformed input encountered. 589 * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. 590 * @throws IOException Thrown by the underlying stream. 591 */ 592 @SuppressWarnings("unchecked") 593 public final <T> T parse(Object input, Type type, Type...args) throws ParseException, IOException { 594 try (ParserPipe pipe = createPipe(input)) { 595 return (T)parseInner(pipe, getClassMeta(type, args)); 596 } 597 } 598 599 /** 600 * Same as {@link #parse(Object,Type,Type...)} but parses from a string and doesn't throw an {@link IOException}. 601 * 602 * @param <T> The class type of the object to create. 603 * @param input 604 * The input. 605 * <br>Character-based parsers can handle the following input class types: 606 * <ul> 607 * <li><jk>null</jk> 608 * <li>{@link Reader} 609 * <li>{@link CharSequence} 610 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 611 * {@link ReaderParser.Builder#streamCharset(Charset)} property value). 612 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 613 * {@link ReaderParser.Builder#streamCharset(Charset)} property value). 614 * <li>{@link File} containing system encoded text (or charset defined by 615 * {@link ReaderParser.Builder#fileCharset(Charset)} property value). 616 * </ul> 617 * <br>Stream-based parsers can handle the following input class types: 618 * <ul> 619 * <li><jk>null</jk> 620 * <li>{@link InputStream} 621 * <li><code><jk>byte</jk>[]</code> 622 * <li>{@link File} 623 * </ul> 624 * @param type 625 * The object type to create. 626 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 627 * @param args 628 * The type arguments of the class if it's a collection or map. 629 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 630 * <br>Ignored if the main type is not a map or collection. 631 * @return The parsed object. 632 * @throws ParseException Malformed input encountered. 633 * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. 634 */ 635 @SuppressWarnings("unchecked") 636 public final <T> T parse(String input, Type type, Type...args) throws ParseException { 637 try (ParserPipe pipe = createPipe(input)) { 638 return (T)parseInner(pipe, getClassMeta(type, args)); 639 } catch (IOException e) { 640 throw new ParseException(e); // Shouldn't happen. 641 } 642 } 643 644 /** 645 * Same as {@link #parse(Object, Type, Type...)} except optimized for a non-parameterized class. 646 * 647 * <p> 648 * This is the preferred parse method for simple types since you don't need to cast the results. 649 * 650 * <h5 class='section'>Examples:</h5> 651 * <p class='bjava'> 652 * ReaderParser <jv>parser</jv> = JsonParser.<jsf>DEFAULT</jsf>; 653 * 654 * <jc>// Parse into a string.</jc> 655 * String <jv>string</jv> = <jv>parser</jv>.parse(<jv>json</jv>, String.<jk>class</jk>); 656 * 657 * <jc>// Parse into a bean.</jc> 658 * MyBean <jv>bean</jv> = <jv>parser</jv>.parse(<jv>json</jv>, MyBean.<jk>class</jk>); 659 * 660 * <jc>// Parse into a bean array.</jc> 661 * MyBean[] <jv>beanArray</jv> = <jv>parser</jv>.parse(<jv>json</jv>, MyBean[].<jk>class</jk>); 662 * 663 * <jc>// Parse into a linked-list of objects.</jc> 664 * List <jv>list</jv> = <jv>parser</jv>.parse(<jv>json</jv>, LinkedList.<jk>class</jk>); 665 * 666 * <jc>// Parse into a map of object keys/values.</jc> 667 * Map <jv>map</jv> = <jv>parser</jv>.parse(<jv>json</jv>, TreeMap.<jk>class</jk>); 668 * </p> 669 * 670 * @param <T> The class type of the object being created. 671 * @param input 672 * The input. 673 * See {@link #parse(Object, Type, Type...)} for details. 674 * @param type The object type to create. 675 * @return The parsed object. 676 * @throws ParseException Malformed input encountered. 677 * @throws IOException Thrown by the underlying stream. 678 */ 679 public final <T> T parse(Object input, Class<T> type) throws ParseException, IOException { 680 try (ParserPipe pipe = createPipe(input)) { 681 return parseInner(pipe, getClassMeta(type)); 682 } 683 } 684 685 /** 686 * Same as {@link #parse(Object, Class)} but parses from a string and doesn't throw an {@link IOException}. 687 * 688 * <p> 689 * This is the preferred parse method for simple types since you don't need to cast the results. 690 * 691 * <h5 class='section'>Examples:</h5> 692 * <p class='bjava'> 693 * ReaderParser <jv>parser</jv> = JsonParser.<jsf>DEFAULT</jsf>; 694 * 695 * <jc>// Parse into a string.</jc> 696 * String <jv>string</jv> = <jv>parser</jv>.parse(<jv>json</jv>, String.<jk>class</jk>); 697 * 698 * <jc>// Parse into a bean.</jc> 699 * MyBean <jv>bean</jv> = <jv>parser</jv>.parse(<jv>json</jv>, MyBean.<jk>class</jk>); 700 * 701 * <jc>// Parse into a bean array.</jc> 702 * MyBean[] <jv>beanArray</jv> = <jv>parser</jv>.parse(<jv>json</jv>, MyBean[].<jk>class</jk>); 703 * 704 * <jc>// Parse into a linked-list of objects.</jc> 705 * List <jv>list</jv> = <jv>parser</jv>.parse(<jv>json</jv>, LinkedList.<jk>class</jk>); 706 * 707 * <jc>// Parse into a map of object keys/values.</jc> 708 * Map <jv>map</jv> = <jv>parser</jv>.parse(<jv>json</jv>, TreeMap.<jk>class</jk>); 709 * </p> 710 * 711 * @param <T> The class type of the object being created. 712 * @param input 713 * The input. 714 * See {@link #parse(Object, Type, Type...)} for details. 715 * @param type The object type to create. 716 * @return The parsed object. 717 * @throws ParseException Malformed input encountered. 718 */ 719 public final <T> T parse(String input, Class<T> type) throws ParseException { 720 try (ParserPipe pipe = createPipe(input)) { 721 return parseInner(pipe, getClassMeta(type)); 722 } catch (IOException e) { 723 throw new ParseException(e); // Shouldn't happen. 724 } 725 } 726 727 /** 728 * Same as {@link #parse(Object, Type, Type...)} except the type has already been converted into a {@link ClassMeta} 729 * object. 730 * 731 * <p> 732 * This is mostly an internal method used by the framework. 733 * 734 * @param <T> The class type of the object being created. 735 * @param input 736 * The input. 737 * See {@link #parse(Object, Type, Type...)} for details. 738 * @param type The object type to create. 739 * @return The parsed object. 740 * @throws ParseException Malformed input encountered. 741 * @throws IOException Thrown by the underlying stream. 742 */ 743 public final <T> T parse(Object input, ClassMeta<T> type) throws ParseException, IOException { 744 try (ParserPipe pipe = createPipe(input)) { 745 return parseInner(pipe, type); 746 } 747 } 748 749 /** 750 * Same as {@link #parse(Object, ClassMeta)} except parses from a string and doesn't throw an {@link IOException}. 751 * 752 * <p> 753 * This is mostly an internal method used by the framework. 754 * 755 * @param <T> The class type of the object being created. 756 * @param input 757 * The input. 758 * See {@link #parse(Object, Type, Type...)} for details. 759 * @param type The object type to create. 760 * @return The parsed object. 761 * @throws ParseException Malformed input encountered. 762 */ 763 public final <T> T parse(String input, ClassMeta<T> type) throws ParseException { 764 try (ParserPipe pipe = createPipe(input)) { 765 return parseInner(pipe, type); 766 } catch (IOException e) { 767 throw new ParseException(e); // Shouldn't happen. 768 } 769 } 770 771 /** 772 * Entry point for all parsing calls. 773 * 774 * <p> 775 * Calls the {@link #doParse(ParserPipe, ClassMeta)} implementation class and catches/re-wraps any exceptions 776 * thrown. 777 * 778 * @param pipe The parser input. 779 * @param type The class type of the object to create. 780 * @param <T> The class type of the object to create. 781 * @return The parsed object. 782 * @throws ParseException Malformed input encountered. 783 * @throws IOException Thrown by the underlying stream. 784 */ 785 private <T> T parseInner(ParserPipe pipe, ClassMeta<T> type) throws ParseException, IOException { 786 if (type.isVoid()) 787 return null; 788 try { 789 return doParse(pipe, type); 790 } catch (ParseException | IOException e) { 791 throw e; 792 } catch (StackOverflowError e) { 793 throw new ParseException(this, "Depth too deep. Stack overflow occurred."); 794 } catch (Exception e) { 795 throw new ParseException(this, e, "Exception occurred. exception={0}, message={1}.", 796 e.getClass().getSimpleName(), e.getLocalizedMessage()); 797 } finally { 798 checkForWarnings(); 799 } 800 } 801 802 /** 803 * Parses the contents of the specified reader and loads the results into the specified map. 804 * 805 * <p> 806 * Reader must contain something that serializes to a map (such as text containing a JSON object). 807 * 808 * <p> 809 * Used in the following locations: 810 * <ul class='spaced-list'> 811 * <li> 812 * The various character-based constructors in {@link JsonMap} (e.g. 813 * {@link JsonMap#JsonMap(CharSequence,Parser)}). 814 * </ul> 815 * 816 * @param <K> The key class type. 817 * @param <V> The value class type. 818 * @param input The input. See {@link #parse(Object, ClassMeta)} for supported input types. 819 * @param m The map being loaded. 820 * @param keyType The class type of the keys, or <jk>null</jk> to default to <code>String.<jk>class</jk></code>. 821 * @param valueType The class type of the values, or <jk>null</jk> to default to whatever is being parsed. 822 * @return The same map that was passed in to allow this method to be chained. 823 * @throws ParseException Malformed input encountered. 824 * @throws UnsupportedOperationException If not implemented. 825 */ 826 public final <K,V> Map<K,V> parseIntoMap(Object input, Map<K,V> m, Type keyType, Type valueType) throws ParseException { 827 try (ParserPipe pipe = createPipe(input)) { 828 return doParseIntoMap(pipe, m, keyType, valueType); 829 } catch (ParseException e) { 830 throw e; 831 } catch (Exception e) { 832 throw new ParseException(this, e); 833 } finally { 834 checkForWarnings(); 835 } 836 } 837 838 /** 839 * Implementation method. 840 * 841 * <p> 842 * Default implementation throws an {@link UnsupportedOperationException}. 843 * 844 * @param <K> The key type. 845 * @param <V> The value type. 846 * @param pipe The parser input. 847 * @param m The map being loaded. 848 * @param keyType The class type of the keys, or <jk>null</jk> to default to <code>String.<jk>class</jk></code>. 849 * @param valueType The class type of the values, or <jk>null</jk> to default to whatever is being parsed. 850 * @return The same map that was passed in to allow this method to be chained. 851 * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed. 852 */ 853 protected <K,V> Map<K,V> doParseIntoMap(ParserPipe pipe, Map<K,V> m, Type keyType, Type valueType) throws Exception { 854 throw new UnsupportedOperationException("Parser '"+className(getClass())+"' does not support this method."); 855 } 856 857 /** 858 * Parses the contents of the specified reader and loads the results into the specified collection. 859 * 860 * <p> 861 * Used in the following locations: 862 * <ul class='spaced-list'> 863 * <li> 864 * The various character-based constructors in {@link JsonList} (e.g. 865 * {@link JsonList#JsonList(CharSequence,Parser)}. 866 * </ul> 867 * 868 * @param <E> The element class type. 869 * @param input The input. See {@link #parse(Object, ClassMeta)} for supported input types. 870 * @param c The collection being loaded. 871 * @param elementType The class type of the elements, or <jk>null</jk> to default to whatever is being parsed. 872 * @return The same collection that was passed in to allow this method to be chained. 873 * @throws ParseException Malformed input encountered. 874 * @throws UnsupportedOperationException If not implemented. 875 */ 876 public final <E> Collection<E> parseIntoCollection(Object input, Collection<E> c, Type elementType) throws ParseException { 877 try (ParserPipe pipe = createPipe(input)) { 878 return doParseIntoCollection(pipe, c, elementType); 879 } catch (ParseException e) { 880 throw e; 881 } catch (StackOverflowError e) { 882 throw new ParseException(this, "Depth too deep. Stack overflow occurred."); 883 } catch (IOException e) { 884 throw new ParseException(this, e, "I/O exception occurred. exception={0}, message={1}.", 885 e.getClass().getSimpleName(), e.getLocalizedMessage()); 886 } catch (Exception e) { 887 throw new ParseException(this, e, "Exception occurred. exception={0}, message={1}.", 888 e.getClass().getSimpleName(), e.getLocalizedMessage()); 889 } finally { 890 checkForWarnings(); 891 } 892 } 893 894 /** 895 * Implementation method. 896 * 897 * <p> 898 * Default implementation throws an {@link UnsupportedOperationException}. 899 * 900 * @param <E> The element type. 901 * @param pipe The parser input. 902 * @param c The collection being loaded. 903 * @param elementType The class type of the elements, or <jk>null</jk> to default to whatever is being parsed. 904 * @return The same collection that was passed in to allow this method to be chained. 905 * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed. 906 */ 907 protected <E> Collection<E> doParseIntoCollection(ParserPipe pipe, Collection<E> c, Type elementType) throws Exception { 908 throw new UnsupportedOperationException("Parser '"+className(getClass())+"' does not support this method."); 909 } 910 911 /** 912 * Parses the specified array input with each entry in the object defined by the {@code argTypes} 913 * argument. 914 * 915 * <p> 916 * Used for converting arrays (e.g. <js>"[arg1,arg2,...]"</js>) into an {@code Object[]} that can be passed 917 * to the {@code Method.invoke(target, args)} method. 918 * 919 * <p> 920 * Used in the following locations: 921 * <ul class='spaced-list'> 922 * <li> 923 * Used to parse argument strings in the {@link ObjectIntrospector#invokeMethod(Method, Reader)} method. 924 * </ul> 925 * 926 * @param input The input. Subclasses can support different input types. 927 * @param argTypes Specifies the type of objects to create for each entry in the array. 928 * @return An array of parsed objects. 929 * @throws ParseException Malformed input encountered. 930 */ 931 public final Object[] parseArgs(Object input, Type[] argTypes) throws ParseException { 932 try (ParserPipe pipe = createPipe(input)) { 933 return doParse(pipe, getArgsClassMeta(argTypes)); 934 } catch (ParseException e) { 935 throw e; 936 } catch (StackOverflowError e) { 937 throw new ParseException(this, "Depth too deep. Stack overflow occurred."); 938 } catch (IOException e) { 939 throw new ParseException(this, e, "I/O exception occurred. exception={0}, message={1}.", 940 e.getClass().getSimpleName(), e.getLocalizedMessage()); 941 } catch (Exception e) { 942 throw new ParseException(this, e, "Exception occurred. exception={0}, message={1}.", 943 e.getClass().getSimpleName(), e.getLocalizedMessage()); 944 } finally { 945 checkForWarnings(); 946 } 947 } 948 949 /** 950 * Converts the specified string to the specified type. 951 * 952 * @param outer 953 * The outer object if we're converting to an inner object that needs to be created within the context 954 * of an outer object. 955 * @param s The string to convert. 956 * @param type The class type to convert the string to. 957 * @return The string converted as an object of the specified type. 958 * @param <T> The class type to convert the string to. 959 * @throws ParseException Malformed input encountered. 960 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 961 */ 962 @SuppressWarnings({ "unchecked", "rawtypes" }) 963 protected final <T> T convertAttrToType(Object outer, String s, ClassMeta<T> type) throws ParseException { 964 if (s == null) 965 return null; 966 967 if (type == null) 968 type = (ClassMeta<T>)object(); 969 ObjectSwap swap = type.getSwap(this); 970 ClassMeta<?> sType = swap == null ? type : swap.getSwapClassMeta(this); 971 972 Object o = s; 973 if (sType.isChar()) 974 o = parseCharacter(s); 975 else if (sType.isNumber()) 976 o = parseNumber(s, (Class<? extends Number>)sType.getInnerClass()); 977 else if (sType.isBoolean()) 978 o = Boolean.parseBoolean(s); 979 else if (! (sType.isCharSequence() || sType.isObject())) { 980 if (sType.canCreateNewInstanceFromString(outer)) 981 o = sType.newInstanceFromString(outer, s); 982 else 983 throw new ParseException(this, "Invalid conversion from string to class ''{0}''", type); 984 } 985 986 if (swap != null) 987 o = unswap(swap, o, type); 988 989 return (T)o; 990 } 991 992 /** 993 * Convenience method for calling the {@link ParentProperty @ParentProperty} method on the specified object if it 994 * exists. 995 * 996 * @param cm The class type of the object. 997 * @param o The object. 998 * @param parent The parent to set. 999 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 1000 */ 1001 protected static final void setParent(ClassMeta<?> cm, Object o, Object parent) throws ExecutableException { 1002 Setter m = cm.getParentProperty(); 1003 if (m != null) 1004 m.set(o, parent); 1005 } 1006 1007 /** 1008 * Convenience method for calling the {@link NameProperty @NameProperty} method on the specified object if it exists. 1009 * 1010 * @param cm The class type of the object. 1011 * @param o The object. 1012 * @param name The name to set. 1013 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 1014 */ 1015 protected static final void setName(ClassMeta<?> cm, Object o, Object name) throws ExecutableException { 1016 if (cm != null) { 1017 Setter m = cm.getNameProperty(); 1018 if (m != null) 1019 m.set(o, name); 1020 } 1021 } 1022 1023 /** 1024 * Returns the listener associated with this session. 1025 * 1026 * @param <T> The listener type. 1027 * @param c The listener class to cast to. 1028 * @return The listener associated with this session, or <jk>null</jk> if there is no listener. 1029 */ 1030 @SuppressWarnings("unchecked") 1031 public <T extends ParserListener> T getListener(Class<T> c) { 1032 return (T)listener; 1033 } 1034 1035 /** 1036 * The {@link #createPipe(Object)} method should call this method to set the pipe for debugging purposes. 1037 * 1038 * @param pipe The pipe created for this session. 1039 * @return The same pipe. 1040 */ 1041 protected ParserPipe setPipe(ParserPipe pipe) { 1042 this.pipe = pipe; 1043 return pipe; 1044 } 1045 1046 /** 1047 * Returns the current position into the reader or input stream. 1048 * 1049 * @return 1050 * The current position into the reader or input stream. 1051 * <br>Never <jk>null</jk>. 1052 */ 1053 public Position getPosition() { 1054 if (mark.line != -1 || mark.column != -1 || mark.position != -1) 1055 return mark; 1056 if (pipe == null) 1057 return Position.UNKNOWN; 1058 return pipe.getPosition(); 1059 } 1060 1061 /** 1062 * Marks the current position. 1063 */ 1064 protected void mark() { 1065 if (pipe != null) { 1066 Position p = pipe.getPosition(); 1067 mark.line = p.line; 1068 mark.column = p.column; 1069 mark.position = p.position; 1070 } 1071 } 1072 1073 /** 1074 * Unmarks the current position. 1075 */ 1076 protected void unmark() { 1077 mark.line = -1; 1078 mark.column = -1; 1079 mark.position = -1; 1080 } 1081 1082 /** 1083 * Returns the input as a string. 1084 * 1085 * <p> 1086 * This always returns a value for input of type {@link CharSequence}. 1087 * <br>For other input types, use {@link org.apache.juneau.Context.Builder#debug()} setting to enable caching to a string 1088 * before parsing so that this method returns the input. 1089 * 1090 * @return The input as a string, or <jk>null</jk> if no pipe has been created or we're reading from an uncached reader or input stream source. 1091 */ 1092 public String getInputAsString() { 1093 return pipe == null ? null : pipe.getInputAsString(); 1094 } 1095 1096 /** 1097 * Invokes the specified swap on the specified object. 1098 * 1099 * @param swap The swap to invoke. 1100 * @param o The input object. 1101 * @param eType The expected type. 1102 * @return The swapped object. 1103 * @throws ParseException If swap method threw an exception. 1104 */ 1105 @SuppressWarnings({ "rawtypes", "unchecked" }) 1106 protected Object unswap(ObjectSwap swap, Object o, ClassMeta<?> eType) throws ParseException { 1107 try { 1108 return swap.unswap(this, o, eType); 1109 } catch (Exception e) { 1110 throw new ParseException(e); 1111 } 1112 } 1113 1114 /** 1115 * Creates a reusable {@link StringBuilder} object from an internal pool. 1116 * 1117 * <p> 1118 * String builders are returned to the pool by calling {@link #returnStringBuilder(StringBuilder)}. 1119 * 1120 * @return A new or previously returned string builder. 1121 */ 1122 protected final StringBuilder getStringBuilder() { 1123 if (sbStack.isEmpty()) 1124 return new StringBuilder(); 1125 return sbStack.pop(); 1126 } 1127 1128 /** 1129 * Returns a {@link StringBuilder} object back into the internal reuse pool. 1130 * 1131 * @param sb The string builder to return to the pool. No-op if <jk>null</jk>. 1132 */ 1133 protected final void returnStringBuilder(StringBuilder sb) { 1134 if (sb == null) 1135 return; 1136 sb.setLength(0); 1137 sbStack.push(sb); 1138 } 1139 1140 //----------------------------------------------------------------------------------------------------------------- 1141 // Properties 1142 //----------------------------------------------------------------------------------------------------------------- 1143 1144 /** 1145 * Auto-close streams. 1146 * 1147 * @see Parser.Builder#autoCloseStreams() 1148 * @return 1149 * <jk>true</jk> if <l>InputStreams</l> and <l>Readers</l> passed into parsers will be closed 1150 * after parsing is complete. 1151 */ 1152 protected final boolean isAutoCloseStreams() { 1153 return ctx.isAutoCloseStreams(); 1154 } 1155 1156 /** 1157 * Debug output lines. 1158 * 1159 * @see Parser.Builder#debugOutputLines(int) 1160 * @return 1161 * The number of lines of input before and after the error location to be printed as part of the exception message. 1162 */ 1163 protected final int getDebugOutputLines() { 1164 return ctx.getDebugOutputLines(); 1165 } 1166 1167 /** 1168 * Returns the listener associated with this session. 1169 * 1170 * @return The listener associated with this session, or <jk>null</jk> if there is no listener. 1171 */ 1172 public ParserListener getListener() { 1173 return listener; 1174 } 1175 1176 /** 1177 * Strict mode. 1178 * 1179 * @see Parser.Builder#strict() 1180 * @return 1181 * <jk>true</jk> if strict mode for the parser is enabled. 1182 */ 1183 protected final boolean isStrict() { 1184 return ctx.isStrict(); 1185 } 1186 1187 /** 1188 * Trim parsed strings. 1189 * 1190 * @see Parser.Builder#trimStrings() 1191 * @return 1192 * <jk>true</jk> if string values will be trimmed of whitespace using {@link String#trim()} before being added to 1193 * the POJO. 1194 */ 1195 protected final boolean isTrimStrings() { 1196 return ctx.isTrimStrings(); 1197 } 1198 1199 /** 1200 * Unbuffered. 1201 * 1202 * @see Parser.Builder#unbuffered() 1203 * @return 1204 * <jk>true</jk> if parsers don't use internal buffering during parsing. 1205 */ 1206 protected final boolean isUnbuffered() { 1207 return ctx.isUnbuffered(); 1208 } 1209 1210 /** 1211 * HTTP part schema of object being parsed. 1212 * 1213 * @return HTTP part schema of object being parsed, or <jk>null</jk> if not specified. 1214 */ 1215 public final HttpPartSchema getSchema() { 1216 return schema; 1217 } 1218 1219 //----------------------------------------------------------------------------------------------------------------- 1220 // Other methods 1221 //----------------------------------------------------------------------------------------------------------------- 1222 1223 /** 1224 * Parser listener. 1225 * 1226 * @see Parser.Builder#listener(Class) 1227 * @return 1228 * Class used to listen for errors and warnings that occur during parsing. 1229 */ 1230 protected final Class<? extends ParserListener> getListenerClass() { 1231 return ctx.getListener(); 1232 } 1233 1234 @Override /* ContextSession */ 1235 protected JsonMap properties() { 1236 return filteredMap("javaMethod", javaMethod, "listener", listener, "outer", outer); 1237 } 1238}