View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.parser;
18  
19  import static org.apache.juneau.commons.utils.AssertionUtils.*;
20  import static org.apache.juneau.commons.utils.StringUtils.*;
21  import static org.apache.juneau.commons.utils.ThrowableUtils.*;
22  import static org.apache.juneau.commons.utils.Utils.*;
23  
24  import java.io.*;
25  import java.lang.reflect.*;
26  import java.nio.charset.*;
27  import java.util.*;
28  import java.util.function.*;
29  
30  import org.apache.juneau.*;
31  import org.apache.juneau.annotation.*;
32  import org.apache.juneau.collections.*;
33  import org.apache.juneau.commons.collections.FluentMap;
34  import org.apache.juneau.commons.reflect.*;
35  import org.apache.juneau.cp.*;
36  import org.apache.juneau.httppart.*;
37  import org.apache.juneau.objecttools.*;
38  import org.apache.juneau.swap.*;
39  
40  /**
41   * Session object that lives for the duration of a single use of {@link Parser}.
42   *
43   * <h5 class='section'>Notes:</h5><ul>
44   * 	<li class='warn'>This class is not thread safe and is typically discarded after one use.
45   * </ul>
46   *
47   * <h5 class='section'>See Also:</h5><ul>
48   * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SerializersAndParsers">Serializers and Parsers</a>
49  
50   * </ul>
51   */
52  public class ParserSession extends BeanSession {
53  	/**
54  	 * Builder class.
55  	 */
56  	public static class Builder extends BeanSession.Builder {
57  
58  		private HttpPartSchema schema;
59  		private Method javaMethod;
60  		private Object outer;
61  		private Parser ctx;
62  
63  		/**
64  		 * Constructor
65  		 *
66  		 * @param ctx The context creating this session.
67  		 * 	<br>Cannot be <jk>null</jk>.
68  		 */
69  		protected Builder(Parser ctx) {
70  			super(assertArgNotNull("ctx", ctx).getBeanContext());
71  			this.ctx = ctx;
72  			mediaTypeDefault(ctx.getPrimaryMediaType());
73  		}
74  
75  		@Override /* Overridden from Builder */
76  		public <T> Builder apply(Class<T> type, Consumer<T> apply) {
77  			super.apply(type, apply);
78  			return this;
79  		}
80  
81  		@Override
82  		public ParserSession build() {
83  			return new ParserSession(this);
84  		}
85  
86  		@Override /* Overridden from Builder */
87  		public Builder debug(Boolean value) {
88  			super.debug(value);
89  			return this;
90  		}
91  
92  		/**
93  		 * The java method that called this serializer, usually the method in a REST servlet.
94  		 *
95  		 * @param value
96  		 * 	The new property value.
97  		 * 	<br>Can be <jk>null</jk>.
98  		 * @return This object.
99  		 */
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 }