juneau-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamesbog...@apache.org
Subject [08/12] incubator-juneau git commit: Allow serializer and parser sessions to be reused.
Date Fri, 28 Jul 2017 18:22:23 GMT
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java
index 17571c0..6cb332a 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParserSession.java
@@ -12,23 +12,28 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.json;
 
+import static org.apache.juneau.internal.StringUtils.*;
+
 import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.http.*;
+import org.apache.juneau.internal.*;
 import org.apache.juneau.parser.*;
+import org.apache.juneau.transform.*;
 
 /**
  * Session object that lives for the duration of a single use of {@link JsonParser}.
  *
  * <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 against multiple inputs.
  */
-public final class JsonParserSession extends ParserSession {
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public final class JsonParserSession extends ReaderParserSession {
 
-	private ParserReader reader;
+	private static final AsciiSet decChars = new AsciiSet("0123456789");
 
 	/**
 	 * Create a new session using properties specified in the context.
@@ -36,46 +41,11 @@ public final class JsonParserSession extends ParserSession {
 	 * @param ctx
 	 * 	The context creating this session object.
 	 * 	The context contains all the configuration settings for this object.
-	 * @param input
-	 * 	The input.
-	 * 	Can be any of the following types:
-	 * 	<ul>
-	 * 		<li><jk>null</jk>
-	 * 		<li>{@link Reader}
-	 * 		<li>{@link CharSequence}
-	 * 		<li>{@link InputStream} containing UTF-8 encoded text.
-	 * 		<li>{@link File} containing system encoded text.
-	 * 	</ul>
-	 * @param op
-	 * 	The override properties.
-	 * 	These override any context properties defined in the context.
-	 * @param javaMethod The java method that called this parser, usually the method in a REST servlet.
-	 * @param outer The outer object for instantiating top-level non-static inner classes.
-	 * @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 args
+	 * 	Runtime session arguments.
 	 */
-	public JsonParserSession(JsonParserContext ctx, ObjectMap op, Object input, Method javaMethod, Object outer,
-			Locale locale, TimeZone timeZone, MediaType mediaType) {
-		super(ctx, op, input, javaMethod, outer, locale, timeZone, mediaType);
-	}
-
-	@Override /* ParserSession */
-	public ParserReader getReader() throws Exception {
-		if (reader == null) {
-			Object input = getInput();
-			if (input == null)
-				return null;
-			if (input instanceof CharSequence)
-				reader = new ParserReader((CharSequence)input);
-			else
-				reader = new ParserReader(super.getReader());
-		}
-		return reader;
+	protected JsonParserSession(JsonParserContext ctx, ParserSessionArgs args) {
+		super(ctx, args);
 	}
 
 	/**
@@ -89,7 +59,7 @@ public final class JsonParserSession extends ParserSession {
 	 * @param cp The codepoint.
 	 * @return <jk>true</jk> if the specified character is whitespace.
 	 */
-	public final boolean isWhitespace(int cp) {
+	protected final boolean isWhitespace(int cp) {
 		if (isStrict())
 				return cp <= 0x20 && (cp == 0x09 || cp == 0x0A || cp == 0x0D || cp == 0x20);
 		return Character.isWhitespace(cp);
@@ -101,21 +71,677 @@ public final class JsonParserSession extends ParserSession {
 	 * @param cp The codepoint.
 	 * @return <jk>true</jk> if the specified character is whitespace or '/'.
 	 */
-	public final boolean isCommentOrWhitespace(int cp) {
+	protected final boolean isCommentOrWhitespace(int cp) {
 		if (cp == '/')
 			return true;
 		if (isStrict())
 			return cp <= 0x20 && (cp == 0x09 || cp == 0x0A || cp == 0x0D || cp == 0x20);
-	return Character.isWhitespace(cp);
+		return Character.isWhitespace(cp);
 	}
 
 	@Override /* ParserSession */
-	public Map<String,Object> getLastLocation() {
-		Map<String,Object> m = super.getLastLocation();
-		if (reader != null) {
-			m.put("line", reader.getLine());
-			m.put("column", reader.getColumn());
-		}
+	protected <T> T doParse(ParserPipe pipe, ClassMeta<T> type) throws Exception {
+		ParserReader r = pipe.getParserReader();
+		if (r == null)
+			return null;
+		T o = parseAnything(type, r, getOuter(), null);
+		validateEnd(r);
+		return o;
+	}
+
+	@Override /* ReaderParserSession */
+	protected <K,V> Map<K,V> doParseIntoMap(ParserPipe pipe, Map<K,V> m, Type keyType, Type valueType) throws Exception {
+		ParserReader r = pipe.getParserReader();
+		m = parseIntoMap2(r, m, (ClassMeta<K>)getClassMeta(keyType), (ClassMeta<V>)getClassMeta(valueType), null);
+		validateEnd(r);
 		return m;
 	}
+
+	@Override /* ReaderParserSession */
+	protected <E> Collection<E> doParseIntoCollection(ParserPipe pipe, Collection<E> c, Type elementType) throws Exception {
+		ParserReader r = pipe.getParserReader();
+		c = parseIntoCollection2(r, c, getClassMeta(elementType), null);
+		validateEnd(r);
+		return c;
+	}
+
+	private <T> T parseAnything(ClassMeta<T> eType, ParserReader r, Object outer, BeanPropertyMeta pMeta) throws Exception {
+
+		if (eType == null)
+			eType = (ClassMeta<T>)object();
+		PojoSwap<T,Object> transform = (PojoSwap<T,Object>)eType.getPojoSwap();
+		ClassMeta<?> sType = eType.getSerializedClassMeta();
+		setCurrentClass(sType);
+		String wrapperAttr = sType.getExtendedMeta(JsonClassMeta.class).getWrapperAttr();
+
+		Object o = null;
+
+		skipCommentsAndSpace(r);
+		if (wrapperAttr != null)
+			skipWrapperAttrStart(r, wrapperAttr);
+		int c = r.peek();
+		if (c == -1) {
+			if (isStrict())
+				throw new ParseException(r.getLocation(this), "Empty input.");
+			// Let o be null.
+		} else if ((c == ',' || c == '}' || c == ']')) {
+			if (isStrict())
+				throw new ParseException(r.getLocation(this), "Missing value detected.");
+			// Handle bug in Cognos 10.2.1 that can product non-existent values.
+			// Let o be null;
+		} else if (c == 'n') {
+			parseKeyword("null", r);
+		} else if (sType.isObject()) {
+			if (c == '{') {
+				ObjectMap m2 = new ObjectMap(this);
+				parseIntoMap2(r, m2, string(), object(), pMeta);
+				o = cast(m2, pMeta, eType);
+			} else if (c == '[') {
+				o = parseIntoCollection2(r, new ObjectList(this), object(), pMeta);
+			} else if (c == '\'' || c == '"') {
+				o = parseString(r);
+				if (sType.isChar())
+					o = o.toString().charAt(0);
+			} else if (c >= '0' && c <= '9' || c == '-' || c == '.') {
+				o = parseNumber(r, null);
+			} else if (c == 't') {
+				parseKeyword("true", r);
+				o = Boolean.TRUE;
+			} else {
+				parseKeyword("false", r);
+				o = Boolean.FALSE;
+			}
+		} else if (sType.isBoolean()) {
+			o = parseBoolean(r);
+		} else if (sType.isCharSequence()) {
+			o = parseString(r);
+		} else if (sType.isChar()) {
+			o = parseString(r).charAt(0);
+		} else if (sType.isNumber()) {
+			o = parseNumber(r, (Class<? extends Number>)sType.getInnerClass());
+		} else if (sType.isMap()) {
+			Map m = (sType.canCreateNewInstance(outer) ? (Map)sType.newInstance(outer) : new ObjectMap(this));
+			o = parseIntoMap2(r, m, sType.getKeyType(), sType.getValueType(), pMeta);
+		} else if (sType.isCollection()) {
+			if (c == '{') {
+				ObjectMap m = new ObjectMap(this);
+				parseIntoMap2(r, m, string(), object(), pMeta);
+				o = cast(m, pMeta, eType);
+			} else {
+				Collection l = (sType.canCreateNewInstance(outer) ? (Collection)sType.newInstance() : new ObjectList(this));
+				o = parseIntoCollection2(r, l, sType, pMeta);
+			}
+		} else if (sType.canCreateNewBean(outer)) {
+			BeanMap m = newBeanMap(outer, sType.getInnerClass());
+			o = parseIntoBeanMap2(r, m).getBean();
+		} else if (sType.canCreateNewInstanceFromString(outer) && (c == '\'' || c == '"')) {
+			o = sType.newInstanceFromString(outer, parseString(r));
+		} else if (sType.canCreateNewInstanceFromNumber(outer) && isFirstNumberChar((char)c)) {
+			o = sType.newInstanceFromNumber(this, outer, parseNumber(r, sType.getNewInstanceFromNumberClass()));
+		} else if (sType.isArray() || sType.isArgs()) {
+			if (c == '{') {
+				ObjectMap m = new ObjectMap(this);
+				parseIntoMap2(r, m, string(), object(), pMeta);
+				o = cast(m, pMeta, eType);
+			} else {
+				ArrayList l = (ArrayList)parseIntoCollection2(r, new ArrayList(), sType, pMeta);
+				o = toArray(sType, l);
+			}
+		} else if (c == '{') {
+			Map m = new ObjectMap(this);
+			parseIntoMap2(r, m, sType.getKeyType(), sType.getValueType(), pMeta);
+			if (m.containsKey(getBeanTypePropertyName(eType)))
+				o = cast((ObjectMap)m, pMeta, eType);
+			else
+				throw new ParseException(r.getLocation(this), "Class ''{0}'' could not be instantiated.  Reason: ''{1}''",
+						sType.getInnerClass().getName(), sType.getNotABeanReason());
+		} else if (sType.canCreateNewInstanceFromString(outer) && ! isStrict()) {
+			o = sType.newInstanceFromString(outer, parseString(r));
+		} else {
+			throw new ParseException(r.getLocation(this), "Unrecognized syntax for class type ''{0}'', starting character ''{1}''",
+				sType, (char)c);
+		}
+
+		if (wrapperAttr != null)
+			skipWrapperAttrEnd(r);
+
+		if (transform != null && o != null)
+			o = transform.unswap(this, o, eType);
+
+		if (outer != null)
+			setParent(eType, o, outer);
+
+		return (T)o;
+	}
+
+	private Number parseNumber(ParserReader r, Class<? extends Number> type) throws Exception {
+		int c = r.peek();
+		if (c == '\'' || c == '"')
+			return parseNumber(r, parseString(r), type);
+		return parseNumber(r, parseNumberString(r), type);
+	}
+
+	private Number parseNumber(ParserReader r, String s, Class<? extends Number> type) throws Exception {
+
+		// JSON has slightly different number rules from Java.
+		// Strict mode enforces these different rules, lax does not.
+		if (isStrict()) {
+
+			// Lax allows blank strings to represent 0.
+			// Strict does not allow blank strings.
+			if (s.length() == 0)
+				throw new ParseException(r.getLocation(this), "Invalid JSON number: ''{0}''", s);
+
+			// Need to weed out octal and hexadecimal formats:  0123,-0123,0x123,-0x123.
+			// Don't weed out 0 or -0.
+			boolean isNegative = false;
+			char c = s.charAt(0);
+			if (c == '-') {
+				isNegative = true;
+				c = (s.length() == 1 ? 'x' : s.charAt(1));
+			}
+
+			// JSON doesn't allow '.123' and '-.123'.
+			if (c == '.')
+				throw new ParseException(loc(r), "Invalid JSON number: ''{0}''", s);
+
+			// '01' is not a valid number, but '0.1', '0e1', '0e+1' are valid.
+			if (c == '0' && s.length() > (isNegative ? 2 : 1)) {
+				char c2 = s.charAt((isNegative ? 2 : 1));
+				if (c2 != '.' && c2 != 'e' && c2 != 'E')
+					throw new ParseException(loc(r), "Invalid JSON number: ''{0}''", s);
+			}
+
+			// JSON doesn't allow '1.' or '0.e1'.
+			int i = s.indexOf('.');
+			if (i != -1 && (s.length() == (i+1) || ! decChars.contains(s.charAt(i+1))))
+				throw new ParseException(loc(r), "Invalid JSON number: ''{0}''", s);
+
+		}
+		return StringUtils.parseNumber(s, type);
+	}
+
+	private Boolean parseBoolean(ParserReader r) throws Exception {
+		int c = r.peek();
+		if (c == '\'' || c == '"')
+			return Boolean.valueOf(parseString(r));
+		if (c == 't') {
+			parseKeyword("true", r);
+			return Boolean.TRUE;
+		}
+		parseKeyword("false", r);
+		return Boolean.FALSE;
+	}
+
+
+	private <K,V> Map<K,V> parseIntoMap2(ParserReader r, Map<K,V> m, ClassMeta<K> keyType,
+			ClassMeta<V> valueType, BeanPropertyMeta pMeta) throws Exception {
+
+		if (keyType == null)
+			keyType = (ClassMeta<K>)string();
+
+		int S0=0; // Looking for outer {
+		int S1=1; // Looking for attrName start.
+		int S3=3; // Found attrName end, looking for :.
+		int S4=4; // Found :, looking for valStart: { [ " ' LITERAL.
+		int S5=5; // Looking for , or }
+		int S6=6; // Found , looking for attr start.
+
+		int state = S0;
+		String currAttr = null;
+		int c = 0;
+		while (c != -1) {
+			c = r.read();
+			if (state == S0) {
+				if (c == '{')
+					state = S1;
+			} else if (state == S1) {
+				if (c == '}') {
+					return m;
+				} else if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else {
+					currAttr = parseFieldName(r.unread());
+					state = S3;
+				}
+			} else if (state == S3) {
+				if (c == ':')
+					state = S4;
+			} else if (state == S4) {
+				if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else {
+					K key = convertAttrToType(m, currAttr, keyType);
+					V value = parseAnything(valueType, r.unread(), m, pMeta);
+					setName(valueType, value, key);
+					m.put(key, value);
+					state = S5;
+				}
+			} else if (state == S5) {
+				if (c == ',')
+					state = S6;
+				else if (isCommentOrWhitespace(c))
+					skipCommentsAndSpace(r.unread());
+				else if (c == '}') {
+					return m;
+				} else {
+					break;
+				}
+			} else if (state == S6) {
+				if (c == '}') {
+					break;
+				} else if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else {
+					currAttr = parseFieldName(r.unread());
+					state = S3;
+				}
+			}
+		}
+		if (state == S0)
+			throw new ParseException(loc(r), "Expected '{' at beginning of JSON object.");
+		if (state == S1)
+			throw new ParseException(loc(r), "Could not find attribute name on JSON object.");
+		if (state == S3)
+			throw new ParseException(loc(r), "Could not find ':' following attribute name on JSON object.");
+		if (state == S4)
+			throw new ParseException(loc(r), "Expected one of the following characters: {,[,',\",LITERAL.");
+		if (state == S5)
+			throw new ParseException(loc(r), "Could not find '}' marking end of JSON object.");
+		if (state == S6)
+			throw new ParseException(loc(r), "Unexpected '}' found in JSON object.");
+
+		return null; // Unreachable.
+	}
+
+	/*
+	 * Parse a JSON attribute from the character array at the specified position, then
+	 * set the position marker to the last character in the field name.
+	 */
+	private String parseFieldName(ParserReader r) throws Exception {
+		int c = r.peek();
+		if (c == '\'' || c == '"')
+			return parseString(r);
+		if (isStrict())
+			throw new ParseException(loc(r), "Unquoted attribute detected.");
+		r.mark();
+		// Look for whitespace.
+		while (c != -1) {
+			c = r.read();
+			if (c == ':' || isWhitespace(c) || c == '/') {
+				r.unread();
+				String s = r.getMarked().intern();
+				return s.equals("null") ? null : s;
+			}
+		}
+		throw new ParseException(loc(r), "Could not find the end of the field name.");
+	}
+
+	private <E> Collection<E> parseIntoCollection2(ParserReader r, Collection<E> l,
+			ClassMeta<?> type, BeanPropertyMeta pMeta) throws Exception {
+
+		int S0=0; // Looking for outermost [
+		int S1=1; // Looking for starting [ or { or " or ' or LITERAL or ]
+		int S2=2; // Looking for , or ]
+		int S3=3; // Looking for starting [ or { or " or ' or LITERAL
+
+		int argIndex = 0;
+
+		int state = S0;
+		int c = 0;
+		while (c != -1) {
+			c = r.read();
+			if (state == S0) {
+				if (c == '[')
+					state = S1;
+			} else if (state == S1) {
+				if (c == ']') {
+					return l;
+				} else if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else if (c != -1) {
+					l.add((E)parseAnything(type.isArgs() ? type.getArg(argIndex++) : type.getElementType(), r.unread(), l, pMeta));
+					state = S2;
+				}
+			} else if (state == S2) {
+				if (c == ',') {
+					state = S3;
+				} else if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else if (c == ']') {
+					return l;
+				} else {
+					break;  // Invalid character found.
+				}
+			} else if (state == S3) {
+				if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else if (c == ']') {
+					break;
+				} else if (c != -1) {
+					l.add((E)parseAnything(type.isArgs() ? type.getArg(argIndex++) : type.getElementType(), r.unread(), l, pMeta));
+					state = S2;
+				}
+			}
+		}
+		if (state == S0)
+			throw new ParseException(loc(r), "Expected '[' at beginning of JSON array.");
+		if (state == S1)
+			throw new ParseException(loc(r), "Expected one of the following characters: {,[,',\",LITERAL.");
+		if (state == S2)
+			throw new ParseException(loc(r), "Expected ',' or ']'.");
+		if (state == S3)
+			throw new ParseException(loc(r), "Unexpected trailing comma in array.");
+
+		return null;  // Unreachable.
+	}
+
+	private <T> BeanMap<T> parseIntoBeanMap2(ParserReader r, BeanMap<T> m) throws Exception {
+
+		int S0=0; // Looking for outer {
+		int S1=1; // Looking for attrName start.
+		int S3=3; // Found attrName end, looking for :.
+		int S4=4; // Found :, looking for valStart: { [ " ' LITERAL.
+		int S5=5; // Looking for , or }
+
+		int state = S0;
+		String currAttr = "";
+		int c = 0;
+		int currAttrLine = -1, currAttrCol = -1;
+		while (c != -1) {
+			c = r.read();
+			if (state == S0) {
+				if (c == '{')
+					state = S1;
+			} else if (state == S1) {
+				if (c == '}') {
+					return m;
+				} else if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else {
+					r.unread();
+					currAttrLine= r.getLine();
+					currAttrCol = r.getColumn();
+					currAttr = parseFieldName(r);
+					state = S3;
+				}
+			} else if (state == S3) {
+				if (c == ':')
+					state = S4;
+			} else if (state == S4) {
+				if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else {
+					if (! currAttr.equals(getBeanTypePropertyName(m.getClassMeta()))) {
+						BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr);
+						setCurrentProperty(pMeta);
+						if (pMeta == null) {
+							onUnknownProperty(r.getPipe(), currAttr, m, currAttrLine, currAttrCol);
+							parseAnything(object(), r.unread(), m.getBean(false), null); // Read content anyway to ignore it
+						} else {
+							ClassMeta<?> cm = pMeta.getClassMeta();
+							Object value = parseAnything(cm, r.unread(), m.getBean(false), pMeta);
+							setName(cm, value, currAttr);
+							pMeta.set(m, currAttr, value);
+						}
+						setCurrentProperty(null);
+					}
+					state = S5;
+				}
+			} else if (state == S5) {
+				if (c == ',')
+					state = S1;
+				else if (isCommentOrWhitespace(c))
+					skipCommentsAndSpace(r.unread());
+				else if (c == '}') {
+					return m;
+				}
+			}
+		}
+		if (state == S0)
+			throw new ParseException(loc(r), "Expected '{' at beginning of JSON object.");
+		if (state == S1)
+			throw new ParseException(loc(r), "Could not find attribute name on JSON object.");
+		if (state == S3)
+			throw new ParseException(loc(r), "Could not find ':' following attribute name on JSON object.");
+		if (state == S4)
+			throw new ParseException(loc(r), "Expected one of the following characters: {,[,',\",LITERAL.");
+		if (state == S5)
+			throw new ParseException(loc(r), "Could not find '}' marking end of JSON object.");
+
+		return null; // Unreachable.
+	}
+
+	/*
+	 * Starting from the specified position in the character array, returns the
+	 * position of the character " or '.
+	 * If the string consists of a concatenation of strings (e.g. 'AAA' + "BBB"), this method
+	 * will automatically concatenate the strings and return the result.
+	 */
+	private String parseString(ParserReader r) throws Exception  {
+		r.mark();
+		int qc = r.read();		// The quote character being used (" or ')
+		if (qc != '"' && isStrict()) {
+			String msg = (
+				qc == '\''
+				? "Invalid quote character \"{0}\" being used."
+				: "Did not find quote character marking beginning of string.  Character=\"{0}\""
+			);
+			throw new ParseException(loc(r), msg, (char)qc);
+		}
+		final boolean isQuoted = (qc == '\'' || qc == '"');
+		String s = null;
+		boolean isInEscape = false;
+		int c = 0;
+		while (c != -1) {
+			c = r.read();
+			// Strict syntax requires that all control characters be escaped.
+			if (isStrict() && c <= 0x1F)
+				throw new ParseException("Unescaped control character encountered: ''0x{0}''", String.format("%04X", c));
+			if (isInEscape) {
+				switch (c) {
+					case 'n': r.replace('\n'); break;
+					case 'r': r.replace('\r'); break;
+					case 't': r.replace('\t'); break;
+					case 'f': r.replace('\f'); break;
+					case 'b': r.replace('\b'); break;
+					case '\\': r.replace('\\'); break;
+					case '/': r.replace('/'); break;
+					case '\'': r.replace('\''); break;
+					case '"': r.replace('"'); break;
+					case 'u': {
+						String n = r.read(4);
+						try {
+							r.replace(Integer.parseInt(n, 16), 6);
+						} catch (NumberFormatException e) {
+							throw new ParseException(loc(r), "Invalid Unicode escape sequence in string.");
+						}
+						break;
+					}
+					default:
+						throw new ParseException(loc(r), "Invalid escape sequence in string.");
+				}
+				isInEscape = false;
+			} else {
+				if (c == '\\') {
+					isInEscape = true;
+					r.delete();
+				} else if (isQuoted) {
+					if (c == qc) {
+						s = r.getMarked(1, -1);
+						break;
+					}
+				} else {
+					if (c == ',' || c == '}' || c == ']' || isWhitespace(c)) {
+						s = r.getMarked(0, -1);
+						r.unread();
+						break;
+					} else if (c == -1) {
+						s = r.getMarked(0, 0);
+						break;
+					}
+				}
+			}
+		}
+		if (s == null)
+			throw new ParseException(loc(r), "Could not find expected end character ''{0}''.", (char)qc);
+
+		// Look for concatenated string (i.e. whitespace followed by +).
+		skipCommentsAndSpace(r);
+		if (r.peek() == '+') {
+			if (isStrict())
+				throw new ParseException(loc(r), "String concatenation detected.");
+			r.read();	// Skip past '+'
+			skipCommentsAndSpace(r);
+			s += parseString(r);
+		}
+		return trim(s); // End of input reached.
+	}
+
+	/*
+	 * Looks for the keywords true, false, or null.
+	 * Throws an exception if any of these keywords are not found at the specified position.
+	 */
+	private void parseKeyword(String keyword, ParserReader r) throws Exception {
+		try {
+			String s = r.read(keyword.length());
+			if (s.equals(keyword))
+				return;
+			throw new ParseException(loc(r), "Unrecognized syntax.");
+		} catch (IndexOutOfBoundsException e) {
+			throw new ParseException(loc(r), "Unrecognized syntax.");
+		}
+	}
+
+	/*
+	 * Doesn't actually parse anything, but moves the position beyond any whitespace or comments.
+	 * If positionOnNext is 'true', then the cursor will be set to the point immediately after
+	 * the comments and whitespace.  Otherwise, the cursor will be set to the last position of
+	 * the comments and whitespace.
+	 */
+	private void skipCommentsAndSpace(ParserReader r) throws Exception {
+		int c = 0;
+		while ((c = r.read()) != -1) {
+			if (! isWhitespace(c)) {
+				if (c == '/') {
+					if (isStrict())
+						throw new ParseException(loc(r), "Javascript comment detected.");
+					skipComments(r);
+				} else {
+					r.unread();
+					return;
+				}
+			}
+		}
+	}
+
+	/*
+	 * Doesn't actually parse anything, but moves the position beyond the construct "{wrapperAttr:" when
+	 * the @Json.wrapperAttr() annotation is used on a class.
+	 */
+	private void skipWrapperAttrStart(ParserReader r, String wrapperAttr) throws Exception {
+
+		int S0=0; // Looking for outer {
+		int S1=1; // Looking for attrName start.
+		int S3=3; // Found attrName end, looking for :.
+		int S4=4; // Found :, looking for valStart: { [ " ' LITERAL.
+
+		int state = S0;
+		String currAttr = null;
+		int c = 0;
+		while (c != -1) {
+			c = r.read();
+			if (state == S0) {
+				if (c == '{')
+					state = S1;
+			} else if (state == S1) {
+				if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else {
+					currAttr = parseFieldName(r.unread());
+					if (! currAttr.equals(wrapperAttr))
+						throw new ParseException(loc(r),
+							"Expected to find wrapper attribute ''{0}'' but found attribute ''{1}''", wrapperAttr, currAttr);
+					state = S3;
+				}
+			} else if (state == S3) {
+				if (c == ':')
+					state = S4;
+			} else if (state == S4) {
+				if (isCommentOrWhitespace(c)) {
+					skipCommentsAndSpace(r.unread());
+				} else {
+					r.unread();
+					return;
+				}
+			}
+		}
+		if (state == S0)
+			throw new ParseException(loc(r), "Expected '{' at beginning of JSON object.");
+		if (state == S1)
+			throw new ParseException(loc(r), "Could not find attribute name on JSON object.");
+		if (state == S3)
+			throw new ParseException(loc(r), "Could not find ':' following attribute name on JSON object.");
+		if (state == S4)
+			throw new ParseException(loc(r), "Expected one of the following characters: {,[,',\",LITERAL.");
+	}
+
+	/*
+	 * Doesn't actually parse anything, but moves the position beyond the construct "}" when
+	 * the @Json.wrapperAttr() annotation is used on a class.
+	 */
+	private void skipWrapperAttrEnd(ParserReader r) throws ParseException, IOException {
+		int c = 0;
+		while ((c = r.read()) != -1) {
+			if (! isWhitespace(c)) {
+				if (c == '/') {
+					if (isStrict())
+						throw new ParseException(loc(r), "Javascript comment detected.");
+					skipComments(r);
+				} else if (c == '}') {
+					return;
+				} else {
+					throw new ParseException(loc(r), "Could not find '}' at the end of JSON wrapper object.");
+				}
+			}
+		}
+	}
+
+	/*
+	 * Doesn't actually parse anything, but when positioned at the beginning of comment,
+	 * it will move the pointer to the last character in the comment.
+	 */
+	private void skipComments(ParserReader r) throws ParseException, IOException {
+		int c = r.read();
+		//  "/* */" style comments
+		if (c == '*') {
+			while (c != -1)
+				if ((c = r.read()) == '*')
+					if ((c = r.read()) == '/')
+						return;
+		//  "//" style comments
+		} else if (c == '/') {
+			while (c != -1) {
+				c = r.read();
+				if (c == -1 || c == '\n')
+					return;
+			}
+		}
+		throw new ParseException(loc(r), "Open ended comment.");
+	}
+
+	/*
+	 * 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 void validateEnd(ParserReader r) throws Exception {
+		skipCommentsAndSpace(r);
+		int c = r.read();
+		if (c != -1 && c != ';')  // var x = {...}; expressions can end with a semicolon.
+			throw new ParseException(loc(r), "Remainder after parse: ''{0}''.", (char)c);
+	}
+
+	private ObjectMap loc(ParserReader r) {
+		return getLastLocation().append("line", r.getLine()).append("column", r.getColumn());
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
index 9829016..66d6bae 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializer.java
@@ -12,17 +12,11 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.json;
 
-import static org.apache.juneau.internal.ClassUtils.*;
 import static org.apache.juneau.serializer.SerializerContext.*;
 
-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.serializer.*;
-import org.apache.juneau.transform.*;
 
 /**
  * Serializes POJO metadata to HTTP responses as JSON.
@@ -40,125 +34,21 @@ import org.apache.juneau.transform.*;
 @Produces(value="application/json+schema,text/json+schema",contentType="application/json")
 public final class JsonSchemaSerializer extends JsonSerializer {
 
-	private final JsonSerializerContext ctx;
-
 	/**
 	 * Constructor.
 	 *
 	 * @param propertyStore Initialize with the specified config property store.
 	 */
 	public JsonSchemaSerializer(PropertyStore propertyStore) {
-		this(propertyStore, null);
-	}
-
-	/**
-	 * Constructor.
-	 *
-	 * @param propertyStore Initialize with the specified config property store.
-	 * @param overrideProperties
-	 */
-	public JsonSchemaSerializer(PropertyStore propertyStore, Map<String,Object> overrideProperties) {
-		super(propertyStore);
-		this.ctx = this.propertyStore.create(overrideProperties).getContext(JsonSerializerContext.class);
-	}
-
-	@Override /* CoreObject */
-	protected ObjectMap getOverrideProperties() {
-		return super.getOverrideProperties().append(SERIALIZER_detectRecursions, true)
-			.append(SERIALIZER_ignoreRecursions, true);
+		super(
+			propertyStore.copy()
+			.append(SERIALIZER_detectRecursions, true)
+			.append(SERIALIZER_ignoreRecursions, true)
+		);
 	}
 
-
-	//--------------------------------------------------------------------------------
-	// Entry point methods
-	//--------------------------------------------------------------------------------
-
 	@Override /* Serializer */
-	public JsonSerializerSession createSession(ObjectMap op, Method javaMethod, Locale locale,
-			TimeZone timeZone, MediaType mediaType, UriContext uriContext) {
-		return new JsonSerializerSession(ctx, op, javaMethod, locale, timeZone, mediaType, uriContext);
-	}
-
-	@Override /* JsonSerializer */
-	protected void doSerialize(SerializerSession session, SerializerOutput out, Object o) throws Exception {
-		JsonSerializerSession s = (JsonSerializerSession)session;
-		ObjectMap schema = getSchema(s, session.getClassMetaForObject(o), "root", null);
-		serializeAnything(s, s.getJsonWriter(out), schema, s.getExpectedRootType(o), "root", null);
-	}
-
-	/*
-	 * Creates a schema representation of the specified class type.
-	 *
-	 * @param eType The class type to get the schema of.
-	 * @param ctx Serialize context used to prevent infinite loops.
-	 * @param attrName The name of the current attribute.
-	 * @return A schema representation of the specified class.
-	 * @throws SerializeException If a problem occurred trying to convert the output.
-	 */
-	@SuppressWarnings({ "unchecked", "rawtypes" })
-	private ObjectMap getSchema(JsonSerializerSession session, ClassMeta<?> eType, String attrName, String[] pNames)
-			throws Exception {
-		ObjectMap out = new ObjectMap();
-
-		if (eType == null)
-			eType = object();
-
-		ClassMeta<?> aType;			// The actual type (will be null if recursion occurs)
-		ClassMeta<?> sType;			// The serialized type
-
-		aType = session.push(attrName, eType, null);
-
-		sType = eType.getSerializedClassMeta();
-		String type = null;
-
-		if (sType.isEnum() || sType.isCharSequence() || sType.isChar())
-			type = "string";
-		else if (sType.isNumber())
-			type = "number";
-		else if (sType.isBoolean())
-			type = "boolean";
-		else if (sType.isMapOrBean())
-			type = "object";
-		else if (sType.isCollectionOrArray())
-			type = "array";
-		else
-			type = "any";
-
-		out.put("type", type);
-		out.put("description", eType.toString());
-		PojoSwap f = eType.getPojoSwap();
-		if (f != null)
-			out.put("transform", f);
-
-		if (aType != null) {
-			if (sType.isEnum())
-				out.put("enum", getEnumStrings((Class<Enum<?>>)sType.getInnerClass()));
-			else if (sType.isCollectionOrArray()) {
-				ClassMeta componentType = sType.getElementType();
-				if (sType.isCollection() && isParentClass(Set.class, sType.getInnerClass()))
-					out.put("uniqueItems", true);
-				out.put("items", getSchema(session, componentType, "items", pNames));
-			} else if (sType.isBean()) {
-				ObjectMap properties = new ObjectMap();
-				BeanMeta bm = session.getBeanMeta(sType.getInnerClass());
-				if (pNames != null)
-					bm = new BeanMetaFiltered(bm, pNames);
-				for (Iterator<BeanPropertyMeta> i = bm.getPropertyMetas().iterator(); i.hasNext();) {
-					BeanPropertyMeta p = i.next();
-					properties.put(p.getName(), getSchema(session, p.getClassMeta(), p.getName(), p.getProperties()));
-				}
-				out.put("properties", properties);
-			}
-		}
-		session.pop();
-		return out;
-	}
-
-	@SuppressWarnings({ "unchecked", "rawtypes" })
-	private static List<String> getEnumStrings(Class<? extends Enum> c) {
-		List<String> l = new LinkedList<String>();
-		for (Object e : EnumSet.allOf(c))
-			l.add(e.toString());
-		return l;
+	public WriterSerializerSession createSession(SerializerSessionArgs args) {
+		return new JsonSchemaSerializerSession(ctx, args);
 	}
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
new file mode 100644
index 0000000..f9e875d
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSchemaSerializerSession.java
@@ -0,0 +1,129 @@
+// ***************************************************************************************************************************
+// * 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.json;
+
+import static org.apache.juneau.internal.ClassUtils.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transform.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link JsonSchemaSerializer}.
+ *
+ * <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 JsonSchemaSerializerSession extends JsonSerializerSession {
+
+	/**
+	 * 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 JsonSchemaSerializerSession(JsonSerializerContext ctx, SerializerSessionArgs args) {
+		super(ctx, args);
+	}
+
+	@Override /* SerializerSession */
+	protected void doSerialize(SerializerPipe out, Object o) throws Exception {
+		ObjectMap schema = getSchema(getClassMetaForObject(o), "root", null);
+		serializeAnything(getJsonWriter(out), schema, getExpectedRootType(o), "root", null);
+	}
+
+	/*
+	 * Creates a schema representation of the specified class type.
+	 *
+	 * @param eType The class type to get the schema of.
+	 * @param ctx Serialize context used to prevent infinite loops.
+	 * @param attrName The name of the current attribute.
+	 * @return A schema representation of the specified class.
+	 * @throws SerializeException If a problem occurred trying to convert the output.
+	 */
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	private ObjectMap getSchema(ClassMeta<?> eType, String attrName, String[] pNames) throws Exception {
+		ObjectMap out = new ObjectMap();
+
+		if (eType == null)
+			eType = object();
+
+		ClassMeta<?> aType;			// The actual type (will be null if recursion occurs)
+		ClassMeta<?> sType;			// The serialized type
+
+		aType = push(attrName, eType, null);
+
+		sType = eType.getSerializedClassMeta();
+		String type = null;
+
+		if (sType.isEnum() || sType.isCharSequence() || sType.isChar())
+			type = "string";
+		else if (sType.isNumber())
+			type = "number";
+		else if (sType.isBoolean())
+			type = "boolean";
+		else if (sType.isMapOrBean())
+			type = "object";
+		else if (sType.isCollectionOrArray())
+			type = "array";
+		else
+			type = "any";
+
+		out.put("type", type);
+		out.put("description", eType.toString());
+		PojoSwap f = eType.getPojoSwap();
+		if (f != null)
+			out.put("transform", f);
+
+		if (aType != null) {
+			if (sType.isEnum())
+				out.put("enum", getEnumStrings((Class<Enum<?>>)sType.getInnerClass()));
+			else if (sType.isCollectionOrArray()) {
+				ClassMeta componentType = sType.getElementType();
+				if (sType.isCollection() && isParentClass(Set.class, sType.getInnerClass()))
+					out.put("uniqueItems", true);
+				out.put("items", getSchema(componentType, "items", pNames));
+			} else if (sType.isBean()) {
+				ObjectMap properties = new ObjectMap();
+				BeanMeta bm = getBeanMeta(sType.getInnerClass());
+				if (pNames != null)
+					bm = new BeanMetaFiltered(bm, pNames);
+				for (Iterator<BeanPropertyMeta> i = bm.getPropertyMetas().iterator(); i.hasNext();) {
+					BeanPropertyMeta p = i.next();
+					properties.put(p.getName(), getSchema(p.getClassMeta(), p.getName(), p.getProperties()));
+				}
+				out.put("properties", properties);
+			}
+		}
+		pop();
+		return out;
+	}
+
+	@SuppressWarnings({ "unchecked", "rawtypes" })
+	private static List<String> getEnumStrings(Class<? extends Enum> c) {
+		List<String> l = new LinkedList<String>();
+		for (Object e : EnumSet.allOf(c))
+			l.add(e.toString());
+		return l;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
index 82edd79..db0a78d 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
@@ -14,14 +14,11 @@ package org.apache.juneau.json;
 
 import static org.apache.juneau.json.JsonSerializerContext.*;
 
-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.serializer.*;
-import org.apache.juneau.transform.*;
 
 /**
  * Serializes POJO models to JSON.
@@ -132,12 +129,10 @@ public class JsonSerializer extends WriterSerializer {
 		 * @param propertyStore The property store containing all the settings for this object.
 		 */
 		public Readable(PropertyStore propertyStore) {
-			super(propertyStore);
-		}
-
-		@Override /* CoreObject */
-		protected ObjectMap getOverrideProperties() {
-			return super.getOverrideProperties().append(SERIALIZER_useWhitespace, true);
+			super(
+				propertyStore.copy()
+				.append(SERIALIZER_useWhitespace, true)
+			);
 		}
 	}
 
@@ -151,12 +146,11 @@ public class JsonSerializer extends WriterSerializer {
 		 * @param propertyStore The property store containing all the settings for this object.
 		 */
 		public Simple(PropertyStore propertyStore) {
-			super(propertyStore);
-		}
-
-		@Override /* CoreObject */
-		protected ObjectMap getOverrideProperties() {
-			return super.getOverrideProperties().append(JSON_simpleMode, true).append(SERIALIZER_quoteChar, '\'');
+			super(
+				propertyStore.copy()
+				.append(JSON_simpleMode, true)
+				.append(SERIALIZER_quoteChar, '\'')
+			);
 		}
 	}
 
@@ -169,13 +163,12 @@ public class JsonSerializer extends WriterSerializer {
 		 * @param propertyStore The property store containing all the settings for this object.
 		 */
 		public SimpleReadable(PropertyStore propertyStore) {
-			super(propertyStore);
-		}
-
-		@Override /* CoreObject */
-		protected ObjectMap getOverrideProperties() {
-			return super.getOverrideProperties().append(JSON_simpleMode, true).append(SERIALIZER_quoteChar, '\'')
-				.append(SERIALIZER_useWhitespace, true);
+			super(
+				propertyStore.copy()
+				.append(JSON_simpleMode, true)
+				.append(SERIALIZER_quoteChar, '\'')
+				.append(SERIALIZER_useWhitespace, true)
+			);
 		}
 	}
 
@@ -191,18 +184,18 @@ public class JsonSerializer extends WriterSerializer {
 		 * @param propertyStore The property store containing all the settings for this object.
 		 */
 		public SimpleReadableSafe(PropertyStore propertyStore) {
-			super(propertyStore);
-		}
-
-		@Override /* CoreObject */
-		protected ObjectMap getOverrideProperties() {
-			return super.getOverrideProperties().append(JSON_simpleMode, true).append(SERIALIZER_quoteChar, '\'')
-				.append(SERIALIZER_useWhitespace, true).append(SERIALIZER_detectRecursions, true);
+			super(
+				propertyStore.copy()
+				.append(JSON_simpleMode, true)
+				.append(SERIALIZER_quoteChar, '\'')
+				.append(SERIALIZER_useWhitespace, true)
+				.append(SERIALIZER_detectRecursions, true)
+			);
 		}
 	}
 
 
-	private final JsonSerializerContext ctx;
+	final JsonSerializerContext ctx;
 	private volatile JsonSchemaSerializer schemaSerializer;
 
 	/**
@@ -221,186 +214,13 @@ public class JsonSerializer extends WriterSerializer {
 	}
 
 	/**
-	 * Workhorse method.
-	 *
-	 * <p>
-	 * Determines the type of object, and then calls the appropriate type-specific serialization method.
-	 */
-	@SuppressWarnings({ "rawtypes", "unchecked" })
-	SerializerWriter serializeAnything(JsonSerializerSession session, JsonWriter out, Object o, ClassMeta<?> eType,
-			String attrName, BeanPropertyMeta pMeta) throws Exception {
-
-		if (o == null) {
-			out.append("null");
-			return out;
-		}
-
-		if (eType == null)
-			eType = object();
-
-		ClassMeta<?> aType;			// The actual type
-		ClassMeta<?> sType;			// The serialized type
-
-		aType = session.push(attrName, o, eType);
-		boolean isRecursion = aType == null;
-
-		// Handle recursion
-		if (aType == null) {
-			o = null;
-			aType = object();
-		}
-
-		sType = aType.getSerializedClassMeta();
-		String typeName = session.getBeanTypeName(eType, aType, pMeta);
-
-		// Swap if necessary
-		PojoSwap swap = aType.getPojoSwap();
-		if (swap != null) {
-			o = swap.swap(session, o);
-
-			// If the getSwapClass() method returns Object, we need to figure out
-			// the actual type now.
-			if (sType.isObject())
-				sType = session.getClassMetaForObject(o);
-		}
-
-		String wrapperAttr = sType.getExtendedMeta(JsonClassMeta.class).getWrapperAttr();
-		if (wrapperAttr != null) {
-			out.append('{').cr(session.indent).attr(wrapperAttr).append(':').s(session.indent);
-			session.indent++;
-		}
-
-		// '\0' characters are considered null.
-		if (o == null || (sType.isChar() && ((Character)o).charValue() == 0))
-			out.append("null");
-		else if (sType.isNumber() || sType.isBoolean())
-			out.append(o);
-		else if (sType.isBean())
-			serializeBeanMap(session, out, session.toBeanMap(o), typeName);
-		else if (sType.isUri() || (pMeta != null && pMeta.isUri()))
-			out.uriValue(o);
-		else if (sType.isMap()) {
-			if (o instanceof BeanMap)
-				serializeBeanMap(session, out, (BeanMap)o, typeName);
-			else
-				serializeMap(session, out, (Map)o, eType);
-		}
-		else if (sType.isCollection()) {
-			serializeCollection(session, out, (Collection) o, eType);
-		}
-		else if (sType.isArray()) {
-			serializeCollection(session, out, toList(sType.getInnerClass(), o), eType);
-		}
-		else
-			out.stringValue(session.toString(o));
-
-		if (wrapperAttr != null) {
-			session.indent--;
-			out.cre(session.indent-1).append('}');
-		}
-
-		if (! isRecursion)
-			session.pop();
-		return out;
-	}
-
-	@SuppressWarnings({ "rawtypes", "unchecked" })
-	private SerializerWriter serializeMap(JsonSerializerSession session, JsonWriter out, Map m, ClassMeta<?> type)
-			throws Exception {
-
-		ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType();
-
-		m = session.sort(m);
-
-		int i = session.getIndent();
-		out.append('{');
-
-		Iterator mapEntries = m.entrySet().iterator();
-
-		while (mapEntries.hasNext()) {
-			Map.Entry e = (Map.Entry) mapEntries.next();
-			Object value = e.getValue();
-
-			Object key = session.generalize(e.getKey(), keyType);
-
-			out.cr(i).attr(session.toString(key)).append(':').s(i);
-
-			serializeAnything(session, out, value, valueType, (key == null ? null : session.toString(key)), null);
-
-			if (mapEntries.hasNext())
-				out.append(',').smi(i);
-		}
-
-		out.cre(i-1).append('}');
-
-		return out;
-	}
-
-	private SerializerWriter serializeBeanMap(JsonSerializerSession session, JsonWriter out, BeanMap<?> m,
-			String typeName) throws Exception {
-		int i = session.getIndent();
-		out.append('{');
-
-		boolean addComma = false;
-		for (BeanPropertyValue p : m.getValues(session.isTrimNulls(), typeName != null ? session.createBeanTypeNameProperty(m, typeName) : null)) {
-			BeanPropertyMeta pMeta = p.getMeta();
-			ClassMeta<?> cMeta = p.getClassMeta();
-			String key = p.getName();
-			Object value = p.getValue();
-			Throwable t = p.getThrown();
-			if (t != null)
-				session.onBeanGetterException(pMeta, t);
-
-			if (session.canIgnoreValue(cMeta, key, value))
-				continue;
-
-			if (addComma)
-				out.append(',').smi(i);
-
-			out.cr(i).attr(key).append(':').s(i);
-
-			serializeAnything(session, out, value, cMeta, key, pMeta);
-
-			addComma = true;
-		}
-		out.cre(i-1).append('}');
-		return out;
-	}
-
-	@SuppressWarnings({"rawtypes", "unchecked"})
-	private SerializerWriter serializeCollection(JsonSerializerSession session, JsonWriter out, Collection c,
-			ClassMeta<?> type) throws Exception {
-
-		ClassMeta<?> elementType = type.getElementType();
-
-		c = session.sort(c);
-
-		out.append('[');
-		int depth = session.getIndent();
-
-		for (Iterator i = c.iterator(); i.hasNext();) {
-
-			Object value = i.next();
-
-			out.cr(depth);
-
-			serializeAnything(session, out, value, elementType, "<iterator>", null);
-
-			if (i.hasNext())
-				out.append(',').smi(depth);
-		}
-		out.cre(depth-1).append(']');
-		return out;
-	}
-
-	/**
 	 * Returns the schema serializer based on the settings of this serializer.
 	 *
 	 * @return The schema serializer.
 	 */
 	public JsonSchemaSerializer getSchemaSerializer() {
 		if (schemaSerializer == null)
-			schemaSerializer = new JsonSchemaSerializer(propertyStore, getOverrideProperties());
+			schemaSerializer = new JsonSchemaSerializer(propertyStore);
 		return schemaSerializer;
 	}
 
@@ -409,14 +229,7 @@ public class JsonSerializer extends WriterSerializer {
 	//--------------------------------------------------------------------------------
 
 	@Override /* Serializer */
-	public JsonSerializerSession createSession(ObjectMap op, Method javaMethod, Locale locale,
-			TimeZone timeZone, MediaType mediaType, UriContext uriContext) {
-		return new JsonSerializerSession(ctx, op, javaMethod, locale, timeZone, mediaType, uriContext);
-	}
-
-	@Override /* Serializer */
-	protected void doSerialize(SerializerSession session, SerializerOutput out, Object o) throws Exception {
-		JsonSerializerSession s = (JsonSerializerSession)session;
-		serializeAnything(s, s.getJsonWriter(out), o, s.getExpectedRootType(o), "root", null);
+	public WriterSerializerSession createSession(SerializerSessionArgs args) {
+		return new JsonSerializerSession(ctx, args);
 	}
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
index 2808f59..12c9433 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
@@ -14,20 +14,20 @@ package org.apache.juneau.json;
 
 import static org.apache.juneau.json.JsonSerializerContext.*;
 
-import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.http.*;
 import org.apache.juneau.serializer.*;
+import org.apache.juneau.transform.*;
 
 /**
  * Session object that lives for the duration of a single use of {@link JsonSerializer}.
  *
  * <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 final class JsonSerializerSession extends SerializerSession {
+public class JsonSerializerSession extends WriterSerializerSession {
 
 	private final boolean
 		simpleMode,
@@ -40,60 +40,203 @@ public final class JsonSerializerSession extends SerializerSession {
 	 * @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.
+	 * @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 JsonSerializerSession(JsonSerializerContext ctx, ObjectMap op, Method javaMethod,
-			Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) {
-		super(ctx, op, javaMethod, locale, timeZone, mediaType, uriContext);
-		if (op == null || op.isEmpty()) {
+	protected JsonSerializerSession(JsonSerializerContext ctx, SerializerSessionArgs args) {
+		super(ctx, args);
+		ObjectMap p = getProperties();
+		if (p.isEmpty()) {
 			simpleMode = ctx.simpleMode;
 			escapeSolidus = ctx.escapeSolidus;
 			addBeanTypeProperties = ctx.addBeanTypeProperties;
 		} else {
-			simpleMode = op.getBoolean(JSON_simpleMode, ctx.simpleMode);
-			escapeSolidus = op.getBoolean(JSON_escapeSolidus, ctx.escapeSolidus);
-			addBeanTypeProperties = op.getBoolean(JSON_addBeanTypeProperties, ctx.addBeanTypeProperties);
+			simpleMode = p.getBoolean(JSON_simpleMode, ctx.simpleMode);
+			escapeSolidus = p.getBoolean(JSON_escapeSolidus, ctx.escapeSolidus);
+			addBeanTypeProperties = p.getBoolean(JSON_addBeanTypeProperties, ctx.addBeanTypeProperties);
 		}
 	}
 
-	/**
-	 * Returns the {@link JsonSerializerContext#JSON_simpleMode} setting value for this session.
-	 *
-	 * @return The {@link JsonSerializerContext#JSON_simpleMode} setting value for this session.
-	 */
-	public final boolean isSimpleMode() {
-		return simpleMode;
+
+	@Override /* SerializerSesssion */
+	protected void doSerialize(SerializerPipe out, Object o) throws Exception {
+		serializeAnything(getJsonWriter(out), o, getExpectedRootType(o), "root", null);
 	}
 
-	/**
-	 * Returns the {@link JsonSerializerContext#JSON_escapeSolidus} setting value for this session.
-	 *
-	 * @return The {@link JsonSerializerContext#JSON_escapeSolidus} setting value for this session.
+	/*
+	 * Workhorse method.
+	 * Determines the type of object, and then calls the appropriate type-specific serialization method.
 	 */
-	public final boolean isEscapeSolidus() {
-		return escapeSolidus;
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	SerializerWriter serializeAnything(JsonWriter out, Object o, ClassMeta<?> eType,	String attrName, BeanPropertyMeta pMeta) throws Exception {
+
+		if (o == null) {
+			out.append("null");
+			return out;
+		}
+
+		if (eType == null)
+			eType = object();
+
+		ClassMeta<?> aType;			// The actual type
+		ClassMeta<?> sType;			// The serialized type
+
+		aType = push(attrName, o, eType);
+		boolean isRecursion = aType == null;
+
+		// Handle recursion
+		if (aType == null) {
+			o = null;
+			aType = object();
+		}
+
+		sType = aType.getSerializedClassMeta();
+		String typeName = getBeanTypeName(eType, aType, pMeta);
+
+		// Swap if necessary
+		PojoSwap swap = aType.getPojoSwap();
+		if (swap != null) {
+			o = swap.swap(this, o);
+
+			// If the getSwapClass() method returns Object, we need to figure out
+			// the actual type now.
+			if (sType.isObject())
+				sType = getClassMetaForObject(o);
+		}
+
+		String wrapperAttr = sType.getExtendedMeta(JsonClassMeta.class).getWrapperAttr();
+		if (wrapperAttr != null) {
+			out.append('{').cr(indent).attr(wrapperAttr).append(':').s(indent);
+			indent++;
+		}
+
+		// '\0' characters are considered null.
+		if (o == null || (sType.isChar() && ((Character)o).charValue() == 0))
+			out.append("null");
+		else if (sType.isNumber() || sType.isBoolean())
+			out.append(o);
+		else if (sType.isBean())
+			serializeBeanMap(out, toBeanMap(o), typeName);
+		else if (sType.isUri() || (pMeta != null && pMeta.isUri()))
+			out.uriValue(o);
+		else if (sType.isMap()) {
+			if (o instanceof BeanMap)
+				serializeBeanMap(out, (BeanMap)o, typeName);
+			else
+				serializeMap(out, (Map)o, eType);
+		}
+		else if (sType.isCollection()) {
+			serializeCollection(out, (Collection) o, eType);
+		}
+		else if (sType.isArray()) {
+			serializeCollection(out, toList(sType.getInnerClass(), o), eType);
+		}
+		else
+			out.stringValue(toString(o));
+
+		if (wrapperAttr != null) {
+			indent--;
+			out.cre(indent-1).append('}');
+		}
+
+		if (! isRecursion)
+			pop();
+		return out;
 	}
 
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	private SerializerWriter serializeMap(JsonWriter out, Map m, ClassMeta<?> type) throws Exception {
+
+		ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType();
+
+		m = sort(m);
+
+		int i = indent;
+		out.append('{');
+
+		Iterator mapEntries = m.entrySet().iterator();
+
+		while (mapEntries.hasNext()) {
+			Map.Entry e = (Map.Entry) mapEntries.next();
+			Object value = e.getValue();
+
+			Object key = generalize(e.getKey(), keyType);
+
+			out.cr(i).attr(toString(key)).append(':').s(i);
+
+			serializeAnything(out, value, valueType, (key == null ? null : toString(key)), null);
+
+			if (mapEntries.hasNext())
+				out.append(',').smi(i);
+		}
+
+		out.cre(i-1).append('}');
+
+		return out;
+	}
+
+	private SerializerWriter serializeBeanMap(JsonWriter out, BeanMap<?> m, String typeName) throws Exception {
+		int i = indent;
+		out.append('{');
+
+		boolean addComma = false;
+		for (BeanPropertyValue p : m.getValues(isTrimNulls(), typeName != null ? createBeanTypeNameProperty(m, typeName) : null)) {
+			BeanPropertyMeta pMeta = p.getMeta();
+			ClassMeta<?> cMeta = p.getClassMeta();
+			String key = p.getName();
+			Object value = p.getValue();
+			Throwable t = p.getThrown();
+			if (t != null)
+				onBeanGetterException(pMeta, t);
+
+			if (canIgnoreValue(cMeta, key, value))
+				continue;
+
+			if (addComma)
+				out.append(',').smi(i);
+
+			out.cr(i).attr(key).append(':').s(i);
+
+			serializeAnything(out, value, cMeta, key, pMeta);
+
+			addComma = true;
+		}
+		out.cre(i-1).append('}');
+		return out;
+	}
+
+	@SuppressWarnings({"rawtypes", "unchecked"})
+	private SerializerWriter serializeCollection(JsonWriter out, Collection c, ClassMeta<?> type) throws Exception {
+
+		ClassMeta<?> elementType = type.getElementType();
+
+		c = sort(c);
+
+		out.append('[');
+
+		for (Iterator i = c.iterator(); i.hasNext();) {
+			Object value = i.next();
+			out.cr(indent);
+			serializeAnything(out, value, elementType, "<iterator>", null);
+			if (i.hasNext())
+				out.append(',').smi(indent);
+		}
+		out.cre(indent-1).append(']');
+		return out;
+	}
+
+
 	/**
 	 * Returns the {@link JsonSerializerContext#JSON_addBeanTypeProperties} setting value for this session.
 	 *
 	 * @return The {@link JsonSerializerContext#JSON_addBeanTypeProperties} setting value for this session.
 	 */
 	@Override /* SerializerSession */
-	public final boolean isAddBeanTypeProperties() {
+	protected final boolean isAddBeanTypeProperties() {
 		return addBeanTypeProperties;
 	}
 
@@ -104,11 +247,13 @@ public final class JsonSerializerSession extends SerializerSession {
 	 * @return The output target object wrapped in an {@link JsonWriter}.
 	 * @throws Exception
 	 */
-	public JsonWriter getJsonWriter(SerializerOutput out) throws Exception {
+	protected final JsonWriter getJsonWriter(SerializerPipe out) throws Exception {
 		Object output = out.getRawOutput();
 		if (output instanceof JsonWriter)
 			return (JsonWriter)output;
-		return new JsonWriter(out.getWriter(), isUseWhitespace(), getMaxIndent(), isEscapeSolidus(), getQuoteChar(),
-			isSimpleMode(), isTrimStrings(), getUriResolver());
+		JsonWriter w = new JsonWriter(out.getWriter(), isUseWhitespace(), getMaxIndent(), escapeSolidus, getQuoteChar(),
+			simpleMode, isTrimStrings(), getUriResolver());
+		out.setWriter(w);
+		return w;
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackInputStream.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackInputStream.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackInputStream.java
index 40a03ec..2fdc81a 100644
--- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackInputStream.java
+++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackInputStream.java
@@ -17,6 +17,8 @@ import static org.apache.juneau.internal.IOUtils.*;
 
 import java.io.*;
 
+import org.apache.juneau.parser.*;
+
 /**
  * Specialized input stream for parsing MessagePack streams.
  *
@@ -27,6 +29,7 @@ import java.io.*;
  */
 public final class MsgPackInputStream extends InputStream {
 
+	private final ParserPipe pipe;
 	private final InputStream is;
 	private DataType currentDataType;
 	private long length;
@@ -57,10 +60,12 @@ public final class MsgPackInputStream extends InputStream {
 	/**
 	 * Constructor.
 	 *
-	 * @param is The input stream being wrapped.
+	 * @param pipe The parser input.
+	 * @throws Exception
 	 */
-	protected MsgPackInputStream(InputStream is) {
-		this.is = is;
+	protected MsgPackInputStream(ParserPipe pipe) throws Exception {
+		this.pipe = pipe;
+		this.is = pipe.getInputStream();
 	}
 
 	@Override /* InputStream */
@@ -485,4 +490,13 @@ public final class MsgPackInputStream extends InputStream {
 	int getPosition() {
 		return pos;
 	}
+
+	/**
+	 * Returns the pipe that was passed into the constructor.
+	 *
+	 * @return The pipe that was passed into the constructor.
+	 */
+	public ParserPipe getPipe() {
+		return pipe;
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java
index 727b465..0d9ab31 100644
--- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java
@@ -12,16 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.msgpack;
 
-import static org.apache.juneau.msgpack.DataType.*;
-
-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.parser.*;
-import org.apache.juneau.transform.*;
 
 /**
  * Parses a MessagePack stream into a POJO model.
@@ -37,14 +30,13 @@ import org.apache.juneau.transform.*;
  * 	<li>{@link MsgPackParserContext}
  * </ul>
  */
-@SuppressWarnings({ "rawtypes", "unchecked" })
 @Consumes("octal/msgpack")
 public class MsgPackParser extends InputStreamParser {
 
 	/** Default parser, all default settings.*/
 	public static final MsgPackParser DEFAULT = new MsgPackParser(PropertyStore.create());
 
-
+	
 	private final MsgPackParserContext ctx;
 
 	/**
@@ -62,168 +54,8 @@ public class MsgPackParser extends InputStreamParser {
 		return new MsgPackParserBuilder(propertyStore);
 	}
 
-	/**
-	 * Workhorse method.
-	 */
-	private <T> T parseAnything(MsgPackParserSession session, ClassMeta<T> eType, MsgPackInputStream is, Object outer,
-			BeanPropertyMeta pMeta) throws Exception {
-
-		if (eType == null)
-			eType = (ClassMeta<T>)object();
-		PojoSwap<T,Object> transform = (PojoSwap<T,Object>)eType.getPojoSwap();
-		ClassMeta<?> sType = eType.getSerializedClassMeta();
-		session.setCurrentClass(sType);
-
-		Object o = null;
-		DataType dt = is.readDataType();
-		int length = (int)is.readLength();
-
-		if (dt != DataType.NULL) {
-			if (dt == BOOLEAN)
-				o = is.readBoolean();
-			else if (dt == INT)
-				o = is.readInt();
-			else if (dt == LONG)
-				o = is.readLong();
-			else if (dt == FLOAT)
-				o = is.readFloat();
-			else if (dt == DOUBLE)
-				o = is.readDouble();
-			else if (dt == STRING)
-				o = session.trim(is.readString());
-			else if (dt == BIN)
-				o = is.readBinary();
-			else if (dt == ARRAY && sType.isObject()) {
-				ObjectList ol = new ObjectList(session);
-				for (int i = 0; i < length; i++)
-					ol.add(parseAnything(session, object(), is, outer, pMeta));
-				o = ol;
-			} else if (dt == MAP && sType.isObject()) {
-				ObjectMap om = new ObjectMap(session);
-				for (int i = 0; i < length; i++)
-					om.put(parseAnything(session, string(), is, outer, pMeta), parseAnything(session, object(), is, om, pMeta));
-				o = session.cast(om, pMeta, eType);
-			}
-
-			if (sType.isObject()) {
-				// Do nothing.
-			} else if (sType.isBoolean() || sType.isCharSequence() || sType.isChar() || sType.isNumber()) {
-				o = session.convertToType(o, sType);
-			} else if (sType.isMap()) {
-				if (dt == MAP) {
-					Map m = (sType.canCreateNewInstance(outer) ? (Map)sType.newInstance(outer) : new ObjectMap(session));
-					for (int i = 0; i < length; i++) {
-						Object key = parseAnything(session, sType.getKeyType(), is, outer, pMeta);
-						ClassMeta<?> vt = sType.getValueType();
-						Object value = parseAnything(session, vt, is, m, pMeta);
-						setName(vt, value, key);
-						m.put(key, value);
-					}
-					o = m;
-				} else {
-					throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, sType);
-				}
-			} else if (sType.canCreateNewBean(outer)) {
-				if (dt == MAP) {
-					BeanMap m = session.newBeanMap(outer, sType.getInnerClass());
-					for (int i = 0; i < length; i++) {
-						String pName = parseAnything(session, string(), is, m.getBean(false), null);
-						BeanPropertyMeta bpm = m.getPropertyMeta(pName);
-						if (bpm == null) {
-							if (pName.equals(session.getBeanTypePropertyName(eType)))
-								parseAnything(session, session.string(), is, null, null);
-							else
-								session.onUnknownProperty(pName, m, 0, is.getPosition());
-						} else {
-							ClassMeta<?> cm = bpm.getClassMeta();
-							Object value = parseAnything(session, cm, is, m.getBean(false), bpm);
-							setName(cm, value, pName);
-							bpm.set(m, pName, value);
-						}
-					}
-					o = m.getBean();
-				} else {
-					throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, sType);
-				}
-			} else if (sType.canCreateNewInstanceFromString(outer) && dt == STRING) {
-				o = sType.newInstanceFromString(outer, o == null ? "" : o.toString());
-			} else if (sType.canCreateNewInstanceFromNumber(outer) && dt.isOneOf(INT, LONG, FLOAT, DOUBLE)) {
-				o = sType.newInstanceFromNumber(session, outer, (Number)o);
-			} else if (sType.isCollection()) {
-				if (dt == MAP) {
-					ObjectMap m = new ObjectMap(session);
-					for (int i = 0; i < length; i++)
-						m.put(parseAnything(session, string(), is, outer, pMeta), parseAnything(session, object(), is, m, pMeta));
-					o = session.cast(m, pMeta, eType);
-				} else if (dt == ARRAY) {
-					Collection l = (
-						sType.canCreateNewInstance(outer)
-						? (Collection)sType.newInstance()
-						: new ObjectList(session)
-					);
-					for (int i = 0; i < length; i++)
-						l.add(parseAnything(session, sType.getElementType(), is, l, pMeta));
-					o = l;
-				} else {
-					throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, sType);
-				}
-			} else if (sType.isArray() || sType.isArgs()) {
-				if (dt == MAP) {
-					ObjectMap m = new ObjectMap(session);
-					for (int i = 0; i < length; i++)
-						m.put(parseAnything(session, string(), is, outer, pMeta), parseAnything(session, object(), is, m, pMeta));
-					o = session.cast(m, pMeta, eType);
-				} else if (dt == ARRAY) {
-					Collection l = (
-						sType.isCollection() && sType.canCreateNewInstance(outer)
-						? (Collection)sType.newInstance()
-						: new ObjectList(session)
-					);
-					for (int i = 0; i < length; i++)
-						l.add(parseAnything(session, sType.isArgs() ? sType.getArg(i) : sType.getElementType(), is, l, pMeta));
-					o = session.toArray(sType, l);
-				} else {
-					throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, sType);
-				}
-			} else if (dt == MAP) {
-				ObjectMap m = new ObjectMap(session);
-				for (int i = 0; i < length; i++)
-					m.put(parseAnything(session, string(), is, outer, pMeta), parseAnything(session, object(), is, m, pMeta));
-				if (m.containsKey(session.getBeanTypePropertyName(eType)))
-					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 {
-				throw new ParseException(session, "Invalid data type {0} encountered for parse type {1}", dt, sType);
-			}
-		}
-
-		if (transform != null && o != null)
-			o = transform.unswap(session, o, eType);
-
-		if (outer != null)
-			setParent(eType, o, outer);
-
-		return (T)o;
-	}
-
-
-	//--------------------------------------------------------------------------------
-	// Entry point methods
-	//--------------------------------------------------------------------------------
-
-	@Override /* Parser */
-	public MsgPackParserSession createSession(Object input, ObjectMap op, Method javaMethod, Object outer,
-			Locale locale, TimeZone timeZone, MediaType mediaType) {
-		return new MsgPackParserSession(ctx, op, input, javaMethod, outer, locale, timeZone, mediaType);
-	}
-
 	@Override /* Parser */
-	protected <T> T doParse(ParserSession session, ClassMeta<T> type) throws Exception {
-		MsgPackParserSession s = (MsgPackParserSession)session;
-		MsgPackInputStream is = s.getInputStream();
-		T o = parseAnything(s, type, is, s.getOuter(), null);
-		return o;
+	public MsgPackParserSession createSession(ParserSessionArgs args) {
+		return new MsgPackParserSession(ctx, args);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
index 93f16e6..8827370 100644
--- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParserSession.java
@@ -12,23 +12,23 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.msgpack;
 
-import java.io.*;
-import java.lang.reflect.*;
+import static org.apache.juneau.msgpack.DataType.*;
+
 import java.util.*;
 
 import org.apache.juneau.*;
-import org.apache.juneau.http.*;
 import org.apache.juneau.parser.*;
+import org.apache.juneau.transform.*;
 
 /**
  * Session object that lives for the duration of a single use of {@link MsgPackParser}.
  *
  * <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 against multiple inputs.
  */
-public final class MsgPackParserSession extends ParserSession {
-
-	private MsgPackInputStream inputStream;
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public final class MsgPackParserSession extends InputStreamParserSession {
 
 	/**
 	 * Create a new session using properties specified in the context.
@@ -36,46 +36,166 @@ public final class MsgPackParserSession extends ParserSession {
 	 * @param ctx
 	 * 	The context creating this session object.
 	 * 	The context contains all the configuration settings for this object.
-	 * @param input
-	 * 	The input.
-	 * 	Can be any of the following types:
-	 * 	<ul>
-	 * 		<li><jk>null</jk>
-	 * 		<li>{@link Reader}
-	 * 		<li>{@link CharSequence}
-	 * 		<li>{@link InputStream} containing UTF-8 encoded text.
-	 * 		<li>{@link File} containing system encoded text.
-	 * 	</ul>
-	 * @param op
-	 * 	The override properties.
-	 * 	These override any context properties defined in the context.
-	 * @param javaMethod The java method that called this parser, usually the method in a REST servlet.
-	 * @param outer The outer object for instantiating top-level non-static inner classes.
-	 * @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 args
+	 * 	Runtime session arguments.
 	 */
-	public MsgPackParserSession(MsgPackParserContext ctx, ObjectMap op, Object input, Method javaMethod, Object outer,
-			Locale locale, TimeZone timeZone, MediaType mediaType) {
-		super(ctx, op, input, javaMethod, outer, locale, timeZone, mediaType);
+	protected MsgPackParserSession(MsgPackParserContext ctx, ParserSessionArgs args) {
+		super(ctx, args);
 	}
 
 	@Override /* ParserSession */
-	public MsgPackInputStream getInputStream() throws ParseException {
-		if (inputStream == null)
-			inputStream = new MsgPackInputStream(super.getInputStream());
-		return inputStream;
+	protected <T> T doParse(ParserPipe pipe, ClassMeta<T> type) throws Exception {
+		MsgPackInputStream is = new MsgPackInputStream(pipe);
+		T o = parseAnything(type, is, getOuter(), null);
+		return o;
 	}
 
-	@Override /* ParserSession */
-	public Map<String,Object> getLastLocation() {
-		Map<String,Object> m = super.getLastLocation();
-		if (inputStream != null)
-			m.put("position", inputStream.getPosition());
-		return m;
+	/*
+	 * Workhorse method.
+	 */
+	private <T> T parseAnything(ClassMeta<T> eType, MsgPackInputStream is, Object outer, BeanPropertyMeta pMeta) throws Exception {
+
+		if (eType == null)
+			eType = (ClassMeta<T>)object();
+		PojoSwap<T,Object> transform = (PojoSwap<T,Object>)eType.getPojoSwap();
+		ClassMeta<?> sType = eType.getSerializedClassMeta();
+		setCurrentClass(sType);
+
+		Object o = null;
+		DataType dt = is.readDataType();
+		int length = (int)is.readLength();
+
+		if (dt != DataType.NULL) {
+			if (dt == BOOLEAN)
+				o = is.readBoolean();
+			else if (dt == INT)
+				o = is.readInt();
+			else if (dt == LONG)
+				o = is.readLong();
+			else if (dt == FLOAT)
+				o = is.readFloat();
+			else if (dt == DOUBLE)
+				o = is.readDouble();
+			else if (dt == STRING)
+				o = trim(is.readString());
+			else if (dt == BIN)
+				o = is.readBinary();
+			else if (dt == ARRAY && sType.isObject()) {
+				ObjectList ol = new ObjectList(this);
+				for (int i = 0; i < length; i++)
+					ol.add(parseAnything(object(), is, outer, pMeta));
+				o = ol;
+			} else if (dt == MAP && sType.isObject()) {
+				ObjectMap om = new ObjectMap(this);
+				for (int i = 0; i < length; i++)
+					om.put(parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, om, pMeta));
+				o = cast(om, pMeta, eType);
+			}
+
+			if (sType.isObject()) {
+				// Do nothing.
+			} else if (sType.isBoolean() || sType.isCharSequence() || sType.isChar() || sType.isNumber()) {
+				o = convertToType(o, sType);
+			} else if (sType.isMap()) {
+				if (dt == MAP) {
+					Map m = (sType.canCreateNewInstance(outer) ? (Map)sType.newInstance(outer) : new ObjectMap(this));
+					for (int i = 0; i < length; i++) {
+						Object key = parseAnything(sType.getKeyType(), is, outer, pMeta);
+						ClassMeta<?> vt = sType.getValueType();
+						Object value = parseAnything(vt, is, m, pMeta);
+						setName(vt, value, key);
+						m.put(key, value);
+					}
+					o = m;
+				} else {
+					throw new ParseException(loc(is), "Invalid data type {0} encountered for parse type {1}", dt, sType);
+				}
+			} else if (sType.canCreateNewBean(outer)) {
+				if (dt == MAP) {
+					BeanMap m = newBeanMap(outer, sType.getInnerClass());
+					for (int i = 0; i < length; i++) {
+						String pName = parseAnything(string(), is, m.getBean(false), null);
+						BeanPropertyMeta bpm = m.getPropertyMeta(pName);
+						if (bpm == null) {
+							if (pName.equals(getBeanTypePropertyName(eType)))
+								parseAnything(string(), is, null, null);
+							else
+								onUnknownProperty(is.getPipe(), pName, m, 0, is.getPosition());
+						} else {
+							ClassMeta<?> cm = bpm.getClassMeta();
+							Object value = parseAnything(cm, is, m.getBean(false), bpm);
+							setName(cm, value, pName);
+							bpm.set(m, pName, value);
+						}
+					}
+					o = m.getBean();
+				} else {
+					throw new ParseException(loc(is), "Invalid data type {0} encountered for parse type {1}", dt, sType);
+				}
+			} else if (sType.canCreateNewInstanceFromString(outer) && dt == STRING) {
+				o = sType.newInstanceFromString(outer, o == null ? "" : o.toString());
+			} else if (sType.canCreateNewInstanceFromNumber(outer) && dt.isOneOf(INT, LONG, FLOAT, DOUBLE)) {
+				o = sType.newInstanceFromNumber(this, outer, (Number)o);
+			} else if (sType.isCollection()) {
+				if (dt == MAP) {
+					ObjectMap m = new ObjectMap(this);
+					for (int i = 0; i < length; i++)
+						m.put(parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
+					o = cast(m, pMeta, eType);
+				} else if (dt == ARRAY) {
+					Collection l = (
+						sType.canCreateNewInstance(outer)
+						? (Collection)sType.newInstance()
+						: new ObjectList(this)
+					);
+					for (int i = 0; i < length; i++)
+						l.add(parseAnything(sType.getElementType(), is, l, pMeta));
+					o = l;
+				} else {
+					throw new ParseException(loc(is), "Invalid data type {0} encountered for parse type {1}", dt, sType);
+				}
+			} else if (sType.isArray() || sType.isArgs()) {
+				if (dt == MAP) {
+					ObjectMap m = new ObjectMap(this);
+					for (int i = 0; i < length; i++)
+						m.put(parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
+					o = cast(m, pMeta, eType);
+				} else if (dt == ARRAY) {
+					Collection l = (
+						sType.isCollection() && sType.canCreateNewInstance(outer)
+						? (Collection)sType.newInstance()
+						: new ObjectList(this)
+					);
+					for (int i = 0; i < length; i++)
+						l.add(parseAnything(sType.isArgs() ? sType.getArg(i) : sType.getElementType(), is, l, pMeta));
+					o = toArray(sType, l);
+				} else {
+					throw new ParseException(loc(is), "Invalid data type {0} encountered for parse type {1}", dt, sType);
+				}
+			} else if (dt == MAP) {
+				ObjectMap m = new ObjectMap(this);
+				for (int i = 0; i < length; i++)
+					m.put(parseAnything(string(), is, outer, pMeta), parseAnything(object(), is, m, pMeta));
+				if (m.containsKey(getBeanTypePropertyName(eType)))
+					o = cast(m, pMeta, eType);
+				else
+					throw new ParseException(loc(is), "Class ''{0}'' could not be instantiated.  Reason: ''{1}''",
+						sType.getInnerClass().getName(), sType.getNotABeanReason());
+			} else {
+				throw new ParseException(loc(is), "Invalid data type {0} encountered for parse type {1}", dt, sType);
+			}
+		}
+
+		if (transform != null && o != null)
+			o = transform.unswap(this, o, eType);
+
+		if (outer != null)
+			setParent(eType, o, outer);
+
+		return (T)o;
+	}
+
+	private ObjectMap loc(MsgPackInputStream is) {
+		return getLastLocation().append("position", is.getPosition());
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java
index ad5ed97..ad4dbce 100644
--- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializer.java
@@ -12,14 +12,9 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.msgpack;
 
-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.serializer.*;
-import org.apache.juneau.transform.*;
 
 /**
  * Serializes POJO models to MessagePack.
@@ -63,169 +58,8 @@ public class MsgPackSerializer extends OutputStreamSerializer {
 		return new MsgPackSerializerBuilder(propertyStore);
 	}
 
-	/**
-	 * Workhorse method.
-	 *
-	 * <p>
-	 * Determines the type of object, and then calls the appropriate type-specific serialization method.
-	 */
-	@SuppressWarnings({ "rawtypes", "unchecked" })
-	MsgPackOutputStream serializeAnything(MsgPackSerializerSession session, MsgPackOutputStream out, Object o,
-			ClassMeta<?> eType, String attrName, BeanPropertyMeta pMeta) throws Exception {
-
-		if (o == null)
-			return out.appendNull();
-
-		if (eType == null)
-			eType = object();
-
-		ClassMeta<?> aType;			// The actual type
-		ClassMeta<?> sType;			// The serialized type
-
-		aType = session.push(attrName, o, eType);
-		boolean isRecursion = aType == null;
-
-		// Handle recursion
-		if (aType == null) {
-			o = null;
-			aType = object();
-		}
-
-		sType = aType.getSerializedClassMeta();
-		String typeName = session.getBeanTypeName(eType, aType, pMeta);
-
-		// Swap if necessary
-		PojoSwap swap = aType.getPojoSwap();
-		if (swap != null) {
-			o = swap.swap(session, o);
-
-			// If the getSwapClass() method returns Object, we need to figure out
-			// the actual type now.
-			if (sType.isObject())
-				sType = session.getClassMetaForObject(o);
-		}
-
-		// '\0' characters are considered null.
-		if (o == null || (sType.isChar() && ((Character)o).charValue() == 0))
-			out.appendNull();
-		else if (sType.isBoolean())
-			out.appendBoolean((Boolean)o);
-		else if (sType.isNumber())
-			out.appendNumber((Number)o);
-		else if (sType.isBean())
-			serializeBeanMap(session, out, session.toBeanMap(o), typeName);
-		else if (sType.isUri() || (pMeta != null && pMeta.isUri()))
-			out.appendString(session.resolveUri(o.toString()));
-		else if (sType.isMap()) {
-			if (o instanceof BeanMap)
-				serializeBeanMap(session, out, (BeanMap)o, typeName);
-			else
-				serializeMap(session, out, (Map)o, eType);
-		}
-		else if (sType.isCollection()) {
-			serializeCollection(session, out, (Collection) o, eType);
-		}
-		else if (sType.isArray()) {
-			serializeCollection(session, out, toList(sType.getInnerClass(), o), eType);
-		} else
-			out.appendString(session.toString(o));
-
-		if (! isRecursion)
-			session.pop();
-		return out;
-	}
-
-	@SuppressWarnings({ "rawtypes", "unchecked" })
-	private void serializeMap(MsgPackSerializerSession session, MsgPackOutputStream out, Map m, ClassMeta<?> type)
-			throws Exception {
-
-		ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType();
-
-		m = session.sort(m);
-
-		// The map size may change as we're iterating over it, so
-		// grab a snapshot of the entries in a separate list.
-		List<SimpleMapEntry> entries = new ArrayList<SimpleMapEntry>(m.size());
-		for (Map.Entry e : (Set<Map.Entry>)m.entrySet())
-			entries.add(new SimpleMapEntry(e.getKey(), e.getValue()));
-
-		out.startMap(entries.size());
-
-		for (SimpleMapEntry e : entries) {
-			Object value = e.value;
-			Object key = session.generalize(e.key, keyType);
-
-			serializeAnything(session, out, key, keyType, null, null);
-			serializeAnything(session, out, value, valueType, null, null);
-		}
-	}
-
-	private void serializeBeanMap(MsgPackSerializerSession session, MsgPackOutputStream out, final BeanMap<?> m,
-			String typeName) throws Exception {
-
-		List<BeanPropertyValue> values = m.getValues(session.isTrimNulls(), typeName != null ? session.createBeanTypeNameProperty(m, typeName) : null);
-
-		int size = values.size();
-		for (BeanPropertyValue p : values)
-			if (p.getThrown() != null)
-				size--;
-		out.startMap(size);
-
-		for (BeanPropertyValue p : values) {
-			BeanPropertyMeta pMeta = p.getMeta();
-			ClassMeta<?> cMeta = p.getClassMeta();
-			String key = p.getName();
-			Object value = p.getValue();
-			Throwable t = p.getThrown();
-			if (t != null)
-				session.onBeanGetterException(pMeta, t);
-			else {
-				serializeAnything(session, out, key, null, null, null);
-				serializeAnything(session, out, value, cMeta, key, pMeta);
-			}
-		}
-	}
-
-	private static class SimpleMapEntry {
-		final Object key;
-		final Object value;
-
-		private SimpleMapEntry(Object key, Object value) {
-			this.key = key;
-			this.value = value;
-		}
-	}
-
-	@SuppressWarnings({"rawtypes", "unchecked"})
-	private void serializeCollection(MsgPackSerializerSession session, MsgPackOutputStream out, Collection c,
-			ClassMeta<?> type) throws Exception {
-
-		ClassMeta<?> elementType = type.getElementType();
-		List<Object> l = new ArrayList<Object>(c.size());
-
-		c = session.sort(c);
-		l.addAll(c);
-
-		out.startArray(l.size());
-
-		for (Object o : l)
-			serializeAnything(session, out, o, elementType, "<iterator>", null);
-	}
-
-
-	//--------------------------------------------------------------------------------
-	// Entry point methods
-	//--------------------------------------------------------------------------------
-
-	@Override /* Serializer */
-	public MsgPackSerializerSession createSession(ObjectMap op, Method javaMethod, Locale locale,
-			TimeZone timeZone, MediaType mediaType, UriContext uriContext) {
-		return new MsgPackSerializerSession(ctx, op, javaMethod, locale, timeZone, mediaType, uriContext);
-	}
-
 	@Override /* Serializer */
-	protected void doSerialize(SerializerSession session, SerializerOutput out, Object o) throws Exception {
-		MsgPackSerializerSession s = (MsgPackSerializerSession)session;
-		serializeAnything(s, s.getMsgPackOutputStream(out), o, s.getExpectedRootType(o), "root", null);
+	public OutputStreamSerializerSession createSession(SerializerSessionArgs args) {
+		return new MsgPackSerializerSession(ctx, args);
 	}
 }


Mime
View raw message