New SERIALIZER_maxIndent setting.

Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/7239f3e3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/7239f3e3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/7239f3e3

Branch: refs/heads/master
Commit: 7239f3e34150fd432b79049c4896229de1ed8f19
Parents: 257776b
Author: JamesBognar <[email protected]>
Authored: Tue Jun 13 10:27:34 2017 -0400
Committer: JamesBognar <[email protected]>
Committed: Tue Jun 13 10:27:34 2017 -0400

----------------------------------------------------------------------
 .../java/org/apache/juneau/MaxIndentTest.java   | 313 +++++++++++++++++++
 .../juneau/ini/ConfigFileInterfaceTest.java     |   4 +-
 .../apache/juneau/csv/CsvSerializerBuilder.java |   6 +
 .../java/org/apache/juneau/dto/html5/Input.java |  13 +
 .../apache/juneau/html/HtmlDocSerializer.java   |  12 +-
 .../juneau/html/HtmlDocSerializerSession.java   |   2 +-
 .../juneau/html/HtmlDocTemplateBasic.java       |  52 +--
 .../org/apache/juneau/html/HtmlSerializer.java  |  74 ++---
 .../juneau/html/HtmlSerializerSession.java      |   2 +-
 .../juneau/html/HtmlStrippedDocSerializer.java  |   2 +-
 .../java/org/apache/juneau/html/HtmlWriter.java |  16 +-
 .../apache/juneau/html/SimpleHtmlWriter.java    |   2 +-
 .../java/org/apache/juneau/ini/ConfigFile.java  |  23 +-
 .../org/apache/juneau/ini/ConfigFileImpl.java   |  29 +-
 .../apache/juneau/ini/ConfigFileWrapped.java    |   8 +-
 .../apache/juneau/jso/JsoSerializerBuilder.java |   6 +
 .../org/apache/juneau/json/JsonSerializer.java  |  24 +-
 .../juneau/json/JsonSerializerBuilder.java      |   6 +
 .../juneau/json/JsonSerializerSession.java      |   2 +-
 .../java/org/apache/juneau/json/JsonWriter.java |  41 ++-
 .../msgpack/MsgPackSerializerBuilder.java       |   6 +
 .../plaintext/PlainTextSerializerBuilder.java   |   6 +
 .../juneau/serializer/SerializerBuilder.java    |  25 ++
 .../juneau/serializer/SerializerContext.java    |  24 +-
 .../serializer/SerializerGroupBuilder.java      |  11 +
 .../juneau/serializer/SerializerSession.java    |  13 +-
 .../juneau/serializer/SerializerWriter.java     |  58 +++-
 .../apache/juneau/soap/SoapXmlSerializer.java   |   6 +-
 .../org/apache/juneau/uon/UonSerializer.java    |   6 +-
 .../apache/juneau/uon/UonSerializerBuilder.java |   6 +
 .../apache/juneau/uon/UonSerializerSession.java |   2 +-
 .../java/org/apache/juneau/uon/UonWriter.java   |  15 +-
 .../apache/juneau/xml/XmlSchemaSerializer.java  |  65 ++--
 .../org/apache/juneau/xml/XmlSerializer.java    |  14 +-
 .../apache/juneau/xml/XmlSerializerBuilder.java |   6 +
 .../apache/juneau/xml/XmlSerializerSession.java |   2 +-
 .../java/org/apache/juneau/xml/XmlWriter.java   |  21 +-
 juneau-core/src/main/javadoc/overview.html      |   1 +
 .../juneau/rest/client/RestClientBuilder.java   |  11 +
 .../org/apache/juneau/rest/RestRequest.java     |  12 +
 40 files changed, 747 insertions(+), 200 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core-test/src/test/java/org/apache/juneau/MaxIndentTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/MaxIndentTest.java 
b/juneau-core-test/src/test/java/org/apache/juneau/MaxIndentTest.java
new file mode 100644
index 0000000..f50bcdd
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/MaxIndentTest.java
@@ -0,0 +1,313 @@
+// 
***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                
                                              *
+// *                                                                           
                                              *
+// *  http://www.apache.org/licenses/LICENSE-2.0                               
                                              *
+// *                                                                           
                                              *
+// * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the 
License.                                              *
+// 
***************************************************************************************************************************
+package org.apache.juneau;
+
+import java.util.*;
+
+import org.apache.juneau.html.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.test.pojos.*;
+import org.apache.juneau.uon.*;
+import org.apache.juneau.urlencoding.*;
+import org.apache.juneau.xml.*;
+import org.junit.*;
+import org.junit.runner.*;
+import org.junit.runners.*;
+
+/**
+ * Exhaustive serialization tests DynaBean support.
+ */
+@RunWith(Parameterized.class)
+@SuppressWarnings({"javadoc","serial"})
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class MaxIndentTest {
+
+       @Parameterized.Parameters
+       public static Collection<Object[]> getParameters() {
+               return Arrays.asList(new Object[][] {
+                       {       /* 0 */
+                               new Input(
+                                       "List1dOfBeans-0",
+                                       new List1dOfBeans().init1(),
+                                       0,
+                                       /* Json */              "[{a:1, 
b:'foo'}]",
+                                       /* Xml */               
"<array><object><a>1</a><b>foo</b></object></array>\n",
+                                       /* Html */              "<table 
_type='array'><tr><th>a</th><th>b</th></tr><tr><td>1</td><td>foo</td></tr></table>\n",
+                                       /* Uon */               
"@((a=1,b=foo))",
+                                       /* UrlEnc */    "0=(a=1,b=foo)"
+                               )
+                       },
+                       {       /* 1 */
+                               new Input(
+                                       "List1dOfBeans-1",
+                                       new List1dOfBeans().init1(),
+                                       1,
+                                       /* Json */              "[\n\t{a:1, 
b:'foo'}\n]",
+                                       /* Xml */               
"<array>\n\t<object><a>1</a><b>foo</b></object>\n</array>\n",
+                                       /* Html */              "<table 
_type='array'>\n\t<tr><th>a</th><th>b</th></tr>\n\t<tr><td>1</td><td>foo</td></tr>\n</table>\n",
+                                       /* Uon */               
"@(\n\t(a=1,b=foo)\n)",
+                                       /* UrlEnc */    
"0=(\n\ta=1,\n\tb=foo\n)"
+                               )
+                       },
+                       {       /* 2 */
+                               new Input(
+                                       "List1dOfBeans-2",
+                                       new List1dOfBeans().init1(),
+                                       2,
+                                       /* Json */              "[\n\t{\n\t\ta: 
1,\n\t\tb: 'foo'\n\t}\n]",
+                                       /* Xml */               
"<array>\n\t<object>\n\t\t<a>1</a>\n\t\t<b>foo</b>\n\t</object>\n</array>\n",
+                                       /* Html */              "<table 
_type='array'>\n\t<tr>\n\t\t<th>a</th>\n\t\t<th>b</th>\n\t</tr>\n\t<tr>\n\t\t<td>1</td>\n\t\t<td>foo</td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               
"@(\n\t(\n\t\ta=1,\n\t\tb=foo\n\t)\n)",
+                                       /* UrlEnc */    
"0=(\n\ta=1,\n\tb=foo\n)"
+                               )
+                       },
+                       {       /* 3 */
+                               new Input(
+                                       "List2dOfBeans-0",
+                                       new List2dOfBeans().init2(),
+                                       0,
+                                       /* Json */              "[[{a:1, 
b:'foo'}]]",
+                                       /* Xml */               
"<array><array><object><a>1</a><b>foo</b></object></array></array>\n",
+                                       /* Html */              "<ul><li><table 
_type='array'><tr><th>a</th><th>b</th></tr><tr><td>1</td><td>foo</td></tr></table></li></ul>\n",
+                                       /* Uon */               
"@(@((a=1,b=foo)))",
+                                       /* UrlEnc */    "0=@((a=1,b=foo))"
+                               )
+                       },
+                       {       /* 4 */
+                               new Input(
+                                       "List2dOfBeans-1",
+                                       new List2dOfBeans().init2(),
+                                       1,
+                                       /* Json */              "[\n\t[{a:1, 
b:'foo'}]\n]",
+                                       /* Xml */               
"<array>\n\t<array><object><a>1</a><b>foo</b></object></array>\n</array>\n",
+                                       /* Html */              
"<ul>\n\t<li><table 
_type='array'><tr><th>a</th><th>b</th></tr><tr><td>1</td><td>foo</td></tr></table></li>\n</ul>\n",
+                                       /* Uon */               
"@(\n\t@((a=1,b=foo))\n)",
+                                       /* UrlEnc */    "0=@(\n\t(a=1,b=foo)\n)"
+                               )
+                       },
+                       {       /* 5 */
+                               new Input(
+                                       "List2dOfBeans-2",
+                                       new List2dOfBeans().init2(),
+                                       2,
+                                       /* Json */              
"[\n\t[\n\t\t{a:1, b:'foo'}\n\t]\n]",
+                                       /* Xml */               
"<array>\n\t<array>\n\t\t<object><a>1</a><b>foo</b></object>\n\t</array>\n</array>\n",
+                                       /* Html */              
"<ul>\n\t<li>\n\t\t<table 
_type='array'><tr><th>a</th><th>b</th></tr><tr><td>1</td><td>foo</td></tr></table>\n\t</li>\n</ul>\n",
+                                       /* Uon */               
"@(\n\t@(\n\t\t(a=1,b=foo)\n\t)\n)",
+                                       /* UrlEnc */    
"0=@(\n\t(\n\t\ta=1,\n\t\tb=foo\n\t)\n)"
+                               )
+                       },
+                       {       /* 6 */
+                               new Input(
+                                       "List2dOfBeans-3",
+                                       new List2dOfBeans().init2(),
+                                       3,
+                                       /* Json */              
"[\n\t[\n\t\t{\n\t\t\ta: 1,\n\t\t\tb: 'foo'\n\t\t}\n\t]\n]",
+                                       /* Xml */               
"<array>\n\t<array>\n\t\t<object>\n\t\t\t<a>1</a>\n\t\t\t<b>foo</b>\n\t\t</object>\n\t</array>\n</array>\n",
+                                       /* Html */              
"<ul>\n\t<li>\n\t\t<table 
_type='array'>\n\t\t\t<tr><th>a</th><th>b</th></tr>\n\t\t\t<tr><td>1</td><td>foo</td></tr>\n\t\t</table>\n\t</li>\n</ul>\n",
+                                       /* Uon */               
"@(\n\t@(\n\t\t(\n\t\t\ta=1,\n\t\t\tb=foo\n\t\t)\n\t)\n)",
+                                       /* UrlEnc */    
"0=@(\n\t(\n\t\ta=1,\n\t\tb=foo\n\t)\n)"
+                               )
+                       },
+                       {       /* 7 */
+                               new Input(
+                                       "Map1dOfBeans-0",
+                                       new Map1dOfBeans().init1(),
+                                       0,
+                                       /* Json */              "{a:{a:1, 
b:'foo'}}",
+                                       /* Xml */               
"<object><a><a>1</a><b>foo</b></a></object>\n",
+                                       /* Html */              
"<table><tr><td>a</td><td><table><tr><td>a</td><td>1</td></tr><tr><td>b</td><td>foo</td></tr></table></td></tr></table>\n",
+                                       /* Uon */               
"(a=(a=1,b=foo))",
+                                       /* UrlEnc */    "a=(a=1,b=foo)"
+                               )
+                       },
+                       {       /* 8 */
+                               new Input(
+                                       "Map1dOfBeans-1",
+                                       new Map1dOfBeans().init1(),
+                                       1,
+                                       /* Json */              "{\n\ta: {a:1, 
b:'foo'}\n}",
+                                       /* Xml */               
"<object>\n\t<a><a>1</a><b>foo</b></a>\n</object>\n",
+                                       /* Html */              
"<table>\n\t<tr><td>a</td><td><table><tr><td>a</td><td>1</td></tr><tr><td>b</td><td>foo</td></tr></table></td></tr>\n</table>\n",
+                                       /* Uon */               
"(\n\ta=(a=1,b=foo)\n)",
+                                       /* UrlEnc */    
"a=(\n\ta=1,\n\tb=foo\n)"
+                               )
+                       },
+                       {       /* 9 */
+                               new Input(
+                                       "Map1dOfBeans-2",
+                                       new Map1dOfBeans().init1(),
+                                       2,
+                                       /* Json */              "{\n\ta: 
{\n\t\ta: 1,\n\t\tb: 'foo'\n\t}\n}",
+                                       /* Xml */               
"<object>\n\t<a>\n\t\t<a>1</a>\n\t\t<b>foo</b>\n\t</a>\n</object>\n",
+                                       /* Html */              
"<table>\n\t<tr>\n\t\t<td>a</td>\n\t\t<td><table><tr><td>a</td><td>1</td></tr><tr><td>b</td><td>foo</td></tr></table></td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               
"(\n\ta=(\n\t\ta=1,\n\t\tb=foo\n\t)\n)",
+                                       /* UrlEnc */    
"a=(\n\ta=1,\n\tb=foo\n)"
+                               )
+                       },
+                       {       /* 10 */
+                               new Input(
+                                       "Map2dOfBeans-0",
+                                       new Map2dOfBeans().init2(),
+                                       0,
+                                       /* Json */              "{b:{a:{a:1, 
b:'foo'}}}",
+                                       /* Xml */               
"<object><b><a><a>1</a><b>foo</b></a></b></object>\n",
+                                       /* Html */              
"<table><tr><td>b</td><td><table><tr><td>a</td><td><table><tr><td>a</td><td>1</td></tr><tr><td>b</td><td>foo</td></tr></table></td></tr></table></td></tr></table>\n",
+                                       /* Uon */               
"(b=(a=(a=1,b=foo)))",
+                                       /* UrlEnc */    "b=(a=(a=1,b=foo))"
+                               )
+                       },
+                       {       /* 11 */
+                               new Input(
+                                       "Map2dOfBeans-1",
+                                       new Map2dOfBeans().init2(),
+                                       1,
+                                       /* Json */              "{\n\tb: 
{a:{a:1, b:'foo'}}\n}",
+                                       /* Xml */               
"<object>\n\t<b><a><a>1</a><b>foo</b></a></b>\n</object>\n",
+                                       /* Html */              
"<table>\n\t<tr><td>b</td><td><table><tr><td>a</td><td><table><tr><td>a</td><td>1</td></tr><tr><td>b</td><td>foo</td></tr></table></td></tr></table></td></tr>\n</table>\n",
+                                       /* Uon */               
"(\n\tb=(a=(a=1,b=foo))\n)",
+                                       /* UrlEnc */    
"b=(\n\ta=(a=1,b=foo)\n)"
+                               )
+                       },
+                       {       /* 12 */
+                               new Input(
+                                       "Map2dOfBeans-2",
+                                       new Map2dOfBeans().init2(),
+                                       2,
+                                       /* Json */              "{\n\tb: 
{\n\t\ta: {a:1, b:'foo'}\n\t}\n}",
+                                       /* Xml */               
"<object>\n\t<b>\n\t\t<a><a>1</a><b>foo</b></a>\n\t</b>\n</object>\n",
+                                       /* Html */              
"<table>\n\t<tr>\n\t\t<td>b</td>\n\t\t<td><table><tr><td>a</td><td><table><tr><td>a</td><td>1</td></tr><tr><td>b</td><td>foo</td></tr></table></td></tr></table></td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               
"(\n\tb=(\n\t\ta=(a=1,b=foo)\n\t)\n)",
+                                       /* UrlEnc */    
"b=(\n\ta=(\n\t\ta=1,\n\t\tb=foo\n\t)\n)"
+                               )
+                       },
+                       {       /* 13 */
+                               new Input(
+                                       "Map2dOfBeans-3",
+                                       new Map2dOfBeans().init2(),
+                                       3,
+                                       /* Json */              "{\n\tb: 
{\n\t\ta: {\n\t\t\ta: 1,\n\t\t\tb: 'foo'\n\t\t}\n\t}\n}",
+                                       /* Xml */               
"<object>\n\t<b>\n\t\t<a>\n\t\t\t<a>1</a>\n\t\t\t<b>foo</b>\n\t\t</a>\n\t</b>\n</object>\n",
+                                       /* Html */              
"<table>\n\t<tr>\n\t\t<td>b</td>\n\t\t<td>\n\t\t\t<table><tr><td>a</td><td><table><tr><td>a</td><td>1</td></tr><tr><td>b</td><td>foo</td></tr></table></td></tr></table>\n\t\t</td>\n\t</tr>\n</table>\n",
+                                       /* Uon */               
"(\n\tb=(\n\t\ta=(\n\t\t\ta=1,\n\t\t\tb=foo\n\t\t)\n\t)\n)",
+                                       /* UrlEnc */    
"b=(\n\ta=(\n\t\ta=1,\n\t\tb=foo\n\t)\n)"
+                               )
+                       },
+               });
+       }
+       
+       Input input;
+       
+       public MaxIndentTest(Input input) {
+               this.input = input;
+       }
+       
+       static class Input {
+               String label;
+               Object in;
+               int maxDepth;
+               String json, xml, html, uon, urlEnc;
+               
+               Input(String label, Object in, int maxDepth, String json, 
String xml, String html, String uon, String urlEnc) {
+                       this.label = label;
+                       this.in = in;
+                       this.maxDepth = maxDepth;
+                       this.json = json;
+                       this.xml = xml;
+                       this.html = html;
+                       this.uon = uon;
+                       this.urlEnc = urlEnc;
+               }
+       }
+       
+       public static class List1dOfBeans extends LinkedList<ABean> {
+               public List1dOfBeans init1() {
+                       add(new ABean().init());
+                       return this;
+               }
+       }
+       
+       public static class List2dOfBeans extends LinkedList<List1dOfBeans> {
+               public List2dOfBeans init2() {
+                       add(new List1dOfBeans().init1());
+                       return this;
+               }
+       }
+
+       public static class Map1dOfBeans extends LinkedHashMap<String,ABean> {
+               public Map1dOfBeans init1() {
+                       put("a", new ABean().init());
+                       return this;
+               }
+       }
+       
+       public static class Map2dOfBeans extends 
LinkedHashMap<String,Map1dOfBeans> {
+               public Map2dOfBeans init2() {
+                       put("b", new Map1dOfBeans().init1());
+                       return this;
+               }
+       }
+
+       @Test
+       public void a1_serializeJson() throws Exception {
+               WriterSerializer s = 
JsonSerializer.DEFAULT_LAX_READABLE.builder().maxIndent(input.maxDepth).build();
+               testSerialize("json", s, input.json);
+       }
+       
+       @Test
+       public void b11_serializeXml() throws Exception {
+               WriterSerializer s = 
XmlSerializer.DEFAULT_SQ_READABLE.builder().maxIndent(input.maxDepth).build();
+               testSerialize("xml", s, input.xml);
+       }
+       
+       @Test
+       public void c11_serializeHtml() throws Exception {
+               WriterSerializer s = 
HtmlSerializer.DEFAULT_SQ_READABLE.builder().maxIndent(input.maxDepth).build();
+               testSerialize("html", s, input.html);
+       }
+       
+       @Test
+       public void d11_serializeUon() throws Exception {
+               WriterSerializer s = 
UonSerializer.DEFAULT_READABLE.builder().maxIndent(input.maxDepth).build();
+               testSerialize("uon", s, input.uon);
+       }
+       
+       @Test
+       public void e11_serializeUrlEncoding() throws Exception {
+               WriterSerializer s = 
UrlEncodingSerializer.DEFAULT_READABLE.builder().maxIndent(input.maxDepth).build();
+               testSerialize("urlEncoding", s, input.urlEnc);
+       }
+       
+       private void testSerialize(String testName, Serializer s, String 
expected) throws Exception {
+               try {
+                       String r = s.isWriterSerializer() ? 
((WriterSerializer)s).serialize(input.in) : 
((OutputStreamSerializer)s).serializeToHex(input.in);
+                       
+                       // Specifying "xxx" in the expected results will spit 
out what we should populate the field with.
+                       if (expected.equals("xxx")) {
+                               System.out.println(input.label + "/" + testName 
+ "=\n" + r.replaceAll("\n", "\\\\n").replaceAll("\t", "\\\\t")); // NOT DEBUG
+                               System.out.println(r);
+                               return;
+                       }
+                       
+                       TestUtils.assertEquals(expected, r, "{0}/{1} 
parse-normal failed", input.label, testName);
+                       
+               } catch (AssertionError e) {
+                       throw e;
+               } catch (Exception e) {
+                       e.printStackTrace();
+                       throw new AssertionError(input.label + "/" + testName + 
" failed.  exception=" + e.getLocalizedMessage());
+               }
+       }
+       
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
----------------------------------------------------------------------
diff --git 
a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
 
b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
index 7bc4163..4d34568 100644
--- 
a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
+++ 
b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
@@ -182,7 +182,7 @@ public class ConfigFileInterfaceTest {
        public void testBeanList() throws Exception {
                proxy.setBeanList(Arrays.asList(new ABean().init()));
                assertObjectEquals("[{a:1,b:'foo'}]", proxy.getBeanList());
-               assertEquals("\n[\n\t{a:1,b:'foo'}\n]", cf.get("A", 
"beanList"));
+               assertEquals("[{a:1,b:'foo'}]", cf.get("A", "beanList"));
                assertType(ABean.class, proxy.getBeanList().get(0));
        }
 
@@ -248,7 +248,7 @@ public class ConfigFileInterfaceTest {
        public void testTypedBeanList() throws Exception {
                proxy.setTypedBeanList(Arrays.asList((TypedBean)new 
TypedBeanImpl().init()));
                assertObjectEquals("[{_type:'TypedBeanImpl',a:1,b:'foo'}]", 
proxy.getTypedBeanList());
-               assertEquals("\n[\n\t{_type:'TypedBeanImpl',a:1,b:'foo'}\n]", 
cf.get("A", "typedBeanList"));
+               assertEquals("[{_type:'TypedBeanImpl',a:1,b:'foo'}]", 
cf.get("A", "typedBeanList"));
                assertType(TypedBeanImpl.class, 
proxy.getTypedBeanList().get(0));
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java 
b/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java
index 098e9df..c09fd02 100644
--- a/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/csv/CsvSerializerBuilder.java
@@ -85,6 +85,12 @@ public class CsvSerializerBuilder extends SerializerBuilder {
        }
 
        @Override /* SerializerBuilder */
+       public CsvSerializerBuilder maxIndent(int value) {
+               super.maxIndent(value);
+               return this;
+       }
+
+       @Override /* SerializerBuilder */
        public CsvSerializerBuilder addBeanTypeProperties(boolean value) {
                super.addBeanTypeProperties(value);
                return this;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/dto/html5/Input.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/dto/html5/Input.java 
b/juneau-core/src/main/java/org/apache/juneau/dto/html5/Input.java
index 53c8615..37f4dbf 100644
--- a/juneau-core/src/main/java/org/apache/juneau/dto/html5/Input.java
+++ b/juneau-core/src/main/java/org/apache/juneau/dto/html5/Input.java
@@ -315,6 +315,19 @@ public class Input extends HtmlElementVoid {
                return this;
        }
 
+
+       /**
+        * <a class="doclink" 
href="https://www.w3.org/TR/html5/forms.html#attr-input-readonly";>readonly</a> 
attribute.
+        * Whether to allow the value to be edited by the user.
+        * @param readonly If <jk>true</jk>, adds 
<code>readonly="readonly"</code>.
+        * @return This object (for method chaining).
+        */
+       public final Input readonly(boolean readonly) {
+               if (readonly)
+                       readonly("readonly");
+               return this;
+       }
+
        /**
         * <a class="doclink" 
href="https://www.w3.org/TR/html5/forms.html#attr-input-readonly";>required</a> 
attribute.
         * Whether the control is required for form submission.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java
index 65c980a..694a0a6 100644
--- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializer.java
@@ -78,14 +78,14 @@ public class HtmlDocSerializer extends 
HtmlStrippedDocSerializer {
                HtmlWriter w = s.getWriter();
                HtmlDocTemplate t = s.getTemplate();
 
-               w.sTag("html").nl();
-               w.sTag(1, "head").nl();
+               w.sTag("html").nl(0);
+               w.sTag(1, "head").nl(1);
                t.head(s, w, this, o);
-               w.eTag(1, "head").nl();
-               w.sTag(1, "body").nl();
+               w.eTag(1, "head").nl(1);
+               w.sTag(1, "body").nl(1);
                t.body(s, w, this, o);
-               w.eTag(1, "body").nl();
-               w.eTag("html").nl();
+               w.eTag(1, "body").nl(1);
+               w.eTag("html").nl(0);
        }
 
        /**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java
 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java
index 15fff6b..88874a3 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocSerializerSession.java
@@ -212,6 +212,6 @@ public final class HtmlDocSerializerSession extends 
HtmlSerializerSession {
                Object output = getOutput();
                if (output instanceof HtmlWriter)
                        return (HtmlWriter)output;
-               return new HtmlWriter(super.getWriter(), isUseWhitespace(), 
isTrimStrings(), getQuoteChar(), getUriResolver());
+               return new HtmlWriter(super.getWriter(), isUseWhitespace(), 
getMaxIndent(), isTrimStrings(), getQuoteChar(), getUriResolver());
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java
index 05f9705..1853f00 100644
--- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java
+++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlDocTemplateBasic.java
@@ -26,9 +26,9 @@ public class HtmlDocTemplateBasic implements HtmlDocTemplate {
        @Override /* HtmlDocTemplate */
        public void head(HtmlDocSerializerSession session, HtmlWriter w, 
HtmlDocSerializer s, Object o) throws Exception {
                if (hasCss(session)) {
-                       w.oTag(1, "style").attr("type", 
"text/css").appendln(">").nl();
+                       w.oTag(1, "style").attr("type", 
"text/css").appendln(">").nl(1);
                        css(session, w, s, o);
-                       w.eTag(1, "style").nl();
+                       w.ie(1).eTag("style").nl(1);
                }
        }
 
@@ -52,35 +52,35 @@ public class HtmlDocTemplateBasic implements 
HtmlDocTemplate {
        public void body(HtmlDocSerializerSession session, HtmlWriter w, 
HtmlDocSerializer s, Object o) throws Exception {
 
                if (hasHeader(session)) {
-                       w.sTag(1, "header").nl();
+                       w.sTag(1, "header").nl(1);
                        header(session, w, s, o);
-                       w.eTag(1, "header").nl();
+                       w.ie(1).eTag("header").nl(1);
                }
 
                if (hasNav(session)) {
-                       w.sTag(1, "nav").nl();
+                       w.sTag(1, "nav").nl(1);
                        nav(session, w, s, o);
-                       w.eTag(1, "nav").nl();
+                       w.ie(1).eTag("nav").nl(1);
                }
 
-               w.sTag(1, "section").nl();
+               w.sTag(1, "section").nl(1);
 
-               w.sTag(2, "article").nl();
+               w.sTag(2, "article").nl(2);
                article(session, w, s, o);
-               w.eTag(2, "article").nl();
+               w.ie(2).eTag("article").nl(2);
 
                if (hasAside(session)) {
-                       w.sTag(2, "aside").nl();
+                       w.sTag(2, "aside").nl(2);
                        aside(session, w, s, o);
-                       w.eTag(2, "aside").nl();
+                       w.ie(2).eTag("aside").nl(2);
                }
 
-               w.eTag(1, "section").nl();
+               w.ie(1).eTag("section").nl(1);
 
                if (hasFooter(session)) {
-                       w.sTag(1, "footer").nl();
+                       w.sTag(1, "footer").nl(1);
                        footer(session, w, s, o);
-                       w.eTag(1, "footer").nl();
+                       w.ie(1).eTag("footer").nl(1);
                }
        }
 
@@ -90,17 +90,17 @@ public class HtmlDocTemplateBasic implements 
HtmlDocTemplate {
                String header = session.getHeader();
                if (header != null) {
                        if (exists(header))
-                               w.append(header).nl();
+                               w.append(2, header).nl(2);
                } else {
                        String title = session.getTitle();
                        String description = session.getDescription();
                        String branding = session.getBranding();
                        if (exists(title))
-                               w.oTag(1, "h3").attr("class", 
"title").append('>').text(title).eTag("h3").nl();
+                               w.oTag(3, "h3").attr("class", 
"title").append('>').text(title).eTag("h3").nl(3);
                        if (exists(description))
-                               w.oTag(1, "h5").attr("class", 
"description").append('>').text(description).eTag("h5").nl();
+                               w.oTag(3, "h5").attr("class", 
"description").append('>').text(description).eTag("h5").nl(3);
                        if (exists(branding))
-                               w.append(branding).nl();
+                               w.append(3, branding).nl(3);
                }
        }
 
@@ -110,14 +110,14 @@ public class HtmlDocTemplateBasic implements 
HtmlDocTemplate {
                String nav = session.getNav();
                if (nav != null) {
                        if (exists(nav))
-                               w.append(nav).nl();
+                               w.append(2, nav).nl(2);
                } else {
                        Map<String,String> htmlLinks = session.getLinks();
                        boolean first = true;
                        if (htmlLinks != null) {
                                for (Map.Entry<String,String> e : 
htmlLinks.entrySet()) {
                                        if (! first)
-                                               w.append(3, " - ").nl();
+                                               w.append(3, " - ").nl(3);
                                        first = false;
                                        w.oTag("a").attr("class", 
"link").attr("href", session.resolveUri(e.getValue()), 
true).cTag().text(e.getKey(), true).eTag("a");
                                }
@@ -136,21 +136,21 @@ public class HtmlDocTemplateBasic implements 
HtmlDocTemplate {
        public void article(HtmlDocSerializerSession session, HtmlWriter w, 
HtmlDocSerializer s, Object o) throws Exception {
                // To allow for page formatting using CSS, we encapsulate the 
data inside two div tags:
                // <div class='outerdata'><div class='data' 
id='data'>...</div></div>
-               w.oTag(3, "div").attr("class","outerdata").append('>').nl();
-               w.oTag(4, "div").attr("class","data").attr("id", 
"data").append('>').nl();
+               w.oTag(3, "div").attr("class","outerdata").append('>').nl(3);
+               w.oTag(4, "div").attr("class","data").attr("id", 
"data").append('>').nl(4);
 
                if (o == null) {
-                       w.append("<null/>").nl();
+                       w.append(5, "<null/>").nl(5);
                } else if (ObjectUtils.isEmpty(o)){
                        String m = session.getNoResultsMessage();
                        if (exists(m))
-                               w.append(m).nl();
+                               w.append(5, m).nl(5);
                } else {
                        s.parentSerialize(session, o);
                }
 
-               w.eTag(4, "div").nl();
-               w.eTag(3, "div").nl();
+               w.ie(4).eTag("div").nl(4);
+               w.ie(4).eTag("div").nl(3);
        }
 
        @Override /* HtmlDocTemplate */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java
index 934bd77..8d9d087 100644
--- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java
@@ -299,7 +299,7 @@ public class HtmlSerializer extends XmlSerializer {
                                cr = CR_SIMPLE;
 
                        } else if (sType.isMap() || (wType != null && 
wType.isMap())) {
-                               out.nlIf(! isRoot);
+                               out.nlIf(! isRoot, indent+1);
                                if (o instanceof BeanMap)
                                        serializeBeanMap(session, out, 
(BeanMap)o, eType, pMeta);
                                else
@@ -315,12 +315,12 @@ public class HtmlSerializer extends XmlSerializer {
                                        out.oTag("a").attrUri("href", 
urlProp).append('>').text(nameProp).eTag("a");
                                        cr = CR_SIMPLE;
                                } else {
-                                       out.nlIf(! isRoot);
+                                       out.nlIf(! isRoot, indent+2);
                                        serializeBeanMap(session, out, m, 
eType, pMeta);
                                }
 
                        } else if (sType.isCollection() || sType.isArray() || 
(wType != null && wType.isCollection())) {
-                               out.nlIf(! isRoot);
+                               out.nlIf(! isRoot, indent+1);
                                serializeCollection(session, out, o, sType, 
eType, name, pMeta);
 
                        } else if (session.isUri(sType, pMeta, o)) {
@@ -365,12 +365,12 @@ public class HtmlSerializer extends XmlSerializer {
                if (typeName != null && ppMeta != null && ppMeta.getClassMeta() 
!= aType)
                        out.attr(session.getBeanTypePropertyName(sType), 
typeName);
 
-               out.appendln(">");
+               out.append(">").nl(i+1);
                if (session.isAddKeyValueTableHeaders() && ! 
(aType.getExtendedMeta(HtmlClassMeta.class).isNoTableHeaders() || (ppMeta != 
null && 
ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isNoTableHeaders()))) {
-                       out.sTag(i+1, "tr").nl();
-                       out.sTag(i+2, "th").append("key").eTag("th").nl();
-                       out.sTag(i+2, "th").append("value").eTag("th").nl();
-                       out.eTag(i+1, "tr").nl();
+                       out.sTag(i+1, "tr").nl(i+2);
+                       out.sTag(i+2, "th").append("key").eTag("th").nl(i+3);
+                       out.sTag(i+2, "th").append("value").eTag("th").nl(i+3);
+                       out.ie(i+1).eTag("tr").nl(i+2);
                }
                for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) {
 
@@ -384,20 +384,20 @@ public class HtmlSerializer extends XmlSerializer {
                                session.onError(t, "Could not call getValue() 
on property ''{0}'', {1}", e.getKey(), t.getLocalizedMessage());
                        }
 
-                       out.sTag(i+1, "tr").nl();
+                       out.sTag(i+1, "tr").nl(i+2);
                        out.sTag(i+2, "td");
                        ContentResult cr = serializeAnything(session, out, key, 
keyType, null, 2, null, false);
                        if (cr == CR_NORMAL)
                                out.i(i+2);
-                       out.eTag("td").nl();
+                       out.eTag("td").nl(i+2);
                        out.sTag(i+2, "td");
                        cr = serializeAnything(session, out, value, valueType, 
(key == null ? "_x0000_" : session.toString(key)), 2, null, false);
                        if (cr == CR_NORMAL)
-                               out.i(i+2);
-                       out.eTag("td").nl();
-                       out.eTag(i+1, "tr").nl();
+                               out.ie(i+2);
+                       out.eTag("td").nl(i+2);
+                       out.ie(i+1).eTag("tr").nl(i+1);
                }
-               out.eTag(i, "table").nl();
+               out.ie(i).eTag("table").nl(i);
        }
 
        @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -410,12 +410,12 @@ public class HtmlSerializer extends XmlSerializer {
                if (typeName != null && eType != m.getClassMeta())
                        
out.attr(session.getBeanTypePropertyName(m.getClassMeta()), typeName);
 
-               out.append('>').nl();
+               out.append('>').nl(i);
                if (session.isAddKeyValueTableHeaders() && ! 
(m.getClassMeta().getExtendedMeta(HtmlClassMeta.class).isNoTableHeaders() || 
(ppMeta != null && 
ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isNoTableHeaders()))) {
-                       out.sTag(i+1, "tr").nl();
-                       out.sTag(i+2, "th").append("key").eTag("th").nl();
-                       out.sTag(i+2, "th").append("value").eTag("th").nl();
-                       out.eTag(i+1, "tr").nl();
+                       out.sTag(i+1, "tr").nl(i+1);
+                       out.sTag(i+2, "th").append("key").eTag("th").nl(i+2);
+                       out.sTag(i+2, "th").append("value").eTag("th").nl(i+2);
+                       out.ie(i+1).eTag("tr").nl(i+1);
                }
 
                for (BeanPropertyValue p : m.getValues(session.isTrimNulls())) {
@@ -436,8 +436,8 @@ public class HtmlSerializer extends XmlSerializer {
                        if (session.canIgnoreValue(cMeta, key, value))
                                continue;
 
-                       out.sTag(i+1, "tr").nl();
-                       out.sTag(i+2, "td").text(key).eTag("td").nl();
+                       out.sTag(i+1, "tr").nl(i+1);
+                       out.sTag(i+2, "td").text(key).eTag("td").nl(i+2);
                        out.oTag(i+2, "td");
                        String style = render == null ? null : 
render.getStyle(session, value);
                        if (style != null)
@@ -460,10 +460,10 @@ public class HtmlSerializer extends XmlSerializer {
                                e.printStackTrace();
                                session.onBeanGetterException(pMeta, e);
                        }
-                       out.eTag("td").nl();
-                       out.eTag(i+1, "tr").nl();
+                       out.eTag("td").nl(i+2);
+                       out.ie(i+1).eTag("tr").nl(i+1);
                }
-               out.eTag(i, "table").nl();
+               out.ie(i).eTag("table").nl(i);
        }
 
        @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -499,14 +499,14 @@ public class HtmlSerializer extends XmlSerializer {
 
                if (th != null) {
 
-                       out.oTag(i, "table").attr(btpn, type2).append('>').nl();
-                       out.sTag(i+1, "tr").nl();
+                       out.oTag(i, "table").attr(btpn, 
type2).append('>').nl(i+1);
+                       out.sTag(i+1, "tr").nl(i+2);
                        for (Object key : th) {
                                out.sTag(i+2, "th");
                                out.text(session.convertToType(key, 
String.class));
-                               out.eTag("th").nl();
+                               out.eTag("th").nl(i+2);
                        }
-                       out.eTag(i+1, "tr").nl();
+                       out.ie(i+1).eTag("tr").nl(i+1);
 
                        for (Object o : c) {
                                ClassMeta<?> cm = 
session.getClassMetaForObject(o);
@@ -523,7 +523,7 @@ public class HtmlSerializer extends XmlSerializer {
 
                                if (typeName != null && eType.getElementType() 
!= cm)
                                        out.attr(typeProperty, typeName);
-                               out.cTag().nl();
+                               out.cTag().nl(i+2);
 
                                if (cm == null) {
                                        serializeAnything(session, out, o, 
null, null, 1, null, false);
@@ -536,7 +536,7 @@ public class HtmlSerializer extends XmlSerializer {
                                                ContentResult cr = 
serializeAnything(session, out, m2.get(k), eType.getElementType(), 
session.toString(k), 2, null, false);
                                                if (cr == CR_NORMAL)
                                                        out.i(i+2);
-                                               out.eTag("td").nl();
+                                               out.eTag("td").nl(i+2);
                                        }
                                } else {
                                        BeanMap m2 = null;
@@ -570,26 +570,26 @@ public class HtmlSerializer extends XmlSerializer {
                                                        out.i(i+2);
                                                if (link != null)
                                                        out.eTag("a");
-                                               out.eTag("td").nl();
+                                               out.eTag("td").nl(i+2);
                                        }
                                }
-                               out.eTag(i+1, "tr").nl();
+                               out.ie(i+1).eTag("tr").nl(i+1);
                        }
-                       out.eTag(i, "table").nl();
+                       out.ie(i).eTag("table").nl(i);
 
                } else {
                        out.oTag(i, "ul");
                        if (! type2.equals("array"))
                                out.attr(btpn, type2);
-                       out.append('>').nl();
+                       out.append('>').nl(i+1);
                        for (Object o : c) {
                                out.sTag(i+1, "li");
                                ContentResult cr = serializeAnything(session, 
out, o, eType.getElementType(), name, 1, null, false);
                                if (cr == CR_NORMAL)
-                                       out.i(i+1);
-                               out.eTag("li").nl();
+                                       out.ie(i+1);
+                               out.eTag("li").nl(i+1);
                        }
-                       out.eTag(i, "ul").nl();
+                       out.ie(i).eTag("ul").nl(i);
                }
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
index 8b28821..6f528dd 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializerSession.java
@@ -90,7 +90,7 @@ public class HtmlSerializerSession extends 
XmlSerializerSession {
                Object output = getOutput();
                if (output instanceof HtmlWriter)
                        return (HtmlWriter)output;
-               return new HtmlWriter(super.getWriter(), isUseWhitespace(), 
isTrimStrings(), getQuoteChar(), getUriResolver());
+               return new HtmlWriter(super.getWriter(), isUseWhitespace(), 
getMaxIndent(), isTrimStrings(), getQuoteChar(), getUriResolver());
        }
 
        /**

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/html/HtmlStrippedDocSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/html/HtmlStrippedDocSerializer.java
 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlStrippedDocSerializer.java
index 29f4c78..815e2ef 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/html/HtmlStrippedDocSerializer.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlStrippedDocSerializer.java
@@ -55,7 +55,7 @@ public class HtmlStrippedDocSerializer extends HtmlSerializer 
{
                if (o == null
                        || (o instanceof Collection && 
((Collection<?>)o).size() == 0)
                        || (o.getClass().isArray() && Array.getLength(o) == 0))
-                       w.sTag(1, "p").append("No Results").eTag("p").nl();
+                       w.sTag(1, "p").append("No Results").eTag("p").nl(1);
                else
                        super.doSerialize(s, o);
        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java 
b/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java
index 192a73b..fbbd225 100644
--- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java
+++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlWriter.java
@@ -29,12 +29,13 @@ public class HtmlWriter extends XmlWriter {
         *
         * @param out The writer being wrapped.
         * @param useWhitespace If <jk>true</jk>, tabs will be used in output.
+        * @param maxIndent The maximum indentation level.
         * @param trimStrings If <jk>true</jk>, strings should be trimmed 
before they're serialized.
         * @param quoteChar The quote character to use (i.e. <js>'\''</js> or 
<js>'"'</js>)
         * @param uriResolver The URI resolver for resolving URIs to absolute 
or root-relative form.
         */
-       public HtmlWriter(Writer out, boolean useWhitespace, boolean 
trimStrings, char quoteChar, UriResolver uriResolver) {
-               super(out, useWhitespace, trimStrings, quoteChar, uriResolver, 
false, null);
+       public HtmlWriter(Writer out, boolean useWhitespace, int maxIndent, 
boolean trimStrings, char quoteChar, UriResolver uriResolver) {
+               super(out, useWhitespace, maxIndent, trimStrings, quoteChar, 
uriResolver, false, null);
        }
 
        /**
@@ -283,6 +284,13 @@ public class HtmlWriter extends XmlWriter {
        }
 
        @Override /* SerializerWriter */
+       public HtmlWriter cre(int depth) throws IOException {
+               if (depth > 0)
+                       super.cre(depth);
+               return this;
+       }
+
+       @Override /* SerializerWriter */
        public HtmlWriter appendln(int indent, String text) throws IOException {
                super.appendln(indent, text);
                return this;
@@ -325,8 +333,8 @@ public class HtmlWriter extends XmlWriter {
        }
 
        @Override /* SerializerWriter */
-       public HtmlWriter nl() throws IOException {
-               super.nl();
+       public HtmlWriter nl(int indent) throws IOException {
+               super.nl(indent);
                return this;
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java 
b/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java
index f122b36..fcac7ec 100644
--- a/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java
+++ b/juneau-core/src/main/java/org/apache/juneau/html/SimpleHtmlWriter.java
@@ -28,7 +28,7 @@ public class SimpleHtmlWriter extends HtmlWriter {
         * Constructor.
         */
        public SimpleHtmlWriter() {
-               super(new StringWriter(), true, false, '\'', null);
+               super(new StringWriter(), true, 100, false, '\'', null);
        }
 
        @Override /* Object */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java 
b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
index 172d3d9..12c7dfc 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
@@ -59,15 +59,16 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @param value The new value.
         * @param serializer The serializer to use for serializing the object.
         *      If <jk>null</jk>, then uses the predefined serializer on the 
config file.
-        * @param encoded
+        * @param encoded If <jk>true</jk>, then encode the value using the 
encoder associated with this config file.
+        * @param newline If <jk>true</jk>, then put serialized output on a 
separate line from the key.
         * @return The previous value, or <jk>null</jk> if the section or key 
did not previously exist.
         * @throws SerializeException If the object value could not be 
converted to a JSON string for some reason.
         * @throws UnsupportedOperationException If config file is read only.
         */
-       public abstract String put(String sectionName, String sectionKey, 
Object value, Serializer serializer, boolean encoded) throws SerializeException;
+       public abstract String put(String sectionName, String sectionKey, 
Object value, Serializer serializer, boolean encoded, boolean newline) throws 
SerializeException;
 
        /**
-        * Identical to {@link #put(String, String, Object, Serializer, 
boolean)} except used when the value is a simple string
+        * Identical to {@link #put(String, String, Object, Serializer, 
boolean, boolean)} except used when the value is a simple string
         * to avoid having to catch a {@link SerializeException}.
         *
         * @param sectionName The section name.  Must not be <jk>null</jk>.
@@ -196,10 +197,11 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @param o The object to serialize.
         * @param serializer The serializer to use for serializing the object.
         *      If <jk>null</jk>, then uses the predefined serializer on the 
config file.
+        * @param newline If <jk>true</jk>, add a newline at the beginning of 
the value.
         * @return The serialized object.
         * @throws SerializeException
         */
-       protected abstract String serialize(Object o, Serializer serializer) 
throws SerializeException;
+       protected abstract String serialize(Object o, Serializer serializer, 
boolean newline) throws SerializeException;
 
        /**
         * Converts the specified string to an object of the specified type.
@@ -645,7 +647,7 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @throws UnsupportedOperationException If config file is read only.
         */
        public final String put(String key, Object value) throws 
SerializeException {
-               return put(key, value, null, isEncoded(key));
+               return put(key, value, null, isEncoded(key), false);
        }
 
        /**
@@ -660,7 +662,7 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @throws UnsupportedOperationException If config file is read only.
         */
        public final String put(String key, Object value, Serializer 
serializer) throws SerializeException {
-               return put(key, value, serializer, isEncoded(key));
+               return put(key, value, serializer, isEncoded(key), false);
        }
 
        /**
@@ -683,7 +685,7 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @throws UnsupportedOperationException If config file is read only.
         */
        public final String put(String key, Object value, boolean encoded) 
throws SerializeException {
-               return put(key, value, null, encoded);
+               return put(key, value, null, encoded, false);
        }
 
        /**
@@ -694,13 +696,14 @@ public abstract class ConfigFile implements 
Map<String,Section> {
         * @param serializer The serializer to use for serializing the object.
         *      If <jk>null</jk>, then uses the predefined serializer on the 
config file.
         * @param encoded If <jk>true</jk>, value is encoded by the registered 
encoder when the config file is persisted to disk.
+        * @param newline If <jk>true</jk>, a newline is added to the beginning 
of the input.
         * @return The previous value, or <jk>null</jk> if the section or key 
did not previously exist.
         * @throws SerializeException If serializer could not serialize the 
value or if a serializer is not registered with this config file.
         * @throws UnsupportedOperationException If config file is read only.
         */
-       public final String put(String key, Object value, Serializer 
serializer, boolean encoded) throws SerializeException {
+       public final String put(String key, Object value, Serializer 
serializer, boolean encoded, boolean newline) throws SerializeException {
                assertFieldNotNull(key, "key");
-               return put(getSectionName(key), getSectionKey(key), 
serialize(value, serializer), encoded);
+               return put(getSectionName(key), getSectionKey(key), 
serialize(value, serializer, newline), encoded);
        }
 
        /**
@@ -913,7 +916,7 @@ public abstract class ConfigFile implements 
Map<String,Section> {
                                        if (method.equals(rm))
                                                return 
ConfigFile.this.getObject(sectionName, pd.getName(), rm.getGenericReturnType());
                                        if (method.equals(wm))
-                                               return 
ConfigFile.this.put(sectionName, pd.getName(), args[0], null, false);
+                                               return 
ConfigFile.this.put(sectionName, pd.getName(), args[0], null, false, false);
                                }
                                throw new 
UnsupportedOperationException("Unsupported interface method.  method=[ " + 
method + " ]");
                        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java 
b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
index 44353f3..838d663 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
@@ -199,7 +199,7 @@ public final class ConfigFileImpl extends ConfigFile {
 
        @SuppressWarnings("hiding")
        @Override /* ConfigFile */
-       protected String serialize(Object value, Serializer serializer) throws 
SerializeException {
+       protected String serialize(Object value, Serializer serializer, boolean 
newline) throws SerializeException {
                if (value == null)
                        return "";
                if (serializer == null)
@@ -208,27 +208,12 @@ public final class ConfigFileImpl extends ConfigFile {
                if (isSimpleType(c))
                        return value.toString();
 
-               BeanContext bc = serializer.getBeanContext();
-               ClassMeta<?> cm = bc.getClassMetaForObject(value);
                String r = null;
+               if (newline)
+                       r = "\n" + (String)serializer.serialize(value);
+               else
+                       r = (String)serializer.serialize(value);
 
-               // For arrays of bean/collections, we have special formatting.
-               if (cm.isCollectionOrArray()) {
-                       Object v1 = ArrayUtils.getFirst(value);
-                       if (bc.getClassMetaForObject(v1).isMapOrBean()) {
-                               List<Object> l = new ArrayList<Object>();
-                               if (cm.isCollection()) {
-                                       for (Object o : (Collection<?>)value)
-                                               l.add(serializer.serialize(o));
-                               } else {
-                                       for (int i = 0; i < 
Array.getLength(value); i++)
-                                               
l.add(serializer.serialize(Array.get(value, i)));
-                               }
-                               return "\n[\n\t" + StringUtils.join(l, ",\n\t") 
+ "\n]";
-                       }
-               }
-
-               r = (String)serializer.serialize(value);
                if (r.startsWith("'"))
                        return r.substring(1, r.length()-1);
                return r;
@@ -498,10 +483,10 @@ public final class ConfigFileImpl extends ConfigFile {
 
        @SuppressWarnings("hiding")
        @Override /* ConfigFile */
-       public String put(String sectionName, String sectionKey, Object value, 
Serializer serializer, boolean encoded) throws SerializeException {
+       public String put(String sectionName, String sectionKey, Object value, 
Serializer serializer, boolean encoded, boolean newline) throws 
SerializeException {
                assertFieldNotNull(sectionKey, "sectionKey");
                Section s = getSection(sectionName, true);
-               return s.put(sectionKey, serialize(value, serializer), encoded);
+               return s.put(sectionKey, serialize(value, serializer, newline), 
encoded);
        }
 
        @Override /* ConfigFile */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java 
b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
index dd8e2ee..6e5df2b 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
@@ -252,8 +252,8 @@ public final class ConfigFileWrapped extends ConfigFile {
        }
 
        @Override /* ConfigFile */
-       public String put(String sectionName, String sectionKey, Object value, 
Serializer serializer, boolean encoded) throws SerializeException {
-               return cf.put(sectionName, sectionKey, value, serializer, 
encoded);
+       public String put(String sectionName, String sectionKey, Object value, 
Serializer serializer, boolean encoded, boolean newline) throws 
SerializeException {
+               return cf.put(sectionName, sectionKey, value, serializer, 
encoded, newline);
        }
 
        @Override /* ConfigFile */
@@ -277,8 +277,8 @@ public final class ConfigFileWrapped extends ConfigFile {
        }
 
        @Override /* ConfigFile */
-       protected String serialize(Object o, Serializer s) throws 
SerializeException {
-               return cf.serialize(o, s);
+       protected String serialize(Object o, Serializer s, boolean newline) 
throws SerializeException {
+               return cf.serialize(o, s, newline);
        }
 
        @Override /* ConfigFile */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java 
b/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java
index bd2e498..39a4d63 100644
--- a/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/jso/JsoSerializerBuilder.java
@@ -85,6 +85,12 @@ public class JsoSerializerBuilder extends SerializerBuilder {
        }
 
        @Override /* SerializerBuilder */
+       public JsoSerializerBuilder maxIndent(int value) {
+               super.maxIndent(value);
+               return this;
+       }
+
+       @Override /* SerializerBuilder */
        public JsoSerializerBuilder addBeanTypeProperties(boolean value) {
                super.addBeanTypeProperties(value);
                return this;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
index 4ec9298..4f2de4c 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializer.java
@@ -241,7 +241,7 @@ public class JsonSerializer extends WriterSerializer {
 
                String wrapperAttr = 
sType.getExtendedMeta(JsonClassMeta.class).getWrapperAttr();
                if (wrapperAttr != null) {
-                       
out.append('{').cr(session.indent).attr(wrapperAttr).append(':').s();
+                       
out.append('{').cr(session.indent).attr(wrapperAttr).append(':').s(session.indent);
                        session.indent++;
                }
 
@@ -271,7 +271,7 @@ public class JsonSerializer extends WriterSerializer {
 
                if (wrapperAttr != null) {
                        session.indent--;
-                       out.cr(session.indent-1).append('}');
+                       out.cre(session.indent-1).append('}');
                }
 
                if (! isRecursion)
@@ -286,7 +286,7 @@ public class JsonSerializer extends WriterSerializer {
 
                m = session.sort(m);
 
-               int depth = session.getIndent();
+               int i = session.getIndent();
                out.append('{');
 
                Iterator mapEntries = m.entrySet().iterator();
@@ -297,21 +297,21 @@ public class JsonSerializer extends WriterSerializer {
 
                        Object key = session.generalize(e.getKey(), keyType);
 
-                       
out.cr(depth).attr(session.toString(key)).append(':').s();
+                       out.cr(i).attr(session.toString(key)).append(':').s(i);
 
                        serializeAnything(session, out, value, valueType, (key 
== null ? null : session.toString(key)), null);
 
                        if (mapEntries.hasNext())
-                               out.append(',');
+                               out.append(',').smi(i);
                }
 
-               out.cr(depth-1).append('}');
+               out.cre(i-1).append('}');
 
                return out;
        }
 
        private SerializerWriter serializeBeanMap(JsonSerializerSession 
session, JsonWriter out, BeanMap<?> m, String typeName) throws Exception {
-               int depth = session.getIndent();
+               int i = session.getIndent();
                out.append('{');
 
                boolean addComma = false;
@@ -328,15 +328,15 @@ public class JsonSerializer extends WriterSerializer {
                                continue;
 
                        if (addComma)
-                               out.append(',');
+                               out.append(',').smi(i);
 
-                       out.cr(depth).attr(key).append(':').s();
+                       out.cr(i).attr(key).append(':').s(i);
 
                        serializeAnything(session, out, value, cMeta, key, 
pMeta);
 
                        addComma = true;
                }
-               out.cr(depth-1).append('}');
+               out.cre(i-1).append('}');
                return out;
        }
 
@@ -359,9 +359,9 @@ public class JsonSerializer extends WriterSerializer {
                        serializeAnything(session, out, value, elementType, 
"<iterator>", null);
 
                        if (i.hasNext())
-                               out.append(',');
+                               out.append(',').smi(depth);
                }
-               out.cr(depth-1).append(']');
+               out.cre(depth-1).append(']');
                return out;
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java
index 92adbd4..97e0daa 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerBuilder.java
@@ -150,6 +150,12 @@ public class JsonSerializerBuilder extends 
SerializerBuilder {
        }
 
        @Override /* SerializerBuilder */
+       public JsonSerializerBuilder maxIndent(int value) {
+               super.maxIndent(value);
+               return this;
+       }
+
+       @Override /* SerializerBuilder */
        public JsonSerializerBuilder addBeanTypeProperties(boolean value) {
                super.addBeanTypeProperties(value);
                return this;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
index 7cda1e9..b5e7a46 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonSerializerSession.java
@@ -96,6 +96,6 @@ public final class JsonSerializerSession extends 
SerializerSession {
                Object output = getOutput();
                if (output instanceof JsonWriter)
                        return (JsonWriter)output;
-               return new JsonWriter(super.getWriter(), isUseWhitespace(), 
isEscapeSolidus(), getQuoteChar(), isSimpleMode(), isTrimStrings(), 
getUriResolver());
+               return new JsonWriter(super.getWriter(), isUseWhitespace(), 
getMaxIndent(), isEscapeSolidus(), getQuoteChar(), isSimpleMode(), 
isTrimStrings(), getUriResolver());
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/json/JsonWriter.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonWriter.java 
b/juneau-core/src/main/java/org/apache/juneau/json/JsonWriter.java
index 2644123..f6f5eb2 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonWriter.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonWriter.java
@@ -57,14 +57,15 @@ public final class JsonWriter extends SerializerWriter {
         * Constructor.
         * @param out The writer being wrapped.
         * @param useWhitespace If <jk>true</jk>, tabs and spaces will be used 
in output.
+        * @param maxIndent The maximum indentation level.
         * @param escapeSolidus If <jk>true</jk>, forward slashes should be 
escaped in the output.
         * @param quoteChar The quote character to use (i.e. <js>'\''</js> or 
<js>'"'</js>)
         * @param laxMode If <jk>true</jk>, JSON attributes will only be quoted 
when necessary.
         * @param trimStrings If <jk>true</jk>, strings will be trimmed before 
being serialized.
         * @param uriResolver The URI resolver for resolving URIs to absolute 
or root-relative form.
         */
-       protected JsonWriter(Writer out, boolean useWhitespace, boolean 
escapeSolidus, char quoteChar, boolean laxMode, boolean trimStrings, 
UriResolver uriResolver) {
-               super(out, useWhitespace, trimStrings, quoteChar, uriResolver);
+       protected JsonWriter(Writer out, boolean useWhitespace, int maxIndent, 
boolean escapeSolidus, char quoteChar, boolean laxMode, boolean trimStrings, 
UriResolver uriResolver) {
+               super(out, useWhitespace, maxIndent, trimStrings, quoteChar, 
uriResolver);
                this.laxMode = laxMode;
                this.escapeSolidus = escapeSolidus;
                this.ec = escapeSolidus ? encodedChars2 : encodedChars;
@@ -185,6 +186,25 @@ public final class JsonWriter extends SerializerWriter {
        }
 
        @Override /* SerializerWriter */
+       public JsonWriter cre(int depth) throws IOException {
+               super.cre(depth);
+               return this;
+       }
+
+       /**
+        * Performs an indentation only if we're currently past max indentation.
+        *
+        * @param depth The current indentation depth.
+        * @return This object (for method chaining).
+        * @throws IOException
+        */
+       public JsonWriter smi(int depth) throws IOException {
+               if (depth > maxIndent)
+                       super.s();
+               return this;
+       }
+
+       @Override /* SerializerWriter */
        public JsonWriter appendln(int indent, String text) throws IOException {
                super.appendln(indent, text);
                return this;
@@ -214,6 +234,19 @@ public final class JsonWriter extends SerializerWriter {
                return this;
        }
 
+       /**
+        * Adds a space only if the current indentation level is below 
maxIndent.
+        * 
+        * @param indent
+        * @return This object (for method chaining).
+        * @throws IOException
+        */
+       public JsonWriter s(int indent) throws IOException {
+               if (indent <= maxIndent)
+                       super.s();
+               return this;
+       }
+
        @Override /* SerializerWriter */
        public JsonWriter q() throws IOException {
                super.q();
@@ -227,8 +260,8 @@ public final class JsonWriter extends SerializerWriter {
        }
 
        @Override /* SerializerWriter */
-       public JsonWriter nl() throws IOException {
-               super.nl();
+       public JsonWriter nl(int indent) throws IOException {
+               super.nl(indent);
                return this;
        }
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java
 
b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java
index d0e79b7..6eb01fe 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackSerializerBuilder.java
@@ -85,6 +85,12 @@ public class MsgPackSerializerBuilder extends 
SerializerBuilder {
        }
 
        @Override /* SerializerBuilder */
+       public MsgPackSerializerBuilder maxIndent(int value) {
+               super.maxIndent(value);
+               return this;
+       }
+
+       @Override /* SerializerBuilder */
        public MsgPackSerializerBuilder addBeanTypeProperties(boolean value) {
                super.addBeanTypeProperties(value);
                return this;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializerBuilder.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializerBuilder.java
 
b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializerBuilder.java
index 8995da2..222e502 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializerBuilder.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/plaintext/PlainTextSerializerBuilder.java
@@ -85,6 +85,12 @@ public class PlainTextSerializerBuilder extends 
SerializerBuilder {
        }
 
        @Override /* SerializerBuilder */
+       public PlainTextSerializerBuilder maxIndent(int value) {
+               super.maxIndent(value);
+               return this;
+       }
+
+       @Override /* SerializerBuilder */
        public PlainTextSerializerBuilder addBeanTypeProperties(boolean value) {
                super.addBeanTypeProperties(value);
                return this;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java
index 0d3a773..a1ee7c8 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerBuilder.java
@@ -203,6 +203,31 @@ public class SerializerBuilder extends CoreObjectBuilder {
        }
 
        /**
+        * <b>Configuration property:</b>  Maximum indentation.
+        * <p>
+        * <ul>
+        *      <li><b>Name:</b> <js>"Serializer.maxIndent"</js>
+        *      <li><b>Data type:</b> <code>Integer</code>
+        *      <li><b>Default:</b> <code>100</code>
+        *      <li><b>Session-overridable:</b> <jk>true</jk>
+        * </ul>
+        * <p>
+        * Specifies the maximum indentation level in the serialized document.
+        * <p>
+        * <h5 class='section'>Notes:</h5>
+        * <ul>
+        *      <li>This is equivalent to calling 
<code>property(<jsf>SERIALIZER_maxIndent</jsf>, value)</code>.
+        * </ul>
+        *
+        * @param value The new value for this property.
+        * @return This object (for method chaining).
+        * @see SerializerContext#SERIALIZER_maxIndent
+        */
+       public SerializerBuilder maxIndent(int value) {
+               return property(SERIALIZER_maxIndent, value);
+       }
+
+       /**
         * <b>Configuration property:</b>  Add <js>"_type"</js> properties when 
needed.
         * <p>
         * <ul>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
index ac65ab2..da53f8f 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerContext.java
@@ -108,10 +108,28 @@ public class SerializerContext extends BeanContext {
         * </ul>
         * <p>
         * If <jk>true</jk>, whitespace is added to the output to improve 
readability.
+        * <p>
+        * This setting does not apply to the MessagePack serializer.
         */
        public static final String SERIALIZER_useWhitespace = 
"Serializer.useWhitespace";
 
        /**
+        * <b>Configuration property:</b>  Maximum indentation.
+        * <p>
+        * <ul>
+        *      <li><b>Name:</b> <js>"Serializer.maxIndent"</js>
+        *      <li><b>Data type:</b> <code>Integer</code>
+        *      <li><b>Default:</b> <code>100</code>
+        *      <li><b>Session-overridable:</b> <jk>true</jk>
+        * </ul>
+        * <p>
+        * Specifies the maximum indentation level in the serialized document.
+        * <p>
+        * This setting does not apply to the MessagePack or RDF serializers.
+        */
+       public static final String SERIALIZER_maxIndent = 
"Serializer.maxIndent";
+
+       /**
         * <b>Configuration property:</b>  Add <js>"_type"</js> properties when 
needed.
         * <p>
         * <ul>
@@ -138,6 +156,8 @@ public class SerializerContext extends BeanContext {
         * </ul>
         * <p>
         * This is the character used for quoting attributes and values.
+        * <p>
+        * This setting does not apply to the MessagePack or RDF serializers.
         */
        public static final String SERIALIZER_quoteChar = 
"Serializer.quoteChar";
 
@@ -350,7 +370,7 @@ public class SerializerContext extends BeanContext {
        public static final String SERIALIZER_listener = "Serializer.listener";
 
 
-       final int maxDepth, initialDepth;
+       final int maxDepth, initialDepth, maxIndent;
        final boolean
                detectRecursions,
                ignoreRecursions,
@@ -382,6 +402,7 @@ public class SerializerContext extends BeanContext {
                detectRecursions = ps.getProperty(SERIALIZER_detectRecursions, 
boolean.class, false);
                ignoreRecursions = ps.getProperty(SERIALIZER_ignoreRecursions, 
boolean.class, false);
                useWhitespace = ps.getProperty(SERIALIZER_useWhitespace, 
boolean.class, false);
+               maxIndent = ps.getProperty(SERIALIZER_maxIndent, int.class, 
100);
                addBeanTypeProperties = 
ps.getProperty(SERIALIZER_addBeanTypeProperties, boolean.class, true);
                trimNulls = ps.getProperty(SERIALIZER_trimNullProperties, 
boolean.class, true);
                trimEmptyCollections = 
ps.getProperty(SERIALIZER_trimEmptyCollections, boolean.class, false);
@@ -406,6 +427,7 @@ public class SerializerContext extends BeanContext {
                                .append("detectRecursions", detectRecursions)
                                .append("ignoreRecursions", ignoreRecursions)
                                .append("useWhitespace", useWhitespace)
+                               .append("maxIndent", maxIndent)
                                .append("addBeanTypeProperties", 
addBeanTypeProperties)
                                .append("trimNulls", trimNulls)
                                .append("trimEmptyCollections", 
trimEmptyCollections)

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java
 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java
index 65c188d..9601e94 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerGroupBuilder.java
@@ -268,6 +268,17 @@ public class SerializerGroupBuilder {
        }
 
        /**
+        * Sets the {@link SerializerContext#SERIALIZER_maxIndent} property on 
all serializers in this group.
+        *
+        * @param value The new value for this property.
+        * @return This object (for method chaining).
+        * @see SerializerContext#SERIALIZER_maxIndent
+        */
+       public SerializerGroupBuilder maxIndent(boolean value) {
+               return property(SERIALIZER_maxIndent, value);
+       }
+
+       /**
         * Sets the {@link SerializerContext#SERIALIZER_addBeanTypeProperties} 
property on all serializers in this group.
         *
         * @param value The new value for this property.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
index b8371ac..a103f86 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerSession.java
@@ -41,7 +41,7 @@ import org.apache.juneau.transform.*;
  */
 public class SerializerSession extends BeanSession {
 
-       private final int maxDepth, initialDepth;
+       private final int maxDepth, initialDepth, maxIndent;
        private final boolean
                detectRecursions,
                ignoreRecursions,
@@ -113,6 +113,7 @@ public class SerializerSession extends BeanSession {
                        detectRecursions = ctx.detectRecursions;
                        ignoreRecursions = ctx.ignoreRecursions;
                        useWhitespace = ctx.useWhitespace;
+                       maxIndent = ctx.maxIndent;
                        addBeanTypeProperties = ctx.addBeanTypeProperties;
                        trimNulls = ctx.trimNulls;
                        trimEmptyCollections = ctx.trimEmptyCollections;
@@ -131,6 +132,7 @@ public class SerializerSession extends BeanSession {
                        detectRecursions = 
op.getBoolean(SERIALIZER_detectRecursions, ctx.detectRecursions);
                        ignoreRecursions = 
op.getBoolean(SERIALIZER_ignoreRecursions, ctx.ignoreRecursions);
                        useWhitespace = op.getBoolean(SERIALIZER_useWhitespace, 
ctx.useWhitespace);
+                       maxIndent = op.getInt(SERIALIZER_maxIndent, 
ctx.maxIndent);
                        addBeanTypeProperties = 
op.getBoolean(SERIALIZER_addBeanTypeProperties, ctx.addBeanTypeProperties);
                        trimNulls = 
op.getBoolean(SERIALIZER_trimNullProperties, ctx.trimNulls);
                        trimEmptyCollections = 
op.getBoolean(SERIALIZER_trimEmptyCollections, ctx.trimEmptyCollections);
@@ -308,6 +310,15 @@ public class SerializerSession extends BeanSession {
        }
 
        /**
+        * Returns the {@link SerializerContext#SERIALIZER_maxIndent} setting 
value for this session.
+        *
+        * @return The {@link SerializerContext#SERIALIZER_maxIndent} setting 
value for this session.
+        */
+       public final int getMaxIndent() {
+               return maxIndent;
+       }
+
+       /**
         * Returns the {@link 
SerializerContext#SERIALIZER_addBeanTypeProperties} setting value for this 
session.
         *
         * @return The {@link 
SerializerContext#SERIALIZER_addBeanTypeProperties} setting value for this 
session.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
index 3250d1a..7f7784b 100644
--- 
a/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
+++ 
b/juneau-core/src/main/java/org/apache/juneau/serializer/SerializerWriter.java
@@ -36,6 +36,9 @@ public class SerializerWriter extends Writer {
        /** Use-whitespace flag. */
        protected final boolean useWhitespace;
 
+       /** Max indentation levels. */
+       protected final int maxIndent;
+
        /** Trim strings flag. */
        protected final boolean trimStrings;
 
@@ -49,13 +52,15 @@ public class SerializerWriter extends Writer {
         * @param out The writer being wrapped.
         * @param useWhitespace If <jk>true</jk>, calling {@link #cr(int)} will 
create an indentation and calling
         *      {@link #s()} will write a space character.
+        * @param maxIndent The maximum indentation level.
         * @param trimStrings If <jk>true</jk>, strings should be trimmed 
before they're serialized.
         * @param quoteChar The character to write when {@link #q()} is called.
         * @param uriResolver The URI resolver for resolving URIs to absolute 
or root-relative form.
         */
-       public SerializerWriter(Writer out, boolean useWhitespace, boolean 
trimStrings, char quoteChar, UriResolver uriResolver) {
+       public SerializerWriter(Writer out, boolean useWhitespace, int 
maxIndent, boolean trimStrings, char quoteChar, UriResolver uriResolver) {
                this.out = out;
                this.useWhitespace = useWhitespace;
+               this.maxIndent = maxIndent;
                this.trimStrings = trimStrings;
                this.quoteChar = quoteChar;
                this.uriResolver = uriResolver;
@@ -71,8 +76,23 @@ public class SerializerWriter extends Writer {
         * @return This object (for method chaining).
         */
        public SerializerWriter cr(int depth) throws IOException {
-               if (useWhitespace)
-                       return nl().i(depth);
+               if (useWhitespace && depth <= maxIndent)
+                       return nl(depth).i(depth);
+               return this;
+       }
+
+       /**
+        * Performs a carriage return at the end of a line.
+        * <p>
+        * Adds a newline and the specified number of tabs (if the {@code 
useWhitespace} 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 cre(int depth) throws IOException {
+               if (useWhitespace && depth <= maxIndent-1)
+                       return nl(depth).i(depth);
                return this;
        }
 
@@ -138,7 +158,7 @@ public class SerializerWriter extends Writer {
                i(indent);
                out.write(text);
                if (newline)
-                       nl();
+                       nl(indent);
                return this;
        }
 
@@ -149,9 +169,9 @@ public class SerializerWriter extends Writer {
         * or any other type that returns a URI via it's 
<code>toString()</code> method.
         * <p>
         * The URI is resolved based on the {@link 
SerializerContext#SERIALIZER_uriRelativity} and
-        * {@link SerializerContext#SERIALIZER_uriResolution} settings and the 
{@link UriContext} that's part of the 
+        * {@link SerializerContext#SERIALIZER_uriResolution} settings and the 
{@link UriContext} that's part of the
         * session.
-        * 
+        *
         * @param uri The URI to serialize.
         * @return This object (for method chaining).
         * @throws IOException If a problem occurred trying to write to the 
writer.
@@ -205,7 +225,21 @@ public class SerializerWriter extends Writer {
         * @return This object (for method chaining).
         */
        public SerializerWriter i(int indent) throws IOException {
-               if (useWhitespace)
+               if (useWhitespace && indent <= maxIndent)
+                       for (int i = 0; i < indent; i++)
+                               out.write('\t');
+               return this;
+       }
+
+       /**
+        * Writes an end-of-line indent to the writer if the {@code 
useWhitespace} 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 ie(int indent) throws IOException {
+               if (useWhitespace && indent <= maxIndent-1)
                        for (int i = 0; i < indent; i++)
                                out.write('\t');
                return this;
@@ -214,11 +248,12 @@ public class SerializerWriter extends Writer {
        /**
         * Writes a newline to the writer if the {@code useWhitespace} setting 
is enabled.
         *
+        * @param indent The current indentation level.
         * @throws IOException If a problem occurred trying to write to the 
writer.
         * @return This object (for method chaining).
         */
-       public SerializerWriter nl() throws IOException {
-               if (useWhitespace)
+       public SerializerWriter nl(int indent) throws IOException {
+               if (useWhitespace && indent <= maxIndent)
                        out.write('\n');
                return this;
        }
@@ -227,11 +262,12 @@ public class SerializerWriter extends Writer {
         * Writes a newline to the writer if the {@code useWhitespace} setting 
is enabled and the boolean flag is true.
         *
         * @param b The boolean flag.
+        * @param indent The current indentation level.
         * @return This object (for method chaining).
         * @throws IOException If a problem occurred trying to write to the 
writer.
         */
-       public SerializerWriter nlIf(boolean b) throws IOException {
-               if (b && useWhitespace)
+       public SerializerWriter nlIf(boolean b, int indent) throws IOException {
+               if (b && useWhitespace && indent <= maxIndent)
                        out.write('\n');
                return this;
        }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
index 580a799..b8bfc8c 100644
--- a/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/soap/SoapXmlSerializer.java
@@ -67,10 +67,10 @@ public final class SoapXmlSerializer extends XmlSerializer {
                w.oTag("soap", "Envelope")
                        .attr("xmlns", "soap", 
s.getProperty(SOAPXML_SOAPAction, "http://www.w3.org/2003/05/soap-envelope";))
                        .appendln(">");
-               w.sTag(1, "soap", "Body").nl();
+               w.sTag(1, "soap", "Body").nl(1);
                super.doSerialize(s, o);
-               w.eTag(1, "soap", "Body").nl();
-               w.eTag("soap", "Envelope").nl();
+               w.ie(1).eTag("soap", "Body").nl(1);
+               w.eTag("soap", "Envelope").nl(0);
        }
 
        @Override /* Serializer */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializer.java 
b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializer.java
index 96901d2..8bd45e0 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializer.java
@@ -303,7 +303,7 @@ public class UonSerializer extends WriterSerializer {
                }
 
                if (m.size() > 0)
-                       out.cr(depth-1);
+                       out.cre(depth-1);
 
                if (! session.isPlainTextParams())
                        out.append(')');
@@ -343,7 +343,7 @@ public class UonSerializer extends WriterSerializer {
                }
 
                if (m.size() > 0)
-                       out.cr(depth-1);
+                       out.cre(depth-1);
                if (! session.isPlainTextParams())
                        out.append(')');
 
@@ -370,7 +370,7 @@ public class UonSerializer extends WriterSerializer {
                }
 
                if (c.size() > 0)
-                       out.cr(depth-1);
+                       out.cre(depth-1);
                if (! session.isPlainTextParams())
                        out.append(')');
 

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java 
b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java
index d031a9b..bc1d18b 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerBuilder.java
@@ -127,6 +127,12 @@ public class UonSerializerBuilder extends 
SerializerBuilder {
        }
 
        @Override /* SerializerBuilder */
+       public UonSerializerBuilder maxIndent(int value) {
+               super.maxIndent(value);
+               return this;
+       }
+
+       @Override /* SerializerBuilder */
        public UonSerializerBuilder addBeanTypeProperties(boolean value) {
                super.addBeanTypeProperties(value);
                return this;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
----------------------------------------------------------------------
diff --git 
a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java 
b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
index b7c6c66..33a082e 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/UonSerializerSession.java
@@ -98,6 +98,6 @@ public class UonSerializerSession extends SerializerSession {
                Object output = getOutput();
                if (output instanceof UonWriter)
                        return (UonWriter)output;
-               return new UonWriter(this, super.getWriter(), 
isUseWhitespace(), isEncodeChars(), isTrimStrings(), isPlainTextParams(), 
getUriResolver());
+               return new UonWriter(this, super.getWriter(), 
isUseWhitespace(), getMaxIndent(), isEncodeChars(), isTrimStrings(), 
isPlainTextParams(), getUriResolver());
        }
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java 
b/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java
index 1aafc74..ef0940c 100644
--- a/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java
+++ b/juneau-core/src/main/java/org/apache/juneau/uon/UonWriter.java
@@ -51,13 +51,14 @@ public final class UonWriter extends SerializerWriter {
         * @param session The session that created this writer.
         * @param out The writer being wrapped.
         * @param useWhitespace If <jk>true</jk>, tabs will be used in output.
+        * @param maxIndent The maximum indentation level.
         * @param encodeChars If <jk>true</jk>, special characters should be 
encoded.
         * @param trimStrings If <jk>true</jk>, strings should be trimmed 
before they're serialized.
         * @param plainTextParams If <jk>true</jk>, don't use UON notation for 
values.
         * @param uriResolver The URI resolver for resolving URIs to absolute 
or root-relative form.
         */
-       protected UonWriter(UonSerializerSession session, Writer out, boolean 
useWhitespace, boolean encodeChars, boolean trimStrings, boolean 
plainTextParams, UriResolver uriResolver) {
-               super(out, useWhitespace, trimStrings, '\'', uriResolver);
+       protected UonWriter(UonSerializerSession session, Writer out, boolean 
useWhitespace, int maxIndent, boolean encodeChars, boolean trimStrings, boolean 
plainTextParams, UriResolver uriResolver) {
+               super(out, useWhitespace, maxIndent, trimStrings, '\'', 
uriResolver);
                this.session = session;
                this.encodeChars = encodeChars;
                this.plainTextParams = plainTextParams;
@@ -180,6 +181,12 @@ public final class UonWriter extends SerializerWriter {
        }
 
        @Override /* SerializerWriter */
+       public UonWriter cre(int depth) throws IOException {
+               super.cre(depth);
+               return this;
+       }
+
+       @Override /* SerializerWriter */
        public UonWriter appendln(int indent, String text) throws IOException {
                super.appendln(indent, text);
                return this;
@@ -216,8 +223,8 @@ public final class UonWriter extends SerializerWriter {
        }
 
        @Override /* SerializerWriter */
-       public UonWriter nl() throws IOException {
-               super.nl();
+       public UonWriter nl(int indent) throws IOException {
+               super.nl(indent);
                return this;
        }
 

Reply via email to