juneau-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamesbog...@apache.org
Subject [05/12] incubator-juneau git commit: Allow serializer and parser sessions to be reused.
Date Fri, 28 Jul 2017 18:22:20 GMT
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
index ade91df..4b6c17a 100644
--- a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
@@ -16,16 +16,18 @@ import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.serializer.SerializerContext.*;
 
+import java.io.*;
 import java.lang.reflect.*;
 import java.text.*;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.http.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.soap.*;
 import org.apache.juneau.transform.*;
 
 /**
- * Context object that lives for the duration of a single use of {@link Serializer}.
+ * Serializer session that lives for the duration of a single use of {@link Serializer}.
  *
  * <p>
  * Used by serializers for the following purposes:
@@ -41,9 +43,10 @@ import org.apache.juneau.transform.*;
  * </ul>
  *
  * <p>
- * This class is NOT thread safe.  It is meant to be discarded after one-time use.
+ * This class is NOT thread safe.
+ * It is typically discarded after one-time use although it can be reused within the same thread.
  */
-public class SerializerSession extends BeanSession {
+public abstract class SerializerSession extends BeanSession {
 
 	private final int maxDepth, initialDepth, maxIndent;
 	private final boolean
@@ -61,17 +64,19 @@ public class SerializerSession extends BeanSession {
 	private final char quoteChar;
 	private final UriResolver uriResolver;
 
-	/** The current indentation depth into the model. */
-	public int indent;
-
 	private final Map<Object,Object> set;                                           // Contains the current objects in the current branch of the model.
 	private final LinkedList<StackElement> stack = new LinkedList<StackElement>();  // Contains the current objects in the current branch of the model.
-	private boolean isBottom;                                                       // If 'true', then we're at a leaf in the model (i.e. a String, Number, Boolean, or null).
 	private final Method javaMethod;                                                // Java method that invoked this serializer.
+
+	// Writable properties
+	private boolean isBottom;                                                       // If 'true', then we're at a leaf in the model (i.e. a String, Number, Boolean, or null).
 	private BeanPropertyMeta currentProperty;
 	private ClassMeta<?> currentClass;
 	private final SerializerListener listener;
 
+	/** The current indentation depth into the model. */
+	public int indent;
+
 
 	/**
 	 * Create a new session using properties specified in the context.
@@ -79,29 +84,26 @@ public class SerializerSession extends BeanSession {
 	 * @param ctx
 	 * 	The context creating this session object.
 	 * 	The context contains all the configuration settings for this object.
-	 * @param op
-	 * 	The override properties.
-	 * 	These override any context properties defined in the context.
-	 * @param javaMethod The java method that called this serializer, usually the method in a REST servlet.
-	 * @param locale
-	 * 	The session locale.
-	 * 	If <jk>null</jk>, then the locale defined on the context is used.
-	 * @param timeZone
-	 * 	The session timezone.
-	 * 	If <jk>null</jk>, then the timezone defined on the context is used.
-	 * @param mediaType The session media type (e.g. <js>"application/json"</js>).
-	 * @param uriContext
-	 * 	The URI context.
-	 * 	Identifies the current request URI used for resolution of URIs to absolute or root-relative form.
-	 */
-	public SerializerSession(SerializerContext ctx, ObjectMap op, Method javaMethod, Locale locale,
-			TimeZone timeZone, MediaType mediaType, UriContext uriContext) {
-		super(ctx, op, locale, timeZone, mediaType);
-		this.javaMethod = javaMethod;
+	 * 	<br>If <jk>null</jk>, defaults to {@link SerializerContext#DEFAULT}.
+	 * @param args
+	 * 	Runtime arguments.
+	 * 	These specify session-level information such as locale and URI context.
+	 * 	It also include session-level properties that override the properties defined on the bean and
+	 * 	serializer contexts.
+	 * 	<br>If <jk>null</jk>, defaults to {@link SerializerSessionArgs#DEFAULT}.
+	 */
+	protected SerializerSession(SerializerContext ctx, SerializerSessionArgs args) {
+		super(ctx != null ? ctx : SerializerContext.DEFAULT, args != null ? args : SerializerSessionArgs.DEFAULT);
+		if (ctx == null)
+			ctx = SerializerContext.DEFAULT;
+		if (args == null)
+			args = SerializerSessionArgs.DEFAULT;
+		this.javaMethod = args.javaMethod;
 		UriResolution uriResolution;
 		UriRelativity uriRelativity;
 		Class<?> listenerClass;
-		if (op == null || op.isEmpty()) {
+		ObjectMap p = getProperties();
+		if (p.isEmpty()) {
 			maxDepth = ctx.maxDepth;
 			initialDepth = ctx.initialDepth;
 			detectRecursions = ctx.detectRecursions;
@@ -121,27 +123,27 @@ public class SerializerSession extends BeanSession {
 			uriRelativity = ctx.uriRelativity;
 			listenerClass = ctx.listener;
 		} else {
-			maxDepth = op.getInt(SERIALIZER_maxDepth, ctx.maxDepth);
-			initialDepth = op.getInt(SERIALIZER_initialDepth, ctx.initialDepth);
-			detectRecursions = op.getBoolean(SERIALIZER_detectRecursions, ctx.detectRecursions);
-			ignoreRecursions = op.getBoolean(SERIALIZER_ignoreRecursions, ctx.ignoreRecursions);
-			useWhitespace = op.getBoolean(SERIALIZER_useWhitespace, ctx.useWhitespace);
-			maxIndent = op.getInt(SERIALIZER_maxIndent, ctx.maxIndent);
-			addBeanTypeProperties = op.getBoolean(SERIALIZER_addBeanTypeProperties, ctx.addBeanTypeProperties);
-			trimNulls = op.getBoolean(SERIALIZER_trimNullProperties, ctx.trimNulls);
-			trimEmptyCollections = op.getBoolean(SERIALIZER_trimEmptyCollections, ctx.trimEmptyCollections);
-			trimEmptyMaps = op.getBoolean(SERIALIZER_trimEmptyMaps, ctx.trimEmptyMaps);
-			trimStrings = op.getBoolean(SERIALIZER_trimStrings, ctx.trimStrings);
-			quoteChar = op.getString(SERIALIZER_quoteChar, ""+ctx.quoteChar).charAt(0);
-			sortCollections = op.getBoolean(SERIALIZER_sortCollections, ctx.sortMaps);
-			sortMaps = op.getBoolean(SERIALIZER_sortMaps, ctx.sortMaps);
-			abridged = op.getBoolean(SERIALIZER_abridged, ctx.abridged);
-			uriResolution = op.get(UriResolution.class, SERIALIZER_uriResolution, UriResolution.ROOT_RELATIVE);
-			uriRelativity = op.get(UriRelativity.class, SERIALIZER_uriRelativity, UriRelativity.RESOURCE);
-			listenerClass = op.get(Class.class, SERIALIZER_listener, ctx.listener);
+			maxDepth = p.getInt(SERIALIZER_maxDepth, ctx.maxDepth);
+			initialDepth = p.getInt(SERIALIZER_initialDepth, ctx.initialDepth);
+			detectRecursions = p.getBoolean(SERIALIZER_detectRecursions, ctx.detectRecursions);
+			ignoreRecursions = p.getBoolean(SERIALIZER_ignoreRecursions, ctx.ignoreRecursions);
+			useWhitespace = p.getBoolean(SERIALIZER_useWhitespace, ctx.useWhitespace);
+			maxIndent = p.getInt(SERIALIZER_maxIndent, ctx.maxIndent);
+			addBeanTypeProperties = p.getBoolean(SERIALIZER_addBeanTypeProperties, ctx.addBeanTypeProperties);
+			trimNulls = p.getBoolean(SERIALIZER_trimNullProperties, ctx.trimNulls);
+			trimEmptyCollections = p.getBoolean(SERIALIZER_trimEmptyCollections, ctx.trimEmptyCollections);
+			trimEmptyMaps = p.getBoolean(SERIALIZER_trimEmptyMaps, ctx.trimEmptyMaps);
+			trimStrings = p.getBoolean(SERIALIZER_trimStrings, ctx.trimStrings);
+			quoteChar = p.getString(SERIALIZER_quoteChar, ""+ctx.quoteChar).charAt(0);
+			sortCollections = p.getBoolean(SERIALIZER_sortCollections, ctx.sortMaps);
+			sortMaps = p.getBoolean(SERIALIZER_sortMaps, ctx.sortMaps);
+			abridged = p.getBoolean(SERIALIZER_abridged, ctx.abridged);
+			uriResolution = p.get(UriResolution.class, SERIALIZER_uriResolution, UriResolution.ROOT_RELATIVE);
+			uriRelativity = p.get(UriRelativity.class, SERIALIZER_uriRelativity, UriRelativity.RESOURCE);
+			listenerClass = p.get(Class.class, SERIALIZER_listener, ctx.listener);
 		}
 
-		uriResolver = new UriResolver(uriResolution, uriRelativity, uriContext == null ? ctx.uriContext : uriContext);
+		uriResolver = new UriResolver(uriResolution, uriRelativity, args.uriContext == null ? ctx.uriContext : args.uriContext);
 
 		listener = newInstance(SerializerListener.class, listenerClass);
 
@@ -154,11 +156,102 @@ public class SerializerSession extends BeanSession {
 	}
 
 	/**
+	 * Wraps the specified input object into a {@link ParserPipe} object so that it can be easily converted into
+	 * a stream or reader.
+	 *
+	 * @param output
+	 * 	The output location.
+	 * 	<br>For character-based serializers, this can be any of the following types:
+	 * 	<ul>
+	 * 		<li>{@link Writer}
+	 * 		<li>{@link OutputStream} - Output will be written as UTF-8 encoded stream.
+	 * 		<li>{@link File} - Output will be written as system-default encoded stream.
+	 * 		<li>{@link StringBuilder}
+	 * 	</ul>
+	 * 	<br>For byte-based serializers, this can be any of the following types:
+	 * 	<ul>
+	 * 		<li>{@link OutputStream}
+	 * 		<li>{@link File}
+	 * 	</ul>
+	 * @return
+	 * 	A new {@link ParserPipe} wrapper around the specified input object.
+	 */
+	protected SerializerPipe createPipe(Object output) {
+		return new SerializerPipe(output);
+	}
+
+
+	//--------------------------------------------------------------------------------
+	// Abstract methods
+	//--------------------------------------------------------------------------------
+
+	/**
+	 * Serializes a POJO to the specified output stream or writer.
+	 *
+	 * <p>
+	 * This method should NOT close the context object.
+	 *
+	 * @param pipe Where to send the output from the serializer.
+	 * @param o The object to serialize.
+	 * @throws Exception If thrown from underlying stream, or if the input contains a syntax error or is malformed.
+	 */
+	protected abstract void doSerialize(SerializerPipe pipe, Object o) throws Exception;
+
+	/**
+	 * Shortcut method for serializing objects directly to either a <code>String</code> or <code><jk>byte</jk>[]</code>
+	 * depending on the serializer type.
+	 *
+	 * @param o The object to serialize.
+	 * @return
+	 * 	The serialized object.
+	 * 	<br>Character-based serializers will return a <code>String</code>
+	 * 	<br>Stream-based serializers will return a <code><jk>byte</jk>[]</code>
+	 * @throws SerializeException If a problem occurred trying to convert the output.
+	 */
+	public abstract Object serialize(Object o) throws SerializeException;
+
+	/**
+	 * Returns <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}.
+	 *
+	 * @return <jk>true</jk> if this serializer subclasses from {@link WriterSerializer}.
+	 */
+	public abstract boolean isWriterSerializer();
+
+
+	//--------------------------------------------------------------------------------
+	// Other methods
+	//--------------------------------------------------------------------------------
+
+	/**
+	 * Serialize the specified object using the specified session.
+	 *
+	 * @param out Where to send the output from the serializer.
+	 * @param o The object to serialize.
+	 * @throws SerializeException If a problem occurred trying to convert the output.
+	 */
+	public final void serialize(Object out, Object o) throws SerializeException {
+		SerializerPipe pipe = createPipe(out);
+		try {
+			doSerialize(pipe, o);
+		} catch (SerializeException e) {
+			throw e;
+		} catch (StackOverflowError e) {
+			throw new SerializeException(this,
+				"Stack overflow occurred.  This can occur when trying to serialize models containing loops.  It's recommended you use the SerializerContext.SERIALIZER_detectRecursions setting to help locate the loop.").initCause(e);
+		} catch (Exception e) {
+			throw new SerializeException(this, e);
+		} finally {
+			pipe.close();
+			close();
+		}
+	}
+
+	/**
 	 * Sets the current bean property being serialized for proper error messages.
 	 *
 	 * @param currentProperty The current property being serialized.
 	 */
-	public void setCurrentProperty(BeanPropertyMeta currentProperty) {
+	protected final void setCurrentProperty(BeanPropertyMeta currentProperty) {
 		this.currentProperty = currentProperty;
 	}
 
@@ -167,7 +260,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @param currentClass The current class being serialized.
 	 */
-	public void setCurrentClass(ClassMeta<?> currentClass) {
+	protected final void setCurrentClass(ClassMeta<?> currentClass) {
 		this.currentClass = currentClass;
 	}
 
@@ -180,7 +273,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The Java method that invoked this serializer.
 	*/
-	public final Method getJavaMethod() {
+	protected final Method getJavaMethod() {
 		return javaMethod;
 	}
 
@@ -189,7 +282,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The URI resolver.
 	 */
-	public final UriResolver getUriResolver() {
+	protected final UriResolver getUriResolver() {
 		return uriResolver;
 	}
 
@@ -198,7 +291,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_maxDepth} setting value for this session.
 	 */
-	public final int getMaxDepth() {
+	protected final int getMaxDepth() {
 		return maxDepth;
 	}
 
@@ -207,7 +300,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_initialDepth} setting value for this session.
 	 */
-	public final int getInitialDepth() {
+	protected final int getInitialDepth() {
 		return initialDepth;
 	}
 
@@ -216,7 +309,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_detectRecursions} setting value for this session.
 	 */
-	public final boolean isDetectRecursions() {
+	protected final boolean isDetectRecursions() {
 		return detectRecursions;
 	}
 
@@ -225,7 +318,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_ignoreRecursions} setting value for this session.
 	 */
-	public final boolean isIgnoreRecursions() {
+	protected final boolean isIgnoreRecursions() {
 		return ignoreRecursions;
 	}
 
@@ -234,7 +327,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_useWhitespace} setting value for this session.
 	 */
-	public final boolean isUseWhitespace() {
+	protected final boolean isUseWhitespace() {
 		return useWhitespace;
 	}
 
@@ -243,7 +336,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_maxIndent} setting value for this session.
 	 */
-	public final int getMaxIndent() {
+	protected final int getMaxIndent() {
 		return maxIndent;
 	}
 
@@ -252,7 +345,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_addBeanTypeProperties} setting value for this session.
 	 */
-	public boolean isAddBeanTypeProperties() {
+	protected boolean isAddBeanTypeProperties() {
 		return addBeanTypeProperties;
 	}
 
@@ -261,7 +354,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_quoteChar} setting value for this session.
 	 */
-	public final char getQuoteChar() {
+	protected final char getQuoteChar() {
 		return quoteChar;
 	}
 
@@ -270,7 +363,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_trimNullProperties} setting value for this session.
 	 */
-	public final boolean isTrimNulls() {
+	protected final boolean isTrimNulls() {
 		return trimNulls;
 	}
 
@@ -279,7 +372,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_trimEmptyCollections} setting value for this session.
 	 */
-	public final boolean isTrimEmptyCollections() {
+	protected final boolean isTrimEmptyCollections() {
 		return trimEmptyCollections;
 	}
 
@@ -288,7 +381,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_trimEmptyMaps} setting value for this session.
 	 */
-	public final boolean isTrimEmptyMaps() {
+	protected final boolean isTrimEmptyMaps() {
 		return trimEmptyMaps;
 	}
 
@@ -297,7 +390,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_trimStrings} setting value for this session.
 	 */
-	public final boolean isTrimStrings() {
+	protected final boolean isTrimStrings() {
 		return trimStrings;
 	}
 
@@ -306,7 +399,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_sortCollections} setting value for this session.
 	 */
-	public final boolean isSortCollections() {
+	protected final boolean isSortCollections() {
 		return sortCollections;
 	}
 
@@ -315,7 +408,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return The {@link SerializerContext#SERIALIZER_sortMaps} setting value for this session.
 	 */
-	public final boolean isSortMaps() {
+	protected final boolean isSortMaps() {
 		return sortMaps;
 	}
 
@@ -330,7 +423,7 @@ public class SerializerSession extends BeanSession {
 	 * 	once (since they can be expensive).
 	 * @throws SerializeException If recursion occurred.
 	 */
-	public ClassMeta<?> push(String attrName, Object o, ClassMeta<?> eType) throws SerializeException {
+	protected final ClassMeta<?> push(String attrName, Object o, ClassMeta<?> eType) throws SerializeException {
 		indent++;
 		isBottom = true;
 		if (o == null)
@@ -363,7 +456,7 @@ public class SerializerSession extends BeanSession {
 	 * @return <jk>true</jk> if recursion detected.
 	 * @throws SerializeException If recursion occurred.
 	 */
-	public boolean willRecurse(String attrName, Object o, ClassMeta<?> cm) throws SerializeException {
+	protected final boolean willRecurse(String attrName, Object o, ClassMeta<?> cm) throws SerializeException {
 		if (! (detectRecursions || isDebug()))
 			return false;
 		if (! set.containsKey(o))
@@ -378,7 +471,7 @@ public class SerializerSession extends BeanSession {
 	/**
 	 * Pop an object off the stack.
 	 */
-	public void pop() {
+	protected final void pop() {
 		indent--;
 		if ((detectRecursions || isDebug()) && ! isBottom)  {
 			Object o = stack.removeLast().o;
@@ -391,21 +484,12 @@ public class SerializerSession extends BeanSession {
 	}
 
 	/**
-	 * The current indentation depth.
-	 *
-	 * @return The current indentation depth.
-	 */
-	public int getIndent() {
-		return indent;
-	}
-
-	/**
 	 * Specialized warning when an exception is thrown while executing a bean getter.
 	 *
 	 * @param p The bean map entry representing the bean property.
 	 * @param t The throwable that the bean getter threw.
 	 */
-	public void onBeanGetterException(BeanPropertyMeta p, Throwable t) {
+	protected final void onBeanGetterException(BeanPropertyMeta p, Throwable t) {
 		if (listener != null)
 			listener.onBeanGetterException(this, t, p);
 		String prefix = (isDebug() ? getStack(false) + ": " : "");
@@ -420,7 +504,7 @@ public class SerializerSession extends BeanSession {
 	 * @param msg The warning message.
 	 * @param args Optional {@link MessageFormat}-style arguments.
 	 */
-	public final void onError(Throwable t, String msg, Object... args) {
+	protected final void onError(Throwable t, String msg, Object... args) {
 		if (listener != null)
 			listener.onError(this, t, format(msg, args));
 		super.addWarning(msg, args);
@@ -432,7 +516,7 @@ public class SerializerSession extends BeanSession {
 	 * @param o The input string to trim.
 	 * @return The trimmed string, or <jk>null</jk> if the input was <jk>null</jk>.
 	 */
-	public final String trim(Object o) {
+	protected final String trim(Object o) {
 		if (o == null)
 			return null;
 		String s = o.toString();
@@ -450,7 +534,7 @@ public class SerializerSession extends BeanSession {
 	 * @throws SerializeException If a problem occurred trying to convert the output.
 	 */
 	@SuppressWarnings({ "rawtypes", "unchecked" })
-	public final Object generalize(Object o, ClassMeta<?> type) throws SerializeException {
+	protected final Object generalize(Object o, ClassMeta<?> type) throws SerializeException {
 		if (o == null)
 			return null;
 		PojoSwap f = (type == null || type.isObject() ? getClassMeta(o.getClass()).getPojoSwap() : type.getPojoSwap());
@@ -468,7 +552,7 @@ public class SerializerSession extends BeanSession {
 	 * @return <jk>true</jk> if the specified value should not be serialized.
 	 * @throws SerializeException If recursion occurred.
 	 */
-	public final boolean canIgnoreValue(ClassMeta<?> cm, String attrName, Object value) throws SerializeException {
+	protected final boolean canIgnoreValue(ClassMeta<?> cm, String attrName, Object value) throws SerializeException {
 
 		if (trimNulls && value == null)
 			return true;
@@ -509,7 +593,7 @@ public class SerializerSession extends BeanSession {
 	 * @param m The map being sorted.
 	 * @return A new sorted {@link TreeMap}.
 	 */
-	public final <K,V> Map<K,V> sort(Map<K,V> m) {
+	protected final <K,V> Map<K,V> sort(Map<K,V> m) {
 		if (sortMaps && m != null && (! m.isEmpty()) && m.keySet().iterator().next() instanceof Comparable<?>)
 			return new TreeMap<K,V>(m);
 		return m;
@@ -521,13 +605,40 @@ public class SerializerSession extends BeanSession {
 	 * @param c The collection being sorted.
 	 * @return A new sorted {@link TreeSet}.
 	 */
-	public final <E> Collection<E> sort(Collection<E> c) {
+	protected final <E> Collection<E> sort(Collection<E> c) {
 		if (sortCollections && c != null && (! c.isEmpty()) && c.iterator().next() instanceof Comparable<?>)
 			return new TreeSet<E>(c);
 		return c;
 	}
 
 	/**
+	 * Converts the contents of the specified object array to a list.
+	 *
+	 * <p>
+	 * Works on both object and primitive arrays.
+	 *
+	 * <p>
+	 * In the case of multi-dimensional arrays, the outgoing list will contain elements of type n-1 dimension.
+	 * i.e. if {@code type} is <code><jk>int</jk>[][]</code> then {@code list} will have entries of type
+	 * <code><jk>int</jk>[]</code>.
+	 *
+	 * @param type The type of array.
+	 * @param array The array being converted.
+	 * @return The array as a list.
+	 */
+	protected static final List<Object> toList(Class<?> type, Object array) {
+		Class<?> componentType = type.getComponentType();
+		if (componentType.isPrimitive()) {
+			int l = Array.getLength(array);
+			List<Object> list = new ArrayList<Object>(l);
+			for (int i = 0; i < l; i++)
+				list.add(Array.get(array, i));
+			return list;
+		}
+		return Arrays.asList((Object[])array);
+	}
+
+	/**
 	 * Converts a String to an absolute URI based on the {@link UriContext} on this session.
 	 *
 	 * @param uri
@@ -554,7 +665,7 @@ public class SerializerSession extends BeanSession {
 	 * 	</ul>
 	 * @return The resolved URI.
 	 */
-	public String resolveUri(Object uri) {
+	public final String resolveUri(Object uri) {
 		return uriResolver.resolve(uri);
 	}
 
@@ -592,7 +703,7 @@ public class SerializerSession extends BeanSession {
 	 * @param uri The URI to relativize.
 	 * @return The relativized URI.
 	 */
-	public String relativizeUri(Object relativeTo, Object uri) {
+	protected final String relativizeUri(Object relativeTo, Object uri) {
 		return uriResolver.relativize(relativeTo, uri);
 	}
 
@@ -602,7 +713,7 @@ public class SerializerSession extends BeanSession {
 	 * @param o The object to convert to a <code>String</code>.
 	 * @return The
 	 */
-	public String toString(Object o) {
+	public final String toString(Object o) {
 		if (o == null)
 			return null;
 		if (o.getClass() == Class.class)
@@ -613,11 +724,6 @@ public class SerializerSession extends BeanSession {
 		return s;
 	}
 
-	@Override
-	public boolean close() {
-		return super.close();
-	}
-
 	private static class StackElement {
 		private int depth;
 		private String name;
@@ -663,7 +769,7 @@ public class SerializerSession extends BeanSession {
 	 *
 	 * @return A map, typically containing something like <code>{line:123,column:456,currentProperty:"foobar"}</code>
 	 */
-	public Map<String,Object> getLastLocation() {
+	protected final Map<String,Object> getLastLocation() {
 		Map<String,Object> m = new LinkedHashMap<String,Object>();
 		if (currentClass != null)
 			m.put("currentClass", currentClass);
@@ -681,7 +787,7 @@ public class SerializerSession extends BeanSession {
 	 * @param typeName The type name of the bean.
 	 * @return A new bean property value.
 	 */
-	public BeanPropertyValue createBeanTypeNameProperty(BeanMap<?> m, String typeName) {
+	protected final static BeanPropertyValue createBeanTypeNameProperty(BeanMap<?> m, String typeName) {
 		BeanMeta<?> bm = m.getMeta();
 		return new BeanPropertyValue(bm.getTypeProperty(), bm.getTypeProperty().getName(), typeName, null);
 	}
@@ -694,7 +800,7 @@ public class SerializerSession extends BeanSession {
 	 * @param pMeta The current bean property being serialized.
 	 * @return The bean dictionary name, or <jk>null</jk> if a name could not be found.
 	 */
-	public String getBeanTypeName(ClassMeta<?> eType, ClassMeta<?> aType, BeanPropertyMeta pMeta) {
+	protected final String getBeanTypeName(ClassMeta<?> eType, ClassMeta<?> aType, BeanPropertyMeta pMeta) {
 		if (eType == aType)
 			return null;
 
@@ -748,7 +854,25 @@ public class SerializerSession extends BeanSession {
 	 * @param o The object to get the expected type on.
 	 * @return The expected type.
 	 */
-	public ClassMeta<?> getExpectedRootType(Object o) {
+	protected final ClassMeta<?> getExpectedRootType(Object o) {
 		return abridged ? getClassMetaForObject(o) : object();
 	}
+
+	/**
+	 * Optional method that specifies HTTP request headers for this serializer.
+	 *
+	 * <p>
+	 * For example, {@link SoapXmlSerializer} needs to set a <code>SOAPAction</code> header.
+	 *
+	 * <p>
+	 * This method is typically meaningless if the serializer is being used stand-alone (i.e. outside of a REST server
+	 * or client).
+	 *
+	 * @return
+	 * 	The HTTP headers to set on HTTP requests.
+	 * 	Never <jk>null</jk>.
+	 */
+	public Map<String,String> getResponseHeaders() {
+		return Collections.emptyMap();
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSessionArgs.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSessionArgs.java b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSessionArgs.java
new file mode 100644
index 0000000..68077b4
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSessionArgs.java
@@ -0,0 +1,65 @@
+// ***************************************************************************************************************************
+// * 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.serializer;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.http.*;
+
+/**
+ * Runtime arguments common to all serializer sessions.
+ *
+ * <p>
+ * This object specifies information such as session locale or URI context.
+ */
+public final class SerializerSessionArgs extends BeanSessionArgs {
+
+	/**
+	 * Default session arguments.
+	 */
+	public static final SerializerSessionArgs DEFAULT = new SerializerSessionArgs(ObjectMap.EMPTY_MAP, null, null, null, null, null);
+
+	final Method javaMethod;
+	final UriContext uriContext;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param properties
+	 * 	Session-level properties.
+	 * 	These override context-level properties.
+	 * 	Can be <jk>null</jk>.
+	 * @param javaMethod
+	 * 	The java method that called this serializer, usually the method in a REST servlet.
+	 * 	Can be <jk>null</jk>.
+	 * @param locale
+	 * 	The session locale.
+	 * 	If <jk>null</jk>, then the locale defined on the context is used.
+	 * @param timeZone
+	 * 	The session timezone.
+	 * 	If <jk>null</jk>, then the timezone defined on the context is used.
+	 * @param mediaType
+	 * 	The session media type (e.g. <js>"application/json"</js>).
+	 * 	Can be <jk>null</jk>.
+	 * @param uriContext
+	 * 	The URI context.
+	 * 	Identifies the current request URI used for resolution of URIs to absolute or root-relative form.
+	 */
+	public SerializerSessionArgs(ObjectMap properties, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) {
+		super(properties, locale, timeZone, mediaType);
+		this.javaMethod = javaMethod;
+		this.uriContext = uriContext;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java b/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
index fc782d7..11d2138 100644
--- a/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializer.java
@@ -12,29 +12,13 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.serializer;
 
-import java.io.*;
-import java.lang.reflect.*;
-import java.util.*;
-
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
-import org.apache.juneau.http.*;
 import org.apache.juneau.utils.*;
 
 /**
  * Subclass of {@link Serializer} for character-based serializers.
  *
- * <h5 class='section'>Description:</h5>
- *
- * This class is typically the parent class of all character-based serializers.
- * It has 2 abstract methods to implement...
- * <ul class='spaced-list'>
- * 	<li>
- * 		{@link #createSession(ObjectMap, Method, Locale, TimeZone, MediaType, UriContext)}
- * 	<li>
- * 		{@link #doSerialize(SerializerSession, SerializerOutput, Object)}
- * </ul>
- *
  * <h6 class='topic'>@Produces annotation</h6>
  *
  * The media types that this serializer can produce is specified through the {@link Produces @Produces} annotation.
@@ -54,16 +38,24 @@ public abstract class WriterSerializer extends Serializer {
 		super(propertyStore);
 	}
 
-	@Override /* Serializer */
-	public boolean isWriterSerializer() {
-		return true;
-	}
+
+	//--------------------------------------------------------------------------------
+	// Abstract methods
+	//--------------------------------------------------------------------------------
+
+	@Override /* SerializerSession */
+	public abstract WriterSerializerSession createSession(SerializerSessionArgs args);
 
 
 	//--------------------------------------------------------------------------------
 	// Other methods
 	//--------------------------------------------------------------------------------
 
+	@Override /* Serializer */
+	public final boolean isWriterSerializer() {
+		return true;
+	}
+
 	/**
 	 * Convenience method for serializing an object to a <code>String</code>.
 	 *
@@ -71,12 +63,9 @@ public abstract class WriterSerializer extends Serializer {
 	 * @return The output serialized to a string.
 	 * @throws SerializeException If a problem occurred trying to convert the output.
 	 */
-	@Override
+	@Override /* Serializer */
 	public final String serialize(Object o) throws SerializeException {
-		StringWriter w = new StringWriter();
-		SerializerOutput out = new SerializerOutput(w);
-		serialize(createSession(), out, o);
-		return w.toString();
+		return createSession(null).serialize(o);
 	}
 
 	/**
@@ -90,10 +79,7 @@ public abstract class WriterSerializer extends Serializer {
 	 */
 	public final String toString(Object o) {
 		try {
-			StringWriter w = new StringWriter();
-			SerializerOutput out = new SerializerOutput(w);
-			serialize(createSession(), out, o);
-			return w.toString();
+			return serialize(o);
 		} catch (Exception e) {
 			throw new RuntimeException(e);
 		}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializerSession.java
new file mode 100644
index 0000000..b91085f
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/serializer/WriterSerializerSession.java
@@ -0,0 +1,80 @@
+// ***************************************************************************************************************************
+// * 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.serializer;
+
+import java.io.*;
+
+/**
+ * Subclass of {@link SerializerSession} for character-based serializers.
+ *
+ * <h5 class='section'>Description:</h5>
+ *
+ * This class is typically the parent class of all character-based serializers.
+ * <br>It has 1 abstract method to implement...
+ * <ul class='spaced-list'>
+ * 	<li>
+ * 		{@link #doSerialize(SerializerPipe, Object)}
+ * </ul>
+ *
+ * <p>
+ * This class is NOT thread safe.
+ * It is typically discarded after one-time use although it can be reused within the same thread.
+ */
+public abstract class WriterSerializerSession extends SerializerSession {
+
+	/**
+	 * Create a new session using properties specified in the context.
+	 *
+	 * @param ctx
+	 * 	The context creating this session object.
+	 * 	The context contains all the configuration settings for this object.
+	 * @param args
+	 * 	Runtime arguments.
+	 * 	These specify session-level information such as locale and URI context.
+	 * 	It also include session-level properties that override the properties defined on the bean and
+	 * 	serializer contexts.
+	 * 	<br>If <jk>null</jk>, defaults to {@link SerializerSessionArgs#DEFAULT}.
+	 */
+	protected WriterSerializerSession(SerializerContext ctx, SerializerSessionArgs args) {
+		super(ctx, args);
+	}
+
+	/**
+	 * Constructor for sessions that don't require context.
+	 *
+	 * @param args
+	 * 	Runtime session arguments.
+	 */
+	protected WriterSerializerSession(SerializerSessionArgs args) {
+		this(null, args);
+	}
+
+	@Override /* SerializerSession */
+	public final boolean isWriterSerializer() {
+		return true;
+	}
+
+	/**
+	 * Convenience method for serializing an object to a <code>String</code>.
+	 *
+	 * @param o The object to serialize.
+	 * @return The output serialized to a string.
+	 * @throws SerializeException If a problem occurred trying to convert the output.
+	 */
+	@Override /* SerializerSession */
+	public final String serialize(Object o) throws SerializeException {
+		StringWriter w = new StringWriter();
+		serialize(w, o);
+		return w.toString();
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
index c9053a4..77f722b 100644
--- a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
@@ -12,8 +12,6 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.soap;
 
-import static org.apache.juneau.soap.SoapXmlSerializerContext.*;
-
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
 import org.apache.juneau.serializer.*;
@@ -44,6 +42,8 @@ import org.apache.juneau.xml.*;
 @Produces(value="text/xml+soap",contentType="text/xml")
 public final class SoapXmlSerializer extends XmlSerializer {
 
+	private final SoapXmlSerializerContext ctx;
+
 	/**
 	 * Constructor.
 	 *
@@ -51,35 +51,11 @@ public final class SoapXmlSerializer extends XmlSerializer {
 	 */
 	public SoapXmlSerializer(PropertyStore propertyStore) {
 		super(propertyStore);
-	}
-
-
-	//--------------------------------------------------------------------------------
-	// Overridden methods
-	//--------------------------------------------------------------------------------
-
-	@Override /* Serializer */
-	protected void doSerialize(SerializerSession session, SerializerOutput out, Object o) throws Exception {
-		XmlSerializerSession s = (XmlSerializerSession)session;
-		XmlWriter w = s.getXmlWriter(out);
-		w.append("<?xml")
-			.attr("version", "1.0")
-			.attr("encoding", "UTF-8")
-			.appendln("?>");
-		w.oTag("soap", "Envelope")
-			.attr("xmlns", "soap", s.getProperty(SOAPXML_SOAPAction, "http://www.w3.org/2003/05/soap-envelope"))
-			.appendln(">");
-		w.sTag(1, "soap", "Body").nl(1);
-		s.indent += 2;
-		w.flush();
-		super.doSerialize(s, out, o);
-		w.ie(1).eTag("soap", "Body").nl(1);
-		w.eTag("soap", "Envelope").nl(0);
+		this.ctx = createContext(SoapXmlSerializerContext.class);
 	}
 
 	@Override /* Serializer */
-	public ObjectMap getResponseHeaders(ObjectMap properties) {
-		return new ObjectMap(super.getResponseHeaders(properties))
-			.append("SOAPAction", properties.getString(SOAPXML_SOAPAction, "http://www.w3.org/2003/05/soap-envelope"));
+	public WriterSerializerSession createSession(SerializerSessionArgs args) {
+		return new SoapXmlSerializerSession(ctx, args);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java
index e6eb5d6..77f64cc 100644
--- a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java
+++ b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerContext.java
@@ -12,6 +12,8 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.soap;
 
+import org.apache.juneau.*;
+import org.apache.juneau.xml.*;
 
 /**
  * Properties associated with the {@link SoapXmlSerializer} class.
@@ -40,7 +42,19 @@ package org.apache.juneau.soap;
  * 	</li>
  * </ul>
  */
-public final class SoapXmlSerializerContext {
+public final class SoapXmlSerializerContext extends XmlSerializerContext {
+
+	/**
+	 * Constructor
+	 *
+	 * <p>
+	 * Typically only called from {@link PropertyStore#getContext(Class)}.
+	 *
+	 * @param ps The property store that created this context.
+	 */
+	public SoapXmlSerializerContext(PropertyStore ps) {
+		super(ps);
+	}
 
 	/**
 	 * <b>Configuration property:</b>  The <code>SOAPAction</code> HTTP header value to set on responses.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerSession.java
new file mode 100644
index 0000000..3960607
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializerSession.java
@@ -0,0 +1,75 @@
+// ***************************************************************************************************************************
+// * 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.soap;
+
+import static org.apache.juneau.soap.SoapXmlSerializerContext.*;
+
+import java.util.*;
+
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.utils.*;
+import org.apache.juneau.xml.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link SoapXmlSerializer}.
+ *
+ * <p>
+ * This class is NOT thread safe.
+ * It is typically discarded after one-time use although it can be reused within the same thread.
+ */
+public class SoapXmlSerializerSession extends XmlSerializerSession {
+
+	/**
+	 * Create a new session using properties specified in the context.
+	 *
+	 * @param ctx
+	 * 	The context creating this session object.
+	 * 	The context contains all the configuration settings for this object.
+	 * @param args
+	 * 	Runtime arguments.
+	 * 	These specify session-level information such as locale and URI context.
+	 * 	It also include session-level properties that override the properties defined on the bean and
+	 * 	serializer contexts.
+	 * 	<br>If <jk>null</jk>, defaults to {@link SerializerSessionArgs#DEFAULT}.
+	 */
+	public SoapXmlSerializerSession(SoapXmlSerializerContext ctx, SerializerSessionArgs args) {
+		super(ctx, args);
+	}
+
+	//--------------------------------------------------------------------------------
+	// Overridden methods
+	//--------------------------------------------------------------------------------
+
+	@Override /* SerializerSession */
+	protected void doSerialize(SerializerPipe out, Object o) throws Exception {
+		XmlWriter w = getXmlWriter(out);
+		w.append("<?xml")
+			.attr("version", "1.0")
+			.attr("encoding", "UTF-8")
+			.appendln("?>");
+		w.oTag("soap", "Envelope")
+			.attr("xmlns", "soap", getProperty(SOAPXML_SOAPAction, "http://www.w3.org/2003/05/soap-envelope"))
+			.appendln(">");
+		w.sTag(1, "soap", "Body").nl(1);
+		indent += 2;
+		w.flush();
+		super.doSerialize(out, o);
+		w.ie(1).eTag("soap", "Body").nl(1);
+		w.eTag("soap", "Envelope").nl(0);
+	}
+
+	@Override /* Serializer */
+	public Map<String,String> getResponseHeaders() {
+		return new AMap<String,String>().append("SOAPAction", getProperty(SOAPXML_SOAPAction, "http://www.w3.org/2003/05/soap-envelope"));
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java b/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java
index cc8a19c..7410a7d 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java
@@ -13,17 +13,10 @@
 package org.apache.juneau.uon;
 
 import static org.apache.juneau.uon.UonParserContext.*;
-import static org.apache.juneau.internal.StringUtils.*;
-
-import java.lang.reflect.*;
-import java.util.*;
 
 import org.apache.juneau.*;
 import org.apache.juneau.annotation.*;
-import org.apache.juneau.http.*;
-import org.apache.juneau.internal.*;
 import org.apache.juneau.parser.*;
-import org.apache.juneau.transform.*;
 
 /**
  * Parses UON (a notation for URL-encoded query parameter values) text into POJO models.
@@ -45,7 +38,6 @@ import org.apache.juneau.transform.*;
  * 	<li>{@link BeanContext}
  * </ul>
  */
-@SuppressWarnings({ "rawtypes", "unchecked" })
 @Consumes("text/uon")
 public class UonParser extends ReaderParser {
 
@@ -55,11 +47,6 @@ public class UonParser extends ReaderParser {
 	/** Reusable instance of {@link UonParser} with decodeChars set to true. */
 	public static final UonParser DEFAULT_DECODING = new UonParser.Decoding(PropertyStore.create());
 
-	// Characters that need to be preceeded with an escape character.
-	private static final AsciiSet escapedChars = new AsciiSet("~'\u0001\u0002");
-
-	private static final char AMP='\u0001', EQ='\u0002';  // Flags set in reader to denote & and = characters.
-
 
 	/** Default parser, decoding. */
 	public static class Decoding extends UonParser {
@@ -70,12 +57,7 @@ public class UonParser extends ReaderParser {
 		 * @param propertyStore The property store containing all the settings for this object.
 		 */
 		public Decoding(PropertyStore propertyStore) {
-			super(propertyStore);
-		}
-
-		@Override /* CoreObject */
-		protected ObjectMap getOverrideProperties() {
-			return super.getOverrideProperties().append(UON_decodeChars, true);
+			super(propertyStore.copy().append(UON_decodeChars, true));
 		}
 	}
 
@@ -98,696 +80,16 @@ public class UonParser extends ReaderParser {
 	}
 
 	/**
-	 * Workhorse method.
-	 *
-	 * @param session The parser context for this parse.
-	 * @param eType The class type being parsed, or <jk>null</jk> if unknown.
-	 * @param r The reader being parsed.
-	 * @param outer The outer object (for constructing nested inner classes).
-	 * @param isUrlParamValue
-	 * 	If <jk>true</jk>, then we're parsing a top-level URL-encoded value which is treated a bit different than the
-	 * 	default case.
-	 * @param pMeta The current bean property being parsed.
-	 * @return The parsed object.
-	 * @throws Exception
-	 */
-	protected <T> T parseAnything(UonParserSession session, ClassMeta<T> eType, ParserReader r, Object outer,
-			boolean isUrlParamValue, BeanPropertyMeta pMeta) throws Exception {
-
-		if (eType == null)
-			eType = (ClassMeta<T>)object();
-		PojoSwap<T,Object> transform = (PojoSwap<T,Object>)eType.getPojoSwap();
-		ClassMeta<?> sType = eType.getSerializedClassMeta();
-
-		Object o = null;
-
-		int c = r.peekSkipWs();
-
-		if (c == -1 || c == AMP) {
-			// If parameter is blank and it's an array or collection, return an empty list.
-			if (sType.isCollectionOrArray())
-				o = sType.newInstance();
-			else if (sType.isString() || sType.isObject())
-				o = "";
-			else if (sType.isPrimitive())
-				o = sType.getPrimitiveDefault();
-			// Otherwise, leave null.
-		} else if (sType.isVoid()) {
-			String s = parseString(session, r, isUrlParamValue);
-			if (s != null)
-				throw new ParseException(session, "Expected ''null'' for void value, but was ''{0}''.", s);
-		} else if (sType.isObject()) {
-			if (c == '(') {
-				ObjectMap m = new ObjectMap(session);
-				parseIntoMap(session, r, m, string(), object(), pMeta);
-				o = session.cast(m, pMeta, eType);
-			} else if (c == '@') {
-				Collection l = new ObjectList(session);
-				o = parseIntoCollection(session, r, l, sType, isUrlParamValue, pMeta);
-			} else {
-				String s = parseString(session, r, isUrlParamValue);
-				if (c != '\'') {
-					if ("true".equals(s) || "false".equals(s))
-						o = Boolean.valueOf(s);
-					else if (! "null".equals(s)) {
-						if (isNumeric(s))
-							o = StringUtils.parseNumber(s, Number.class);
-						else
-							o = s;
-					}
-				} else {
-					o = s;
-				}
-			}
-		} else if (sType.isBoolean()) {
-			o = parseBoolean(session, r);
-		} else if (sType.isCharSequence()) {
-			o = parseString(session, r, isUrlParamValue);
-		} else if (sType.isChar()) {
-			String s = parseString(session, r, isUrlParamValue);
-			o = s == null ? null : s.charAt(0);
-		} else if (sType.isNumber()) {
-			o = parseNumber(session, r, (Class<? extends Number>)sType.getInnerClass());
-		} else if (sType.isMap()) {
-			Map m = (sType.canCreateNewInstance(outer) ? (Map)sType.newInstance(outer) : new ObjectMap(session));
-			o = parseIntoMap(session, r, m, sType.getKeyType(), sType.getValueType(), pMeta);
-		} else if (sType.isCollection()) {
-			if (c == '(') {
-				ObjectMap m = new ObjectMap(session);
-				parseIntoMap(session, r, m, string(), object(), pMeta);
-				// Handle case where it's a collection, but serialized as a map with a _type or _value key.
-				if (m.containsKey(session.getBeanTypePropertyName(sType)))
-					o = session.cast(m, pMeta, eType);
-				// Handle case where it's a collection, but only a single value was specified.
-				else {
-					Collection l = (
-						sType.canCreateNewInstance(outer)
-						? (Collection)sType.newInstance(outer)
-						: new ObjectList(session)
-					);
-					l.add(m.cast(sType.getElementType()));
-					o = l;
-				}
-			} else {
-				Collection l = (
-					sType.canCreateNewInstance(outer)
-					? (Collection)sType.newInstance(outer)
-					: new ObjectList(session)
-				);
-				o = parseIntoCollection(session, r, l, sType, isUrlParamValue, pMeta);
-			}
-		} else if (sType.canCreateNewBean(outer)) {
-			BeanMap m = session.newBeanMap(outer, sType.getInnerClass());
-			m = parseIntoBeanMap(session, r, m);
-			o = m == null ? null : m.getBean();
-		} else if (sType.canCreateNewInstanceFromString(outer)) {
-			String s = parseString(session, r, isUrlParamValue);
-			if (s != null)
-				o = sType.newInstanceFromString(outer, s);
-		} else if (sType.canCreateNewInstanceFromNumber(outer)) {
-			o = sType.newInstanceFromNumber(session, outer, parseNumber(session, r, sType.getNewInstanceFromNumberClass()));
-		} else if (sType.isArray() || sType.isArgs()) {
-			if (c == '(') {
-				ObjectMap m = new ObjectMap(session);
-				parseIntoMap(session, r, m, string(), object(), pMeta);
-				// Handle case where it's an array, but serialized as a map with a _type or _value key.
-				if (m.containsKey(session.getBeanTypePropertyName(sType)))
-					o = session.cast(m, pMeta, eType);
-				// Handle case where it's an array, but only a single value was specified.
-				else {
-					ArrayList l = new ArrayList(1);
-					l.add(m.cast(sType.getElementType()));
-					o = session.toArray(sType, l);
-				}
-			} else {
-				ArrayList l = (ArrayList)parseIntoCollection(session, r, new ArrayList(), sType, isUrlParamValue, pMeta);
-				o = session.toArray(sType, l);
-			}
-		} else if (c == '(') {
-			// It could be a non-bean with _type attribute.
-			ObjectMap m = new ObjectMap(session);
-			parseIntoMap(session, r, m, string(), object(), pMeta);
-			if (m.containsKey(session.getBeanTypePropertyName(sType)))
-				o = session.cast(m, pMeta, eType);
-			else
-				throw new ParseException(session, "Class ''{0}'' could not be instantiated.  Reason: ''{1}''",
-					sType.getInnerClass().getName(), sType.getNotABeanReason());
-		} else if (c == 'n') {
-			r.read();
-			parseNull(session, r);
-		} else {
-			throw new ParseException(session, "Class ''{0}'' could not be instantiated.  Reason: ''{1}''",
-				sType.getInnerClass().getName(), sType.getNotABeanReason());
-		}
-
-		if (o == null && sType.isPrimitive())
-			o = sType.getPrimitiveDefault();
-		if (transform != null && o != null)
-			o = transform.unswap(session, o, eType);
-
-		if (outer != null)
-			setParent(eType, o, outer);
-
-		return (T)o;
-	}
-
-	private <K,V> Map<K,V> parseIntoMap(UonParserSession session, ParserReader r, Map<K,V> m, ClassMeta<K> keyType,
-			ClassMeta<V> valueType, BeanPropertyMeta pMeta) throws Exception {
-
-		if (keyType == null)
-			keyType = (ClassMeta<K>)string();
-
-		int c = r.read();
-		if (c == -1 || c == AMP)
-			return null;
-		if (c == 'n')
-			return (Map<K,V>)parseNull(session, r);
-		if (c != '(')
-			throw new ParseException(session, "Expected '(' at beginning of object.");
-
-		final int S1=1; // Looking for attrName start.
-		final int S2=2; // Found attrName end, looking for =.
-		final int S3=3; // Found =, looking for valStart.
-		final int S4=4; // Looking for , or )
-		boolean isInEscape = false;
-
-		int state = S1;
-		K currAttr = null;
-		while (c != -1 && c != AMP) {
-			c = r.read();
-			if (! isInEscape) {
-				if (state == S1) {
-					if (c == ')')
-						return m;
-					if (Character.isWhitespace(c))
-						skipSpace(r);
-					else {
-						r.unread();
-						Object attr = parseAttr(session, r, session.isDecodeChars());
-						currAttr = attr == null ? null : convertAttrToType(session, m, session.trim(attr.toString()), keyType);
-						state = S2;
-						c = 0; // Avoid isInEscape if c was '\'
-					}
-				} else if (state == S2) {
-					if (c == EQ || c == '=')
-						state = S3;
-					else if (c == -1 || c == ',' || c == ')' || c == AMP) {
-						if (currAttr == null) {
-							// Value was '%00'
-							r.unread();
-							return null;
-						}
-						m.put(currAttr, null);
-						if (c == ')' || c == -1 || c == AMP)
-							return m;
-						state = S1;
-					}
-				} else if (state == S3) {
-					if (c == -1 || c == ',' || c == ')' || c == AMP) {
-						V value = convertAttrToType(session, m, "", valueType);
-						m.put(currAttr, value);
-						if (c == -1 || c == ')' || c == AMP)
-							return m;
-						state = S1;
-					} else  {
-						V value = parseAnything(session, valueType, r.unread(), m, false, pMeta);
-						setName(valueType, value, currAttr);
-						m.put(currAttr, value);
-						state = S4;
-						c = 0; // Avoid isInEscape if c was '\'
-					}
-				} else if (state == S4) {
-					if (c == ',')
-						state = S1;
-					else if (c == ')' || c == -1 || c == AMP) {
-						return m;
-					}
-				}
-			}
-			isInEscape = isInEscape(c, r, isInEscape);
-		}
-		if (state == S1)
-			throw new ParseException(session, "Could not find attribute name on object.");
-		if (state == S2)
-			throw new ParseException(session, "Could not find '=' following attribute name on object.");
-		if (state == S3)
-			throw new ParseException(session, "Dangling '=' found in object entry");
-		if (state == S4)
-			throw new ParseException(session, "Could not find ')' marking end of object.");
-
-		return null; // Unreachable.
-	}
-
-	private <E> Collection<E> parseIntoCollection(UonParserSession session, ParserReader r, Collection<E> l,
-			ClassMeta<E> type, boolean isUrlParamValue, BeanPropertyMeta pMeta) throws Exception {
-
-		int c = r.readSkipWs();
-		if (c == -1 || c == AMP)
-			return null;
-		if (c == 'n')
-			return (Collection<E>)parseNull(session, r);
-
-		int argIndex = 0;
-
-		// If we're parsing a top-level parameter, we're allowed to have comma-delimited lists outside parenthesis (e.g. "&foo=1,2,3&bar=a,b,c")
-		// This is not allowed at lower levels since we use comma's as end delimiters.
-		boolean isInParens = (c == '@');
-		if (! isInParens) {
-			if (isUrlParamValue)
-				r.unread();
-			else
-				throw new ParseException(session, "Could not find '(' marking beginning of collection.");
-		} else {
-			r.read();
-		}
-
-		if (isInParens) {
-			final int S1=1; // Looking for starting of first entry.
-			final int S2=2; // Looking for starting of subsequent entries.
-			final int S3=3; // Looking for , or ) after first entry.
-
-			int state = S1;
-			while (c != -1 && c != AMP) {
-				c = r.read();
-				if (state == S1 || state == S2) {
-					if (c == ')') {
-						if (state == S2) {
-							l.add((E)parseAnything(session, type.isArgs() ? type.getArg(argIndex++) : type.getElementType(),
-									r.unread(), l, false, pMeta));
-							r.read();
-						}
-						return l;
-					} else if (Character.isWhitespace(c)) {
-						skipSpace(r);
-					} else {
-						l.add((E)parseAnything(session, type.isArgs() ? type.getArg(argIndex++) : type.getElementType(),
-								r.unread(), l, false, pMeta));
-						state = S3;
-					}
-				} else if (state == S3) {
-					if (c == ',') {
-						state = S2;
-					} else if (c == ')') {
-						return l;
-					}
-				}
-			}
-			if (state == S1 || state == S2)
-				throw new ParseException(session, "Could not find start of entry in array.");
-			if (state == S3)
-				throw new ParseException(session, "Could not find end of entry in array.");
-
-		} else {
-			final int S1=1; // Looking for starting of entry.
-			final int S2=2; // Looking for , or & or END after first entry.
-
-			int state = S1;
-			while (c != -1 && c != AMP) {
-				c = r.read();
-				if (state == S1) {
-					if (Character.isWhitespace(c)) {
-						skipSpace(r);
-					} else {
-						l.add((E)parseAnything(session, type.isArgs() ? type.getArg(argIndex++) : type.getElementType(),
-								r.unread(), l, false, pMeta));
-						state = S2;
-					}
-				} else if (state == S2) {
-					if (c == ',') {
-						state = S1;
-					} else if (Character.isWhitespace(c)) {
-						skipSpace(r);
-					} else if (c == AMP || c == -1) {
-						r.unread();
-						return l;
-					}
-				}
-			}
-		}
-
-		return null;  // Unreachable.
-	}
-
-	private <T> BeanMap<T> parseIntoBeanMap(UonParserSession session, ParserReader r, BeanMap<T> m) throws Exception {
-
-		int c = r.readSkipWs();
-		if (c == -1 || c == AMP)
-			return null;
-		if (c == 'n')
-			return (BeanMap<T>)parseNull(session, r);
-		if (c != '(')
-			throw new ParseException(session, "Expected '(' at beginning of object.");
-
-		final int S1=1; // Looking for attrName start.
-		final int S2=2; // Found attrName end, looking for =.
-		final int S3=3; // Found =, looking for valStart.
-		final int S4=4; // Looking for , or }
-		boolean isInEscape = false;
-
-		int state = S1;
-		String currAttr = "";
-		int currAttrLine = -1, currAttrCol = -1;
-		while (c != -1 && c != AMP) {
-			c = r.read();
-			if (! isInEscape) {
-				if (state == S1) {
-					if (c == ')' || c == -1 || c == AMP) {
-						return m;
-					}
-					if (Character.isWhitespace(c))
-						skipSpace(r);
-					else {
-						r.unread();
-						currAttrLine= r.getLine();
-						currAttrCol = r.getColumn();
-						currAttr = parseAttrName(session, r, session.isDecodeChars());
-						if (currAttr == null)  // Value was '%00'
-							return null;
-						state = S2;
-					}
-				} else if (state == S2) {
-					if (c == EQ || c == '=')
-						state = S3;
-					else if (c == -1 || c == ',' || c == ')' || c == AMP) {
-						m.put(currAttr, null);
-						if (c == ')' || c == -1 || c == AMP)
-							return m;
-						state = S1;
-					}
-				} else if (state == S3) {
-					if (c == -1 || c == ',' || c == ')' || c == AMP) {
-						if (! currAttr.equals(session.getBeanTypePropertyName(m.getClassMeta()))) {
-							BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr);
-							if (pMeta == null) {
-								session.onUnknownProperty(currAttr, m, currAttrLine, currAttrCol);
-							} else {
-								Object value = session.convertToType("", pMeta.getClassMeta());
-								pMeta.set(m, currAttr, value);
-							}
-						}
-						if (c == -1 || c == ')' || c == AMP)
-							return m;
-						state = S1;
-					} else {
-						if (! currAttr.equals(session.getBeanTypePropertyName(m.getClassMeta()))) {
-							BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr);
-							if (pMeta == null) {
-								session.onUnknownProperty(currAttr, m, currAttrLine, currAttrCol);
-								parseAnything(session, object(), r.unread(), m.getBean(false), false, null); // Read content anyway to ignore it
-							} else {
-								session.setCurrentProperty(pMeta);
-								ClassMeta<?> cm = pMeta.getClassMeta();
-								Object value = parseAnything(session, cm, r.unread(), m.getBean(false), false, pMeta);
-								setName(cm, value, currAttr);
-								pMeta.set(m, currAttr, value);
-								session.setCurrentProperty(null);
-							}
-						}
-						state = S4;
-					}
-				} else if (state == S4) {
-					if (c == ',')
-						state = S1;
-					else if (c == ')' || c == -1 || c == AMP) {
-						return m;
-					}
-				}
-			}
-			isInEscape = isInEscape(c, r, isInEscape);
-		}
-		if (state == S1)
-			throw new ParseException(session, "Could not find attribute name on object.");
-		if (state == S2)
-			throw new ParseException(session, "Could not find '=' following attribute name on object.");
-		if (state == S3)
-			throw new ParseException(session, "Could not find value following '=' on object.");
-		if (state == S4)
-			throw new ParseException(session, "Could not find ')' marking end of object.");
-
-		return null; // Unreachable.
-	}
-
-	private static Object parseNull(UonParserSession session, ParserReader r) throws Exception {
-		String s = parseString(session, r, false);
-		if ("ull".equals(s))
-			return null;
-		throw new ParseException(session, "Unexpected character sequence: ''{0}''", s);
-	}
-
-	/**
-	 * Convenience method for parsing an attribute from the specified parser.
-	 *
-	 * @param session
-	 * @param r
-	 * @param encoded
-	 * @return The parsed object
-	 * @throws Exception
-	 */
-	protected static final Object parseAttr(UonParserSession session, ParserReader r, boolean encoded) throws Exception {
-		Object attr;
-		attr = parseAttrName(session, r, encoded);
-		return attr;
-	}
-
-	/**
-	 * Parses an attribute name from the specified reader.
-	 *
-	 * @param session
-	 * @param r
-	 * @param encoded
-	 * @return The parsed attribute name.
-	 * @throws Exception
-	 */
-	protected static String parseAttrName(UonParserSession session, ParserReader r, boolean encoded) throws Exception {
-
-		// If string is of form 'xxx', we're looking for ' at the end.
-		// Otherwise, we're looking for '&' or '=' or WS or -1 denoting the end of this string.
-
-		int c = r.peekSkipWs();
-		if (c == '\'')
-			return parsePString(session, r);
-
-		r.mark();
-		boolean isInEscape = false;
-		if (encoded) {
-			while (c != -1) {
-				c = r.read();
-				if (! isInEscape) {
-					if (c == AMP || c == EQ || c == -1 || Character.isWhitespace(c)) {
-						if (c != -1)
-							r.unread();
-						String s = r.getMarked();
-						return ("null".equals(s) ? null : s);
-					}
-				}
-				else if (c == AMP)
-					r.replace('&');
-				else if (c == EQ)
-					r.replace('=');
-				isInEscape = isInEscape(c, r, isInEscape);
-			}
-		} else {
-			while (c != -1) {
-				c = r.read();
-				if (! isInEscape) {
-					if (c == '=' || c == -1 || Character.isWhitespace(c)) {
-						if (c != -1)
-							r.unread();
-						String s = r.getMarked();
-						return ("null".equals(s) ? null : session.trim(s));
-					}
-				}
-				isInEscape = isInEscape(c, r, isInEscape);
-			}
-		}
-
-		// We should never get here.
-		throw new ParseException(session, "Unexpected condition.");
-	}
-
-
-	/**
-	 * Returns true if the next character in the stream is preceded by an escape '~' character.
-	 *
-	 * @param c The current character.
-	 * @param r The reader.
-	 * @param prevIsInEscape What the flag was last time.
-	 */
-	private static final boolean isInEscape(int c, ParserReader r, boolean prevIsInEscape) throws Exception {
-		if (c == '~' && ! prevIsInEscape) {
-			c = r.peek();
-			if (escapedChars.contains(c)) {
-				r.delete();
-				return true;
-			}
-		}
-		return false;
-	}
-
-	/**
-	 * Parses a string value from the specified reader.
-	 *
-	 * @param session
-	 * @param r
-	 * @param isUrlParamValue
-	 * @return The parsed string.
-	 * @throws Exception
-	 */
-	protected static String parseString(UonParserSession session, ParserReader r, boolean isUrlParamValue) throws Exception {
-
-		// If string is of form 'xxx', we're looking for ' at the end.
-		// Otherwise, we're looking for ',' or ')' or -1 denoting the end of this string.
-
-		int c = r.peekSkipWs();
-		if (c == '\'')
-			return parsePString(session, r);
-
-		r.mark();
-		boolean isInEscape = false;
-		String s = null;
-		AsciiSet endChars = (isUrlParamValue ? endCharsParam : endCharsNormal);
-		while (c != -1) {
-			c = r.read();
-			if (! isInEscape) {
-				// If this is a URL parameter value, we're looking for:  &
-				// If not, we're looking for:  &,)
-				if (endChars.contains(c)) {
-					r.unread();
-					c = -1;
-				}
-			}
-			if (c == -1)
-				s = r.getMarked();
-			else if (c == EQ)
-				r.replace('=');
-			else if (Character.isWhitespace(c) && ! isUrlParamValue) {
-				s = r.getMarked(0, -1);
-				skipSpace(r);
-				c = -1;
-			}
-			isInEscape = isInEscape(c, r, isInEscape);
-		}
-
-		if (isUrlParamValue)
-			s = trim(s);
-
-		return ("null".equals(s) ? null : session.trim(s));
-	}
-
-	private static final AsciiSet endCharsParam = new AsciiSet(""+AMP), endCharsNormal = new AsciiSet(",)"+AMP);
-
-
-	/*=
-	 * Parses a string of the form "'foo'"
-	 * All whitespace within parenthesis are preserved.
-	 */
-	static String parsePString(UonParserSession session, ParserReader r) throws Exception {
-
-		r.read(); // Skip first quote.
-		r.mark();
-		int c = 0;
-
-		boolean isInEscape = false;
-		while (c != -1) {
-			c = r.read();
-			if (! isInEscape) {
-				if (c == '\'')
-					return session.trim(r.getMarked(0, -1));
-			}
-			if (c == EQ)
-				r.replace('=');
-			isInEscape = isInEscape(c, r, isInEscape);
-		}
-		throw new ParseException(session, "Unmatched parenthesis");
-	}
-
-	private static Boolean parseBoolean(UonParserSession session, ParserReader r) throws Exception {
-		String s = parseString(session, r, false);
-		if (s == null || s.equals("null"))
-			return null;
-		if (s.equals("true"))
-			return true;
-		if (s.equals("false"))
-			return false;
-		throw new ParseException(session, "Unrecognized syntax for boolean.  ''{0}''.", s);
-	}
-
-	private static Number parseNumber(UonParserSession session, ParserReader r, Class<? extends Number> c) throws Exception {
-		String s = parseString(session, r, false);
-		if (s == null)
-			return null;
-		return StringUtils.parseNumber(s, c);
-	}
-
-	/*
-	 * Call this method after you've finished a parsing a string to make sure that if there's any
-	 * remainder in the input, that it consists only of whitespace and comments.
-	 */
-	private static void validateEnd(UonParserSession session, ParserReader r) throws Exception {
-		while (true) {
-			int c = r.read();
-			if (c == -1)
-				return;
-			if (! Character.isWhitespace(c))
-				throw new ParseException(session, "Remainder after parse: ''{0}''.", (char)c);
-		}
-	}
-
-	private static void skipSpace(ParserReader r) throws Exception {
-		int c = 0;
-		while ((c = r.read()) != -1) {
-			if (c <= 2 || ! Character.isWhitespace(c)) {
-				r.unread();
-				return;
-			}
-		}
-	}
-
-	/**
 	 * Create a UON parser session for parsing parameter values.
 	 *
-	 * @param input
 	 * @return A new parser session.
 	 */
-	protected final UonParserSession createParameterSession(Object input) {
-		return new UonParserSession(ctx, input);
+	protected final UonParserSession createParameterSession() {
+		return new UonParserSession(ctx);
 	}
 
-
-	//--------------------------------------------------------------------------------
-	// Entry point methods
-	//--------------------------------------------------------------------------------
-
 	@Override /* Parser */
-	public UonParserSession createSession(Object input, ObjectMap op, Method javaMethod, Object outer, Locale locale,
-			TimeZone timeZone, MediaType mediaType) {
-		return new UonParserSession(ctx, op, input, javaMethod, outer, locale, timeZone, mediaType);
-	}
-
-	@Override /* Parser */
-	protected <T> T doParse(ParserSession session, ClassMeta<T> type) throws Exception {
-		UonParserSession s = (UonParserSession)session;
-		UonReader r = s.getReader();
-		T o = parseAnything(s, type, r, s.getOuter(), true, null);
-		validateEnd(s, r);
-		return o;
-	}
-
-	@Override /* ReaderParser */
-	protected <K,V> Map<K,V> doParseIntoMap(ParserSession session, Map<K,V> m, Type keyType, Type valueType) throws Exception {
-		UonParserSession s = (UonParserSession)session;
-		UonReader r = s.getReader();
-		m = parseIntoMap(s, r, m, (ClassMeta<K>)session.getClassMeta(keyType), (ClassMeta<V>)session.getClassMeta(valueType), null);
-		validateEnd(s, r);
-		return m;
-	}
-
-	@Override /* ReaderParser */
-	protected <E> Collection<E> doParseIntoCollection(ParserSession session, Collection<E> c, Type elementType) throws Exception {
-		UonParserSession s = (UonParserSession)session;
-		UonReader r = s.getReader();
-		c = parseIntoCollection(s, r, c, (ClassMeta<E>)session.getClassMeta(elementType), false, null);
-		validateEnd(s, r);
-		return c;
+	public UonParserSession createSession(ParserSessionArgs args) {
+		return new UonParserSession(ctx, args);
 	}
 }


Mime
View raw message