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