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.marshall.parser; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020import static org.apache.juneau.commons.utils.StringUtils.*; 021import static org.apache.juneau.commons.utils.ThrowableUtils.*; 022 023import java.io.*; 024import java.lang.reflect.*; 025import java.nio.charset.*; 026import java.time.*; 027import java.time.Duration; 028import java.time.temporal.*; 029import java.util.*; 030import java.util.function.*; 031 032import javax.xml.datatype.*; 033 034import org.apache.juneau.commons.bean.*; 035import org.apache.juneau.commons.collections.*; 036import org.apache.juneau.commons.function.*; 037import org.apache.juneau.commons.inject.*; 038import org.apache.juneau.commons.reflect.*; 039import org.apache.juneau.marshall.*; 040import org.apache.juneau.marshall.collections.*; 041import org.apache.juneau.marshall.httppart.*; 042import org.apache.juneau.marshall.objecttools.*; 043import org.apache.juneau.marshall.swap.spi.*; 044import org.apache.juneau.marshall.utils.*; 045 046/** 047 * Session object that lives for the duration of a single use of {@link Parser}. 048 * 049 * <h5 class='section'>Notes:</h5><ul> 050 * <li class='warn'>This class is not thread safe and is typically discarded after one use. 051 * </ul> 052 * 053 * <h5 class='section'>See Also:</h5><ul> 054 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a> 055 056 * </ul> 057 */ 058@SuppressWarnings({ 059 "java:S112", // throws Exception intentional - callback/lifecycle method 060 "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants 061 "java:S1452", // create(), getClassMeta() and getListenerClass() expose wildcard Builder<?>/ClassMeta<?>/Class<? extends ParserListener> because the concrete type is not known statically 062 "rawtypes", // Raw types necessary for generic type handling throughout parser session 063 "resource", // pipe field holds the per-parse ParserPipe whose lifecycle is managed by the session, not owned as a standalone field. 064 "unchecked" // Type erasure requires unchecked casts throughout parser session 065}) 066public class ParserSession extends MarshallingSession { 067 068 /** 069 * Default value for {@link Builder#maxParseDepth(int)}. 070 */ 071 protected static final int DEFAULT_MAX_PARSE_DEPTH = 1000; 072 073 /** 074 * Builder class. 075 */ 076 @SuppressWarnings({ 077 "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name. 078 }) 079 public abstract static class Builder<SELF extends Builder<SELF>> extends MarshallingSession.Builder<SELF> { 080 081 private HttpPartSchema schema; 082 private Method javaMethod; 083 private Object outer; 084 private boolean trimStrings; 085 private Nulls nulls; 086 private int maxParseDepth = DEFAULT_MAX_PARSE_DEPTH; 087 private final Parser ctx; 088 089 /** 090 * Constructor 091 * 092 * @param ctx The context creating this session. 093 * <br>Cannot be <jk>null</jk>. 094 */ 095 protected Builder(Parser ctx) { 096 super(reqnn("ctx", ctx).getMarshallingContext()); 097 this.ctx = ctx; 098 mediaTypeDefault(ctx.getPrimaryMediaType()); 099 trimStrings = ctx.isTrimStrings(); 100 nulls = ctx.getNulls(); 101 } 102 103 @Override 104 public ParserSession build() { 105 return new ParserSession(this); 106 } 107 108 /** 109 * The java method that called this serializer, usually the method in a REST servlet. 110 * 111 * @param value 112 * The new property value. 113 * <br>Can be <jk>null</jk> to leave (or clear) the associated java method unset. 114 * @return This object. 115 */ 116 public SELF javaMethod(Method value) { 117 javaMethod = value; 118 return self(); 119 } 120 121 122 /** 123 * The outer object for instantiating top-level non-static inner classes. 124 * 125 * @param value 126 * The new property value. 127 * <br>Can be <jk>null</jk> (no outer object will be used, suitable for static or top-level classes). 128 * @return This object. 129 */ 130 public SELF outer(Object value) { 131 outer = value; 132 return self(); 133 } 134 135 /** 136 * The shared recursive-parse-depth budget honored by binary codec sessions (CBOR, Protobuf, MsgPack) 137 * at their recursive databind entry points. 138 * 139 * @param value 140 * The new property value. 141 * <br>Defaults to {@value ParserSession#DEFAULT_MAX_PARSE_DEPTH}. 142 * @return This object. 143 * @see ParserSession#enterParseDepth() 144 */ 145 public SELF maxParseDepth(int value) { 146 maxParseDepth = value; 147 return self(); 148 } 149 150 @Override /* Overridden from Builder */ 151 public SELF property(String key, Object value) { 152 if (key == null) { 153 super.property(key, value); 154 return self(); 155 } 156 switch (key) { 157 case "javaMethod", "ParserSession.javaMethod": 158 return javaMethod(cvt(value, Method.class)); 159 case "outer", "ParserSession.outer": 160 return outer(value); 161 case "schema", "ParserSession.schema": 162 return schema(cvt(value, HttpPartSchema.class)); 163 case "trimStrings", "ParserSession.trimStrings": 164 return trimStrings(cvt(value, Boolean.class)); 165 case "nulls", "ParserSession.nulls": 166 return nulls(cvt(value, Nulls.class)); 167 case "maxParseDepth", "ParserSession.maxParseDepth": 168 return maxParseDepth(cvt(value, Integer.class)); 169 default: 170 super.property(key, value); 171 return self(); 172 } 173 } 174 175 /** 176 * Trim parsed strings. 177 * 178 * @param value The new value for this setting. 179 * @return This object. 180 */ 181 public SELF trimStrings(boolean value) { 182 trimStrings = value; 183 return self(); 184 } 185 186 /** 187 * Per-session override for the parser's null-coercion policy. 188 * 189 * @param value The new value for this setting. {@code null} is treated as {@link Nulls#NOT_SET}. 190 * @return This object. 191 * @see Parser.Builder#nulls(Nulls) 192 * @since 10.0.0 193 */ 194 public SELF nulls(Nulls value) { 195 nulls = value == null ? Nulls.NOT_SET : value; 196 return self(); 197 } 198 199 /** 200 * HTTP-part schema. 201 * 202 * <p> 203 * Used for schema-based serializers and parsers to define additional formatting. 204 * 205 * @param value 206 * The new value for this property. 207 * <br>Can be <jk>null</jk> (will not set the value, keeps existing schema or remains <jk>null</jk>). 208 * @return This object. 209 */ 210 public SELF schema(HttpPartSchema value) { 211 if (nn(value)) 212 schema = value; 213 return self(); 214 } 215 216 /** 217 * Same as {@link #schema(HttpPartSchema)} but doesn't overwrite the value if it is already set. 218 * 219 * @param value 220 * The new value for this property. 221 * <br>If <jk>null</jk>, the value will not be set (keeps existing schema or remains <jk>null</jk>). 222 * @return This object. 223 */ 224 public SELF schemaDefault(HttpPartSchema value) { 225 if (nn(value) && schema == null) 226 schema = value; 227 return self(); 228 } 229 230 } 231 232 /** 233 * Concrete default builder leaf for the non-subclassed {@code create()} path (CRTP terminal). 234 */ 235 public static final class DefaultBuilder extends Builder<DefaultBuilder> { 236 237 DefaultBuilder(Parser ctx) { 238 super(ctx); 239 } 240 } 241 242 /** 243 * Creates a new builder for this object. 244 * 245 * @param ctx The context creating this session. 246 * <br>Cannot be <jk>null</jk>. 247 * @return A new builder. 248 */ 249 public static Builder<?> create(Parser ctx) { 250 return new DefaultBuilder(reqnn("ctx", ctx)); 251 } 252 253 /** 254 * Convenience method for calling the {@link NameProperty @NameProperty} method on the specified object if it exists. 255 * 256 * @param cm The class type of the object. 257 * <br>Can be <jk>null</jk> (no-op). 258 * @param o The object. 259 * @param name The name to set. 260 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 261 */ 262 protected static final void setName(ClassMeta<?> cm, Object o, Object name) throws ExecutableException { 263 if (nn(cm)) { 264 Property m = cm.getNameProperty(); 265 if (nn(m) && m.canWrite()) 266 m.set(o, name); 267 } 268 } 269 270 /** 271 * Convenience method for calling the {@link ParentProperty @ParentProperty} method on the specified object if it 272 * exists. 273 * 274 * @param cm The class type of the object. 275 * <br>Can be <jk>null</jk> (no-op). 276 * @param o The object. 277 * @param parent The parent to set. 278 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 279 */ 280 protected static final void setParent(ClassMeta<?> cm, Object o, Object parent) throws ExecutableException { 281 if (cm == null) 282 return; 283 Property m = cm.getParentProperty(); 284 if (nn(m) && m.canWrite()) 285 m.set(o, parent); 286 } 287 288 /** 289 * Returns the nearest enclosing bean used as the parent for {@link ParentProperty @ParentProperty} injection. 290 * 291 * <p> 292 * Unlike {@link #getOuter()} (which also serves as the instantiation context for non-static member/inner classes), 293 * this value tracks only the nearest enclosing <i>bean</i> as the object graph is parsed, deliberately skipping any 294 * intermediate collections/maps/arrays. It is {@code null} at the document root (no enclosing bean), which means a 295 * bean parsed at the root or directly inside a top-level collection has no parent injected. 296 * 297 * @return The nearest enclosing bean, or {@code null} if there is none. 298 */ 299 protected final Object parentBean() { 300 return parentBean; 301 } 302 303 /** 304 * Sets the nearest-enclosing-bean tracker used for {@link ParentProperty @ParentProperty} injection. 305 * 306 * <p> 307 * Callers should invoke this when they begin populating a bean's properties (passing that bean), then pass the 308 * returned previous value back to this method once done, restoring the tracker stack-style. 309 * 310 * @param value The new nearest enclosing bean (typically the bean currently being populated). 311 * @return The previous value, to be passed back to this method to restore the tracker. 312 */ 313 protected final Object swapParentBean(Object value) { 314 var old = parentBean; 315 parentBean = value; 316 return old; 317 } 318 319 private final HttpPartSchema schema; 320 private final Method javaMethod; 321 private final Object outer; 322 private final boolean trimStrings; 323 private final Nulls nulls; 324 private final int maxParseDepth; 325 private final Parser ctx; 326 private final ParserListener listener; 327 private final Deque<StringBuilder> sbStack; 328 private BeanPropertyMeta currentProperty; 329 private ClassMeta<?> currentClass; 330 private Object parentBean; 331 private final Position mark = new Position(-1); 332 private ParserPipe pipe; 333 private int parseDepth; 334 335 /** 336 * Constructor. 337 * 338 * @param builder The builder for this object. 339 * <br>Cannot be <jk>null</jk>. 340 */ 341 protected ParserSession(Builder<?> builder) { 342 super(builder); 343 ctx = builder.ctx; 344 javaMethod = builder.javaMethod; 345 outer = builder.outer; 346 parentBean = builder.outer; 347 schema = builder.schema; 348 trimStrings = builder.trimStrings; 349 nulls = builder.nulls == null ? Nulls.NOT_SET : builder.nulls; 350 maxParseDepth = builder.maxParseDepth; 351 listener = BeanInstantiator.createOrNull(ctx.getListener()); 352 sbStack = new ArrayDeque<>(); 353 } 354 355 /** 356 * Returns the input as a string. 357 * 358 * <p> 359 * This always returns a value for input of type {@link CharSequence}. 360 * <br>For other input types, use {@link org.apache.juneau.marshall.Context.Builder#debug() Context.Builder#debug()} setting to enable caching to a string 361 * before parsing so that this method returns the input. 362 * 363 * @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. 364 */ 365 public String getInputAsString() { return pipe == null ? null : pipe.getInputAsString(); } 366 367 /** 368 * Returns information used to determine at what location in the parse a failure occurred. 369 * 370 * @return A map, typically containing something like <c>{line:123,column:456,currentProperty:"foobar"}</c> 371 */ 372 public final JsonMap getLastLocation() { 373 var m = new JsonMap(); 374 if (nn(currentClass)) 375 m.put("currentClass", currentClass.toString(true)); 376 if (nn(currentProperty)) 377 m.put("currentProperty", currentProperty); 378 return m; 379 } 380 381 /** 382 * Returns the listener associated with this session. 383 * 384 * @return The listener associated with this session, or <jk>null</jk> if there is no listener. 385 */ 386 public ParserListener getListener() { return listener; } 387 388 /** 389 * Returns the listener associated with this session. 390 * 391 * @param <T> The listener type. 392 * @param c The listener class to cast to. 393 * @return The listener associated with this session, or <jk>null</jk> if there is no listener. 394 */ 395 public <T extends ParserListener> T getListener(Class<T> c) { 396 return (T)listener; 397 } 398 399 /** 400 * Returns the current position into the reader or input stream. 401 * 402 * @return 403 * The current position into the reader or input stream. 404 * <br>Never <jk>null</jk>. 405 */ 406 public Position getPosition() { 407 if (mark.line != -1 || mark.column != -1 || mark.position != -1) 408 return mark; 409 if (pipe == null) 410 return Position.UNKNOWN; 411 return pipe.getPosition(); 412 } 413 414 /** 415 * HTTP part schema of object being parsed. 416 * 417 * @return HTTP part schema of object being parsed, or <jk>null</jk> if not specified. 418 */ 419 public final HttpPartSchema getSchema() { return schema; } 420 421 /** 422 * Returns <jk>true</jk> if this parser subclasses from {@link ReaderParser}. 423 * 424 * @return <jk>true</jk> if this parser subclasses from {@link ReaderParser}. 425 */ 426 public boolean isReaderParser() { return false; } 427 428 /** 429 * Same as {@link #read(Object, Type, Type...)} except optimized for a non-parameterized class. 430 * 431 * <p> 432 * This is the preferred parse method for simple types since you don't need to cast the results. 433 * 434 * <h5 class='section'>Examples:</h5> 435 * <p class='bjava'> 436 * ReaderParser <jv>parser</jv> = JsonParser.<jsf>DEFAULT</jsf>; 437 * 438 * <jc>// Parse into a string.</jc> 439 * String <jv>string</jv> = <jv>parser</jv>.read(<jv>json</jv>, String.<jk>class</jk>); 440 * 441 * <jc>// Parse into a bean.</jc> 442 * MyBean <jv>bean</jv> = <jv>parser</jv>.read(<jv>json</jv>, MyBean.<jk>class</jk>); 443 * 444 * <jc>// Parse into a bean array.</jc> 445 * MyBean[] <jv>beanArray</jv> = <jv>parser</jv>.read(<jv>json</jv>, MyBean[].<jk>class</jk>); 446 * 447 * <jc>// Parse into a linked-list of objects.</jc> 448 * List <jv>list</jv> = <jv>parser</jv>.read(<jv>json</jv>, LinkedList.<jk>class</jk>); 449 * 450 * <jc>// Parse into a map of object keys/values.</jc> 451 * Map <jv>map</jv> = <jv>parser</jv>.read(<jv>json</jv>, TreeMap.<jk>class</jk>); 452 * </p> 453 * 454 * @param <T> The class type of the object being created. 455 * @param input 456 * The input. 457 * See {@link #read(Object, Type, Type...)} for details. 458 * @param type The object type to create. 459 * @return The parsed object. 460 * @throws ParseException Malformed input encountered. 461 * @throws IOException Thrown by the underlying stream. 462 */ 463 public final <T> T read(Object input, Class<T> type) throws ParseException, IOException { 464 try (var p = createPipe(input)) { 465 return readInner(p, getClassMeta(type)); 466 } 467 } 468 469 /** 470 * Same as {@link #read(Object, Type, Type...)} except the type has already been converted into a {@link ClassMeta} 471 * object. 472 * 473 * <p> 474 * This is mostly an internal method used by the framework. 475 * 476 * @param <T> The class type of the object being created. 477 * @param input 478 * The input. 479 * See {@link #read(Object, Type, Type...)} for details. 480 * @param type The object type to create. 481 * @return The parsed object. 482 * @throws ParseException Malformed input encountered. 483 * @throws IOException Thrown by the underlying stream. 484 */ 485 public final <T> T read(Object input, ClassMeta<T> type) throws ParseException, IOException { 486 try (var p = createPipe(input)) { 487 return readInner(p, type); 488 } 489 } 490 491 /** 492 * Parses input into the specified object type. 493 * 494 * <p> 495 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 496 * 497 * <h5 class='section'>Examples:</h5> 498 * <p class='bjava'> 499 * ReaderParser <jv>parser</jv> = JsonParser.<jsf>DEFAULT</jsf>; 500 * 501 * <jc>// Parse into a linked-list of strings.</jc> 502 * List <jv>list1</jv> = <jv>parser</jv>.read(<jv>json</jv>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 503 * 504 * <jc>// Parse into a linked-list of beans.</jc> 505 * List <jv>list2</jv> = <jv>parser</jv>.read(<jv>json</jv>, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 506 * 507 * <jc>// Parse into a linked-list of linked-lists of strings.</jc> 508 * List <jv>list3</jv> = <jv>parser</jv>.read(<jv>json</jv>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 509 * 510 * <jc>// Parse into a map of string keys/values.</jc> 511 * Map <jv>map1</jv> = <jv>parser</jv>.read(<jv>json</jv>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 512 * 513 * <jc>// Parse into a map containing string keys and values of lists containing beans.</jc> 514 * Map <jv>map2</jv> = <jv>parser</jv>.read(<jv>json</jv>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 515 * </p> 516 * 517 * <p> 518 * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type. 519 * 520 * <p> 521 * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 522 * 523 * <p> 524 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 525 * 526 * <h5 class='section'>Notes:</h5><ul> 527 * <li class='note'> 528 * Use the {@link #read(Object, Class)} method instead if you don't need a parameterized map/collection. 529 * </ul> 530 * 531 * @param <T> The class type of the object to create. 532 * @param input 533 * The input. 534 * <br>Character-based parsers can handle the following input class types: 535 * <ul> 536 * <li><jk>null</jk> 537 * <li>{@link Reader} 538 * <li>{@link CharSequence} 539 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 540 * {@link ReaderParser.Builder#streamCharset(Charset)} property value). 541 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 542 * {@link ReaderParser.Builder#streamCharset(Charset)} property value). 543 * </ul> 544 * <br>Stream-based parsers can handle the following input class types: 545 * <ul> 546 * <li><jk>null</jk> 547 * <li>{@link InputStream} 548 * <li><code><jk>byte</jk>[]</code> 549 * </ul> 550 * @param type 551 * The object type to create. 552 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 553 * @param args 554 * The type arguments of the class if it's a collection or map. 555 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 556 * <br>Ignored if the main type is not a map or collection. 557 * @return The parsed object. 558 * @throws ParseException Malformed input encountered. 559 * @see MarshallingSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. 560 * @throws IOException Thrown by the underlying stream. 561 */ 562 public final <T> T read(Object input, Type type, Type...args) throws ParseException, IOException { 563 try (var p = createPipe(input)) { 564 return (T)readInner(p, getClassMeta(type, args)); 565 } 566 } 567 568 /** 569 * Same as {@link #read(Object, Class)} but parses from a string and doesn't throw an {@link IOException}. 570 * 571 * <p> 572 * This is the preferred parse method for simple types since you don't need to cast the results. 573 * 574 * <h5 class='section'>Examples:</h5> 575 * <p class='bjava'> 576 * ReaderParser <jv>parser</jv> = JsonParser.<jsf>DEFAULT</jsf>; 577 * 578 * <jc>// Parse into a string.</jc> 579 * String <jv>string</jv> = <jv>parser</jv>.read(<jv>json</jv>, String.<jk>class</jk>); 580 * 581 * <jc>// Parse into a bean.</jc> 582 * MyBean <jv>bean</jv> = <jv>parser</jv>.read(<jv>json</jv>, MyBean.<jk>class</jk>); 583 * 584 * <jc>// Parse into a bean array.</jc> 585 * MyBean[] <jv>beanArray</jv> = <jv>parser</jv>.read(<jv>json</jv>, MyBean[].<jk>class</jk>); 586 * 587 * <jc>// Parse into a linked-list of objects.</jc> 588 * List <jv>list</jv> = <jv>parser</jv>.read(<jv>json</jv>, LinkedList.<jk>class</jk>); 589 * 590 * <jc>// Parse into a map of object keys/values.</jc> 591 * Map <jv>map</jv> = <jv>parser</jv>.read(<jv>json</jv>, TreeMap.<jk>class</jk>); 592 * </p> 593 * 594 * @param <T> The class type of the object being created. 595 * @param input 596 * The input. 597 * See {@link #read(Object, Type, Type...)} for details. 598 * @param type The object type to create. 599 * @return The parsed object. 600 * @throws ParseException Malformed input encountered. 601 */ 602 public final <T> T read(String input, Class<T> type) throws ParseException { 603 try (var p = createPipe(input)) { 604 return readInner(p, getClassMeta(type)); 605 } catch (IOException e) { 606 throw new ParseException(e); // Shouldn't happen. 607 } 608 } 609 610 /** 611 * Same as {@link #read(Object, ClassMeta)} except parses from a string and doesn't throw an {@link IOException}. 612 * 613 * <p> 614 * This is mostly an internal method used by the framework. 615 * 616 * @param <T> The class type of the object being created. 617 * @param input 618 * The input. 619 * See {@link #read(Object, Type, Type...)} for details. 620 * @param type The object type to create. 621 * @return The parsed object. 622 * @throws ParseException Malformed input encountered. 623 */ 624 public final <T> T read(String input, ClassMeta<T> type) throws ParseException { 625 try (var p = createPipe(input)) { 626 return readInner(p, type); 627 } catch (IOException e) { 628 throw new ParseException(e); // Shouldn't happen. 629 } 630 } 631 632 /** 633 * Same as {@link #read(Object,Type,Type...)} but parses from a string and doesn't throw an {@link IOException}. 634 * 635 * @param <T> The class type of the object to create. 636 * @param input 637 * The input. 638 * <br>Character-based parsers can handle the following input class types: 639 * <ul> 640 * <li><jk>null</jk> 641 * <li>{@link Reader} 642 * <li>{@link CharSequence} 643 * <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by 644 * {@link ReaderParser.Builder#streamCharset(Charset)} property value). 645 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by 646 * {@link ReaderParser.Builder#streamCharset(Charset)} property value). 647 * </ul> 648 * <br>Stream-based parsers can handle the following input class types: 649 * <ul> 650 * <li><jk>null</jk> 651 * <li>{@link InputStream} 652 * <li><code><jk>byte</jk>[]</code> 653 * </ul> 654 * @param type 655 * The object type to create. 656 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 657 * @param args 658 * The type arguments of the class if it's a collection or map. 659 * <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} 660 * <br>Ignored if the main type is not a map or collection. 661 * @return The parsed object. 662 * @throws ParseException Malformed input encountered. 663 * @see MarshallingSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. 664 */ 665 public final <T> T read(String input, Type type, Type...args) throws ParseException { 666 try (var p = createPipe(input)) { 667 return (T)readInner(p, getClassMeta(type, args)); 668 } catch (IOException e) { 669 throw new ParseException(e); // Shouldn't happen. 670 } 671 } 672 673 /** 674 * Parses the specified array input with each entry in the object defined by the {@code argTypes} 675 * argument. 676 * 677 * <p> 678 * Used for converting arrays (e.g. <js>"[arg1,arg2,...]"</js>) into an {@code Object[]} that can be passed 679 * to the {@code Method.invoke(target, args)} method. 680 * 681 * <p> 682 * Used in the following locations: 683 * <ul class='spaced-list'> 684 * <li> 685 * Used to parse argument strings in the {@link ObjectIntrospector#invokeMethod(Method, Reader)} method. 686 * </ul> 687 * 688 * @param input The input. Subclasses can support different input types. 689 * @param argTypes Specifies the type of objects to create for each entry in the array. 690 * @return An array of parsed objects. 691 * @throws ParseException Malformed input encountered. 692 */ 693 public final Object[] readArgs(Object input, Type[] argTypes) throws ParseException { 694 try (var p = createPipe(input)) { 695 return doRead(p, getArgsClassMeta(argTypes)); 696 } catch (ParseException e) { 697 throw e; 698 } catch (@SuppressWarnings("unused") StackOverflowError e) { 699 throw new ParseException(this, "Depth too deep. Stack overflow occurred."); 700 } catch (IOException e) { 701 throw new ParseException(this, e, "I/O exception occurred. exception=%s, message=%s.", cns(e), localizedMessage(e)); 702 } catch (Exception e) { 703 throw new ParseException(this, e, "Exception occurred. exception=%s, message=%s.", cns(e), localizedMessage(e)); 704 } finally { 705 checkForWarnings(); 706 } 707 } 708 709 /** 710 * Parses the contents of the specified reader and loads the results into the specified collection. 711 * 712 * <p> 713 * Used in the following locations: 714 * <ul class='spaced-list'> 715 * <li> 716 * The various character-based constructors in {@link JsonList} (e.g. 717 * {@link JsonList#JsonList(CharSequence,Parser)}. 718 * </ul> 719 * 720 * @param <E> The element class type. 721 * @param input The input. See {@link #read(Object, ClassMeta)} for supported input types. 722 * @param c The collection being loaded. 723 * @param elementType The class type of the elements, or <jk>null</jk> to default to whatever is being parsed. 724 * @return The same collection that was passed in to allow this method to be chained. 725 * @throws ParseException Malformed input encountered. 726 * @throws UnsupportedOperationException If not implemented. 727 */ 728 public final <E> Collection<E> readIntoCollection(Object input, Collection<E> c, Type elementType) throws ParseException { 729 try (var p = createPipe(input)) { 730 return doReadIntoCollection(p, c, elementType); 731 } catch (ParseException e) { 732 throw e; 733 } catch (@SuppressWarnings("unused") StackOverflowError e) { 734 throw new ParseException(this, "Depth too deep. Stack overflow occurred."); 735 } catch (IOException e) { 736 throw new ParseException(this, e, "I/O exception occurred. exception=%s, message=%s.", cns(e), localizedMessage(e)); 737 } catch (Exception e) { 738 throw new ParseException(this, e, "Exception occurred. exception=%s, message=%s.", cns(e), localizedMessage(e)); 739 } finally { 740 checkForWarnings(); 741 } 742 } 743 744 /** 745 * Parses the contents of the specified reader and loads the results into the specified map. 746 * 747 * <p> 748 * Reader must contain something that serializes to a map (such as text containing a JSON object). 749 * 750 * <p> 751 * Used in the following locations: 752 * <ul class='spaced-list'> 753 * <li> 754 * The various character-based constructors in {@link JsonMap} (e.g. 755 * {@link JsonMap#JsonMap(CharSequence,Parser)}). 756 * </ul> 757 * 758 * @param <K> The key class type. 759 * @param <V> The value class type. 760 * @param input The input. See {@link #read(Object, ClassMeta)} for supported input types. 761 * @param m The map being loaded. 762 * @param keyType The class type of the keys, or <jk>null</jk> to default to <code>String.<jk>class</jk></code>. 763 * @param valueType The class type of the values, or <jk>null</jk> to default to whatever is being parsed. 764 * @return The same map that was passed in to allow this method to be chained. 765 * @throws ParseException Malformed input encountered. 766 * @throws UnsupportedOperationException If not implemented. 767 */ 768 public final <K,V> Map<K,V> readIntoMap(Object input, Map<K,V> m, Type keyType, Type valueType) throws ParseException { 769 try (var p = createPipe(input)) { 770 return doReadIntoMap(p, m, keyType, valueType); 771 } catch (ParseException e) { 772 throw e; 773 } catch (Exception e) { 774 throw new ParseException(this, e); 775 } finally { 776 checkForWarnings(); 777 } 778 } 779 780 /** 781 * Entry point for all parsing calls. 782 * 783 * <p> 784 * Calls the {@link #doRead(ParserPipe, ClassMeta)} implementation class and catches/re-wraps any exceptions 785 * thrown. 786 * 787 * @param pipe The parser input. 788 * @param type The class type of the object to create. 789 * @param <T> The class type of the object to create. 790 * @return The parsed object. 791 * @throws ParseException Malformed input encountered. 792 * @throws IOException Thrown by the underlying stream. 793 */ 794 private <T> T readInner(ParserPipe pipe, ClassMeta<T> type) throws ParseException, IOException { 795 if (type.isVoid()) 796 return null; 797 if (BeanSupplier.class.isAssignableFrom(type.inner()) && !BeanChannel.class.isAssignableFrom(type.inner())) 798 throw new ParseException(this, "BeanSupplier cannot be used as a parser target. Use BeanConsumer or BeanChannel for round-trip support."); 799 try { 800 var r = doRead(pipe, type); 801 // Primitive optionals (OptionalInt/Long/Double) are parsed as first-class optionals — the format 802 // parsers wrap the element in a generic Optional, so coerce the top-level result to the matching 803 // primitive-optional type (mirrors the Optional<T> contract; absent/null already resolved to empty). 804 if (type.isPrimitiveOptional() && ! type.inner().isInstance(r)) 805 return convertToType(r, type); 806 return r; 807 } catch (ParseException | IOException e) { 808 throw e; 809 } catch (@SuppressWarnings("unused") StackOverflowError e) { 810 throw new ParseException(this, "Depth too deep. Stack overflow occurred."); 811 } catch (Exception e) { 812 throw new ParseException(this, e, "Exception occurred. exception=%s, message=%s.", cns(e), localizedMessage(e)); 813 } finally { 814 checkForWarnings(); 815 } 816 } 817 818 /** 819 * Parses the input and streams each parsed element into the given {@link BeanConsumer}. 820 * 821 * <p> 822 * This is the primary API for large-dataset parsing where parsed beans should be consumed lazily 823 * (e.g. inserted directly into a database) rather than collected into an in-memory collection. 824 * 825 * <p> 826 * The parser drives the full consumer lifecycle: 827 * <ol> 828 * <li>Calls {@link BeanConsumer#begin()} before parsing starts 829 * <li>Calls {@link BeanConsumer#acceptThrows(Object)} for each parsed element 830 * <li>If {@link BeanConsumer#acceptThrows(Object)} throws, calls {@link BeanConsumer#onError(Exception)}; 831 * if {@code onError()} absorbs the exception, parsing continues to the next element 832 * <li>Always calls {@link BeanConsumer#complete()} at the end (like {@code finally}) 833 * </ol> 834 * 835 * <p> 836 * The default implementation parses the input into a {@link List} first and then feeds each element 837 * to the consumer. Format-specific subclasses may override {@link #doReadToBeanConsumer} for true 838 * streaming behavior without loading all elements into memory. 839 * 840 * @param <T> The element type. 841 * @param input The input to parse. 842 * @param consumer The consumer to receive parsed elements. 843 * @param elementType The type of each element. 844 * @throws ParseException If a parse error occurs that causes {@code onError()} to rethrow. 845 * @throws IOException If an I/O error occurs reading the input. 846 */ 847 public final <T> void readToBeanConsumer(Object input, BeanConsumer<T> consumer, Class<T> elementType) throws ParseException, IOException { 848 try (var p = createPipe(input)) { 849 doReadToBeanConsumer(p, consumer, elementType); 850 } 851 } 852 853 /** 854 * Format-specific implementation for {@link #readToBeanConsumer}. 855 * 856 * <p> 857 * The default implementation parses the input as a {@link List} and feeds each element to the consumer. 858 * Override this method in format-specific parsers to support true streaming without collecting all 859 * elements into memory. 860 * 861 * @param <T> The element type. 862 * @param pipe The parser input pipe. 863 * @param consumer The consumer to receive parsed elements. 864 * @param elementType The type of each element. 865 * @throws ParseException If a parse error occurs. 866 * @throws IOException If an I/O error occurs. 867 */ 868 protected <T> void doReadToBeanConsumer(ParserPipe pipe, BeanConsumer<T> consumer, Class<T> elementType) throws ParseException, IOException { 869 List<T> list = (List<T>) doRead(pipe, getClassMeta(List.class, elementType)); 870 if (list == null) 871 return; 872 var bodyThrown = drainToConsumer(consumer, list); 873 var completeThrown = completeQuietly(consumer); 874 if (bodyThrown != null) { 875 if (completeThrown != null) 876 bodyThrown.addSuppressed(completeThrown); 877 if (bodyThrown instanceof ParseException bodyThrown2) 878 throw bodyThrown2; 879 if (bodyThrown instanceof IOException bodyThrown2) 880 throw bodyThrown2; 881 throw new ParseException(this, bodyThrown, "Exception occurred. exception=%s, message=%s.", cns(bodyThrown), localizedMessage(bodyThrown)); 882 } 883 if (completeThrown != null) 884 throw new ParseException(this, completeThrown, "Exception occurred in BeanConsumer.complete(). exception=%s, message=%s.", cns(completeThrown), localizedMessage(completeThrown)); 885 } 886 887 private static <T> Exception drainToConsumer(BeanConsumer<T> consumer, List<T> list) { 888 try { 889 consumer.begin(); 890 for (var element : list) 891 acceptOrError(consumer, element); 892 } catch (Exception e) { 893 return e; 894 } 895 return null; 896 } 897 898 private static <T> void acceptOrError(BeanConsumer<T> consumer, T element) throws Exception { 899 try { 900 consumer.acceptThrows(element); 901 } catch (Exception e) { 902 consumer.onError(e); 903 } 904 } 905 906 private static Exception completeQuietly(BeanConsumer<?> consumer) { 907 try { 908 consumer.complete(); 909 return null; 910 } catch (Exception e) { 911 return e; 912 } 913 } 914 915 /** 916 * Throws {@link MissingRequiredPropertyException} if the bean map is missing {@code @BeanProp(required=true)} properties. 917 * 918 * <p> 919 * Call once per bean, after all input properties have been put into the map and before {@link BeanMap#getBean()} 920 * constructs it. Do not call it for intermediate bean maps that are filled in later. 921 * 922 * @param <T> The bean type. 923 * @param m The bean map. Can be <jk>null</jk>. 924 * @return The same bean map. 925 */ 926 protected final <T> BeanMap<T> checkRequired(BeanMap<T> m) { 927 return MissingRequiredPropertyException.check(this, m); 928 } 929 930 /** 931 * Converts the specified {@link MarshalledMap} into a bean identified by the <js>"_type"</js> property in the map. 932 * 933 * @param m The map to convert to a bean. 934 * <br>Must not be <jk>null</jk>. 935 * @param pMeta The current bean property being parsed. 936 * <br>Can be <jk>null</jk> (the <c>@MarshalledProp(dictionary={})</c> registry lookup is skipped). 937 * @param eType The current expected type being parsed. 938 * <br>Can be <jk>null</jk> (the <c>@Marshalled(dictionary={})</c> registry lookup on the expected type is skipped). 939 * @return 940 * The converted bean, or the same map if the <js>"_type"</js> entry wasn't found or didn't resolve to a bean. 941 */ 942 protected final Object cast(MarshalledMap m, BeanPropertyMeta pMeta, ClassMeta<?> eType) { 943 944 var btpn = getBeanTypePropertyName(eType); 945 946 var o = m.get(btpn); 947 if (o == null) 948 return m; 949 var typeName = o.toString(); 950 951 var cm = getClassMeta(typeName, pMeta, eType); 952 953 if (nn(cm)) { 954 var bm = m.getMarshallingSession().newBeanMap(cm.inner()); 955 956 // Iterate through all the entries in the map and set the individual field values. 957 m.forEach((k, v) -> { 958 if (! k.equals(btpn)) { 959 // Attempt to recursively cast child maps. 960 if (v instanceof MarshalledMap v2) 961 v = cast(v2, pMeta, eType); 962 bm.put(k, v); 963 } 964 }); 965 return checkRequired(bm).getBean(); 966 } 967 968 return m; 969 } 970 971 /** 972 * Converts the specified string to the specified type. 973 * 974 * @param outer 975 * The outer object if we're converting to an inner object that needs to be created within the context 976 * of an outer object. 977 * <br>Can be <jk>null</jk> if the target is not a non-static inner class requiring an enclosing instance. 978 * @param s The string to convert. 979 * <br>Can be <jk>null</jk> (returns <jk>null</jk>). 980 * @param type The class type to convert the string to. 981 * <br>Can be <jk>null</jk> (defaults to <c>Object</c>). 982 * @return The string converted as an object of the specified type, or <jk>null</jk> if the input string was <jk>null</jk>. 983 * @param <T> The class type to convert the string to. 984 * @throws ParseException Malformed input encountered. 985 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 986 */ 987 @SuppressWarnings({ 988 "java:S3776" // Cognitive complexity acceptable for type-conversion dispatch across all supported target types 989 }) 990 protected final <T> T convertAttrToType(Object outer, String s, ClassMeta<T> type) throws ParseException { 991 if (s == null) 992 return null; 993 994 if (type == null) 995 type = (ClassMeta<T>)object(); 996 var swap = type.getSwap(this); 997 var sType = swap == null ? type : swap.getSwapClassMeta(this); 998 999 var o = (Object)s; 1000 if (sType.isChar()) 1001 o = parseCharacter(s); 1002 else if (sType.isNumber()) 1003 o = parseNumber(s, (Class<? extends Number>)sType.inner()); 1004 else if (sType.isBoolean()) 1005 o = Boolean.parseBoolean(s); 1006 else if (sType.isDate()) 1007 o = readDate(s, sType); 1008 else if (sType.isCalendar()) 1009 o = readCalendar(s, sType); 1010 else if (sType.isTemporal()) 1011 o = readTemporal(s, sType); 1012 else if (sType.isDuration()) 1013 o = readDuration(s); 1014 else if (sType.isPeriod()) 1015 o = readPeriod(s); 1016 else if (! (sType.isCharSequence() || sType.isObject())) { 1017 if (sType.canCreateNewInstanceFromString(outer)) 1018 o = sType.newInstanceFromString(outer, s); 1019 else 1020 throw new ParseException(this, "Invalid conversion from string to class '%s'", type); 1021 } 1022 1023 if (nn(swap)) 1024 o = unswap(swap, o, type); 1025 1026 return (T)o; 1027 } 1028 1029 /** 1030 * Wraps the specified input object into a {@link ParserPipe} object so that it can be easily converted into 1031 * a stream or reader. 1032 * 1033 * @param input 1034 * The input. 1035 * <br>For character-based parsers, this can be any of the following types: 1036 * <ul> 1037 * <li><jk>null</jk> 1038 * <li>{@link Reader} 1039 * <li>{@link CharSequence} 1040 * <li>{@link InputStream} containing UTF-8 encoded text (or whatever the encoding specified by 1041 * {@link ReaderParser.Builder#streamCharset(Charset)}). 1042 * <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or whatever the encoding specified by 1043 * {@link ReaderParser.Builder#streamCharset(Charset)}). 1044 * </ul> 1045 * <br>For byte-based parsers, this can be any of the following types: 1046 * <ul> 1047 * <li><jk>null</jk> 1048 * <li>{@link InputStream} 1049 * <li><code><jk>byte</jk>[]</code> 1050 * <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser.Builder#binaryFormat(BinaryFormat)} setting. 1051 * </ul> 1052 * @return 1053 * A new {@link ParserPipe} wrapper around the specified input object. 1054 */ 1055 protected ParserPipe createPipe(Object input) { 1056 return null; 1057 } 1058 1059 /** 1060 * Workhorse method. 1061 * 1062 * <p> 1063 * Subclasses are expected to implement this method or {@link Parser#doRead(ParserSession,ParserPipe,ClassMeta)}. 1064 * 1065 * <p> 1066 * The default implementation of this method simply calls {@link Parser#doRead(ParserSession,ParserPipe,ClassMeta)}. 1067 * 1068 * @param pipe Where to get the input from. 1069 * @param type 1070 * The class type of the object to create. 1071 * If <jk>null</jk> or <code>Object.<jk>class</jk></code>, object type is based on what's being parsed. 1072 * For example, when parsing JSON text, it may return a <c>String</c>, <c>Number</c>, 1073 * <c>JsonMap</c>, etc... 1074 * @param <T> The class type of the object to create. 1075 * @return The parsed object. 1076 * @throws IOException Thrown by underlying stream. 1077 * @throws ParseException Malformed input encountered. 1078 * @throws ExecutableException Exception occurred on invoked constructor/method/field. 1079 */ 1080 protected <T> T doRead(ParserPipe pipe, ClassMeta<T> type) throws IOException, ParseException, ExecutableException { 1081 return ctx.doRead(this, pipe, type); 1082 } 1083 1084 /** 1085 * Implementation method. 1086 * 1087 * <p> 1088 * Default implementation throws an {@link UnsupportedOperationException}. 1089 * 1090 * @param <E> The element type. 1091 * @param pipe The parser input. 1092 * @param c The collection being loaded. 1093 * @param elementType The class type of the elements, or <jk>null</jk> to default to whatever is being parsed. 1094 * @return The same collection that was passed in to allow this method to be chained. 1095 * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed. 1096 */ 1097 protected <E> Collection<E> doReadIntoCollection(ParserPipe pipe, Collection<E> c, Type elementType) throws Exception { 1098 throw uoex("Parser '%s' does not support this method.", cn(getClass())); 1099 } 1100 1101 /** 1102 * Implementation method. 1103 * 1104 * <p> 1105 * Default implementation throws an {@link UnsupportedOperationException}. 1106 * 1107 * @param <K> The key type. 1108 * @param <V> The value type. 1109 * @param pipe The parser input. 1110 * @param m The map being loaded. 1111 * @param keyType The class type of the keys, or <jk>null</jk> to default to <code>String.<jk>class</jk></code>. 1112 * @param valueType The class type of the values, or <jk>null</jk> to default to whatever is being parsed. 1113 * @return The same map that was passed in to allow this method to be chained. 1114 * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed. 1115 */ 1116 protected <K,V> Map<K,V> doReadIntoMap(ParserPipe pipe, Map<K,V> m, Type keyType, Type valueType) throws Exception { 1117 throw uoex("Parser '%s' does not support this method.", cn(getClass())); 1118 } 1119 1120 /** 1121 * Give the specified dictionary name, resolve it to a class. 1122 * 1123 * @param typeName The dictionary name to resolve. 1124 * @param pMeta The bean property we're currently parsing. 1125 * <br>Can be <jk>null</jk> (the <c>@MarshalledProp(dictionary={})</c> registry lookup is skipped). 1126 * @param eType The expected type we're currently parsing. 1127 * <br>Can be <jk>null</jk> (the <c>@Marshalled(dictionary={})</c> registry lookup on the expected type is skipped). 1128 * @return The resolved class, or <jk>null</jk> if the type name could not be resolved. 1129 */ 1130 protected final ClassMeta<?> getClassMeta(String typeName, BeanPropertyMeta pMeta, ClassMeta<?> eType) { 1131 BeanRegistry br = null; 1132 1133 // Resolve via @MarshalledProp(dictionary={}) 1134 if (nn(pMeta)) { 1135 br = (BeanRegistry) pMeta.getBeanRegistry(); 1136 if (nn(br) && br.hasName(typeName)) 1137 return br.getClassMeta(typeName); 1138 } 1139 1140 // Resolve via @Marshalled(dictionary={}) on the expected type where the 1141 // expected type is an interface with subclasses. 1142 if (nn(eType)) { 1143 br = eType.getBeanRegistry(); 1144 if (nn(br) && br.hasName(typeName)) 1145 return br.getClassMeta(typeName); 1146 } 1147 1148 // Last resort, resolve using the session registry. 1149 return getBeanRegistry().getClassMeta(typeName); 1150 } 1151 1152 /** 1153 * Debug output lines. 1154 * 1155 * @see Parser.Builder#debugOutputLines(int) 1156 * @return 1157 * The number of lines of input before and after the error location to be printed as part of the exception message. 1158 */ 1159 protected final int getDebugOutputLines() { return ctx.getDebugOutputLines(); } 1160 1161 /** 1162 * Returns the Java method that invoked this parser. 1163 * 1164 * <p> 1165 * When using the REST API, this is the Java method invoked by the REST call. 1166 * Can be used to access annotations defined on the method or class. 1167 * 1168 * @return The Java method that invoked this parser, or <jk>null</jk> if not set. 1169 */ 1170 protected final Method getJavaMethod() { return javaMethod; } 1171 1172 /** 1173 * Parser listener. 1174 * 1175 * @see Parser.Builder#listener(Class) 1176 * @return 1177 * Class used to listen for errors and warnings that occur during parsing, or <jk>null</jk> if none was configured. 1178 */ 1179 protected final Class<? extends ParserListener> getListenerClass() { return ctx.getListener(); } 1180 1181 /** 1182 * Returns the outer object used for instantiating top-level non-static member classes. 1183 * 1184 * <p> 1185 * When using the REST API, this is the servlet object. 1186 * 1187 * @return The outer object, or <jk>null</jk> if none was specified. 1188 */ 1189 protected final Object getOuter() { return outer; } 1190 1191 /** 1192 * Creates a reusable {@link StringBuilder} object from an internal pool. 1193 * 1194 * <p> 1195 * String builders are returned to the pool by calling {@link #returnStringBuilder(StringBuilder)}. 1196 * 1197 * @return A new or previously returned string builder. 1198 */ 1199 protected final StringBuilder getStringBuilder() { 1200 if (sbStack.isEmpty()) 1201 return new StringBuilder(); 1202 return sbStack.pop(); 1203 } 1204 1205 /** 1206 * Auto-close streams. 1207 * 1208 * @see Parser.Builder#autoCloseStreams() 1209 * @return 1210 * <jk>true</jk> if <l>InputStreams</l> and <l>Readers</l> passed into parsers will be closed 1211 * after parsing is complete. 1212 */ 1213 protected final boolean isAutoCloseStreams() { return ctx.isAutoCloseStreams(); } 1214 1215 /** 1216 * Trim parsed strings. 1217 * 1218 * @see Parser.Builder#trimStrings() 1219 * @return 1220 * <jk>true</jk> if string values will be trimmed of whitespace using {@link String#trim()} before being added to 1221 * the POJO. 1222 */ 1223 protected final boolean isTrimStrings() { return trimStrings; } 1224 1225 /** 1226 * Returns the effective {@link Nulls} default for this session — the per-session override (set via 1227 * {@link ParserSession.Builder#nulls(Nulls)}) when present, otherwise the parser context's 1228 * {@link Parser#getNulls()} value. 1229 * 1230 * @return The current session-level value (never <jk>null</jk>; {@link Nulls#NOT_SET} when unconfigured). 1231 * @since 10.0.0 1232 */ 1233 public final Nulls getNulls() { return nulls; } 1234 1235 /** 1236 * Unbuffered. 1237 * 1238 * @see Parser.Builder#unbuffered() 1239 * @return 1240 * <jk>true</jk> if parsers don't use internal buffering during parsing. 1241 */ 1242 protected final boolean isUnbuffered() { return ctx.isUnbuffered(); } 1243 1244 /** 1245 * Duration parse hint format. 1246 * 1247 * @return The configured duration parse hint format. 1248 */ 1249 protected final DurationFormat getDurationFormat() { return ctx.getMarshallingContext().getDurationFormat(); } 1250 1251 /** 1252 * Period parse hint format. 1253 * 1254 * @return The configured period parse hint format. 1255 */ 1256 protected final PeriodFormat getPeriodFormat() { return ctx.getMarshallingContext().getPeriodFormat(); } 1257 1258 /** 1259 * Calendar parse hint format. 1260 * 1261 * @return The configured calendar parse hint format. 1262 */ 1263 protected final CalendarFormat getCalendarFormat() { return ctx.getMarshallingContext().getCalendarFormat(); } 1264 1265 /** 1266 * Date parse hint format. 1267 * 1268 * @return The configured date parse hint format. 1269 */ 1270 protected final DateFormat getDateFormat() { return ctx.getMarshallingContext().getDateFormat(); } 1271 1272 /** 1273 * Temporal parse hint format. 1274 * 1275 * @return The configured temporal parse hint format. 1276 */ 1277 protected final TemporalFormat getTemporalFormat() { return ctx.getMarshallingContext().getTemporalFormat(); } 1278 1279 /** 1280 * Time-zone parse hint format. 1281 * 1282 * @return The configured time-zone parse hint format. 1283 */ 1284 protected final TimeZoneFormat getTimeZoneFormat() { return ctx.getMarshallingContext().getTimeZoneFormat(); } 1285 1286 /** 1287 * Locale parse hint format. 1288 * 1289 * @return The configured locale parse hint format. 1290 */ 1291 protected final LocaleFormat getLocaleFormat() { return ctx.getMarshallingContext().getLocaleFormat(); } 1292 1293 /** 1294 * Parses an ISO 8601 / numeric wire value into a {@link Duration} using this session's configured 1295 * {@linkplain #getDurationFormat() duration format} as the disambiguation hint. 1296 * 1297 * @param s The wire value to parse. <jk>null</jk> or empty returns <jk>null</jk>. 1298 * @return The parsed {@link Duration}, or <jk>null</jk> if {@code s} is <jk>null</jk> or empty. 1299 */ 1300 protected final Duration readDuration(String s) { 1301 return Iso8601Utils.parseDuration(s, getDurationFormat()); 1302 } 1303 1304 /** 1305 * Parses an ISO 8601 / numeric wire value into a {@link Period} using this session's configured 1306 * {@linkplain #getPeriodFormat() period format} as the disambiguation hint. 1307 * 1308 * @param s The wire value to parse. <jk>null</jk> or empty returns <jk>null</jk>. 1309 * @return The parsed {@link Period}, or <jk>null</jk> if {@code s} is <jk>null</jk> or empty. 1310 */ 1311 protected final Period readPeriod(String s) { 1312 return Iso8601Utils.parsePeriod(s, getPeriodFormat()); 1313 } 1314 1315 /** 1316 * Parses an ISO 8601 wire value into a {@link Date} target using this session's configured 1317 * {@linkplain #getDateFormat() date format} and {@linkplain #getTimeZone() time zone}. 1318 * 1319 * @param <T> The target type (unbounded to match {@link Iso8601Utils#parseDate}). 1320 * @param s The wire value to parse. <jk>null</jk> returns <jk>null</jk>. 1321 * @param cm The target class metadata. 1322 * @return The parsed value, or <jk>null</jk> if {@code s} is <jk>null</jk> or the target is not a 1323 * recognized {@link Date} type. 1324 */ 1325 protected final <T> T readDate(String s, ClassMeta<T> cm) { 1326 return Iso8601Utils.parseDate(s, cm, getDateFormat(), getTimeZone()); 1327 } 1328 1329 /** 1330 * Parses an ISO 8601 wire value into a {@link Calendar} or {@link XMLGregorianCalendar} 1331 * target using this session's configured {@linkplain #getCalendarFormat() calendar format} and 1332 * {@linkplain #getTimeZone() time zone}. 1333 * 1334 * @param <T> The target type (unbounded because {@link XMLGregorianCalendar} is not a 1335 * {@link Calendar} subclass). 1336 * @param s The wire value to parse. <jk>null</jk> returns <jk>null</jk>. 1337 * @param cm The target class metadata. 1338 * @return The parsed value, or <jk>null</jk> if {@code s} is <jk>null</jk> or the target is not a 1339 * recognized calendar type. 1340 */ 1341 protected final <T> T readCalendar(String s, ClassMeta<T> cm) { 1342 return Iso8601Utils.parseCalendar(s, cm, getCalendarFormat(), getTimeZone()); 1343 } 1344 1345 /** 1346 * Parses an ISO 8601 wire value into the specified {@link Temporal} subtype using 1347 * this session's configured {@linkplain #getTemporalFormat() temporal format} and 1348 * {@linkplain #getTimeZone() time zone}. 1349 * 1350 * @param <T> The target type (unbounded to match {@link Iso8601Utils#parseTemporal}). 1351 * @param s The wire value to parse. <jk>null</jk> returns <jk>null</jk>. 1352 * @param cm The target class metadata. 1353 * @return The parsed value, or <jk>null</jk> if {@code s} is <jk>null</jk> or the target is not a 1354 * {@link Temporal} subtype. 1355 */ 1356 protected final <T> T readTemporal(String s, ClassMeta<T> cm) { 1357 return Iso8601Utils.parseTemporal(s, cm, getTemporalFormat(), getTimeZone()); 1358 } 1359 1360 /** 1361 * Enters one level of recursive databind parsing, enforcing the shared {@link Builder#maxParseDepth(int)} 1362 * budget (default {@value #DEFAULT_MAX_PARSE_DEPTH}). 1363 * 1364 * <p> 1365 * Binary codec sessions (CBOR, Protobuf, MsgPack) call this at their recursive databind entry point before 1366 * descending into a nested array/map/message element, and must call {@link #exitParseDepth()} in a matching 1367 * <jk>finally</jk> block regardless of outcome. Exceeding the budget throws a {@link ParseException} so an 1368 * adversarial deeply-nested document fails cleanly instead of exhausting the call stack with a 1369 * {@link StackOverflowError}. 1370 * 1371 * <p> 1372 * Intentionally not applied to the JSON/XML/text parsers, whose recursive-descent parsers already rely on 1373 * {@link StackOverflowError} conversion at the outer parse boundary. 1374 * 1375 * @throws ParseException If the configured maximum parse depth was exceeded. 1376 */ 1377 protected final void enterParseDepth() throws ParseException { 1378 if (++parseDepth > maxParseDepth) { 1379 parseDepth--; 1380 throw new ParseException(this, "Maximum parse depth exceeded (%s).", maxParseDepth); 1381 } 1382 } 1383 1384 /** 1385 * Exits one level of recursive databind parsing previously entered via {@link #enterParseDepth()}. 1386 * 1387 * <p> 1388 * Must be called exactly once per successful {@link #enterParseDepth()} call, in a matching <jk>finally</jk> 1389 * block so the depth counter is restored even when the nested parse throws. 1390 */ 1391 protected final void exitParseDepth() { 1392 parseDepth--; 1393 } 1394 1395 /** 1396 * Runs a local, parser-controlled allocation (e.g. pre-sizing an array/collection/buffer from a 1397 * wire-declared count) and converts an {@link OutOfMemoryError} thrown specifically from it into a bounded 1398 * {@link ParseException}. 1399 * 1400 * <p> 1401 * Only an {@link OutOfMemoryError} thrown from {@code alloc}'s own execution is converted here. Any other 1402 * {@link OutOfMemoryError} — one raised elsewhere in the call stack, signaling a JVM that is 1403 * genuinely running out of heap — is <b>not</b> this method's concern and is never observed by it; a 1404 * dying JVM is not a client input-validation failure and must not be reported as one. Format-specific 1405 * parser sessions should route untrusted-length-driven local allocations through this helper instead of 1406 * relying on a blanket catch at an outer parse boundary. 1407 * 1408 * @param <T> The allocated object's type. 1409 * @param what A short human-readable description of what's being allocated, used in the exception message 1410 * if the allocation fails. 1411 * <br>Must not be <jk>null</jk>. 1412 * @param alloc The allocation to attempt. 1413 * <br>Must not be <jk>null</jk>. 1414 * @return The allocated object. 1415 * @throws ParseException If {@code alloc} itself threw an {@link OutOfMemoryError}. 1416 */ 1417 protected final <T> T allocateLocal(String what, Supplier<T> alloc) throws ParseException { 1418 try { 1419 return alloc.get(); 1420 } catch (@SuppressWarnings("unused") OutOfMemoryError e) { 1421 throw new ParseException(this, "Out of memory occurred. Input too large to parse. Allocation: %s", what); 1422 } 1423 } 1424 1425 /** 1426 * Marks the current position. 1427 */ 1428 protected void mark() { 1429 if (nn(pipe)) { 1430 Position p = pipe.getPosition(); 1431 mark.line = p.line; 1432 mark.column = p.column; 1433 mark.position = p.position; 1434 } 1435 } 1436 1437 /** 1438 * Specialized warning when an exception is thrown while executing a bean setter. 1439 * 1440 * @param p The bean map entry representing the bean property. 1441 * <br>Must not be <jk>null</jk>. 1442 * @param t The throwable that the bean setter threw. 1443 */ 1444 protected final void onBeanSetterException(BeanPropertyMeta p, Throwable t) { 1445 if (nn(listener)) 1446 listener.onBeanSetterException(this, t, p); 1447 var prefix = ""; 1448 addWarning("%sCould not call setValue() on property '%s' of class '%s', exception = %s", prefix, p.getName(), p.getBeanMeta().getBeanInfo(), localizedMessage(t)); 1449 } 1450 1451 /** 1452 * Method that gets called when an unknown bean property name is encountered. 1453 * 1454 * @param propertyName The unknown bean property name. 1455 * <br>Must not be <jk>null</jk>. 1456 * @param beanMap The bean that doesn't have the expected property. 1457 * <br>Must not be <jk>null</jk>. 1458 * @param value The parsed value. 1459 * <br>Can be <jk>null</jk>, in which case whether the property is ignored is governed by 1460 * {@link MarshallingContext.Builder#disableIgnoreUnknownNullBeanProperties()} rather than 1461 * {@link MarshallingContext.Builder#ignoreUnknownBeanProperties()}. 1462 * @throws ParseException 1463 * Automatically thrown if {@link MarshallingContext.Builder#ignoreUnknownBeanProperties()} setting on this parser is 1464 * <jk>false</jk> 1465 * @param <T> The class type of the bean map that doesn't have the expected property. 1466 */ 1467 protected final <T> void onUnknownProperty(String propertyName, BeanMap<T> beanMap, Object value) throws ParseException { 1468 var bmcm = (ClassMeta<T>) beanMap.getBeanInfo(); 1469 if (propertyName.equals(getBeanTypePropertyName(bmcm))) 1470 return; 1471 if (! isIgnoreUnknownBeanProperties() && (nn(value) || ! isIgnoreUnknownNullBeanProperties())) 1472 throw new ParseException(this, "Unknown property '%s' encountered while trying to parse into class '%s'", propertyName, bmcm); 1473 if (nn(listener)) 1474 listener.onUnknownBeanProperty(this, propertyName, bmcm.inner(), beanMap.getBean()); 1475 } 1476 1477 @Override /* Overridden from MarshallingSession */ 1478 protected FluentMap<String,Object> properties() { 1479 return super.properties() 1480 .a("javaMethod", javaMethod) 1481 .a("listener", listener) 1482 .a("outer", outer) 1483 .a("trimStrings", trimStrings) 1484 .a("nulls", nulls) 1485 .a("maxParseDepth", maxParseDepth); 1486 } 1487 1488 /** 1489 * Returns a {@link StringBuilder} object back into the internal reuse pool. 1490 * 1491 * @param sb The string builder to return to the pool. No-op if <jk>null</jk>. 1492 */ 1493 protected final void returnStringBuilder(StringBuilder sb) { 1494 if (sb == null) 1495 return; 1496 sb.setLength(0); 1497 sbStack.push(sb); 1498 } 1499 1500 /** 1501 * Sets the current class being parsed for proper error messages. 1502 * 1503 * @param currentClass The current class being parsed. 1504 */ 1505 protected final void setCurrentClass(ClassMeta<?> currentClass) { this.currentClass = currentClass; } 1506 1507 /** 1508 * Sets the current bean property being parsed for proper error messages. 1509 * 1510 * @param currentProperty The current property being parsed. 1511 */ 1512 protected final void setCurrentProperty(BeanPropertyMeta currentProperty) { this.currentProperty = currentProperty; } 1513 1514 /** 1515 * The {@link #createPipe(Object)} method should call this method to set the pipe for debugging purposes. 1516 * 1517 * @param pipe The pipe created for this session. 1518 * @return The same pipe. 1519 */ 1520 protected ParserPipe setPipe(ParserPipe pipe) { 1521 this.pipe = pipe; 1522 return pipe; 1523 } 1524 1525 /** 1526 * Trims the specified object if it's a <c>String</c> and {@link #isTrimStrings()} returns <jk>true</jk>. 1527 * 1528 * @param <K> The object type. 1529 * @param o The object to trim. 1530 * @return The trimmed string if it's a string. 1531 */ 1532 protected final <K> K trim(K o) { 1533 if (isTrimStrings() && o instanceof String o2) 1534 return (K)o2.trim(); 1535 return o; 1536 1537 } 1538 1539 /** 1540 * Trims the specified string if {@link ParserSession#isTrimStrings()} returns <jk>true</jk>. 1541 * 1542 * @param s The input string to trim. 1543 * @return The trimmed string, or <jk>null</jk> if the input was <jk>null</jk>. 1544 */ 1545 protected final String trim(String s) { 1546 if (isTrimStrings() && nn(s)) 1547 return s.trim(); 1548 return s; 1549 } 1550 1551 /** 1552 * Unmarks the current position. 1553 */ 1554 protected void unmark() { 1555 mark.line = -1; 1556 mark.column = -1; 1557 mark.position = -1; 1558 } 1559 1560 /** 1561 * Invokes the specified swap on the specified object. 1562 * 1563 * @param swap The swap to invoke. 1564 * <br>Must not be <jk>null</jk>. 1565 * @param o The input object. 1566 * @param eType The expected type. 1567 * @return The swapped object. 1568 * @throws ParseException If swap method threw an exception. 1569 */ 1570 protected Object unswap(ObjectSwap swap, Object o, ClassMeta<?> eType) throws ParseException { 1571 try { 1572 return swap.unswap(this, o, eType); 1573 } catch (Exception e) { 1574 throw new ParseException(e); 1575 } 1576 } 1577}