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 2021/02/06 16:00:42 UTC

[juneau] branch master updated: CollectionUtils refactoring.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 3ddd7f7  CollectionUtils refactoring.
3ddd7f7 is described below

commit 3ddd7f7afe256278592d199536c3ea740e890110
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Sat Feb 6 11:00:11 2021 -0500

    CollectionUtils refactoring.
---
 .../org/apache/juneau/dto/swagger/HeaderInfo.java  |   4 +-
 .../java/org/apache/juneau/dto/swagger/Items.java  |   4 +-
 .../org/apache/juneau/dto/swagger/Operation.java   |  48 +--
 .../apache/juneau/dto/swagger/ParameterInfo.java   |  12 +-
 .../apache/juneau/dto/swagger/ResponseInfo.java    |  14 +-
 .../org/apache/juneau/dto/swagger/SchemaInfo.java  |  20 +-
 .../apache/juneau/dto/swagger/SecurityScheme.java  |   6 +-
 .../org/apache/juneau/dto/swagger/Swagger.java     |  75 ++--
 .../apache/juneau/internal/CollectionUtils.java    | 465 ++-------------------
 .../org/apache/juneau/internal/ListBuilder.java    | 234 +++++++++++
 .../org/apache/juneau/internal/MapBuilder.java     | 222 ++++++++++
 .../org/apache/juneau/internal/SetBuilder.java     | 237 +++++++++++
 .../java/org/apache/juneau/mstat/ThrownStats.java  |   4 +-
 .../java/org/apache/juneau/reflect/ClassInfo.java  |   7 +-
 .../java/org/apache/juneau/rest/RestContext.java   |  21 +-
 .../apache/juneau/dto/swagger/HeaderInfo_Test.java |   4 +-
 .../apache/juneau/dto/swagger/Swagger_Test.java    |   2 +-
 .../apache/juneau/utils/CollectionUtilsTest.java   |  64 ---
 18 files changed, 857 insertions(+), 586 deletions(-)

diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/HeaderInfo.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/HeaderInfo.java
index 7076856..a80407c 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/HeaderInfo.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/HeaderInfo.java
@@ -382,7 +382,7 @@ public class HeaderInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public HeaderInfo addEnum(Collection<Object> value) {
-		_enum = addToSet(_enum, value);
+		_enum = setBuilder(_enum).sparse().addAll(value).build();
 		return this;
 	}
 
@@ -421,7 +421,7 @@ public class HeaderInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public HeaderInfo _enum(Object...value) {
-		setEnum(toSet(value, Object.class));
+		setEnum(setBuilder(Object.class).sparse().addAny(value).build());
 		return this;
 	}
 
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Items.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Items.java
index f71d92d..a1e29c5 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Items.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Items.java
@@ -326,7 +326,7 @@ public class Items extends SwaggerElement {
 	 * 	<br>Can be <jk>null</jk> to unset the property.
 	 */
 	public void addEnum(Collection<Object> value) {
-		_enum = addToSet(_enum, value);
+		_enum = setBuilder(_enum).sparse().addAll(value).build();
 	}
 
 	/**
@@ -359,7 +359,7 @@ public class Items extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Items _enum(Object...value) {
-		setEnum(toSet(value, Object.class));
+		setEnum(setBuilder(Object.class).sparse().addAny(value).build());
 		return this;
 	}
 
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Operation.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Operation.java
index 7b9f85b..b7addf5 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Operation.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Operation.java
@@ -251,7 +251,7 @@ public class Operation extends SwaggerElement {
 	 * 	<br>Can be <jk>null</jk> to unset the property.
 	 */
 	public void addConsumes(Collection<MediaType> value) {
-		consumes = addToSet(consumes, value);
+		consumes = setBuilder(consumes).sparse().addAll(value).build();
 	}
 
 	/**
@@ -277,7 +277,7 @@ public class Operation extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Operation consumes(MediaType...value) {
-		setConsumes(toSet(value, MediaType.class));
+		setConsumes(setBuilder(MediaType.class).sparse().add(value).build());
 		return this;
 	}
 
@@ -308,7 +308,7 @@ public class Operation extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Operation consumes(String...value) {
-		setConsumes(toSet(value, MediaType.class));
+		setConsumes(setBuilder(MediaType.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -682,7 +682,7 @@ public class Operation extends SwaggerElement {
 	 * 	<br>Ignored if <jk>null</jk>.
 	 */
 	public void addParameters(Collection<ParameterInfo> value) {
-		parameters = addToList(parameters, value);
+		parameters = listBuilder(parameters).sparse().addAll(value).build();
 	}
 
 	/**
@@ -708,7 +708,7 @@ public class Operation extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Operation parameters(ParameterInfo...value) {
-		setParameters(toList(value, ParameterInfo.class));
+		setParameters(listBuilder(ParameterInfo.class).sparse().add(value).build());
 		return this;
 	}
 
@@ -739,7 +739,7 @@ public class Operation extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Operation parameters(String...value) {
-		setParameters(toList(value, ParameterInfo.class));
+		setParameters(listBuilder(ParameterInfo.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -785,7 +785,7 @@ public class Operation extends SwaggerElement {
 	 * 	<br>Value MUST be as described under {@doc ExtSwaggerMimeTypes}.
 	 */
 	public void addProduces(Collection<MediaType> value) {
-		produces = addToSet(produces, value);
+		produces = setBuilder(produces).sparse().addAll(value).build();
 	}
 
 	/**
@@ -811,7 +811,7 @@ public class Operation extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Operation produces(MediaType...value) {
-		setProduces(toSet(value, MediaType.class));
+		setProduces(setBuilder(MediaType.class).sparse().add(value).build());
 		return this;
 	}
 
@@ -842,7 +842,7 @@ public class Operation extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Operation produces(String...value) {
-		setProduces(toSet(value, MediaType.class));
+		setProduces(setBuilder(MediaType.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -919,7 +919,7 @@ public class Operation extends SwaggerElement {
 	 * 	<br>Ignored if <jk>null</jk>.
 	 */
 	public void addResponses(Map<String,ResponseInfo> values) {
-		responses = addToMap(responses, values);
+		responses = mapBuilder(responses).sparse().addAll(values).build();
 	}
 
 	/**
@@ -977,7 +977,7 @@ public class Operation extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Operation responses(String json) {
-		setResponses(toMap(json, String.class, ResponseInfo.class));
+		setResponses(mapBuilder(String.class,ResponseInfo.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -1029,7 +1029,7 @@ public class Operation extends SwaggerElement {
 	 * 	<br>Ignored if <jk>null</jk>.
 	 */
 	public void addSchemes(Collection<String> value) {
-		schemes = addToSet(schemes, value);
+		schemes = setBuilder(schemes).sparse().addAll(value).build();
 	}
 
 	/**
@@ -1056,7 +1056,7 @@ public class Operation extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Operation schemes(String...value) {
-		setSchemes(toSet(value, String.class));
+		setSchemes(setBuilder(String.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -1118,7 +1118,7 @@ public class Operation extends SwaggerElement {
 	 * The new value for this property.
 	 */
 	public void addSecurity(Collection<Map<String,List<String>>> values) {
-		security = addToList(security, values);
+		security = listBuilder(security).sparse().addAll(values).build();
 	}
 
 	/**
@@ -1185,7 +1185,7 @@ public class Operation extends SwaggerElement {
 	 */
 	@SuppressWarnings({ "unchecked", "rawtypes" })
 	public Operation security(String value) {
-		setSecurity((Collection)toList(value, Map.class, String.class, List.class, String.class));
+		setSecurity((Collection)listBuilder(Map.class,String.class,List.class,String.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -1290,7 +1290,7 @@ public class Operation extends SwaggerElement {
 	 * 	The values to add to this property.
 	 */
 	public void addTags(Collection<String> value) {
-		tags = addToSet(tags, value);
+		tags = setBuilder(tags).sparse().addAll(value).build();
 	}
 
 	/**
@@ -1341,7 +1341,7 @@ public class Operation extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Operation tags(String...value) {
-		setTags(toSet(value, String.class));
+		setTags(setBuilder(String.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -1373,18 +1373,18 @@ public class Operation extends SwaggerElement {
 		if (property == null)
 			return this;
 		switch (property) {
-			case "consumes": return consumes(toList(value, MediaType.class));
+			case "consumes": return consumes(listBuilder(MediaType.class).sparse().addAny(value).build());
 			case "deprecated": return deprecated(toBoolean(value));
 			case "description": return description(stringify(value));
 			case "externalDocs": return externalDocs(toType(value, ExternalDocumentation.class));
 			case "operationId": return operationId(stringify(value));
-			case "parameters": return parameters(toList(value, ParameterInfo.class));
-			case "produces": return produces(toList(value, MediaType.class));
-			case "responses": return responses(toMap(value, String.class, ResponseInfo.class));
-			case "schemes": return schemes(toList(value, String.class));
-			case "security": return security((List)toList(value, Map.class, String.class, List.class, String.class));
+			case "parameters": return parameters(listBuilder(ParameterInfo.class).sparse().addAny(value).build());
+			case "produces": return produces(listBuilder(MediaType.class).sparse().addAny(value).build());
+			case "responses": return responses(mapBuilder(String.class,ResponseInfo.class).sparse().addAny(value).build());
+			case "schemes": return schemes(listBuilder(String.class).sparse().addAny(value).build());
+			case "security": return security((List)listBuilder(Map.class,String.class,List.class,String.class).sparse().addAny(value).build());
 			case "summary": return summary(stringify(value));
-			case "tags": return tags(toList(value, String.class));
+			case "tags": return tags(listBuilder(String.class).sparse().addAny(value).build());
 			default:
 				super.set(property, value);
 				return this;
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ParameterInfo.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ParameterInfo.java
index 3f4b501..94e683a 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ParameterInfo.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ParameterInfo.java
@@ -575,7 +575,7 @@ public class ParameterInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ParameterInfo addEnum(Collection<Object> value) {
-		_enum = addToSet(_enum, value);
+		_enum = setBuilder(_enum).sparse().addAll(value).build();
 		return this;
 	}
 
@@ -609,7 +609,7 @@ public class ParameterInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ParameterInfo _enum(Object...value) {
-		setEnum(toSet(value, Object.class));
+		setEnum(setBuilder(Object.class).sparse().addAny(value).build());
 		return this;
 	}
 
@@ -690,7 +690,7 @@ public class ParameterInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ParameterInfo addExamples(Map<String,String> values) {
-		examples = addToMap(examples, values);
+		examples = mapBuilder(examples).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -705,7 +705,7 @@ public class ParameterInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ParameterInfo example(String name, String value) {
-		examples = addToMap(examples, name, value);
+		examples = mapBuilder(examples).sparse().add(name, value).build();
 		return this;
 	}
 
@@ -738,7 +738,7 @@ public class ParameterInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ParameterInfo examples(String json) {
-		setExamples(toMap(json, String.class, String.class));
+		setExamples(mapBuilder(String.class,String.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -1939,7 +1939,7 @@ public class ParameterInfo extends SwaggerElement {
 			case "description": return description(stringify(value));
 			case "enum": return _enum(value);
 			case "example": return example(stringify(value));
-			case "examples": return examples(toMap(value, String.class, String.class));
+			case "examples": return examples(mapBuilder(String.class,String.class).sparse().addAny(value).build());
 			case "exclusiveMaximum": return exclusiveMaximum(toBoolean(value));
 			case "exclusiveMinimum": return exclusiveMinimum(toBoolean(value));
 			case "format": return format(stringify(value));
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ResponseInfo.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ResponseInfo.java
index c0cfad0..b3ac618 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ResponseInfo.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/ResponseInfo.java
@@ -277,7 +277,7 @@ public class ResponseInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ResponseInfo addExamples(Map<String,Object> values) {
-		examples = addToMap(examples, values);
+		examples = mapBuilder(examples).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -292,7 +292,7 @@ public class ResponseInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ResponseInfo example(String mimeType, Object example) {
-		examples = addToMap(examples, mimeType, example);
+		examples =  mapBuilder(examples).sparse().add(mimeType, example).build();
 		return this;
 	}
 
@@ -330,7 +330,7 @@ public class ResponseInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ResponseInfo examples(String value) {
-		setExamples(toMap(value, String.class, Object.class));
+		setExamples(mapBuilder(String.class,Object.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -373,7 +373,7 @@ public class ResponseInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ResponseInfo addHeaders(Map<String,HeaderInfo> values) {
-		headers = addToMap(headers, values);
+		headers = mapBuilder(headers).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -432,7 +432,7 @@ public class ResponseInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public ResponseInfo headers(String json) {
-		setHeaders(toMap(json, String.class, HeaderInfo.class));
+		setHeaders(mapBuilder(String.class,HeaderInfo.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -558,8 +558,8 @@ public class ResponseInfo extends SwaggerElement {
 		switch (property) {
 			case "description": return description(stringify(value));
 			case "example": return example(value);
-			case "examples": return examples(toMap(value, String.class, Object.class));
-			case "headers": return headers(toMap(value, String.class, HeaderInfo.class));
+			case "examples": return examples(mapBuilder(String.class,Object.class).sparse().addAny(value).build());
+			case "headers": return headers(mapBuilder(String.class,HeaderInfo.class).sparse().addAny(value).build());
 			case "schema": return schema(stringify(value));
 			default:
 				super.set(property, value);
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java
index 2125e50..bb5a448 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SchemaInfo.java
@@ -253,7 +253,7 @@ public class SchemaInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SchemaInfo addAllOf(Collection<Object> values) {
-		allOf = addToSet(allOf, values);
+		allOf = setBuilder(allOf).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -288,7 +288,7 @@ public class SchemaInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SchemaInfo allOf(Object...value) {
-		setAllOf(toSet(value, Object.class));
+		setAllOf(setBuilder(Object.class).sparse().addAny(value).build());
 		return this;
 	}
 
@@ -474,7 +474,7 @@ public class SchemaInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SchemaInfo addEnum(Collection<Object> value) {
-		_enum = addToSet(_enum, value);
+		_enum = setBuilder(_enum).sparse().addAll(value).build();
 		return this;
 	}
 
@@ -496,7 +496,7 @@ public class SchemaInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SchemaInfo _enum(Object...value) {
-		setEnum(toSet(value, Object.class));
+		setEnum(setBuilder(Object.class).sparse().addAny(value).build());
 		return this;
 	}
 
@@ -1485,7 +1485,7 @@ public class SchemaInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SchemaInfo addProperties(Map<String,SchemaInfo> values) {
-		properties = addToMap(properties, values);
+		properties = mapBuilder(properties).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -1518,7 +1518,7 @@ public class SchemaInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SchemaInfo properties(String json) {
-		setProperties(toMap(json, String.class, SchemaInfo.class));
+		setProperties(mapBuilder(String.class,SchemaInfo.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -1678,7 +1678,7 @@ public class SchemaInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SchemaInfo addRequired(Collection<String> value) {
-		required = addToSet(required, value);
+		required = setBuilder(required).sparse().addAny(value).build();
 		return this;
 	}
 
@@ -1711,7 +1711,7 @@ public class SchemaInfo extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SchemaInfo required(String...value) {
-		setRequired(toSet(value, String.class));
+		setRequired(setBuilder(String.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -2017,10 +2017,10 @@ public class SchemaInfo extends SwaggerElement {
 			case "minProperties": return minProperties(toInteger(value));
 			case "multipleOf": return multipleOf(toNumber(value));
 			case "pattern": return pattern(stringify(value));
-			case "properties": return properties(toMap(value,String.class,SchemaInfo.class));
+			case "properties": return properties(mapBuilder(String.class,SchemaInfo.class).sparse().addAny(value).build());
 			case "readOnly": return readOnly(toBoolean(value));
 			case "$ref": return ref(stringify(value));
-			case "required": return required(toList(value,String.class));
+			case "required": return required(listBuilder(String.class).sparse().addAny(value).build());
 			case "title": return title(stringify(value));
 			case "type": return type(stringify(value));
 			case "uniqueItems": return uniqueItems(toBoolean(value));
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SecurityScheme.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SecurityScheme.java
index de3b670..5e522fc 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SecurityScheme.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/SecurityScheme.java
@@ -473,7 +473,7 @@ public class SecurityScheme extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SecurityScheme addScopes(Map<String,String> values) {
-		scopes = addToMap(scopes, values);
+		scopes = mapBuilder(scopes).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -515,7 +515,7 @@ public class SecurityScheme extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public SecurityScheme scopes(String json) {
-		setScopes(toMap(json, String.class, String.class));
+		setScopes(mapBuilder(String.class,String.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -683,7 +683,7 @@ public class SecurityScheme extends SwaggerElement {
 			case "flow": return flow(stringify(value));
 			case "in": return in(stringify(value));
 			case "name": return name(stringify(value));
-			case "scopes": return scopes(toMap(value, String.class, String.class));
+			case "scopes": return scopes(mapBuilder(String.class,String.class).sparse().addAny(value).build());
 			case "tokenUrl": return tokenUrl(stringify(value));
 			case "type": return type(stringify(value));
 			default:
diff --git a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Swagger.java b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
index e028260..f55884b 100644
--- a/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
+++ b/juneau-core/juneau-dto/src/main/java/org/apache/juneau/dto/swagger/Swagger.java
@@ -269,7 +269,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addConsumes(Collection<MediaType> values) {
-		consumes = addToSet(consumes, values);
+		consumes = setBuilder(consumes).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -311,7 +311,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger consumes(MediaType...value) {
-		setConsumes(toSet(value, MediaType.class));
+		setConsumes(setBuilder(MediaType.class).sparse().add(value).build());
 		return this;
 	}
 
@@ -327,7 +327,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger consumes(String...value) {
-		setConsumes(toSet(value, MediaType.class));
+		setConsumes(setBuilder(MediaType.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -373,7 +373,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addDefinitions(Map<String,OMap> values) {
-		definitions = addToMap(definitions, values);
+		definitions = mapBuilder(definitions).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -388,7 +388,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger definition(String name, OMap schema) {
-		definitions = addToMap(definitions, name, schema);
+		definitions = mapBuilder(definitions).sparse().add(name, schema).build();
 		return this;
 	}
 
@@ -430,7 +430,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger definitions(String json) {
-		setDefinitions(toMap(json, String.class, OMap.class));
+		setDefinitions(mapBuilder(String.class,OMap.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -698,7 +698,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addParameters(Map<String,ParameterInfo> values) {
-		parameters = addToMap(parameters, values);
+		parameters = mapBuilder(parameters).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -713,7 +713,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger parameter(String name, ParameterInfo parameter) {
-		parameters = addToMap(parameters, name, parameter);
+		parameters = mapBuilder(parameters).sparse().add(name, parameter).build();
 		return this;
 	}
 
@@ -752,7 +752,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger parameters(String json) {
-		setParameters(toMap(json, String.class, ParameterInfo.class));
+		setParameters(mapBuilder(String.class,ParameterInfo.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -783,7 +783,7 @@ public class Swagger extends SwaggerElement {
 	 * 	<br>Property value is required.
 	 */
 	public void setPaths(Map<String,OperationMap> value) {
-		paths = newSortedMap(value, PATH_COMPARATOR);
+		paths = mapBuilder(String.class,OperationMap.class).sparse().sorted(PATH_COMPARATOR).addAll(value).build();
 	}
 
 	/**
@@ -798,7 +798,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addPaths(Map<String,OperationMap> values) {
-		paths = addToSortedMap(paths, values, PATH_COMPARATOR);
+		paths = mapBuilder(paths).sparse().sorted(PATH_COMPARATOR).addAll(values).build();
 		return this;
 	}
 
@@ -862,9 +862,8 @@ public class Swagger extends SwaggerElement {
 	 * 	The values to set on this property as JSON.
 	 * @return This object (for method chaining).
 	 */
-	@SuppressWarnings({ "unchecked", "rawtypes" })
 	public Swagger paths(String json) {
-		setPaths(addToMap(new TreeMap(PATH_COMPARATOR), new Object[]{json}, String.class, Map.class, String.class, Operation.class));
+		setPaths(mapBuilder(String.class,OperationMap.class).sparse().sorted(PATH_COMPARATOR).addJson(json).build());
 		return this;
 	}
 
@@ -912,7 +911,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addProduces(Collection<MediaType> values) {
-		produces = addToSet(produces, values);
+		produces = setBuilder(produces).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -954,7 +953,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger produces(MediaType...value) {
-		setProduces(toSet(value, MediaType.class));
+		setProduces(setBuilder(MediaType.class).sparse().add(value).build());
 		return this;
 	}
 
@@ -970,7 +969,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger produces(String...value) {
-		setProduces(toSet(value, MediaType.class));
+		setProduces(setBuilder(MediaType.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -1016,7 +1015,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addResponses(Map<String,ResponseInfo> values) {
-		responses = addToMap(responses, values);
+		responses = mapBuilder(responses).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -1031,7 +1030,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger response(String name, ResponseInfo response) {
-		responses = addToMap(responses, name, response);
+		responses = mapBuilder(responses).sparse().add(name, response).build();
 		return this;
 	}
 
@@ -1073,7 +1072,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger responses(String json) {
-		setResponses(toMap(json, String.class, ResponseInfo.class));
+		setResponses(mapBuilder(String.class,ResponseInfo.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -1133,7 +1132,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addSchemes(Collection<String> values) {
-		schemes = addToSet(schemes, values);
+		schemes = setBuilder(schemes).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -1176,7 +1175,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger schemes(String...value) {
-		setSchemes(toSet(value, String.class));
+		setSchemes(setBuilder(String.class).sparse().addJson(value).build());
 		return this;
 	}
 
@@ -1222,7 +1221,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addSecurity(Collection<Map<String,List<String>>> values) {
-		security = addToList(security, values);
+		security = listBuilder(security).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -1282,7 +1281,7 @@ public class Swagger extends SwaggerElement {
 	 */
 	@SuppressWarnings({ "unchecked", "rawtypes" })
 	public Swagger security(String json) {
-		setSecurity((List)toList(json, Map.class, String.class, List.class, String.class));
+		setSecurity((List)listBuilder(Map.class,String.class,List.class,String.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -1328,7 +1327,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addSecurityDefinitions(Map<String,SecurityScheme> values) {
-		securityDefinitions = addToMap(securityDefinitions, values);
+		securityDefinitions = mapBuilder(securityDefinitions).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -1343,7 +1342,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger securityDefinition(String name, SecurityScheme securityScheme) {
-		securityDefinitions = addToMap(securityDefinitions, name, securityScheme);
+		securityDefinitions = mapBuilder(securityDefinitions).sparse().add(name, securityScheme).build();
 		return this;
 	}
 
@@ -1385,7 +1384,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger securityDefinitions(String json) {
-		setSecurityDefinitions(toMap(json, String.class, SecurityScheme.class));
+		setSecurityDefinitions(mapBuilder(String.class,SecurityScheme.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -1497,7 +1496,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger addTags(Collection<Tag> values) {
-		tags = addToSet(tags, values);
+		tags = setBuilder(tags).sparse().addAll(values).build();
 		return this;
 	}
 
@@ -1539,7 +1538,7 @@ public class Swagger extends SwaggerElement {
 	 * @return This object (for method chaining).
 	 */
 	public Swagger tags(String json) {
-		setTags(toSet(json, Tag.class));
+		setTags(setBuilder(Tag.class).sparse().addJson(json).build());
 		return this;
 	}
 
@@ -1700,20 +1699,20 @@ public class Swagger extends SwaggerElement {
 			return this;
 		switch (property) {
 			case "basePath": return basePath(stringify(value));
-			case "consumes": return consumes(toList(value, MediaType.class));
-			case "definitions": return definitions(toMap(value, String.class, OMap.class));
+			case "consumes": return consumes(listBuilder(MediaType.class).sparse().addAny(value).build());
+			case "definitions": return definitions(mapBuilder(String.class,OMap.class).sparse().addAny(value).build());
 			case "externalDocs": return externalDocs(toType(value, ExternalDocumentation.class));
 			case "host": return host(stringify(value));
 			case "info": return info(toType(value, Info.class));
-			case "parameters": return parameters(toMap(value, String.class, ParameterInfo.class));
-			case "paths": return paths(toMap(value, String.class, OperationMap.class));
-			case "produces": return produces(toList(value, MediaType.class));
-			case "responses": return responses(toMap(value, String.class, ResponseInfo.class));
-			case "schemes": return schemes(toList(value, String.class));
-			case "security": return security((List)toList(value, Map.class, String.class, List.class, String.class));
-			case "securityDefinitions": return securityDefinitions(toMap(value, String.class, SecurityScheme.class));
+			case "parameters": return parameters(mapBuilder(String.class,ParameterInfo.class).sparse().addAny(value).build());
+			case "paths": return paths(mapBuilder(String.class,OperationMap.class).sparse().addAny(value).build());
+			case "produces": return produces(listBuilder(MediaType.class).sparse().addAny(value).build());
+			case "responses": return responses(mapBuilder(String.class,ResponseInfo.class).sparse().addAny(value).build());
+			case "schemes": return schemes(listBuilder(String.class).sparse().addAny(value).build());
+			case "security": return security((List)listBuilder(Map.class,String.class,List.class,String.class).sparse().addAny(value).build());
+			case "securityDefinitions": return securityDefinitions(mapBuilder(String.class,SecurityScheme.class).sparse().addAny(value).build());
 			case "swagger": return swagger(stringify(value));
-			case "tags": return tags(toList(value, Tag.class));
+			case "tags": return tags(listBuilder(Tag.class).sparse().addAny(value).build());
 			default:
 				super.set(property, value);
 				return this;
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
index d952730..25892ed 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
@@ -12,35 +12,15 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.internal;
 
-import static org.apache.juneau.internal.StringUtils.*;
-import static org.apache.juneau.internal.ConverterUtils.*;
-
 import java.lang.reflect.*;
 import java.util.*;
 
-import org.apache.juneau.*;
-import org.apache.juneau.collections.*;
-import org.apache.juneau.parser.*;
-
 /**
  * Utility methods for collections.
  */
 public final class CollectionUtils {
 
 	/**
-	 * Add a value to a list if the value is not null.
-	 *
-	 * @param l The list to add to.
-	 * @param o The element to add.
-	 * @return The same list.
-	 */
-	public static <T> List<T> addIfNotNull(List<T> l, T o) {
-		if (o != null)
-			l.add(o);
-		return l;
-	}
-
-	/**
 	 * Returns an iterable over the specified enumeration.
 	 *
 	 * @param e The collection to iterate over.
@@ -107,238 +87,23 @@ public final class CollectionUtils {
 	}
 
 	/**
-	 * Adds a set of values to an existing list.
-	 *
-	 * @param appendTo
-	 * 	The list to append to.
-	 * 	<br>If <jk>null</jk>, a new {@link ArrayList} will be created.
-	 * @param values The values to add.
-	 * @param type The data type of the elements.
-	 * @param args The generic type arguments of the data type.
-	 * @return The converted value, or <jk>null</jk> if the input was null.
-	 */
-	public static <T> List<T> addToList(List<T> appendTo, Object[] values, Class<T> type, Type...args) {
-		if (values == null)
-			return appendTo;
-		try {
-			List<T> l = appendTo;
-			if (appendTo == null)
-				l = new ArrayList<>();
-			for (Object o : values) {
-				if (o != null) {
-					if (isJsonArray(o, false)) {
-						for (Object o2 : new OList(o.toString()))
-							l.add(toType(o2, type, args));
-					} else if (o instanceof Collection) {
-						for (Object o2 : (Collection<?>)o)
-							l.add(toType(o2, type, args));
-					} else if (o.getClass().isArray()) {
-						for (int i = 0; i < Array.getLength(o); i++)
-							l.add(toType(Array.get(o, i), type, args));
-					} else {
-						l.add(toType(o, type, args));
-					}
-				}
-			}
-			return l;
-		} catch (ParseException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	/**
-	 * Adds a set of values to an existing set.
-	 *
-	 * @param appendTo
-	 * 	The set to append to.
-	 * 	<br>If <jk>null</jk>, a new {@link LinkedHashSet} will be created.
-	 * @param values The values to add.
-	 * @param type The data type of the elements.
-	 * @param args The generic type arguments of the data type.
-	 * @return The converted value, or <jk>null</jk> if the input was null.
-	 */
-	public static <T> Set<T> addToSet(Set<T> appendTo, Object[] values, Class<T> type, Type...args) {
-		if (values == null)
-			return appendTo;
-		try {
-			Set<T> l = appendTo;
-			if (appendTo == null)
-				l = new LinkedHashSet<>();
-			for (Object o : values) {
-				if (o != null) {
-					if (isJsonArray(o, false)) {
-						for (Object o2 : new OList(o.toString()))
-							l.add(toType(o2, type, args));
-					} else if (o instanceof Collection) {
-						for (Object o2 : (Collection<?>)o)
-							l.add(toType(o2, type, args));
-					} else if (o.getClass().isArray()) {
-						for (int i = 0; i < Array.getLength(o); i++)
-							l.add(toType(Array.get(o, i), type, args));
-					} else {
-						l.add(toType(o, type, args));
-					}
-				}
-			}
-			return l;
-		} catch (ParseException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	/**
-	 * Creates a new list from the specified values.
-	 *
-	 * @param values The values to add.
-	 * @param type The data type of the elements.
-	 * @param args The generic type arguments of the data type.
-	 * @return The converted value, or <jk>null</jk> if the input was null.
-	 */
-	public static <T> List<T> toList(Object[] values, Class<T> type, Type...args) {
-		return addToList(new ArrayList<T>(), values, type, args);
-	}
-
-	/**
-	 * Creates a new list from the specified values.
-	 *
-	 * @param value The value to add.
-	 * @param type The data type of the elements.
-	 * @param args The generic type arguments of the data type.
-	 * @return The converted value, or <jk>null</jk> if the input was null.
-	 */
-	public static <T> List<T> toList(Object value, Class<T> type, Type...args) {
-		return addToList(new ArrayList<T>(), new Object[]{value}, type, args);
-	}
-
-	/**
-	 * Creates a new set from the specified values.
-	 *
-	 * @param values The values to add.
-	 * @param type The data type of the elements.
-	 * @param args The generic type arguments of the data type.
-	 * @return The converted value, or <jk>null</jk> if the input was null.
-	 */
-	public static <T> Set<T> toSet(Object[] values, Class<T> type, Type...args) {
-		return addToSet(new LinkedHashSet<T>(), values, type, args);
-	}
-
-	/**
-	 * Creates a new set from the specified value.
-	 *
-	 * @param value The value to add.
-	 * @param type The data type of the elements.
-	 * @param args The generic type arguments of the data type.
-	 * @return The converted value, or <jk>null</jk> if the input was null.
-	 */
-	public static <T> Set<T> toSet(Object value, Class<T> type, Type...args) {
-		return addToSet(new LinkedHashSet<T>(), new Object[]{value}, type, args);
-	}
-
-	/**
-	 * Adds a set of values to an existing map.
-	 *
-	 * @param appendTo
-	 * 	The map to append to.
-	 * 	<br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
-	 * @param values The values to add.
-	 * @param keyType The data type of the keys.
-	 * @param valueType The data type of the values.
-	 * @param valueTypeArgs The generic type arguments of the data type of the values.
-	 * @return The converted value, or <jk>null</jk> if the input was null.
-	 */
-	@SuppressWarnings("unchecked")
-	public static <K,V> Map<K,V> addToMap(Map<K,V> appendTo, Object[] values, Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) {
-		if (values == null)
-			return appendTo;
-		try {
-			Map<K,V> m = appendTo;
-			if (m == null)
-				m = new LinkedHashMap<>();
-			for (Object o : values) {
-				if (o != null) {
-					if (isJsonObject(o, false)) {
-						for (Map.Entry<String,Object> e : OMap.ofJson(o.toString()).entrySet())
-							m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs));
-					} else if (o instanceof Map) {
-						for (Map.Entry<Object,Object> e : ((Map<Object,Object>)o).entrySet())
-							m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs));
-					} else {
-						throw new BasicRuntimeException("Invalid object type {0} passed to addToMap()", o.getClass().getName());
-					}
-				}
-			}
-			return m.isEmpty() ? null : m;
-		} catch (ParseException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-	/**
-	 * Creates a new map from the specified values.
-	 *
-	 * @param values The values to add.
-	 * @param keyType The data type of the keys.
-	 * @param valueType The data type of the values.
-	 * @param valueTypeArgs The generic type arguments of the data type of the values.
-	 * @return The converted value, or <jk>null</jk> if the input was null.
-	 */
-	public static <K,V> Map<K,V> toMap(Object[] values, Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) {
-		return addToMap(new LinkedHashMap<>(), values, keyType, valueType, valueTypeArgs);
-	}
-
-	/**
-	 * Creates a new map from the specified value.
-	 *
-	 * @param values The values to add.
-	 * @param keyType The data type of the keys.
-	 * @param valueType The data type of the values.
-	 * @param valueTypeArgs The generic type arguments of the data type of the values.
-	 * @return The converted value, or <jk>null</jk> if the input was null.
-	 */
-	public static <K,V> Map<K,V> toMap(Object values, Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) {
-		return addToMap(new LinkedHashMap<>(), new Object[]{values}, keyType, valueType, valueTypeArgs);
-	}
-
-	/**
 	 * Creates a new list from the specified collection.
 	 *
 	 * @param val The value to copy from.
 	 * @return A new {@link ArrayList}, or <jk>null</jk> if the input was null.
 	 */
-	public static <T> AList<T> newList(Collection<T> val) {
-		return AList.nullable(val);
+	public static <T> List<T> newList(Collection<T> val) {
+		return val == null ? null : new ArrayList<>(val);
 	}
 
 	/**
-	 * Creates a new list from the specified array.
-	 *
-	 * @param val The value to copy from.
-	 * @return A new {@link ArrayList}, or <jk>null</jk> if the input was null.
-	 */
-	@SafeVarargs
-	public static <T> AList<T> newList(T...val) {
-		return AList.of(val);
-	}
-
-	/**
-	 * Creates a new unmodifiable list from the specified collection.
-	 *
-	 * @param val The value to copy from.
-	 * @return A new {@link ArrayList}, or <jk>null</jk> if the input was null.
-	 */
-	public static <T> List<T> newUnmodifiableList(Collection<T> val) {
-		return Collections.unmodifiableList(newList(val));
-	}
-
-	/**
-	 * Creates a new unmodifiable list from the specified array.
+	 * Creates a new list from the specified collection.
 	 *
 	 * @param val The value to copy from.
 	 * @return A new {@link ArrayList}, or <jk>null</jk> if the input was null.
 	 */
-	@SafeVarargs
-	public static <T> List<T> newUnmodifiableList(T...val) {
-		return Collections.unmodifiableList(newList(val));
+	public static <T> List<T> newList(List<T> val) {
+		return val == null ? null : new ArrayList<>(val);
 	}
 
 	/**
@@ -347,78 +112,18 @@ public final class CollectionUtils {
 	 * @param val The value to copy from.
 	 * @return A new {@link LinkedHashSet}, or <jk>null</jk> if the input was null.
 	 */
-	public static <T> ASet<T> newSet(Collection<T> val) {
-		return ASet.nullable(val);
+	public static <T> Set<T> newSet(Collection<T> val) {
+		return val == null ? null : new LinkedHashSet<>(val);
 	}
 
 	/**
-	 * Creates a new set from the specified array.
-	 *
-	 * @param val The value to copy from.
-	 * @return A new {@link LinkedHashSet}, or <jk>null</jk> if the input was null.
-	 */
-	@SafeVarargs
-	public static <T> ASet<T> newSet(T...val) {
-		return ASet.of(val);
-	}
-
-	/**
-	 * Creates a new unmodifiable set from the specified collection.
-	 *
-	 * @param val The value to copy from.
-	 * @return A new {@link LinkedHashSet}, or <jk>null</jk> if the input was null.
-	 */
-	public static <T> Set<T> newUnmodifiableSet(Collection<T> val) {
-		return Collections.unmodifiableSet(newSet(val));
-	}
-
-	/**
-	 * Creates a new unmodifiable set from the specified array.
+	 * Creates a new set from the specified collection.
 	 *
 	 * @param val The value to copy from.
 	 * @return A new {@link LinkedHashSet}, or <jk>null</jk> if the input was null.
 	 */
-	@SafeVarargs
-	public static <T> Set<T> newUnmodifiableSet(T...val) {
-		return Collections.unmodifiableSet(newSet(val));
-	}
-
-	/**
-	 * Copies the specified values into an existing list.
-	 *
-	 * @param l
-	 * 	The list to add to.
-	 * 	<br>If <jk>null</jk>, a new {@link ArrayList} will be created.
-	 * @param val The values to add.
-	 * @return The list with values copied into it.
-	 */
-	public static <T> List<T> addToList(List<T> l, Collection<T> val) {
-		if (val != null) {
-			if (l == null)
-				l = new ArrayList<>(val);
-			else
-				l.addAll(val);
-		}
-		return l;
-	}
-
-	/**
-	 * Copies the specified values into an existing list.
-	 *
-	 * @param l
-	 * 	The list to add to.
-	 * 	<br>If <jk>null</jk>, a new {@link ArrayList} will be created.
-	 * @param val The values to add.
-	 * @return The list with values copied into it.
-	 */
-	public static <T> Set<T> addToSet(Set<T> l, Collection<T> val) {
-		if (val != null) {
-			if (l == null)
-				l = new LinkedHashSet<>(val);
-			else
-				l.addAll(val);
-		}
-		return l;
+	public static <T> Set<T> newSet(Set<T> val) {
+		return val == null ? null : new LinkedHashSet<>(val);
 	}
 
 	/**
@@ -428,153 +133,73 @@ public final class CollectionUtils {
 	 * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null.
 	 */
 	public static <K,V> Map<K,V> newMap(Map<K,V> val) {
-		if (val == null)
-			return null;
-		return new LinkedHashMap<>(val);
-	}
-
-	/**
-	 * Creates a new unmodifiable map from the specified map.
-	 *
-	 * @param val The value to copy from.
-	 * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null.
-	 */
-	public static <K,V> Map<K,V> newUnmodifiableMap(Map<K,V> val) {
-		return Collections.unmodifiableMap(newMap(val));
-	}
-
-	/**
-	 * Copies the specified values into an existing map.
-	 *
-	 * @param m
-	 * 	The map to add to.
-	 * 	<br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
-	 * @param val The values to add.
-	 * @return The list with values copied into it.
-	 */
-	public static <K,V> Map<K,V> addToMap(Map<K,V> m, Map<K,V> val) {
-		if (val != null) {
-			if (m == null)
-				m = new LinkedHashMap<>(val);
-			else
-				m.putAll(val);
-		}
-		return m;
+		return val == null ? null : new LinkedHashMap<>(val);
 	}
 
 	/**
-	 * Adds a single entry into an existing map.
+	 * Instantiates a new builder on top of the specified map.
 	 *
-	 * @param m
-	 * 	The map to add to.
-	 * 	<br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
-	 * @param key The entry key.
-	 * @param value The entry value.
-	 * @return The list with values copied into it.
+	 * @param addTo The map to add to.
+	 * @return A new builder on top of the specified map.
 	 */
-	public static <K,V> Map<K,V> addToMap(Map<K,V> m, K key, V value) {
-		if (m == null)
-			m = new LinkedHashMap<>();
-		m.put(key, value);
-		return m;
+	public static <K,V> MapBuilder<K,V> mapBuilder(Map<K,V> addTo) {
+		return new MapBuilder<>(addTo);
 	}
 
 	/**
-	 * Creates a new map from the specified map.
+	 * Instantiates a new builder of the specified map type.
 	 *
-	 * @param val The value to copy from.
-	 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering.
-	 * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null.
+	 * @param keyType The key type.
+	 * @param valueType The value type.
+	 * @param valueTypeArgs The value type args.
+	 * @return A new builder on top of the specified map.
 	 */
-	public static <K,V> Map<K,V> newSortedMap(Map<K,V> val, Comparator<K> comparator) {
-		if (val == null)
-			return null;
-		Map<K,V> m = new TreeMap<>(comparator);
-		m.putAll(val);
-		return m;
+	public static <K,V> MapBuilder<K,V> mapBuilder(Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) {
+		return new MapBuilder<>(keyType, valueType, valueTypeArgs);
 	}
 
 	/**
-	 * Creates a case-insensitive ordered set out of the specified string values.
+	 * Instantiates a new builder on top of the specified list.
 	 *
-	 * @param values The values to populate the set with.
-	 * @return A new ordered set.
+	 * @param addTo The list to add to.
+	 * @return A new builder on top of the specified list.
 	 */
-	public static Set<String> newSortedCaseInsensitiveSet(String...values) {
-		Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) {
-			private static final long serialVersionUID = 1L;
-			@Override
-			public boolean contains(Object v) {
-				return v == null ? false : super.contains(v);
-			}
-		};
-		for (String v : values)
-			if (v != null)
-				s.add(v);
-		return s;
+	public static <E> ListBuilder<E> listBuilder(List<E> addTo) {
+		return new ListBuilder<>(addTo);
 	}
 
 	/**
-	 * Creates a case-insensitive ordered set out of the specified string values.
+	 * Instantiates a new builder of the specified list type.
 	 *
-	 * @param values
-	 * 	A comma-delimited list of the values to populate the set with.
-	 * @return A new ordered set.
+	 * @param elementType The element type.
+	 * @param elementTypeArgs The element type args.
+	 * @return A new builder on top of the specified list.
 	 */
-	public static Set<String> newSortedCaseInsensitiveSet(String values) {
-		return newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values)));
+	public static <E> ListBuilder<E> listBuilder(Class<E> elementType, Type...elementTypeArgs) {
+		return new ListBuilder<>(elementType, elementTypeArgs);
 	}
 
 	/**
-	 * Same as {@link #newSortedCaseInsensitiveSet(String)} but makes the set unmodifiable.
+	 * Instantiates a new builder on top of the specified set.
 	 *
-	 * @param values
-	 * 	A comma-delimited list of the values to populate the set with.
-	 * @return A new ordered set.
+	 * @param addTo The set to add to.
+	 * @return A new builder on top of the specified set.
 	 */
-	public static Set<String> newUnmodifiableSortedCaseInsensitiveSet(String values) {
-		return Collections.unmodifiableSet(newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values))));
+	public static <E> SetBuilder<E> setBuilder(Set<E> addTo) {
+		return new SetBuilder<>(addTo);
 	}
 
 	/**
-	 * Copies the specified values into an existing map.
+	 * Instantiates a new builder of the specified set.
 	 *
-	 * @param m
-	 * 	The map to add to.
-	 * 	<br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
-	 * @param val The values to add.
-	 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering.
-	 * @return The list with values copied into it.
+	 * @param elementType The element type.
+	 * @param elementTypeArgs The element type args.
+	 * @return A new builder on top of the specified set.
 	 */
-	public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, Map<K,V> val, Comparator<K> comparator) {
-		if (val != null) {
-			if (m == null) {
-				m = new TreeMap<>(comparator);
-				m.putAll(val);
-			} else {
-				m.putAll(val);
-			}
-		}
-		return m;
+	public static <E> SetBuilder<E> setBuilder(Class<E> elementType, Type...elementTypeArgs) {
+		return new SetBuilder<>(elementType, elementTypeArgs);
 	}
 
-	/**
-	 * Adds a single entry into an existing map.
-	 *
-	 * @param m
-	 * 	The map to add to.
-	 * 	<br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
-	 * @param key The entry key.
-	 * @param value The entry value.
-	 * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering.
-	 * @return The list with values copied into it.
-	 */
-	public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, K key, V value, Comparator<K> comparator) {
-		if (m == null)
-			m = new TreeMap<>(comparator);
-		m.put(key, value);
-		return m;
-	}
 
 	/**
 	 * Simple passthrough to {@link Collections#emptySet()}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ListBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ListBuilder.java
new file mode 100644
index 0000000..1cb5791
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ListBuilder.java
@@ -0,0 +1,234 @@
+// ***************************************************************************************************************************
+// * 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.internal;
+
+import static org.apache.juneau.internal.ConverterUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.collections.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * Builder for lists.
+ *
+ * @param <E> Element type.
+ */
+public class ListBuilder<E> {
+
+	private List<E> list;
+	private boolean unmodifiable = false, sparse = false;
+	private Comparator<E> comparator;
+
+	private Class<E> elementType;
+	private Type[] elementTypeArgs;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param elementType The element type.
+	 * @param elementTypeArgs The element type generic arguments if there are any.
+	 */
+	public ListBuilder(Class<E> elementType, Type...elementTypeArgs) {
+		this.elementType = elementType;
+		this.elementTypeArgs = elementTypeArgs;
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param addTo The list to add to.
+	 */
+	public ListBuilder(List<E> addTo) {
+		this.list = addTo;
+	}
+
+	/**
+	 * Builds the list.
+	 *
+	 * @return A list conforming to the settings on this builder.
+	 */
+	public List<E> build() {
+		if (sparse) {
+			if (list != null && list.isEmpty())
+				list = null;
+		} else {
+			if (list == null)
+				list = new ArrayList<>(0);
+		}
+		if (list != null) {
+			if (comparator != null)
+				Collections.sort(list, comparator);
+			if (unmodifiable)
+				list = Collections.unmodifiableList(list);
+		}
+ 		return list;
+	}
+
+	/**
+	 * When specified, the {@link #build()} method will return <jk>null</jk> if the list is empty.
+	 *
+	 * <p>
+	 * Otherwise {@link #build()} will never return <jk>null</jk>.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public ListBuilder<E> sparse() {
+		this.sparse = true;
+		return this;
+	}
+
+	/**
+	 * When specified, {@link #build()} will return an unmodifiable list.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public ListBuilder<E> unmodifiable() {
+		this.unmodifiable = true;
+		return this;
+	}
+
+	/**
+	 * Forces the existing list to be copied instead of appended to.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public ListBuilder<E> copy() {
+		if (list != null)
+			list = new ArrayList<>(list);
+		return this;
+	}
+
+	/**
+	 * Sorts the contents of the list.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	@SuppressWarnings("unchecked")
+	public ListBuilder<E> sorted() {
+		return sorted((Comparator<E>)Comparator.naturalOrder());
+	}
+
+	/**
+	 * Sorts the contents of the list using the specified comparator.
+	 *
+	 * @param comparator The comparator to use for sorting.
+	 * @return This object (for method chaining).
+	 */
+	public ListBuilder<E> sorted(Comparator<E> comparator) {
+		this.comparator = comparator;
+		return this;
+	}
+
+	/**
+	 * Appends the contents of the specified collection into this list.
+	 *
+	 * <p>
+	 * This is a no-op if the value is <jk>null</jk>.
+	 *
+	 * @param value The collection to add to this list.
+	 * @return This object (for method chaining).
+	 */
+	public ListBuilder<E> addAll(Collection<E> value) {
+		if (value != null) {
+			if (list == null)
+				list = new LinkedList<>(value);
+			else
+				list.addAll(value);
+		}
+		return this;
+	}
+
+	/**
+	 * Adds a single value to this list.
+	 *
+	 * @param value The value to add to this list.
+	 * @return This object (for method chaining).
+	 */
+	public ListBuilder<E> add(E value) {
+		if (list == null)
+			list = new ArrayList<>();
+		list.add(value);
+		return this;
+	}
+
+	/**
+	 * Adds multiple values to this list.
+	 *
+	 * @param values The values to add to this list.
+	 * @return This object (for method chaining).
+	 */
+	@SuppressWarnings("unchecked")
+	public ListBuilder<E> add(E...values) {
+		for (E v : values)
+			add(v);
+		return this;
+	}
+
+	/**
+	 * Adds entries to this list via JSON array strings.
+	 *
+	 * @param values The JSON array strings to parse and add to this list.
+	 * @return This object (for method chaining).
+	 */
+	public ListBuilder<E> addJson(String...values) {
+		return addAny((Object[])values);
+	}
+
+	/**
+	 * Adds arbitrary values to this list.
+	 *
+	 * <p>
+	 * Objects can be any of the following:
+	 * <ul>
+	 * 	<li>The same type or convertible to the element type of this list.
+	 * 	<li>Collections or arrays of anything on this list.
+	 * 	<li>JSON array strings parsed and convertible to the element type of this list.
+	 * </ul>
+	 *
+	 * @param values The values to add.
+	 * @return This object (for method chaining).
+	 */
+	@SuppressWarnings("unchecked")
+	public ListBuilder<E> addAny(Object...values) {
+		if (elementType == null)
+			throw new RuntimeException("Unknown element type.  Cannot use this method.");
+		try {
+			if (values != null) {
+				for (Object o : values) {
+					if (o != null) {
+						if (o instanceof Collection) {
+							for (Object o2 : (Collection<?>)o)
+								addAny(o2);
+						} else if (o.getClass().isArray()) {
+							for (int i = 0; i < Array.getLength(o); i++)
+								addAny(Array.get(o, i));
+						} else if (isJsonArray(o, false)) {
+							for (Object o2 : new OList(o.toString()))
+								addAny(o2);
+						} else if (elementType.isInstance(o)) {
+							add((E)o);
+						} else {
+							add(toType(o, elementType, elementTypeArgs));
+						}
+					}
+				}
+			}
+		} catch (ParseException e) {
+			throw new RuntimeException(e);
+		}
+		return this;
+	}
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/MapBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/MapBuilder.java
new file mode 100644
index 0000000..d27ddb4
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/MapBuilder.java
@@ -0,0 +1,222 @@
+// ***************************************************************************************************************************
+// * 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.internal;
+
+import static org.apache.juneau.internal.ConverterUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.collections.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * Builder for maps.
+ *
+ * @param <K> Key type.
+ * @param <V> Value type.
+ */
+public class MapBuilder<K,V> {
+
+	private Map<K,V> map;
+	private boolean unmodifiable = false, sparse = false;
+	private Comparator<K> comparator = null;
+
+	private Class<K> keyType;
+	private Class<V> valueType;
+	private Type[] valueTypeArgs;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param keyType The key type.
+	 * @param valueType The value type.
+	 * @param valueTypeArgs The value type generic arguments if there are any.
+	 */
+	public MapBuilder(Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) {
+		this.keyType = keyType;
+		this.valueType = valueType;
+		this.valueTypeArgs = valueTypeArgs;
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param addTo The map to add to.
+	 */
+	public MapBuilder(Map<K,V> addTo) {
+		this.map = addTo;
+	}
+
+	/**
+	 * Builds the map.
+	 *
+	 * @return A map conforming to the settings on this builder.
+	 */
+	public Map<K,V> build() {
+		if (sparse) {
+			if (map != null && map.isEmpty())
+				map = null;
+		} else {
+			if (map == null)
+				map = new LinkedHashMap<>();
+		}
+		if (map != null) {
+			if (comparator != null) {
+				Map<K,V> m2 = new TreeMap<>(comparator);
+				m2.putAll(map);
+				map = m2;
+			}
+			if (unmodifiable)
+				map = Collections.unmodifiableMap(map);
+		}
+		return map;
+	}
+
+	/**
+	 * When specified, the {@link #build()} method will return <jk>null</jk> if the map is empty.
+	 *
+	 * <p>
+	 * Otherwise {@link #build()} will never return <jk>null</jk>.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public MapBuilder<K,V> sparse() {
+		this.sparse = true;
+		return this;
+	}
+
+	/**
+	 * When specified, {@link #build()} will return an unmodifiable map.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public MapBuilder<K,V> unmodifiable() {
+		this.unmodifiable = true;
+		return this;
+	}
+
+	/**
+	 * Forces the existing set to be copied instead of appended to.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public MapBuilder<K,V> copy() {
+		if (map != null)
+			map = new LinkedHashMap<>(map);
+		return this;
+	}
+
+	/**
+	 * Converts the set into a {@link SortedMap}.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	@SuppressWarnings("unchecked")
+	public MapBuilder<K,V> sorted() {
+		return sorted((Comparator<K>)Comparator.naturalOrder());
+	}
+
+	/**
+	 * Converts the set into a {@link SortedMap} using the specified comparator.
+	 *
+	 * @param comparator The comparator to use for sorting.
+	 * @return This object (for method chaining).
+	 */
+	public MapBuilder<K,V> sorted(Comparator<K> comparator) {
+		this.comparator = comparator;
+		return this;
+	}
+
+	/**
+	 * Appends the contents of the specified map into this map.
+	 *
+	 * <p>
+	 * This is a no-op if the value is <jk>null</jk>.
+	 *
+	 * @param value The map to add to this map.
+	 * @return This object (for method chaining).
+	 */
+	public MapBuilder<K,V> addAll(Map<K,V> value) {
+		if (value != null) {
+			if (map == null)
+				map = new LinkedHashMap<>(value);
+			else
+				map.putAll(value);
+		}
+		return this;
+	}
+
+	/**
+	 * Adds a single entry to this map.
+	 *
+	 * @param key The map key.
+	 * @param value The map value.
+	 * @return This object (for method chaining).
+	 */
+	public MapBuilder<K,V> add(K key, V value) {
+		if (map == null)
+			map = new LinkedHashMap<>();
+		map.put(key, value);
+		return this;
+	}
+
+	/**
+	 * Adds entries to this list via JSON object strings.
+	 *
+	 * @param values The JSON object strings to parse and add to this list.
+	 * @return This object (for method chaining).
+	 */
+	public MapBuilder<K,V> addJson(String...values) {
+		return addAny((Object[])values);
+	}
+
+	/**
+	 * Adds arbitrary values to this list.
+	 *
+	 * <p>
+	 * Objects can be any of the following:
+	 * <ul>
+	 * 	<li>Maps of key/value types convertible to the key/value types of this map.
+	 * 	<li>JSON object strings parsed and convertible to the key/value types of this map.
+	 * </ul>
+	 *
+	 * @param values The values to add.
+	 * @return This object (for method chaining).
+	 */
+	@SuppressWarnings("unchecked")
+	public MapBuilder<K,V> addAny(Object...values) {
+		if (keyType == null || valueType == null)
+			throw new RuntimeException("Unknown key and value types.  Cannot use this method.");
+		try {
+			for (Object o : values) {
+				if (o != null) {
+					if (o instanceof Map) {
+						for (Map.Entry<Object,Object> e : ((Map<Object,Object>)o).entrySet())
+							add(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs));
+					} else if (isJsonObject(o, false)) {
+						for (Map.Entry<String,Object> e : OMap.ofJson(o.toString()).entrySet())
+							add(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs));
+					} else {
+						throw new BasicRuntimeException("Invalid object type {0} passed to addAny()", o.getClass().getName());
+					}
+				}
+			}
+		} catch (ParseException e) {
+			throw new RuntimeException(e);
+		}
+		return this;
+	}
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/SetBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/SetBuilder.java
new file mode 100644
index 0000000..add89ca
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/SetBuilder.java
@@ -0,0 +1,237 @@
+// ***************************************************************************************************************************
+// * 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.internal;
+
+import static org.apache.juneau.internal.ConverterUtils.*;
+import static org.apache.juneau.internal.StringUtils.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.collections.*;
+import org.apache.juneau.parser.*;
+
+/**
+ * Builder for sets.
+ *
+ * @param <E> Element type.
+ */
+public class SetBuilder<E> {
+
+	private Set<E> set;
+	private boolean unmodifiable, sparse;
+	private Comparator<E> comparator;
+
+	private Class<E> elementType;
+	private Type[] elementTypeArgs;
+
+	/**
+	 * Constructor.
+	 *
+	 * @param elementType The element type.
+	 * @param elementTypeArgs The element type generic arguments if there are any.
+	 */
+	public SetBuilder(Class<E> elementType, Type...elementTypeArgs) {
+		this.elementType = elementType;
+		this.elementTypeArgs = elementTypeArgs;
+	}
+
+	/**
+	 * Constructor.
+	 *
+	 * @param addTo The set to add to.
+	 */
+	public SetBuilder(Set<E> addTo) {
+		this.set = addTo;
+	}
+
+	/**
+	 * Builds the set.
+	 *
+	 * @return A set conforming to the settings on this builder.
+	 */
+	public Set<E> build() {
+		if (sparse) {
+			if (set != null && set.isEmpty())
+				set = null;
+		} else {
+			if (set == null)
+				set = new LinkedHashSet<>(0);
+		}
+		if (set != null) {
+			if (comparator != null) {
+				Set<E> s = new TreeSet<>(comparator);
+				s.addAll(set);
+				set = s;
+			}
+			if (unmodifiable)
+				set = Collections.unmodifiableSet(set);
+		}
+		return set;
+	}
+
+	/**
+	 * When specified, the {@link #build()} method will return <jk>null</jk> if the set is empty.
+	 *
+	 * <p>
+	 * Otherwise {@link #build()} will never return <jk>null</jk>.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public SetBuilder<E> sparse() {
+		this.sparse = true;
+		return this;
+	}
+
+	/**
+	 * When specified, {@link #build()} will return an unmodifiable set.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public SetBuilder<E> unmodifiable() {
+		this.unmodifiable = true;
+		return this;
+	}
+
+	/**
+	 * Forces the existing set to be copied instead of appended to.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public SetBuilder<E> copy() {
+		if (set != null)
+			set = new LinkedHashSet<>(set);
+		return this;
+	}
+
+	/**
+	 * Converts the set into a {@link SortedSet}.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	@SuppressWarnings("unchecked")
+	public SetBuilder<E> sorted() {
+		return sorted((Comparator<E>)Comparator.naturalOrder());
+	}
+
+	/**
+	 * Converts the set into a {@link SortedSet} using the specified comparator.
+	 *
+	 * @param comparator The comparator to use for sorting.
+	 * @return This object (for method chaining).
+	 */
+	public SetBuilder<E> sorted(Comparator<E> comparator) {
+		this.comparator = comparator;
+		return this;
+	}
+
+	/**
+	 * Appends the contents of the specified collection into this set.
+	 *
+	 * <p>
+	 * This is a no-op if the value is <jk>null</jk>.
+	 *
+	 * @param value The collection to add to this set.
+	 * @return This object (for method chaining).
+	 */
+	public SetBuilder<E> addAll(Collection<E> value) {
+		if (value != null) {
+			if (set == null)
+				set = new LinkedHashSet<>(value);
+			else
+				set.addAll(value);
+		}
+		return this;
+	}
+
+	/**
+	 * Adds a single value to this set.
+	 *
+	 * @param value The value to add to this set.
+	 * @return This object (for method chaining).
+	 */
+	public SetBuilder<E> add(E value) {
+		if (set == null)
+			set = new LinkedHashSet<>();
+		set.add(value);
+		return this;
+	}
+
+	/**
+	 * Adds multiple values to this set.
+	 *
+	 * @param values The values to add to this set.
+	 * @return This object (for method chaining).
+	 */
+	@SuppressWarnings("unchecked")
+	public SetBuilder<E> add(E...values) {
+		for (E v : values)
+			add(v);
+		return this;
+	}
+
+	/**
+	 * Adds entries to this set via JSON array strings.
+	 *
+	 * @param values The JSON array strings to parse and add to this set.
+	 * @return This object (for method chaining).
+	 */
+	public SetBuilder<E> addJson(String...values) {
+		return addAny((Object[])values);
+	}
+
+	/**
+	 * Adds arbitrary values to this set.
+	 *
+	 * <p>
+	 * Objects can be any of the following:
+	 * <ul>
+	 * 	<li>The same type or convertible to the element type of this set.
+	 * 	<li>Collections or arrays of anything on this set.
+	 * 	<li>JSON array strings parsed and convertible to the element type of this set.
+	 * </ul>
+	 *
+	 * @param values The values to add.
+	 * @return This object (for method chaining).
+	 */
+	@SuppressWarnings("unchecked")
+	public SetBuilder<E> addAny(Object...values) {
+		if (elementType == null)
+			throw new RuntimeException("Unknown element type.  Cannot use this method.");
+		try {
+			if (values != null) {
+				for (Object o : values) {
+					if (o != null) {
+						if (o instanceof Collection) {
+							for (Object o2 : (Collection<?>)o)
+								addAny(o2);
+						} else if (o.getClass().isArray()) {
+							for (int i = 0; i < Array.getLength(o); i++)
+								addAny(Array.get(o, i));
+						} else if (isJsonArray(o, false)) {
+							for (Object o2 : new OList(o.toString()))
+								addAny(o2);
+						} else if (elementType.isInstance(o)) {
+							add((E)o);
+						} else {
+							add(toType(o, elementType, elementTypeArgs));
+						}
+					}
+				}
+			}
+		} catch (ParseException e) {
+			throw new RuntimeException(e);
+		}
+		return this;
+	}
+}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/mstat/ThrownStats.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/mstat/ThrownStats.java
index 2d54151..eb224e4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/mstat/ThrownStats.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/mstat/ThrownStats.java
@@ -53,7 +53,7 @@ public class ThrownStats implements Cloneable {
 		this.guid = new Random().nextLong();
 		this.thrownClass = builder.throwable.getClass();
 		this.firstMessage = builder.throwable.getMessage();
-		this.stackTrace = newUnmodifiableList(builder.stackTrace);
+		this.stackTrace = listBuilder(builder.stackTrace).copy().unmodifiable().build();
 		this.causedBy = ofNullable(builder.causedBy);
 		this.hash = builder.hash;
 		this.count = new AtomicInteger(0);
@@ -69,7 +69,7 @@ public class ThrownStats implements Cloneable {
 		this.guid = x.guid;
 		this.thrownClass = x.thrownClass;
 		this.firstMessage = x.firstMessage;
-		this.stackTrace = newUnmodifiableList(x.stackTrace);
+		this.stackTrace = listBuilder(x.stackTrace).copy().unmodifiable().build();
 		this.causedBy = Optional.ofNullable(x.causedBy.isPresent() ? x.causedBy.get().clone() : null);
 		this.hash = x.hash;
 		this.count = new AtomicInteger(x.count.get());
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
index d5d4a8b..862ba2f 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
@@ -14,7 +14,6 @@ package org.apache.juneau.reflect;
 
 import static org.apache.juneau.internal.StringUtils.*;
 import static org.apache.juneau.reflect.ReflectFlags.*;
-import static org.apache.juneau.internal.CollectionUtils.*;
 import static org.apache.juneau.internal.ObjectUtils.*;
 
 import java.lang.annotation.*;
@@ -2479,6 +2478,12 @@ public final class ClassInfo {
 		}
 	}
 
+	private static <T> List<T> addIfNotNull(List<T> l, T o) {
+		if (o != null)
+			l.add(o);
+		return l;
+	}
+
 	//-----------------------------------------------------------------------------------------------------------------
 	// Other
 	//-----------------------------------------------------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index e5ec8ff..4887238 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -13,7 +13,6 @@
 package org.apache.juneau.rest;
 
 import static javax.servlet.http.HttpServletResponse.*;
-import static org.apache.juneau.internal.CollectionUtils.*;
 import static org.apache.juneau.internal.ObjectUtils.*;
 import static org.apache.juneau.internal.IOUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
@@ -51,6 +50,7 @@ import org.apache.juneau.http.*;
 import org.apache.juneau.http.annotation.Response;
 import org.apache.juneau.httppart.*;
 import org.apache.juneau.httppart.bean.*;
+import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.jsonschema.*;
 import org.apache.juneau.marshall.*;
@@ -3526,9 +3526,9 @@ public class RestContext extends BeanContext {
 			uriRelativity = getProperty(REST_uriRelativity, UriRelativity.class, UriRelativity.RESOURCE);
 
 			allowBodyParam = ! getBooleanProperty(REST_disableAllowBodyParam);
-			allowedHeaderParams = newUnmodifiableSortedCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedHeaderParams, "Accept,Content-Type"));
-			allowedMethodParams = newUnmodifiableSortedCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedMethodParams, "HEAD,OPTIONS"));
-			allowedMethodHeaders = newUnmodifiableSortedCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedMethodHeaders, ""));
+			allowedHeaderParams = newCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedHeaderParams, "Accept,Content-Type"));
+			allowedMethodParams = newCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedMethodParams, "HEAD,OPTIONS"));
+			allowedMethodHeaders = newCaseInsensitiveSet(getStringPropertyWithNone(REST_allowedMethodHeaders, ""));
 			renderResponseStackTraces = getBooleanProperty(REST_renderResponseStackTraces);
 			clientVersionHeader = getStringProperty(REST_clientVersionHeader, "X-Client-Version");
 
@@ -3580,6 +3580,19 @@ public class RestContext extends BeanContext {
 		return new RestOperationInvoker(m, findHookMethodParams(m, getBeanFactory()), getMethodExecStats(m));
 	}
 
+	private Set<String> newCaseInsensitiveSet(String value) {
+		Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) {
+			private static final long serialVersionUID = 1L;
+			@Override
+			public boolean contains(Object v) {
+				return v == null ? false : super.contains(v);
+			}
+		};
+		for (String v : StringUtils.split(value))
+			s.add(v);
+		return Collections.unmodifiableSet(s);
+	}
+
 	/**
 	 * Instantiates the bean factory for this REST resource.
 	 *
diff --git a/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/HeaderInfo_Test.java b/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/HeaderInfo_Test.java
index 0fa39f4..0066e23 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/HeaderInfo_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/HeaderInfo_Test.java
@@ -308,7 +308,7 @@ public class HeaderInfo_Test {
 		assertObject(t._enum()).isType(Set.class).asJson().is("['foo','bar']");
 
 		t._enum(new Object[0]);
-		assertObject(t._enum()).isType(Set.class).asJson().is("[]");
+		assertObject(t._enum()).isNull();
 
 		t._enum((Collection<Object>)null);
 		assertObject(t._enum()).isNull();
@@ -458,7 +458,7 @@ public class HeaderInfo_Test {
 		assertObject(t).asJson().is("{description:'d',type:'j',format:'g',items:{type:'h'},collectionFormat:'c','default':'a',maximum:123.0,exclusiveMaximum:true,minimum:123.0,exclusiveMinimum:true,maxLength:123,minLength:123,pattern:'i',maxItems:123,minItems:123,uniqueItems:true,'enum':['b'],multipleOf:123.0,'$ref':'ref',example:'e'}");
 
 		assertObject(t.get("default", Object.class)).isType(StringBuilder.class).asString().is("a");
-		assertObject(t.get("enum", Object.class)).isType(Set.class).asString().is("['b']");
+		assertObject(t.get("enum", Object.class)).isType(Set.class).asJson().is("['b']");
 		assertObject(t.get("collectionFormat", Object.class)).isType(String.class).is("c");
 		assertObject(t.get("description", Object.class)).isType(String.class).is("d");
 		assertObject(t.get("example", Object.class)).isType(StringBuilder.class).asString().is("e");
diff --git a/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/Swagger_Test.java b/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/Swagger_Test.java
index 4de8d32..96e8381 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/Swagger_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/dto/swagger/Swagger_Test.java
@@ -178,7 +178,7 @@ public class Swagger_Test {
 		assertObject(t.paths()).isType(Map.class).asJson().is("{foo:{bar:{summary:'baz'}}}");
 
 		t.paths(AMap.create());
-		assertObject(t.paths()).isType(Map.class).asJson().is("{}");
+		assertObject(t.paths()).isNull();
 
 		t.paths((Map<String,OperationMap>)null);
 		assertObject(t.paths()).isNull();
diff --git a/juneau-utest/src/test/java/org/apache/juneau/utils/CollectionUtilsTest.java b/juneau-utest/src/test/java/org/apache/juneau/utils/CollectionUtilsTest.java
deleted file mode 100755
index c9ac26e..0000000
--- a/juneau-utest/src/test/java/org/apache/juneau/utils/CollectionUtilsTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// ***************************************************************************************************************************
-// * 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.utils;
-
-import static org.apache.juneau.internal.CollectionUtils.*;
-import static org.junit.Assert.*;
-import static org.junit.runners.MethodSorters.*;
-
-import java.util.*;
-
-import org.apache.commons.lang3.*;
-import org.junit.*;
-
-@FixMethodOrder(NAME_ASCENDING)
-public class CollectionUtilsTest {
-
-	private String[] strings(String s) {
-		return StringUtils.split(s, ',');
-	}
-
-	@Test
-	public void testSortedCaseInsensitiveSet() {
-		Set<String> s = newSortedCaseInsensitiveSet("foo,Bar,BAZ");
-		for (String ss : strings("foo,Foo,FOO,bar,Bar,BAR,baz,Baz,BAZ"))
-			assertTrue(s.contains(ss));
-		for (String ss : strings("qux"))
-			assertFalse(s.contains(ss));
-	}
-
-	@Test
-	public void testSortedCaseInsensitiveSet_empty() {
-		Set<String> s = newSortedCaseInsensitiveSet("");
-		assertFalse(s.contains("foo"));
-		assertFalse(s.contains(""));
-		assertFalse(s.contains(null));
-	}
-
-	@Test
-	public void testSortedCaseInsensitiveSet_null() {
-		String ss = null;
-		Set<String> s = newSortedCaseInsensitiveSet(ss);
-		assertFalse(s.contains("foo"));
-		assertFalse(s.contains(""));
-		assertFalse(s.contains(null));
-	}
-
-	@Test
-	public void testSortedCaseInsensitiveSet_containsNull() {
-		Set<String> s = newSortedCaseInsensitiveSet(null, "foo");
-		assertTrue(s.contains("foo"));
-		assertFalse(s.contains(""));
-		assertFalse(s.contains(null));
-	}
-}