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 }