http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerProperties.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerProperties.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerProperties.java
new file mode 100755
index 0000000..669a832
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerProperties.java
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * 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.serializer;
+
+import com.ibm.juno.core.*;
+
+/**
+ * Configurable properties common to all {@link Serializer} classes.
+ * <p>
+ *     Use the {@link Serializer#setProperty(String, Object)} method to set 
property values.
+ *
+ * @author James Bognar ([email protected])
+ */
+public final class SerializerProperties implements Cloneable {
+
+       /**
+        * Max depth ({@link Integer}, default=<code>100</code>).
+        * <p>
+        * Abort serialization if specified depth is reached in the POJO tree.
+        * If this depth is exceeded, an exception is thrown.
+        * This prevents stack overflows from occurring when trying to 
serialize models with recursive references.
+        */
+       public static final String SERIALIZER_maxDepth = "Serializer.maxDepth";
+
+       /**
+        * Initial depth ({@link Integer}, default=<code>0</code>).
+        * <p>
+        * The initial indentation level at the root.
+        * Useful when constructing document fragments that need to be indented 
at a certain level.
+        */
+       public static final String SERIALIZER_initialDepth = 
"Serializer.initialDepth";
+
+       /**
+        * Automatically detect POJO recursions ({@link Boolean}, 
default=<jk>false</jk>).
+        * <p>
+        * Specifies that recursions should be checked for during serialization.
+        * <p>
+        * Recursions can occur when serializing models that aren't true trees, 
but rather contain loops.
+        * <p>
+        * The behavior when recursions are detected depends on the value for 
{@link #SERIALIZER_ignoreRecursions}.
+        * <p>
+        * For example, if a model contains the links A-&gt;B-&gt;C-&gt;A, then 
the JSON generated will look like
+        *      the following when <jsf>SERIALIZER_ignoreRecursions</jsf> is 
<jk>true</jk>...
+        * <code>{A:{B:{C:null}}}</code><br>
+        * <p>
+        * Note:  Checking for recursion can cause a small performance penalty.
+        */
+       public static final String SERIALIZER_detectRecursions = 
"Serializer.detectRecursions";
+
+       /**
+        * Ignore recursion errors ({@link Boolean}, default=<jk>false</jk>).
+        * <p>
+        * Used in conjunction with {@link #SERIALIZER_detectRecursions}.
+        * Setting is ignored if <jsf>SERIALIZER_detectRecursions</jsf> is 
<jk>false</jk>.
+        * <p>
+        * If <jk>true</jk>, when we encounter the same object when serializing 
a tree,
+        *      we set the value to <jk>null</jk>.
+        * Otherwise, an exception is thrown.
+        */
+       public static final String SERIALIZER_ignoreRecursions = 
"Serializer.ignoreRecursions";
+
+       /**
+        * Debug mode ({@link Boolean}, default=<jk>false</jk>).
+        * <p>
+        * Enables the following additional information during serialization:
+        * <ul>
+        *      <li>When bean getters throws exceptions, the exception includes 
the object stack information
+        *              in order to determine how that method was invoked.
+        *      <li>Enables {#link SERIALIZER_detectRecursions}.
+        * </ul>
+        */
+       public static final String SERIALIZER_debug = "Serializer.debug";
+
+       /**
+        * Use indentation in output ({@link Boolean}, default=<jk>false</jk>).
+        * <p>
+        * If <jk>true</jk>, newlines and indentation is added to the output to 
improve readability.
+        */
+       public static final String SERIALIZER_useIndentation = 
"Serializer.useIndentation";
+
+       /**
+        * Add class attributes to output ({@link Boolean}, 
default=<jk>false</jk>).
+        * <p>
+        * If <jk>true</jk>, then <js>"_class"</js> attributes will be added to 
beans if their type cannot be inferred through reflection.
+        * This is used to recreate the correct objects during parsing if the 
object types cannot be inferred.
+        * For example, when serializing a {@code Map<String,Object>} field, 
where the bean class cannot be determined from the value type.
+        */
+       public static final String SERIALIZER_addClassAttrs = 
"Serializer.addClassAttrs";
+
+       /**
+        * Quote character ({@link Character}, default=<js>'"'</js>.
+        * <p>
+        * This is the character used for quoting attributes and values.
+        */
+       public static final String SERIALIZER_quoteChar = 
"Serializer.quoteChar";
+
+       /**
+        * Boolean.  Trim null bean property values from output.
+        * <p>
+        *      If <jk>true</jk>, null bean values will not be serialized to 
the output.
+        * <p>
+        *      Note that enabling this setting has the following effects on 
parsing:
+        *      <ul>
+        *              <li>Map entries with <jk>null</jk> values will be lost.
+        *      </ul>
+        * <p>
+        *      Default is <jk>true</jk>.
+        */
+       public static final String SERIALIZER_trimNullProperties = 
"Serializer.trimNullProperties";
+
+       /**
+        * Boolean.  Trim empty lists and arrays from output.
+        * <p>
+        *      If <jk>true</jk>, empty list values will not be serialized to 
the output.
+        * <p>
+        *      Note that enabling this setting has the following effects on 
parsing:
+        *      <ul>
+        *              <li>Map entries with empty list values will be lost.
+        *              <li>Bean properties with empty list values will not be 
set.
+        *      </ul>
+        * <p>
+        *      Default is <jk>false</jk>.
+        */
+       public static final String SERIALIZER_trimEmptyLists = 
"Serializer.trimEmptyLists";
+
+       /**
+        * Boolean.  Trim empty maps from output.
+        * <p>
+        *      If <jk>true</jk>, empty map values will not be serialized to 
the output.
+        * <p>
+        *      Note that enabling this setting has the following effects on 
parsing:
+        *      <ul>
+        *              <li>Bean properties with empty map values will not be 
set.
+        *      </ul>
+        * <p>
+        *      Default is <jk>false</jk>.
+        */
+       public static final String SERIALIZER_trimEmptyMaps = 
"Serializer.trimEmptyMaps";
+
+       /**
+        * String.  URI base for relative URIs.
+        * <p>
+        *      Prepended to relative URIs during serialization (along with the 
{@link #SERIALIZER_absolutePathUriBase} if specified.
+        *      (i.e. URIs not containing a schema and not starting with 
<js>'/'</js>).
+        *      (e.g. <js>"foo/bar"</js>)
+        * <p>
+        *      Default is <js>""</js>.
+        *
+        * <dl>
+        *      <dt>Examples:</dt>
+        *      <dd>
+        * <table class='styled'>
+        *      
<tr><th>SERIALIZER_relativeUriBase</th><th>URI</th><th>Serialized URI</th></tr>
+        *      <tr>
+        *              <td><code>http://foo:9080/bar/baz</code></td>
+        *              <td><code>mywebapp</code></td>
+        *              <td><code>http://foo:9080/bar/baz/mywebapp</code></td>
+        *      </tr>
+        *      <tr>
+        *              <td><code>http://foo:9080/bar/baz</code></td>
+        *              <td><code>/mywebapp</code></td>
+        *              <td><code>/mywebapp</code></td>
+        *      </tr>
+        *      <tr>
+        *              <td><code>http://foo:9080/bar/baz</code></td>
+        *              <td><code>http://mywebapp</code></td>
+        *              <td><code>http://mywebapp</code></td>
+        *      </tr>
+        * </table>
+        *      </dd>
+        * </dl>
+        */
+       public static final String SERIALIZER_relativeUriBase = 
"Serializer.relativeUriBase";
+
+       /**
+        * Boolean.  Sort arrays and collections alphabetically before 
serializing.
+        * <p>
+        *      Note that this introduces a performance penalty.
+        * <p>
+        *      Default is <jk>false</jk>.
+        */
+       public static final String SERIALIZER_sortCollections = 
"Serializer.sortCollections";
+
+       /**
+        * Boolean.  Sort maps alphabetically before serializing.
+        * <p>
+        *      Note that this introduces a performance penalty.
+        * <p>
+        *      Default is <jk>false</jk>.
+        */
+       public static final String SERIALIZER_sortMaps = "Serializer.sortMaps";
+
+       /**
+        * String.  URI base for relative URIs with absolute paths.
+        * <p>
+        *      Prepended to relative absolute-path URIs during serialization.
+        *      (i.e. URIs starting with <js>'/'</js>).
+        *      (e.g. <js>"/foo/bar"</js>)
+        * <p>
+        *      Default is <js>""</js>.
+        *
+        * <dl>
+        *      <dt>Examples:</dt>
+        *      <dd>
+        * <table class='styled'>
+        *      
<tr><th>SERIALIZER_absolutePathUriBase</th><th>URI</th><th>Serialized 
URI</th></tr>
+        *      <tr>
+        *              <td><code>http://foo:9080/bar/baz</code></td>
+        *              <td><code>mywebapp</code></td>
+        *              <td><code>mywebapp</code></td>
+        *      </tr>
+        *      <tr>
+        *              <td><code>http://foo:9080/bar/baz</code></td>
+        *              <td><code>/mywebapp</code></td>
+        *              <td><code>http://foo:9080/bar/baz/mywebapp</code></td>
+        *      </tr>
+        *      <tr>
+        *              <td><code>http://foo:9080/bar/baz</code></td>
+        *              <td><code>http://mywebapp</code></td>
+        *              <td><code>http://mywebapp</code></td>
+        *      </tr>
+        * </table>
+        *      </dd>
+        * </dl>
+        */
+       public static final String SERIALIZER_absolutePathUriBase = 
"Serializer.absolutePathUriBase";
+
+       int maxDepth = 100, initialDepth = 0;
+       boolean
+               debug = false,
+               detectRecursions = false,
+               ignoreRecursions = false,
+               useIndentation = false,
+               addClassAttrs = false,
+               trimNulls = true,
+               trimEmptyLists = false,
+               trimEmptyMaps = false,
+               sortCollections = false,
+               sortMaps = false;
+       char quoteChar = '"';
+       String relativeUriBase="", absolutePathUriBase="";
+
+       /**
+        * 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(SERIALIZER_maxDepth))
+                       maxDepth = bc.convertToType(value, Integer.class);
+               else if (property.equals(SERIALIZER_debug))
+                       debug = bc.convertToType(value, Boolean.class);
+               else if (property.equals(SERIALIZER_detectRecursions))
+                       detectRecursions = bc.convertToType(value, 
Boolean.class);
+               else if (property.equals(SERIALIZER_ignoreRecursions))
+                       ignoreRecursions = bc.convertToType(value, 
Boolean.class);
+               else if (property.equals(SERIALIZER_useIndentation))
+                       useIndentation = bc.convertToType(value, Boolean.class);
+               else if (property.equals(SERIALIZER_addClassAttrs))
+                       addClassAttrs = bc.convertToType(value, Boolean.class);
+               else if (property.equals(SERIALIZER_quoteChar))
+                       quoteChar = bc.convertToType(value, Character.class);
+               else if (property.equals(SERIALIZER_trimNullProperties))
+                       trimNulls = bc.convertToType(value, Boolean.class);
+               else if (property.equals(SERIALIZER_trimEmptyLists))
+                       trimEmptyLists = bc.convertToType(value, Boolean.class);
+               else if (property.equals(SERIALIZER_trimEmptyMaps))
+                       trimEmptyMaps = bc.convertToType(value, Boolean.class);
+               else if (property.equals(SERIALIZER_relativeUriBase))
+                       relativeUriBase = value == null ? null : 
value.toString();
+               else if (property.equals(SERIALIZER_absolutePathUriBase))
+                       absolutePathUriBase = value == null ? null : 
value.toString();
+               else if (property.equals(SERIALIZER_sortCollections))
+                       sortCollections = bc.convertToType(value, 
Boolean.class);
+               else if (property.equals(SERIALIZER_sortMaps))
+                       sortMaps = bc.convertToType(value, Boolean.class);
+               else
+                       return false;
+               return true;
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Cloneable */
+       public SerializerProperties clone() {
+               try {
+                       return (SerializerProperties)super.clone();
+               } catch (CloneNotSupportedException e) {
+                       throw new RuntimeException(e); // Won'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/serializer/SerializerWriter.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.class
new file mode 100755
index 0000000..0bd3142
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.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/serializer/SerializerWriter.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.java
new file mode 100755
index 0000000..7a91e6a
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.java
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * 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.serializer;
+
+import java.io.*;
+import java.net.*;
+
+import com.ibm.juno.core.utils.*;
+
+/**
+ * Simple wrapper around a standard {@link Writer} with additional methods.
+ * <p>
+ * Modeled after the Java ProcessBuilder class so that you can chain commands 
to reduce
+ *     the need for string concatenation for performance reasons.
+ *
+ * <h6 class='topic'>Example</h6>
+ * <p class='bcode'>
+ *     writer.append(<js>"foo"</js>).nl().i(5).append(<js>"bar"</js>);
+ * </p>
+ *
+ * @author James Bognar ([email protected])
+ */
+public class SerializerWriter extends Writer {
+
+       /** The underlying writer. */
+       protected final Writer out;
+
+       /** Use-indentation flag. */
+       protected final boolean useIndentation;
+
+       /** Use-whitespace flag. */
+       protected final boolean useWhitespace;
+
+       /** The quote character being used by this writer. */
+       protected final char quoteChar;
+
+       /** The base (e.g. <js>https://localhost:9443/contextPath";</js>) for 
relative URIs (e.g. <js>"my/path"</js>). */
+       protected final String relativeUriBase;
+
+       /** The base (e.g. <js>https://localhost:9443";</js>) for relative URIs 
with absolute paths (e.g. <js>"/contextPath/my/path"</js>). */
+       protected final String absolutePathUriBase;
+
+       /**
+        * @param out The writer being wrapped.
+        * @param useIndentation If <jk>true</jk>, calling {@link #cr(int)} 
will create an indentation.
+        * @param useWhitespace If <jk>true</jk>, calling {@link #s()} will 
write a space character.
+        * @param quoteChar The character to write when {@link #q()} is called.
+        * @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>).
+        */
+       public SerializerWriter(Writer out, boolean useIndentation, boolean 
useWhitespace, char quoteChar, String relativeUriBase, String 
absolutePathUriBase) {
+               this.out = out;
+               this.useIndentation = useIndentation;
+               this.useWhitespace = useWhitespace;
+               this.quoteChar = quoteChar;
+               this.relativeUriBase = relativeUriBase;
+               this.absolutePathUriBase = absolutePathUriBase;
+       }
+
+       /**
+        * Performs a carriage return.
+        * <p>
+        *      Adds a newline and the specified number of tabs (if the {@code 
useIndentation} setting is enabled) to the output.
+        *
+        * @param depth The indentation.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter cr(int depth) throws IOException {
+               if (useIndentation)
+                       return nl().i(depth);
+               return this;
+       }
+
+       /**
+        * Writes an indent (if the {@code useIndentation} setting is enabled), 
followed by text,
+        *      followed by a newline (if the {@code useIndentation} setting is 
enabled).
+        *
+        * @param indent The number of tabs to indent.
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object.
+        */
+       public SerializerWriter appendln(int indent, String text) throws 
IOException {
+               return append(indent, true, text);
+       }
+
+       /**
+        * Writes the specified text followed by a newline (if the {@code 
useIndentation} setting is enabled).
+        *
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object.
+        */
+       public SerializerWriter appendln(String text) throws IOException {
+               return append(0, true, text);
+       }
+
+       /**
+        * Writes an indent (if the {@code useIndentation} setting is enabled), 
followed by text.
+        *
+        * @param indent The number of tabs to indent.
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object.
+        */
+       public SerializerWriter append(int indent, String text) throws 
IOException {
+               return append(indent, false, text);
+       }
+
+       /**
+        * Writes an indent (if the {@code useIndentation} setting is enabled), 
followed by text.
+        *
+        * @param indent The number of tabs to indent.
+        * @param c The character to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object.
+        */
+       public SerializerWriter append(int indent, char c) throws IOException {
+               return i(indent).append(c);
+       }
+
+       /**
+        * Writes an indent (if the {@code useIndentation} setting is enabled), 
followed by text,
+        *      optionally followed by a newline (if the {@code useIndentation} 
setting is enabled).
+        *
+        * @param indent The number of tabs to indent.
+        * @param newline If <jk>true</jk>, then a newline is written.
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       private SerializerWriter append(int indent, boolean newline, String 
text) throws IOException {
+               i(indent);
+               out.write(text);
+               if (newline)
+                       nl();
+               return this;
+       }
+
+       /**
+        * Appends the specified object as a URI.
+        * <p>
+        * Object is converted to a <code>String</code> using 
<code>toString()</code>, so this will work on {@link URL} or {@link URI} 
objects,
+        * or any other type that returns a URI via it's 
<code>toString()</code> method.
+        * <p>
+        * If the URI is relative (i.e. without a schema and not prepended with 
<js>'/'</js>) the URI
+        * will be prepended with {@link #absolutePathUriBase} and {@link 
#relativeUriBase}.
+        * <p>
+        * If the URI is context-absolute (i.e. without a schema, but prepended 
with <js>'/'</js>)
+        * the URI will be prepended with {@link #absolutePathUriBase}.
+        *
+        * @param uri The URI to serialize.
+        * @return This object (for method chaining).
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        */
+       public SerializerWriter appendUri(Object uri) 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 append(s);
+       }
+
+       /**
+        * Adds a whitespace character to the output if the {@code 
useWhitespace} setting is enabled.
+        *
+        * @return This object (for method chaining).
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        */
+       public SerializerWriter s() throws IOException {
+               if (useWhitespace)
+                       out.write(' ');
+               return this;
+       }
+
+       /**
+        * Adds the quote character specified by the {@code quoteChar} setting 
to the output.
+        *
+        * @return This object (for method chaining).
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        */
+       public SerializerWriter q() throws IOException {
+               out.write(quoteChar);
+               return this;
+       }
+
+       /**
+        * Writes an indent to the writer if the {@code useIndentation} setting 
is enabled.
+        *
+        * @param indent The number of tabs to indent.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter i(int indent) throws IOException {
+               if (useIndentation)
+                       for (int i = 0; i < indent; i++)
+                               out.write('\t');
+               return this;
+       }
+
+       /**
+        * Writes a newline to the writer if the {@code useIndentation} setting 
is enabled.
+        *
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter nl() throws IOException {
+               if (useIndentation)
+                       out.write('\n');
+               return this;
+       }
+
+       /**
+        * Writes the specified text to the writer.
+        *
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter append(Object text) throws IOException {
+               out.append(text == null ? null : text.toString());
+               return this;
+       }
+
+       /**
+        * Writes the specified text to the writer.
+        *
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter append(String text) throws IOException {
+               if (text != null)
+                       out.append(text);
+               return this;
+       }
+
+       /**
+        * Writes the specified text to the writer if b is true.
+        *
+        * @param b Boolean flag.
+        * @param text The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter appendIf(boolean b, String text) throws 
IOException {
+               if (b)
+                       out.write(text);
+               return this;
+       }
+
+       /**
+        * Writes the specified text to the writer if b is true.
+        *
+        * @param b Boolean flag.
+        * @param c The text to write.
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @return This object (for method chaining).
+        */
+       public SerializerWriter appendIf(boolean b, char c) throws IOException {
+               if (b)
+                       out.write(c);
+               return this;
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Writer */
+       public SerializerWriter append(char c) throws IOException {
+               out.write(c);
+               return this;
+       }
+
+       @Override /* Writer */
+       public void write(char[] cbuf, int off, int len) throws IOException {
+               out.write(cbuf, off, len);
+       }
+
+       @Override /* Writer */
+       public void flush() throws IOException {
+               out.flush();
+       }
+
+       @Override /* Writer */
+       public void close() throws IOException {
+               out.close();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.class
new file mode 100755
index 0000000..a6fcb1d
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.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/serializer/StringObject.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.java
new file mode 100755
index 0000000..ae9a506
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * 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.serializer;
+
+import java.io.*;
+
+import com.ibm.juno.core.*;
+
+/**
+ * A serializer/object pair used for delayed object serialization.
+ * <p>
+ * Useful in certain conditions such as logging when you don't want to 
needlessly serialize objects.
+ * <p>
+ * Instances of this method are created by the {@link 
WriterSerializer#toStringObject(Object)} method.
+ *
+ * <h6 class='topic'>Example</h6>
+ * <p class='bcode'>
+ *     <jc>// The POJO will not be serialized unless DEBUG is enabled.</jc>
+ *     logger.log(<jsf>DEBUG</jsf>, <js>"Object contents are: {0}"</js>, 
JsonSerializer.<jsf>DEFAULT</jsf>.toObjectString(myPojo));
+ * </p>
+ *
+ * @author James Bognar ([email protected])
+ */
+public class StringObject implements CharSequence, Writable {
+
+       private final WriterSerializer s;
+       private final Object o;
+       private String results;
+
+       /**
+        * Constructor.
+        * @param s The serializer to use to serialize the object.
+        * @param o The object to be serialized.
+        */
+       public StringObject(WriterSerializer s, Object o) {
+               this.s = s;
+               this.o = o;
+       }
+
+       @Override /* Object */
+       public String toString() {
+               if (results == null)
+                       results = s.toString(o);
+               return results;
+       }
+
+       @Override /* CharSequence */
+       public int length() {
+               return toString().length();
+       }
+
+       @Override /* CharSequence */
+       public char charAt(int index) {
+               return toString().charAt(index);
+       }
+
+       @Override /* CharSequence */
+       public CharSequence subSequence(int start, int end) {
+               return toString().subSequence(start, end);
+       }
+
+       @Override /* Writable */
+       public void writeTo(Writer w) throws IOException {
+               try {
+                       s.serialize(o, w);
+               } catch (SerializeException e) {
+                       throw new IOException(e);
+               }
+       }
+
+       @Override /* Writable */
+       public String getMediaType() {
+               return s.getMediaTypes()[0];
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.class
new file mode 100755
index 0000000..a1b81b4
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.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/serializer/WriterSerializer.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.java
new file mode 100755
index 0000000..6309176
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * 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.serializer;
+
+import java.io.*;
+import java.lang.reflect.*;
+
+import com.ibm.juno.core.*;
+import com.ibm.juno.core.annotation.*;
+
+/**
+ * Subclass of {@link Serializer} for character-based serializers.
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ *     This class is typically the parent class of all character-based 
serializers.
+ *     It has 2 abstract methods to implement...
+ * <ul>
+ *     <li>{@link #createContext(ObjectMap, Method)}
+ *     <li>{@link #doSerialize(Object, Writer, SerializerContext)}
+ * </ul>
+ *
+ *
+ * <h6 class='topic'>@Produces annotation</h6>
+ * <p>
+ *     The media types that this serializer can produce is specified through 
the {@link Produces @Produces} annotation.
+ * <p>
+ *     However, the media types can also be specified programmatically by 
overriding the {@link #getMediaTypes()}
+ *             and {@link #getResponseContentType()} methods.
+ *
+ * @author James Bognar ([email protected])
+ */
+public abstract class WriterSerializer extends Serializer<Writer> {
+
+       @Override /* Serializer */
+       public boolean isWriterSerializer() {
+               return true;
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Abstract methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Serializer */
+       protected abstract void doSerialize(Object o, Writer out, 
SerializerContext ctx) throws IOException, SerializeException;
+
+
+       
//--------------------------------------------------------------------------------
+       // Other methods
+       
//--------------------------------------------------------------------------------
+
+       /**
+        * Internal test method.
+        *
+        * @param o The object to serialize.
+        * @param ctx The serialize context.
+        *      This object is automatically closed after serialization.
+        * @return The output serialized to a string.
+        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        */
+       public final String serialize(Object o, SerializerContext ctx) throws 
SerializeException {
+               try {
+                       StringWriter w = new StringWriter();
+                       serialize(o, w, ctx);
+                       return w.toString();
+               } catch (IOException e) { // Shouldn't happen.
+                       throw new RuntimeException(e);
+               }
+       }
+
+       /**
+        * Convenience method for serializing an object to a String.
+        *
+        * @param o The object to serialize.
+        * @return The output serialized to a string.
+        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        */
+       public final String serialize(Object o) throws SerializeException {
+               try {
+                       StringWriter w = new StringWriter();
+                       serialize(o, w, createContext());
+                       return w.toString();
+               } catch (IOException e) {
+                       throw new RuntimeException(e); // Shouldn't happen.
+               }
+       }
+
+       /**
+        * Identical to {@link #serialize(Object)} except throws a {@link 
RuntimeException}
+        * instead of a {@link SerializeException}.
+        * This is typically good enough for debugging purposes.
+        *
+        * @param o The object to serialize.
+        * @return The serialized object.
+        */
+       public final String toString(Object o) {
+               try {
+                       StringWriter w = new StringWriter();
+                       serialize(o, w, createContext());
+                       return w.toString();
+               } catch (Exception e) {
+                       throw new RuntimeException(e);
+               }
+       }
+
+       /**
+        * Wraps the specified object inside a {@link StringObject}.
+        *
+        * @param o The object to wrap.
+        * @return The wrapped object.
+        */
+       public final StringObject toStringObject(Object o) {
+               return new StringObject(this, o);
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* CoreApi */
+       public WriterSerializer addNotBeanClasses(Class<?>...classes) throws 
LockedException {
+               super.addNotBeanClasses(classes);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public WriterSerializer addFilters(Class<?>...classes) throws 
LockedException {
+               super.addFilters(classes);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public <T> WriterSerializer addImplClass(Class<T> interfaceClass, 
Class<? extends T> implClass) throws LockedException {
+               super.addImplClass(interfaceClass, implClass);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public WriterSerializer setClassLoader(ClassLoader classLoader) throws 
LockedException {
+               super.setClassLoader(classLoader);
+               return this;
+       }
+
+       @Override /* Lockable */
+       public WriterSerializer lock() {
+               super.lock();
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public WriterSerializer clone() throws CloneNotSupportedException {
+               WriterSerializer c = (WriterSerializer)super.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/serializer/package.html
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/package.html 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/package.html
new file mode 100755
index 0000000..4fe443f
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/package.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<!--
+    Licensed Materials - Property of IBM
+    (c) Copyright IBM Corporation 2014. All Rights Reserved.
+   
+    Note to U.S. Government Users Restricted Rights:  
+    Use, duplication or disclosure restricted by GSA ADP Schedule 
+    Contract with IBM Corp. 
+ -->
+<html>
+<head>
+       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+       <style type="text/css">
+               /* For viewing in Page Designer */
+               @IMPORT url("../../../../../../javadoc.css");
+
+               /* For viewing in REST interface */
+               @IMPORT url("../htdocs/javadoc.css");
+               body { 
+                       margin: 20px; 
+               }       
+       </style>
+       <script>
+               /* Replace all @code and @link tags. */ 
+               window.onload = function() {
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>');
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, 
'<code>$3</code>');
+               }
+       </script>
+</head>
+<body>
+<p>Serializer API</p>
+
+<script>
+       function toggle(x) {
+               var div = x.nextSibling;
+               while (div != null && div.nodeType != 1)
+                       div = div.nextSibling;
+               if (div != null) {
+                       var d = div.style.display;
+                       if (d == 'block' || d == '') {
+                               div.style.display = 'none';
+                               x.className += " closed";
+                       } else {
+                               div.style.display = 'block';
+                               x.className = 
x.className.replace(/(?:^|\s)closed(?!\S)/g , '' );
+                       }
+               }
+       }
+</script>
+
+<a id='TOC'></a><h5 class='toc'>Table of Contents</h5>
+<ol class='toc'>
+       <li><p><a class='doclink' href='#Serializer'>Serializer API</a></p>
+       <ol>
+               <li><p><a class='doclink' href='#SerializerGroup'>The 
SerializerGroup class</a></p>
+       </ol>
+       <li><p><a class='doclink' href='#DefiningSerializer'>Defining a new 
Serializer</a></p>
+</ol>
+
+<!-- 
========================================================================================================
 -->
+<a id="Serializer"></a>
+<h2 class='topic' onclick='toggle(this)'>1 - Serializer API</h2>
+<div class='topic'>
+       <p>
+               The serialization API is designed to be easily extensible by 
developers.<br>
+               If you are writing your own serializer, you will typically 
subclass directly from either {@link 
com.ibm.juno.core.serializer.WriterSerializer}
+                       or {@link 
com.ibm.juno.core.serializer.OutputStreamSerializer}.<br>
+       </p>
+
+       <!-- 
========================================================================================================
 -->
+       <a id="SerializerGroup"></a>
+       <h3 class='topic' onclick='toggle(this)'>1.1 - The SerializerGroup 
class</h3>
+       <div class='topic'>
+               <p>
+                       The {@link 
com.ibm.juno.core.serializer.SerializerGroup} class represents a group of 
serializers registered with the media types they handle.
+               </p>
+               
+               <h6 class='topic'>Features</h6>         
+               <p>
+                       The <code>SerializerGroup</code> class provides the 
following features:
+               <ul>
+                       <li>Finds serializers based on HTTP <code>Accept</code> 
header values.
+                       <li>Sets common properties on all serializers in a 
single method call.
+                       <li>Locks all serializers in a single method call.
+                       <li>Clones existing groups and all serializers within 
the group in a single method call.
+               </ul>
+               
+               <p>
+                       Refer to {@link 
com.ibm.juno.core.serializer.SerializerGroup} for additional information.
+               </p>
+       </div> 
+</div>
+
+
+<!-- 
========================================================================================================
 -->
+<a id="DefiningSerializer"></a>
+<h2 class='topic' onclick='toggle(this)'>2 - Defining a new Serializer</h2>
+<div class='topic'>
+       <p>
+               Defining a new serializer is quite simple if you subclass 
directly from {@link com.ibm.juno.core.serializer.WriterSerializer} 
+                       or {@link 
com.ibm.juno.core.serializer.OutputStreamSerializer}.<br>
+               In each case, you simply need to implement a single
+                       method and specify a {@link 
com.ibm.juno.core.annotation.Produces} annotation.
+       </p>
+       <p>
+               The following example shows a simple serializer that converts 
images to output streams using standard JRE classes.
+       </p>
+       <p class='bcode'>
+       <ja>@Produces</ja>({<js>"image/png"</js>,<js>"image/jpeg"</js>})
+       <jk>public static class</jk> ImageSerializer <jk>extends</jk> 
OutputStreamSerializer {
+               <ja>@Override</ja>
+               <jk>public void</jk> serialize(Object o, OutputStream out, 
SerializerContext ctx) <jk>throws</jk> IOException, SerializeException {
+                       RenderedImage image = (RenderedImage)o;
+                       String mediaType = ctx.getMediaType();
+                       ImageIO.<jsm>write</jsm>(image, 
mediaType.substring(mediaType.indexOf(<js>'/'</js>)+1), out);
+               }       
+       }
+       </p>
+       <p>
+               Serializer that take advantage of the entire {@link 
com.ibm.juno.core.CoreApi} interface to be able to serialize arbitrary beans 
and POJOs is
+                       considerably more complex and outside the scope of this 
document.<br>  
+               If developing such a serializer, the best course of action 
would be to replicate what occurs in the {@link 
com.ibm.juno.core.json.JsonSerializer} class.
+       </p>
+</div>
+
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.class
new file mode 100755
index 0000000..1d9487b
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.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/soap/SoapXmlSerializer.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.java
new file mode 100755
index 0000000..df249f2
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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.soap;
+
+import static com.ibm.juno.core.soap.SoapXmlSerializerProperties.*;
+
+import java.io.*;
+
+import com.ibm.juno.core.*;
+import com.ibm.juno.core.annotation.*;
+import com.ibm.juno.core.serializer.*;
+import com.ibm.juno.core.xml.*;
+
+/**
+ * Serializes POJOs to HTTP responses as XML+SOAP.
+ *
+ *
+ * <h6 class='topic'>Media types</h6>
+ * <p>
+ *     Handles <code>Accept</code> types: <code>text/xml+soap</code>
+ * <p>
+ *     Produces <code>Content-Type</code> types: <code>text/xml+soap</code>
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ *     Essentially the same output as {@link XmlDocSerializer}, except wrapped 
in a standard SOAP envelope.
+ *
+ *
+ * <h6 class='topic'>Configurable properties</h6>
+ * <p>
+ *     This class has the following properties associated with it:
+ * <ul>
+ *     <li>{@link SoapXmlSerializerProperties}
+ *     <li>{@link XmlSerializerProperties}
+ *     <li>{@link SerializerProperties}
+ *     <li>{@link BeanContextProperties}
+ * </ul>
+ *
+ *
+ * @author James Bognar ([email protected])
+ */
+@Produces(value="text/xml+soap",contentType="text/xml")
+public final class SoapXmlSerializer extends XmlSerializer {
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Serializer */
+       protected void doSerialize(Object o, Writer out, SerializerContext ctx) 
throws IOException, SerializeException {
+               XmlSerializerContext xctx = (XmlSerializerContext)ctx;
+               XmlSerializerWriter w = xctx.getWriter(out);
+               w.append("<?xml")
+                       .attr("version", "1.0")
+                       .attr("encoding", "UTF-8")
+                       .appendln("?>");
+               w.oTag("soap", "Envelope")
+                       .attr("xmlns", "soap", 
xctx.getProperties().getString(SOAPXML_SOAPAction, 
"http://www.w3.org/2003/05/soap-envelope";))
+                       .appendln(">");
+               w.sTag(1, "soap", "Body").nl();
+               super.serialize(o, w, ctx);
+               w.eTag(1, "soap", "Body").nl();
+               w.eTag("soap", "Envelope").nl();
+       }
+
+       @Override /* Serializer */
+       public ObjectMap getResponseHeaders(ObjectMap properties) {
+               return super.getResponseHeaders(properties)
+                       .append("SOAPAction", 
properties.getString(SOAPXML_SOAPAction, 
"http://www.w3.org/2003/05/soap-envelope";));
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.class
new file mode 100755
index 0000000..9aac21c
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.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/soap/SoapXmlSerializerProperties.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.java
new file mode 100755
index 0000000..f66a7f2
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014. 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.soap;
+
+/**
+ * Properties associated with the {@link SoapXmlSerializer} class.
+ *
+ * @author James Bognar ([email protected])
+ */
+public final class SoapXmlSerializerProperties {
+
+       /**
+        * The <code>SOAPAction</code> HTTP header value to set on responses.
+        * <p>
+        * Default is <js>"http://www.w3.org/2003/05/soap-envelope";</js>.
+        */
+       public static final String SOAPXML_SOAPAction = 
"SoapXmlSerializer.SOAPAction";
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/package.html
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/package.html 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/package.html
new file mode 100755
index 0000000..3b4f818
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/package.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<!--
+    Licensed Materials - Property of IBM
+    (c) Copyright IBM Corporation 2014. All Rights Reserved.
+   
+    Note to U.S. Government Users Restricted Rights:  
+    Use, duplication or disclosure restricted by GSA ADP Schedule 
+    Contract with IBM Corp. 
+ -->
+<html>
+<head>
+       <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+       <style type="text/css">
+               /* For viewing in Page Designer */
+               @IMPORT url("../../../../../../javadoc.css");
+
+               /* For viewing in REST interface */
+               @IMPORT url("../htdocs/javadoc.css");
+               body { 
+                       margin: 20px; 
+               }       
+       </style>
+       <script>
+               /* Replace all @code and @link tags. */ 
+               window.onload = function() {
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>');
+                       document.body.innerHTML = 
document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, 
'<code>$3</code>');
+               }
+       </script>
+</head>
+<body>
+<p>SOAP/XML serialization and parsing support</p>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser$Decoding.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser$Decoding.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser$Decoding.class
new file mode 100755
index 0000000..c4824b2
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser$Decoding.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/UonParser.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.class
new file mode 100755
index 0000000..1a58860
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.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/UonParser.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.java
new file mode 100755
index 0000000..6638562
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.java
@@ -0,0 +1,861 @@
+/*******************************************************************************
+ * 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 UON (a notation for URL-encoded query parameter values) text into 
POJO models.
+ *
+ *
+ * <h6 class='topic'>Media types</h6>
+ * <p>
+ *     Handles <code>Content-Type</code> types: <code>text/uon</code>
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <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 ([email protected])
+ */
+@SuppressWarnings({ "rawtypes", "unchecked" })
+@Consumes("text/uon")
+public class UonParser extends ReaderParser {
+
+       /** Reusable instance of {@link UonParser}, all default settings. */
+       public static final UonParser DEFAULT = new UonParser().lock();
+
+       /** Reusable instance of {@link UonParser.Decoding}. */
+       public static final UonParser DEFAULT_DECODING = new Decoding().lock();
+
+       /** Reusable instance of {@link UonParser}, all default settings, 
whitespace-aware. */
+       public static final UonParser DEFAULT_WS_AWARE = new 
UonParser().setProperty(UON_whitespaceAware, true).lock();
+
+       // Characters that need to be preceeded with an escape character.
+       private static final AsciiSet escapedChars = new 
AsciiSet(",()~=$\u0001\u0002");
+
+       private static final char NUL='\u0000', AMP='\u0001', EQ='\u0002';  // 
Flags set in reader to denote & and = characters.
+
+       /**
+        * Equivalent to <code><jk>new</jk> 
UrlEncodingParser().setProperty(UonParserProperties.<jsf>UON_decodeChars</jsf>,<jk>true</jk>);</code>.
+        */
+       public static class Decoding extends UonParser {
+               /** Constructor */
+               public Decoding() {
+                       setProperty(UON_decodeChars, true);
+               }
+       }
+
+       /** UON parser properties currently set on this serializer. */
+       protected transient UonParserProperties upp = new UonParserProperties();
+
+       /** URL-Encoding properties currently set on this serializer. */
+       protected transient UrlEncodingProperties uep = new 
UrlEncodingProperties();
+
+       /**
+        * Workhorse method.
+        *
+        * @param nt The class type being parsed, or <jk>null</jk> if unknown.
+        * @param ctx The parser context for this parse.
+        * @param r The reader being parsed.
+        * @param p If this is a bean property value, the bean property meta, 
or null if this is not a bean property value being parsed.
+        * @param outer The outer object (for constructing nested inner 
classes).
+        * @param isUrlParamValue If <jk>true</jk>, then we're parsing a 
top-level URL-encoded value which is treated a bit different than the default 
case.
+        * @param name The parent field or map key name.
+        * @return The parsed object.
+        * @throws ParseException
+        */
+       protected <T> T parseAnything(ClassMeta<T> nt, UonParserContext ctx, 
ParserReader r, BeanPropertyMeta p, Object outer, boolean isUrlParamValue, 
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();
+
+               int line = r.getLine();
+               int column = r.getColumn();
+               Object o = null;
+               try {
+                       // Parse type flag '$x'
+                       char flag = readFlag(r, (char)0);
+
+                       int c = r.peek();
+
+                       if (c == -1 || c == AMP) {
+                               // If parameter is blank and it's an array or 
collection, return an empty list.
+                               if (ft.isArray() || ft.isCollection())
+                                       o = ft.newInstance();
+                               else if (ft.isString() || ft.isObject())
+                                       o = "";
+                               else if (ft.isPrimitive())
+                                       o = ft.getPrimitiveDefault();
+                               // Otherwise, leave null.
+                       } else if (ft.isObject()) {
+                               if (flag == 0 || flag == 's') {
+                                       o = parseString(r, isUrlParamValue, 
ctx);
+                               } else if (flag == 'b') {
+                                       o = parseBoolean(r, ctx);
+                               } else if (flag == 'n') {
+                                       o = parseNumber(r, null, ctx);
+                               } else if (flag == 'o') {
+                                       ObjectMap m = new ObjectMap(bc);
+                                       parseIntoMap(ctx, r, m, string(), 
object());
+                                       o = m.cast();
+                               } else if (flag == 'a') {
+                                       Collection l = new ObjectList(bc);
+                                       o = parseIntoCollection(ctx, r, l, 
ft.getElementType(), isUrlParamValue);
+                               } else {
+                                       throw new ParseException(line, column, 
"Unexpected flag character ''{0}''.", flag);
+                               }
+                       } else if (ft.isBoolean()) {
+                               o = parseBoolean(r, ctx);
+                       } else if (ft.isCharSequence()) {
+                               o = parseString(r, isUrlParamValue, ctx);
+                       } else if (ft.isChar()) {
+                               String s = parseString(r, isUrlParamValue, ctx);
+                               o = s == null ? null : s.charAt(0);
+                       } else if (ft.isNumber()) {
+                               o = parseNumber(r, (Class<? extends 
Number>)ft.getInnerClass(), ctx);
+                       } else if (ft.isMap()) {
+                               Map m = (ft.canCreateNewInstance(outer) ? 
(Map)ft.newInstance(outer) : new ObjectMap(bc));
+                               o = parseIntoMap(ctx, r, m, ft.getKeyType(), 
ft.getValueType());
+                       } else if (ft.isCollection()) {
+                               if (flag == 'o') {
+                                       ObjectMap m = new ObjectMap(bc);
+                                       parseIntoMap(ctx, r, m, string(), 
object());
+                                       // Handle case where it's a collection, 
but serialized as a map with a _class or _value key.
+                                       if (m.containsKey("_class") || 
m.containsKey("_value"))
+                                               o = m.cast();
+                                       // Handle case where it's a collection, 
but only a single value was specified.
+                                       else {
+                                               Collection l = 
(ft.canCreateNewInstance(outer) ? (Collection)ft.newInstance(outer) : new 
ObjectList(bc));
+                                               
l.add(m.cast(ft.getElementType()));
+                                               o = l;
+                                       }
+                               } else {
+                                       Collection l = 
(ft.canCreateNewInstance(outer) ? (Collection)ft.newInstance(outer) : new 
ObjectList(bc));
+                                       o = parseIntoCollection(ctx, r, l, 
ft.getElementType(), isUrlParamValue);
+                               }
+                       } 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 if (ft.canCreateNewInstanceFromString(outer)) {
+                               String s = parseString(r, isUrlParamValue, ctx);
+                               if (s != null)
+                                       o = ft.newInstanceFromString(outer, s);
+                       } else if (ft.isArray()) {
+                               if (flag == 'o') {
+                                       ObjectMap m = new ObjectMap(bc);
+                                       parseIntoMap(ctx, r, m, string(), 
object());
+                                       // Handle case where it's an array, but 
serialized as a map with a _class or _value key.
+                                       if (m.containsKey("_class") || 
m.containsKey("_value"))
+                                               o = m.cast();
+                                       // Handle case where it's an array, but 
only a single value was specified.
+                                       else {
+                                               ArrayList l = new ArrayList(1);
+                                               
l.add(m.cast(ft.getElementType()));
+                                               o = bc.toArray(ft, l);
+                                       }
+                               } else {
+                                       ArrayList l = 
(ArrayList)parseIntoCollection(ctx, r, new ArrayList(), ft.getElementType(), 
isUrlParamValue);
+                                       o = bc.toArray(ft, l);
+                               }
+                       } else if (flag == 'o') {
+                               // It could be a non-bean with _class attribute.
+                               ObjectMap m = new ObjectMap(bc);
+                               parseIntoMap(ctx, r, m, string(), object());
+                               if (m.containsKey("_class"))
+                                       o = m.cast();
+                               else
+                                       throw new ParseException(line, column, 
"Class ''{0}'' could not be instantiated.  Reason: ''{1}''", 
ft.getInnerClass().getName(), ft.getNotABeanReason());
+                       } else {
+                               throw new ParseException(line, column, "Class 
''{0}'' could not be instantiated.  Reason: ''{1}''", 
ft.getInnerClass().getName(), ft.getNotABeanReason());
+                       }
+
+                       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) {
+                       if (p == null)
+                               throw new ParseException("Error occurred trying 
to parse into class ''{0}''", ft).initCause(e);
+                       throw new ParseException("Error occurred trying to 
parse value for bean property ''{0}'' on class ''{1}''",
+                               p.getName(), p.getBeanMeta().getClassMeta()
+                       ).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 {
+               int line = r.getLine();
+               int column = r.getColumn();
+
+               if (keyType == null)
+                       keyType = (ClassMeta<K>)string();
+
+               int c = r.read();
+               if (c == -1 || c == NUL || c == AMP)
+                       return null;
+               if (c != '(')
+                       throw new ParseException(line, column, "Expected '(' at 
beginning of object.");
+
+               final int S1=1; // Looking for attrName start.
+               final int S2=2; // Found attrName end, looking for =.
+               final int S3=3; // Found =, looking for valStart.
+               final int S4=4; // Looking for , or )
+               boolean isInEscape = false;
+
+               int state = S1;
+               K currAttr = null;
+               while (c != -1 && c != AMP) {
+                       c = r.read();
+                       if (! isInEscape) {
+                               if (state == S1) {
+                                       if (c == ')')
+                                               return m;
+                                       if ((c == '\n' || c == '\r') && 
ctx.whitespaceAware)
+                                               skipSpace(r);
+                                       else {
+                                               r.unread();
+                                               Object attr = parseAttr(r, 
ctx.decodeChars, ctx);
+                                               currAttr = (attr == null ? null 
: ctx.getBeanContext().convertToType(attr, keyType));
+                                               state = S2;
+                                               c = 0; // Avoid isInEscape if c 
was '\'
+                                       }
+                               } else if (state == S2) {
+                                       if (c == EQ || c == '=')
+                                               state = S3;
+                                       else if (c == -1 || c == ',' || c == 
')' || c == AMP) {
+                                               if (currAttr == null) {
+                                                       // Value was '%00'
+                                                       r.unread();
+                                                       return null;
+                                               }
+                                               m.put(currAttr, null);
+                                               if (c == ')' || c == -1 || c == 
AMP)
+                                                       return m;
+                                               state = S1;
+                                       }
+                               } else if (state == S3) {
+                                       if (c == -1 || c == ',' || c == ')' || 
c == AMP) {
+                                               V value = convertAttrToType(m, 
"", valueType);
+                                               m.put(currAttr, value);
+                                               if (c == -1 || c == ')' || c == 
AMP)
+                                                       return m;
+                                               state = S1;
+                                       } else  {
+                                               V value = 
parseAnything(valueType, ctx, r.unread(), null, m, false, currAttr);
+                                               m.put(currAttr, value);
+                                               state = S4;
+                                               c = 0; // Avoid isInEscape if c 
was '\'
+                                       }
+                               } else if (state == S4) {
+                                       if (c == ',')
+                                               state = S1;
+                                       else if (c == ')' || c == -1 || c == 
AMP) {
+                                               return m;
+                                       }
+                               }
+                       }
+                       isInEscape = isInEscape(c, r, isInEscape);
+               }
+               if (state == S1)
+                       throw new ParseException(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, "Dangling '=' 
found in object entry");
+               if (state == S4)
+                       throw new ParseException(line, column, "Could not find 
')' marking end of object.");
+
+               return null; // Unreachable.
+       }
+
+       private <E> Collection<E> parseIntoCollection(UonParserContext ctx, 
ParserReader r, Collection<E> l, ClassMeta<E> elementType, boolean 
isUrlParamValue) throws ParseException, IOException {
+               int line = r.getLine();
+               int column = r.getColumn();
+
+               int c = r.read();
+               if (c == -1 || c == NUL || c == AMP)
+                       return null;
+
+               // If we're parsing a top-level parameter, we're allowed to 
have comma-delimited lists outside parenthesis (e.g. "&foo=1,2,3&bar=a,b,c")
+               // This is not allowed at lower levels since we use comma's as 
end delimiters.
+               boolean isInParens = (c == '(');
+               if (! isInParens)
+                       if (isUrlParamValue)
+                               r.unread();
+                       else
+                               throw new ParseException(line, column, "Could 
not find '(' marking beginning of collection.");
+
+               if (isInParens) {
+                       final int S1=1; // Looking for starting of first entry.
+                       final int S2=2; // Looking for starting of subsequent 
entries.
+                       final int S3=3; // Looking for , or ) after first entry.
+
+                       int state = S1;
+                       while (c != -1 && c != AMP) {
+                               c = r.read();
+                               if (state == S1 || state == S2) {
+                                       if (c == ')') {
+                                               if (state == S2) {
+                                                       
l.add(parseAnything(elementType, ctx, r.unread(), null, l, false, null));
+                                                       r.read();
+                                               }
+                                               return l;
+                                       } else if ((c == '\n' || c == '\r') && 
ctx.whitespaceAware) {
+                                               skipSpace(r);
+                                       } else {
+                                               
l.add(parseAnything(elementType, ctx, r.unread(), null, l, false, null));
+                                               state = S3;
+                                       }
+                               } else if (state == S3) {
+                                       if (c == ',') {
+                                               state = S2;
+                                       } else if (c == ')') {
+                                               return l;
+                                       }
+                               }
+                       }
+                       if (state == S1 || state == S2)
+                               throw new ParseException(line, column, "Could 
not find start of entry in array.");
+                       if (state == S3)
+                               throw new ParseException(line, column, "Could 
not find end of entry in array.");
+
+               } else {
+                       final int S1=1; // Looking for starting of entry.
+                       final int S2=2; // Looking for , or & or END after 
first entry.
+
+                       int state = S1;
+                       while (c != -1 && c != AMP) {
+                               c = r.read();
+                               if (state == S1) {
+                                       if ((c == '\n' || c == '\r') && 
ctx.whitespaceAware) {
+                                               skipSpace(r);
+                                       } else {
+                                               
l.add(parseAnything(elementType, ctx, r.unread(), null, l, false, null));
+                                               state = S2;
+                                       }
+                               } else if (state == S2) {
+                                       if (c == ',') {
+                                               state = S1;
+                                       } else if ((c == '\n' || c == '\r') && 
ctx.whitespaceAware) {
+                                               skipSpace(r);
+                                       } else if (c == AMP || c == -1) {
+                                               r.unread();
+                                               return l;
+                                       }
+                               }
+                       }
+               }
+
+               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.read();
+               if (c == -1 || c == NUL || c == AMP)
+                       return null;
+               if (c != '(')
+                       throw new ParseException(line, column, "Expected '(' at 
beginning of object.");
+
+               final int S1=1; // Looking for attrName start.
+               final int S2=2; // Found attrName end, looking for =.
+               final int S3=3; // Found =, looking for valStart.
+               final int S4=4; // Looking for , or }
+               boolean isInEscape = false;
+
+               int state = S1;
+               String currAttr = "";
+               int currAttrLine = -1, currAttrCol = -1;
+               while (c != -1 && c != AMP) {
+                       c = r.read();
+                       if (! isInEscape) {
+                               if (state == S1) {
+                                       if (c == ')' || c == -1 || c == AMP) {
+                                               return m;
+                                       }
+                                       if ((c == '\n' || c == '\r') && 
ctx.whitespaceAware)
+                                               skipSpace(r);
+                                       else {
+                                               r.unread();
+                                               currAttrLine= r.getLine();
+                                               currAttrCol = r.getColumn();
+                                               currAttr = parseAttrName(r, 
ctx.decodeChars);
+                                               if (currAttr == null)  // Value 
was '%00'
+                                                       return null;
+                                               state = S2;
+                                       }
+                               } else if (state == S2) {
+                                       if (c == EQ || c == '=')
+                                               state = S3;
+                                       else if (c == -1 || c == ',' || c == 
')' || c == AMP) {
+                                               m.put(currAttr, null);
+                                               if (c == ')' || c == -1 || c == 
AMP)
+                                                       return m;
+                                               state = S1;
+                                       }
+                               } else if (state == S3) {
+                                       if (c == -1 || c == ',' || c == ')' || 
c == AMP) {
+                                               if (! 
currAttr.equals("_class")) {
+                                                       BeanPropertyMeta pMeta 
= m.getPropertyMeta(currAttr);
+                                                       if (pMeta == null) {
+                                                               if 
(m.getMeta().isSubTyped()) {
+                                                                       
m.put(currAttr, "");
+                                                               } else {
+                                                                       
onUnknownProperty(ctx, currAttr, m, currAttrLine, currAttrCol);
+                                                               }
+                                                       } else {
+                                                               Object value = 
ctx.getBeanContext().convertToType("", pMeta.getClassMeta());
+                                                               pMeta.set(m, 
value);
+                                                       }
+                                               }
+                                               if (c == -1 || c == ')' || c == 
AMP)
+                                                       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), false, currAttr));
+                                                               } else {
+                                                                       
onUnknownProperty(ctx, currAttr, m, currAttrLine, currAttrCol);
+                                                                       
parseAnything(object(), ctx, r.unread(), null, m.getBean(false), false, null); 
// Read content anyway to ignore it
+                                                               }
+                                                       } else {
+                                                               Object value = 
parseAnything(pMeta.getClassMeta(), ctx, r.unread(), pMeta, m.getBean(false), 
false, currAttr);
+                                                               pMeta.set(m, 
value);
+                                                       }
+                                               }
+                                               state = S4;
+                                       }
+                               } else if (state == S4) {
+                                       if (c == ',')
+                                               state = S1;
+                                       else if (c == ')' || c == -1 || c == 
AMP) {
+                                               return m;
+                                       }
+                               }
+                       }
+                       isInEscape = isInEscape(c, r, isInEscape);
+               }
+               if (state == S1)
+                       throw new ParseException(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 
')' marking end of object.");
+
+               return null; // Unreachable.
+       }
+
+       Object parseAttr(ParserReader r, boolean encoded, UonParserContext ctx) 
throws IOException, ParseException {
+               Object attr;
+               int c = r.peek();
+               if (c == '$') {
+                       char f = readFlag(r, (char)0);
+                       if (f == 'b')
+                               attr = parseBoolean(r, ctx);
+                       else if (f == 'n')
+                               attr = parseNumber(r, null, ctx);
+                       else
+                               attr = parseAttrName(r, encoded);
+               } else {
+                       attr = parseAttrName(r, encoded);
+               }
+               return attr;
+       }
+
+       String parseAttrName(ParserReader r, boolean encoded) throws 
IOException, ParseException {
+
+               // If string is of form '(xxx)', we're looking for ')' at the 
end.
+               // Otherwise, we're looking for '&' or '=' or -1 denoting the 
end of this string.
+
+               int line = r.getLine();
+               int column = r.getColumn();
+               int c = r.peek();
+               if (c == '$')
+                       readFlag(r, 's');
+               if (c == '(')
+                       return parsePString(r);
+
+               r.mark();
+               boolean isInEscape = false;
+               if (encoded) {
+                       while (c != -1) {
+                               c = r.read();
+                               if (! isInEscape) {
+                                       if (c == AMP || c == EQ || c == -1) {
+                                               if (c != -1)
+                                                       r.unread();
+                                               String s = r.getMarked();
+                                               return (s.equals("\u0000") ? 
null : s);
+                                       }
+                               }
+                               else if (c == AMP)
+                                       r.replace('&');
+                               else if (c == EQ)
+                                       r.replace('=');
+                               isInEscape = isInEscape(c, r, isInEscape);
+                       }
+               } else {
+                       while (c != -1) {
+                               c = r.read();
+                               if (! isInEscape) {
+                                       if (c == '=' || c == -1) {
+                                               if (c != -1)
+                                                       r.unread();
+                                               String s = r.getMarked();
+                                               return (s.equals("\u0000") ? 
null : s);
+                                       }
+                               }
+                               isInEscape = isInEscape(c, r, isInEscape);
+                       }
+               }
+
+               // We should never get here.
+               throw new ParseException(line, column, "Unexpected condition.");
+       }
+
+
+       /**
+        * Returns true if the next character in the stream is preceeded by an 
escape '~' character.
+        * @param c The current character.
+        * @param r The reader.
+        * @param prevIsInEscape What the flag was last time.
+        */
+       private static final boolean isInEscape(int c, ParserReader r, boolean 
prevIsInEscape) throws IOException {
+               if (c == '~' && ! prevIsInEscape) {
+                       c = r.peek();
+                       if (escapedChars.contains(c)) {
+                               r.delete();
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       String parseString(ParserReader r, boolean isUrlParamValue, 
UonParserContext ctx) throws IOException, ParseException {
+
+               // If string is of form '(xxx)', we're looking for ')' at the 
end.
+               // Otherwise, we're looking for ',' or ')' or -1 denoting the 
end of this string.
+
+               int c = r.peek();
+               if (c == '(')
+                       return parsePString(r);
+
+               r.mark();
+               boolean isInEscape = false;
+               String s = null;
+               AsciiSet endChars = (isUrlParamValue ? endCharsParam : 
endCharsNormal);
+               while (c != -1) {
+                       c = r.read();
+                       if (! isInEscape) {
+                               // If this is a URL parameter value, we're 
looking for:  &
+                               // If not, we're looking for:  &,)
+                               if (endChars.contains(c)) {
+                                       r.unread();
+                                       c = -1;
+                               }
+                       }
+                       if (c == -1)
+                               s = r.getMarked();
+                       else if (c == EQ)
+                               r.replace('=');
+                       else if ((c == '\n' || c == '\r') && 
ctx.whitespaceAware) {
+                               s = r.getMarked(0, -1);
+                               skipSpace(r);
+                               c = -1;
+                       }
+                       isInEscape = isInEscape(c, r, isInEscape);
+               }
+
+               return (s == null || s.equals("\u0000") ? null : s);
+       }
+
+       private static final AsciiSet endCharsParam = new AsciiSet(""+AMP), 
endCharsNormal = new AsciiSet(",)"+AMP);
+
+
+       /**
+        * Parses a string of the form "(foo)"
+        * All whitespace within parenthesis are preserved.
+        */
+       static String parsePString(ParserReader r) throws IOException, 
ParseException {
+
+               int line = r.getLine();
+               int column = r.getColumn();
+               r.read(); // Skip first parenthesis.
+               r.mark();
+               int c = 0;
+
+               boolean isInEscape = false;
+               while (c != -1) {
+                       c = r.read();
+                       if (! isInEscape) {
+                               if (c == ')')
+                                       return r.getMarked(0, -1);
+                       }
+                       if (c == EQ)
+                               r.replace('=');
+                       isInEscape = isInEscape(c, r, isInEscape);
+               }
+               throw new ParseException(line, column, "Unmatched parenthesis");
+       }
+
+       private Boolean parseBoolean(ParserReader r, UonParserContext ctx) 
throws IOException, ParseException {
+               int line = r.getLine();
+               int column = r.getColumn();
+               readFlag(r, 'b');
+               String s = parseString(r, false, ctx);
+               if (s == null)
+                       return null;
+               if (s.equals("true"))
+                       return true;
+               if (s.equals("false"))
+                       return false;
+               throw new ParseException(line, column, "Unrecognized syntax for 
boolean.  ''{0}''.", s);
+       }
+
+       private Number parseNumber(ParserReader r, Class<? extends Number> c, 
UonParserContext ctx) throws IOException, ParseException {
+               readFlag(r, 'n');
+               String s = parseString(r, false, ctx);
+               if (s == null)
+                       return null;
+               return StringUtils.parseNumber(s, c);
+       }
+
+       /*
+        * Call this method after you've finished a parsing a string to make 
sure that if there's any
+        * remainder in the input, that it consists only of whitespace and 
comments.
+        */
+       private void validateEnd(ParserReader r) throws ParseException, 
IOException {
+               int line = r.getLine();
+               int column = r.getColumn();
+               int c = r.read();
+               if (c != -1)
+                       throw new ParseException(line, column, "Remainder after 
parse: ''{0}''.", (char)c);
+       }
+
+       /**
+        * Reads flag character from "$x(" construct if flag is present.
+        * Returns 0 if no flag is present.
+        */
+       static char readFlag(ParserReader r, char expected) throws IOException, 
ParseException {
+               int line = r.getLine();
+               int column = r.getColumn();
+               char c = (char)r.peek();
+               if (c == '$') {
+                       r.read();
+                       char f = (char)r.read();
+                       if (expected != 0 && f != expected)
+                               throw new ParseException(line, column, 
"Unexpected flag character: ''{0}''.  Expected ''{1}''.", f, expected);
+                       c = (char)r.peek();
+                       // Type flag must be followed by '('
+                       if (c != '(')
+                               throw new ParseException(line, column, 
"Unexpected character following flag: ''{0}''.", c);
+                       return f;
+               }
+               return 0;
+       }
+
+       private Object[] parseArgs(UonParserContext ctx, ParserReader r, 
ClassMeta<?>[] argTypes) throws ParseException, IOException {
+               int line = r.getLine();
+               int column = r.getColumn();
+
+               final int S1=1; // Looking for start of entry
+               final int S2=2; // Looking for , or )
+
+               Object[] o = new Object[argTypes.length];
+               int i = 0;
+
+               int c = r.read();
+               if (c == -1 || c == AMP)
+                       return null;
+               if (c != '(')
+                       throw new ParseException("Expected '(' at beginning of 
args array.");
+
+               int state = S1;
+               while (c != -1 && c != AMP) {
+                       c = r.read();
+                       if (state == S1) {
+                               if (c == ')')
+                                       return o;
+                               o[i] = parseAnything(argTypes[i], ctx, 
r.unread(), null, ctx.getOuter(), false, null);
+                               i++;
+                               state = S2;
+                       } else if (state == S2) {
+                               if (c == ',') {
+                                       state = S1;
+                               } else if (c == ')') {
+                                       return o;
+                               }
+                       }
+               }
+
+               throw new ParseException(line, column, "Did not find ')' at the 
end of args array.");
+       }
+
+       private static void skipSpace(ParserReader r) throws IOException {
+               int c = 0;
+               while ((c = r.read()) != -1) {
+                       if (c > ' ' || c <= 2) {
+                               r.unread();
+                               return;
+                       }
+               }
+       }
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Parser */
+       public UonParserContext createContext(ObjectMap properties, Method 
javaMethod, Object outer) {
+               return new UonParserContext(getBeanContext(), pp, upp, uep, 
properties, javaMethod, outer);
+       }
+
+       @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, null, ctx.getOuter(), true, 
null);
+               validateEnd(r);
+               return o;
+       }
+
+       @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);
+               readFlag(r, 'o');
+               m = parseIntoMap(uctx, r, m, 
ctx.getBeanContext().getClassMeta(keyType), 
ctx.getBeanContext().getClassMeta(valueType));
+               validateEnd(r);
+               return m;
+       }
+
+       @Override /* ReaderParser */
+       protected <E> Collection<E> doParseIntoCollection(Reader in, int 
estimatedSize, Collection<E> c, Type elementType, ParserContext ctx) throws 
ParseException, IOException {
+               UonParserContext uctx = (UonParserContext)ctx;
+               UonParserReader r = uctx.getUrlEncodingParserReader(in, 
estimatedSize);
+               readFlag(r, 'a');
+               c = parseIntoCollection(uctx, r, c, 
ctx.getBeanContext().getClassMeta(elementType), false);
+               validateEnd(r);
+               return c;
+       }
+
+       @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);
+               readFlag(r, 'a');
+               Object[] a = parseArgs(uctx, r, argTypes);
+               return a;
+       }
+
+       @Override /* Parser */
+       public UonParser 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 UonParser setProperties(ObjectMap properties) throws 
LockedException {
+               for (Map.Entry<String,Object> e : properties.entrySet())
+                       setProperty(e.getKey(), e.getValue());
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public UonParser addNotBeanClasses(Class<?>...classes) throws 
LockedException {
+               super.addNotBeanClasses(classes);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public UonParser addFilters(Class<?>...classes) throws LockedException {
+               super.addFilters(classes);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public <T> UonParser addImplClass(Class<T> interfaceClass, Class<? 
extends T> implClass) throws LockedException {
+               super.addImplClass(interfaceClass, implClass);
+               return this;
+       }
+
+       @Override /* CoreApi */
+       public UonParser setClassLoader(ClassLoader classLoader) throws 
LockedException {
+               super.setClassLoader(classLoader);
+               return this;
+       }
+
+       @Override /* Lockable */
+       public UonParser lock() {
+               super.lock();
+               return this;
+       }
+
+       @Override /* Lockable */
+       public UonParser clone() {
+               try {
+                       UonParser c = (UonParser)super.clone();
+                       c.upp = upp.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/UonParserContext.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.class
new file mode 100755
index 0000000..87fa9f6
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.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/UonParserContext.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.java
new file mode 100755
index 0000000..631bdcd
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * 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.UonParserProperties.*;
+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.parser.*;
+
+/**
+ * Context object that lives for the duration of a single parsing in {@link 
UonParser} and {@link UrlEncodingParser}.
+ * <p>
+ *
+ * @author James Bognar ([email protected])
+ */
+public class UonParserContext extends ParserContext {
+
+       boolean decodeChars, whitespaceAware, expandedParams;
+
+       /**
+        * Create a new parser context with the specified options.
+        *
+        * @param beanContext The bean context being used.
+        * @param pp The default parser properties.
+        * @param upp The default UON-Encoding properties.
+        * @param uep The default URL-Encoding properties.
+        * @param op The override properties.
+        * @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.
+        */
+       public UonParserContext(BeanContext beanContext, ParserProperties pp, 
UonParserProperties upp, UrlEncodingProperties uep, ObjectMap op, Method 
javaMethod, Object outer) {
+               super(beanContext, pp, op, javaMethod, outer);
+               if (op == null || op.isEmpty()) {
+                       decodeChars = upp.decodeChars;
+                       whitespaceAware = upp.whitespaceAware;
+                       expandedParams = uep.expandedParams;
+               } else {
+                       decodeChars = op.getBoolean(UON_decodeChars, 
upp.decodeChars);
+                       whitespaceAware = op.getBoolean(UON_whitespaceAware, 
upp.whitespaceAware);
+                       expandedParams = op.getBoolean(URLENC_expandedParams, 
uep.expandedParams);
+               }
+       }
+
+       /**
+        * 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 reader in a {@link UonParserReader}.
+        *
+        * @param r The reader to wrap.
+        * @param estimatedSize The estimated size of the input.
+        * @return The wrapped reader.
+        */
+       public final UonParserReader getUrlEncodingParserReader(Reader r, int 
estimatedSize) {
+               if (r instanceof UonParserReader)
+                       return (UonParserReader)r;
+               return new UonParserReader(r, Math.min(8096, estimatedSize), 
decodeChars);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.class
new file mode 100755
index 0000000..cffaddd
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.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/UonParserProperties.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.java
new file mode 100755
index 0000000..f7b16ef
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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.parser.*;
+
+/**
+ * Configurable properties on the {@link UonParser} and {@link 
UrlEncodingParser} classes.
+ * <p>
+ *     Use the {@link UonParser#setProperty(String, Object)} method to set 
property values.
+ * <p>
+ *     In addition to these properties, the following properties are also 
applicable for {@link UonParser}.
+ * <ul>
+ *     <li>{@link ParserProperties}
+ *     <li>{@link BeanContextProperties}
+ * </ul>
+ *
+ * @author James Bognar ([email protected])
+ */
+public class UonParserProperties implements Cloneable {
+
+       /**
+        * Decode <js>"%xx"</js> sequences. ({@link Boolean}, 
default=<jk>false</jk> for {@link UonParser}, <jk>true</jk> for {@link 
UrlEncodingParser}).
+        * <p>
+        * Specify <jk>true</jk> if URI encoded characters should be decoded, 
<jk>false</jk>
+        *      if they've already been decoded before being passed to this 
parser.
+        */
+       public static final String UON_decodeChars = "UonParser.decodeChars";
+
+       /**
+        * Expect input to contain readable whitespace characters from using 
the {@link UonSerializerProperties#UON_useWhitespace} setting ({@link Boolean}, 
default=<jk>false</jk>).
+        */
+       public static final String UON_whitespaceAware = 
"UonParser.whitespaceAware";
+
+       boolean
+               decodeChars = false,
+               whitespaceAware = 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_decodeChars))
+                       decodeChars = bc.convertToType(value, Boolean.class);
+               else if (property.equals(UON_whitespaceAware))
+                       whitespaceAware = bc.convertToType(value, 
Boolean.class);
+               else
+                       return false;
+               return true;
+       }
+
+
+       
//--------------------------------------------------------------------------------
+       // Overridden methods
+       
//--------------------------------------------------------------------------------
+
+       @Override /* Cloneable */
+       public UonParserProperties clone() {
+               try {
+                       return (UonParserProperties)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/UonParserReader.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.class
new file mode 100755
index 0000000..f8585fb
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.class
 differ

Reply via email to