You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/06/13 14:27:40 UTC

[1/2] incubator-juneau git commit: New SERIALIZER_maxIndent setting.

Repository: incubator-juneau
Updated Branches:
  refs/heads/master 257776b98 -> 7239f3e34


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
index aeb5fc2..71179fd 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
@@ -267,7 +267,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
 			this.defaultNs = defaultNs;
 			this.targetNs = targetNs;
 			this.session = session;
-			w = new XmlWriter(sw, session.isUseWhitespace(), session.isTrimStrings(), session.getQuoteChar(), null, true, null);
+			w = new XmlWriter(sw, session.isUseWhitespace(), session.getMaxIndent(), session.isTrimStrings(), session.getQuoteChar(), null, true, null);
 			int i = session.getIndent();
 			w.oTag(i, "schema");
 			w.attr("xmlns", xs.getUri());
@@ -277,13 +277,13 @@ public class XmlSchemaSerializer extends XmlSerializer {
 				w.attr("attributeFormDefault", "qualified");
 			for (Namespace ns2 : allNs)
 				w.attr("xmlns", ns2.name, ns2.uri);
-			w.append('>').nl();
+			w.append('>').nl(i);
 			for (Namespace ns : allNs) {
 				if (ns != targetNs) {
 					w.oTag(i+1, "import")
 						.attr("namespace", ns.getUri())
 						.attr("schemaLocation", ns.getName()+".xsd")
-						.append("/>").nl();
+						.append("/>").nl(i+1);
 				}
 			}
 		}
@@ -303,7 +303,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
 			w.oTag(i, "element")
 				.attr("name", XmlUtils.encodeElementName(name))
 				.attr("type", type)
-				.append('/').append('>').nl();
+				.append('/').append('>').nl(i);
 
 			schemas.queueType(ns, null, ft);
 			schemas.processQueue();
@@ -321,7 +321,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
 			w.oTag(i, "attribute")
 				.attr("name", name)
 				.attr("type", type)
-				.append('/').append('>').nl();
+				.append('/').append('>').nl(i);
 
 			return true;
 		}
@@ -345,10 +345,10 @@ public class XmlSchemaSerializer extends XmlSerializer {
 			if ((xbm != null && (xbm.getContentFormat() != null && xbm.getContentFormat().isOneOf(TEXT,TEXT_PWS,MIXED,MIXED_PWS,XMLTEXT))) || ! cm.isMapOrBean())
 				w.attr("mixed", "true");
 
-			w.cTag().nl();
+			w.cTag().nl(i);
 
 			if (! (cm.isMapOrBean() || cm.isCollectionOrArray() || (cm.isAbstract() && ! cm.isNumber()) || cm.isObject())) {
-				w.oTag(i+1, "attribute").attr("name", session.getBeanTypePropertyName(cm)).attr("type", "string").ceTag().nl();
+				w.oTag(i+1, "attribute").attr("name", session.getBeanTypePropertyName(cm)).attr("type", "string").ceTag().nl(i+1);
 
 			} else {
 
@@ -366,12 +366,12 @@ public class XmlSchemaSerializer extends XmlSerializer {
 
 					XmlBeanMeta xbm2 = bm.getExtendedMeta(XmlBeanMeta.class);
 					if (xbm2.getContentProperty() != null && xbm2.getContentFormat() == ELEMENTS) {
-						w.sTag(i+1, "sequence").nl();
+						w.sTag(i+1, "sequence").nl(i+1);
 						w.oTag(i+2, "any")
 							.attr("processContents", "skip")
 							.attr("minOccurs", 0)
-							.ceTag().nl();
-						w.eTag(i+1, "sequence").nl();
+							.ceTag().nl(i+2);
+						w.eTag(i+1, "sequence").nl(i+1);
 
 					} else if (hasChildElements) {
 
@@ -396,15 +396,15 @@ public class XmlSchemaSerializer extends XmlSerializer {
 						if (hasOtherNsElement || hasCollapsed) {
 							// If this bean has any child elements in another namespace,
 							// we need to add an <any> element.
-							w.oTag(i+1, "choice").attr("maxOccurs", "unbounded").cTag().nl();
+							w.oTag(i+1, "choice").attr("maxOccurs", "unbounded").cTag().nl(i+1);
 							w.oTag(i+2, "any")
 								.attr("processContents", "skip")
 								.attr("minOccurs", 0)
-								.ceTag().nl();
-							w.eTag(i+1, "choice").nl();
+								.ceTag().nl(i+2);
+							w.eTag(i+1, "choice").nl(i+1);
 
 						} else {
-							w.sTag(i+1, "all").nl();
+							w.sTag(i+1, "all").nl(i+1);
 							for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
 								XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
 								if (xmlMeta.getXmlFormat() != ATTR) {
@@ -423,7 +423,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
 											.attr("type", getXmlType(cNs, ct2))
 											.attr("minOccurs", 0);
 
-										w.ceTag().nl();
+										w.ceTag().nl(i+2);
 									} else {
 										// Child element is in another namespace.
 										schemas.queueElement(cNs, pMeta.getName(), ct2);
@@ -432,7 +432,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
 
 								}
 							}
-							w.eTag(i+1, "all").nl();
+							w.eTag(i+1, "all").nl(i+1);
 						}
 
 					}
@@ -447,9 +447,9 @@ public class XmlSchemaSerializer extends XmlSerializer {
 						if (pNs != targetNs) {
 							schemas.queueAttribute(pNs, pMeta.getName(), pMeta.getClassMeta());
 							w.oTag(i+1, "attribute")
-							//.attr("name", pMeta.getName(), true)
-							.attr("ref", pNs.getName() + ':' + pMeta.getName())
-							.ceTag().nl();
+								//.attr("name", pMeta.getName(), true)
+								.attr("ref", pNs.getName() + ':' + pMeta.getName())
+								.ceTag().nl(i+1);
 						}
 
 						// Otherwise, it's just a plain attribute of this bean.
@@ -457,7 +457,7 @@ public class XmlSchemaSerializer extends XmlSerializer {
 							w.oTag(i+1, "attribute")
 								.attr("name", pMeta.getName(), true)
 								.attr("type", getXmlAttrType(pMeta.getClassMeta()))
-								.ceTag().nl();
+								.ceTag().nl(i+1);
 						}
 					}
 
@@ -465,43 +465,43 @@ public class XmlSchemaSerializer extends XmlSerializer {
 				} else if (cm.isCollectionOrArray()) {
 					ClassMeta<?> elementType = cm.getElementType();
 					if (elementType.isObject()) {
-						w.sTag(i+1, "sequence").nl();
+						w.sTag(i+1, "sequence").nl(i+1);
 						w.oTag(i+2, "any")
 							.attr("processContents", "skip")
 							.attr("maxOccurs", "unbounded")
 							.attr("minOccurs", "0")
-							.ceTag().nl();
-						w.eTag(i+1, "sequence").nl();
+							.ceTag().nl(i+2);
+						w.eTag(i+1, "sequence").nl(i+1);
 					} else {
 						Namespace cNs = first(elementType.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
 						schemas.queueType(cNs, null, elementType);
-						w.sTag(i+1, "sequence").nl();
+						w.sTag(i+1, "sequence").nl(i+1);
 						w.oTag(i+2, "any")
 							.attr("processContents", "skip")
 							.attr("maxOccurs", "unbounded")
 							.attr("minOccurs", "0")
-							.ceTag().nl();
-						w.eTag(i+1, "sequence").nl();
+							.ceTag().nl(i+2);
+						w.eTag(i+1, "sequence").nl(i+1);
 					}
 
 				//----- Map -----
 				} else if (cm.isMap() || cm.isAbstract() || cm.isObject()) {
-					w.sTag(i+1, "sequence").nl();
+					w.sTag(i+1, "sequence").nl(i+1);
 					w.oTag(i+2, "any")
 						.attr("processContents", "skip")
 						.attr("maxOccurs", "unbounded")
 						.attr("minOccurs", "0")
-						.ceTag().nl();
-					w.eTag(i+1, "sequence").nl();
+						.ceTag().nl(i+2);
+					w.eTag(i+1, "sequence").nl(i+1);
 				}
 
 				w.oTag(i+1, "attribute")
 					.attr("name", session.getBeanTypePropertyName(null))
 					.attr("type", "string")
-					.ceTag().nl();
+					.ceTag().nl(i+1);
 			}
 
-			w.eTag(i, "complexType").nl();
+			w.eTag(i, "complexType").nl(i);
 			schemas.processQueue();
 
 			return true;
@@ -529,7 +529,8 @@ public class XmlSchemaSerializer extends XmlSerializer {
 		@Override /* Object */
 		public String toString() {
 			try {
-				w.eTag(session.getIndent(), "schema").nl();
+				int i = session.getIndent();
+				w.eTag(i, "schema").nl(i);
 			} catch (IOException e) {
 				throw new RuntimeException(e); // Shouldn't happen.
 			}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
index 84c86aa..73fdf7e 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
@@ -512,7 +512,7 @@ public class XmlSerializer extends WriterSerializer {
 				out.append('>');
 
 			if (cr && ! (sType.isMapOrBean()))
-				out.nl();
+				out.nl(indent+1);
 		}
 
 		ContentResult rc = CR_ELEMENTS;
@@ -568,9 +568,9 @@ public class XmlSerializer extends WriterSerializer {
 				out.append('/').append('>');
 			}
 			else
-				out.i(cr && rc != CR_MIXED ? indent : 0).eTag(elementNs, en, encodeEn);
+				out.ie(cr && rc != CR_MIXED ? indent : 0).eTag(elementNs, en, encodeEn);
 			if (! isMixed)
-				out.nl();
+				out.nl(indent);
 		}
 
 		return out;
@@ -600,7 +600,7 @@ public class XmlSerializer extends WriterSerializer {
 
 			if (! hasChildren) {
 				hasChildren = true;
-				out.append('>').nlIf(! isMixed);
+				out.append('>').nlIf(! isMixed, session.getIndent());
 			}
 			serializeAnything(session, out, value, valueType, session.toString(k), null, false, XmlFormat.DEFAULT, isMixed, false, null);
 		}
@@ -704,7 +704,7 @@ public class XmlSerializer extends WriterSerializer {
 
 				if (! hasChildren) {
 					hasChildren = true;
-					out.appendIf(! isCollapsed, '>').nlIf(! isMixed);
+					out.appendIf(! isCollapsed, '>').nlIf(! isMixed, session.getIndent());
 				}
 
 				XmlBeanPropertyMeta xbpm = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
@@ -713,7 +713,7 @@ public class XmlSerializer extends WriterSerializer {
 		}
 		if (! hasContent)
 			return (hasChildren ? CR_ELEMENTS : isVoidElement ? CR_VOID : CR_EMPTY);
-		out.append('>').nlIf(! isMixed);
+		out.append('>').nlIf(! isMixed, session.getIndent());
 
 		// Serialize XML content.
 		if (content != null) {
@@ -739,7 +739,7 @@ public class XmlSerializer extends WriterSerializer {
 					out.i(session.indent);
 				out.text(content);
 				if (! isMixed)
-					out.nl();
+					out.nl(session.indent);
 			}
 		}
 		return isMixed ? CR_MIXED : CR_ELEMENTS;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java
index a4db76c..bcf450f 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerBuilder.java
@@ -273,6 +273,12 @@ public class XmlSerializerBuilder extends SerializerBuilder {
 	}
 
 	@Override /* SerializerBuilder */
+	public XmlSerializerBuilder maxIndent(int value) {
+		super.maxIndent(value);
+		return this;
+	}
+
+	@Override /* SerializerBuilder */
 	public XmlSerializerBuilder 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/xml/XmlSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
index 5ee1962..ca67989 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java
@@ -201,6 +201,6 @@ public class XmlSerializerSession extends SerializerSession {
 		Object output = getOutput();
 		if (output instanceof XmlWriter)
 			return (XmlWriter)output;
-		return new XmlWriter(super.getWriter(), isUseWhitespace(), isTrimStrings(), getQuoteChar(), getUriResolver(), isEnableNamespaces(), getDefaultNamespace());
+		return new XmlWriter(super.getWriter(), isUseWhitespace(), getMaxIndent(), isTrimStrings(), getQuoteChar(), getUriResolver(), isEnableNamespaces(), getDefaultNamespace());
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java
index 3166b98..21d1d2a 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java
@@ -37,14 +37,15 @@ public class XmlWriter extends SerializerWriter {
 	 *
 	 * @param out The wrapped writer.
 	 * @param useWhitespace If <jk>true</jk> XML elements will be indented.
+	 * @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 for attributes.  Should be <js>'\''</js> or <js>'"'</js>.
 	 * @param uriResolver The URI resolver for resolving URIs to absolute or root-relative form.
 	 * @param enableNs Flag to indicate if XML namespaces are enabled.
 	 * @param defaultNamespace The default namespace if XML namespaces are enabled.
 	 */
-	public XmlWriter(Writer out, boolean useWhitespace, boolean trimStrings, char quoteChar, UriResolver uriResolver, boolean enableNs, Namespace defaultNamespace) {
-		super(out, useWhitespace, trimStrings, quoteChar, uriResolver);
+	public XmlWriter(Writer out, boolean useWhitespace, int maxIndent, boolean trimStrings, char quoteChar, UriResolver uriResolver, boolean enableNs, Namespace defaultNamespace) {
+		super(out, useWhitespace, maxIndent, trimStrings, quoteChar, uriResolver);
 		this.enableNs = enableNs;
 		this.defaultNsPrefix = defaultNamespace == null ? null : defaultNamespace.name;
 	}
@@ -570,6 +571,12 @@ public class XmlWriter extends SerializerWriter {
 	}
 
 	@Override /* SerializerWriter */
+	public XmlWriter cre(int depth) throws IOException {
+		super.cre(depth);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
 	public XmlWriter appendln(int indent, String text) throws IOException {
 		super.appendln(indent, text);
 		return this;
@@ -612,8 +619,14 @@ public class XmlWriter extends SerializerWriter {
 	}
 
 	@Override /* SerializerWriter */
-	public XmlWriter nl() throws IOException {
-		super.nl();
+	public XmlWriter ie(int indent) throws IOException {
+		super.ie(indent);
+		return this;
+	}
+
+	@Override /* SerializerWriter */
+	public XmlWriter 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/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index 81a3f33..460915d 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -6263,6 +6263,7 @@
 						<li>{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_uriContext}
 						<li>{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_uriRelativity}
 						<li>{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_uriResolution}
+						<li>{@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_maxIndent}
 				</ul>
 			<li>New annotation property: {@link org.apache.juneau.annotation.BeanProperty#value()}.
 				<br>The following two annotations are considered equivalent:

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7239f3e3/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
index 23b31bf..fd2b66a 100644
--- a/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
+++ b/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
@@ -913,6 +913,17 @@ public class RestClientBuilder extends CoreObjectBuilder {
 	}
 
 	/**
+	 * 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 RestClientBuilder 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-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
----------------------------------------------------------------------
diff --git a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
index b4ef754..755109f 100644
--- a/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
+++ b/juneau-rest/src/main/java/org/apache/juneau/rest/RestRequest.java
@@ -201,6 +201,18 @@ public final class RestRequest extends HttpServletRequestWrapper {
 		return "HTTP " + getMethod() + " " + getRequestURI() + (qs == null ? "" : "?" + qs);
 	}
 
+	/**
+	 * Same as {@link #getAttribute(String)} but returns a default value if not found.
+	 *
+	 * @param name The request attribute name.
+	 * @param def The default value if the attribute doesn't exist.
+	 * @return The request attribute value.
+	 */
+	public Object getAttribute(String name, Object def) {
+		Object o = super.getAttribute(name);
+		return (o == null ? def : o);
+	}
+
 
 	//--------------------------------------------------------------------------------
 	// Properties


[2/2] incubator-juneau git commit: New SERIALIZER_maxIndent setting.

Posted by ja...@apache.org.
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 <ja...@apache.org>
Authored: Tue Jun 13 10:27:34 2017 -0400
Committer: JamesBognar <ja...@apache.org>
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;
 	}