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.getListenerClass());
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   @Override /* Session */
078   public ObjectMap asMap() {
079      return super.asMap()
080         .append("ParserSession", new ObjectMap()
081            .append("javaMethod", javaMethod)
082            .append("listener", listener)
083            .append("outer", outer)
084         );
085   }
086
087   //-----------------------------------------------------------------------------------------------------------------
088   // Abstract methods
089   //-----------------------------------------------------------------------------------------------------------------
090
091   /**
092    * Workhorse method.
093    *
094    * <p>
095    * Subclasses are expected to implement this method.
096    *
097    * @param pipe Where to get the input from.
098    * @param type
099    *    The class type of the object to create.
100    *    If <jk>null</jk> or <code>Object.<jk>class</jk></code>, object type is based on what's being parsed.
101    *    For example, when parsing JSON text, it may return a <code>String</code>, <code>Number</code>,
102    *    <code>ObjectMap</code>, etc...
103    * @param <T> The class type of the object to create.
104    * @return The parsed object.
105    * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed.
106    */
107   protected abstract <T> T doParse(ParserPipe pipe, ClassMeta<T> type) throws Exception;
108
109   /**
110    * Returns <jk>true</jk> if this parser subclasses from {@link ReaderParser}.
111    *
112    * @return <jk>true</jk> if this parser subclasses from {@link ReaderParser}.
113    */
114   public abstract boolean isReaderParser();
115
116
117   //-----------------------------------------------------------------------------------------------------------------
118   // Other methods
119   //-----------------------------------------------------------------------------------------------------------------
120
121   /**
122    * Wraps the specified input object into a {@link ParserPipe} object so that it can be easily converted into
123    * a stream or reader.
124    *
125    * @param input
126    *    The input.
127    *    <br>For character-based parsers, this can be any of the following types:
128    *    <ul>
129    *       <li><jk>null</jk>
130    *       <li>{@link Reader}
131    *       <li>{@link CharSequence}
132    *       <li>{@link InputStream} containing UTF-8 encoded text (or whatever the encoding specified by
133    *          {@link ReaderParser#RPARSER_inputStreamCharset}).
134    *       <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or whatever the encoding specified by
135    *          {@link ReaderParser#RPARSER_inputStreamCharset}).
136    *       <li>{@link File} containing system encoded text (or whatever the encoding specified by
137    *          {@link ReaderParser#RPARSER_fileCharset}).
138    *    </ul>
139    *    <br>For byte-based parsers, this can be any of the following types:
140    *    <ul>
141    *       <li><jk>null</jk>
142    *       <li>{@link InputStream}
143    *       <li><code><jk>byte</jk>[]</code>
144    *       <li>{@link File}
145    *       <li>{@link CharSequence} containing encoded bytes according to the {@link InputStreamParser#ISPARSER_binaryFormat} setting.
146    *    </ul>
147    * @return
148    *    A new {@link ParserPipe} wrapper around the specified input object.
149    */
150   // TODO - Make abstract in 8.0
151   public ParserPipe createPipe(Object input) { 
152      return null;
153   }
154
155   /**
156    * Returns information used to determine at what location in the parse a failure occurred.
157    *
158    * @return A map, typically containing something like <code>{line:123,column:456,currentProperty:"foobar"}</code>
159    */
160   public final ObjectMap getLastLocation() {
161      ObjectMap m = new ObjectMap();
162      if (currentClass != null)
163         m.put("currentClass", currentClass.toString(true));
164      if (currentProperty != null)
165         m.put("currentProperty", currentProperty);
166      return m;
167   }
168
169   /**
170    * Returns the Java method that invoked this parser.
171    *
172    * <p>
173    * When using the REST API, this is the Java method invoked by the REST call.
174    * Can be used to access annotations defined on the method or class.
175    *
176    * @return The Java method that invoked this parser.
177   */
178   protected final Method getJavaMethod() {
179      return javaMethod;
180   }
181
182   /**
183    * Returns the outer object used for instantiating top-level non-static member classes.
184    *
185    * <p>
186    * When using the REST API, this is the servlet object.
187    *
188    * @return The outer object.
189   */
190   protected final Object getOuter() {
191      return outer;
192   }
193
194   /**
195    * Sets the current bean property being parsed for proper error messages.
196    *
197    * @param currentProperty The current property being parsed.
198    */
199   protected final void setCurrentProperty(BeanPropertyMeta currentProperty) {
200      this.currentProperty = currentProperty;
201   }
202
203   /**
204    * Sets the current class being parsed for proper error messages.
205    *
206    * @param currentClass The current class being parsed.
207    */
208   protected final void setCurrentClass(ClassMeta<?> currentClass) {
209      this.currentClass = currentClass;
210   }
211
212   /**
213    * Trims the specified object if it's a <code>String</code> and {@link #isTrimStrings()} returns <jk>true</jk>.
214    *
215    * @param o The object to trim.
216    * @return The trimmed string if it's a string.
217    */
218   @SuppressWarnings("unchecked")
219   protected final <K> K trim(K o) {
220      if (isTrimStrings() && o instanceof String)
221         return (K)o.toString().trim();
222      return o;
223
224   }
225
226   /**
227    * Trims the specified string if {@link ParserSession#isTrimStrings()} returns <jk>true</jk>.
228    *
229    * @param s The input string to trim.
230    * @return The trimmed string, or <jk>null</jk> if the input was <jk>null</jk>.
231    */
232   protected final String trim(String s) {
233      if (isTrimStrings() && s != null)
234         return s.trim();
235      return s;
236   }
237
238   /**
239    * Converts the specified <code>ObjectMap</code> into a bean identified by the <js>"_type"</js> property in the map.
240    *
241    * @param m The map to convert to a bean.
242    * @param pMeta The current bean property being parsed.
243    * @param eType The current expected type being parsed.
244    * @return
245    *    The converted bean, or the same map if the <js>"_type"</js> entry wasn't found or didn't resolve to a bean.
246    */
247   protected final Object cast(ObjectMap m, BeanPropertyMeta pMeta, ClassMeta<?> eType) {
248
249      String btpn = getBeanTypePropertyName(eType);
250
251      Object o = m.get(btpn);
252      if (o == null)
253         return m;
254      String typeName = o.toString();
255
256      ClassMeta<?> cm = getClassMeta(typeName, pMeta, eType);
257
258      if (cm != null) {
259         BeanMap<?> bm = m.getBeanSession().newBeanMap(cm.getInnerClass());
260
261         // Iterate through all the entries in the map and set the individual field values.
262         for (Map.Entry<String,Object> e : m.entrySet()) {
263            String k = e.getKey();
264            Object v = e.getValue();
265            if (! k.equals(btpn)) {
266               // Attempt to recursively cast child maps.
267               if (v instanceof ObjectMap)
268                  v = cast((ObjectMap)v, pMeta, eType);
269               bm.put(k, v);
270            }
271         }
272         return bm.getBean();
273      }
274
275      return m;
276   }
277
278   /**
279    * Give the specified dictionary name, resolve it to a class.
280    *
281    * @param typeName The dictionary name to resolve.
282    * @param pMeta The bean property we're currently parsing.
283    * @param eType The expected type we're currently parsing.
284    * @return The resolved class, or <jk>null</jk> if the type name could not be resolved.
285    */
286   protected final ClassMeta<?> getClassMeta(String typeName, BeanPropertyMeta pMeta, ClassMeta<?> eType) {
287      BeanRegistry br = null;
288
289      // Resolve via @BeanProperty(beanDictionary={})
290      if (pMeta != null) {
291         br = pMeta.getBeanRegistry();
292         if (br != null && br.hasName(typeName))
293            return br.getClassMeta(typeName);
294      }
295
296      // Resolve via @Bean(beanDictionary={}) on the expected type where the
297      // expected type is an interface with subclasses.
298      if (eType != null) {
299         br = eType.getBeanRegistry();
300         if (br != null && br.hasName(typeName))
301            return br.getClassMeta(typeName);
302      }
303
304      // Last resort, resolve using the session registry.
305      return getBeanRegistry().getClassMeta(typeName);
306   }
307
308   /**
309    * Method that gets called when an unknown bean property name is encountered.
310    *
311    * @param propertyName The unknown bean property name.
312    * @param beanMap The bean that doesn't have the expected property.
313    * @throws ParseException
314    *    Automatically thrown if {@link BeanContext#BEAN_ignoreUnknownBeanProperties} setting on this parser is
315    *    <jk>false</jk>
316    * @param <T> The class type of the bean map that doesn't have the expected property.
317    */
318   protected final <T> void onUnknownProperty(String propertyName, BeanMap<T> beanMap) throws ParseException {
319      if (propertyName.equals(getBeanTypePropertyName(beanMap.getClassMeta())))
320         return;
321      if (! isIgnoreUnknownBeanProperties())
322         throw new ParseException(this,
323            "Unknown property ''{0}'' encountered while trying to parse into class ''{1}''", propertyName,
324            beanMap.getClassMeta());
325      if (listener != null)
326         listener.onUnknownBeanProperty(this, propertyName, beanMap.getClassMeta().getInnerClass(), beanMap.getBean());
327   }
328
329   /**
330    * Parses input into the specified object type.
331    *
332    * <p>
333    * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
334    *
335    * <h5 class='section'>Examples:</h5>
336    * <p class='bcode w800'>
337    *    ReaderParser p = JsonParser.<jsf>DEFAULT</jsf>;
338    *
339    *    <jc>// Parse into a linked-list of strings.</jc>
340    *    List l = p.parse(json, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
341    *
342    *    <jc>// Parse into a linked-list of beans.</jc>
343    *    List l = p.parse(json, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
344    *
345    *    <jc>// Parse into a linked-list of linked-lists of strings.</jc>
346    *    List l = p.parse(json, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
347    *
348    *    <jc>// Parse into a map of string keys/values.</jc>
349    *    Map m = p.parse(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
350    *
351    *    <jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
352    *    Map m = p.parse(json, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
353    * </p>
354    *
355    * <p>
356    * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type.
357    *
358    * <p>
359    * <code>Map</code> classes are assumed to be followed by zero or two meta objects indicating the key and value types.
360    *
361    * <p>
362    * The array can be arbitrarily long to indicate arbitrarily complex data structures.
363    *
364    * <h5 class='section'>Notes:</h5>
365    * <ul class='spaced-list'>
366    *    <li>
367    *       Use the {@link #parse(Object, Class)} method instead if you don't need a parameterized map/collection.
368    * </ul>
369    *
370    * @param <T> The class type of the object to create.
371    * @param input
372    *    The input.
373    *    <br>Character-based parsers can handle the following input class types:
374    *    <ul>
375    *       <li><jk>null</jk>
376    *       <li>{@link Reader}
377    *       <li>{@link CharSequence}
378    *       <li>{@link InputStream} containing UTF-8 encoded text (or charset defined by
379    *          {@link ReaderParser#RPARSER_inputStreamCharset} property value).
380    *       <li><code><jk>byte</jk>[]</code> containing UTF-8 encoded text (or charset defined by
381    *          {@link ReaderParser#RPARSER_inputStreamCharset} property value).
382    *       <li>{@link File} containing system encoded text (or charset defined by
383    *          {@link ReaderParser#RPARSER_fileCharset} property value).
384    *    </ul>
385    *    <br>Stream-based parsers can handle the following input class types:
386    *    <ul>
387    *       <li><jk>null</jk>
388    *       <li>{@link InputStream}
389    *       <li><code><jk>byte</jk>[]</code>
390    *       <li>{@link File}
391    *    </ul>
392    * @param type
393    *    The object type to create.
394    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
395    * @param args
396    *    The type arguments of the class if it's a collection or map.
397    *    <br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
398    *    <br>Ignored if the main type is not a map or collection.
399    * @return The parsed object.
400    * @throws ParseException
401    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
402    * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections.
403    */
404   @SuppressWarnings("unchecked")
405   public final <T> T parse(Object input, Type type, Type...args) throws ParseException {
406      try (ParserPipe pipe = createPipe(input)) {
407         return (T)parseInner(pipe, getClassMeta(type, args));
408      }
409   }
410
411   /**
412    * Same as {@link #parse(Object, Type, Type...)} except optimized for a non-parameterized class.
413    *
414    * <p>
415    * This is the preferred parse method for simple types since you don't need to cast the results.
416    *
417    * <h5 class='section'>Examples:</h5>
418    * <p class='bcode w800'>
419    *    ReaderParser p = JsonParser.<jsf>DEFAULT</jsf>;
420    *
421    *    <jc>// Parse into a string.</jc>
422    *    String s = p.parse(json, String.<jk>class</jk>);
423    *
424    *    <jc>// Parse into a bean.</jc>
425    *    MyBean b = p.parse(json, MyBean.<jk>class</jk>);
426    *
427    *    <jc>// Parse into a bean array.</jc>
428    *    MyBean[] ba = p.parse(json, MyBean[].<jk>class</jk>);
429    *
430    *    <jc>// Parse into a linked-list of objects.</jc>
431    *    List l = p.parse(json, LinkedList.<jk>class</jk>);
432    *
433    *    <jc>// Parse into a map of object keys/values.</jc>
434    *    Map m = p.parse(json, TreeMap.<jk>class</jk>);
435    * </p>
436    *
437    * @param <T> The class type of the object being created.
438    * @param input
439    *    The input.
440    *    See {@link #parse(Object, Type, Type...)} for details.
441    * @param type The object type to create.
442    * @return The parsed object.
443    * @throws ParseException
444    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
445    */
446   public final <T> T parse(Object input, Class<T> type) throws ParseException {
447      try (ParserPipe pipe = createPipe(input)) {
448         return parseInner(pipe, getClassMeta(type));
449      }
450   }
451
452   /**
453    * Same as {@link #parse(Object, Type, Type...)} except the type has already been converted into a {@link ClassMeta}
454    * object.
455    *
456    * <p>
457    * This is mostly an internal method used by the framework.
458    *
459    * @param <T> The class type of the object being created.
460    * @param input
461    *    The input.
462    *    See {@link #parse(Object, Type, Type...)} for details.
463    * @param type The object type to create.
464    * @return The parsed object.
465    * @throws ParseException
466    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
467    */
468   public final <T> T parse(Object input, ClassMeta<T> type) throws ParseException {
469      try (ParserPipe pipe = createPipe(input)) {
470         return parseInner(pipe, type);
471      }
472   }
473
474   /**
475    * Entry point for all parsing calls.
476    *
477    * <p>
478    * Calls the {@link #doParse(ParserPipe, ClassMeta)} implementation class and catches/re-wraps any exceptions
479    * thrown.
480    *
481    * @param pipe The parser input.
482    * @param type The class type of the object to create.
483    * @param <T> The class type of the object to create.
484    * @return The parsed object.
485    * @throws ParseException
486    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
487    */
488   private <T> T parseInner(ParserPipe pipe, ClassMeta<T> type) throws ParseException {
489      if (type.isVoid())
490         return null;
491      try {
492         return doParse(pipe, type);
493      } catch (ParseException e) {
494         throw e;
495      } catch (StackOverflowError e) {
496         throw new ParseException(this, "Depth too deep.  Stack overflow occurred.");
497      } catch (IOException e) {
498         throw new ParseException(this, e, "I/O exception occurred.  exception={0}, message={1}.",
499            e.getClass().getSimpleName(), e.getLocalizedMessage());
500      } catch (Exception e) {
501         throw new ParseException(this, e, "Exception occurred.  exception={0}, message={1}.",
502            e.getClass().getSimpleName(), e.getLocalizedMessage());
503      } finally {
504         checkForWarnings();
505      }
506   }
507
508   /**
509    * Parses the contents of the specified reader and loads the results into the specified map.
510    *
511    * <p>
512    * Reader must contain something that serializes to a map (such as text containing a JSON object).
513    *
514    * <p>
515    * Used in the following locations:
516    * <ul class='spaced-list'>
517    *    <li>
518    *       The various character-based constructors in {@link ObjectMap} (e.g.
519    *       {@link ObjectMap#ObjectMap(CharSequence,Parser)}).
520    * </ul>
521    *
522    * @param <K> The key class type.
523    * @param <V> The value class type.
524    * @param input The input.  See {@link #parse(Object, ClassMeta)} for supported input types.
525    * @param m The map being loaded.
526    * @param keyType The class type of the keys, or <jk>null</jk> to default to <code>String.<jk>class</jk></code>.
527    * @param valueType The class type of the values, or <jk>null</jk> to default to whatever is being parsed.
528    * @return The same map that was passed in to allow this method to be chained.
529    * @throws ParseException If the input contains a syntax error or is malformed, or is not valid for the specified type.
530    * @throws UnsupportedOperationException If not implemented.
531    */
532   public final <K,V> Map<K,V> parseIntoMap(Object input, Map<K,V> m, Type keyType, Type valueType) throws ParseException {
533      try (ParserPipe pipe = createPipe(input)) {
534         return doParseIntoMap(pipe, m, keyType, valueType);
535      } catch (ParseException e) {
536         throw e;
537      } catch (Exception e) {
538         throw new ParseException(this, e);
539      } finally {
540         checkForWarnings();
541      }
542   }
543
544   /**
545    * Implementation method.
546    *
547    * <p>
548    * Default implementation throws an {@link UnsupportedOperationException}.
549    *
550    * @param pipe The parser input.
551    * @param m The map being loaded.
552    * @param keyType The class type of the keys, or <jk>null</jk> to default to <code>String.<jk>class</jk></code>.
553    * @param valueType The class type of the values, or <jk>null</jk> to default to whatever is being parsed.
554    * @return The same map that was passed in to allow this method to be chained.
555    * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed.
556    */
557   protected <K,V> Map<K,V> doParseIntoMap(ParserPipe pipe, Map<K,V> m, Type keyType, Type valueType) throws Exception {
558      throw new UnsupportedOperationException("Parser '"+getClass().getName()+"' does not support this method.");
559   }
560
561   /**
562    * Parses the contents of the specified reader and loads the results into the specified collection.
563    *
564    * <p>
565    * Used in the following locations:
566    * <ul class='spaced-list'>
567    *    <li>
568    *       The various character-based constructors in {@link ObjectList} (e.g.
569    *       {@link ObjectList#ObjectList(CharSequence,Parser)}.
570    * </ul>
571    *
572    * @param <E> The element class type.
573    * @param input The input.  See {@link #parse(Object, ClassMeta)} for supported input types.
574    * @param c The collection being loaded.
575    * @param elementType The class type of the elements, or <jk>null</jk> to default to whatever is being parsed.
576    * @return The same collection that was passed in to allow this method to be chained.
577    * @throws ParseException
578    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
579    * @throws UnsupportedOperationException If not implemented.
580    */
581   public final <E> Collection<E> parseIntoCollection(Object input, Collection<E> c, Type elementType) throws ParseException {
582      try (ParserPipe pipe = createPipe(input)) {
583         return doParseIntoCollection(pipe, c, elementType);
584      } catch (ParseException e) {
585         throw e;
586      } catch (StackOverflowError e) {
587         throw new ParseException(this, "Depth too deep.  Stack overflow occurred.");
588      } catch (IOException e) {
589         throw new ParseException(this, e, "I/O exception occurred.  exception={0}, message={1}.",
590            e.getClass().getSimpleName(), e.getLocalizedMessage());
591      } catch (Exception e) {
592         throw new ParseException(this, e, "Exception occurred.  exception={0}, message={1}.",
593            e.getClass().getSimpleName(), e.getLocalizedMessage());
594      } finally {
595         checkForWarnings();
596      }
597   }
598
599   /**
600    * Implementation method.
601    *
602    * <p>
603    * Default implementation throws an {@link UnsupportedOperationException}.
604    *
605    * @param pipe The parser input.
606    * @param c The collection being loaded.
607    * @param elementType The class type of the elements, or <jk>null</jk> to default to whatever is being parsed.
608    * @return The same collection that was passed in to allow this method to be chained.
609    * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed.
610    */
611   protected <E> Collection<E> doParseIntoCollection(ParserPipe pipe, Collection<E> c, Type elementType) throws Exception {
612      throw new UnsupportedOperationException("Parser '"+getClass().getName()+"' does not support this method.");
613   }
614
615   /**
616    * Parses the specified array input with each entry in the object defined by the {@code argTypes}
617    * argument.
618    *
619    * <p>
620    * Used for converting arrays (e.g. <js>"[arg1,arg2,...]"</js>) into an {@code Object[]} that can be passed
621    * to the {@code Method.invoke(target, args)} method.
622    *
623    * <p>
624    * Used in the following locations:
625    * <ul class='spaced-list'>
626    *    <li>
627    *       Used to parse argument strings in the {@link PojoIntrospector#invokeMethod(Method, Reader)} method.
628    * </ul>
629    *
630    * @param input The input.  Subclasses can support different input types.
631    * @param argTypes Specifies the type of objects to create for each entry in the array.
632    * @return An array of parsed objects.
633    * @throws ParseException
634    *    If the input contains a syntax error or is malformed, or is not valid for the specified type.
635    */
636   public final Object[] parseArgs(Object input, Type[] argTypes) throws ParseException {
637      try (ParserPipe pipe = createPipe(input)) {
638         return doParse(pipe, getArgsClassMeta(argTypes));
639      } catch (ParseException e) {
640         throw e;
641      } catch (StackOverflowError e) {
642         throw new ParseException(this, "Depth too deep.  Stack overflow occurred.");
643      } catch (IOException e) {
644         throw new ParseException(this, e, "I/O exception occurred.  exception={0}, message={1}.",
645            e.getClass().getSimpleName(), e.getLocalizedMessage());
646      } catch (Exception e) {
647         throw new ParseException(this, e, "Exception occurred.  exception={0}, message={1}.",
648            e.getClass().getSimpleName(), e.getLocalizedMessage());
649      } finally {
650         checkForWarnings();
651      }
652   }
653
654   /**
655    * Converts the specified string to the specified type.
656    *
657    * @param outer
658    *    The outer object if we're converting to an inner object that needs to be created within the context
659    *    of an outer object.
660    * @param s The string to convert.
661    * @param type The class type to convert the string to.
662    * @return The string converted as an object of the specified type.
663    * @throws Exception If the input contains a syntax error or is malformed, or is not valid for the specified type.
664    * @param <T> The class type to convert the string to.
665    */
666   @SuppressWarnings({ "unchecked", "rawtypes" })
667   protected final <T> T convertAttrToType(Object outer, String s, ClassMeta<T> type) throws Exception {
668      if (s == null)
669         return null;
670
671      if (type == null)
672         type = (ClassMeta<T>)object();
673      PojoSwap swap = type.getPojoSwap(this);
674      ClassMeta<?> sType = swap == null ? type : swap.getSwapClassMeta(this);
675
676      Object o = s;
677      if (sType.isChar())
678         o = parseCharacter(s);
679      else if (sType.isNumber())
680         if (type.canCreateNewInstanceFromNumber(outer))
681            o = type.newInstanceFromNumber(this, outer, parseNumber(s, type.getNewInstanceFromNumberClass()));
682         else
683            o = parseNumber(s, (Class<? extends Number>)sType.getInnerClass());
684      else if (sType.isBoolean())
685         o = Boolean.parseBoolean(s);
686      else if (! (sType.isCharSequence() || sType.isObject())) {
687         if (sType.canCreateNewInstanceFromString(outer))
688            o = sType.newInstanceFromString(outer, s);
689         else
690            throw new ParseException(this, "Invalid conversion from string to class ''{0}''", type);
691      }
692
693      if (swap != null)
694         o = swap.unswap(this, o, type);
695
696      return (T)o;
697   }
698
699   /**
700    * Convenience method for calling the {@link ParentProperty @ParentProperty} method on the specified object if it
701    * exists.
702    *
703    * @param cm The class type of the object.
704    * @param o The object.
705    * @param parent The parent to set.
706    * @throws Exception
707    */
708   protected static final void setParent(ClassMeta<?> cm, Object o, Object parent) throws Exception {
709      Setter m = cm.getParentProperty();
710      if (m != null)
711         m.set(o, parent);
712   }
713
714   /**
715    * Convenience method for calling the {@link NameProperty @NameProperty} method on the specified object if it exists.
716    *
717    * @param cm The class type of the object.
718    * @param o The object.
719    * @param name The name to set.
720    * @throws Exception
721    */
722   protected static final void setName(ClassMeta<?> cm, Object o, Object name) throws Exception {
723      if (cm != null) {
724         Setter m = cm.getNameProperty();
725         if (m != null)
726            m.set(o, name);
727      }
728   }
729
730   /**
731    * Returns the listener associated with this session.
732    *
733    * @return The listener associated with this session, or <jk>null</jk> if there is no listener.
734    */
735   public ParserListener getListener() {
736      return listener;
737   }
738
739   /**
740    * Returns the listener associated with this session.
741    *
742    * @param c The listener class to cast to.
743    * @return The listener associated with this session, or <jk>null</jk> if there is no listener.
744    */
745   @SuppressWarnings("unchecked")
746   public <T extends ParserListener> T getListener(Class<T> c) {
747      return (T)listener;
748   }
749
750   /**
751    * The {@link #createPipe(Object)} method should call this method to set the pipe for debugging purposes.
752    *
753    * @param pipe The pipe created for this session.
754    * @return The same pipe.
755    */
756   protected ParserPipe setPipe(ParserPipe pipe) {
757      this.pipe = pipe;
758      return pipe;
759   }
760
761   /**
762    * Returns the current position into the reader or input stream.
763    *
764    * @return
765    *    The current position into the reader or input stream.
766    *    <br>Never <jk>null</jk>.
767    */
768   public Position getPosition() {
769      if (mark.line != -1 || mark.column != -1 || mark.position != -1)
770         return mark;
771      if (pipe == null)
772         return Position.UNKNOWN;
773      return pipe.getPosition();
774   }
775
776   /**
777    * Marks the current position.
778    */
779   protected void mark() {
780      if (pipe != null) {
781         Position p = pipe.getPosition();
782         mark.line = p.line;
783         mark.column = p.column;
784         mark.position = p.position;
785      }
786   }
787
788   /**
789    * Unmarks the current position.
790    */
791   protected void unmark() {
792      mark.line = -1;
793      mark.column = -1;
794      mark.position = -1;
795   }
796
797   /**
798    * Returns the input as a string.
799    *
800    * <p>
801    * This always returns a value for input of type {@link CharSequence}.
802    * <br>For other input types, use {@link BeanContext#BEAN_debug} setting to enable caching to a string
803    * before parsing so that this method returns the input.
804    *
805    * @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.
806    */
807   public String getInputAsString() {
808      return pipe == null ? null : pipe.getInputAsString();
809   }
810
811   //-----------------------------------------------------------------------------------------------------------------
812   // Properties
813   //-----------------------------------------------------------------------------------------------------------------
814
815   /**
816    * Configuration property:  Trim parsed strings.
817    *
818    * @see Parser#PARSER_trimStrings
819    * @return
820    *    <jk>true</jk> if string values will be trimmed of whitespace using {@link String#trim()} before being added to
821    *    the POJO.
822    */
823   protected final boolean isTrimStrings() {
824      return ctx.isTrimStrings();
825   }
826
827   /**
828    * Configuration property:  Strict mode.
829    *
830    * @see Parser#PARSER_strict
831    * @return
832    *    <jk>true</jk> if strict mode for the parser is enabled.
833    */
834   protected final boolean isStrict() {
835      return ctx.isStrict();
836   }
837
838   /**
839    * Configuration property:  Auto-close streams.
840    *
841    * @see Parser#PARSER_autoCloseStreams
842    * @return
843    *    <jk>true</jk> if <l>InputStreams</l> and <l>Readers</l> passed into parsers will be closed
844    *    after parsing is complete.
845    */
846   protected final boolean isAutoCloseStreams() {
847      return ctx.isAutoCloseStreams();
848   }
849
850   /**
851    * Configuration property:  Unbuffered.
852    *
853    * @see Parser#PARSER_unbuffered
854    * @return
855    *    <jk>true</jk> if parsers don't use internal buffering during parsing.
856    */
857   protected final boolean isUnbuffered() {
858      return ctx.isUnbuffered();
859   }
860
861   /**
862    * Configuration property:  Debug output lines.
863    *
864    * @see Parser#PARSER_debugOutputLines
865    * @return
866    *    The number of lines of input before and after the error location to be printed as part of the exception message.
867    */
868   protected final int getDebugOutputLines() {
869      return ctx.getDebugOutputLines();
870   }
871
872   /**
873    * Configuration property:  Parser listener.
874    *
875    * @see Parser#PARSER_listener
876    * @return
877    *    Class used to listen for errors and warnings that occur during parsing.
878    */
879   protected final Class<? extends ParserListener> getListenerClass() {
880      return ctx.getListenerClass();
881   }
882}