You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/01 00:07:59 UTC

[10/51] [partial] incubator-juneau git commit: Initial Juno contents from IBM JazzHub repo

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.java
new file mode 100755
index 0000000..621164e
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core.urlencoding;
+
+import java.io.*;
+
+import com.ibm.juno.core.parser.*;
+
+/**
+ * Same functionality as {@link ParserReader} except automatically decoded <code>%xx</code> escape sequences.
+ * <p>
+ * Escape sequences are assumed to be encoded UTF-8.  Extended Unicode (&gt;\u10000) is supported.
+ * <p>
+ * If decoding is enabled, the following character replacements occur so that boundaries are not lost:
+ * <ul>
+ * 	<li><js>'&'</js> -&gt; <js>'\u0001'</js>
+ * 	<li><js>'='</js> -&gt; <js>'\u0002'</js>
+ * </ul>
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class UonParserReader extends ParserReader {
+
+	private final boolean decodeChars;
+	private final char[] buff;
+	private int iCurrent, iEnd;
+
+	/**
+	 * Constructor for input from a {@link CharSequence}.
+	 *
+	 * @param in The character sequence being read from.
+	 * @param decodeChars If <jk>true</jk>, decode <code>%xx</code> escape sequences.
+	 */
+	public UonParserReader(CharSequence in, boolean decodeChars) {
+		super(in);
+		this.decodeChars = decodeChars;
+		if (in == null || ! decodeChars)
+			this.buff = new char[0];
+		else
+			this.buff = new char[in.length() < 1024 ? in.length() : 1024];
+	}
+
+	/**
+	 * Constructor for input from a {@link Reader}).
+	 *
+	 * @param r The Reader being wrapped.
+	 * @param buffSize Buffer size.
+	 * @param decodeChars If <jk>true</jk>, decode <code>%xx</code> escape sequences.
+	 */
+	public UonParserReader(Reader r, int buffSize, boolean decodeChars) {
+		super(r, buffSize);
+		this.decodeChars = decodeChars;
+		buffSize = decodeChars ? (buffSize <= 0 ? 1024 : Math.max(buffSize, 20)) : 0;
+		this.buff = new char[buffSize];
+	}
+
+	@Override /* Reader */
+	public final int read(char[] cbuf, int off, int len) throws IOException {
+
+		if (! decodeChars)
+			return super.read(cbuf, off, len);
+
+		// Copy any remainder to the beginning of the buffer.
+		int remainder = iEnd - iCurrent;
+		if (remainder > 0)
+			System.arraycopy(buff, iCurrent, buff, 0, remainder);
+		iCurrent = 0;
+
+		int expected = buff.length - remainder;
+
+		int x = super.read(buff, remainder, expected);
+		if (x == -1 && remainder == 0)
+			return -1;
+
+		iEnd = remainder + (x == -1 ? 0 : x);
+
+		int i = 0;
+		while (i < len) {
+			if (iCurrent >= iEnd)
+				return i;
+			char c = buff[iCurrent++];
+			if (c == '+') {
+				cbuf[off + i++] = ' ';
+			} else if (c == '&') {
+				cbuf[off + i++] = '\u0001';
+			} else if (c == '=') {
+				cbuf[off + i++] = '\u0002';
+			} else if (c != '%') {
+				cbuf[off + i++] = c;
+			} else {
+				int iMark = iCurrent-1;  // Keep track of current position.
+
+				// Stop if there aren't at least two more characters following '%' in the buffer,
+				// or there aren't at least two more positions open in cbuf to handle double-char chars.
+				if (iMark+2 >= iEnd || i+2 > len) {
+					iCurrent--;
+					return i;
+				}
+
+				int b0 = readEncodedByte();
+				int cx;
+
+				// 0xxxxxxx
+				if (b0 < 128) {
+					cx = b0;
+
+				// 10xxxxxx
+				} else if (b0 < 192) {
+					throw new IOException("Invalid hex value for first escape pattern in UTF-8 sequence:  " + b0);
+
+				// 110xxxxx	10xxxxxx
+				// 11000000(192) - 11011111(223)
+				} else if (b0 < 224) {
+					cx = readUTF8(b0-192, 1);
+					if (cx == -1) {
+						iCurrent = iMark;
+						return i;
+					}
+
+				// 1110xxxx	10xxxxxx	10xxxxxx
+				// 11100000(224) - 11101111(239)
+				} else if (b0 < 240) {
+					cx = readUTF8(b0-224, 2);
+					if (cx == -1) {
+						iCurrent = iMark;
+						return i;
+					}
+
+				// 11110xxx	10xxxxxx	10xxxxxx	10xxxxxx
+				// 11110000(240) - 11110111(247)
+				} else if (b0 < 248) {
+					cx = readUTF8(b0-240, 3);
+					if (cx == -1) {
+						iCurrent = iMark;
+						return i;
+					}
+
+				} else
+					throw new IOException("Invalid hex value for first escape pattern in UTF-8 sequence:  " + b0);
+
+				if (cx < 0x10000)
+					cbuf[off + i++] = (char)cx;
+				else {
+					cx -= 0x10000;
+					cbuf[off + i++] = (char)(0xd800 + (cx >> 10));
+					cbuf[off + i++] = (char)(0xdc00 + (cx & 0x3ff));
+				}
+			}
+		}
+		return i;
+	}
+
+	private final int readUTF8(int n, final int numBytes) throws IOException {
+		if (iCurrent + numBytes*3 > iEnd)
+			return -1;
+		for (int i = 0; i < numBytes; i++) {
+			n <<= 6;
+			n += readHex()-128;
+		}
+		return n;
+	}
+
+	private final int readHex() throws IOException {
+		int c = buff[iCurrent++];
+		if (c != '%')
+			throw new IOException("Did not find expected '%' character in UTF-8 sequence.");
+		return readEncodedByte();
+	}
+
+	private final int readEncodedByte() throws IOException {
+		if (iEnd <= iCurrent + 1)
+			throw new IOException("Incomplete trailing escape pattern");
+		int h = buff[iCurrent++];
+		int l = buff[iCurrent++];
+		h = fromHexChar(h);
+		l = fromHexChar(l);
+		return (h << 4) + l;
+	}
+
+	private final int fromHexChar(int c) throws IOException {
+		if (c >= '0' && c <= '9')
+			return c - '0';
+		if (c >= 'a' && c <= 'f')
+			return 10 + c - 'a';
+		if (c >= 'A' && c <= 'F')
+			return 10 + c - 'A';
+		throw new IOException("Invalid hex character '"+c+"' found in escape pattern.");
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Encoding.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Encoding.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Encoding.class
new file mode 100755
index 0000000..5fca57c
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Encoding.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Readable.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Readable.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Readable.class
new file mode 100755
index 0000000..c4e6b57
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Readable.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Simple.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Simple.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Simple.class
new file mode 100755
index 0000000..1ecf09c
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$Simple.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$SimpleEncoding.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$SimpleEncoding.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$SimpleEncoding.class
new file mode 100755
index 0000000..2d0a383
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer$SimpleEncoding.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer.class
new file mode 100755
index 0000000..4ca7cf1
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer.java
new file mode 100755
index 0000000..cb16038
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializer.java
@@ -0,0 +1,532 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core.urlencoding;
+
+import static com.ibm.juno.core.serializer.SerializerProperties.*;
+import static com.ibm.juno.core.urlencoding.UonSerializerProperties.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.ibm.juno.core.*;
+import com.ibm.juno.core.annotation.*;
+import com.ibm.juno.core.filter.*;
+import com.ibm.juno.core.serializer.*;
+
+/**
+ * Serializes POJO models to UON (a notation for URL-encoded query parameter values).
+ *
+ *
+ * <h6 class='topic'>Media types</h6>
+ * <p>
+ * 	Handles <code>Accept</code> types: <code>text/uon</code>
+ * <p>
+ * 	Produces <code>Content-Type</code> types: <code>text/uon</code>
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ * 	This serializer provides several serialization options.  Typically, one of the predefined DEFAULT serializers will be sufficient.
+ * 	However, custom serializers can be constructed to fine-tune behavior.
+ *
+ *
+ * <h6 class='topic'>Configurable properties</h6>
+ * <p>
+ * 	This class has the following properties associated with it:
+ * <ul>
+ * 	<li>{@link UonSerializerProperties}
+ * 	<li>{@link SerializerProperties}
+ * 	<li>{@link BeanContextProperties}
+ * </ul>
+ * <p>
+ * 	The following shows a sample object defined in Javascript:
+ * </p>
+ * <p class='bcode'>
+ * 	{
+ * 		id: 1,
+ * 		name: <js>'John Smith'</js>,
+ * 		uri: <js>'http://sample/addressBook/person/1'</js>,
+ * 		addressBookUri: <js>'http://sample/addressBook'</js>,
+ * 		birthDate: <js>'1946-08-12T00:00:00Z'</js>,
+ * 		otherIds: <jk>null</jk>,
+ * 		addresses: [
+ * 			{
+ * 				uri: <js>'http://sample/addressBook/address/1'</js>,
+ * 				personUri: <js>'http://sample/addressBook/person/1'</js>,
+ * 				id: 1,
+ * 				street: <js>'100 Main Street'</js>,
+ * 				city: <js>'Anywhereville'</js>,
+ * 				state: <js>'NY'</js>,
+ * 				zip: 12345,
+ * 				isCurrent: <jk>true</jk>,
+ * 			}
+ * 		]
+ * 	}
+ * </p>
+ * <p>
+ * 	Using the "strict" syntax defined in this document, the equivalent
+ * 		UON notation would be as follows:
+ * </p>
+ * <p class='bcode'>
+ * 	$o(
+ * 		<xa>id</xa>=$n(<xs>1</xs>),
+ * 		<xa>name</xa>=<xs>John+Smith</xs>,
+ * 		<xa>uri</xa>=<xs>http://sample/addressBook/person/1</xs>,
+ * 		<xa>addressBookUri</xa>=<xs>http://sample/addressBook</xs>,
+ * 		<xa>birthDate</xa>=<xs>1946-08-12T00:00:00Z</xs>,
+ * 		<xa>otherIds</xa>=<xs>%00</xs>,
+ * 		<xa>addresses</xa>=$a(
+ * 			$o(
+ * 				<xa>uri</xa>=<xs>http://sample/addressBook/address/1</xs>,
+ * 				<xa>personUri</xa>=<xs>http://sample/addressBook/person/1</xs>,
+ * 				<xa>id</xa>=$n(<xs>1</xs>),
+ * 				<xa>street</xa>=<xs>100+Main+Street</xs>,
+ * 				<xa>city</xa>=<xs>Anywhereville</xs>,
+ * 				<xa>state</xa>=<xs>NY</xs>,
+ * 				<xa>zip</xa>=$n(<xs>12345</xs>),
+ * 				<xa>isCurrent</xa>=$b(<xs>true</xs>)
+ * 			)
+ * 		)
+ * 	)
+ * </p>
+ * <p>
+ * 	A secondary "lax" syntax is available when the data type of the
+ * 		values are already known on the receiving end of the transmission:
+ * </p>
+ * <p class='bcode'>
+ * 	(
+ * 		<xa>id</xa>=<xs>1</xs>,
+ * 		<xa>name</xa>=<xs>John+Smith</xs>,
+ * 		<xa>uri</xa>=<xs>http://sample/addressBook/person/1</xs>,
+ * 		<xa>addressBookUri</xa>=<xs>http://sample/addressBook</xs>,
+ * 		<xa>birthDate</xa>=<xs>1946-08-12T00:00:00Z</xs>,
+ * 		<xa>otherIds</xa>=<xs>%00</xs>,
+ * 		<xa>addresses</xa>=(
+ * 			(
+ * 				<xa>uri</xa>=<xs>http://sample/addressBook/address/1</xs>,
+ * 				<xa>personUri</xa>=<xs>http://sample/addressBook/person/1</xs>,
+ * 				<xa>id</xa>=<xs>1</xs>,
+ * 				<xa>street</xa>=<xs>100+Main+Street</xs>,
+ * 				<xa>city</xa>=<xs>Anywhereville</xs>,
+ * 				<xa>state</xa>=<xs>NY</xs>,
+ * 				<xa>zip</xa>=<xs>12345</xs>,
+ * 				<xa>isCurrent</xa>=<xs>true</xs>
+ * 			)
+ * 		)
+ * 	)
+ * </p>
+ *
+ *
+ * <h6 class='topic'>Examples</h6>
+ * <p class='bcode'>
+ * 	<jc>// Serialize a Map</jc>
+ * 	Map m = <jk>new</jk> ObjectMap(<js>"{a:'b',c:1,d:false,e:['f',1,false],g:{h:'i'}}"</js>);
+ *
+ * 	<jc>// Serialize to value equivalent to JSON.</jc>
+ * 	<jc>// Produces "$o(a=b,c=$n(1),d=$b(false),e=$a(f,$n(1),$b(false)),g=$o(h=i))"</jc>
+ * 	String s = UonSerializer.<jsf>DEFAULT</jsf>.serialize(s);
+ *
+ * 	<jc>// Serialize to simplified value (for when data type is already known by receiver).</jc>
+ * 	<jc>// Produces "(a=b,c=1,d=false,e=(f,1,false),g=(h=i))"</jc>
+ * 	String s = UonSerializer.<jsf>DEFAULT_SIMPLE</jsf>.serialize(s);
+ *
+ * 	<jc>// Serialize a bean</jc>
+ * 	<jk>public class</jk> Person {
+ * 		<jk>public</jk> Person(String s);
+ * 		<jk>public</jk> String getName();
+ * 		<jk>public int</jk> getAge();
+ * 		<jk>public</jk> Address getAddress();
+ * 		<jk>public boolean</jk> deceased;
+ * 	}
+ *
+ * 	<jk>public class</jk> Address {
+ * 		<jk>public</jk> String getStreet();
+ * 		<jk>public</jk> String getCity();
+ * 		<jk>public</jk> String getState();
+ * 		<jk>public int</jk> getZip();
+ * 	}
+ *
+ * 	Person p = <jk>new</jk> Person(<js>"John Doe"</js>, 23, <js>"123 Main St"</js>, <js>"Anywhere"</js>, <js>"NY"</js>, 12345, <jk>false</jk>);
+ *
+ * 	<jc>// Produces "$o(name=John Doe,age=23,address=$o(street=123 Main St,city=Anywhere,state=NY,zip=$n(12345)),deceased=$b(false))"</jc>
+ * 	String s = UonSerializer.<jsf>DEFAULT</jsf>.serialize(s);
+ *
+ * 	<jc>// Produces "(name=John Doe,age=23,address=(street=123 Main St,city=Anywhere,state=NY,zip=12345),deceased=false)"</jc>
+ * 	String s = UonSerializer.<jsf>DEFAULT_SIMPLE</jsf>.serialize(s);
+ * </p>
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+@Produces("text/uon")
+public class UonSerializer extends WriterSerializer {
+
+	/** Reusable instance of {@link UonSerializer}, all default settings. */
+	public static final UonSerializer DEFAULT = new UonSerializer().lock();
+
+	/** Reusable instance of {@link UonSerializer.Simple}. */
+	public static final UonSerializer DEFAULT_SIMPLE = new Simple().lock();
+
+	/** Reusable instance of {@link UonSerializer.Readable}. */
+	public static final UonSerializer DEFAULT_READABLE = new Readable().lock();
+
+	/** Reusable instance of {@link UonSerializer.Encoding}. */
+	public static final UonSerializer DEFAULT_ENCODING = new Encoding().lock();
+
+	/** Reusable instance of {@link UonSerializer.SimpleEncoding}. */
+	public static final UonSerializer DEFAULT_SIMPLE_ENCODING = new SimpleEncoding().lock();
+
+	/**
+	 * Equivalent to <code><jk>new</jk> UonSerializer().setProperty(UonSerializerProperties.<jsf>UON_simpleMode</jsf>,<jk>true</jk>);</code>.
+	 */
+	@Produces(value={"text/uon-simple"},contentType="text/uon")
+	public static class Simple extends UonSerializer {
+		/** Constructor */
+		public Simple() {
+			setProperty(UON_simpleMode, true);
+		}
+	}
+
+	/**
+	 * Equivalent to <code><jk>new</jk> UonSerializer().setProperty(UonSerializerProperties.<jsf>UON_useWhitespace</jsf>,<jk>true</jk>);</code>.
+	 */
+	public static class Readable extends UonSerializer {
+		/** Constructor */
+		public Readable() {
+			setProperty(UON_useWhitespace, true);
+			setProperty(SERIALIZER_useIndentation, true);
+		}
+	}
+
+	/**
+	 * Equivalent to <code><jk>new</jk> UonSerializer().setProperty(UonSerializerProperties.<jsf>UON_encodeChars</jsf>,<jk>true</jk>);</code>.
+	 */
+	public static class Encoding extends UonSerializer {
+		/** Constructor */
+		public Encoding() {
+			setProperty(UON_encodeChars, true);
+		}
+	}
+
+	/**
+	 * Equivalent to <code><jk>new</jk> UonSerializer().setProperty(UonSerializerProperties.<jsf>UON_simpleMode</jsf>,<jk>true</jk>).setProperty(UonSerializerProperties.<jsf>UON_encodeChars</jsf>,<jk>true</jk>);</code>.
+	 */
+	@Produces(value={"text/uon-simple"},contentType="text/uon")
+	public static class SimpleEncoding extends UonSerializer {
+		/** Constructor */
+		public SimpleEncoding() {
+			setProperty(UON_simpleMode, true);
+			setProperty(UON_encodeChars, true);
+		}
+	}
+
+	/** UON serializer properties currently set on this serializer. */
+	protected transient UonSerializerProperties usp = new UonSerializerProperties();
+
+	/** URL-Encoding properties currently set on this serializer. */
+	protected transient UrlEncodingProperties uep = new UrlEncodingProperties();
+
+
+	/**
+	 * Workhorse method. Determines the type of object, and then calls the
+	 * appropriate type-specific serialization method.
+	 *
+	 * @param out The writer to serialize to.
+	 * @param o The object being serialized.
+	 * @param eType The expected type of the object if this is a bean property.
+	 * @param ctx The context that exist for the duration of a serialize.
+	 * @param attrName The bean property name if this is a bean property.  <jk>null</jk> if this isn't a bean property being serialized.
+	 * @param pMeta The bean property metadata.
+	 * @param quoteEmptyStrings <jk>true</jk> if this is the first entry in an array.
+	 * @param isTop If we haven't recursively called this method.
+	 * @return The same writer passed in.
+	 * @throws SerializeException
+	 */
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	protected SerializerWriter serializeAnything(UonSerializerWriter out, Object o, ClassMeta<?> eType, UonSerializerContext ctx,
+			String attrName, BeanPropertyMeta pMeta, boolean quoteEmptyStrings, boolean isTop) throws SerializeException {
+		try {
+
+			BeanContext bc = ctx.getBeanContext();
+
+			if (o == null) {
+				out.appendObject(null, false, false, isTop);
+				return out;
+			}
+
+			if (eType == null)
+				eType = object();
+
+			boolean addClassAttr;		// Add "_class" attribute to element?
+			ClassMeta<?> aType;			// The actual type
+			ClassMeta<?> gType;			// The generic type
+
+			aType = ctx.push(attrName, o, eType);
+			boolean isRecursion = aType == null;
+
+			// Handle recursion
+			if (aType == null) {
+				o = null;
+				aType = object();
+			}
+
+			gType = aType.getFilteredClassMeta();
+			addClassAttr = (ctx.isAddClassAttrs() && ! eType.equals(aType));
+
+			// Filter if necessary
+			PojoFilter filter = aType.getPojoFilter();				// The filter
+			if (filter != null) {
+				o = filter.filter(o);
+
+				// If the filter's getFilteredClass() method returns Object, we need to figure out
+				// the actual type now.
+				if (gType.isObject())
+					gType = bc.getClassMetaForObject(o);
+			}
+
+			// '\0' characters are considered null.
+			if (o == null || (gType.isChar() && ((Character)o).charValue() == 0))
+				out.appendObject(null, false, false, isTop);
+			else if (gType.hasToObjectMapMethod())
+				serializeMap(out, gType.toObjectMap(o), eType, ctx);
+			else if (gType.isBean())
+				serializeBeanMap(out, bc.forBean(o), addClassAttr, ctx);
+			else if (gType.isUri() || (pMeta != null && (pMeta.isUri() || pMeta.isBeanUri())))
+				out.appendUri(o, isTop);
+			else if (gType.isMap()) {
+				if (o instanceof BeanMap)
+					serializeBeanMap(out, (BeanMap)o, addClassAttr, ctx);
+				else
+					serializeMap(out, (Map)o, eType, ctx);
+			}
+			else if (gType.isCollection()) {
+				if (addClassAttr)
+					serializeCollectionMap(out, (Collection)o, gType, ctx);
+				else
+					serializeCollection(out, (Collection) o, eType, ctx);
+			}
+			else if (gType.isArray()) {
+				if (addClassAttr)
+					serializeCollectionMap(out, toList(gType.getInnerClass(), o), gType, ctx);
+				else
+					serializeCollection(out, toList(gType.getInnerClass(), o), eType, ctx);
+			}
+			else {
+				out.appendObject(o, quoteEmptyStrings, false, isTop);
+			}
+
+			if (! isRecursion)
+				ctx.pop();
+			return out;
+		} catch (SerializeException e) {
+			throw e;
+		} catch (StackOverflowError e) {
+			throw e;
+		} catch (Throwable e) {
+			throw new SerializeException("Exception occured trying to process object of type ''{0}''", (o == null ? null : o.getClass().getName())).initCause(e);
+		}
+	}
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	private SerializerWriter serializeMap(UonSerializerWriter out, Map m, ClassMeta<?> type, UonSerializerContext ctx) throws IOException, SerializeException {
+
+		m = sort(ctx, m);
+
+		ClassMeta<?> keyType = type.getKeyType(), valueType = type.getValueType();
+
+		int depth = ctx.getIndent();
+		out.startFlag('o');
+
+		Iterator mapEntries = m.entrySet().iterator();
+
+		while (mapEntries.hasNext()) {
+			Map.Entry e = (Map.Entry) mapEntries.next();
+			Object value = e.getValue();
+			Object key = generalize(ctx, e.getKey(), keyType);
+			out.cr(depth).appendObject(key, ctx.useWhitespace, false, false).append('=');
+			serializeAnything(out, value, valueType, ctx, (key == null ? null : key.toString()), null, ctx.useWhitespace, false);
+			if (mapEntries.hasNext())
+				out.append(',');
+		}
+
+		if (m.size() > 0)
+			out.cr(depth-1);
+		out.append(')');
+
+		return out;
+	}
+
+	@SuppressWarnings({ "rawtypes" })
+	private SerializerWriter serializeCollectionMap(UonSerializerWriter out, Collection o, ClassMeta<?> type, UonSerializerContext ctx) throws IOException, SerializeException {
+		int i = ctx.getIndent();
+		out.startFlag('o').nl();
+		out.append(i, "_class=").appendObject(type, false, false, false).append(',').nl();
+		out.append(i, "items=");
+		ctx.indent++;
+		serializeCollection(out, o, type, ctx);
+		ctx.indent--;
+
+		if (o.size() > 0)
+			out.cr(i-1);
+		out.append(')');
+
+		return out;
+	}
+
+	@SuppressWarnings({ "rawtypes" })
+	private SerializerWriter serializeBeanMap(UonSerializerWriter out, BeanMap m, boolean addClassAttr, UonSerializerContext ctx) throws IOException, SerializeException {
+		int depth = ctx.getIndent();
+
+		out.startFlag('o');
+
+		Iterator mapEntries = m.entrySet().iterator();
+
+		// Print out "_class" attribute on this bean if required.
+		if (addClassAttr) {
+			String attr = "_class";
+			out.cr(depth).appendObject(attr, false, false, false).append('=').append(m.getClassMeta().getInnerClass().getName());
+			if (mapEntries.hasNext())
+				out.append(',');
+		}
+
+		boolean addComma = false;
+
+		while (mapEntries.hasNext()) {
+			BeanMapEntry p = (BeanMapEntry)mapEntries.next();
+			BeanPropertyMeta pMeta = p.getMeta();
+
+			String key = p.getKey();
+			Object value = null;
+			try {
+				value = p.getValue();
+			} catch (StackOverflowError e) {
+				throw e;
+			} catch (Throwable t) {
+				ctx.addBeanGetterWarning(pMeta, t);
+			}
+
+			if (canIgnoreValue(ctx, pMeta.getClassMeta(), key, value))
+				continue;
+
+			if (addComma)
+				out.append(',');
+
+			out.cr(depth).appendObject(key, false, false, false).append('=');
+
+			serializeAnything(out, value, pMeta.getClassMeta(), ctx, key, pMeta, false, false);
+
+			addComma = true;
+		}
+
+		if (m.size() > 0)
+			out.cr(depth-1);
+		out.append(')');
+
+		return out;
+	}
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	private SerializerWriter serializeCollection(UonSerializerWriter out, Collection c, ClassMeta<?> type, UonSerializerContext ctx) throws IOException, SerializeException {
+
+		ClassMeta<?> elementType = type.getElementType();
+
+		c = sort(ctx, c);
+
+		out.startFlag('a');
+
+		int depth = ctx.getIndent();
+		boolean quoteEmptyString = (c.size() == 1 || ctx.useWhitespace);
+
+		for (Iterator i = c.iterator(); i.hasNext();) {
+			out.cr(depth);
+			serializeAnything(out, i.next(), elementType, ctx, "<iterator>", null, quoteEmptyString, false);
+			if (i.hasNext())
+				out.append(',');
+		}
+
+		if (c.size() > 0)
+			out.cr(depth-1);
+		out.append(')');
+
+		return out;
+	}
+
+	//--------------------------------------------------------------------------------
+	// Overridden methods
+	//--------------------------------------------------------------------------------
+
+	@Override /* Serializer */
+	public UonSerializerContext createContext(ObjectMap properties, Method javaMethod) {
+		return new UonSerializerContext(getBeanContext(), sp, usp, uep, properties, javaMethod);
+	}
+
+	@Override /* Serializer */
+	protected void doSerialize(Object o, Writer out, SerializerContext ctx) throws IOException, SerializeException {
+		UonSerializerContext uctx = (UonSerializerContext)ctx;
+		serializeAnything(uctx.getWriter(out), o, null, uctx, "root", null, false, true);
+	}
+
+	@Override /* CoreApi */
+	public UonSerializer setProperty(String property, Object value) throws LockedException {
+		checkLock();
+		if (! usp.setProperty(property, value))
+			if (! uep.setProperty(property, value))
+				super.setProperty(property, value);
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public UonSerializer setProperties(ObjectMap properties) throws LockedException {
+		for (Map.Entry<String,Object> e : properties.entrySet())
+			setProperty(e.getKey(), e.getValue());
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public UonSerializer addNotBeanClasses(Class<?>...classes) throws LockedException {
+		super.addNotBeanClasses(classes);
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public UonSerializer addFilters(Class<?>...classes) throws LockedException {
+		super.addFilters(classes);
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public <T> UonSerializer addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException {
+		super.addImplClass(interfaceClass, implClass);
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public UonSerializer setClassLoader(ClassLoader classLoader) throws LockedException {
+		super.setClassLoader(classLoader);
+		return this;
+	}
+
+	@Override /* Lockable */
+	public UonSerializer lock() {
+		super.lock();
+		return this;
+	}
+
+	@Override /* Lockable */
+	public UonSerializer clone() {
+		try {
+			UonSerializer c = (UonSerializer)super.clone();
+			c.usp = usp.clone();
+			c.uep = uep.clone();
+			return c;
+		} catch (CloneNotSupportedException e) {
+			throw new RuntimeException(e); // Shouldn't happen
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerContext.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerContext.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerContext.class
new file mode 100755
index 0000000..d1e9fd4
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerContext.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerContext.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerContext.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerContext.java
new file mode 100755
index 0000000..849afef
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerContext.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core.urlencoding;
+
+import static com.ibm.juno.core.urlencoding.UonSerializerProperties.*;
+import static com.ibm.juno.core.urlencoding.UrlEncodingProperties.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+
+import com.ibm.juno.core.*;
+import com.ibm.juno.core.serializer.*;
+
+/**
+ * Context object that lives for the duration of a single serialization of {@link UonSerializer} and {@link UrlEncodingSerializer}.
+ * <p>
+ * 	See {@link SerializerContext} for details.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class UonSerializerContext extends SerializerContext {
+
+	boolean simpleMode, useWhitespace, encodeChars, expandedParams;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param beanContext The bean context being used by the serializer.
+	 * @param sp Default general serializer properties.
+	 * @param usp Default UON serializer properties.
+	 * @param uep Default URL-Encoding properties.
+	 * @param op Override properties.
+	 * @param javaMethod Java method that invoked this serializer.
+	 * 	When using the REST API, this is the Java method invoked by the REST call.
+	 * 	Can be used to access annotations defined on the method or class.
+	 */
+	protected UonSerializerContext(BeanContext beanContext, SerializerProperties sp, UonSerializerProperties usp, UrlEncodingProperties uep, ObjectMap op, Method javaMethod) {
+		super(beanContext, sp, op, javaMethod);
+		if (op == null || op.isEmpty()) {
+			simpleMode = usp.simpleMode;
+			useWhitespace = usp.useWhitespace;
+			encodeChars = usp.encodeChars;
+			expandedParams = uep.expandedParams;
+		} else {
+			simpleMode = op.getBoolean(UON_simpleMode, usp.simpleMode);
+			useWhitespace = op.getBoolean(UON_useWhitespace, usp.useWhitespace);
+			encodeChars = op.getBoolean(UON_encodeChars, usp.encodeChars);
+			expandedParams = op.getBoolean(URLENC_expandedParams, uep.expandedParams);
+
+		}
+	}
+
+	/**
+	 * Returns the {@link UonSerializerProperties#UON_simpleMode} setting value in this context.
+	 *
+	 * @return The {@link UonSerializerProperties#UON_simpleMode} setting value in this context.
+	 */
+	public final boolean isSimpleMode() {
+		return simpleMode;
+	}
+
+	/**
+	 * Returns the {@link UonSerializerProperties#UON_encodeChars} setting value in this context.
+	 *
+	 * @return The {@link UonSerializerProperties#UON_encodeChars} setting value in this context.
+	 */
+	public final boolean isEncodeChars() {
+		return encodeChars;
+	}
+
+	/**
+	 * Returns the {@link UrlEncodingProperties#URLENC_expandedParams} setting value in this context.
+	 *
+	 * @return The {@link UrlEncodingProperties#URLENC_expandedParams} setting value in this context.
+	 */
+	public final boolean isExpandedParams() {
+		return expandedParams;
+	}
+
+	/**
+	 * Wraps the specified writer in a {@link UonSerializerWriter}.
+	 *
+	 * @param out The writer to wrap.
+	 * @return The wrapped writer.
+	 */
+	protected UonSerializerWriter getWriter(Writer out) {
+		if (out instanceof UonSerializerWriter)
+			return (UonSerializerWriter)out;
+		return new UonSerializerWriter(out, useWhitespace, isSimpleMode(), isEncodeChars(), getRelativeUriBase(), getAbsolutePathUriBase());
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerProperties.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerProperties.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerProperties.class
new file mode 100755
index 0000000..d8f0914
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerProperties.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerProperties.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerProperties.java
new file mode 100755
index 0000000..e6912fb
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerProperties.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core.urlencoding;
+
+import com.ibm.juno.core.*;
+import com.ibm.juno.core.serializer.*;
+
+/**
+ * Configurable properties on the {@link UonSerializer} and {@link UrlEncodingSerializer} classes.
+ * <p>
+ * 	Use the {@link UonSerializer#setProperty(String, Object)} method to set property values.
+ * <p>
+ * 	In addition to these properties, the following properties are also applicable for {@link UonSerializer}.
+ * <ul>
+ * 	<li>{@link SerializerProperties}
+ * 	<li>{@link BeanContextProperties}
+ * </ul>
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class UonSerializerProperties implements Cloneable {
+
+	/**
+	 * Use simplified output ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * If <jk>true</jk>, type flags will not be prepended to values in most cases.
+	 * <p>
+	 * Use this setting if the data types of the values (e.g. object/array/boolean/number/string)
+	 * 	is known on the receiving end.
+	 * <p>
+	 * It should be noted that the default behavior produces a data structure that can
+	 * 	be losslessly converted into JSON, and any JSON can be losslessly represented
+	 * 	in a URL-encoded value.  However, this strict equivalency does not exist
+	 * 	when simple mode is used.
+	 * <p>
+	 * <table class='styled'>
+	 * 	<tr>
+	 * 		<th>Input (in JSON)</th>
+	 * 		<th>Normal mode output</th>
+	 * 		<th>Simple mode output</th>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td class='code'>{foo:'bar',baz:'bing'}</td>
+	 * 		<td class='code'>$o(foo=bar,baz=bing)</td>
+	 * 		<td class='code'>(foo=bar,baz=bing)</td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td class='code'>{foo:{bar:'baz'}}</td>
+	 * 		<td class='code'>$o(foo=$o(bar=baz))</td>
+	 * 		<td class='code'>(foo=(bar=baz))</td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td class='code'>['foo','bar']</td>
+	 * 		<td class='code'>$a(foo,bar)</td>
+	 * 		<td class='code'>(foo,bar)</td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td class='code'>['foo',['bar','baz']]</td>
+	 * 		<td class='code'>$a(foo,$a(bar,baz))</td>
+	 * 		<td class='code'>(foo,(bar,baz))</td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td class='code'>true</td>
+	 * 		<td class='code'>$b(true)</td>
+	 * 		<td class='code'>true</td>
+	 * 	</tr>
+	 * 	<tr>
+	 * 		<td class='code'>123</td>
+	 * 		<td class='code'>$n(123)</td>
+	 * 		<td class='code'>123</td>
+	 * 	</tr>
+	 * </table>
+	 */
+	public static final String UON_simpleMode = "UonSerializer.simpleMode";
+
+	/**
+	 * Use whitespace in output ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * If <jk>true</jk>, whitespace is added to the output to improve readability.
+	 */
+	public static final String UON_useWhitespace = "UonSerializer.useWhitespace";
+
+	/**
+	 * Encode non-valid URI characters to <js>"%xx"</js> constructs. ({@link Boolean}, default=<jk>false</jk> for {@link UonSerializer}, <jk>true</jk> for {@link UrlEncodingSerializer}).
+	 * <p>
+	 * If <jk>true</jk>, non-valid URI characters will be converted to <js>"%xx"</js> sequences.
+	 * Set to <jk>false</jk> if parameter value is being passed to some other code that will already
+	 * 	perform URL-encoding of non-valid URI characters.
+	 */
+	public static final String UON_encodeChars = "UonSerializer.encodeChars";
+
+	boolean
+		simpleMode = false,
+		useWhitespace = false,
+		encodeChars = false;
+
+	/**
+	 * Sets the specified property value.
+	 * @param property The property name.
+	 * @param value The property value.
+	 * @return <jk>true</jk> if property name was valid and property was set.
+	 */
+	public boolean setProperty(String property, Object value) {
+		BeanContext bc = BeanContext.DEFAULT;
+		if (property.equals(UON_simpleMode))
+			simpleMode = bc.convertToType(value, Boolean.class);
+		else if (property.equals(UON_useWhitespace))
+			useWhitespace = bc.convertToType(value, Boolean.class);
+		else if (property.equals(UON_encodeChars))
+			encodeChars = bc.convertToType(value, Boolean.class);
+		else
+			return false;
+		return true;
+	}
+
+
+	//--------------------------------------------------------------------------------
+	// Overridden methods
+	//--------------------------------------------------------------------------------
+
+	@Override /* Cloneable */
+	public UonSerializerProperties clone() {
+		try {
+			return (UonSerializerProperties)super.clone();
+		} catch (CloneNotSupportedException e) {
+			throw new RuntimeException(e); // Shouldn't happen
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerWriter.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerWriter.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerWriter.class
new file mode 100755
index 0000000..4fc3d48
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerWriter.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerWriter.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerWriter.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerWriter.java
new file mode 100755
index 0000000..3205234
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonSerializerWriter.java
@@ -0,0 +1,265 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core.urlencoding;
+
+import java.io.*;
+
+import com.ibm.juno.core.serializer.*;
+import com.ibm.juno.core.utils.*;
+
+/**
+ * Specialized writer for serializing UON-encoded text.
+ * <p>
+ * 	<b>Note:  This class is not intended for external use.</b>
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class UonSerializerWriter extends SerializerWriter {
+
+	private final boolean simpleMode, encodeChars;
+
+	// Characters that do not need to be URL-encoded in strings.
+	private static final AsciiSet unencodedChars = new AsciiSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;/?:@-_.!*'$(),~=");
+
+	// Characters that do not need to be URL-encoded in attribute names.
+	// Identical to unencodedChars, but excludes '='.
+	private static final AsciiSet unencodedCharsAttrName = new AsciiSet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789;/?:@-_.!*'$(),~");
+
+	// Characters that need to be preceeded with an escape character.
+	private static final AsciiSet escapedChars = new AsciiSet(",()~=");
+
+	// AsciiSet that maps no characters.
+	private static final AsciiSet emptyCharSet = new AsciiSet("");
+
+	private static char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+	/**
+	 * Constructor.
+	 *
+	 * @param out The writer being wrapped.
+	 * @param useIndentation If <jk>true</jk>, tabs will be used in output.
+	 * @param simpleMode If <jk>true</jk>, type flags will not be generated in output.
+	 * @param encodeChars If <jk>true</jk>, special characters should be encoded.
+	 * @param relativeUriBase The base (e.g. <js>https://localhost:9443/contextPath"</js>) for relative URIs (e.g. <js>"my/path"</js>).
+	 * @param absolutePathUriBase The base (e.g. <js>https://localhost:9443"</js>) for relative URIs with absolute paths (e.g. <js>"/contextPath/my/path"</js>).
+	 */
+	protected UonSerializerWriter(Writer out, boolean useIndentation, boolean simpleMode, boolean encodeChars, String relativeUriBase, String absolutePathUriBase) {
+		super(out, useIndentation, false, '\'', relativeUriBase, absolutePathUriBase);
+		this.simpleMode = simpleMode;
+		this.encodeChars = encodeChars;
+	}
+
+	/**
+	 * Serializes the specified simple object as a UON string value.
+	 *
+	 * @param o The object being serialized.
+	 * @param quoteEmptyStrings Special case where we're serializing an array containing an empty string.
+	 * @param isTopAttrName If this is a top-level attribute name we're serializing.
+	 * @param isTop If this is a top-level value we're serializing.
+	 * @return This object (for method chaining).
+	 * @throws IOException Should never happen.
+	 */
+	protected UonSerializerWriter appendObject(Object o, boolean quoteEmptyStrings, boolean isTopAttrName, boolean isTop) throws IOException {
+
+		char typeFlag = 0;
+
+		if (o == null)
+			o = "\u0000";
+		else if (o.equals("\u0000"))
+			typeFlag = 's';
+
+		String s = o.toString();
+		if (s.isEmpty()) {
+			if (quoteEmptyStrings)
+				typeFlag = 's';
+		} else if (s.charAt(0) == '(' || s.charAt(0) == '$') {
+			typeFlag = 's';
+		} else if (useIndentation && (s.indexOf('\n') != -1 || (s.charAt(0) <= ' ' && s.charAt(0) != 0))) {
+			// Strings containing newline characters must always be quoted so that they're not confused with whitespace.
+			// Also, strings starting with whitespace must be quoted so that the contents are not ignored when whitespace is ignored.
+			typeFlag = 's';
+		} else if (! simpleMode) {
+			if (o instanceof Boolean)
+				typeFlag = 'b';
+			else if (o instanceof Number)
+				typeFlag = 'n';
+		}
+
+		if (typeFlag != 0)
+			startFlag(typeFlag);
+
+		AsciiSet unenc = (isTopAttrName ? unencodedCharsAttrName : unencodedChars);
+		AsciiSet esc = (isTop && typeFlag == 0 ? emptyCharSet : escapedChars);
+
+		for (int i = 0; i < s.length(); i++) {
+			char c = s.charAt(i);
+			if (esc.contains(c))
+				append('~');
+			if ((!encodeChars) || unenc.contains(c))
+				append(c);
+			else {
+				if (c == ' ')
+					append('+');
+				else {
+					int p = s.codePointAt(i);
+					if (p < 0x0080)
+						appendHex(p);
+					else if (p < 0x0800) {
+						int p1=p>>>6;
+						appendHex(p1+192).appendHex((p&63)+128);
+					} else if (p < 0x10000) {
+						int p1=p>>>6, p2=p1>>>6;
+						appendHex(p2+224).appendHex((p1&63)+128).appendHex((p&63)+128);
+					} else {
+						i++;  // Two-byte codepoint...skip past surrogate pair lower byte.
+						int p1=p>>>6, p2=p1>>>6, p3=p2>>>6;
+						appendHex(p3+240).appendHex((p2&63)+128).appendHex((p1&63)+128).appendHex((p&63)+128);
+					}
+				}
+			}
+		}
+
+		if (typeFlag != 0)
+			append(')');
+
+		return this;
+	}
+
+	/**
+	 * Prints <code>$f(</code> in normal mode, and <code>(</code> in simple mode.
+	 *
+	 * @param f The flag character.
+	 * @return This object (for method chaining).
+	 * @throws IOException
+	 */
+	protected UonSerializerWriter startFlag(char f) throws IOException {
+		if (f != 's' && ! simpleMode)
+			append('$').append(f);
+		append('(');
+		return this;
+	}
+
+	/**
+	 * Prints out a two-byte %xx sequence for the given byte value.
+	 */
+	private UonSerializerWriter appendHex(int b) throws IOException {
+		if (b > 255)
+			throw new IOException("Invalid value passed to appendHex.  Must be in the range 0-255.  Value=" + b);
+		append('%').append(hexArray[b>>>4]).append(hexArray[b&0x0F]);
+		return this;
+	}
+
+	/**
+	 * Appends a URI to the output.
+	 *
+	 * @param uri The URI to append to the output.
+	 * @param isTop If this is a top-level value we're serializing.
+	 * @return This object (for method chaining).
+	 * @throws IOException
+	 */
+	public SerializerWriter appendUri(Object uri, boolean isTop) throws IOException {
+		String s = uri.toString();
+		if (s.indexOf("://") == -1) {
+			if (StringUtils.startsWith(s, '/')) {
+				if (absolutePathUriBase != null)
+					append(absolutePathUriBase);
+			} else {
+				if (relativeUriBase != null) {
+					append(relativeUriBase);
+					if (! relativeUriBase.equals("/"))
+						append("/");
+
+				}
+			}
+		}
+		return appendObject(s, false, false, isTop);
+	}
+
+	//--------------------------------------------------------------------------------
+	// Overridden methods
+	//--------------------------------------------------------------------------------
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter cr(int depth) throws IOException {
+		super.cr(depth);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter appendln(int indent, String text) throws IOException {
+		super.appendln(indent, text);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter appendln(String text) throws IOException {
+		super.appendln(text);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter append(int indent, String text) throws IOException {
+		super.append(indent, text);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter append(int indent, char c) throws IOException {
+		super.append(indent, c);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter q() throws IOException {
+		super.q();
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter i(int indent) throws IOException {
+		super.i(indent);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter nl() throws IOException {
+		super.nl();
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter append(Object text) throws IOException {
+		super.append(text);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter append(String text) throws IOException {
+		super.append(text);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter appendIf(boolean b, String text) throws IOException {
+		super.appendIf(b, text);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter appendIf(boolean b, char c) throws IOException {
+		super.appendIf(b, c);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public UonSerializerWriter append(char c) throws IOException {
+		super.append(c);
+		return this;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingClassMeta.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingClassMeta.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingClassMeta.class
new file mode 100755
index 0000000..307849c
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingClassMeta.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingClassMeta.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingClassMeta.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingClassMeta.java
new file mode 100755
index 0000000..884dd1c
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingClassMeta.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core.urlencoding;
+
+import com.ibm.juno.core.urlencoding.annotation.*;
+import com.ibm.juno.core.utils.*;
+
+/**
+ * Metadata on classes specific to the URL-Encoding serializers and parsers pulled from the {@link UrlEncoding @UrlEncoding} annotation on the class.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public class UrlEncodingClassMeta {
+
+	private final UrlEncoding urlEncoding;
+	private final boolean expandedParams;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param c The class that this annotation is defined on.
+	 */
+	public UrlEncodingClassMeta(Class<?> c) {
+		this.urlEncoding = ReflectionUtils.getAnnotation(UrlEncoding.class, c);
+		if (urlEncoding != null) {
+			expandedParams = urlEncoding.expandedParams();
+		} else {
+			expandedParams = false;
+		}
+	}
+
+	/**
+	 * Returns the {@link UrlEncoding} annotation defined on the class.
+	 *
+	 * @return The value of the {@link UrlEncoding} annotation, or <jk>null</jk> if annotation is not specified.
+	 */
+	protected UrlEncoding getAnnotation() {
+		return urlEncoding;
+	}
+
+	/**
+	 * Returns the {@link UrlEncoding#expandedParams()} annotation defined on the class.
+	 *
+	 * @return The value of the {@link UrlEncoding#expandedParams()} annotation.
+	 */
+	protected boolean isExpandedParams() {
+		return expandedParams;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingParser.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingParser.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingParser.class
new file mode 100755
index 0000000..9b91ca9
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingParser.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingParser.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingParser.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingParser.java
new file mode 100755
index 0000000..feb4548
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingParser.java
@@ -0,0 +1,568 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2013, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core.urlencoding;
+
+import static com.ibm.juno.core.urlencoding.UonParserProperties.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.ibm.juno.core.*;
+import com.ibm.juno.core.annotation.*;
+import com.ibm.juno.core.filter.*;
+import com.ibm.juno.core.parser.*;
+import com.ibm.juno.core.utils.*;
+
+/**
+ * Parses URL-encoded text into POJO models.
+ *
+ *
+ * <h6 class='topic'>Media types</h6>
+ * <p>
+ * 	Handles <code>Content-Type</code> types: <code>application/x-www-form-urlencoded</code>
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ * 	Parses URL-Encoded text (e.g. <js>"foo=bar&baz=bing"</js>) into POJOs.
+ * <p>
+ * 	Expects parameter values to be in UON notation.
+ * <p>
+ * 	This parser uses a state machine, which makes it very fast and efficient.
+ *
+ *
+ * <h6 class='topic'>Configurable properties</h6>
+ * <p>
+ * 	This class has the following properties associated with it:
+ * <ul>
+ * 	<li>{@link UonParserProperties}
+ * 	<li>{@link ParserProperties}
+ * 	<li>{@link BeanContextProperties}
+ * </ul>
+ *
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+@SuppressWarnings({ "rawtypes", "unchecked", "hiding" })
+@Consumes("application/x-www-form-urlencoded")
+public class UrlEncodingParser extends UonParser {
+
+	/** Reusable instance of {@link UrlEncodingParser}. */
+	public static final UrlEncodingParser DEFAULT = new UrlEncodingParser().lock();
+
+	/** Reusable instance of {@link UrlEncodingParser}. */
+	public static final UrlEncodingParser DEFAULT_WS_AWARE = new UrlEncodingParser().setProperty(UON_whitespaceAware, true).lock();
+
+	/**
+	 * Constructor.
+	 */
+	public UrlEncodingParser() {
+		setProperty(UON_decodeChars, true);
+	}
+
+	private <T> T parseAnything(ClassMeta<T> nt, UonParserContext ctx, ParserReader r, Object outer, Object name) throws ParseException {
+
+		BeanContext bc = ctx.getBeanContext();
+		if (nt == null)
+			nt = (ClassMeta<T>)object();
+		PojoFilter<T,Object> filter = (PojoFilter<T,Object>)nt.getPojoFilter();
+		ClassMeta<?> ft = nt.getFilteredClassMeta();
+
+		try {
+			int c = r.peek();
+			if (c == '?')
+				r.read();
+
+			Object o;
+
+			if (ft.isObject()) {
+				ObjectMap m = new ObjectMap(bc);
+				parseIntoMap(ctx, r, m, bc.string(), bc.object());
+				o = m.cast();
+			} else if (ft.isMap()) {
+				Map m = (ft.canCreateNewInstance() ? (Map)ft.newInstance() : new ObjectMap(bc));
+				o = parseIntoMap(ctx, r, m, ft.getKeyType(), ft.getValueType());
+			} else if (ft.canCreateNewInstanceFromObjectMap(outer)) {
+				ObjectMap m = new ObjectMap(bc);
+				parseIntoMap(ctx, r, m, string(), object());
+				o = ft.newInstanceFromObjectMap(outer, m);
+			} else if (ft.canCreateNewBean(outer)) {
+				BeanMap m = bc.newBeanMap(outer, ft.getInnerClass());
+				m = parseIntoBeanMap(ctx, r, m);
+				o = m == null ? null : m.getBean();
+			} else {
+				// It could be a non-bean with _class attribute.
+				ObjectMap m = new ObjectMap(bc);
+				ClassMeta<Object> valueType = object();
+				parseIntoMap(ctx, r, m, string(), valueType);
+				if (m.containsKey("_class"))
+					o = m.cast();
+				else if (m.containsKey("_value"))
+					o = ctx.getBeanContext().convertToType(m.get("_value"), ft);
+				else if (ft.isCollection()) {
+					// ?1=foo&2=bar...
+					Collection c2 = ft.canCreateNewInstance() ? (Collection)ft.newInstance() : new ObjectList(bc);
+					Map<Integer,Object> t = new TreeMap<Integer,Object>();
+					for (Map.Entry<String,Object> e : m.entrySet()) {
+						String k = e.getKey();
+						if (StringUtils.isNumeric(k))
+							t.put(Integer.valueOf(k), bc.convertToType(e.getValue(), ft.getElementType()));
+					}
+					c2.addAll(t.values());
+					o = c2;
+				} else {
+					if (ft.getNotABeanReason() != null)
+						throw new ParseException("Class ''{0}'' could not be instantiated as application/x-www-form-urlencoded.  Reason: ''{1}''", ft, ft.getNotABeanReason());
+					throw new ParseException("Malformed application/x-www-form-urlencoded input for class ''{0}''.", ft);
+				}
+			}
+
+			if (filter != null && o != null)
+				o = filter.unfilter(o, nt);
+
+			if (outer != null)
+				setParent(nt, o, outer);
+
+			if (name != null)
+				setName(nt, o, name);
+
+			return (T)o;
+
+		} catch (RuntimeException e) {
+			throw e;
+		} catch (Exception e) {
+			throw new ParseException("Error occurred trying to parse into class ''{0}''", ft).initCause(e);
+		}
+	}
+
+	private <K,V> Map<K,V> parseIntoMap(UonParserContext ctx, ParserReader r, Map<K,V> m, ClassMeta<K> keyType, ClassMeta<V> valueType) throws ParseException, IOException {
+
+		if (keyType == null)
+			keyType = (ClassMeta<K>)string();
+
+		int c = r.peek();
+		if (c == -1)
+			return m;
+
+		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 end.
+		boolean isInEscape = false;
+
+		int state = S1;
+		K currAttr = null;
+		while (c != -1) {
+			c = r.read();
+			if (! isInEscape) {
+				if (state == S1) {
+					if (c == -1)
+						return m;
+					r.unread();
+					Object attr = parseAttr(r, true, ctx);
+					currAttr = ctx.getBeanContext().convertToType(attr, keyType);
+					state = S2;
+					c = 0; // Avoid isInEscape if c was '\'
+				} else if (state == S2) {
+					if (c == '\u0002')
+						state = S3;
+					else if (c == -1 || c == '\u0001') {
+						m.put(currAttr, null);
+						if (c == -1)
+							return m;
+						state = S1;
+					}
+				} else if (state == S3) {
+					if (c == -1 || c == '\u0001') {
+						V value = convertAttrToType(m, "", valueType);
+						m.put(currAttr, value);
+						if (c == -1)
+							return m;
+						state = S1;
+					} else  {
+						// For performance, we bypass parseAnything for string values.
+						V value = (V)(valueType.isString() ? super.parseString(r.unread(), true, ctx) : super.parseAnything(valueType, ctx, r.unread(), null, m, true, null));
+
+						// If we already encountered this parameter, turn it into a list.
+						if (m.containsKey(currAttr) && valueType.isObject()) {
+							Object v2 = m.get(currAttr);
+							if (! (v2 instanceof ObjectList)) {
+								v2 = new ObjectList(v2);
+								m.put(currAttr, (V)v2);
+							}
+							((ObjectList)v2).add(value);
+						} else {
+						m.put(currAttr, value);
+						}
+						state = S4;
+						c = 0; // Avoid isInEscape if c was '\'
+					}
+				} else if (state == S4) {
+					if (c == '\u0001')
+						state = S1;
+					else if (c == -1) {
+						return m;
+					}
+				}
+			}
+			isInEscape = (c == '\\' && ! isInEscape);
+		}
+		if (state == S1)
+			throw new ParseException("Could not find attribute name on object.");
+		if (state == S2)
+			throw new ParseException("Could not find '=' following attribute name on object.");
+		if (state == S3)
+			throw new ParseException("Dangling '=' found in object entry");
+		if (state == S4)
+			throw new ParseException("Could not find end of object.");
+
+		return null; // Unreachable.
+	}
+
+	private <T> BeanMap<T> parseIntoBeanMap(UonParserContext ctx, ParserReader r, BeanMap<T> m) throws ParseException, IOException {
+		int line = r.getLine();
+		int column = r.getColumn();
+
+		int c = r.peek();
+		if (c == -1)
+			return m;
+
+		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 = r.read();
+			if (! isInEscape) {
+				if (state == S1) {
+					if (c == -1) {
+						return m;
+					}
+					r.unread();
+					currAttrLine= r.getLine();
+					currAttrCol = r.getColumn();
+					currAttr = parseAttrName(r, true);
+					if (currAttr == null)  // Value was '%00'
+						return null;
+					state = S2;
+				} else if (state == S2) {
+					if (c == '\u0002')
+						state = S3;
+					else if (c == -1 || c == '\u0001') {
+						m.put(currAttr, null);
+						if (c == -1)
+							return m;
+						state = S1;
+					}
+				} else if (state == S3) {
+					if (c == -1 || c == '\u0001') {
+						if (! currAttr.equals("_class")) {
+							BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr);
+							if (pMeta == null) {
+								if (m.getMeta().isSubTyped()) {
+									m.put(currAttr, "");
+								} else {
+									onUnknownProperty(ctx, currAttr, m, currAttrLine, currAttrCol);
+								}
+							} else {
+								try {
+									// In cases of "&foo=", create an empty instance of the value if createable.
+									// Otherwise, leave it null.
+									ClassMeta<?> cm = pMeta.getClassMeta();
+									if (cm.canCreateNewInstance())
+										pMeta.set(m, cm.newInstance());
+								} catch (Exception e) {
+									throw new ParseException(e);
+								}
+							}
+						}
+						if (c == -1)
+							return m;
+						state = S1;
+					} else {
+						if (! currAttr.equals("_class")) {
+							BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr);
+							if (pMeta == null) {
+								if (m.getMeta().isSubTyped()) {
+									m.put(currAttr, parseAnything(object(), ctx, r.unread(), null, m.getBean(false), true, currAttr));
+								} else {
+									onUnknownProperty(ctx, currAttr, m, currAttrLine, currAttrCol);
+									parseAnything(object(), ctx, r.unread(), null, m.getBean(false), true, null); // Read content anyway to ignore it
+								}
+							} else {
+								if (shouldUseExpandedParams(pMeta, ctx)) {
+									ClassMeta cm = pMeta.getClassMeta();
+									Object value = parseAnything(cm.getElementType(), ctx, r.unread(), pMeta, m.getBean(false), true, currAttr);
+									pMeta.add(m, value);
+								} else {
+									Object value = parseAnything(pMeta.getClassMeta(), ctx, r.unread(), pMeta, m.getBean(false), true, currAttr);
+									pMeta.set(m, value);
+								}
+							}
+						}
+						state = S4;
+					}
+				} else if (state == S4) {
+					if (c == '\u0001')
+						state = S1;
+					else if (c == -1) {
+						return m;
+					}
+				}
+			}
+			isInEscape = (c == '\\' && ! isInEscape);
+		}
+		if (state == S1)
+			throw new ParseException(line, column, "Could not find attribute name on object.");
+		if (state == S2)
+			throw new ParseException(line, column, "Could not find '=' following attribute name on object.");
+		if (state == S3)
+			throw new ParseException(line, column, "Could not find value following '=' on object.");
+		if (state == S4)
+			throw new ParseException(line, column, "Could not find end of object.");
+
+		return null; // Unreachable.
+	}
+
+	/**
+	 * Returns true if the specified bean property should be expanded as multiple key-value pairs.
+	 */
+	private final boolean shouldUseExpandedParams(BeanPropertyMeta<?> pMeta, UonParserContext ctx) {
+		ClassMeta cm = pMeta.getClassMeta();
+		if (cm.isArray() || cm.isCollection()) {
+			if (ctx.isExpandedParams())
+				return true;
+			if (pMeta.getBeanMeta().getClassMeta().getUrlEncodingMeta().isExpandedParams())
+				return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Parse a URL query string into a simple map of key/value pairs.
+	 *
+	 * @param qs The query string to parse.
+	 * @return A sorted {@link TreeMap} of query string entries.
+	 * @throws IOException
+	 */
+	public Map<String,String[]> parseIntoSimpleMap(String qs) throws IOException {
+
+		Map<String,String[]> m = new TreeMap<String,String[]>();
+
+		if (StringUtils.isEmpty(qs))
+			return m;
+
+		UonParserReader r = new UonParserReader(qs, true);
+
+		final int S1=1; // Looking for attrName start.
+		final int S2=2; // Found attrName start, looking for = or & or end.
+		final int S3=3; // Found =, looking for valStart.
+		final int S4=4; // Found valStart, looking for & or end.
+
+		try {
+			int c = r.peek();
+			if (c == '?')
+				r.read();
+
+			int state = S1;
+			String currAttr = null;
+			while (c != -1) {
+				c = r.read();
+				if (state == S1) {
+					if (c != -1) {
+						r.unread();
+						r.mark();
+						state = S2;
+					}
+				} else if (state == S2) {
+					if (c == -1) {
+						add(m, r.getMarked(), null);
+					} else if (c == '\u0001') {
+						m.put(r.getMarked(0,-1), null);
+						state = S1;
+					} else if (c == '\u0002') {
+						currAttr = r.getMarked(0,-1);
+						state = S3;
+					}
+				} else if (state == S3) {
+					if (c == -1 || c == '\u0001') {
+						add(m, currAttr, "");
+					} else {
+						if (c == '\u0002')
+							r.replace('=');
+						r.unread();
+						r.mark();
+						state = S4;
+					}
+				} else if (state == S4) {
+					if (c == -1) {
+						add(m, currAttr, r.getMarked());
+					} else if (c == '\u0001') {
+						add(m, currAttr, r.getMarked(0,-1));
+						state = S1;
+					} else if (c == '\u0002') {
+						r.replace('=');
+					}
+				}
+			}
+		} finally {
+			r.close();
+		}
+
+		return m;
+	}
+
+	private static void add(Map<String,String[]> m, String key, String val) {
+		boolean b = m.containsKey(key);
+		if (val == null) {
+			if (! b)
+				m.put(key, null);
+		} else if (b && m.get(key) != null) {
+			m.put(key, ArrayUtils.append(m.get(key), val));
+		} else {
+			m.put(key, new String[]{val});
+		}
+	}
+
+	private Object[] parseArgs(UonParserContext ctx, ParserReader r, ClassMeta<?>[] argTypes) throws ParseException {
+		// TODO - This can be made more efficient.
+		BeanContext bc = ctx.getBeanContext();
+		ClassMeta<TreeMap<Integer,String>> cm = bc.getMapClassMeta(TreeMap.class, Integer.class, String.class);
+		TreeMap<Integer,String> m = parseAnything(cm, ctx, r, ctx.getOuter(), null);
+		Object[] vals = m.values().toArray(new Object[m.size()]);
+		if (vals.length != argTypes.length)
+			throw new ParseException("Argument lengths don't match.  vals={0}, argTypes={1}", vals.length, argTypes.length);
+		for (int i = 0; i < vals.length; i++) {
+			String s = String.valueOf(vals[i]);
+			vals[i] = super.parseAnything(argTypes[i], ctx, ctx.getUrlEncodingParserReader(new StringReader(s), s.length()), null, ctx.getOuter(), true, null);
+		}
+
+		return vals;
+	}
+
+	/**
+	 * Parses a single query parameter value into the specified class type.
+	 *
+	 * @param in The input query string value.
+	 * @param type The class type of the object to create.
+	 * @return A new instance of the specified type.
+	 * @throws ParseException
+	 */
+	public <T> T parseParameter(CharSequence in, ClassMeta<T> type) throws ParseException {
+		if (in == null)
+			return null;
+		UonParserContext uctx = (UonParserContext)createContext();
+		uctx.decodeChars = false;
+		UonParserReader r = uctx.getUrlEncodingParserReader(wrapReader(in), in.length());
+		return super.parseAnything(type, uctx, r, null, null, true, null);
+	}
+
+	/**
+	 * Parses a single query parameter value into the specified class type.
+	 *
+	 * @param in The input query string value.
+	 * @param type The class type of the object to create.
+	 * @return A new instance of the specified type.
+	 * @throws ParseException
+	 */
+	public <T> T parseParameter(CharSequence in, Class<T> type) throws ParseException {
+		return parseParameter(in, getBeanContext().getClassMeta(type));
+	}
+
+	//--------------------------------------------------------------------------------
+	// Overridden methods
+	//--------------------------------------------------------------------------------
+
+	@Override /* Parser */
+	protected <T> T doParse(Reader in, int estimatedSize, ClassMeta<T> type, ParserContext ctx) throws ParseException, IOException {
+		UonParserContext uctx = (UonParserContext)ctx;
+		type = ctx.getBeanContext().normalizeClassMeta(type);
+		UonParserReader r = uctx.getUrlEncodingParserReader(in, estimatedSize);
+		T o = parseAnything(type, uctx, r, ctx.getOuter(), null);
+		return o;
+	}
+
+	@Override /* ReaderParser */
+	protected Object[] doParseArgs(Reader in, int estimatedSize, ClassMeta<?>[] argTypes, ParserContext ctx) throws ParseException, IOException {
+		UonParserContext uctx = (UonParserContext)ctx;
+		UonParserReader r = uctx.getUrlEncodingParserReader(in, estimatedSize);
+		Object[] a = parseArgs(uctx, r, argTypes);
+		return a;
+	}
+
+	@Override /* ReaderParser */
+	protected <K,V> Map<K,V> doParseIntoMap(Reader in, int estimatedSize, Map<K,V> m, Type keyType, Type valueType, ParserContext ctx) throws ParseException, IOException {
+		UonParserContext uctx = (UonParserContext)ctx;
+		UonParserReader r = uctx.getUrlEncodingParserReader(in, estimatedSize);
+		if (r.peek() == '?')
+			r.read();
+		m = parseIntoMap(uctx, r, m, ctx.getBeanContext().getClassMeta(keyType), ctx.getBeanContext().getClassMeta(valueType));
+		return m;
+	}
+
+	@Override /* Parser */
+	public UrlEncodingParser setProperty(String property, Object value) throws LockedException {
+		checkLock();
+		if (! upp.setProperty(property, value))
+			if (! uep.setProperty(property, value))
+				super.setProperty(property, value);
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public UrlEncodingParser setProperties(ObjectMap properties) throws LockedException {
+		for (Map.Entry<String,Object> e : properties.entrySet())
+			setProperty(e.getKey(), e.getValue());
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public UrlEncodingParser addNotBeanClasses(Class<?>...classes) throws LockedException {
+		super.addNotBeanClasses(classes);
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public UrlEncodingParser addFilters(Class<?>...classes) throws LockedException {
+		super.addFilters(classes);
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public <T> UrlEncodingParser addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException {
+		super.addImplClass(interfaceClass, implClass);
+		return this;
+	}
+
+	@Override /* CoreApi */
+	public UrlEncodingParser setClassLoader(ClassLoader classLoader) throws LockedException {
+		super.setClassLoader(classLoader);
+		return this;
+	}
+
+	@Override /* Lockable */
+	public UrlEncodingParser lock() {
+		super.lock();
+		return this;
+	}
+
+	@Override /* Lockable */
+	public UrlEncodingParser clone() {
+		UrlEncodingParser c = (UrlEncodingParser)super.clone();
+		c.upp = upp.clone();
+		c.uep = uep.clone();
+		return c;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingProperties.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingProperties.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingProperties.class
new file mode 100755
index 0000000..2e6d92e
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingProperties.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingProperties.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingProperties.java
new file mode 100755
index 0000000..4c65132
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingProperties.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core.urlencoding;
+
+import com.ibm.juno.core.*;
+
+/**
+ * Configurable properties on the {@link UrlEncodingSerializer} and {@link UrlEncodingParser} classes.
+ * <p>
+ * 	Use the {@link UrlEncodingSerializer#setProperty(String, Object)} and
+ * 	{@link UrlEncodingParser#setProperty(String, Object)} methods to set property values.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class UrlEncodingProperties implements Cloneable {
+
+	/**
+	 * Serialize bean property collections/arrays as separate key/value pairs ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * 	If <jk>false</jk>, serializing the array <code>[1,2,3]</code> results in <code>?key=$a(1,2,3)</code>.
+	 * 	If <jk>true</jk>, serializing the same array results in <code>?key=1&key=2&key=3</code>.
+	 * <p>
+	 * 	Example:
+	 * <p class='bcode'>
+	 * 	<jk>public class</jk> A {
+	 * 		<jk>public</jk> String[] f1 = {<js>"a"</js>,<js>"b"</js>};
+	 * 		<jk>public</jk> List&lt;String&gt; f2 = <jk>new</jk> LinkedList&lt;String&gt;(Arrays.<jsm>asList</jsm>(<jk>new</jk> String[]{<js>"c"</js>,<js>"d"</js>}));
+	 * 	}
+	 *
+	 * 	UrlEncodingSerializer s1 = <jk>new</jk> UrlEncodingParser();
+	 * 	UrlEncodingSerializer s2 = <jk>new</jk> UrlEncodingParser().setProperty(UrlEncodingProperties.<jsf>URLENC_expandedParams</jsf>, <jk>true</jk>);
+	 *
+	 * 	String s1 = p1.serialize(<jk>new</jk> A()); <jc>// Produces "f1=(a,b)&f2=(c,d)"</jc>
+	 * 	String s2 = p2.serialize(<jk>new</jk> A()); <jc>// Produces "f1=a&f1=b&f2=c&f2=d"</jc>
+	 * </p>
+	 * <p>
+	 * 	<b>Important note:</b>  If parsing multi-part parameters, it's highly recommended to use Collections or Lists
+	 * 	as bean property types instead of arrays since arrays have to be recreated from scratch every time a value
+	 * 	is added to it.
+	 * <p>
+	 * 	This option only applies to beans.
+	 */
+	public static final String URLENC_expandedParams = "UrlEncoding.expandedParams";
+
+	boolean
+		expandedParams = false;
+
+	/**
+	 * Sets the specified property value.
+	 * @param property The property name.
+	 * @param value The property value.
+	 * @return <jk>true</jk> if property name was valid and property was set.
+	 */
+	public boolean setProperty(String property, Object value) {
+		BeanContext bc = BeanContext.DEFAULT;
+		if (property.equals(URLENC_expandedParams))
+			expandedParams = bc.convertToType(value, Boolean.class);
+		else
+			return false;
+		return true;
+	}
+
+
+	//--------------------------------------------------------------------------------
+	// Overridden methods
+	//--------------------------------------------------------------------------------
+
+	@Override /* Cloneable */
+	public UrlEncodingProperties clone() {
+		try {
+			return (UrlEncodingProperties)super.clone();
+		} catch (CloneNotSupportedException e) {
+			throw new RuntimeException(e); // Shouldn't happen
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$Readable.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$Readable.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$Readable.class
new file mode 100755
index 0000000..37a40a0
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$Readable.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$Simple.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$Simple.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$Simple.class
new file mode 100755
index 0000000..51f3f69
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$Simple.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$SimpleExpanded.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$SimpleExpanded.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$SimpleExpanded.class
new file mode 100755
index 0000000..e11f2ac
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer$SimpleExpanded.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer.class
new file mode 100755
index 0000000..83bb958
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UrlEncodingSerializer.class differ