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