All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.parser.Parser Maven / Gradle / Ivy

// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
// * with the License.  You may obtain a copy of the License at                                                              *
// *                                                                                                                         *
// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
// *                                                                                                                         *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
// * specific language governing permissions and limitations under the License.                                              *
// ***************************************************************************************************************************
package org.apache.juneau.parser;

import static org.apache.juneau.collections.JsonMap.*;
import static org.apache.juneau.common.internal.StringUtils.*;
import static org.apache.juneau.internal.CollectionUtils.*;

import java.io.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.nio.charset.*;
import java.util.*;

import org.apache.juneau.*;
import org.apache.juneau.collections.*;
import org.apache.juneau.html.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.msgpack.*;
import org.apache.juneau.objecttools.*;
import org.apache.juneau.swap.*;
import org.apache.juneau.swaps.*;
import org.apache.juneau.uon.*;
import org.apache.juneau.utils.*;
import org.apache.juneau.xml.*;

/**
 * Parent class for all Juneau parsers.
 *
 * 
Valid data conversions
*

* Parsers can parse any parsable POJO types, as specified in the POJO Categories. * *

* Some examples of conversions are shown below... *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Data typeClass typeJSON exampleXML exampleClass examples
objectMaps, Java beans{name:'John Smith',age:21}<object> * <name type='string'>John Smith</name> * <age type='number'>21</age> * </object>HashMap, TreeMap<String,Integer>
arrayCollections, Java arrays[1,2,3]<array> * <number>1</number> * <number>2</number> * <number>3</number> * </array>List<Integer>, int[], Float[], Set<Person>
numberNumbers123<number>123</number>Integer, Long, Float, int
booleanBooleanstrue<boolean>true</boolean>Boolean
stringCharSequences'foobar'<string>foobar</string>String, StringBuilder
* *

* In addition, any class types with {@link ObjectSwap ObjectSwaps} associated with them on the registered * bean context can also be passed in. * *

* For example, if the {@link TemporalCalendarSwap} transform is used to generalize {@code Calendar} objects to {@code String} * objects. * When registered with this parser, you can construct {@code Calendar} objects from {@code Strings} using the * following syntax... *

* Calendar calendar = parser.parse("'Sun Mar 03 04:05:06 EST 2001'", GregorianCalendar.class); *

* *

* If Object.class is specified as the target type, then the parser automatically determines the * data types and generates the following object types... *

* * * * * * *
JSON typeClass type
object{@link JsonMap}
array{@link JsonList}
number{@link Number}
(depending on length and format, could be {@link Integer}, * {@link Double}, {@link Float}, etc...)
boolean{@link Boolean}
string{@link String}
* *
Notes:
    *
  • This class is thread safe and reusable. *
* *
See Also:
*/ public class Parser extends BeanContextable { //------------------------------------------------------------------------------------------------------------------- // Static //------------------------------------------------------------------------------------------------------------------- /** * Creates a new builder for this object. * * @return A new builder. */ public static Builder create() { return new Builder(); } //------------------------------------------------------------------------------------------------------------------- // Static //------------------------------------------------------------------------------------------------------------------- /** * Represents no Parser. */ public static abstract class Null extends Parser { private Null(Builder builder) { super(builder); } } /** * Instantiates a builder of the specified parser class. * *

* Looks for a public static method called create that returns an object that can be passed into a public * or protected constructor of the class. * * @param c The builder to create. * @return A new builder. */ public static Builder createParserBuilder(Class c) { return (Builder)Context.createBuilder(c); } //------------------------------------------------------------------------------------------------------------------- // Builder //------------------------------------------------------------------------------------------------------------------- /** * Builder class. */ @FluentSetters public static class Builder extends BeanContextable.Builder { boolean autoCloseStreams, strict, trimStrings, unbuffered; String consumes; int debugOutputLines; Class listener; /** * Constructor, default settings. */ protected Builder() { autoCloseStreams = env("Parser.autoCloseStreams", false); strict = env("Parser.strict", false); trimStrings = env("Parser.trimStrings", false); unbuffered = env("Parser.unbuffered", false); debugOutputLines = env("Parser.debugOutputLines", 5); listener = null; consumes = null; } /** * Copy constructor. * * @param copyFrom The bean to copy from. */ protected Builder(Parser copyFrom) { super(copyFrom); autoCloseStreams = copyFrom.autoCloseStreams; strict = copyFrom.strict; trimStrings = copyFrom.trimStrings; unbuffered = copyFrom.unbuffered; debugOutputLines = copyFrom.debugOutputLines; listener = copyFrom.listener; consumes = copyFrom.consumes; } /** * Copy constructor. * * @param copyFrom The builder to copy from. */ protected Builder(Builder copyFrom) { super(copyFrom); autoCloseStreams = copyFrom.autoCloseStreams; strict = copyFrom.strict; trimStrings = copyFrom.trimStrings; unbuffered = copyFrom.unbuffered; debugOutputLines = copyFrom.debugOutputLines; listener = copyFrom.listener; consumes = copyFrom.consumes; } @Override /* Context.Builder */ public Builder copy() { return new Builder(this); } @Override /* Context.Builder */ public Parser build() { return build(Parser.class); } @Override /* Context.Builder */ public HashKey hashKey() { return HashKey.of( super.hashKey(), autoCloseStreams, strict, trimStrings, unbuffered, debugOutputLines, listener, consumes ); } //----------------------------------------------------------------------------------------------------------------- // Properties //----------------------------------------------------------------------------------------------------------------- /** * Specifies the media type that this parser consumes. * * @param value The value for this setting. * @return This object. */ @FluentSetter public Builder consumes(String value) { this.consumes = value; return this; } /** * Returns the current value for the 'consumes' property. * * @return The current value for the 'consumes' property. */ public String getConsumes() { return consumes; } /** * Auto-close streams. * *

* When enabled, InputStreams and Readers passed into parsers will be closed * after parsing is complete. * *

Example:
*

* // Create a parser using strict mode. * ReaderParser parser = JsonParser * .create() * .autoCloseStreams() * .build(); * * Reader myReader = new FileReader("/tmp/myfile.json"); * MyBean myBean = parser.parse(myReader, MyBean.class); * * assertTrue(myReader.isClosed()); *

* * @return This object. */ @FluentSetter public Builder autoCloseStreams() { return autoCloseStreams(true); } /** * Same as {@link #autoCloseStreams()} but allows you to explicitly specify the value. * * @param value The value for this setting. * @return This object. */ @FluentSetter public Builder autoCloseStreams(boolean value) { autoCloseStreams = value; return this; } /** * Debug output lines. * *

* When parse errors occur, this specifies the number of lines of input before and after the * error location to be printed as part of the exception message. * *

Example:
*

* // Create a parser whose exceptions print out 100 lines before and after the parse error location. * ReaderParser parser = JsonParser * .create() * .debug() // Enable debug mode to capture Reader contents as strings. * .debugOuputLines(100) * .build(); * * Reader myReader = new FileReader("/tmp/mybadfile.json"); * try { * parser.parse(myReader, Object.class); * } catch (ParseException e) { * System.err.println(e.getMessage()); // Will display 200 lines of the output. * } *

* * @param value * The new value for this property. *
The default value is 5. * @return This object. */ @FluentSetter public Builder debugOutputLines(int value) { debugOutputLines = value; return this; } /** * Parser listener. * *

* Class used to listen for errors and warnings that occur during parsing. * *

Example:
*

* // Define our parser listener. * // Simply captures all unknown bean property events. * public class MyParserListener extends ParserListener { * * // A simple property to store our events. * public List<String> events = new LinkedList<>(); * * @Override * public <T> void onUnknownBeanProperty(ParserSession session, String propertyName, Class<T> beanClass, T bean) { * Position position = parser.getPosition(); * events.add(propertyName + "," + position.getLine() + "," + position.getColumn()); * } * } * * // Create a parser using our listener. * ReaderParser parser = JsonParser * .create() * .listener(MyParserListener.class) * .build(); * * // Create a session object. * // Needed because listeners are created per-session. * try (ReaderParserSession session = parser.createSession()) { * * // Parse some JSON object. * MyBean myBean = session.parse("{...}", MyBean.class); * * // Get the listener. * MyParserListener listener = session.getListener(MyParserListener.class); * * // Dump the results to the console. * Json5.DEFAULT.println(listener.events); * } *

* * @param value The new value for this property. * @return This object. */ @FluentSetter public Builder listener(Class value) { listener = value; return this; } /** * Strict mode. * *

* When enabled, strict mode for the parser is enabled. * *

* Strict mode can mean different things for different parsers. * *

* * * * * * * * * *
Parser classStrict behavior
All reader-based parsers * When enabled, throws {@link ParseException ParseExceptions} on malformed charset input. * Otherwise, malformed input is ignored. *
{@link JsonParser} * When enabled, throws exceptions on the following invalid JSON syntax: *
    *
  • Unquoted attributes. *
  • Missing attribute values. *
  • Concatenated strings. *
  • Javascript comments. *
  • Numbers and booleans when Strings are expected. *
  • Numbers valid in Java but not JSON (e.g. octal notation, etc...) *
*
* *
Example:
*

* // Create a parser using strict mode. * ReaderParser parser = JsonParser * .create() * .strict() * .build(); * * // Use it. * try { * String json = "{unquotedAttr:'value'}"; * parser.parse(json, MyBean.class); * } catch (ParseException e) { * assertTrue(e.getMessage().contains("Unquoted attribute detected."); * } *

* * @return This object. */ @FluentSetter public Builder strict() { return strict(true); } /** * Same as {@link #strict()} but allows you to explicitly specify the value. * * @param value The value for this setting. * @return This object. */ @FluentSetter public Builder strict(boolean value) { strict = value; return this; } /** * Trim parsed strings. * *

* When enabled, string values will be trimmed of whitespace using {@link String#trim()} before being added to * the POJO. * *

Example:
*

* // Create a parser with trim-strings enabled. * ReaderParser parser = JsonParser * .create() * .trimStrings() * .build(); * * // Use it. * String json = "{' foo ':' bar '}"; * Map<String,String> myMap = parser.parse(json, HashMap.class, String.class, String.class); * * // Make sure strings are parsed. * assertEquals("bar", myMap.get("foo")); *

* * @return This object. */ @FluentSetter public Builder trimStrings() { return trimStrings(true); } /** * Same as {@link #trimStrings()} but allows you to explicitly specify the value. * * @param value The value for this setting. * @return This object. */ @FluentSetter public Builder trimStrings(boolean value) { trimStrings = value; return this; } /** * Unbuffered. * *

* When enabled, don't use internal buffering during parsing. * *

* This is useful in cases when you want to parse the same input stream or reader multiple times * because it may contain multiple independent POJOs to parse. *
Buffering would cause the parser to read past the current POJO in the stream. * *

Example:
*

* // Create a parser using strict mode. * ReaderParser parser = JsonParser. * .create() * .unbuffered(true) * .build(); * * // If you're calling parse on the same input multiple times, use a session instead of the parser directly. * // It's more efficient because we don't need to recalc the session settings again. * ReaderParserSession session = parser.createSession(); * * // Read input with multiple POJOs * Reader json = new StringReader("{foo:'bar'}{foo:'baz'}"); * MyBean myBean1 = session.parse(json, MyBean.class); * MyBean myBean2 = session.parse(json, MyBean.class); *

* *
Notes:
    *
  • * This only allows for multi-input streams for the following parsers: *
      *
    • {@link JsonParser} *
    • {@link UonParser} *
    * It has no effect on the following parsers: *
      *
    • {@link MsgPackParser} - It already doesn't use buffering. *
    • {@link XmlParser}, {@link HtmlParser} - These use StAX which doesn't allow for more than one root element anyway. *
    • RDF parsers - These read everything into an internal model before any parsing begins. *
    *
* * @return This object. */ @FluentSetter public Builder unbuffered() { return unbuffered(true); } /** * Same as {@link #unbuffered()} but allows you to explicitly specify the value. * * @param value The value for this setting. * @return This object. */ @FluentSetter public Builder unbuffered(boolean value) { unbuffered = value; return this; } // @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder annotations(Annotation...values) { super.annotations(values); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder apply(AnnotationWorkList work) { super.apply(work); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder applyAnnotations(java.lang.Class...fromClasses) { super.applyAnnotations(fromClasses); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder applyAnnotations(Method...fromMethods) { super.applyAnnotations(fromMethods); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder cache(Cache value) { super.cache(value); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder debug() { super.debug(); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder debug(boolean value) { super.debug(value); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder impl(Context value) { super.impl(value); return this; } @Override /* GENERATED - org.apache.juneau.Context.Builder */ public Builder type(Class value) { super.type(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanClassVisibility(Visibility value) { super.beanClassVisibility(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanConstructorVisibility(Visibility value) { super.beanConstructorVisibility(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanContext(BeanContext value) { super.beanContext(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanContext(BeanContext.Builder value) { super.beanContext(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanDictionary(java.lang.Class...values) { super.beanDictionary(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanFieldVisibility(Visibility value) { super.beanFieldVisibility(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanInterceptor(Class on, Class> value) { super.beanInterceptor(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanMapPutReturnsOldValue() { super.beanMapPutReturnsOldValue(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanMethodVisibility(Visibility value) { super.beanMethodVisibility(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanProperties(Map values) { super.beanProperties(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanProperties(Class beanClass, String properties) { super.beanProperties(beanClass, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanProperties(String beanClassName, String properties) { super.beanProperties(beanClassName, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesExcludes(Map values) { super.beanPropertiesExcludes(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesExcludes(Class beanClass, String properties) { super.beanPropertiesExcludes(beanClass, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesExcludes(String beanClassName, String properties) { super.beanPropertiesExcludes(beanClassName, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesReadOnly(Map values) { super.beanPropertiesReadOnly(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesReadOnly(Class beanClass, String properties) { super.beanPropertiesReadOnly(beanClass, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesReadOnly(String beanClassName, String properties) { super.beanPropertiesReadOnly(beanClassName, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesWriteOnly(Map values) { super.beanPropertiesWriteOnly(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesWriteOnly(Class beanClass, String properties) { super.beanPropertiesWriteOnly(beanClass, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beanPropertiesWriteOnly(String beanClassName, String properties) { super.beanPropertiesWriteOnly(beanClassName, properties); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beansRequireDefaultConstructor() { super.beansRequireDefaultConstructor(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beansRequireSerializable() { super.beansRequireSerializable(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder beansRequireSettersForGetters() { super.beansRequireSettersForGetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder dictionaryOn(Class on, java.lang.Class...values) { super.dictionaryOn(on, values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableBeansRequireSomeProperties() { super.disableBeansRequireSomeProperties(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableIgnoreMissingSetters() { super.disableIgnoreMissingSetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableIgnoreTransientFields() { super.disableIgnoreTransientFields(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableIgnoreUnknownNullBeanProperties() { super.disableIgnoreUnknownNullBeanProperties(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder disableInterfaceProxies() { super.disableInterfaceProxies(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder example(Class pojoClass, T o) { super.example(pojoClass, o); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder example(Class pojoClass, String json) { super.example(pojoClass, json); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder findFluentSetters() { super.findFluentSetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder findFluentSetters(Class on) { super.findFluentSetters(on); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder ignoreInvocationExceptionsOnGetters() { super.ignoreInvocationExceptionsOnGetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder ignoreInvocationExceptionsOnSetters() { super.ignoreInvocationExceptionsOnSetters(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder ignoreUnknownBeanProperties() { super.ignoreUnknownBeanProperties(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder ignoreUnknownEnumValues() { super.ignoreUnknownEnumValues(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder implClass(Class interfaceClass, Class implClass) { super.implClass(interfaceClass, implClass); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder implClasses(Map,Class> values) { super.implClasses(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder interfaceClass(Class on, Class value) { super.interfaceClass(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder interfaces(java.lang.Class...value) { super.interfaces(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder locale(Locale value) { super.locale(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder mediaType(MediaType value) { super.mediaType(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder notBeanClasses(java.lang.Class...values) { super.notBeanClasses(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder notBeanPackages(String...values) { super.notBeanPackages(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder propertyNamer(Class value) { super.propertyNamer(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder propertyNamer(Class on, Class value) { super.propertyNamer(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder sortProperties() { super.sortProperties(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder sortProperties(java.lang.Class...on) { super.sortProperties(on); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder stopClass(Class on, Class value) { super.stopClass(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder swap(Class normalClass, Class swappedClass, ThrowingFunction swapFunction) { super.swap(normalClass, swappedClass, swapFunction); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder swap(Class normalClass, Class swappedClass, ThrowingFunction swapFunction, ThrowingFunction unswapFunction) { super.swap(normalClass, swappedClass, swapFunction, unswapFunction); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder swaps(java.lang.Class...values) { super.swaps(values); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder timeZone(TimeZone value) { super.timeZone(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder typeName(Class on, String value) { super.typeName(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder typePropertyName(String value) { super.typePropertyName(value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder typePropertyName(Class on, String value) { super.typePropertyName(on, value); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder useEnumNames() { super.useEnumNames(); return this; } @Override /* GENERATED - org.apache.juneau.BeanContextable.Builder */ public Builder useJavaBeanIntrospector() { super.useJavaBeanIntrospector(); return this; } // } //------------------------------------------------------------------------------------------------------------------- // Instance //------------------------------------------------------------------------------------------------------------------- final boolean trimStrings, strict, autoCloseStreams, unbuffered; final int debugOutputLines; final String consumes; final Class listener; /** General parser properties currently set on this parser. */ private final MediaType[] consumesArray; /** * Constructor. * * @param builder The builder this object. */ protected Parser(Builder builder) { super(builder); consumes = builder.consumes; trimStrings = builder.trimStrings; strict = builder.strict; autoCloseStreams = builder.autoCloseStreams; debugOutputLines = builder.debugOutputLines; unbuffered = builder.unbuffered; listener = builder.listener; String[] _consumes = split(consumes != null ? consumes : ""); this.consumesArray = new MediaType[_consumes.length]; for (int i = 0; i < _consumes.length; i++) { this.consumesArray[i] = MediaType.of(_consumes[i]); } } @Override /* Context */ public Builder copy() { return new Builder(this); } //----------------------------------------------------------------------------------------------------------------- // Abstract methods //----------------------------------------------------------------------------------------------------------------- /** * Returns true if this parser subclasses from {@link ReaderParser}. * * @return true if this parser subclasses from {@link ReaderParser}. */ public boolean isReaderParser() { return true; } //----------------------------------------------------------------------------------------------------------------- // Other methods //----------------------------------------------------------------------------------------------------------------- /** * Parses input into the specified object type. * *

* The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). * *

Examples:
*

* ReaderParser parser = JsonParser.DEFAULT; * * // Parse into a linked-list of strings. * List list1 = parser.parse(json, LinkedList.class, String.class); * * // Parse into a linked-list of beans. * List list2 = parser.parse(json, LinkedList.class, MyBean.class); * * // Parse into a linked-list of linked-lists of strings. * List list3 = parser.parse(json, LinkedList.class, LinkedList.class, String.class); * * // Parse into a map of string keys/values. * Map map1 = parser.parse(json, TreeMap.class, String.class, String.class); * * // Parse into a map containing string keys and values of lists containing beans. * Map map2 = parser.parse(json, TreeMap.class, String.class, List.class, MyBean.class); *

* *

* Collection classes are assumed to be followed by zero or one objects indicating the element type. * *

* Map classes are assumed to be followed by zero or two meta objects indicating the key and value types. * *

* The array can be arbitrarily long to indicate arbitrarily complex data structures. * *

Notes:
    *
  • * Use the {@link #parse(Object, Class)} method instead if you don't need a parameterized map/collection. *
* * @param The class type of the object to create. * @param input * The input. *
Character-based parsers can handle the following input class types: *
    *
  • null *
  • {@link Reader} *
  • {@link CharSequence} *
  • {@link InputStream} containing UTF-8 encoded text (or charset defined by * {@link ReaderParser.Builder#streamCharset(Charset)} property value). *
  • byte[] containing UTF-8 encoded text (or charset defined by * {@link ReaderParser.Builder#streamCharset(Charset)} property value). *
  • {@link File} containing system encoded text (or charset defined by * {@link ReaderParser.Builder#fileCharset(Charset)} property value). *
*
Stream-based parsers can handle the following input class types: *
    *
  • null *
  • {@link InputStream} *
  • byte[] *
  • {@link File} *
  • {@link CharSequence} containing encoded bytes according to the {@link InputStreamParser.Builder#binaryFormat(BinaryFormat)} setting. *
* @param type * The object type to create. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} * @param args * The type arguments of the class if it's a collection or map. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} *
Ignored if the main type is not a map or collection. * @return The parsed object. * @throws ParseException Malformed input encountered. * @throws IOException Thrown by underlying stream. * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections. */ public final T parse(Object input, Type type, Type...args) throws ParseException, IOException { return getSession().parse(input, type, args); } /** * Same as {@link #parse(Object, Type, Type...)} but since it's a {@link String} input doesn't throw an {@link IOException}. * * @param The class type of the object being created. * @param input * The input. * See {@link #parse(Object, Type, Type...)} for details. * @param type * The object type to create. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} * @param args * The type arguments of the class if it's a collection or map. *
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType} *
Ignored if the main type is not a map or collection. * @return The parsed object. * @throws ParseException Malformed input encountered. */ public final T parse(String input, Type type, Type...args) throws ParseException { return getSession().parse(input, type, args); } /** * Same as {@link #parse(Object, Type, Type...)} except optimized for a non-parameterized class. * *

* This is the preferred parse method for simple types since you don't need to cast the results. * *

Examples:
*

* ReaderParser parser = JsonParser.DEFAULT; * * // Parse into a string. * String string = parser.parse(json, String.class); * * // Parse into a bean. * MyBean bean = parser.parse(json, MyBean.class); * * // Parse into a bean array. * MyBean[] beanArray = parser.parse(json, MyBean[].class); * * // Parse into a linked-list of objects. * List list = parser.parse(json, LinkedList.class); * * // Parse into a map of object keys/values. * Map map = parser.parse(json, TreeMap.class); *

* * @param The class type of the object being created. * @param input * The input. * See {@link #parse(Object, Type, Type...)} for details. * @param type The object type to create. * @return The parsed object. * @throws ParseException Malformed input encountered. * @throws IOException Thrown by the underlying stream. */ public final T parse(Object input, Class type) throws ParseException, IOException { return getSession().parse(input, type); } /** * Same as {@link #parse(Object, Class)} but since it's a {@link String} input doesn't throw an {@link IOException}. * * @param The class type of the object being created. * @param input * The input. * See {@link #parse(Object, Type, Type...)} for details. * @param type The object type to create. * @return The parsed object. * @throws ParseException Malformed input encountered. */ public final T parse(String input, Class type) throws ParseException { return getSession().parse(input, type); } /** * Same as {@link #parse(Object, Type, Type...)} except the type has already been converted into a {@link ClassMeta} * object. * *

* This is mostly an internal method used by the framework. * * @param The class type of the object being created. * @param input * The input. * See {@link #parse(Object, Type, Type...)} for details. * @param type The object type to create. * @return The parsed object. * @throws ParseException Malformed input encountered. * @throws IOException Thrown by the underlying stream. */ public final T parse(Object input, ClassMeta type) throws ParseException, IOException { return getSession().parse(input, type); } /** * Same as {@link #parse(Object, ClassMeta)} but since it's a {@link String} input doesn't throw an {@link IOException}. * * @param The class type of the object being created. * @param input * The input. * See {@link #parse(Object, Type, Type...)} for details. * @param type The object type to create. * @return The parsed object. * @throws ParseException Malformed input encountered. */ public final T parse(String input, ClassMeta type) throws ParseException { return getSession().parse(input, type); } @Override /* Context */ public ParserSession.Builder createSession() { return ParserSession.create(this); } @Override /* Context */ public ParserSession getSession() { return createSession().build(); } /** * Workhorse method. * *

* Subclasses are expected to either implement this method or {@link ParserSession#doParse(ParserPipe, ClassMeta)}. * * @param session The current session. * @param pipe Where to get the input from. * @param type * The class type of the object to create. * If null or Object.class, object type is based on what's being parsed. * For example, when parsing JSON text, it may return a String, Number, * JsonMap, etc... * @param The class type of the object to create. * @return The parsed object. * @throws IOException Thrown by underlying stream. * @throws ParseException Malformed input encountered. * @throws ExecutableException Exception occurred on invoked constructor/method/field. */ public T doParse(ParserSession session, ParserPipe pipe, ClassMeta type) throws IOException, ParseException { throw new UnsupportedOperationException(); } //----------------------------------------------------------------------------------------------------------------- // Optional methods //----------------------------------------------------------------------------------------------------------------- /** * Parses the contents of the specified reader and loads the results into the specified map. * *

* Reader must contain something that serializes to a map (such as text containing a JSON object). * *

* Used in the following locations: *

    *
  • * The various character-based constructors in {@link JsonMap} (e.g. * {@link JsonMap#JsonMap(CharSequence,Parser)}). *
* * @param The key class type. * @param The value class type. * @param input The input. See {@link #parse(Object, ClassMeta)} for supported input types. * @param m The map being loaded. * @param keyType The class type of the keys, or null to default to String.class. * @param valueType The class type of the values, or null to default to whatever is being parsed. * @return The same map that was passed in to allow this method to be chained. * @throws ParseException Malformed input encountered. * @throws UnsupportedOperationException If not implemented. */ public final Map parseIntoMap(Object input, Map m, Type keyType, Type valueType) throws ParseException { return getSession().parseIntoMap(input, m, keyType, valueType); } /** * Parses the contents of the specified reader and loads the results into the specified collection. * *

* Used in the following locations: *

    *
  • * The various character-based constructors in {@link JsonList} (e.g. * {@link JsonList#JsonList(CharSequence,Parser)}. *
* * @param The element class type. * @param input The input. See {@link #parse(Object, ClassMeta)} for supported input types. * @param c The collection being loaded. * @param elementType The class type of the elements, or null to default to whatever is being parsed. * @return The same collection that was passed in to allow this method to be chained. * @throws ParseException Malformed input encountered. * @throws UnsupportedOperationException If not implemented. */ public final Collection parseIntoCollection(Object input, Collection c, Type elementType) throws ParseException { return getSession().parseIntoCollection(input, c, elementType); } /** * Parses the specified array input with each entry in the object defined by the {@code argTypes} * argument. * *

* Used for converting arrays (e.g. "[arg1,arg2,...]") into an {@code Object[]} that can be passed * to the {@code Method.invoke(target, args)} method. * *

* Used in the following locations: *

    *
  • * Used to parse argument strings in the {@link ObjectIntrospector#invokeMethod(Method, Reader)} method. *
* * @param input The input. Subclasses can support different input types. * @param argTypes Specifies the type of objects to create for each entry in the array. * @return An array of parsed objects. * @throws ParseException Malformed input encountered. */ public final Object[] parseArgs(Object input, Type[] argTypes) throws ParseException { if (argTypes == null || argTypes.length == 0) return new Object[0]; return getSession().parseArgs(input, argTypes); } //----------------------------------------------------------------------------------------------------------------- // Other methods //----------------------------------------------------------------------------------------------------------------- /** * Returns the media types handled based on the values passed to the consumes constructor parameter. * * @return The list of media types. Never null. */ public final List getMediaTypes() { return ulist(consumesArray); } /** * Returns the first media type handled based on the values passed to the consumes constructor parameter. * * @return The media type. */ public final MediaType getPrimaryMediaType() { return consumesArray == null || consumesArray.length == 0 ? null : consumesArray[0]; } /** * Returns true if this parser can handle the specified content type. * * @param contentType The content type to test. * @return true if this parser can handle the specified content type. */ public boolean canHandle(String contentType) { if (contentType != null) for (MediaType mt : getMediaTypes()) if (contentType.equals(mt.toString())) return true; return false; } //----------------------------------------------------------------------------------------------------------------- // Properties //----------------------------------------------------------------------------------------------------------------- /** * Auto-close streams. * * @see Parser.Builder#autoCloseStreams() * @return * true if InputStreams and Readers passed into parsers will be closed * after parsing is complete. */ protected final boolean isAutoCloseStreams() { return autoCloseStreams; } /** * Debug output lines. * * @see Parser.Builder#debugOutputLines(int) * @return * The number of lines of input before and after the error location to be printed as part of the exception message. */ protected final int getDebugOutputLines() { return debugOutputLines; } /** * Parser listener. * * @see Parser.Builder#listener(Class) * @return * Class used to listen for errors and warnings that occur during parsing. */ protected final Class getListener() { return listener; } /** * Strict mode. * * @see Parser.Builder#strict() * @return * true if strict mode for the parser is enabled. */ protected final boolean isStrict() { return strict; } /** * Trim parsed strings. * * @see Parser.Builder#trimStrings() * @return * true if string values will be trimmed of whitespace using {@link String#trim()} before being added to * the POJO. */ protected final boolean isTrimStrings() { return trimStrings; } /** * Unbuffered. * * @see Parser.Builder#unbuffered() * @return * true if parsers don't use internal buffering during parsing. */ protected final boolean isUnbuffered() { return unbuffered; } //----------------------------------------------------------------------------------------------------------------- // Other methods //----------------------------------------------------------------------------------------------------------------- @Override /* Context */ protected JsonMap properties() { return filteredMap() .append("autoCloseStreams", autoCloseStreams) .append("debugOutputLines", debugOutputLines) .append("listener", listener) .append("strict", strict) .append("trimStrings", trimStrings) .append("unbuffered", unbuffered); } }