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} &mdash; one raised elsewhere in the call stack, signaling a JVM that is
1403    * genuinely running out of heap &mdash; 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}