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