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