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/12/23 21:28:59 UTC

[1/2] juneau git commit: (JUNEAU-76) Improvements to Context/Builder APIs

Repository: juneau
Updated Branches:
  refs/heads/master 803910f74 -> 776647695


http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
index 869c914..e2e1a70 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
@@ -80,6 +80,8 @@ public class PropertyStoreBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public synchronized PropertyStoreBuilder apply(PropertyStore copyFrom) {
+		propertyStore = null;
+		
 		if (copyFrom != null)
 			for (Map.Entry<String,PropertyGroup> e : copyFrom.groups.entrySet()) {
 				String gName = e.getKey();
@@ -163,6 +165,7 @@ public class PropertyStoreBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public synchronized PropertyStoreBuilder remove(String key) {
+		propertyStore = null;
 		return set(key, null);
 	}
 
@@ -176,6 +179,7 @@ public class PropertyStoreBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public synchronized PropertyStoreBuilder set(Map<String,Object> newProperties) {
+		propertyStore = null;
 		clear();
 		add(newProperties);
 		return this;
@@ -191,6 +195,7 @@ public class PropertyStoreBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public synchronized PropertyStoreBuilder add(Map<String,Object> newProperties) {
+		propertyStore = null;
 		
 		if (newProperties != null)
 			for (Map.Entry<String,Object> e : newProperties.entrySet()) 
@@ -220,9 +225,9 @@ public class PropertyStoreBuilder {
 	 * @throws ConfigException If property is not a SET/LIST/MAP property, or the argument is invalid.
 	 */
 	public synchronized PropertyStoreBuilder addTo(String key, String arg, Object value) {
+		propertyStore = null;
 		String g = group(key);
 		String n = g.isEmpty() ? key : key.substring(g.length()+1);
-		propertyStore = null;
 		
 		PropertyGroupBuilder gb = groups.get(g);
 		if (gb == null) {
@@ -254,6 +259,7 @@ public class PropertyStoreBuilder {
 	 * @throws ConfigException If property is not a SET/LIST/MAP property, or the argument is invalid.
 	 */
 	public synchronized PropertyStoreBuilder addTo(String key, Object value) {
+		propertyStore = null;
 		return addTo(key, null, value);
 	}
 
@@ -266,9 +272,9 @@ public class PropertyStoreBuilder {
 	 * @throws ConfigException If property is not a SET or LIST property.
 	 */
 	public synchronized PropertyStoreBuilder removeFrom(String key, Object value) {
+		propertyStore = null;
 		String g = group(key);
 		String n = g.isEmpty() ? key : key.substring(g.length()+1);
-		propertyStore = null;
 		
 		PropertyGroupBuilder gb = groups.get(g);
 
@@ -285,6 +291,32 @@ public class PropertyStoreBuilder {
 	}
 	
 	/**
+	 * Peeks at a property value.
+	 * 
+	 * <p>
+	 * Used for debugging purposes.
+	 * 
+	 * @param key The property key.
+	 * @return The property value, or <jk>null</jk> if it doesn't exist.
+	 */
+	public Object peek(String key) {
+		String g = group(key);
+		String n = g.isEmpty() ? key : key.substring(g.length()+1);
+		
+		PropertyGroupBuilder gb = groups.get(g);
+
+		// Create property group anyway to generate a good error message.
+		if (gb == null) 
+			return null;
+		
+		MutableProperty bp = gb.properties.get(n);
+		if (bp == null)
+			return null;
+		
+		return bp.peek();
+	}
+
+	/**
 	 * Clears all entries in this property store.
 	 */
 	public synchronized void clear() {
@@ -424,6 +456,8 @@ public class PropertyStoreBuilder {
 		abstract void set(Object value);
 
 		abstract void apply(Object value);
+		
+		abstract Object peek();
 
 		void add(String arg, Object value) {
 			throw new ConfigException("Cannot add value {0} ({1}) to property ''{2}'' ({3}).",
@@ -471,6 +505,11 @@ public class PropertyStoreBuilder {
 		synchronized boolean isEmpty() {
 			return this.value == null;
 		}
+
+		@Override /* MutableProperty */
+		synchronized Object peek() {
+			return value;
+		}
 	}
 	
 	//-------------------------------------------------------------------------------------------------------------------
@@ -560,6 +599,11 @@ public class PropertyStoreBuilder {
 		synchronized boolean isEmpty() {
 			return this.value.isEmpty();
 		}
+
+		@Override /* MutableProperty */
+		synchronized Object peek() {
+			return value;
+		}
 	}
 	
 	//-------------------------------------------------------------------------------------------------------------------
@@ -673,6 +717,11 @@ public class PropertyStoreBuilder {
 		synchronized boolean isEmpty() {
 			return this.value.isEmpty();
 		}
+
+		@Override /* MutableProperty */
+		synchronized Object peek() {
+			return value;
+		}
 	}
 	
 	//-------------------------------------------------------------------------------------------------------------------
@@ -743,6 +792,11 @@ public class PropertyStoreBuilder {
 		synchronized boolean isEmpty() {
 			return this.value.isEmpty();
 		}
+
+		@Override /* MutableProperty */
+		synchronized Object peek() {
+			return value;
+		}
 	}
 	
 	

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
index 62ca444..4ccb585 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClient.java
@@ -63,11 +63,220 @@ import org.apache.juneau.urlencoding.*;
 @SuppressWarnings("rawtypes")
 public class RestClient extends BeanContext {
 
-	private static final ConcurrentHashMap<Class,PartSerializer> partSerializerCache = new ConcurrentHashMap<>();
+	//-------------------------------------------------------------------------------------------------------------------
+	// Configurable properties
+	//-------------------------------------------------------------------------------------------------------------------
+
+	private static final String PREFIX = "RestClient.";
+
+	/**
+	 * <b>Configuration property:</b>  Keep HttpClient open.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.keepHttpClientOpen.b"</js>
+	 * 	<li><b>Data type:</b> <code>Boolean</code>
+	 * 	<li><b>Default:</b> <jk>false</jk>
+	 * </ul>
+	 *
+	 * <p>
+	 * Don't close this client when the {@link RestClient#close()} method is called.
+	 */
+	public static final String RESTCLIENT_keepHttpClientOpen = PREFIX + "keepHttpClientOpen.b";
+	
+	/**
+	 * <b>Configuration property:  Shut down executor service on close.</b>  
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.executorServiceShutdownOnClose.b"</js>
+	 * 	<li><b>Data type:</b> <code>Boolean</code>
+	 * 	<li><b>Default:</b> <jk>false</jk>
+	 * </ul>
+	 *
+	 * <p>
+	 * Call {@link ExecutorService#shutdown()} when {@link RestClient#close()} is called.
+	 */
+	public static final String RESTCLIENT_executorServiceShutdownOnClose = PREFIX + "executorServiceShutdownOnClose.b";
+	
+	/**
+	 * <b>Configuration property:</b>  Number of retries to attempt.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.retries.i"</js>
+	 * 	<li><b>Data type:</b> <code>Integer</code>
+	 * 	<li><b>Default:</b> <code>1</code>
+	 * </ul>
+	 *
+	 * <p>
+	 * The number of retries to attempt when the connection cannot be made or a <code>&gt;400</code> response is received.
+	 */
+	public static final String RESTCLIENT_retries = PREFIX + "retries.i";
+	
+	/**
+	 * <b>Configuration property:</b>  The time in milliseconds between retry attempts.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.retryInterval.i"</js>
+	 * 	<li><b>Data type:</b> <code>Integer</code>
+	 * 	<li><b>Default:</b> <code>-1</code>
+	 * </ul>
+	 *
+	 * <p>
+	 * The time in milliseconds between retry attempts.
+	 * <code>-1</code> means retry immediately.
+	 */
+	public static final String RESTCLIENT_retryInterval = PREFIX + "retryInterval.i";
+	
+	/**
+	 * <b>Configuration property:</b>  Retry-on determination object.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.retryOn.o"</js>
+	 * 	<li><b>Data type:</b> <code>Class&lt;? extends {@link RetryOn}</code> or {@link RetryOn}
+	 * 	<li><b>Default:</b> {@link RetryOn#DEFAULT}
+	 * </ul>
+	 *
+	 * <p>
+	 * Object used for determining whether a retry should be attempted.
+	 */
+	public static final String RESTCLIENT_retryOn = PREFIX + "retryOn.o";
+	
+	/**
+	 * <b>Configuration property:</b>  Root URI.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.rootUri.s"</js>
+	 * 	<li><b>Data type:</b> <code>String</code>
+	 * 	<li><b>Default:</b> <jk>false</jk>
+	 * </ul>
+	 *
+	 * <p>
+	 * When set, relative URL strings passed in through the various rest call methods (e.g. {@link RestClient#doGet(Object)}
+	 * will be prefixed with the specified root.
+	 * This root URL is ignored on those methods if you pass in a {@link URL}, {@link URI}, or an absolute URL string.
+	 * Trailing slashes are trimmed.
+	 */
+	public static final String RESTCLIENT_rootUri = PREFIX + "rootUri.s";
+	
+	/**
+	 * <b>Configuration property:</b>  Request headers.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.requestHeader.ms"</js>
+	 * 	<li><b>Data type:</b> <code>Map&lt;String,String&gt;</code>
+	 * 	<li><b>Default:</b> empty map
+	 * </ul>
+	 *
+	 * <p>
+	 * Don't close this client when the {@link RestClient#close()} method is called.
+	 */
+	public static final String RESTCLIENT_headers = PREFIX + "headers.ms";
+
+	/**
+	 * <b>Configuration property:</b>  Serializer.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.serializer.o"</js>
+	 * 	<li><b>Data type:</b> <code>Class&lt;? <jk>extends</jk> Serializer&gt;</code> or {@link Serializer}.
+	 * 	<li><b>Default:</b> {@link JsonSerializer};
+	 * </ul>
+	 * 
+	 * <p>
+	 * The serializer to use for serializing POJOs in request bodies.
+	 */
+	public static final String RESTCLIENT_serializer = PREFIX + "serializer.o";
 
+	/**
+	 * <b>Configuration property:</b>  Parser.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.parser.o"</js>
+	 * 	<li><b>Data type:</b> <code>Class&lt;? <jk>extends</jk> Parser&gt;</code> or {@link Parser}.
+	 * 	<li><b>Default:</b> {@link JsonParser};
+	 * </ul>
+	 * 
+	 * <p>
+	 * The parser to use for parsing POJOs in response bodies.
+	 */
+	public static final String RESTCLIENT_parser = PREFIX + "parser.o";
+
+	/**
+	 * <b>Configuration property:</b>  Part serializer.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.urlEncodingSerializer.o"</js>
+	 * 	<li><b>Data type:</b> <code>Class&lt;? <jk>implements</jk> PartSerializer&gt;</code> or {@link PartSerializer}.
+	 * 	<li><b>Default:</b> {@link UrlEncodingSerializer};
+	 * </ul>
+	 * 
+	 * <p>
+	 * The serializer to use for serializing POJOs in form data, query parameters, headers, and path variables.
+	 */
+	public static final String RESTCLIENT_partSerializer = PREFIX + "partSerializer.o";
+	
+	/**
+	 * <b>Configuration property:</b>  Executor service.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.executorService.o"</js>
+	 * 	<li><b>Data type:</b> <code>Class&lt;? <jk>implements</jk> ExecutorService&gt;</code> or {@link ExecutorService}.
+	 * 	<li><b>Default:</b> <jk>null</jk>.
+	 * </ul>
+	 * 
+	 * Defines the executor service to use when calling future methods on the {@link RestCall} class.
+	 *
+	 * <p>
+	 * This executor service is used to create {@link Future} objects on the following methods:
+	 * <ul>
+	 * 	<li>{@link RestCall#runFuture()}
+	 * 	<li>{@link RestCall#getResponseFuture(Class)}
+	 * 	<li>{@link RestCall#getResponseFuture(Type,Type...)}
+	 * 	<li>{@link RestCall#getResponseAsString()}
+	 * </ul>
+	 *
+	 * <p>
+	 * The default executor service is a single-threaded {@link ThreadPoolExecutor} with a 30 second timeout
+	 * and a queue size of 10.
+	 */
+	public static final String RESTCLIENT_executorService = PREFIX + "executorService.o";
+
+	/**
+	 * <b>Configuration property:</b>  Call interceptors.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.interceptors.lo"</js>
+	 * 	<li><b>Data type:</b> <code>List&lt;Class&lt;? <jk>implements</jk> RestCallInterceptor | RestCallInterceptor</code>&gt.</code>
+	 * 	<li><b>Default:</b> empty list.
+	 * </ul>
+	 * 
+	 * Interceptors that get called immediately after a connection is made.
+	 */
+	public static final String RESTCLIENT_interceptors = PREFIX + "interceptors.lo";
+
+	/**
+	 * Add to the Call interceptors property.
+	 */
+	public static final String RESTCLIENT_interceptors_add = PREFIX + "interceptors.lo/add";
+
+	/**
+	 * <b>Configuration property:</b>  Debug.
+	 *
+	 * <ul>
+	 * 	<li><b>Name:</b> <js>"RestClient.debug.b"</js>
+	 * 	<li><b>Data type:</b> <code>Boolean</code>
+	 * 	<li><b>Default:</b> <jk>false</jk>
+	 * </ul>
+	 *
+	 * <p>
+	 * Enable debug mode.
+	 */
+	public static final String RESTCLIENT_debug = PREFIX + "debug.b";
+	
+
+	private static final ConcurrentHashMap<Class,PartSerializer> partSerializerCache = new ConcurrentHashMap<>();
+	
 	private final Map<String,String> headers;
 	private final CloseableHttpClient httpClient;
-	private final boolean keepHttpClientOpen;
+	private final boolean keepHttpClientOpen, debug;
 	private final UrlEncodingSerializer urlEncodingSerializer;  // Used for form posts only.
 	private final PartSerializer partSerializer;
 	private final String rootUrl;
@@ -81,12 +290,11 @@ public class RestClient extends BeanContext {
 	final RetryOn retryOn;
 	final int retries;
 	final long retryInterval;
-	final boolean debug;
 	final RestCallInterceptor[] intercepters;
 
 	// This is lazy-created.
 	private volatile ExecutorService executorService;
-	boolean executorServiceShutdownOnClose = true;
+	private final boolean executorServiceShutdownOnClose;
 
 	/**
 	 * Instantiates a new clean-slate {@link RestClientBuilder} object.
@@ -105,53 +313,52 @@ public class RestClient extends BeanContext {
 		return new RestClientBuilder(getPropertyStore());
 	}
 
+	@SuppressWarnings("unchecked")
 	RestClient(
 			PropertyStore ps,
-			CloseableHttpClient httpClient,
-			boolean keepHttpClientOpen,
-			Serializer serializer,
-			Parser parser,
-			UrlEncodingSerializer urlEncodingSerializer,
-			PartSerializer partSerializer,
-			Map<String,String> headers,
-			List<RestCallInterceptor> intercepters,
-			String rootUri,
-			RetryOn retryOn,
-			int retries,
-			long retryInterval,
-			boolean debug,
-			ExecutorService executorService,
-			boolean executorServiceShutdownOnClose) {
+			CloseableHttpClient httpClient) {
 		super(ps);
 		this.httpClient = httpClient;
-		this.keepHttpClientOpen = keepHttpClientOpen;
-		this.serializer = serializer;
-		this.parser = parser;
-		this.urlEncodingSerializer = urlEncodingSerializer;
-		this.partSerializer = partSerializer;
-
-		Map<String,String> h2 = new ConcurrentHashMap<>(headers);
-
-		this.headers = Collections.unmodifiableMap(h2);
-		this.rootUrl = rootUri;
-		this.retryOn = retryOn;
-		this.retries = retries;
-		this.retryInterval = retryInterval;
-		this.debug = debug;
-
-		List<RestCallInterceptor> l = new ArrayList<>(intercepters);
-		if (debug)
-			l.add(RestCallLogger.DEFAULT);
+		this.keepHttpClientOpen = getProperty(RESTCLIENT_keepHttpClientOpen, boolean.class, false);
+		this.headers = getMapProperty(RESTCLIENT_headers, String.class);
+		this.retries = getProperty(RESTCLIENT_retries, int.class, 1);
+		this.retryInterval = getProperty(RESTCLIENT_retryInterval, int.class, -1);
+		this.retryOn = getInstanceProperty(RESTCLIENT_retryOn, RetryOn.class, RetryOn.DEFAULT);
+		this.debug = getProperty(RESTCLIENT_debug, boolean.class, false);
+		this.executorServiceShutdownOnClose = getProperty(RESTCLIENT_executorServiceShutdownOnClose, boolean.class, false);
+		this.rootUrl = StringUtils.nullIfEmpty(getProperty(RESTCLIENT_rootUri, String.class, "").replaceAll("\\/$", ""));
+		
+		Object o = getProperty(RESTCLIENT_serializer, Object.class, JsonSerializer.class);
+		if (o instanceof Serializer) {
+			this.serializer = ((Serializer)o).builder().apply(ps).build();
+		} else if (o instanceof Class) {
+			this.serializer = ContextCache.INSTANCE.create((Class<? extends Serializer>)o, ps);
+		} else {
+			throw new ContextRuntimeException("Invalid object type found for property ''{0}'':  ''{1}''", RESTCLIENT_serializer, o.getClass());
+		}
+		
+		o = getProperty(RESTCLIENT_parser, Object.class, JsonParser.class);
+		if (o instanceof Parser) {
+			this.parser = ((Parser)o).builder().apply(ps).build();
+		} else if (o instanceof Class) {
+			this.parser = ContextCache.INSTANCE.create((Class<? extends Parser>)o, ps);
+		} else {
+			throw new ContextRuntimeException("Invalid object type found for property ''{0}'':  ''{1}''", RESTCLIENT_parser, o.getClass());
+		}
 
-		this.intercepters = l.toArray(new RestCallInterceptor[l.size()]);
+		this.urlEncodingSerializer = new SerializerBuilder(ps).build(UrlEncodingSerializer.class);
+		this.partSerializer = getInstanceProperty(RESTCLIENT_partSerializer, PartSerializer.class, urlEncodingSerializer);
+		this.executorService = getInstanceProperty(RESTCLIENT_executorService, ExecutorService.class, null);
+		
+		RestCallInterceptor[] rci = getInstanceArrayProperty(RESTCLIENT_interceptors, RestCallInterceptor.class, new RestCallInterceptor[0]);
+		if (debug)
+			rci = ArrayUtils.append(rci, RestCallLogger.DEFAULT);
+		this.intercepters = rci;
 
 		if (Boolean.getBoolean("org.apache.juneau.rest.client.RestClient.trackLifecycle"))
 			creationStack = Thread.currentThread().getStackTrace();
 		else
 			creationStack = null;
-
-		this.executorService = executorService;
-		this.executorServiceShutdownOnClose = executorServiceShutdownOnClose;
 	}
 
 	/**
@@ -168,7 +375,7 @@ public class RestClient extends BeanContext {
 			httpClient.close();
 		if (executorService != null && executorServiceShutdownOnClose)
 			executorService.shutdown();
-		if (Boolean.getBoolean("org.apache.juneau.rest.client.RestClient.trackLifecycle"))
+		if (creationStack != null)
 			closedStack = Thread.currentThread().getStackTrace();
 	}
 
@@ -183,7 +390,7 @@ public class RestClient extends BeanContext {
 			if (executorService != null && executorServiceShutdownOnClose)
 				executorService.shutdown();
 		} catch (Throwable t) {}
-		if (Boolean.getBoolean("org.apache.juneau.rest.client.RestClient.trackLifecycle"))
+		if (creationStack != null)
 			closedStack = Thread.currentThread().getStackTrace();
 	}
 

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
index c8bddb3..b9c18b0 100644
--- a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
+++ b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client/RestClientBuilder.java
@@ -13,9 +13,10 @@
 package org.apache.juneau.rest.client;
 
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.json.JsonSerializer.*;
 import static org.apache.juneau.parser.Parser.*;
-import static org.apache.juneau.serializer.Serializer.*;
 import static org.apache.juneau.uon.UonSerializer.*;
+import static org.apache.juneau.rest.client.RestClient.*;
 
 import java.lang.reflect.*;
 import java.net.*;
@@ -44,11 +45,9 @@ import org.apache.http.impl.conn.*;
 import org.apache.http.protocol.*;
 import org.apache.juneau.*;
 import org.apache.juneau.http.*;
-import org.apache.juneau.json.*;
 import org.apache.juneau.parser.*;
 import org.apache.juneau.serializer.*;
 import org.apache.juneau.uon.*;
-import org.apache.juneau.urlencoding.*;
 
 /**
  * Builder class for the {@link RestClient} class.
@@ -58,29 +57,9 @@ public class RestClientBuilder extends BeanContextBuilder {
 	private HttpClientConnectionManager httpClientConnectionManager;
 	private HttpClientBuilder httpClientBuilder = createHttpClientBuilder();
 	private CloseableHttpClient httpClient;
-	private boolean keepHttpClientOpen;
-
-	private Class<? extends Serializer> serializerClass = JsonSerializer.class;
-	private Class<? extends Parser> parserClass = JsonParser.class;
-	private Class<? extends PartSerializer> partSerializerClass = UrlEncodingSerializer.class;
-	private Serializer serializer;
-	private Parser parser;
-	private PartSerializer partSerializer;
-
-	private Map<String,String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
-
-	private List<RestCallInterceptor> intercepters = new ArrayList<>();
-
-	private String rootUrl;
 	private SSLOpts sslOpts;
 	private boolean pooled;
 
-	private int retries = 1;
-	private long retryInterval = -1;
-	private RetryOn retryOn = RetryOn.DEFAULT;
-	private boolean debug, executorServiceShutdownOnClose;
-	private ExecutorService executorService;
-
 	/**
 	 * Constructor, default settings.
 	 */
@@ -137,29 +116,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 			
 			PropertyStore ps = psb.build();
 
-			Serializer s =
-				this.serializer != null
-				? this.serializer.builder().apply(ps).build()
-				: new SerializerBuilder(ps).build(this.serializerClass);
-
-			Parser p =
-				this.parser != null
-				? this.parser.builder().apply(ps).build()
-				: new ParserBuilder(ps).build(this.parserClass);
-
-			UrlEncodingSerializer us = new SerializerBuilder(ps).build(UrlEncodingSerializer.class);
-
-			PartSerializer pf = null;
-			if (partSerializer != null)
-				pf = partSerializer;
-			else if (partSerializerClass != null) {
-				if (partSerializerClass == UrlEncodingSerializer.class)
-					pf = us;
-				else
-					pf = partSerializerClass.newInstance();
-			}
-
-			return new RestClient(ps, httpClient, keepHttpClientOpen, s, p, us, pf, headers, intercepters, rootUrl, retryOn, retries, retryInterval, debug, executorService, executorServiceShutdownOnClose);
+			return new RestClient(ps, httpClient);
 		} catch (Exception e) {
 			throw new RuntimeException(e);
 		}
@@ -271,10 +228,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public RestClientBuilder rootUrl(Object rootUrl) {
-		String s = rootUrl.toString();
-		if (s.endsWith("/"))
-			s = s.replaceAll("\\/$", "");
-		this.rootUrl = s;
+		set(RESTCLIENT_rootUri, rootUrl);
 		return this;
 	}
 
@@ -321,12 +275,11 @@ public class RestClientBuilder extends BeanContextBuilder {
 	/**
 	 * Adds an intercepter that gets called immediately after a connection is made.
 	 *
-	 * @param intercepter The intercepter.
+	 * @param interceptor The intercepter.
 	 * @return This object (for method chaining).
 	 */
-	public RestClientBuilder intercepter(RestCallInterceptor intercepter) {
-		intercepters.add(intercepter);
-		return this;
+	public RestClientBuilder interceptor(RestCallInterceptor interceptor) {
+		return addTo(RESTCLIENT_interceptors, interceptor);
 	}
 
 	/**
@@ -337,8 +290,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public RestClientBuilder logTo(Level level, Logger log) {
-		intercepter(new RestCallLogger(level, log));
-		return this;
+		return interceptor(new RestCallLogger(level, log));
 	}
 
 	/**
@@ -351,10 +303,10 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * 	If <jk>null</jk>, uses {@link RetryOn#DEFAULT}.
 	 * @return This object (for method chaining).
 	 */
-	public RestClientBuilder retryable(int retries, long interval, RetryOn retryOn) {
-		this.retries = retries;
-		this.retryInterval = interval;
-		this.retryOn = retryOn;
+	public RestClientBuilder retryable(int retries, int interval, RetryOn retryOn) {
+		set(RESTCLIENT_retries, retries);
+		set(RESTCLIENT_retryInterval, interval);
+		set(RESTCLIENT_retryOn, retryOn);
 		return this;
 	}
 
@@ -376,8 +328,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public RestClientBuilder serializer(Serializer serializer) {
-		this.serializer = serializer;
-		return this;
+		return set(RESTCLIENT_serializer, serializer);
 	}
 
 	/**
@@ -388,8 +339,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public RestClientBuilder serializer(Class<? extends Serializer> serializerClass) {
-		this.serializerClass = serializerClass;
-		return this;
+		return set(RESTCLIENT_serializer, serializerClass);
 	}
 
 	/**
@@ -398,8 +348,8 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * @param parser The parser.
 	 * @return This object (for method chaining).
 	 */
-	public RestClientBuilder parser(Parser parser) {
-		this.parser = parser;
+	public RestClientBuilder parser(Parser parser) {		
+		set(RESTCLIENT_parser, parser);
 		return this;
 	}
 
@@ -411,8 +361,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public RestClientBuilder parser(Class<? extends Parser> parserClass) {
-		this.parserClass = parserClass;
-		return this;
+		return set(RESTCLIENT_parser, parserClass);
 	}
 
 	/**
@@ -423,8 +372,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public RestClientBuilder partSerializer(PartSerializer partSerializer) {
-		this.partSerializer = partSerializer;
-		return this;
+		return set(RESTCLIENT_partSerializer, partSerializer);
 	}
 
 	/**
@@ -437,8 +385,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public RestClientBuilder partSerializer(Class<? extends PartSerializer> partSerializerClass) {
-		this.partSerializerClass = partSerializerClass;
-		return this;
+		return set(RESTCLIENT_partSerializer, partSerializerClass);
 	}
 
 	/**
@@ -468,7 +415,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 */
 	public RestClientBuilder httpClient(CloseableHttpClient httpClient, boolean keepHttpClientOpen) {
 		this.httpClient = httpClient;
-		this.keepHttpClientOpen = keepHttpClientOpen;
+		set(RESTCLIENT_keepHttpClientOpen, keepHttpClientOpen);
 		return this;
 	}
 
@@ -493,8 +440,8 @@ public class RestClientBuilder extends BeanContextBuilder {
 	 * @return This object (for method chaining).
 	 */
 	public RestClientBuilder executorService(ExecutorService executorService, boolean shutdownOnClose) {
-		this.executorService = executorService;
-		this.executorServiceShutdownOnClose = shutdownOnClose;
+		set(RESTCLIENT_executorService, executorService);
+		set(RESTCLIENT_executorServiceShutdownOnClose, shutdownOnClose);
 		return this;
 	}
 
@@ -504,18 +451,6 @@ public class RestClientBuilder extends BeanContextBuilder {
 	//--------------------------------------------------------------------------------
 
 	/**
-	 * Specifies a request header property to add to all requests created by this client.
-	 *
-	 * @param name The HTTP header name.
-	 * @param value The HTTP header value.
-	 * @return This object (for method chaining).
-	 */
-	public RestClientBuilder header(String name, Object value) {
-		this.headers.put(name, value == null ? null : value.toString());
-		return this;
-	}
-
-	/**
 	 * Sets the value for the <code>Accept</code> request header.
 	 *
 	 * <p>
@@ -909,10 +844,41 @@ public class RestClientBuilder extends BeanContextBuilder {
 
 
 	//--------------------------------------------------------------------------------
-	// Context properties
+	// Properties
 	//--------------------------------------------------------------------------------
 
 	/**
+	 * <b>Configuration property:</b>  Keep HttpClient open.
+	 *
+	 * <p>
+	 * Don't close this client when the {@link RestClient#close()} method is called.
+	 *
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul>
+	 * 	<li>This is equivalent to calling <code>set(<jsf>RESTCLIENT_keepHttpClientOpen</jsf>, value)</code>.
+	 * </ul>
+	 *
+	 * @param value The new value for this property.
+	 * @return This object (for method chaining).
+	 * @see RestClient#RESTCLIENT_keepHttpClientOpen
+	 */
+	public RestClientBuilder keepHttpClientOpen(boolean value) {
+		return set(RESTCLIENT_keepHttpClientOpen, value);
+	}
+
+	/**
+	 * <b>Configuration property:</b>  Request headers.
+	 *
+	 * @param key The header name.
+	 * @param value The header value.
+	 * @return This object (for method chaining).
+	 * @see RestClient#RESTCLIENT_headers
+	 */
+	public RestClientBuilder header(String key, Object value) {
+		return addTo(RESTCLIENT_headers, key, value);
+	}
+
+	/**
 	 * Sets the {@link Serializer#SERIALIZER_maxDepth} property on all serializers in this group.
 	 *
 	 * @param value The new value for this property.
@@ -1589,7 +1555,7 @@ public class RestClientBuilder extends BeanContextBuilder {
 	@Override /* ContextBuilder */
 	public RestClientBuilder debug() {
 		super.debug();
-		this.debug = true;
+		set(RESTCLIENT_debug, true);
 		header("Debug", true);
 		return this;
 	}


[2/2] juneau git commit: (JUNEAU-76) Improvements to Context/Builder APIs

Posted by ja...@apache.org.
(JUNEAU-76) Improvements to Context/Builder APIs

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

Branch: refs/heads/master
Commit: 7766476956c1fa895702e511cd15cfb349f6e693
Parents: 803910f
Author: JamesBognar <ja...@apache.org>
Authored: Sat Dec 23 16:28:55 2017 -0500
Committer: JamesBognar <ja...@apache.org>
Committed: Sat Dec 23 16:28:55 2017 -0500

----------------------------------------------------------------------
 .../org/apache/juneau/PropertyStore2Test.java   |   35 +-
 .../apache/juneau/dto/html5/Html5BasicTest.java |   37 +
 .../juneau/dto/html5/Html5BasicTests.java       |   37 -
 .../juneau/xml/XmlIgnoreCommentsTest.java       | 1251 ++++++++++++++++++
 .../juneau/xml/XmlTestIgnoreComments.java       | 1251 ------------------
 juneau-core/juneau-marshall/TODO.txt            |    2 -
 .../java/org/apache/juneau/BeanContext.java     |   24 +-
 .../main/java/org/apache/juneau/Context.java    |   19 +
 .../org/apache/juneau/PropertyConverter.java    |    8 +-
 .../org/apache/juneau/PropertyStoreBuilder.java |   58 +-
 .../apache/juneau/rest/client/RestClient.java   |  293 +++-
 .../juneau/rest/client/RestClientBuilder.java   |  144 +-
 12 files changed, 1689 insertions(+), 1470 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStore2Test.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStore2Test.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStore2Test.java
index a62fa1b..e524471 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStore2Test.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStore2Test.java
@@ -197,11 +197,11 @@ public class PropertyStore2Test {
 		b.set("A.f4.o", StringBuilder.class);
 		b.set("A.f5.o", null);  
 		ps = b.build();
-		assertObjectEquals("{A:{'f1.o':123,'f2.o':true,'f3.o':'123','f4.o':''}}", ps);
+		assertObjectEquals("{A:{'f1.o':123,'f2.o':true,'f3.o':'123','f4.o':'java.lang.StringBuilder'}}", ps);
 		assertType(Integer.class, ps.getProperty("A.f1.o"));
 		assertType(Boolean.class, ps.getProperty("A.f2.o"));
 		assertType(StringBuilder.class, ps.getProperty("A.f3.o"));
-		assertType(StringBuilder.class, ps.getProperty("A.f4.o"));
+		assertType(Class.class, ps.getProperty("A.f4.o"));
 
 		// Test nulls
 		b.set("A.f2.o", null);
@@ -213,13 +213,8 @@ public class PropertyStore2Test {
 		testError(b, "A.f1.o/add", "foo", "Cannot add value 'foo' (String) to property 'f1.o' (Object).");
 		testError(b, "A.f1.o/add.123", "foo", "Cannot add value 'foo' (String) to property 'f1.o' (Object).");
 		testError(b, "A.f1.o/remove", "foo", "Cannot remove value 'foo' (String) from property 'f1.o' (Object).");
-		testError(b, "A.f1.o", ObjectWithNoNoArgConstructor.class, "Value 'org.apache.juneau.PropertyStore2Test$ObjectWithNoNoArgConstructor' (Class) cannot be converted to an Object on property 'f1.o' (Object).");
 	}
 	
-	public static class ObjectWithNoNoArgConstructor {
-		private ObjectWithNoNoArgConstructor() {}
-	}
-
 	@Test
 	public void testSetString() throws Exception {
 		PropertyStoreBuilder b = PropertyStore.create();
@@ -703,14 +698,14 @@ public class PropertyStore2Test {
 		b.set("A.f2.lo", new AList<Object>().appendAll(123, true, new StringBuilder(123), StringBuilder.class, null));  
 		b.set("A.f3.lo", null);
 		ps = b.build();
-		assertObjectEquals("{A:{'f1.lo':[''],'f2.lo':[123,true,'','']}}", ps);
+		assertObjectEquals("{A:{'f1.lo':['java.lang.StringBuilder'],'f2.lo':[123,true,'','java.lang.StringBuilder']}}", ps);
 		assertType(List.class, ps.getProperty("A.f1.lo"));
 		assertType(List.class, ps.getProperty("A.f2.lo"));
-		assertType(StringBuilder.class, ((List<?>)ps.getProperty("A.f1.lo")).get(0));
+		assertType(Class.class, ((List<?>)ps.getProperty("A.f1.lo")).get(0));
 		assertType(Integer.class, ((List<?>)ps.getProperty("A.f2.lo")).get(0));
 		assertType(Boolean.class, ((List<?>)ps.getProperty("A.f2.lo")).get(1));
 		assertType(StringBuilder.class, ((List<?>)ps.getProperty("A.f2.lo")).get(2));
-		assertType(StringBuilder.class, ((List<?>)ps.getProperty("A.f2.lo")).get(3));
+		assertType(Class.class, ((List<?>)ps.getProperty("A.f2.lo")).get(3));
 	
 		b.clear();
 		b.set("A.f1.lo/add", 1);  
@@ -723,31 +718,31 @@ public class PropertyStore2Test {
 		b.set("A.f1.lo/add", new AList<Class<?>>().appendAll(StringBuilder.class));  
 		b.set("A.f1.lo/add", new AList<Class<?>>().appendAll(HashMap.class));
 		b.addTo("A.f1.lo", new AList<Class<?>>().appendAll(LinkedList.class));
-		assertObjectEquals("{A:{'f1.lo':[[],{},'']}}", b.build());
+		assertObjectEquals("{A:{'f1.lo':['java.util.LinkedList','java.util.HashMap','java.lang.StringBuilder']}}", b.build());
 		b.set("A.f1.lo/remove", new AList<Class<?>>().appendAll(HashMap.class));  
 		b.removeFrom("A.f1.lo", new AList<Class<?>>().appendAll());  
 		b.removeFrom("A.f1.lo", new AList<Class<?>>().appendAll(LinkedList.class));  
-		assertObjectEquals("{A:{'f1.lo':['']}}", b.build());
+		assertObjectEquals("{A:{'f1.lo':['java.lang.StringBuilder']}}", b.build());
 
 		b.clear();
 		b.set("A.f1.lo/add", new AList<Object>().appendAll(StringBuilder.class));  
 		b.set("A.f1.lo/add", new AList<Object>().appendAll(HashMap.class));
 		b.addTo("A.f1.lo", new AList<Object>().appendAll(LinkedList.class));
-		assertObjectEquals("{A:{'f1.lo':[[],{},'']}}", b.build());
+		assertObjectEquals("{A:{'f1.lo':['java.util.LinkedList','java.util.HashMap','java.lang.StringBuilder']}}", b.build());
 		b.set("A.f1.lo/remove", new AList<Object>().appendAll(HashMap.class));  
 		b.set("A.f1.lo/remove", new AList<Object>().appendAll());  
 		b.removeFrom("A.f1.lo", new AList<Object>().appendAll(LinkedList.class));  
-		assertObjectEquals("{A:{'f1.lo':['']}}", b.build());
+		assertObjectEquals("{A:{'f1.lo':['java.lang.StringBuilder']}}", b.build());
 
 		b.clear();
 		b.set("A.f1.lo/add", new Class<?>[]{StringBuilder.class});  
 		b.set("A.f1.lo/add", new Class<?>[]{HashMap.class});
 		b.addTo("A.f1.lo", new Class<?>[]{LinkedList.class});
-		assertObjectEquals("{A:{'f1.lo':[[],{},'']}}", b.build());
+		assertObjectEquals("{A:{'f1.lo':['java.util.LinkedList','java.util.HashMap','java.lang.StringBuilder']}}", b.build());
 		b.set("A.f1.lo/remove", new Class<?>[]{HashMap.class});  
 		b.set("A.f1.lo/remove", new Class<?>[]{});  
 		b.removeFrom("A.f1.lo", new Class<?>[]{LinkedList.class});  
-		assertObjectEquals("{A:{'f1.lo':['']}}", b.build());
+		assertObjectEquals("{A:{'f1.lo':['java.lang.StringBuilder']}}", b.build());
 		
 		b.set("A.f1.lo", null);
 		assertObjectEquals("{}", b.build());
@@ -758,7 +753,7 @@ public class PropertyStore2Test {
 		b.set("A.f1.lo/add.1", LinkedList.class);
 		b.set("A.f1.lo/add.0", TestEnum.ONE);
 		b.set("A.f1.lo/add.-10", TestEnum.TWO);
-		assertObjectEquals("{A:{'f1.lo':['TWO','ONE','',[],{}]}}", b.build());
+		assertObjectEquals("{A:{'f1.lo':['TWO','ONE','java.lang.StringBuilder','java.util.LinkedList','java.util.HashMap']}}", b.build());
 
 		try {
 			b.addTo("A.f1.lo", "foo", "bar");
@@ -896,7 +891,7 @@ public class PropertyStore2Test {
 		b.set("A.f4.mo", "{foo:'123',baz:456,qux:null}");  
 		b.set("A.f5.mo", null);
 		ps = b.build();
-		assertObjectEquals("{A:{'f1.mo':{baz:'2',foo:'1'},'f2.mo':{bar:'',foo:123},'f3.mo':{foo:'123'},'f4.mo':{baz:456,foo:'123'}}}", ps);
+		assertObjectEquals("{A:{'f1.mo':{baz:'2',foo:'1'},'f2.mo':{bar:'java.lang.StringBuilder',foo:123},'f3.mo':{foo:'123'},'f4.mo':{baz:456,foo:'123'}}}", ps);
 		assertType(Map.class, ps.getProperty("A.f1.mo"));
 		assertType(Map.class, ps.getProperty("A.f2.mo"));
 		assertType(Map.class, ps.getProperty("A.f3.mo"));
@@ -1288,7 +1283,7 @@ public class PropertyStore2Test {
 		testEquals(b1, b2);
 		
 		b1.set("A.f1.lo", new Object[]{StringBuilder.class});
-		b2.set("A.f1.lo", new Object[]{StringBuilder.class});
+		b2.set("A.f1.lo", new Object[]{StringBuffer.class});
 		testNotEquals(b1, b2);
 
 		b1.set("A.f1.lo", "foo");
@@ -1701,7 +1696,7 @@ public class PropertyStore2Test {
 		b = ps.builder();
 		ps = b.build();
 		
-		assertObjectEquals("{A:{'foo.b':true,'foo.c':'java.lang.String','foo.i':123,'foo.lc':['java.lang.String'],'foo.li':[123],'foo.lo':[''],'foo.ls':['bar'],'foo.mc':{foo:'java.lang.String'},'foo.mi':{foo:123},'foo.mo':{foo:''},'foo.ms':{foo:'bar'},'foo.o':'bar','foo.s':'bar','foo.sc':['java.lang.String'],'foo.si':[123],'foo.ss':['bar']}}", ps);
+		assertObjectEquals("{A:{'foo.b':true,'foo.c':'java.lang.String','foo.i':123,'foo.lc':['java.lang.String'],'foo.li':[123],'foo.lo':['java.lang.StringBuilder'],'foo.ls':['bar'],'foo.mc':{foo:'java.lang.String'},'foo.mi':{foo:123},'foo.mo':{foo:'java.lang.StringBuilder'},'foo.ms':{foo:'bar'},'foo.o':'bar','foo.s':'bar','foo.sc':['java.lang.String'],'foo.si':[123],'foo.ss':['bar']}}", ps);
 	}
 
 	@Test

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/Html5BasicTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/Html5BasicTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/Html5BasicTest.java
new file mode 100644
index 0000000..f8195fd
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/Html5BasicTest.java
@@ -0,0 +1,37 @@
+// ***************************************************************************************************************************
+// * 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.dto.html5;
+
+import static org.apache.juneau.dto.html5.HtmlBuilder.*;
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+public class Html5BasicTest {
+
+	@Test
+	public void testToString() {
+		Form f = form("bar",
+			fieldset(
+				legend("foo:"),
+				"Name:", input("text"), br(),
+				"Email:", input("text"), br(),
+				"X:", keygen().name("X"),
+				label("label")._for("Name")
+			)
+		);
+		
+		String r = f.toString();
+		assertEquals("<form action='bar'><fieldset><legend>foo:</legend>Name:<input type='text'/><br/>Email:<input type='text'/><br/>X:<keygen name='X'/><label for='Name'>label</label></fieldset></form>", r);
+	}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/Html5BasicTests.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/Html5BasicTests.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/Html5BasicTests.java
deleted file mode 100644
index 4429833..0000000
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/dto/html5/Html5BasicTests.java
+++ /dev/null
@@ -1,37 +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.dto.html5;
-
-import static org.apache.juneau.dto.html5.HtmlBuilder.*;
-import static org.junit.Assert.*;
-
-import org.junit.*;
-
-public class Html5BasicTests {
-
-	@Test
-	public void testToString() {
-		Form f = form("bar",
-			fieldset(
-				legend("foo:"),
-				"Name:", input("text"), br(),
-				"Email:", input("text"), br(),
-				"X:", keygen().name("X"),
-				label("label")._for("Name")
-			)
-		);
-		
-		String r = f.toString();
-		assertEquals("<form action='bar'><fieldset><legend>foo:</legend>Name:<input type='text'/><br/>Email:<input type='text'/><br/>X:<keygen name='X'/><label for='Name'>label</label></fieldset></form>", r);
-	}
-}

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlIgnoreCommentsTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlIgnoreCommentsTest.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlIgnoreCommentsTest.java
new file mode 100644
index 0000000..0f9d792
--- /dev/null
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlIgnoreCommentsTest.java
@@ -0,0 +1,1251 @@
+// ***************************************************************************************************************************
+// * 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.xml;
+
+import java.text.*;
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.xml.annotation.*;
+import org.junit.*;
+import org.junit.runner.*;
+import org.junit.runners.*;
+
+/**
+ * Validates that comments in XML files are ignored.
+ */
+@RunWith(Parameterized.class)
+@SuppressWarnings("serial")
+public class XmlIgnoreCommentsTest {
+	
+	@Parameterized.Parameters
+	public static Collection<Object[]> getParameters() {
+		
+		return Arrays.asList(new Object[][] {
+			{ 	/* 0 */
+				"SimpleTypes-1",
+				String.class, 
+				"foo", 
+				"|<string>|foo|</string>|",
+				false
+			},
+			{ 	/* 1 */
+				"SimpleTypes-2",
+				Boolean.class, 
+				true, 
+				"|<boolean>|true|</boolean>|",
+				false
+			},
+			{ 	/* 2 */
+				"SimpleTypes-3",
+				int.class,
+				123,
+				"|<number>|123|</number>|",
+				false
+			},
+			{ 	/* 3 */
+				"SimpleTypes-4",
+				float.class,
+				1.23f,
+				"|<number>|1.23|</number>|",
+				false
+			},
+			{ 	/* 4 */
+				"SimpleTypes-5",
+				String.class,
+				null,
+				"|<null/>|",
+				false
+			},
+			{ 	/* 5 */
+				"Arrays-1",
+				String[].class,
+				new String[]{"foo"},
+				"|<array>|<string>|foo|</string>|</array>|",
+				false
+			},
+			{ 	/* 6 */
+				"Arrays-2",
+				String[].class,
+				new String[]{null},
+				"|<array>|<null/>|</array>|",
+				false
+			},
+			{ 	/* 7 */
+				"Arrays-3",
+				Object[].class,
+				new Object[]{"foo"},
+				"|<array>|<string>|foo|</string>|</array>|",
+				false
+			},
+			{ 	/* 8 */
+				"Arrays-4",
+				int[].class,
+				new int[]{123},
+				"|<array>|<number>|123|</number>|</array>|",
+				false
+			},
+			{ 	/* 9 */
+				"Arrays-5",
+				boolean[].class,
+				new boolean[]{true},
+				"|<array>|<boolean>|true|</boolean>|</array>|",
+				false
+			},
+			{ 	/* 10 */
+				"Arrays-6",
+				String[][].class,
+				new String[][]{{"foo"}},
+				"|<array>|<array>|<string>|foo|</string>|</array>|</array>|",
+				false
+			},
+			{ 	/* 11 */
+				"MapWithStrings",
+				MapWithStrings.class,
+				new MapWithStrings().append("k1", "v1").append("k2", null),
+				"|<object>|<k1>|v1|</k1>|<k2 _type='null'/>|</object>|",
+				false
+			},
+			{ 	/* 12 */
+				"MapsWithNumbers",
+				MapWithNumbers.class,
+				new MapWithNumbers().append("k1", 123).append("k2", 1.23).append("k3", null),
+				"|<object>|<k1>|123|</k1>|<k2>|1.23|</k2>|<k3 _type='null'/>|</object>|",
+				false
+			},
+			{ 	/* 13 */
+				"MapWithObjects",
+				MapWithObjects.class,
+				new MapWithObjects().append("k1", "v1").append("k2", 123).append("k3", 1.23).append("k4", true).append("k5", null),
+				"|<object>|<k1>|v1|</k1>|<k2 _type='number'>|123|</k2>|<k3 _type='number'>|1.23|</k3>|<k4 _type='boolean'>|true|</k4>|<k5 _type='null'/>|</object>|",
+				false
+			},
+			{ 	/* 14 */
+				"ListWithStrings",
+				ListWithStrings.class,
+				new ListWithStrings().append("foo").append(null),
+				"|<array>|<string>|foo|</string>|<null/>|</array>|",
+				false
+			},
+			{ 	/* 15 */
+				"ListWithNumbers",
+				ListWithNumbers.class,
+				new ListWithNumbers().append(123).append(1.23).append(null),
+				"|<array>|<number>|123|</number>|<number>|1.23|</number>|<null/>|</array>|",
+				false
+			},
+			{ 	/* 16 */
+				"ListWithObjects",
+				ListWithObjects.class,
+				new ListWithObjects().append("foo").append(123).append(1.23).append(true).append(null),
+				"|<array>|<string>|foo|</string>|<number>|123|</number>|<number>|1.23|</number>|<boolean>|true|</boolean>|<null/>|</array>|",
+				false
+			},
+			{ 	/* 17 */
+				"BeanWithNormalProperties",
+				BeanWithNormalProperties.class,
+				new BeanWithNormalProperties().init(),
+				"|<object>"
+					+"|<a>|foo|</a>"
+					+"|<b>|123|</b>"
+					+"|<c>|bar|</c>"
+					+"|<d _type='number'>|456|</d>"
+					+"|<e>"
+						+"|<h>|qux|</h>"
+					+"|</e>"
+					+"|<f>"
+						+"|<string>|baz|</string>"
+					+"|</f>"
+					+"|<g>"
+						+"|<number>|789|</number>"
+					+"|</g>"
+				+"|</object>|",
+				false
+			},
+			{ 	/* 18 */
+				"BeanWithMapProperties",
+				BeanWithMapProperties.class,
+				new BeanWithMapProperties().init(),
+				"|<object>"
+					+"|<a>"
+						+"|<k1>|foo|</k1>"
+					+"|</a>"
+					+"|<b>"
+						+"|<k2>|123|</k2>"
+					+"|</b>"
+					+"|<c>"
+						+"|<k3>|bar|</k3>"
+						+"|<k4 _type='number'>|456|</k4>"
+						+"|<k5 _type='boolean'>|true|</k5>"
+						+"|<k6 _type='null'/>"
+					+"|</c>"
+				+"|</object>|",
+				false
+			},
+			{ 	/* 19 */
+				"BeanWithTypeName",
+				BeanWithTypeName.class,
+				new BeanWithTypeName().init(),
+				"|<X>|<a>|123|</a>|<b>|foo|</b>|</X>|",
+				false
+			},
+			{ 	/* 20 */
+				"BeanWithPropertiesWithTypeNames",
+				BeanWithPropertiesWithTypeNames.class,
+				new BeanWithPropertiesWithTypeNames().init(),
+				"|<object>|<b1>|<b>|foo|</b>|</b1>|<b2 _type='B'>|<b>|foo|</b>|</b2>|</object>|",
+				false
+			},
+			{ 	/* 21 */
+				"BeanWithPropertiesWithArrayTypeNames",
+				BeanWithPropertiesWithArrayTypeNames.class,
+				new BeanWithPropertiesWithArrayTypeNames().init(),
+				"|<object>"
+					+"|<b1>"
+						+"|<B>"
+							+"|<b>|foo|</b>"
+						+"|</B>"
+					+"|</b1>"
+					+"|<b2>"
+						+"|<B>"
+							+"|<b>|foo|</b>"
+						+"|</B>"
+					+"|</b2>"
+					+"|<b3>"
+						+"|<B>"
+							+"|<b>|foo|</b>"
+						+"|</B>"
+					+"|</b3>"
+				+"|</object>|",
+				false
+			},
+			{ 	/* 22 */
+				"BeanWithPropertiesWithArray2dTypeNames",
+				BeanWithPropertiesWith2dArrayTypeNames.class,
+				new BeanWithPropertiesWith2dArrayTypeNames().init(),
+				"|<object>"
+					+"|<b1>"
+						+"|<array>"
+							+"|<B>"
+								+"|<b>|foo|</b>"
+							+"|</B>"
+						+"|</array>"
+					+"|</b1>"
+					+"|<b2>"
+						+"|<array>"
+							+"|<B>"
+								+"|<b>|foo|</b>"
+							+"|</B>"
+						+"|</array>"
+					+"|</b2>"
+					+"|<b3>"
+						+"|<array>"
+							+"|<B>"
+								+"|<b>|foo|</b>"
+							+"|</B>"
+						+"|</array>"
+					+"|</b3>"
+				+"|</object>|",
+				false
+			},
+			{ 	/* 23 */
+				"BeanWithPropertiesWithMapTypeNames",
+				BeanWithPropertiesWithMapTypeNames.class,
+				new BeanWithPropertiesWithMapTypeNames().init(),
+				"|<object>"
+					+"|<b1>"
+						+"|<k1>"
+							+"|<b>|foo|</b>"
+						+"|</k1>"
+					+"|</b1>"
+					+"|<b2>"
+						+"|<k2 _type='B'>"
+							+"|<b>|foo|</b>"
+						+"|</k2>"
+					+"|</b2>"
+				+"|</object>|",
+				false
+			},
+			{ 	/* 24 */
+				"BeanWithChildTypeNames",
+				BeanWithChildTypeNames.class,
+				new BeanWithChildTypeNames().init(),
+				"|<object>"
+					+"|<a>"
+						+"|<fx>|fx1|</fx>"
+					+"|</a>"
+					+"|<b _type='X'>"
+						+"|<fx>|fx1|</fx>"
+					+"|</b>"
+					+"|<c>"
+						+"|<X>"
+							+"|<fx>|fx1|</fx>"
+						+"|</X>"
+					+"|</c>"
+					+"|<d>"
+						+"|<X>"
+							+"|<fx>|fx1|</fx>"
+						+"|</X>"
+					+"|</d>"
+				+"|</object>|",
+				false
+			},
+			{ 	/* 25 */
+				"BeanWithChildName",
+				BeanWithChildName.class,
+				new BeanWithChildName().init(),
+				"|<object>|<a>|<X>|foo|</X>|<X>|bar|</X>|</a>|<b>|<Y>|123|</Y>|<Y>|456|</Y>|</b>|</object>|",
+				false
+			},
+			{ 	/* 26 */
+				"BeanWithXmlFormatAttrProperty",
+				BeanWithXmlFormatAttrProperty.class,
+				new BeanWithXmlFormatAttrProperty().init(),
+				"|<object a='foo' b='123'/>|",
+				false
+			},
+			{ 	/* 27 */
+				"BeanWithXmlFormatAttrs",
+				BeanWithXmlFormatAttrs.class,
+				new BeanWithXmlFormatAttrs().init(),
+				"|<object a='foo' b='123'/>|",
+				false
+			},
+			{ 	/* 28 */
+				"BeanWithXmlFormatElementProperty",
+				BeanWithXmlFormatElementProperty.class,
+				new BeanWithXmlFormatElementProperty().init(),
+				"|<object a='foo'><b>123</b></object>|",
+				false
+			},
+			{ 	/* 29 */
+				"BeanWithXmlFormatAttrsProperty",
+				BeanWithXmlFormatAttrsProperty.class,
+				new BeanWithXmlFormatAttrsProperty().init(),
+				"|<object k1='foo' k2='123' b='456'/>|",
+				false
+			},
+			{ 	/* 30 */
+				"BeanWithXmlFormatCollapsedProperty",
+				BeanWithXmlFormatCollapsedProperty.class,
+				new BeanWithXmlFormatCollapsedProperty().init(),
+				"|<object>|<A>|foo|</A>|<A>|bar|</A>|<B>|123|</B>|<B>|456|</B>|</object>|",
+				false
+			},
+			{ 	/* 31 */
+				"BeanWithXmlFormatTextProperty",
+				BeanWithXmlFormatTextProperty.class,
+				new BeanWithXmlFormatTextProperty().init(),
+				"|<object a='foo'>|bar|</object>|",
+				false
+			},
+			{ 	/* 32 */
+				"BeanWithXmlFormatXmlTextProperty",
+				BeanWithXmlFormatXmlTextProperty.class,
+				new BeanWithXmlFormatXmlTextProperty().init(),
+				"|<object a='foo'>|bar|<b>|baz|</b>|qux|</object>|",
+				false
+			},
+			{ 	/* 33 */
+				"BeanWithXmlFormatElementsPropertyCollection",
+				BeanWithXmlFormatElementsPropertyCollection.class,
+				new BeanWithXmlFormatElementsPropertyCollection().init(),
+				"|<object a='foo'>|<string>|bar|</string>|<string>|baz|</string>|<number>|123|</number>|<boolean>|true|</boolean>|<null/>|</object>|",
+				false
+			},
+			{ 	/* 34 */
+				"BeanWithMixedContent",
+				BeanWithMixedContent.class,
+				new BeanWithMixedContent().init(),
+				"|<object>|foo|<X fx='fx1'/>|bar|<Y fy='fy1'/>|baz|</object>|",
+				true
+			},
+			{ 	/* 35 */
+				"BeanWithSpecialCharacters",
+				BeanWithSpecialCharacters.class,
+				new BeanWithSpecialCharacters().init(),
+				"|<object>|<a>|_x0020_ _x0008__x000C_&#x000a;&#x0009;&#x000d; _x0020_|</a>|</object>|",
+				false
+			},
+			{ 	/* 36 */
+				"BeanWithSpecialCharacters2",
+				BeanWithSpecialCharacters2.class,
+				new BeanWithSpecialCharacters2().init(),
+				"|<_x0020__x0020__x0008__x000C__x000A__x0009__x000D__x0020__x0020_>|<_x0020__x0020__x0008__x000C__x000A__x0009__x000D__x0020__x0020_>|_x0020_ _x0008__x000C_&#x000a;&#x0009;&#x000d; _x0020_|</_x0020__x0020__x0008__x000C__x000A__x0009__x000D__x0020__x0020_>|</_x0020__x0020__x0008__x000C__x000A__x0009__x000D__x0020__x0020_>|",
+				false
+			},
+			{ 	/* 37 */
+				"BeanWithNullProperties",
+				BeanWithNullProperties.class,
+				new BeanWithNullProperties(),
+				"|<object/>|",
+				false
+			},
+			{ 	/* 38 */
+				"BeanWithAbstractFields",
+				BeanWithAbstractFields.class,
+				new BeanWithAbstractFields().init(),
+				"|<object>"
+					+"|<a>"
+						+"|<a>|foo|</a>"
+					+"|</a>"
+					+"|<ia _type='A'>"
+						+"|<a>|foo|</a>"
+					+"|</ia>"
+					+"|<aa _type='A'>"
+						+"|<a>|foo|</a>"
+					+"|</aa>"
+					+"|<o _type='A'>"
+						+"|<a>|foo|</a>"
+					+"|</o>"
+				+"|</object>|",
+				false
+			},
+			{ 	/* 39 */
+				"BeanWithAbstractArrayFields",
+				BeanWithAbstractArrayFields.class,
+				new BeanWithAbstractArrayFields().init(),
+				"|<object>"
+					+"|<a>"
+						+"|<A>"
+							+"|<a>|foo|</a>"
+						+"|</A>"
+					+"|</a>"
+					+"|<ia1>"
+						+"|<A>"
+							+"|<a>|foo|</a>"
+						+"|</A>"
+					+"|</ia1>"
+					+"|<ia2>"
+						+"|<A>"
+							+"|<a>|foo|</a>"
+						+"|</A>"
+					+"|</ia2>"
+					+"|<aa1>"
+						+"|<A>"
+							+"|<a>|foo|</a>"
+						+"|</A>"
+					+"|</aa1>"
+					+"|<aa2>"
+						+"|<A>"
+							+"|<a>|foo|</a>"
+						+"|</A>"
+					+"|</aa2>"
+					+"|<o1>"
+						+"|<A>"
+							+"|<a>|foo|</a>"
+						+"|</A>"
+					+"|</o1>"
+					+"|<o2>"
+						+"|<A>"
+							+"|<a>|foo|</a>"
+						+"|</A>"
+					+"|</o2>"
+				+"|</object>",
+				false
+			},
+			{ 	/* 40 */
+				"BeanWithAbstractMapFields",
+				BeanWithAbstractMapFields.class,
+				new BeanWithAbstractMapFields().init(),
+				"|<object>"
+					+"|<a>"
+						+"|<k1>"
+							+"|<a>|foo|</a>"
+						+"|</k1>"
+					+"|</a>"
+					+"|<b>"
+						+"|<k2 _type='A'>"
+							+"|<a>|foo|</a>"
+						+"|</k2>"
+					+"|</b>"
+					+"|<c>"
+						+"|<k3 _type='A'>"
+							+"|<a>|foo|</a>"
+						+"|</k3>"
+					+"|</c>"
+				+"|</object>",
+				false
+			},
+			{ 	/* 41 */
+				"BeanWithAbstractMapArrayFields",
+				BeanWithAbstractMapArrayFields.class,
+				new BeanWithAbstractMapArrayFields().init(),
+				"|<object>"
+					+"|<a>"
+						+"|<a1>"
+							+"|<A>"
+								+"|<a>|foo|</a>"
+							+"|</A>"
+						+"|</a1>"
+					+"|</a>"
+					+"|<ia>"
+						+"|<ia1>"
+							+"|<A>"
+								+"|<a>|foo|</a>"
+							+"|</A>"
+						+"|</ia1>"
+						+"|<ia2>"
+							+"|<A>"
+								+"|<a>|foo|</a>"
+							+"|</A>"
+						+"|</ia2>"
+					+"|</ia>"
+					+"|<aa>"
+						+"|<aa1>"
+							+"|<A>"
+								+"|<a>|foo|</a>"
+							+"|</A>"
+						+"|</aa1>"
+						+"|<aa2>"
+							+"|<A>"
+								+"|<a>|foo|</a>"
+							+"|</A>"
+						+"|</aa2>"
+					+"|</aa>"
+					+"|<o>"
+						+"|<o1>"
+							+"|<A>"
+								+"|<a>|foo|</a>"
+							+"|</A>"
+						+"|</o1>"
+						+"|<o2>"
+							+"|<A>"
+								+"|<a>|foo|</a>"
+							+"|</A>"
+						+"|</o2>"
+					+"|</o>"
+				+"|</object>",
+				false
+			},
+			{ 	/* 42 */
+				"BeanWithWhitespaceTextFields-1",
+				BeanWithWhitespaceTextFields.class,
+				new BeanWithWhitespaceTextFields().init(null),
+				"|<object/>|",
+				false
+			},
+			{ 	/* 43 */
+				"BeanWithWhitespaceTextFields-2",
+				BeanWithWhitespaceTextFields.class,
+				new BeanWithWhitespaceTextFields().init(""),
+				"|<object>|_xE000_|</object>|",
+				false
+			},
+			{ 	/* 44 */
+				"BeanWithWhitespaceTextFields-3",
+				BeanWithWhitespaceTextFields.class,
+				new BeanWithWhitespaceTextFields().init(" "),
+				"|<object>|_x0020_|</object>|",
+				false
+			},
+			{ 	/* 45 */
+				"BeanWithWhitespaceTextFields-4",
+				BeanWithWhitespaceTextFields.class,
+				new BeanWithWhitespaceTextFields().init("  "),
+				"|<object>|_x0020__x0020_|</object>|",
+				false
+			},
+			{ 	/* 46 */
+				"BeanWithWhitespaceTextFields-5",
+				BeanWithWhitespaceTextFields.class,
+				new BeanWithWhitespaceTextFields().init(" foo\n\tbar "),
+				"|<object>|_x0020_foo&#x000a;&#x0009;bar_x0020_|</object>|",
+				false
+			},
+			{ 	/* 47 */
+				"BeanWithWhitespaceTextPwsFields-1",
+				BeanWithWhitespaceTextPwsFields.class,
+				new BeanWithWhitespaceTextPwsFields().init(null),
+				"|<object/>|",
+				false
+			},
+			{ 	/* 48 */
+				"BeanWithWhitespaceTextPwsFields-2",
+				BeanWithWhitespaceTextPwsFields.class,
+				new BeanWithWhitespaceTextPwsFields().init(""),
+				"|<object>|_xE000_|</object>|",
+				true
+			},
+			{ 	/* 49 */
+				"BeanWithWhitespaceTextPwsFields-3",
+				BeanWithWhitespaceTextPwsFields.class,
+				new BeanWithWhitespaceTextPwsFields().init(" "),
+				"|<object>| |</object>|",
+				true
+			},
+			{ 	/* 50 */
+				"BeanWithWhitespaceTextPwsFields-4",
+				BeanWithWhitespaceTextPwsFields.class,
+				new BeanWithWhitespaceTextPwsFields().init("  "),
+				"|<object>|  |</object>|",
+				true
+			},
+			{ 	/* 51 */
+				"BeanWithWhitespaceTextPwsFields-5",
+				BeanWithWhitespaceTextPwsFields.class,
+				new BeanWithWhitespaceTextPwsFields().init("  foobar  "),
+				"|<object>|  foobar  |</object>|",
+				true
+			},
+			{ 	/* 52 */
+				"BeanWithWhitespaceMixedFields-1",
+				BeanWithWhitespaceMixedFields.class,
+				new BeanWithWhitespaceMixedFields().init(null),
+				"|<object/>|",
+				false
+			},
+			{ 	/* 53 */
+				"BeanWithWhitespaceMixedFields-3",
+				BeanWithWhitespaceMixedFields.class,
+				new BeanWithWhitespaceMixedFields().init(new String[]{""}),
+				"|<object>|_xE000_|</object>|",
+				true
+			},
+			{ 	/* 54 */
+				"BeanWithWhitespaceMixedFields-4",
+				BeanWithWhitespaceMixedFields.class,
+				new BeanWithWhitespaceMixedFields().init(new String[]{" "}),
+				"|<object>|_x0020_|</object>|",
+				true
+			},
+			{ 	/* 55 */
+				"BeanWithWhitespaceMixedFields-5",
+				BeanWithWhitespaceMixedFields.class,
+				new BeanWithWhitespaceMixedFields().init(new String[]{"  "}),
+				"|<object>|_x0020__x0020_|</object>|",
+				true
+			},
+			{ 	/* 56 */
+				"BeanWithWhitespaceMixedFields-6",
+				BeanWithWhitespaceMixedFields.class,
+				new BeanWithWhitespaceMixedFields().init(new String[]{"  foobar  "}),
+				"|<object>|_x0020_ foobar _x0020_|</object>|",
+				true
+			},
+			{ 	/* 57 */
+				"BeanWithWhitespaceMixedPwsFields-1",
+				BeanWithWhitespaceMixedPwsFields.class,
+				new BeanWithWhitespaceMixedPwsFields().init(null),
+				"|<object/>|",
+				false
+			},
+			{ 	/* 58 */
+				"BeanWithWhitespaceMixedPwsFields-3",
+				BeanWithWhitespaceMixedPwsFields.class,
+				new BeanWithWhitespaceMixedPwsFields().init(new String[]{""}),
+				"|<object>|_xE000_|</object>|",
+				true
+			},
+			{ 	/* 59 */
+				"BeanWithWhitespaceMixedPwsFields-4",
+				BeanWithWhitespaceMixedPwsFields.class,
+				new BeanWithWhitespaceMixedPwsFields().init(new String[]{" "}),
+				"|<object>| |</object>|",
+				true
+			},
+			{ 	/* 60 */
+				"BeanWithWhitespaceMixedPwsFields-5",
+				BeanWithWhitespaceMixedPwsFields.class,
+				new BeanWithWhitespaceMixedPwsFields().init(new String[]{"  "}),
+				"|<object>|  |</object>|",
+				true
+			},
+			{ 	/* 61 */
+				"BeanWithWhitespaceMixedPwsFields-6",
+				BeanWithWhitespaceMixedPwsFields.class,
+				new BeanWithWhitespaceMixedPwsFields().init(new String[]{"  foobar  "}),
+				"|<object>|  foobar  |</object>|",
+				true
+			},
+		});
+	}
+	
+	private String label;
+	private Class<?> type;
+	private Object expected;
+	private String input;
+	private boolean skipWsTests;
+
+	public XmlIgnoreCommentsTest(String label, Class<?> type, Object expected, String input, boolean skipWsTests) throws Exception {
+		this.label = label;
+		this.type = type;
+		this.expected = expected;
+		this.input = input;
+		this.skipWsTests = skipWsTests;
+	}
+	
+	@Test
+	public void testNoComment() throws Exception {
+		try {
+			Object actual = XmlParser.DEFAULT.parse(input.replace("|", ""), type);
+			TestUtils.assertEqualObjects(expected, actual);
+		} catch (ComparisonFailure e) {
+			throw new ComparisonFailure(MessageFormat.format("Test ''{0}'' failed with comparison error", label), e.getExpected(), e.getActual());
+		} catch (Exception e) {
+			throw new FormattedRuntimeException(e, "Test ''{0}'' failed with error ''{1}''", label, e.getMessage());
+		}
+	}
+
+	@Test
+	public void testNormalComment() throws Exception {
+		try {
+			Object actual = XmlParser.DEFAULT.parse(input.replace("|", "<!--x-->"), type);
+			TestUtils.assertEqualObjects(expected, actual);
+		} catch (ComparisonFailure e) {
+			throw new ComparisonFailure(MessageFormat.format("Test ''{0}'' failed with comparison error", label), e.getExpected(), e.getActual());
+		} catch (Exception e) {
+			throw new FormattedRuntimeException(e, "Test ''{0}'' failed with error ''{1}''", label, e.getMessage());
+		}
+	}
+	
+	@Test
+	public void testCommentWithWhitespace() throws Exception {
+		try {
+			Object actual = XmlParser.DEFAULT.parse(input.replace("|", " \n <!-- \n x \n --> \n "), type);
+			if (! skipWsTests)
+				TestUtils.assertEqualObjects(expected, actual);
+		} catch (ComparisonFailure e) {
+			throw new ComparisonFailure(MessageFormat.format("Test ''{0}'' failed with comparison error", label), e.getExpected(), e.getActual());
+		} catch (Exception e) {
+			throw new FormattedRuntimeException(e, "Test ''{0}'' failed with error ''{1}''", label, e.getMessage());
+		}
+	}
+	
+	@Test
+	public void testDoubleCommentsWithWhitespace() throws Exception {
+		try {
+			Object actual = XmlParser.DEFAULT.parse(input.replace("|", " \n <!-- \n x \n --> \n \n <!-- \n x \n --> \n "), type);
+			if (! skipWsTests)
+				TestUtils.assertEqualObjects(expected, actual);
+		} catch (ComparisonFailure e) {
+			throw new ComparisonFailure(MessageFormat.format("Test ''{0}'' failed with comparison error", label), e.getExpected(), e.getActual());
+		} catch (Exception e) {
+			throw new FormattedRuntimeException(e, "Test ''{0}'' failed with error ''{1}''", label, e.getMessage());
+		}
+	}
+
+	//--------------------------------------------------------------------------------
+	// Test beans
+	//--------------------------------------------------------------------------------
+
+	public static class MapWithStrings extends LinkedHashMap<String,String> {
+		public MapWithStrings append(String key, String value) {
+			put(key, value);
+			return this;
+		}
+	}
+
+	public static class MapWithNumbers extends LinkedHashMap<String,Number> {
+		public MapWithNumbers append(String key, Number value) {
+			put(key, value);
+			return this;
+		}
+	}
+
+	public static class MapWithObjects extends LinkedHashMap<String,Object> {
+		public MapWithObjects append(String key, Object value) {
+			put(key, value);
+			return this;
+		}
+	}
+
+	public static class ListWithStrings extends ArrayList<String> {
+		public ListWithStrings append(String value) {
+			this.add(value);
+			return this;
+		}
+	}
+
+	public static class ListWithNumbers extends ArrayList<Number> {
+		public ListWithNumbers append(Number value) {
+			this.add(value);
+			return this;
+		}
+	}
+
+	public static class ListWithObjects extends ArrayList<Object> {
+		public ListWithObjects append(Object value) {
+			this.add(value);
+			return this;
+		}
+	}
+
+	public static class BeanWithNormalProperties {
+		public String a;
+		public int b;
+		public Object c;
+		public Object d;
+		public Bean1a e;
+		public String[] f;
+		public int[] g;
+
+		BeanWithNormalProperties init() {
+			a = "foo";
+			b = 123;
+			c = "bar";
+			d = 456;
+			e = new Bean1a().init();
+			f = new String[]{ "baz" };
+			g = new int[]{ 789 };
+			return this;
+		}
+	}
+
+	public static class Bean1a {
+		public String h;
+
+		Bean1a init() {
+			h = "qux";
+			return this;
+		}
+	}
+
+	public static class BeanWithMapProperties {
+		@BeanProperty(type=MapWithStrings.class)
+		public Map<String,String> a;
+		@BeanProperty(type=MapWithNumbers.class)
+		public Map<String,Number> b;
+		@BeanProperty(type=MapWithObjects.class)
+		public Map<String,Object> c;
+
+		BeanWithMapProperties init() {
+			a = new MapWithStrings().append("k1","foo");
+			b = new MapWithNumbers().append("k2",123);
+			c = new MapWithObjects().append("k3","bar").append("k4",456).append("k5",true).append("k6",null);
+			return this;
+		}
+	}
+
+	@Bean(typeName="X")
+	public static class BeanWithTypeName {
+		public int a;
+		public String b;
+
+		BeanWithTypeName init() {
+			a = 123;
+			b = "foo";
+			return this;
+		}
+	}
+
+	@Bean(beanDictionary={B.class})
+	public static class BeanWithPropertiesWithTypeNames {
+		public B b1;
+		public Object b2;
+
+		BeanWithPropertiesWithTypeNames init() {
+			b1 = new B().init();
+			b2 = new B().init();
+			return this;
+		}
+	}
+
+	@Bean(beanDictionary={B.class})
+	public static class BeanWithPropertiesWithArrayTypeNames {
+		public B[] b1;
+		public Object[] b2;
+		public Object[] b3;
+
+		BeanWithPropertiesWithArrayTypeNames init() {
+			b1 = new B[]{new B().init()};
+			b2 = new B[]{new B().init()};
+			b3 = new Object[]{new B().init()};
+			return this;
+		}
+	}
+
+	@Bean(beanDictionary={B.class})
+	public static class BeanWithPropertiesWith2dArrayTypeNames {
+		public B[][] b1;
+		public Object[][] b2;
+		public Object[][] b3;
+
+		BeanWithPropertiesWith2dArrayTypeNames init() {
+			b1 = new B[][]{{new B().init()}};
+			b2 = new B[][]{{new B().init()}};
+			b3 = new Object[][]{{new B().init()}};
+			return this;
+		}
+	}
+
+	@Bean(beanDictionary={B.class})
+	public static class BeanWithPropertiesWithMapTypeNames {
+		public Map<String,B> b1;
+		public Map<String,Object> b2;
+
+		BeanWithPropertiesWithMapTypeNames init() {
+			b1 = new HashMap<String,B>();
+			b1.put("k1", new B().init());
+			b2 = new HashMap<String,Object>();
+			b2.put("k2", new B().init());
+			return this;
+		}
+	}
+
+	@Bean(typeName="B")
+	public static class B {
+		public String b;
+
+		B init() {
+			b = "foo";
+			return this;
+		}
+	}
+
+	public static class BeanWithChildTypeNames {
+		public BeanX a;
+		@BeanProperty(beanDictionary=BeanX.class)
+		public Object b;
+		public BeanX[] c;
+		@BeanProperty(beanDictionary=BeanX.class)
+		public Object[] d;
+		BeanWithChildTypeNames init() {
+			a = new BeanX().init();
+			b = new BeanX().init();
+			c = new BeanX[]{new BeanX().init()};
+			d = new Object[]{new BeanX().init()};
+			return this;
+		}
+	}
+
+	public static class BeanWithChildName {
+		@Xml(childName = "X")
+		public String[] a;
+		@Xml(childName = "Y")
+		public int[] b;
+		BeanWithChildName init() {
+			a = new String[] { "foo", "bar" };
+			b = new int[] { 123, 456 };
+			return this;
+		}
+	}
+
+	public static class BeanWithXmlFormatAttrProperty {
+		@Xml(format=XmlFormat.ATTR)
+		public String a;
+		@Xml(format=XmlFormat.ATTR)
+		public int b;
+		BeanWithXmlFormatAttrProperty init() {
+			a = "foo";
+			b = 123;
+			return this;
+		}
+	}
+
+	@Xml(format=XmlFormat.ATTRS)
+	public static class BeanWithXmlFormatAttrs {
+		public String a;
+		public int b;
+		BeanWithXmlFormatAttrs init() {
+			a = "foo";
+			b = 123;
+			return this;
+		}
+	}
+
+	@Xml(format=XmlFormat.ATTRS)
+	public static class BeanWithXmlFormatElementProperty {
+		public String a;
+		@Xml(format=XmlFormat.ELEMENT)
+		public int b;
+		BeanWithXmlFormatElementProperty init() {
+			a = "foo";
+			b = 123;
+			return this;
+		}
+	}
+
+	public static class BeanWithXmlFormatAttrsProperty {
+		@Xml(format=XmlFormat.ATTRS)
+		public Map<String,Object> a;
+		@Xml(format=XmlFormat.ATTR)
+		public int b;
+		BeanWithXmlFormatAttrsProperty init() {
+			a = new ObjectMap().append("k1", "foo").append("k2", "123");
+			b = 456;
+			return this;
+		}
+	}
+
+	public static class BeanWithXmlFormatCollapsedProperty {
+		@Xml(childName="A",format=XmlFormat.COLLAPSED)
+		public String[] a;
+		@Xml(childName="B",format=XmlFormat.COLLAPSED)
+		public int[] b;
+		BeanWithXmlFormatCollapsedProperty init() {
+			a = new String[]{"foo","bar"};
+			b = new int[]{123,456};
+			return this;
+		}
+	}
+
+	public static class BeanWithXmlFormatTextProperty {
+		@Xml(format=XmlFormat.ATTR)
+		public String a;
+		@Xml(format=XmlFormat.TEXT)
+		public String b;
+		BeanWithXmlFormatTextProperty init() {
+			a = "foo";
+			b = "bar";
+			return this;
+		}
+	}
+
+	public static class BeanWithXmlFormatXmlTextProperty {
+		@Xml(format=XmlFormat.ATTR)
+		public String a;
+		@Xml(format=XmlFormat.XMLTEXT)
+		public String b;
+		BeanWithXmlFormatXmlTextProperty init() {
+			a = "foo";
+			b = "bar<b>baz</b>qux";
+			return this;
+		}
+	}
+
+	public static class BeanWithXmlFormatElementsPropertyCollection {
+		@Xml(format=XmlFormat.ATTR)
+		public String a;
+		@Xml(format=XmlFormat.ELEMENTS)
+		public Object[] b;
+		BeanWithXmlFormatElementsPropertyCollection init() {
+			a = "foo";
+			b = new Object[]{"bar","baz",123,true,null};
+			return this;
+		}
+	}
+
+	public static class BeanWithMixedContent {
+		@Xml(format=XmlFormat.MIXED)
+		@BeanProperty(beanDictionary={BeanXSimple.class, BeanYSimple.class})
+		public Object[] a;
+		BeanWithMixedContent init() {
+			a = new Object[]{
+				"foo",
+				new BeanXSimple().init(),
+				"bar",
+				new BeanYSimple().init(),
+				"baz"
+			};
+			return this;
+		}
+	}
+
+	@Bean(typeName="X")
+	public static class BeanX {
+		public String fx;
+		BeanX init() {
+			fx = "fx1";
+			return this;
+		}
+	}
+
+	@Bean(typeName="X")
+	public static class BeanXSimple {
+		@Xml(format=XmlFormat.ATTR)
+		public String fx;
+		BeanXSimple init() {
+			fx = "fx1";
+			return this;
+		}
+	}
+
+	@Bean(typeName="Y")
+	public static class BeanY {
+		public String fy;
+		BeanY init() {
+			fy = "fy1";
+			return this;
+		}
+	}
+
+	@Bean(typeName="Y")
+	public static class BeanYSimple {
+		@Xml(format=XmlFormat.ATTR)
+		public String fy;
+		BeanYSimple init() {
+			fy = "fy1";
+			return this;
+		}
+	}
+
+	public static class BeanWithSpecialCharacters {
+		public String a;
+
+		BeanWithSpecialCharacters init() {
+			a = "  \b\f\n\t\r  ";
+			return this;
+		}
+	}
+
+	@Bean(typeName="  \b\f\n\t\r  ")
+	public static class BeanWithSpecialCharacters2 {
+
+		@BeanProperty(name="  \b\f\n\t\r  ")
+		public String a;
+
+		BeanWithSpecialCharacters2 init() {
+			a = "  \b\f\n\t\r  ";
+			return this;
+		}
+	}
+
+	public static class BeanWithNullProperties {
+		public String a;
+		public String[] b;
+	}
+
+	@Bean(beanDictionary={A.class})
+	public static class BeanWithAbstractFields {
+		public A a;
+		public IA ia;
+		public AA aa;
+		public Object o;
+
+		BeanWithAbstractFields init() {
+			ia = new A().init();
+			aa = new A().init();
+			a = new A().init();
+			o = new A().init();
+			return this;
+		}
+	}
+
+	@Bean(beanDictionary={A.class})
+	public static class BeanWithAbstractArrayFields {
+		public A[] a;
+		public IA[] ia1, ia2;
+		public AA[] aa1, aa2;
+		public Object[] o1, o2;
+
+		BeanWithAbstractArrayFields init() {
+			a = new A[]{new A().init()};
+			ia1 = new A[]{new A().init()};
+			aa1 = new A[]{new A().init()};
+			o1 = new A[]{new A().init()};
+			ia2 = new IA[]{new A().init()};
+			aa2 = new AA[]{new A().init()};
+			o2 = new Object[]{new A().init()};
+			return this;
+		}
+	}
+
+	@Bean(beanDictionary={A.class})
+	public static class BeanWithAbstractMapFields {
+		public Map<String,A> a;
+		public Map<String,AA> b;
+		public Map<String,Object> c;
+
+		BeanWithAbstractMapFields init() {
+			a = new HashMap<String,A>();
+			b = new HashMap<String,AA>();
+			c = new HashMap<String,Object>();
+			a.put("k1", new A().init());
+			b.put("k2", new A().init());
+			c.put("k3", new A().init());
+			return this;
+		}
+	}
+
+	@Bean(beanDictionary={A.class})
+	public static class BeanWithAbstractMapArrayFields {
+		public Map<String,A[]> a;
+		public Map<String,IA[]> ia;
+		public Map<String,AA[]> aa;
+		public Map<String,Object[]> o;
+
+		BeanWithAbstractMapArrayFields init() {
+			a = new LinkedHashMap<String,A[]>();
+			ia = new LinkedHashMap<String,IA[]>();
+			aa = new LinkedHashMap<String,AA[]>();
+			o = new LinkedHashMap<String,Object[]>();
+			a.put("a1", new A[]{new A().init()});
+			ia.put("ia1", new A[]{new A().init()});
+			ia.put("ia2", new IA[]{new A().init()});
+			aa.put("aa1", new A[]{new A().init()});
+			aa.put("aa2", new AA[]{new A().init()});
+			o.put("o1", new A[]{new A().init()});
+			o.put("o2", new Object[]{new A().init()});
+			return this;
+		}
+	}
+
+	public static interface IA {
+		public String getA();
+		public void setA(String a);
+	}
+
+	public static abstract class AA implements IA {}
+
+	@Bean(typeName="A")
+	public static class A extends AA {
+		private String a;
+
+		@Override
+		public String getA() {
+			return a;
+		}
+
+		@Override
+		public void setA(String a) {
+			this.a = a;
+		}
+
+		A init() {
+			this.a = "foo";
+			return this;
+		}
+	}
+
+	public static class BeanWithWhitespaceTextFields {
+		@Xml(format=XmlFormat.TEXT)
+		public String a;
+
+		public BeanWithWhitespaceTextFields init(String s) {
+			a = s;
+			return this;
+		}
+	}
+
+	public static class BeanWithWhitespaceTextPwsFields {
+		@Xml(format=XmlFormat.TEXT_PWS)
+		public String a;
+
+		public BeanWithWhitespaceTextPwsFields init(String s) {
+			a = s;
+			return this;
+		}
+	}
+
+	public static class BeanWithWhitespaceMixedFields {
+		@Xml(format=XmlFormat.MIXED)
+		public String[] a;
+
+		public BeanWithWhitespaceMixedFields init(String[] s) {
+			a = s;
+			return this;
+		}
+	}
+
+	public static class BeanWithWhitespaceMixedPwsFields {
+		@Xml(format=XmlFormat.MIXED_PWS)
+		public String[] a;
+
+		public BeanWithWhitespaceMixedPwsFields init(String[] s) {
+			a = s;
+			return this;
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlTestIgnoreComments.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlTestIgnoreComments.java b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlTestIgnoreComments.java
deleted file mode 100644
index 9e125ea..0000000
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/xml/XmlTestIgnoreComments.java
+++ /dev/null
@@ -1,1251 +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.xml;
-
-import java.text.*;
-import java.util.*;
-
-import org.apache.juneau.*;
-import org.apache.juneau.annotation.*;
-import org.apache.juneau.xml.annotation.*;
-import org.junit.*;
-import org.junit.runner.*;
-import org.junit.runners.*;
-
-/**
- * Validates that comments in XML files are ignored.
- */
-@RunWith(Parameterized.class)
-@SuppressWarnings("serial")
-public class XmlTestIgnoreComments {
-	
-	@Parameterized.Parameters
-	public static Collection<Object[]> getParameters() {
-		
-		return Arrays.asList(new Object[][] {
-			{ 	/* 0 */
-				"SimpleTypes-1",
-				String.class, 
-				"foo", 
-				"|<string>|foo|</string>|",
-				false
-			},
-			{ 	/* 1 */
-				"SimpleTypes-2",
-				Boolean.class, 
-				true, 
-				"|<boolean>|true|</boolean>|",
-				false
-			},
-			{ 	/* 2 */
-				"SimpleTypes-3",
-				int.class,
-				123,
-				"|<number>|123|</number>|",
-				false
-			},
-			{ 	/* 3 */
-				"SimpleTypes-4",
-				float.class,
-				1.23f,
-				"|<number>|1.23|</number>|",
-				false
-			},
-			{ 	/* 4 */
-				"SimpleTypes-5",
-				String.class,
-				null,
-				"|<null/>|",
-				false
-			},
-			{ 	/* 5 */
-				"Arrays-1",
-				String[].class,
-				new String[]{"foo"},
-				"|<array>|<string>|foo|</string>|</array>|",
-				false
-			},
-			{ 	/* 6 */
-				"Arrays-2",
-				String[].class,
-				new String[]{null},
-				"|<array>|<null/>|</array>|",
-				false
-			},
-			{ 	/* 7 */
-				"Arrays-3",
-				Object[].class,
-				new Object[]{"foo"},
-				"|<array>|<string>|foo|</string>|</array>|",
-				false
-			},
-			{ 	/* 8 */
-				"Arrays-4",
-				int[].class,
-				new int[]{123},
-				"|<array>|<number>|123|</number>|</array>|",
-				false
-			},
-			{ 	/* 9 */
-				"Arrays-5",
-				boolean[].class,
-				new boolean[]{true},
-				"|<array>|<boolean>|true|</boolean>|</array>|",
-				false
-			},
-			{ 	/* 10 */
-				"Arrays-6",
-				String[][].class,
-				new String[][]{{"foo"}},
-				"|<array>|<array>|<string>|foo|</string>|</array>|</array>|",
-				false
-			},
-			{ 	/* 11 */
-				"MapWithStrings",
-				MapWithStrings.class,
-				new MapWithStrings().append("k1", "v1").append("k2", null),
-				"|<object>|<k1>|v1|</k1>|<k2 _type='null'/>|</object>|",
-				false
-			},
-			{ 	/* 12 */
-				"MapsWithNumbers",
-				MapWithNumbers.class,
-				new MapWithNumbers().append("k1", 123).append("k2", 1.23).append("k3", null),
-				"|<object>|<k1>|123|</k1>|<k2>|1.23|</k2>|<k3 _type='null'/>|</object>|",
-				false
-			},
-			{ 	/* 13 */
-				"MapWithObjects",
-				MapWithObjects.class,
-				new MapWithObjects().append("k1", "v1").append("k2", 123).append("k3", 1.23).append("k4", true).append("k5", null),
-				"|<object>|<k1>|v1|</k1>|<k2 _type='number'>|123|</k2>|<k3 _type='number'>|1.23|</k3>|<k4 _type='boolean'>|true|</k4>|<k5 _type='null'/>|</object>|",
-				false
-			},
-			{ 	/* 14 */
-				"ListWithStrings",
-				ListWithStrings.class,
-				new ListWithStrings().append("foo").append(null),
-				"|<array>|<string>|foo|</string>|<null/>|</array>|",
-				false
-			},
-			{ 	/* 15 */
-				"ListWithNumbers",
-				ListWithNumbers.class,
-				new ListWithNumbers().append(123).append(1.23).append(null),
-				"|<array>|<number>|123|</number>|<number>|1.23|</number>|<null/>|</array>|",
-				false
-			},
-			{ 	/* 16 */
-				"ListWithObjects",
-				ListWithObjects.class,
-				new ListWithObjects().append("foo").append(123).append(1.23).append(true).append(null),
-				"|<array>|<string>|foo|</string>|<number>|123|</number>|<number>|1.23|</number>|<boolean>|true|</boolean>|<null/>|</array>|",
-				false
-			},
-			{ 	/* 17 */
-				"BeanWithNormalProperties",
-				BeanWithNormalProperties.class,
-				new BeanWithNormalProperties().init(),
-				"|<object>"
-					+"|<a>|foo|</a>"
-					+"|<b>|123|</b>"
-					+"|<c>|bar|</c>"
-					+"|<d _type='number'>|456|</d>"
-					+"|<e>"
-						+"|<h>|qux|</h>"
-					+"|</e>"
-					+"|<f>"
-						+"|<string>|baz|</string>"
-					+"|</f>"
-					+"|<g>"
-						+"|<number>|789|</number>"
-					+"|</g>"
-				+"|</object>|",
-				false
-			},
-			{ 	/* 18 */
-				"BeanWithMapProperties",
-				BeanWithMapProperties.class,
-				new BeanWithMapProperties().init(),
-				"|<object>"
-					+"|<a>"
-						+"|<k1>|foo|</k1>"
-					+"|</a>"
-					+"|<b>"
-						+"|<k2>|123|</k2>"
-					+"|</b>"
-					+"|<c>"
-						+"|<k3>|bar|</k3>"
-						+"|<k4 _type='number'>|456|</k4>"
-						+"|<k5 _type='boolean'>|true|</k5>"
-						+"|<k6 _type='null'/>"
-					+"|</c>"
-				+"|</object>|",
-				false
-			},
-			{ 	/* 19 */
-				"BeanWithTypeName",
-				BeanWithTypeName.class,
-				new BeanWithTypeName().init(),
-				"|<X>|<a>|123|</a>|<b>|foo|</b>|</X>|",
-				false
-			},
-			{ 	/* 20 */
-				"BeanWithPropertiesWithTypeNames",
-				BeanWithPropertiesWithTypeNames.class,
-				new BeanWithPropertiesWithTypeNames().init(),
-				"|<object>|<b1>|<b>|foo|</b>|</b1>|<b2 _type='B'>|<b>|foo|</b>|</b2>|</object>|",
-				false
-			},
-			{ 	/* 21 */
-				"BeanWithPropertiesWithArrayTypeNames",
-				BeanWithPropertiesWithArrayTypeNames.class,
-				new BeanWithPropertiesWithArrayTypeNames().init(),
-				"|<object>"
-					+"|<b1>"
-						+"|<B>"
-							+"|<b>|foo|</b>"
-						+"|</B>"
-					+"|</b1>"
-					+"|<b2>"
-						+"|<B>"
-							+"|<b>|foo|</b>"
-						+"|</B>"
-					+"|</b2>"
-					+"|<b3>"
-						+"|<B>"
-							+"|<b>|foo|</b>"
-						+"|</B>"
-					+"|</b3>"
-				+"|</object>|",
-				false
-			},
-			{ 	/* 22 */
-				"BeanWithPropertiesWithArray2dTypeNames",
-				BeanWithPropertiesWith2dArrayTypeNames.class,
-				new BeanWithPropertiesWith2dArrayTypeNames().init(),
-				"|<object>"
-					+"|<b1>"
-						+"|<array>"
-							+"|<B>"
-								+"|<b>|foo|</b>"
-							+"|</B>"
-						+"|</array>"
-					+"|</b1>"
-					+"|<b2>"
-						+"|<array>"
-							+"|<B>"
-								+"|<b>|foo|</b>"
-							+"|</B>"
-						+"|</array>"
-					+"|</b2>"
-					+"|<b3>"
-						+"|<array>"
-							+"|<B>"
-								+"|<b>|foo|</b>"
-							+"|</B>"
-						+"|</array>"
-					+"|</b3>"
-				+"|</object>|",
-				false
-			},
-			{ 	/* 23 */
-				"BeanWithPropertiesWithMapTypeNames",
-				BeanWithPropertiesWithMapTypeNames.class,
-				new BeanWithPropertiesWithMapTypeNames().init(),
-				"|<object>"
-					+"|<b1>"
-						+"|<k1>"
-							+"|<b>|foo|</b>"
-						+"|</k1>"
-					+"|</b1>"
-					+"|<b2>"
-						+"|<k2 _type='B'>"
-							+"|<b>|foo|</b>"
-						+"|</k2>"
-					+"|</b2>"
-				+"|</object>|",
-				false
-			},
-			{ 	/* 24 */
-				"BeanWithChildTypeNames",
-				BeanWithChildTypeNames.class,
-				new BeanWithChildTypeNames().init(),
-				"|<object>"
-					+"|<a>"
-						+"|<fx>|fx1|</fx>"
-					+"|</a>"
-					+"|<b _type='X'>"
-						+"|<fx>|fx1|</fx>"
-					+"|</b>"
-					+"|<c>"
-						+"|<X>"
-							+"|<fx>|fx1|</fx>"
-						+"|</X>"
-					+"|</c>"
-					+"|<d>"
-						+"|<X>"
-							+"|<fx>|fx1|</fx>"
-						+"|</X>"
-					+"|</d>"
-				+"|</object>|",
-				false
-			},
-			{ 	/* 25 */
-				"BeanWithChildName",
-				BeanWithChildName.class,
-				new BeanWithChildName().init(),
-				"|<object>|<a>|<X>|foo|</X>|<X>|bar|</X>|</a>|<b>|<Y>|123|</Y>|<Y>|456|</Y>|</b>|</object>|",
-				false
-			},
-			{ 	/* 26 */
-				"BeanWithXmlFormatAttrProperty",
-				BeanWithXmlFormatAttrProperty.class,
-				new BeanWithXmlFormatAttrProperty().init(),
-				"|<object a='foo' b='123'/>|",
-				false
-			},
-			{ 	/* 27 */
-				"BeanWithXmlFormatAttrs",
-				BeanWithXmlFormatAttrs.class,
-				new BeanWithXmlFormatAttrs().init(),
-				"|<object a='foo' b='123'/>|",
-				false
-			},
-			{ 	/* 28 */
-				"BeanWithXmlFormatElementProperty",
-				BeanWithXmlFormatElementProperty.class,
-				new BeanWithXmlFormatElementProperty().init(),
-				"|<object a='foo'><b>123</b></object>|",
-				false
-			},
-			{ 	/* 29 */
-				"BeanWithXmlFormatAttrsProperty",
-				BeanWithXmlFormatAttrsProperty.class,
-				new BeanWithXmlFormatAttrsProperty().init(),
-				"|<object k1='foo' k2='123' b='456'/>|",
-				false
-			},
-			{ 	/* 30 */
-				"BeanWithXmlFormatCollapsedProperty",
-				BeanWithXmlFormatCollapsedProperty.class,
-				new BeanWithXmlFormatCollapsedProperty().init(),
-				"|<object>|<A>|foo|</A>|<A>|bar|</A>|<B>|123|</B>|<B>|456|</B>|</object>|",
-				false
-			},
-			{ 	/* 31 */
-				"BeanWithXmlFormatTextProperty",
-				BeanWithXmlFormatTextProperty.class,
-				new BeanWithXmlFormatTextProperty().init(),
-				"|<object a='foo'>|bar|</object>|",
-				false
-			},
-			{ 	/* 32 */
-				"BeanWithXmlFormatXmlTextProperty",
-				BeanWithXmlFormatXmlTextProperty.class,
-				new BeanWithXmlFormatXmlTextProperty().init(),
-				"|<object a='foo'>|bar|<b>|baz|</b>|qux|</object>|",
-				false
-			},
-			{ 	/* 33 */
-				"BeanWithXmlFormatElementsPropertyCollection",
-				BeanWithXmlFormatElementsPropertyCollection.class,
-				new BeanWithXmlFormatElementsPropertyCollection().init(),
-				"|<object a='foo'>|<string>|bar|</string>|<string>|baz|</string>|<number>|123|</number>|<boolean>|true|</boolean>|<null/>|</object>|",
-				false
-			},
-			{ 	/* 34 */
-				"BeanWithMixedContent",
-				BeanWithMixedContent.class,
-				new BeanWithMixedContent().init(),
-				"|<object>|foo|<X fx='fx1'/>|bar|<Y fy='fy1'/>|baz|</object>|",
-				true
-			},
-			{ 	/* 35 */
-				"BeanWithSpecialCharacters",
-				BeanWithSpecialCharacters.class,
-				new BeanWithSpecialCharacters().init(),
-				"|<object>|<a>|_x0020_ _x0008__x000C_&#x000a;&#x0009;&#x000d; _x0020_|</a>|</object>|",
-				false
-			},
-			{ 	/* 36 */
-				"BeanWithSpecialCharacters2",
-				BeanWithSpecialCharacters2.class,
-				new BeanWithSpecialCharacters2().init(),
-				"|<_x0020__x0020__x0008__x000C__x000A__x0009__x000D__x0020__x0020_>|<_x0020__x0020__x0008__x000C__x000A__x0009__x000D__x0020__x0020_>|_x0020_ _x0008__x000C_&#x000a;&#x0009;&#x000d; _x0020_|</_x0020__x0020__x0008__x000C__x000A__x0009__x000D__x0020__x0020_>|</_x0020__x0020__x0008__x000C__x000A__x0009__x000D__x0020__x0020_>|",
-				false
-			},
-			{ 	/* 37 */
-				"BeanWithNullProperties",
-				BeanWithNullProperties.class,
-				new BeanWithNullProperties(),
-				"|<object/>|",
-				false
-			},
-			{ 	/* 38 */
-				"BeanWithAbstractFields",
-				BeanWithAbstractFields.class,
-				new BeanWithAbstractFields().init(),
-				"|<object>"
-					+"|<a>"
-						+"|<a>|foo|</a>"
-					+"|</a>"
-					+"|<ia _type='A'>"
-						+"|<a>|foo|</a>"
-					+"|</ia>"
-					+"|<aa _type='A'>"
-						+"|<a>|foo|</a>"
-					+"|</aa>"
-					+"|<o _type='A'>"
-						+"|<a>|foo|</a>"
-					+"|</o>"
-				+"|</object>|",
-				false
-			},
-			{ 	/* 39 */
-				"BeanWithAbstractArrayFields",
-				BeanWithAbstractArrayFields.class,
-				new BeanWithAbstractArrayFields().init(),
-				"|<object>"
-					+"|<a>"
-						+"|<A>"
-							+"|<a>|foo|</a>"
-						+"|</A>"
-					+"|</a>"
-					+"|<ia1>"
-						+"|<A>"
-							+"|<a>|foo|</a>"
-						+"|</A>"
-					+"|</ia1>"
-					+"|<ia2>"
-						+"|<A>"
-							+"|<a>|foo|</a>"
-						+"|</A>"
-					+"|</ia2>"
-					+"|<aa1>"
-						+"|<A>"
-							+"|<a>|foo|</a>"
-						+"|</A>"
-					+"|</aa1>"
-					+"|<aa2>"
-						+"|<A>"
-							+"|<a>|foo|</a>"
-						+"|</A>"
-					+"|</aa2>"
-					+"|<o1>"
-						+"|<A>"
-							+"|<a>|foo|</a>"
-						+"|</A>"
-					+"|</o1>"
-					+"|<o2>"
-						+"|<A>"
-							+"|<a>|foo|</a>"
-						+"|</A>"
-					+"|</o2>"
-				+"|</object>",
-				false
-			},
-			{ 	/* 40 */
-				"BeanWithAbstractMapFields",
-				BeanWithAbstractMapFields.class,
-				new BeanWithAbstractMapFields().init(),
-				"|<object>"
-					+"|<a>"
-						+"|<k1>"
-							+"|<a>|foo|</a>"
-						+"|</k1>"
-					+"|</a>"
-					+"|<b>"
-						+"|<k2 _type='A'>"
-							+"|<a>|foo|</a>"
-						+"|</k2>"
-					+"|</b>"
-					+"|<c>"
-						+"|<k3 _type='A'>"
-							+"|<a>|foo|</a>"
-						+"|</k3>"
-					+"|</c>"
-				+"|</object>",
-				false
-			},
-			{ 	/* 41 */
-				"BeanWithAbstractMapArrayFields",
-				BeanWithAbstractMapArrayFields.class,
-				new BeanWithAbstractMapArrayFields().init(),
-				"|<object>"
-					+"|<a>"
-						+"|<a1>"
-							+"|<A>"
-								+"|<a>|foo|</a>"
-							+"|</A>"
-						+"|</a1>"
-					+"|</a>"
-					+"|<ia>"
-						+"|<ia1>"
-							+"|<A>"
-								+"|<a>|foo|</a>"
-							+"|</A>"
-						+"|</ia1>"
-						+"|<ia2>"
-							+"|<A>"
-								+"|<a>|foo|</a>"
-							+"|</A>"
-						+"|</ia2>"
-					+"|</ia>"
-					+"|<aa>"
-						+"|<aa1>"
-							+"|<A>"
-								+"|<a>|foo|</a>"
-							+"|</A>"
-						+"|</aa1>"
-						+"|<aa2>"
-							+"|<A>"
-								+"|<a>|foo|</a>"
-							+"|</A>"
-						+"|</aa2>"
-					+"|</aa>"
-					+"|<o>"
-						+"|<o1>"
-							+"|<A>"
-								+"|<a>|foo|</a>"
-							+"|</A>"
-						+"|</o1>"
-						+"|<o2>"
-							+"|<A>"
-								+"|<a>|foo|</a>"
-							+"|</A>"
-						+"|</o2>"
-					+"|</o>"
-				+"|</object>",
-				false
-			},
-			{ 	/* 42 */
-				"BeanWithWhitespaceTextFields-1",
-				BeanWithWhitespaceTextFields.class,
-				new BeanWithWhitespaceTextFields().init(null),
-				"|<object/>|",
-				false
-			},
-			{ 	/* 43 */
-				"BeanWithWhitespaceTextFields-2",
-				BeanWithWhitespaceTextFields.class,
-				new BeanWithWhitespaceTextFields().init(""),
-				"|<object>|_xE000_|</object>|",
-				false
-			},
-			{ 	/* 44 */
-				"BeanWithWhitespaceTextFields-3",
-				BeanWithWhitespaceTextFields.class,
-				new BeanWithWhitespaceTextFields().init(" "),
-				"|<object>|_x0020_|</object>|",
-				false
-			},
-			{ 	/* 45 */
-				"BeanWithWhitespaceTextFields-4",
-				BeanWithWhitespaceTextFields.class,
-				new BeanWithWhitespaceTextFields().init("  "),
-				"|<object>|_x0020__x0020_|</object>|",
-				false
-			},
-			{ 	/* 46 */
-				"BeanWithWhitespaceTextFields-5",
-				BeanWithWhitespaceTextFields.class,
-				new BeanWithWhitespaceTextFields().init(" foo\n\tbar "),
-				"|<object>|_x0020_foo&#x000a;&#x0009;bar_x0020_|</object>|",
-				false
-			},
-			{ 	/* 47 */
-				"BeanWithWhitespaceTextPwsFields-1",
-				BeanWithWhitespaceTextPwsFields.class,
-				new BeanWithWhitespaceTextPwsFields().init(null),
-				"|<object/>|",
-				false
-			},
-			{ 	/* 48 */
-				"BeanWithWhitespaceTextPwsFields-2",
-				BeanWithWhitespaceTextPwsFields.class,
-				new BeanWithWhitespaceTextPwsFields().init(""),
-				"|<object>|_xE000_|</object>|",
-				true
-			},
-			{ 	/* 49 */
-				"BeanWithWhitespaceTextPwsFields-3",
-				BeanWithWhitespaceTextPwsFields.class,
-				new BeanWithWhitespaceTextPwsFields().init(" "),
-				"|<object>| |</object>|",
-				true
-			},
-			{ 	/* 50 */
-				"BeanWithWhitespaceTextPwsFields-4",
-				BeanWithWhitespaceTextPwsFields.class,
-				new BeanWithWhitespaceTextPwsFields().init("  "),
-				"|<object>|  |</object>|",
-				true
-			},
-			{ 	/* 51 */
-				"BeanWithWhitespaceTextPwsFields-5",
-				BeanWithWhitespaceTextPwsFields.class,
-				new BeanWithWhitespaceTextPwsFields().init("  foobar  "),
-				"|<object>|  foobar  |</object>|",
-				true
-			},
-			{ 	/* 52 */
-				"BeanWithWhitespaceMixedFields-1",
-				BeanWithWhitespaceMixedFields.class,
-				new BeanWithWhitespaceMixedFields().init(null),
-				"|<object/>|",
-				false
-			},
-			{ 	/* 53 */
-				"BeanWithWhitespaceMixedFields-3",
-				BeanWithWhitespaceMixedFields.class,
-				new BeanWithWhitespaceMixedFields().init(new String[]{""}),
-				"|<object>|_xE000_|</object>|",
-				true
-			},
-			{ 	/* 54 */
-				"BeanWithWhitespaceMixedFields-4",
-				BeanWithWhitespaceMixedFields.class,
-				new BeanWithWhitespaceMixedFields().init(new String[]{" "}),
-				"|<object>|_x0020_|</object>|",
-				true
-			},
-			{ 	/* 55 */
-				"BeanWithWhitespaceMixedFields-5",
-				BeanWithWhitespaceMixedFields.class,
-				new BeanWithWhitespaceMixedFields().init(new String[]{"  "}),
-				"|<object>|_x0020__x0020_|</object>|",
-				true
-			},
-			{ 	/* 56 */
-				"BeanWithWhitespaceMixedFields-6",
-				BeanWithWhitespaceMixedFields.class,
-				new BeanWithWhitespaceMixedFields().init(new String[]{"  foobar  "}),
-				"|<object>|_x0020_ foobar _x0020_|</object>|",
-				true
-			},
-			{ 	/* 57 */
-				"BeanWithWhitespaceMixedPwsFields-1",
-				BeanWithWhitespaceMixedPwsFields.class,
-				new BeanWithWhitespaceMixedPwsFields().init(null),
-				"|<object/>|",
-				false
-			},
-			{ 	/* 58 */
-				"BeanWithWhitespaceMixedPwsFields-3",
-				BeanWithWhitespaceMixedPwsFields.class,
-				new BeanWithWhitespaceMixedPwsFields().init(new String[]{""}),
-				"|<object>|_xE000_|</object>|",
-				true
-			},
-			{ 	/* 59 */
-				"BeanWithWhitespaceMixedPwsFields-4",
-				BeanWithWhitespaceMixedPwsFields.class,
-				new BeanWithWhitespaceMixedPwsFields().init(new String[]{" "}),
-				"|<object>| |</object>|",
-				true
-			},
-			{ 	/* 60 */
-				"BeanWithWhitespaceMixedPwsFields-5",
-				BeanWithWhitespaceMixedPwsFields.class,
-				new BeanWithWhitespaceMixedPwsFields().init(new String[]{"  "}),
-				"|<object>|  |</object>|",
-				true
-			},
-			{ 	/* 61 */
-				"BeanWithWhitespaceMixedPwsFields-6",
-				BeanWithWhitespaceMixedPwsFields.class,
-				new BeanWithWhitespaceMixedPwsFields().init(new String[]{"  foobar  "}),
-				"|<object>|  foobar  |</object>|",
-				true
-			},
-		});
-	}
-	
-	private String label;
-	private Class<?> type;
-	private Object expected;
-	private String input;
-	private boolean skipWsTests;
-
-	public XmlTestIgnoreComments(String label, Class<?> type, Object expected, String input, boolean skipWsTests) throws Exception {
-		this.label = label;
-		this.type = type;
-		this.expected = expected;
-		this.input = input;
-		this.skipWsTests = skipWsTests;
-	}
-	
-	@Test
-	public void testNoComment() throws Exception {
-		try {
-			Object actual = XmlParser.DEFAULT.parse(input.replace("|", ""), type);
-			TestUtils.assertEqualObjects(expected, actual);
-		} catch (ComparisonFailure e) {
-			throw new ComparisonFailure(MessageFormat.format("Test ''{0}'' failed with comparison error", label), e.getExpected(), e.getActual());
-		} catch (Exception e) {
-			throw new FormattedRuntimeException(e, "Test ''{0}'' failed with error ''{1}''", label, e.getMessage());
-		}
-	}
-
-	@Test
-	public void testNormalComment() throws Exception {
-		try {
-			Object actual = XmlParser.DEFAULT.parse(input.replace("|", "<!--x-->"), type);
-			TestUtils.assertEqualObjects(expected, actual);
-		} catch (ComparisonFailure e) {
-			throw new ComparisonFailure(MessageFormat.format("Test ''{0}'' failed with comparison error", label), e.getExpected(), e.getActual());
-		} catch (Exception e) {
-			throw new FormattedRuntimeException(e, "Test ''{0}'' failed with error ''{1}''", label, e.getMessage());
-		}
-	}
-	
-	@Test
-	public void testCommentWithWhitespace() throws Exception {
-		try {
-			Object actual = XmlParser.DEFAULT.parse(input.replace("|", " \n <!-- \n x \n --> \n "), type);
-			if (! skipWsTests)
-				TestUtils.assertEqualObjects(expected, actual);
-		} catch (ComparisonFailure e) {
-			throw new ComparisonFailure(MessageFormat.format("Test ''{0}'' failed with comparison error", label), e.getExpected(), e.getActual());
-		} catch (Exception e) {
-			throw new FormattedRuntimeException(e, "Test ''{0}'' failed with error ''{1}''", label, e.getMessage());
-		}
-	}
-	
-	@Test
-	public void testDoubleCommentsWithWhitespace() throws Exception {
-		try {
-			Object actual = XmlParser.DEFAULT.parse(input.replace("|", " \n <!-- \n x \n --> \n \n <!-- \n x \n --> \n "), type);
-			if (! skipWsTests)
-				TestUtils.assertEqualObjects(expected, actual);
-		} catch (ComparisonFailure e) {
-			throw new ComparisonFailure(MessageFormat.format("Test ''{0}'' failed with comparison error", label), e.getExpected(), e.getActual());
-		} catch (Exception e) {
-			throw new FormattedRuntimeException(e, "Test ''{0}'' failed with error ''{1}''", label, e.getMessage());
-		}
-	}
-
-	//--------------------------------------------------------------------------------
-	// Test beans
-	//--------------------------------------------------------------------------------
-
-	public static class MapWithStrings extends LinkedHashMap<String,String> {
-		public MapWithStrings append(String key, String value) {
-			put(key, value);
-			return this;
-		}
-	}
-
-	public static class MapWithNumbers extends LinkedHashMap<String,Number> {
-		public MapWithNumbers append(String key, Number value) {
-			put(key, value);
-			return this;
-		}
-	}
-
-	public static class MapWithObjects extends LinkedHashMap<String,Object> {
-		public MapWithObjects append(String key, Object value) {
-			put(key, value);
-			return this;
-		}
-	}
-
-	public static class ListWithStrings extends ArrayList<String> {
-		public ListWithStrings append(String value) {
-			this.add(value);
-			return this;
-		}
-	}
-
-	public static class ListWithNumbers extends ArrayList<Number> {
-		public ListWithNumbers append(Number value) {
-			this.add(value);
-			return this;
-		}
-	}
-
-	public static class ListWithObjects extends ArrayList<Object> {
-		public ListWithObjects append(Object value) {
-			this.add(value);
-			return this;
-		}
-	}
-
-	public static class BeanWithNormalProperties {
-		public String a;
-		public int b;
-		public Object c;
-		public Object d;
-		public Bean1a e;
-		public String[] f;
-		public int[] g;
-
-		BeanWithNormalProperties init() {
-			a = "foo";
-			b = 123;
-			c = "bar";
-			d = 456;
-			e = new Bean1a().init();
-			f = new String[]{ "baz" };
-			g = new int[]{ 789 };
-			return this;
-		}
-	}
-
-	public static class Bean1a {
-		public String h;
-
-		Bean1a init() {
-			h = "qux";
-			return this;
-		}
-	}
-
-	public static class BeanWithMapProperties {
-		@BeanProperty(type=MapWithStrings.class)
-		public Map<String,String> a;
-		@BeanProperty(type=MapWithNumbers.class)
-		public Map<String,Number> b;
-		@BeanProperty(type=MapWithObjects.class)
-		public Map<String,Object> c;
-
-		BeanWithMapProperties init() {
-			a = new MapWithStrings().append("k1","foo");
-			b = new MapWithNumbers().append("k2",123);
-			c = new MapWithObjects().append("k3","bar").append("k4",456).append("k5",true).append("k6",null);
-			return this;
-		}
-	}
-
-	@Bean(typeName="X")
-	public static class BeanWithTypeName {
-		public int a;
-		public String b;
-
-		BeanWithTypeName init() {
-			a = 123;
-			b = "foo";
-			return this;
-		}
-	}
-
-	@Bean(beanDictionary={B.class})
-	public static class BeanWithPropertiesWithTypeNames {
-		public B b1;
-		public Object b2;
-
-		BeanWithPropertiesWithTypeNames init() {
-			b1 = new B().init();
-			b2 = new B().init();
-			return this;
-		}
-	}
-
-	@Bean(beanDictionary={B.class})
-	public static class BeanWithPropertiesWithArrayTypeNames {
-		public B[] b1;
-		public Object[] b2;
-		public Object[] b3;
-
-		BeanWithPropertiesWithArrayTypeNames init() {
-			b1 = new B[]{new B().init()};
-			b2 = new B[]{new B().init()};
-			b3 = new Object[]{new B().init()};
-			return this;
-		}
-	}
-
-	@Bean(beanDictionary={B.class})
-	public static class BeanWithPropertiesWith2dArrayTypeNames {
-		public B[][] b1;
-		public Object[][] b2;
-		public Object[][] b3;
-
-		BeanWithPropertiesWith2dArrayTypeNames init() {
-			b1 = new B[][]{{new B().init()}};
-			b2 = new B[][]{{new B().init()}};
-			b3 = new Object[][]{{new B().init()}};
-			return this;
-		}
-	}
-
-	@Bean(beanDictionary={B.class})
-	public static class BeanWithPropertiesWithMapTypeNames {
-		public Map<String,B> b1;
-		public Map<String,Object> b2;
-
-		BeanWithPropertiesWithMapTypeNames init() {
-			b1 = new HashMap<String,B>();
-			b1.put("k1", new B().init());
-			b2 = new HashMap<String,Object>();
-			b2.put("k2", new B().init());
-			return this;
-		}
-	}
-
-	@Bean(typeName="B")
-	public static class B {
-		public String b;
-
-		B init() {
-			b = "foo";
-			return this;
-		}
-	}
-
-	public static class BeanWithChildTypeNames {
-		public BeanX a;
-		@BeanProperty(beanDictionary=BeanX.class)
-		public Object b;
-		public BeanX[] c;
-		@BeanProperty(beanDictionary=BeanX.class)
-		public Object[] d;
-		BeanWithChildTypeNames init() {
-			a = new BeanX().init();
-			b = new BeanX().init();
-			c = new BeanX[]{new BeanX().init()};
-			d = new Object[]{new BeanX().init()};
-			return this;
-		}
-	}
-
-	public static class BeanWithChildName {
-		@Xml(childName = "X")
-		public String[] a;
-		@Xml(childName = "Y")
-		public int[] b;
-		BeanWithChildName init() {
-			a = new String[] { "foo", "bar" };
-			b = new int[] { 123, 456 };
-			return this;
-		}
-	}
-
-	public static class BeanWithXmlFormatAttrProperty {
-		@Xml(format=XmlFormat.ATTR)
-		public String a;
-		@Xml(format=XmlFormat.ATTR)
-		public int b;
-		BeanWithXmlFormatAttrProperty init() {
-			a = "foo";
-			b = 123;
-			return this;
-		}
-	}
-
-	@Xml(format=XmlFormat.ATTRS)
-	public static class BeanWithXmlFormatAttrs {
-		public String a;
-		public int b;
-		BeanWithXmlFormatAttrs init() {
-			a = "foo";
-			b = 123;
-			return this;
-		}
-	}
-
-	@Xml(format=XmlFormat.ATTRS)
-	public static class BeanWithXmlFormatElementProperty {
-		public String a;
-		@Xml(format=XmlFormat.ELEMENT)
-		public int b;
-		BeanWithXmlFormatElementProperty init() {
-			a = "foo";
-			b = 123;
-			return this;
-		}
-	}
-
-	public static class BeanWithXmlFormatAttrsProperty {
-		@Xml(format=XmlFormat.ATTRS)
-		public Map<String,Object> a;
-		@Xml(format=XmlFormat.ATTR)
-		public int b;
-		BeanWithXmlFormatAttrsProperty init() {
-			a = new ObjectMap().append("k1", "foo").append("k2", "123");
-			b = 456;
-			return this;
-		}
-	}
-
-	public static class BeanWithXmlFormatCollapsedProperty {
-		@Xml(childName="A",format=XmlFormat.COLLAPSED)
-		public String[] a;
-		@Xml(childName="B",format=XmlFormat.COLLAPSED)
-		public int[] b;
-		BeanWithXmlFormatCollapsedProperty init() {
-			a = new String[]{"foo","bar"};
-			b = new int[]{123,456};
-			return this;
-		}
-	}
-
-	public static class BeanWithXmlFormatTextProperty {
-		@Xml(format=XmlFormat.ATTR)
-		public String a;
-		@Xml(format=XmlFormat.TEXT)
-		public String b;
-		BeanWithXmlFormatTextProperty init() {
-			a = "foo";
-			b = "bar";
-			return this;
-		}
-	}
-
-	public static class BeanWithXmlFormatXmlTextProperty {
-		@Xml(format=XmlFormat.ATTR)
-		public String a;
-		@Xml(format=XmlFormat.XMLTEXT)
-		public String b;
-		BeanWithXmlFormatXmlTextProperty init() {
-			a = "foo";
-			b = "bar<b>baz</b>qux";
-			return this;
-		}
-	}
-
-	public static class BeanWithXmlFormatElementsPropertyCollection {
-		@Xml(format=XmlFormat.ATTR)
-		public String a;
-		@Xml(format=XmlFormat.ELEMENTS)
-		public Object[] b;
-		BeanWithXmlFormatElementsPropertyCollection init() {
-			a = "foo";
-			b = new Object[]{"bar","baz",123,true,null};
-			return this;
-		}
-	}
-
-	public static class BeanWithMixedContent {
-		@Xml(format=XmlFormat.MIXED)
-		@BeanProperty(beanDictionary={BeanXSimple.class, BeanYSimple.class})
-		public Object[] a;
-		BeanWithMixedContent init() {
-			a = new Object[]{
-				"foo",
-				new BeanXSimple().init(),
-				"bar",
-				new BeanYSimple().init(),
-				"baz"
-			};
-			return this;
-		}
-	}
-
-	@Bean(typeName="X")
-	public static class BeanX {
-		public String fx;
-		BeanX init() {
-			fx = "fx1";
-			return this;
-		}
-	}
-
-	@Bean(typeName="X")
-	public static class BeanXSimple {
-		@Xml(format=XmlFormat.ATTR)
-		public String fx;
-		BeanXSimple init() {
-			fx = "fx1";
-			return this;
-		}
-	}
-
-	@Bean(typeName="Y")
-	public static class BeanY {
-		public String fy;
-		BeanY init() {
-			fy = "fy1";
-			return this;
-		}
-	}
-
-	@Bean(typeName="Y")
-	public static class BeanYSimple {
-		@Xml(format=XmlFormat.ATTR)
-		public String fy;
-		BeanYSimple init() {
-			fy = "fy1";
-			return this;
-		}
-	}
-
-	public static class BeanWithSpecialCharacters {
-		public String a;
-
-		BeanWithSpecialCharacters init() {
-			a = "  \b\f\n\t\r  ";
-			return this;
-		}
-	}
-
-	@Bean(typeName="  \b\f\n\t\r  ")
-	public static class BeanWithSpecialCharacters2 {
-
-		@BeanProperty(name="  \b\f\n\t\r  ")
-		public String a;
-
-		BeanWithSpecialCharacters2 init() {
-			a = "  \b\f\n\t\r  ";
-			return this;
-		}
-	}
-
-	public static class BeanWithNullProperties {
-		public String a;
-		public String[] b;
-	}
-
-	@Bean(beanDictionary={A.class})
-	public static class BeanWithAbstractFields {
-		public A a;
-		public IA ia;
-		public AA aa;
-		public Object o;
-
-		BeanWithAbstractFields init() {
-			ia = new A().init();
-			aa = new A().init();
-			a = new A().init();
-			o = new A().init();
-			return this;
-		}
-	}
-
-	@Bean(beanDictionary={A.class})
-	public static class BeanWithAbstractArrayFields {
-		public A[] a;
-		public IA[] ia1, ia2;
-		public AA[] aa1, aa2;
-		public Object[] o1, o2;
-
-		BeanWithAbstractArrayFields init() {
-			a = new A[]{new A().init()};
-			ia1 = new A[]{new A().init()};
-			aa1 = new A[]{new A().init()};
-			o1 = new A[]{new A().init()};
-			ia2 = new IA[]{new A().init()};
-			aa2 = new AA[]{new A().init()};
-			o2 = new Object[]{new A().init()};
-			return this;
-		}
-	}
-
-	@Bean(beanDictionary={A.class})
-	public static class BeanWithAbstractMapFields {
-		public Map<String,A> a;
-		public Map<String,AA> b;
-		public Map<String,Object> c;
-
-		BeanWithAbstractMapFields init() {
-			a = new HashMap<String,A>();
-			b = new HashMap<String,AA>();
-			c = new HashMap<String,Object>();
-			a.put("k1", new A().init());
-			b.put("k2", new A().init());
-			c.put("k3", new A().init());
-			return this;
-		}
-	}
-
-	@Bean(beanDictionary={A.class})
-	public static class BeanWithAbstractMapArrayFields {
-		public Map<String,A[]> a;
-		public Map<String,IA[]> ia;
-		public Map<String,AA[]> aa;
-		public Map<String,Object[]> o;
-
-		BeanWithAbstractMapArrayFields init() {
-			a = new LinkedHashMap<String,A[]>();
-			ia = new LinkedHashMap<String,IA[]>();
-			aa = new LinkedHashMap<String,AA[]>();
-			o = new LinkedHashMap<String,Object[]>();
-			a.put("a1", new A[]{new A().init()});
-			ia.put("ia1", new A[]{new A().init()});
-			ia.put("ia2", new IA[]{new A().init()});
-			aa.put("aa1", new A[]{new A().init()});
-			aa.put("aa2", new AA[]{new A().init()});
-			o.put("o1", new A[]{new A().init()});
-			o.put("o2", new Object[]{new A().init()});
-			return this;
-		}
-	}
-
-	public static interface IA {
-		public String getA();
-		public void setA(String a);
-	}
-
-	public static abstract class AA implements IA {}
-
-	@Bean(typeName="A")
-	public static class A extends AA {
-		private String a;
-
-		@Override
-		public String getA() {
-			return a;
-		}
-
-		@Override
-		public void setA(String a) {
-			this.a = a;
-		}
-
-		A init() {
-			this.a = "foo";
-			return this;
-		}
-	}
-
-	public static class BeanWithWhitespaceTextFields {
-		@Xml(format=XmlFormat.TEXT)
-		public String a;
-
-		public BeanWithWhitespaceTextFields init(String s) {
-			a = s;
-			return this;
-		}
-	}
-
-	public static class BeanWithWhitespaceTextPwsFields {
-		@Xml(format=XmlFormat.TEXT_PWS)
-		public String a;
-
-		public BeanWithWhitespaceTextPwsFields init(String s) {
-			a = s;
-			return this;
-		}
-	}
-
-	public static class BeanWithWhitespaceMixedFields {
-		@Xml(format=XmlFormat.MIXED)
-		public String[] a;
-
-		public BeanWithWhitespaceMixedFields init(String[] s) {
-			a = s;
-			return this;
-		}
-	}
-
-	public static class BeanWithWhitespaceMixedPwsFields {
-		@Xml(format=XmlFormat.MIXED_PWS)
-		public String[] a;
-
-		public BeanWithWhitespaceMixedPwsFields init(String[] s) {
-			a = s;
-			return this;
-		}
-	}
-}

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-marshall/TODO.txt
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/TODO.txt b/juneau-core/juneau-marshall/TODO.txt
index 44e619c..f314bd6 100644
--- a/juneau-core/juneau-marshall/TODO.txt
+++ b/juneau-core/juneau-marshall/TODO.txt
@@ -13,7 +13,5 @@
 
 Create tests that ensure serializers don't close output but parsers do close input.
 
-Review property info in all contexts.
-Clean up RestClient/RestClientBuilder constructors.
 $R{servletDescription} tags showing up in REST examples.
 Examples should use dark LAF.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
index 71a751c..4c2fd99 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanContext.java
@@ -945,7 +945,7 @@ public class BeanContext extends Context {
 
 	final String beanTypePropertyName;
 
-	final int hashCode;
+	final int beanHashCode;
 
 	/**
 	 * Constructor.
@@ -958,7 +958,7 @@ public class BeanContext extends Context {
 	public BeanContext(PropertyStore ps) {
 		super(ps);
 
-		hashCode = ps.hashCode("BeanContext");
+		beanHashCode = ps.hashCode("BeanContext");
 
 		beansRequireDefaultConstructor = getProperty(BEAN_beansRequireDefaultConstructor, boolean.class, false);
 		beansRequireSerializable = getProperty(BEAN_beansRequireSerializable, boolean.class, false);
@@ -1032,13 +1032,13 @@ public class BeanContext extends Context {
 		timeZone = getInstanceProperty(BEAN_timeZone, TimeZone.class, null);
 		mediaType = getInstanceProperty(BEAN_mediaType, MediaType.class, null);
 		
-		if (! cmCacheCache.containsKey(hashCode)) {
+		if (! cmCacheCache.containsKey(beanHashCode)) {
 			ConcurrentHashMap<Class,ClassMeta> cm = new ConcurrentHashMap<>();
 			cm.putIfAbsent(String.class, new ClassMeta(String.class, this, null, null, findPojoSwaps(String.class), findChildPojoSwaps(String.class)));
 			cm.putIfAbsent(Object.class, new ClassMeta(Object.class, this, null, null, findPojoSwaps(Object.class), findChildPojoSwaps(Object.class)));
-			cmCacheCache.putIfAbsent(hashCode, cm);
+			cmCacheCache.putIfAbsent(beanHashCode, cm);
 		}
-		cmCache = cmCacheCache.get(hashCode);
+		cmCache = cmCacheCache.get(beanHashCode);
 		cmString = cmCache.get(String.class);
 		cmObject = cmCache.get(Object.class);
 		cmClass = cmCache.get(Class.class);
@@ -1797,20 +1797,6 @@ public class BeanContext extends Context {
 		return cmClass;
 	}
 
-	@Override /* Object */
-	public int hashCode() {
-		return hashCode;
-	}
-
-	@Override /* Object */
-	public boolean equals(Object o) {
-		if (this == o)
-			return true;
-		if (o instanceof BeanContext)
-			return ((BeanContext)o).hashCode == hashCode;
-		return false;
-	}
-
 	@Override /* Context */
 	public ObjectMap asMap() {
 		return super.asMap()

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
index 06178c3..ae78ab7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/Context.java
@@ -15,6 +15,7 @@ package org.apache.juneau;
 import java.util.*;
 
 import org.apache.juneau.annotation.*;
+import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.serializer.*;
 
@@ -36,6 +37,7 @@ import org.apache.juneau.serializer.*;
 public abstract class Context {
 
 	private final PropertyStore propertyStore;
+	private final int hashCode;
 
 	/**
 	 * Constructor for this class.
@@ -47,6 +49,7 @@ public abstract class Context {
 	 */
 	public Context(PropertyStore ps) {
 		this.propertyStore = ps == null ? PropertyStore.DEFAULT : ps;
+		this.hashCode = new HashCode().add(getClass().getName()).add(ps).get();
 	}
 
 	/**
@@ -338,6 +341,22 @@ public abstract class Context {
 	public ObjectMap asMap() {
 		return new ObjectMap();
 	}
+	
+	@Override /* Object */
+	public final int hashCode() {
+		return hashCode;
+	}
+	
+	@Override /* Object */
+	public final boolean equals(Object o) {
+		// Context objects are considered equal if they're the same class and have the same set of properties.
+		if (o == null)
+			return false;
+		if (o.getClass() != this.getClass()) 
+			return false;
+		Context c = (Context)o;
+		return (c.propertyStore.equals(propertyStore));
+	}
 
 	@Override /* Object */
 	public final String toString() {

http://git-wip-us.apache.org/repos/asf/juneau/blob/77664769/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java
index 939240c..99cb2d9 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java
@@ -66,13 +66,7 @@ interface PropertyConverter<T> {
 	static final PropertyConverter<Object> OBJECT_CONVERTER = new PropertyConverter<Object>() {
 		@Override
 		public Object convert(Object o, MutableProperty p) {
-			try {
-				if (o instanceof Class)
-					return ((Class<?>)o).newInstance();
-				return o;
-			} catch (Exception e) {
-				throw new ConfigException("Value ''{0}'' ({1}) cannot be converted to an Object on property ''{2}'' ({3}).", o, o.getClass().getSimpleName(), p.name, p.type);
-			}
+			return o;
 		}
 	};
 }