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 2016/08/01 17:30:07 UTC

[18/53] [partial] incubator-juneau git commit: Merge changes from GitHub repo.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/1b4f98a0/org.apache.juneau/src/main/java/org/apache/juneau/BeanContext.java
----------------------------------------------------------------------
diff --git a/org.apache.juneau/src/main/java/org/apache/juneau/BeanContext.java b/org.apache.juneau/src/main/java/org/apache/juneau/BeanContext.java
new file mode 100644
index 0000000..3b2f51c
--- /dev/null
+++ b/org.apache.juneau/src/main/java/org/apache/juneau/BeanContext.java
@@ -0,0 +1,2069 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ ***************************************************************************************************************************/
+package org.apache.juneau;
+
+import static org.apache.juneau.Visibility.*;
+import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.internal.ThrowableUtils.*;
+
+import java.beans.*;
+import java.io.*;
+import java.lang.reflect.*;
+import java.text.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transform.*;
+import org.apache.juneau.transform.Transform;
+
+/**
+ * Core class of the Juneau architecture.
+ * <p>
+ * 	This class servers multiple purposes:
+ * 	<ul class='spaced-list'>
+ * 		<li>Provides the ability to wrap beans inside {@link Map} interfaces.
+ * 		<li>Serves as a repository for metadata on POJOs, such as associated {@link Transform transforms}, {@link PropertyNamer property namers}, etc...
+ * 			which are used to tailor how POJOs are serialized and parsed.
+ * 		<li>Serves as a common utility class for all {@link Serializer Serializers} and {@link Parser Parsers}
+ * 				for serializing and parsing Java beans.
+ * 	</ul>
+ *
+ *
+ * <h5 class='topic'>Bean Contexts</h5>
+ * <p>
+ * 	Typically, it will be sufficient to use the existing {@link #DEFAULT} contexts for creating
+ * 	bean maps.  However, if you want to tweak any of the settings on the context, you must
+ * 	either clone the default context or create a new one from scratch (whichever is simpler for you).
+ * 	You'll notice that this context class uses a fluent interface for defining settings.
+ * <p>
+ * 	Bean contexts are created by {@link ContextFactory context factories}.
+ * 	The settings on a bean context are fixed at the point they are created by the factory.
+ *
+ *
+ * <h5 class='topic'>BeanContext settings</h5>
+ * 	<code>BeanContexts</code> have several settings that can be used to tweak behavior on how beans are handled.
+ * 	These are denoted as the static <jsf>BEAN_*</jsf> fields on this class.
+ * <p>
+ * 	Some settings (e.g. {@link BeanContext#BEAN_beansRequireDefaultConstructor}) are used to differentiate between bean and non-bean classes.
+ * 	Attempting to create a bean map around one of these objects will throw a {@link BeanRuntimeException}.
+ * 	The purpose for this behavior is so that the serializers can identify these non-bean classes and convert them to plain strings using the {@link Object#toString()} method.
+ * <p>
+ * 	Some settings (e.g. {@link BeanContext#BEAN_beanFieldVisibility}) are used to determine what kinds of properties are detected on beans.
+ * <p>
+ * 	Some settings (e.g. {@link BeanContext#BEAN_beanMapPutReturnsOldValue}) change the runtime behavior of bean maps.
+ * <p>
+ * 	Settings are specified using the {@link ContextFactory#setProperty(String, Object)} method and related convenience methods.
+ *
+ * <h6 class='topic'>Examples</h6>
+ * <p class='bcode'>
+ * 	<jc>// Construct a context from scratch.</jc>
+ * 	BeanContext beanContext = ContextFactory.<jsm>create</jsm>()
+ * 		.setProperty(BeanContext.<jsf>BEAN_beansRequireDefaultConstructor</jsf>, <jk>true</jk>)
+ * 		.addNotBeanClasses(Foo.<jk>class</jk>)
+ * 		.getBeanContext();
+ *
+ * 	<jc>// Clone an existing context factory.</jc>
+ * 	BeanContext beanContext = ContextFactory.<jsm>create</jsm>(otherConfig)
+ * 		.setProperty(BeanContext.<jsf>BEAN_beansRequireDefaultConstructor</jsf>, <jk>true</jk>)
+ * 		.addNotBeanClasses(Foo.<jk>class</jk>)
+ * 		.getBeanContext();
+ * </p>
+ *
+ *
+ * <h5 class='topic'>Bean Maps</h5>
+ * <p>
+ * 	{@link BeanMap BeanMaps} are wrappers around Java beans that allow properties to be retrieved and
+ * 	set using the common {@link Map#put(Object,Object)} and {@link Map#get(Object)} methods.<br>
+ * 	<br>
+ * 	Bean maps are created in two ways...
+ * 	<ol>
+ * 		<li> {@link BeanContext#forBean(Object) BeanContext.forBean()} - Wraps an existing bean inside a {@code Map} wrapper.
+ * 		<li> {@link BeanContext#newBeanMap(Class) BeanContext.newInstance()} - Create a new bean instance wrapped in a {@code Map} wrapper.
+ * 	</ol>
+ *
+ *
+ * <h6 class='topic'>Examples</h6>
+ * <p class='bcode'>
+ * 	<jc>// A sample bean class</jc>
+ * 	<jk>public class</jk> Person {
+ * 		<jk>public</jk> String getName();
+ * 		<jk>public void</jk> setName(String name);
+ * 		<jk>public int</jk> getAge();
+ * 		<jk>public void</jk> setAge(<jk>int</jk> age);
+ * 	}
+ *
+ * 	<jc>// Wrap an existing bean in a new bean map</jc>
+ * 	BeanMap&lt;Person&gt; m1 = BeanContext.<jsf>DEFAULT</jsf>.forBean(<jk>new</jk> Person());
+ * 	m1.put(<js>"name"</js>, <js>"John Smith"</js>);
+ * 	m1.put(<js>"age"</js>, 45);
+ *
+ * 	<jc>// Create a new bean instance wrapped in a new bean map</jc>
+ * 	BeanMap&lt;Person&gt; m2 = BeanContext.<jsf>DEFAULT</jsf>.newInstance(Person.<jk>class</jk>);
+ * 	m2.put(<js>"name"</js>, <js>"John Smith"</js>);
+ * 	m2.put(<js>"age"</js>, 45);
+ * 	Person p = m2.getBean();  <jc>// Get the bean instance that was created.</jc>
+ * </p>
+ *
+ *
+ * <h5 class='topic'>Bean Annotations</h5>
+ * <p>
+ * 	This package contains annotations that can be applied to
+ * 	class definitions to override what properties are detected on a bean.
+ * <h6 class='topic'>Examples</h6>
+ * <p class='bcode'>
+ * 	<jc>// Bean class definition where only property 'name' is detected.</jc>
+ * 	<ja>&#64;Bean</ja>(properties={<js>"name"</js>})
+ * 	<jk>public class</jk> Person {
+ * 		<jk>public</jk> String getName();
+ * 		<jk>public void</jk> setName(String name);
+ * 		<jk>public int</jk> getAge();
+ * 		<jk>public void</jk> setAge(<jk>int</jk> age);
+ * 	}
+ * <p>
+ * 	See {@link Bean @Bean} and {@link BeanProperty @BeanProperty} for more information.
+ *
+ *
+ * <h5 class='topic'>Beans with read-only properties</h5>
+ * <p>
+ * 	Bean maps can also be defined on top of beans with read-only properties by adding a
+ * 	{@link BeanConstructor @BeanConstructor} annotation to one of the constructors on the
+ * 	bean class.  This will allow read-only properties to be set through constructor arguments.
+ * <p>
+ * 	When the <code>@BeanConstructor</code> annotation is present, bean instantiation is delayed until the call to {@link BeanMap#getBean()}.
+ * 	Until then, bean property values are stored in a local cache until <code>getBean()</code> is called.
+ * 	Because of this additional caching step, parsing into read-only beans tends to be slower and use
+ * 	more memory than parsing into beans with writable properties.
+ * <p>
+ * 	Attempting to call {@link BeanMap#put(String,Object)} on a read-only property after calling {@link BeanMap#getBean()}
+ * 	will result in a {@link BeanRuntimeException} being thrown.
+ * 	Multiple calls to {@link BeanMap#getBean()} will return the same bean instance.
+ * <p>
+ * 	Beans can be defined with a combination of read-only and read-write properties.
+ * <p>
+ * 	See {@link BeanConstructor @BeanConstructor} for more information.
+ *
+ *
+ * <h5 class='topic'>Transforms</h5>
+ * <p>
+ * 	{@link Transform Transforms} are used to tailor how beans and non-beans are handled.<br>
+ * 	There are two subclasses of transforms:
+ * 	<ol class='spaced-list'>
+ * 		<li>{@link BeanTransform} - Allows you to tailor handling of bean classes.
+ * 			This class can be considered a programmatic equivalent to the {@link Bean} annotation when
+ * 			annotating classes are not possible (e.g. you don't have access to the source).
+ * 		<li>{@link PojoTransform} - Allows you to convert objects to serializable forms.
+ * 	</ol>
+ * <p>
+ * 	See {@link org.apache.juneau.transform} for more information.
+ *
+ *
+ * <h5 class='topic'>ClassMetas</h5>
+ * <p>
+ * 	The {@link ClassMeta} class is a wrapper around {@link Class} object that provides cached information
+ * 	about that class (e.g. whether it's a {@link Map} or {@link Collection} or bean).
+ * <p>
+ * 	As a general rule, it's best to reuse bean contexts (and therefore serializers and parsers too)
+ * 	whenever possible since it takes some time to populate the internal {@code ClassMeta} object cache.
+ * 	By reusing bean contexts, the class type metadata only needs to be calculated once which significantly
+ * 	improves performance.
+ * <p>
+ * 	See {@link ClassMeta} for more information.
+ *
+ * @author Barry M. Caceres
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+@SuppressWarnings({"unchecked","rawtypes"})
+public class BeanContext extends Context {
+
+
+	/**
+	 * Require no-arg constructor ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * If <jk>true</jk>, a Java class must implement a default no-arg constructor to be considered a bean.
+	 * <p>
+	 * The {@link Bean @Bean} annotation can be used on a class to override this setting when <jk>true</jk>.
+	 */
+	public static final String BEAN_beansRequireDefaultConstructor = "BeanContext.beansRequireDefaultConstructor";
+
+	/**
+	 * Require {@link Serializable} interface ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * If <jk>true</jk>, a Java class must implement the {@link Serializable} interface to be considered a bean.
+	 * <p>
+	 * The {@link Bean @Bean} annotation can be used on a class to override this setting when <jk>true</jk>.
+	 */
+	public static final String BEAN_beansRequireSerializable = "BeanContext.beansRequireSerializable";
+
+	/**
+	 * Require setters for getters ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * If <jk>true</jk>, only getters that have equivalent setters will be considered as properties on a bean.
+	 * Otherwise, they will be ignored.
+	 */
+	public static final String BEAN_beansRequireSettersForGetters = "BeanContext.beansRequireSettersForGetters";
+
+	/**
+	 * Require some properties ({@link Boolean}, default=<jk>true</jk>).
+	 * <p>
+	 * If <jk>true</jk>, then a Java class must contain at least 1 property to be considered a bean.
+	 * <p>
+	 * The {@link Bean @Bean} annotation can be used on a class to override this setting when <jk>true</jk>.
+	 */
+	public static final String BEAN_beansRequireSomeProperties = "BeanContext.beansRequireSomeProperties";
+
+	/**
+	 * Put returns old value ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * If <jk>true</jk>, then the {@link BeanMap#put(String,Object) BeanMap.put()} method will return old property values.
+	 * <p>
+	 * Disabled by default because it introduces a slight performance penalty.
+	 */
+	public static final String BEAN_beanMapPutReturnsOldValue = "BeanContext.beanMapPutReturnsOldValue";
+
+	/**
+	 * Look for bean constructors with the specified minimum visibility ({@link Visibility}, default={@link Visibility#PUBLIC}).
+	 */
+	public static final String BEAN_beanConstructorVisibility = "BeanContext.beanConstructorVisibility";
+
+	/**
+	 * Look for bean classes with the specified minimum visibility ({@link Visibility}, default={@link Visibility#PUBLIC}).
+	 * <p>
+	 * Classes are not considered beans unless they meet the minimum visibility requirements.
+	 * For example, if the visibility is <code>PUBLIC</code> and the bean class is <jk>protected</jk>, then
+	 * 	the class will not be interpreted as a bean class.
+	 */
+	public static final String BEAN_beanClassVisibility = "BeanContext.beanClassVisibility";
+
+	/**
+	 * Look for bean fields with the specified minimum visibility ({@link Visibility}, default={@link Visibility#PUBLIC}).
+	 * <p>
+	 * Fields are not considered bean properties unless they meet the minimum visibility requirements.
+	 * For example, if the visibility is <code>PUBLIC</code> and the bean field is <jk>protected</jk>, then
+	 * 	the field will not be interpreted as a bean property.
+	 * <p>
+	 * Use {@link Visibility#NONE} to prevent bean fields from being interpreted as bean properties altogether.
+	 */
+	public static final String BEAN_beanFieldVisibility = "BeanContext.beanFieldVisibility";
+
+	/**
+	 * Look for bean methods with the specified minimum visibility ({@link Visibility}, default={@link Visibility#PUBLIC}).
+	 * <p>
+	 * Methods are not considered bean getters/setters unless they meet the minimum visibility requirements.
+	 * For example, if the visibility is <code>PUBLIC</code> and the bean method is <jk>protected</jk>, then
+	 * 	the method will not be interpreted as a bean getter or setter.
+	 */
+	public static final String BEAN_methodVisibility = "BeanContext.methodVisibility";
+
+	/**
+	 * Use Java {@link Introspector} for determining bean properties ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * Using the built-in Java bean introspector will not pick up fields or non-standard getters/setters.
+	 * Most {@link Bean @Bean} annotations will be ignored.
+	 */
+	public static final String BEAN_useJavaBeanIntrospector = "BeanContext.useJavaBeanIntrospector";
+
+	/**
+	 * Use interface proxies ({@link Boolean}, default=<jk>true</jk>).
+	 * <p>
+	 * If <jk>true</jk>, then interfaces will be instantiated as proxy classes through the use of an {@link InvocationHandler}
+	 * if there is no other way of instantiating them.
+	 */
+	public static final String BEAN_useInterfaceProxies = "BeanContext.useInterfaceProxies";
+
+	/**
+	 * Ignore unknown properties ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * If <jk>true</jk>, trying to set a value on a non-existent bean property will silently be ignored.
+	 * Otherwise, a {@code RuntimeException} is thrown.
+	 */
+	public static final String BEAN_ignoreUnknownBeanProperties = "BeanContext.ignoreUnknownBeanProperties";
+
+	/**
+	 * Ignore unknown properties with null values ({@link Boolean}, default=<jk>true</jk>).
+	 * <p>
+	 * If <jk>true</jk>, trying to set a <jk>null</jk> value on a non-existent bean property will silently be ignored.
+	 * Otherwise, a {@code RuntimeException} is thrown.
+	 */
+	public static final String BEAN_ignoreUnknownNullBeanProperties = "BeanContext.ignoreUnknownNullBeanProperties";
+
+	/**
+	 * Ignore properties without setters ({@link Boolean}, default=<jk>true</jk>).
+	 * <p>
+	 * If <jk>true</jk>, trying to set a value on a bean property without a setter will silently be ignored.
+	 * Otherwise, a {@code RuntimeException} is thrown.
+	 */
+	public static final String BEAN_ignorePropertiesWithoutSetters = "BeanContext.ignorePropertiesWithoutSetters";
+
+	/**
+	 * Ignore invocation errors on getters ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * If <jk>true</jk>, errors thrown when calling bean getter methods will silently be ignored.
+	 * Otherwise, a {@code BeanRuntimeException} is thrown.
+	 */
+	public static final String BEAN_ignoreInvocationExceptionsOnGetters = "BeanContext.ignoreInvocationExceptionsOnGetters";
+
+	/**
+	 * Ignore invocation errors on setters ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * If <jk>true</jk>, errors thrown when calling bean setter methods will silently be ignored.
+	 * Otherwise, a {@code BeanRuntimeException} is thrown.
+	 */
+	public static final String BEAN_ignoreInvocationExceptionsOnSetters = "BeanContext.ignoreInvocationExceptionsOnSetters";
+
+	/**
+	 * Sort bean properties in alphabetical order ({@link Boolean}, default=<jk>false</jk>).
+	 * <p>
+	 * When <jk>true</jk>, all bean properties will be serialized and access in alphabetical order.
+	 * Otherwise, the natural order of the bean properties is used which is dependent on the
+	 * 	JVM vendor.
+	 * On IBM JVMs, the bean properties are ordered based on their ordering in the Java file.
+	 * On Oracle JVMs, the bean properties are not ordered (which follows the offical JVM specs).
+	 * <p>
+	 * This property is disabled by default so that IBM JVM users don't have to use {@link Bean @Bean} annotations
+	 * to force bean properties to be in a particular order and can just alter the order of the fields/methods
+	 * in the Java file.
+	 */
+	public static final String BEAN_sortProperties = "BeanContext.sortProperties";
+
+	/**
+	 * List of packages whose classes should not be considered beans (<code>Set&lt;String&gt;</code>).
+	 * <p>
+	 * When specified, the current list of ignore packages are appended to.
+	 * The default list of ignore packages are as follows:
+	 * <ul>
+	 * 	<li><code>java.lang</code>
+	 * 	<li><code>java.lang.annotation</code>
+	 * 	<li><code>java.lang.ref</code>
+	 * 	<li><code>java.lang.reflect</code>
+	 * 	<li><code>java.io</code>
+	 * 	<li><code>java.net</code>
+	 * 	<li><code>java.nio.*</code>
+	 * 	<li><code>java.util.*</code>
+	 * </ul>
+	 * <p>
+	 * Any classes within these packages will be serialized to strings using {@link Object#toString()}.
+	 * <p>
+	 * Note that you can specify prefix patterns to include all subpackages.
+	 */
+	public static final String BEAN_notBeanPackages = "BeanContext.notBeanPackages.set";
+
+	/**
+	 * Add to the list of packages whose classes should not be considered beans.
+	 */
+	public static final String BEAN_notBeanPackages_add = "BeanContext.notBeanPackages.set.add";
+
+	/**
+	 * Remove from the list of packages whose classes should not be considered beans.
+	 */
+	public static final String BEAN_notBeanPackages_remove = "BeanContext.notBeanPackages.set.remove";
+
+	/**
+	 * An explicit list of Java classes to be excluded from consideration as being beans (<code>Set&lt;Class&gt;</code>).
+	 * <p>
+	 * Not-bean classes are typically converted to <code>Strings</code> during serialization even if they
+	 * appear to be bean-like.
+	 */
+	public static final String BEAN_notBeanClasses = "BeanContext.notBeanClasses.set";
+
+	/**
+	 * Add to the list of packages whose classes should not be considered beans.
+	 */
+	public static final String BEAN_notBeanClasses_add = "BeanContext.notBeanClasses.set.add";
+
+	/**
+	 * Remove from the list of packages whose classes should not be considered beans.
+	 */
+	public static final String BEAN_notBeanClasses_remove = "BeanContext.notBeanClasses.set.remove";
+
+	/**
+	 * List of transform classes on the bean context (<code>List&lt;Class&gt;</code>).
+	 * <p>
+	 * There are two category of classes that can be passed in through this method:
+	 * <ul class='spaced-list'>
+	 * 	<li>Subclasses of {@link PojoTransform} and {@link BeanTransform}.
+	 * 	<li>Any other class.
+	 * </ul>
+	 * <p>
+	 * When <code>Transform</code> classes are specified, they identify objects that need to be
+	 * 	transformed into some other type during serialization (and optionally the reverse during parsing).
+	 * <p>
+	 * When non-<code>Transform</code> classes are specified, they are wrapped inside {@link BeanTransform BeanTransforms}.
+	 * For example, if you have an interface <code>IFoo</code> and a subclass <code>Foo</code>, and you
+	 * 	only want properties defined on <code>IFoo</code> to be visible as bean properties for <code>Foo</code> objects,
+	 * 	you can simply pass in <code>IFoo.<jk>class</jk></code> to this method.
+	 * </p>
+	 */
+	public static final String BEAN_transforms = "BeanContext.transforms.list";
+
+	/**
+	 * Add to the list of transform classes.
+	 */
+	public static final String BEAN_transforms_add = "BeanContext.transforms.list.add";
+
+	/**
+	 * Remove from the list of transform classes.
+	 */
+	public static final String BEAN_transforms_remove = "BeanContext.transforms.list.remove";
+
+	/**
+	 * Specifies implementation classes for an interface or abstract class (<code>Map&lt;Class,Class&gt;</code>).
+	 * <p>
+	 * For interfaces and abstract classes this method can be used to specify an implementation
+	 * 	class for the interface/abstract class so that instances of the implementation
+	 * 	class are used when instantiated (e.g. during a parse).
+	 */
+	public static final String BEAN_implClasses = "BeanContext.implClasses.map";
+
+	/**
+	 * Adds a new map entry to the {@link #BEAN_implClasses} property.
+	 */
+	public static final String BEAN_implClasses_put = "BeanContext.implClasses.map.put";
+
+	/**
+	 * Specifies the default parser to use when converting <code>Strings</code> to POJOs in the {@link BeanContext#convertToType(Object, Class)} method (<code>Class</code>).
+	 */
+	public static final String BEAN_defaultParser = "BeanContext.defaultParser";
+
+	/*
+	 * The default package pattern exclusion list.
+	 * Any beans in packages in this list will not be considered beans.
+	 */
+	private static final String[] DEFAULT_NOTBEAN_PACKAGES = {
+		"java.lang",
+		"java.lang.annotation",
+		"java.lang.ref",
+		"java.lang.reflect",
+		"java.io",
+		"java.net",
+		"java.nio.*",
+		"java.util.*"
+	};
+
+	/*
+	 * The default bean class exclusion list.
+	 * Anything in this list will not be considered beans.
+	 */
+	private static final Class<?>[] DEFAULT_NOTBEAN_CLASSES = {
+		Map.class,
+		Collection.class,
+		Reader.class,
+		Writer.class,
+		InputStream.class,
+		OutputStream.class,
+		Throwable.class
+	};
+
+
+	static final void loadDefaults(ContextFactory config) {
+		config.setProperty(BEAN_notBeanPackages, DEFAULT_NOTBEAN_PACKAGES);
+		config.setProperty(BEAN_notBeanClasses, DEFAULT_NOTBEAN_CLASSES);
+	}
+
+
+	// This map is important!
+	// We may have many ConfigFactory objects that have identical BeanContext properties.
+	// This map ensures that if the BeanContext properties in the ConfigFactory are the same,
+	// then we reuse the same Class->ClassMeta cache map.
+	// This significantly reduces the number of times we need to construct ClassMeta objects which can be expensive.
+	private static final ConcurrentHashMap<ContextFactory.PropertyMap,Map<Class,ClassMeta>> cmCacheCache = new ConcurrentHashMap<ContextFactory.PropertyMap,Map<Class,ClassMeta>>();
+
+	/** Default config.  All default settings. */
+	public static final BeanContext DEFAULT = ContextFactory.create().getContext(BeanContext.class);
+
+	/** Default config.  All default settings except sort bean properties. */
+	public static final BeanContext DEFAULT_SORTED = ContextFactory.create().setProperty(BEAN_sortProperties, true).getContext(BeanContext.class);
+
+	final boolean
+		beansRequireDefaultConstructor,
+		beansRequireSerializable,
+		beansRequireSettersForGetters,
+		beansRequireSomeProperties,
+		beanMapPutReturnsOldValue,
+		useInterfaceProxies,
+		ignoreUnknownBeanProperties,
+		ignoreUnknownNullBeanProperties,
+		ignorePropertiesWithoutSetters,
+		ignoreInvocationExceptionsOnGetters,
+		ignoreInvocationExceptionsOnSetters,
+		useJavaBeanIntrospector,
+		sortProperties;
+
+	final Visibility
+		beanConstructorVisibility,
+		beanClassVisibility,
+		beanMethodVisibility,
+		beanFieldVisibility;
+
+	final Class<?>[] notBeanClasses;
+	final String[] notBeanPackageNames, notBeanPackagePrefixes;
+	final BeanTransform<?>[] beanTransforms;
+	final PojoTransform<?,?>[] pojoTransforms;
+	final Map<Class<?>,Class<?>> implClasses;
+	final Class<?>[] implKeyClasses, implValueClasses;
+	final ClassLoader classLoader;
+
+	final Map<Class,ClassMeta> cmCache;
+	final ClassMeta<Object> cmObject;  // Reusable ClassMeta that represents general Objects.
+	final ClassMeta<String> cmString;  // Reusable ClassMeta that represents general Strings.
+	final ClassMeta<Class> cmClass;  // Reusable ClassMeta that represents general Classes.
+
+	// Optional default parser set by setDefaultParser().
+	final ReaderParser defaultParser;
+
+	// Holds pending ClassMetas (created, but not yet initialized).
+	final Deque<ClassMeta> pendingClassMetas = new LinkedList<ClassMeta>();
+
+	final int hashCode;
+
+	/**
+	 * Constructor.
+	 * <p>
+	 * Typically only called from {@link ContextFactory#getContext(Class)} or {@link ContextFactory#getBeanContext()}.
+	 *
+	 * @param cf The factory that created this context.
+	 */
+	public BeanContext(ContextFactory cf) {
+		super(cf);
+
+		ContextFactory.PropertyMap pm = cf.getPropertyMap("BeanContext");
+		classLoader = cf.classLoader;
+		defaultParser = cf.defaultParser;
+		hashCode = pm.hashCode;
+
+		beansRequireDefaultConstructor = pm.get(BEAN_beansRequireDefaultConstructor, boolean.class, false);
+		beansRequireSerializable = pm.get(BEAN_beansRequireSerializable, boolean.class, false);
+		beansRequireSettersForGetters = pm.get(BEAN_beansRequireSettersForGetters, boolean.class, false);
+		beansRequireSomeProperties = pm.get(BEAN_beansRequireSomeProperties, boolean.class, true);
+		beanMapPutReturnsOldValue = pm.get(BEAN_beanMapPutReturnsOldValue, boolean.class, false);
+		useInterfaceProxies = pm.get(BEAN_useInterfaceProxies, boolean.class, true);
+		ignoreUnknownBeanProperties = pm.get(BEAN_ignoreUnknownBeanProperties, boolean.class, false);
+		ignoreUnknownNullBeanProperties = pm.get(BEAN_ignoreUnknownNullBeanProperties, boolean.class, true);
+		ignorePropertiesWithoutSetters = pm.get(BEAN_ignorePropertiesWithoutSetters, boolean.class, true);
+		ignoreInvocationExceptionsOnGetters = pm.get(BEAN_ignoreInvocationExceptionsOnGetters, boolean.class, false);
+		ignoreInvocationExceptionsOnSetters = pm.get(BEAN_ignoreInvocationExceptionsOnSetters, boolean.class, false);
+		useJavaBeanIntrospector = pm.get(BEAN_useJavaBeanIntrospector, boolean.class, false);
+		sortProperties = pm.get(BEAN_sortProperties, boolean.class, false);
+
+		beanConstructorVisibility = pm.get(BEAN_beanConstructorVisibility, Visibility.class, PUBLIC);
+		beanClassVisibility = pm.get(BEAN_beanClassVisibility, Visibility.class, PUBLIC);
+		beanMethodVisibility = pm.get(BEAN_methodVisibility, Visibility.class, PUBLIC);
+		beanFieldVisibility = pm.get(BEAN_beanFieldVisibility, Visibility.class, PUBLIC);
+
+		notBeanClasses = pm.get(BEAN_notBeanClasses, Class[].class, new Class[0]);
+
+		List<String> l1 = new LinkedList<String>();
+		List<String> l2 = new LinkedList<String>();
+		for (String s : pm.get(BEAN_notBeanPackages, String[].class, new String[0])) {
+			if (s.endsWith(".*"))
+				l2.add(s.substring(0, s.length()-2));
+			else
+				l1.add(s);
+		}
+		notBeanPackageNames = l1.toArray(new String[l1.size()]);
+		notBeanPackagePrefixes = l2.toArray(new String[l2.size()]);
+
+		LinkedList<BeanTransform<?>> lbf = new LinkedList<BeanTransform<?>>();
+		LinkedList<PojoTransform<?,?>> lpf = new LinkedList<PojoTransform<?,?>>();
+ 		for (Class<?> c : pm.get(BEAN_transforms, Class[].class, new Class[0])) {
+			if (isParentClass(Transform.class, c)) {
+				try {
+					if (isParentClass(BeanTransform.class, c)) {
+						BeanTransform<?> f = (BeanTransform<?>)c.newInstance();
+						//f.setBeanContext(this);
+						lbf.add(f);
+					} else if (isParentClass(PojoTransform.class, c)) {
+						PojoTransform<?,?> f = (PojoTransform<?,?>)c.newInstance();
+						f.setBeanContext(this);
+						lpf.add(f);
+					}
+				} catch (Exception e) {
+					throw new RuntimeException(e);
+				}
+			} else {
+ 				if (! c.getClass().isInterface()) {
+					List<SurrogateTransform<?,?>> l = SurrogateTransform.findTransforms(c);
+					if (! l.isEmpty()) {
+						for (SurrogateTransform<?,?> f : l) {
+							f.setBeanContext(this);
+							lpf.add(f);
+						}
+						continue;
+					}
+				}
+				BeanTransform<?> f = new InterfaceBeanTransform(c);
+				f.setBeanContext(this);
+				lbf.add(f);
+			}
+		}
+ 		beanTransforms = lbf.toArray(new BeanTransform[0]);
+ 		pojoTransforms = lpf.toArray(new PojoTransform[0]);
+
+ 		implClasses = new TreeMap<Class<?>,Class<?>>(new ClassComparator());
+ 		Map<Class,Class> m = pm.getMap(BEAN_implClasses, Class.class, Class.class, null);
+ 		if (m != null)
+	 		for (Map.Entry<Class,Class> e : m.entrySet())
+	 			implClasses.put(e.getKey(), e.getValue());
+		implKeyClasses = implClasses.keySet().toArray(new Class[0]);
+		implValueClasses = implClasses.values().toArray(new Class[0]);
+
+		if (! cmCacheCache.containsKey(pm)) {
+			ConcurrentHashMap<Class,ClassMeta> cm = new ConcurrentHashMap<Class,ClassMeta>();
+			cm.put(String.class, new ClassMeta(String.class, this));
+			cm.put(Object.class, new ClassMeta(Object.class, this));
+			cmCacheCache.putIfAbsent(pm, cm);
+		}
+		this.cmCache = cmCacheCache.get(pm);
+		this.cmString = cmCache.get(String.class);
+		this.cmObject = cmCache.get(Object.class);
+		this.cmClass = cmCache.get(Class.class);
+	}
+
+	/**
+	 * Returns <jk>true</jk> if the specified bean context shares the same cache as this bean context.
+	 * Useful for testing purposes.
+	 *
+	 * @param bc The bean context to compare to.
+	 * @return <jk>true</jk> if the bean contexts have equivalent settings and thus share caches.
+	 */
+	public boolean hasSameCache(BeanContext bc) {
+		return bc.cmCache == this.cmCache;
+	}
+
+	/**
+	 * Bean property getter:  <property>ignoreUnknownBeanProperties</property>.
+	 * See {@link BeanContext#BEAN_ignoreUnknownBeanProperties}.
+	 *
+	 * @return The value of the <property>ignoreUnknownBeanProperties</property> property on this bean.
+	 */
+	public boolean isIgnoreUnknownBeanProperties() {
+		return ignoreUnknownBeanProperties;
+	}
+
+	/**
+	 * Determines whether the specified class is ignored as a bean class based on the various
+	 * 	exclusion parameters specified on this context class.
+	 *
+	 * @param c The class type being tested.
+	 * @return <jk>true</jk> if the specified class matches any of the exclusion parameters.
+	 */
+	protected boolean isNotABean(Class<?> c) {
+		if (c.isArray() || c.isPrimitive() || c.isEnum() || c.isAnnotation())
+			return true;
+		Package p = c.getPackage();
+		if (p != null) {
+			for (String p2 : notBeanPackageNames)
+				if (p.getName().equals(p2))
+					return true;
+			for (String p2 : notBeanPackagePrefixes)
+				if (p.getName().startsWith(p2))
+					return true;
+		}
+		for (Class exclude : notBeanClasses)
+			if (isParentClass(exclude, c))
+				return true;
+		return false;
+	}
+
+	/**
+	 * Prints meta cache statistics to <code>System.out</code>.
+	 */
+	protected static void dumpCacheStats() {
+		try {
+			int ctCount = 0;
+			for (Map<Class,ClassMeta> cm : cmCacheCache.values())
+				ctCount += cm.size();
+			System.out.println(MessageFormat.format("ClassMeta cache: {0} instances in {1} caches", ctCount, cmCacheCache.size()));
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Wraps an object inside a {@link BeanMap} object (i.e. a modifiable {@link Map}).
+	 * <p>
+	 * 	If object is not a true bean, then throws a {@link BeanRuntimeException} with an explanation of why it's not a bean.
+	 *
+	 * <dl>
+	 * 	<dt>Example:</dt>
+	 * 	<dd>
+	 * <p class='bcode'>
+	 * 	<jc>// Construct a bean map around a bean instance</jc>
+	 * 	BeanMap&lt;Person&gt; bm = BeanContext.<jsf>DEFAULT</jsf>.forBean(<jk>new</jk> Person());
+	 * </p>
+	 * 	</dd>
+	 * </dl>
+	 *
+	 * @param <T> The class of the object being wrapped.
+	 * @param o The object to wrap in a map interface.  Must not be null.
+	 * @return The wrapped object.
+	 */
+	public <T> BeanMap<T> forBean(T o) {
+		return this.forBean(o, (Class<T>)o.getClass());
+	}
+
+	/**
+	 * Determines whether the specified object matches the requirements on this context of being a bean.
+	 *
+	 * @param o The object being tested.
+	 * @return <jk>true</jk> if the specified object is considered a bean.
+	 */
+	public boolean isBean(Object o) {
+		if (o == null)
+			return false;
+		return isBean(o.getClass());
+	}
+
+	/**
+	 * Determines whether the specified class matches the requirements on this context of being a bean.
+	 *
+	 * @param c The class being tested.
+	 * @return <jk>true</jk> if the specified class is considered a bean.
+	 */
+	public boolean isBean(Class<?> c) {
+		return getBeanMeta(c) != null;
+	}
+
+	/**
+	 * Wraps an object inside a {@link BeanMap} object (i.e.: a modifiable {@link Map})
+	 * defined as a bean for one of its class, a super class, or an implemented interface.
+	 * <p>
+	 * 	If object is not a true bean, throws a {@link BeanRuntimeException} with an explanation of why it's not a bean.
+	 *
+	 * <dl>
+	 * 	<dt>Example:</dt>
+	 * 	<dd>
+	 * <p class='bcode'>
+	 * 	<jc>// Construct a bean map for new bean using only properties defined in a superclass</jc>
+	 * 	BeanMap&lt;MySubBean&gt; bm = BeanContext.<jsf>DEFAULT</jsf>.forBean(<jk>new</jk> MySubBean(), MySuperBean.<jk>class</jk>);
+	 *
+	 * 	<jc>// Construct a bean map for new bean using only properties defined in an interface</jc>
+	 * 	BeanMap&lt;MySubBean&gt; bm = BeanContext.<jsf>DEFAULT</jsf>.forBean(<jk>new</jk> MySubBean(), MySuperInterface.<jk>class</jk>);
+	 * </p>
+	 * 	</dd>
+	 * </dl>
+	 *
+	 * @param <T> The class of the object being wrapped.
+	 * @param o The object to wrap in a bean interface.  Must not be null.
+	 * @param c The superclass to narrow the bean properties to.  Must not be null.
+	 * @return The bean representation, or <jk>null</jk> if the object is not a true bean.
+	 * @throws NullPointerException If either parameter is null.
+	 * @throws IllegalArgumentException If the specified object is not an an instance of the specified class.
+	 * @throws BeanRuntimeException If specified object is not a bean according to the bean rules
+	 * 		specified in this context class.
+	 */
+	public <T> BeanMap<T> forBean(T o, Class<? super T> c) throws BeanRuntimeException {
+		assertFieldNotNull(o, "o");
+		assertFieldNotNull(c, "c");
+
+		if (! c.isInstance(o))
+			illegalArg("The specified object is not an instance of the specified class.  class=''{0}'', objectClass=''{1}'', object=''{2}''", c.getName(), o.getClass().getName(), 0);
+
+		ClassMeta cm = getClassMeta(c);
+
+		BeanMeta m = cm.getBeanMeta();
+		if (m == null)
+			throw new BeanRuntimeException(c, "Class is not a bean.  Reason=''{0}''", cm.getNotABeanReason());
+		return new BeanMap<T>(o, m);
+	}
+
+	/**
+	 * Creates a new {@link BeanMap} object (i.e. a modifiable {@link Map}) of the given class with uninitialized property values.
+	 * <p>
+	 * 	If object is not a true bean, then throws a {@link BeanRuntimeException} with an explanation of why it's not a bean.
+	 *
+	 * <dl>
+	 * 	<dt>Example:</dt>
+	 * 	<dd>
+	 * <p class='bcode'>
+	 * 	<jc>// Construct a new bean map wrapped around a new Person object</jc>
+	 * 	BeanMap&lt;Person&gt; bm = BeanContext.<jsf>DEFAULT</jsf>.newBeanMap(Person.<jk>class</jk>);
+	 * </p>
+	 * 	</dd>
+	 * </dl>
+	 *
+	 * @param <T> The class of the object being wrapped.
+	 * @param c The name of the class to create a new instance of.
+	 * @return A new instance of the class.
+	 */
+	public <T> BeanMap<T> newBeanMap(Class<T> c) {
+		return newBeanMap(null, c);
+	}
+
+	/**
+	 * Same as {@link #newBeanMap(Class)}, except used for instantiating inner member classes that must
+	 * 	be instantiated within another class instance.
+	 *
+	 * @param <T> The class of the object being wrapped.
+	 * @param c The name of the class to create a new instance of.
+	 * @param outer If class is a member class, this is the instance of the containing class.
+	 * 	Should be <jk>null</jk> if not a member class.
+	 * @return A new instance of the class.
+	 */
+	public <T> BeanMap<T> newBeanMap(Object outer, Class<T> c) {
+		BeanMeta m = getBeanMeta(c);
+		if (m == null)
+			return null;
+		T bean = null;
+		if (m.constructorArgs.length == 0) {
+			bean = newBean(outer, c);
+			// Beans with subtypes won't be instantiated until the sub type property is specified.
+			if (bean == null && ! m.getClassMeta().hasSubTypes())
+				return null;
+		}
+		return new BeanMap<T>(bean, m);
+	}
+
+	/**
+	 * Creates a new empty bean of the specified type, except used for instantiating inner member classes that must
+	 * 	be instantiated within another class instance.
+	 *
+	 * <dl>
+	 * 	<dt>Example:</dt>
+	 * 	<dd>
+	 * <p class='bcode'>
+	 * 	<jc>// Construct a new instance of the specified bean class</jc>
+	 * 	Person p = BeanContext.<jsf>DEFAULT</jsf>.newBean(Person.<jk>class</jk>);
+	 * </p>
+	 * 	</dd>
+	 * </dl>
+	 *
+	 * @param <T> The class type of the bean being created.
+	 * @param c The class type of the bean being created.
+	 * @return A new bean object.
+	 * @throws BeanRuntimeException If the specified class is not a valid bean.
+	 */
+	public <T> T newBean(Class<T> c) throws BeanRuntimeException {
+		return newBean(null, c);
+	}
+
+	/**
+	 * Same as {@link #newBean(Class)}, except used for instantiating inner member classes that must
+	 * 	be instantiated within another class instance.
+	 *
+	 * @param <T> The class type of the bean being created.
+	 * @param c The class type of the bean being created.
+	 * @param outer If class is a member class, this is the instance of the containing class.
+	 * 	Should be <jk>null</jk> if not a member class.
+	 * @return A new bean object.
+	 * @throws BeanRuntimeException If the specified class is not a valid bean.
+	 */
+	public <T> T newBean(Object outer, Class<T> c) throws BeanRuntimeException {
+		ClassMeta<T> cm = getClassMeta(c);
+		BeanMeta m = cm.getBeanMeta();
+		if (m == null)
+			return null;
+		try {
+			T o = (T)m.newBean(outer);
+			if (o == null) {
+				// Beans with subtypes won't be instantiated until the sub type property is specified.
+				if (cm.beanTransform != null && cm.beanTransform.getSubTypeProperty() != null)
+					return null;
+				throw new BeanRuntimeException(c, "Class does not have a no-arg constructor.");
+			}
+			return o;
+		} catch (BeanRuntimeException e) {
+			throw e;
+		} catch (Exception e) {
+			throw new BeanRuntimeException(e);
+		}
+	}
+
+	/**
+	 * Returns the {@link BeanMeta} class for the specified class.
+	 *
+	 * @param <T> The class type to get the meta-data on.
+	 * @param c The class to get the meta-data on.
+	 * @return The {@link BeanMeta} for the specified class, or <jk>null</jk> if the class
+	 * 	is not a bean per the settings on this context.
+	 */
+	public <T> BeanMeta<T> getBeanMeta(Class<T> c) {
+		if (c == null)
+			return null;
+		return getClassMeta(c).getBeanMeta();
+	}
+
+	/**
+	 * Returns the class type bound to this bean context if the specified class type
+	 * 	is from another bean context.
+	 * <p>
+	 * For example, this method allows you to pass in an object from <code>BeanContext.<jsf>DEFAULT</jsf>.getMapClassMeta(...)</code>
+	 * 	to any of the <code>ReaderParser.parse(Reader, ClassMeta, ParserContext)</code> methods, and the parsers
+	 * 	will use this method to replace the class type with the one registered with the parser.
+	 * This ensures that registered transforms are applied correctly.
+	 *
+	 * @param <T> The class type.
+	 * @param cm The class type.
+	 * @return The class type bound by this bean context.
+	 */
+	public <T> ClassMeta<T> normalizeClassMeta(ClassMeta<T> cm) {
+		if (cm == null)
+			return (ClassMeta<T>)object();
+		if (cm.beanContext == this || cm.beanContext.equals(this))
+			return cm;
+		if (cm.isMap()) {
+			ClassMeta<Map> cm2 = (ClassMeta<Map>)cm;
+			cm2 = getMapClassMeta(cm2.getInnerClass(), cm2.getKeyType().getInnerClass(), cm2.getValueType().getInnerClass());
+			return (ClassMeta<T>)cm2;
+		}
+		if (cm.isCollection()) {
+			ClassMeta<Collection> cm2 = (ClassMeta<Collection>)cm;
+			cm2 = getCollectionClassMeta(cm2.getInnerClass(), cm2.getElementType().getInnerClass());
+			return (ClassMeta<T>)cm2;
+		}
+		return getClassMeta(cm.getInnerClass());
+	}
+
+	/**
+	 * Construct a {@code ClassMeta} wrapper around a {@link Class} object.
+	 *
+	 * @param <T> The class type being wrapped.
+	 * @param c The class being wrapped.
+	 * 	of type {@link Class} or {@link ClassMeta}.
+	 * @return If the class is not an array, returns a cached {@link ClassMeta} object.
+	 * 	Otherwise, returns a new {@link ClassMeta} object every time.<br>
+	 */
+	public <T> ClassMeta<T> getClassMeta(Class<T> c) {
+
+		// If this is an array, then we want it wrapped in an uncached ClassMeta object.
+		// Note that if it has a pojo transform, we still want to cache it so that
+		// we can cache something like byte[] with ByteArrayBase64Transform.
+		if (c.isArray() && findPojoTransform(c) == null)
+			return new ClassMeta(c, this);
+
+		// This can happen if we have transforms defined against String or Object.
+		if (cmCache == null)
+			return null;
+
+		ClassMeta<T> cm = cmCache.get(c);
+		if (cm == null) {
+
+			synchronized (this) {
+
+				// Make sure someone didn't already set it while this thread was blocked.
+				cm = cmCache.get(c);
+				if (cm == null) {
+
+					// Note:  Bean properties add the possibility that class reference loops exist.
+					// To handle this possibility, we create a set of pending ClassMetas, and
+					// call init (which finds the bean properties) after it's been added to the pending set.
+					for (ClassMeta pcm : pendingClassMetas)
+						if (pcm.innerClass == c)
+							return pcm;
+
+					cm = new ClassMeta<T>(c, this, true);
+					pendingClassMetas.addLast(cm);
+					try {
+						cm.init();
+					} finally {
+						pendingClassMetas.removeLast();
+					}
+					cmCache.put(c, cm);
+				}
+			}
+		}
+		return cm;
+	}
+
+	/**
+	 * Construct a {@code ClassMeta} wrapper around a {@link Map} object.
+	 *
+	 * @param <K> The map key class type.
+	 * @param <V> The map value class type.
+	 * @param <T> The map class type.
+	 * @param c The map class type.
+	 * @param keyType The map key class type.
+	 * @param valueType The map value class type.
+	 * @return If the key and value types are OBJECT, returns a cached {@link ClassMeta} object.<br>
+	 * 	Otherwise, returns a new {@link ClassMeta} object every time.
+	 */
+	public <K,V,T extends Map<K,V>> ClassMeta<T> getMapClassMeta(Class<T> c, ClassMeta<K> keyType, ClassMeta<V> valueType) {
+		if (keyType.isObject() && valueType.isObject())
+			return getClassMeta(c);
+		return new ClassMeta(c, this).setKeyType(keyType).setValueType(valueType);
+	}
+
+	/**
+	 * Construct a {@code ClassMeta} wrapper around a {@link Map} object.
+	 *
+	 * @param <K> The map key class type.
+	 * @param <V> The map value class type.
+	 * @param <T> The map class type.
+	 * @param c The map class type.
+	 * @param keyType The map key class type.
+	 * @param valueType The map value class type.
+	 * @return If the key and value types are Object, returns a cached {@link ClassMeta} object.<br>
+	 * 	Otherwise, returns a new {@link ClassMeta} object every time.
+	 */
+	public <K,V,T extends Map<K,V>> ClassMeta<T> getMapClassMeta(Class<T> c, Class<K> keyType, Class<V> valueType) {
+		return getMapClassMeta(c, getClassMeta(keyType), getClassMeta(valueType));
+	}
+
+	/**
+	 * Construct a {@code ClassMeta} wrapper around a {@link Map} object.
+	 *
+	 * @param <T> The map class type.
+	 * @param c The map class type.
+	 * @param keyType The map key class type.
+	 * @param valueType The map value class type.
+	 * @return If the key and value types are Object, returns a cached {@link ClassMeta} object.<br>
+	 * 	Otherwise, returns a new {@link ClassMeta} object every time.
+	 */
+	public <T extends Map> ClassMeta<T> getMapClassMeta(Class<T> c, Type keyType, Type valueType) {
+		return getMapClassMeta(c, getClassMeta(keyType), getClassMeta(valueType));
+	}
+
+	/**
+	 * Construct a {@code ClassMeta} wrapper around a {@link Collection} object.
+	 *
+	 * @param <E> The collection element class type.
+	 * @param <T> The collection class type.
+	 * @param c The collection class type.
+	 * @param elementType The collection element class type.
+	 * @return If the element type is <code>OBJECT</code>, returns a cached {@link ClassMeta} object.<br>
+	 * 	Otherwise, returns a new {@link ClassMeta} object every time.
+	 */
+	public <E,T extends Collection<E>> ClassMeta<T> getCollectionClassMeta(Class<T> c, ClassMeta<E> elementType) {
+		if (elementType.isObject())
+			return getClassMeta(c);
+		return new ClassMeta(c, this).setElementType(elementType);
+	}
+
+	/**
+	 * Construct a {@code ClassMeta} wrapper around a {@link Collection} object.
+	 *
+	 * @param <E> The collection element class type.
+	 * @param <T> The collection class type.
+	 * @param c The collection class type.
+	 * @param elementType The collection element class type.
+	 * @return If the element type is <code>OBJECT</code>, returns a cached {@link ClassMeta} object.<br>
+	 * 	Otherwise, returns a new {@link ClassMeta} object every time.
+	 */
+	public <E,T extends Collection<E>> ClassMeta<T> getCollectionClassMeta(Class<T> c, Class<E> elementType) {
+		return getCollectionClassMeta(c, getClassMeta(elementType));
+	}
+
+	/**
+	 * Construct a {@code ClassMeta} wrapper around a {@link Collection} object.
+	 *
+	 * @param <T> The collection class type.
+	 * @param c The collection class type.
+	 * @param elementType The collection element class type.
+	 * @return If the element type is <code>OBJECT</code>, returns a cached {@link ClassMeta} object.<br>
+	 * 	Otherwise, returns a new {@link ClassMeta} object every time.
+	 */
+	public <T extends Collection> ClassMeta<T> getCollectionClassMeta(Class<T> c, Type elementType) {
+		return getCollectionClassMeta(c, getClassMeta(elementType));
+	}
+
+	/**
+	 * Constructs a ClassMeta object given the specified object and parameters.
+	 *
+	 * @param o The parent class type.
+	 * 	Can be any of the following types:
+	 * 	<ul class='spaced-list'>
+	 * 		<li>{@link ClassMeta} object, which just returns the same object.
+	 * 		<li>{@link Class} object (e.g. <code>String.<jk>class</jk></code>).
+	 * 		<li>{@link Type} object (e.g. {@link ParameterizedType} or {@link GenericArrayType}.
+	 * 		<li>Anything else is interpreted as {@code getClassMeta(o.getClass(), parameters);}
+	 * 	</ul>
+	 * @return A ClassMeta object, or <jk>null</jk> if the object is null.
+	 */
+	public ClassMeta getClassMeta(Type o) {
+		return getClassMeta(o, null);
+	}
+
+	ClassMeta getClassMeta(Type o, Map<Class<?>,Class<?>[]> typeVarImpls) {
+		if (o == null)
+			return null;
+
+		if (o instanceof ClassMeta)
+			return (ClassMeta)o;
+
+		Class c = null;
+		if (o instanceof Class) {
+			c = (Class)o;
+		} else if (o instanceof ParameterizedType) {
+			// A parameter (e.g. <String>.
+			c = (Class<?>)((ParameterizedType)o).getRawType();
+		} else if (o instanceof GenericArrayType) {
+			// An array parameter (e.g. <byte[]>.
+			GenericArrayType gat = (GenericArrayType)o;
+			Type gatct = gat.getGenericComponentType();
+			if (gatct instanceof Class) {
+				Class gatctc = (Class)gatct;
+				c = Array.newInstance(gatctc, 0).getClass();
+			} else if (gatct instanceof ParameterizedType) {
+				Class gatctc = (Class<?>)((ParameterizedType)gatct).getRawType();
+				c = Array.newInstance(gatctc, 0).getClass();
+			} else {
+				return null;
+			}
+		} else if (o instanceof TypeVariable) {
+			if (typeVarImpls != null) {
+				TypeVariable t = (TypeVariable) o;
+				String varName = t.getName();
+				int varIndex = -1;
+				Class gc = (Class)t.getGenericDeclaration();
+				TypeVariable[] tv = gc.getTypeParameters();
+				for (int i = 0; i < tv.length; i++) {
+					if (tv[i].getName().equals(varName)) {
+						varIndex = i;
+					}
+				}
+				if (varIndex != -1) {
+
+					// If we couldn't find a type variable implementation, that means
+					// the type was defined at runtime (e.g. Bean b = new Bean<Foo>();)
+					// in which case the type is lost through erasure.
+					// Assume java.lang.Object as the type.
+					if (! typeVarImpls.containsKey(gc))
+						return object();
+
+					return getClassMeta(typeVarImpls.get(gc)[varIndex]);
+				}
+			}
+			// We don't know the bounded type, so just resolve to Object.
+			return object();
+		} else {
+			// This can happen when trying to resolve the "E getFirst()" method on LinkedList, whose type is a TypeVariable
+			// These should just resolve to Object.
+			return object();
+		}
+
+		ClassMeta rawType = getClassMeta(c);
+
+		// If this is a Map or Collection, and the parameter types aren't part
+		// of the class definition itself (e.g. class AddressBook extends List<Person>),
+		// then we need to figure out the parameters.
+		if (rawType.isMap() || rawType.isCollection()) {
+			ClassMeta[] params = findParameters(o, c);
+			if (params == null)
+				return rawType;
+			if (rawType.isMap()) {
+				if (params.length != 2)
+					return rawType;
+				if (params[0].isObject() && params[1].isObject())
+					return rawType;
+				return new ClassMeta(rawType.innerClass, this).setKeyType(params[0]).setValueType(params[1]);
+			}
+			if (rawType.isCollection()) {
+				if (params.length != 1)
+					return rawType;
+				if (params[0].isObject())
+					return rawType;
+				return new ClassMeta(rawType.innerClass, this).setElementType(params[0]);
+			}
+		}
+
+		return rawType;
+	}
+
+	/**
+	 * Given an array of {@link Class} objects, returns an array of corresponding {@link ClassMeta} objects.
+	 * Constructs a new array on each call.
+	 *
+	 * @param classes The array of classes to get class metas for.
+	 * @return An array of {@link ClassMeta} objects corresponding to the classes.  Never <jk>null</jk>.
+	 */
+	public ClassMeta<?>[] getClassMetas(Class<?>[] classes) {
+		assertFieldNotNull(classes, "classes");
+		ClassMeta<?>[] cm = new ClassMeta<?>[classes.length];
+		for (int i = 0; i < classes.length; i++)
+			cm[i] = getClassMeta(classes[i]);
+		return cm;
+	}
+
+	ClassMeta[] findParameters(Type o, Class c) {
+		if (o == null)
+			o = c;
+
+		// Loop until we find a ParameterizedType
+		if (! (o instanceof ParameterizedType)) {
+			loop: do {
+				o = c.getGenericSuperclass();
+				if (o instanceof ParameterizedType)
+					break loop;
+				for (Type t : c.getGenericInterfaces()) {
+					o = t;
+					if (o instanceof ParameterizedType)
+						break loop;
+				}
+				c = c.getSuperclass();
+			} while (c != null);
+		}
+
+		if (o instanceof ParameterizedType) {
+			ParameterizedType pt = (ParameterizedType)o;
+			if (! pt.getRawType().equals(Enum.class)) {
+				List<ClassMeta<?>> l = new LinkedList<ClassMeta<?>>();
+				for (Type pt2 : pt.getActualTypeArguments()) {
+					if (pt2 instanceof WildcardType || pt2 instanceof TypeVariable)
+						return null;
+					l.add(getClassMeta(pt2, null));
+				}
+				if (l.isEmpty())
+					return null;
+				return l.toArray(new ClassMeta[l.size()]);
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Shortcut for calling {@code getClassMeta(o.getClass())}.
+	 *
+	 * @param <T> The class of the object being passed in.
+	 * @param o The class to find the class type for.
+	 * @return The ClassMeta object, or <jk>null</jk> if {@code o} is <jk>null</jk>.
+	 */
+	public <T> ClassMeta<T> getClassMetaForObject(T o) {
+		if (o == null)
+			return null;
+		return (ClassMeta<T>)getClassMeta(o.getClass());
+	}
+
+
+	/**
+	 * Used for determining the class type on a method or field where a {@code @BeanProperty} annotation
+	 * 	may be present.
+	 *
+	 * @param <T> The class type we're wrapping.
+	 * @param p The property annotation on the type if there is one.
+	 * @param t The type.
+	 * @param typeVarImpls Contains known resolved type parameters on the specified class so
+	 * 	that we can result {@code ParameterizedTypes} and {@code TypeVariables}.<br>
+	 * 	Can be <jk>null</jk> if the information is not known.
+	 * @return The new {@code ClassMeta} object wrapped around the {@code Type} object.
+	 */
+	protected <T> ClassMeta<T> getClassMeta(BeanProperty p, Type t, Map<Class<?>,Class<?>[]> typeVarImpls) {
+		ClassMeta<T> cm = getClassMeta(t, typeVarImpls);
+		ClassMeta<T> cm2 = cm;
+		if (p != null) {
+
+			if (p.type() != Object.class)
+				cm2 = getClassMeta(p.type(), typeVarImpls);
+
+			if (cm2.isMap()) {
+				Class<?>[] pParams = (p.params().length == 0 ? new Class[]{Object.class, Object.class} : p.params());
+				if (pParams.length != 2)
+					throw new RuntimeException("Invalid number of parameters specified for Map (must be 2): " + pParams.length);
+				ClassMeta<?> keyType = resolveType(pParams[0], cm2.getKeyType(), cm.getKeyType());
+				ClassMeta<?> valueType = resolveType(pParams[1], cm2.getValueType(), cm.getValueType());
+				if (keyType.isObject() && valueType.isObject())
+					return cm2;
+				return new ClassMeta<T>(cm2.innerClass, this).setKeyType(keyType).setValueType(valueType);
+			}
+
+			if (cm2.isCollection()) {
+				Class<?>[] pParams = (p.params().length == 0 ? new Class[]{Object.class} : p.params());
+				if (pParams.length != 1)
+					throw new RuntimeException("Invalid number of parameters specified for Collection (must be 1): " + pParams.length);
+				ClassMeta<?> elementType = resolveType(pParams[0], cm2.getElementType(), cm.getElementType());
+				if (elementType.isObject())
+					return cm2;
+				return new ClassMeta<T>(cm2.innerClass, this).setElementType(elementType);
+			}
+
+			return cm2;
+		}
+
+		return cm;
+	}
+
+	private ClassMeta<?> resolveType(Type...t) {
+		for (Type tt : t) {
+			if (tt != null) {
+				ClassMeta<?> cm = getClassMeta(tt);
+				if (tt != cmObject)
+					return cm;
+			}
+		}
+		return cmObject;
+	}
+
+	/**
+	 * Converts class name strings to ClassMeta objects.
+	 *
+	 * <dl>
+	 * 	<dt>Example:</dt>
+	 * 	<dd>
+	 * <ul>
+	 * 	<li><js>"java.lang.String"</js>
+	 * 	<li><js>"com.ibm.sample.MyBean[]"</js>
+	 * 	<li><js>"java.util.HashMap<java.lang.String,java.lang.Integer>"</js>
+	 * 	<li><js>"[Ljava.lang.String;"</js> (i.e. the value of <code>String[].<jk>class</jk>.getName()</code>)
+	 * </ul>
+	 * 	</dd>
+	 * </dl>
+	 *
+	 * @param s The class name.
+	 * @return The ClassMeta corresponding to the class name string.
+	 */
+	public ClassMeta<?> getClassMetaFromString(String s) {
+		int d = 0;
+		if (s == null || s.isEmpty())
+			return null;
+
+		// Check for Class.getName() on array class types.
+		if (s.charAt(0) == '[') {
+			try {
+				return getClassMeta(findClass(s));
+			} catch (ClassNotFoundException e) {
+				throw new RuntimeException(e);
+			}
+		}
+
+		int i1 = 0;
+		int i2 = 0;
+		int dim = 0;
+		List<ClassMeta<?>> p = null;
+		for (int i = 0; i < s.length(); i++) {
+			char c = s.charAt(i);
+			if (c == '<') {
+				if (d == 0) {
+					i1 = i;
+					i2 = i+1;
+					p = new LinkedList<ClassMeta<?>>();
+				}
+				d++;
+			} else if (c == '>') {
+				d--;
+				if (d == 0 && p != null)
+					p.add(getClassMetaFromString(s.substring(i2, i)));
+			} else if (c == ',' && d == 1) {
+				if (p != null)
+					p.add(getClassMetaFromString(s.substring(i2, i)));
+				i2 = i+1;
+			}
+			if (c == '[') {
+				if (i1 == 0)
+					i1 = i;
+				dim++;
+			}
+		}
+		if (i1 == 0)
+			i1 = s.length();
+		try {
+			String name = s.substring(0, i1).trim();
+			char x = name.charAt(0);
+			Class<?> c = null;
+			if (x >= 'b' && x <= 's') {
+				if (x == 'b' && name.equals("boolean"))
+					c = boolean.class;
+				else if (x == 'b' && name.equals("byte"))
+					c = byte.class;
+				else if (x == 'c' && name.equals("char"))
+					c = char.class;
+				else if (x == 'd' && name.equals("double"))
+					c = double.class;
+				else if (x == 'i' && name.equals("int"))
+					c = int.class;
+				else if (x == 'l' && name.equals("long"))
+					c = long.class;
+				else if (x == 's' && name.equals("short"))
+					c = short.class;
+				else
+					c = findClass(name);
+			} else {
+				c = findClass(name);
+			}
+
+			ClassMeta<?> cm = getClassMeta(c);
+
+			if (p != null) {
+				if (cm.isMap())
+					cm = new ClassMeta(c, this).setKeyType(p.get(0)).setValueType(p.get(1));
+				if (cm.isCollection())
+					cm = new ClassMeta(c, this).setElementType(p.get(0));
+			}
+
+			while (dim > 0) {
+				cm = new ClassMeta(Array.newInstance(cm.getInnerClass(), 0).getClass(), this);
+				dim--;
+			}
+
+			return cm;
+		} catch (ClassNotFoundException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private Class<?> findClass(String name) throws ClassNotFoundException {
+		return classLoader == null ? Class.forName(name) : Class.forName(name, true, classLoader);
+	}
+
+	/**
+	 * Returns the {@link PojoTransform} associated with the specified class, or <jk>null</jk> if there is no
+	 * pojo transform associated with the class.
+	 *
+	 * @param <T> The class associated with the transform.
+	 * @param c The class associated with the transform.
+	 * @return The transform associated with the class, or null if there is no association.
+	 */
+	protected <T> PojoTransform findPojoTransform(Class<T> c) {
+		// Note:  On first
+		if (c != null)
+			for (PojoTransform f : pojoTransforms)
+				if (isParentClass(f.forClass(), c))
+					return f;
+		return null;
+	}
+
+	/**
+	 * Checks whether a class has a {@link PojoTransform} associated with it in this bean context.
+	 * @param c The class to check.
+	 * @return <jk>true</jk> if the specified class or one of its subclasses has a {@link PojoTransform} associated with it.
+	 */
+	protected boolean hasChildPojoTransforms(Class<?> c) {
+		if (c != null)
+			for (PojoTransform f : pojoTransforms)
+				if (isParentClass(c, f.forClass()))
+					return true;
+		return false;
+	}
+
+	/**
+	 * Returns the {@link BeanTransform} associated with the specified class, or <jk>null</jk> if there is no
+	 * bean transform associated with the class.
+	 *
+	 * @param <T> The class associated with the transform.
+	 * @param c The class associated with the transform.
+	 * @return The transform associated with the class, or null if there is no association.
+	 */
+	protected <T> BeanTransform findBeanTransform(Class<T> c) {
+		if (c != null)
+			for (BeanTransform f : beanTransforms)
+				if (isParentClass(f.forClass(), c))
+					return f;
+		return null;
+	}
+
+	/**
+	 * Gets the no-arg constructor for the specified class.
+	 *
+	 * @param <T> The class to check.
+	 * @param c The class to check.
+	 * @param v The minimum visibility for the constructor.
+	 * @return The no arg constructor, or <jk>null</jk> if the class has no no-arg constructor.
+	 */
+	protected <T> Constructor<? extends T> getImplClassConstructor(Class<T> c, Visibility v) {
+		if (implClasses.isEmpty())
+			return null;
+		Class cc = c;
+		while (cc != null) {
+			Class implClass = implClasses.get(cc);
+			if (implClass != null)
+				return ClassMeta.findNoArgConstructor(implClass, v);
+			for (Class ic : cc.getInterfaces()) {
+				implClass = implClasses.get(ic);
+				if (implClass != null)
+					return ClassMeta.findNoArgConstructor(implClass, v);
+			}
+			cc = cc.getSuperclass();
+		}
+		return null;
+	}
+
+	/**
+	 * Converts the specified value to the specified class type.
+	 * <p>
+	 * 	See {@link #convertToType(Object, ClassMeta)} for the list of valid conversions.
+	 *
+	 * @param <T> The class type to convert the value to.
+	 * @param value The value to convert.
+	 * @param type The class type to convert the value to.
+	 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
+	 * @return The converted value.
+	 */
+	public <T> T convertToType(Object value, Class<T> type) throws InvalidDataConversionException {
+		// Shortcut for most common case.
+		if (value != null && value.getClass() == type)
+			return (T)value;
+		return convertToType(null, value, getClassMeta(type));
+	}
+
+	/**
+	 * Same as {@link #convertToType(Object, Class)}, except used for instantiating inner member classes that must
+	 * 	be instantiated within another class instance.
+	 *
+	 * @param <T> The class type to convert the value to.
+	 * @param outer If class is a member class, this is the instance of the containing class.
+	 * 	Should be <jk>null</jk> if not a member class.
+	 * @param value The value to convert.
+	 * @param type The class type to convert the value to.
+	 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
+	 * @return The converted value.
+	 */
+	public <T> T convertToType(Object outer, Object value, Class<T> type) throws InvalidDataConversionException {
+		return convertToType(outer, value, getClassMeta(type));
+	}
+
+	/**
+	 * Returns a reusable {@link ClassMeta} representation for the class <code>Object</code>.
+	 * <p>
+	 * This <code>ClassMeta</code> is often used to represent "any object type" when an object type
+	 * 	is not known.
+	 * <p>
+	 * This method is identical to calling <code>getClassMeta(Object.<jk>class</jk>)</code> but uses
+	 * 	a cached copy to avoid a hashmap lookup.
+	 *
+	 * @return The {@link ClassMeta} object associated with the <code>Object</code> class.
+	 */
+	public ClassMeta<Object> object() {
+		return cmObject;
+	}
+
+	/**
+	 * Returns a reusable {@link ClassMeta} representation for the class <code>String</code>.
+	 * <p>
+	 * This <code>ClassMeta</code> is often used to represent key types in maps.
+	 * <p>
+	 * This method is identical to calling <code>getClassMeta(String.<jk>class</jk>)</code> but uses
+	 * 	a cached copy to avoid a hashmap lookup.
+	 *
+	 * @return The {@link ClassMeta} object associated with the <code>String</code> class.
+	 */
+	public ClassMeta<String> string() {
+		return cmString;
+	}
+
+	/**
+	 * Returns a reusable {@link ClassMeta} representation for the class <code>Class</code>.
+	 * <p>
+	 * This <code>ClassMeta</code> is often used to represent key types in maps.
+	 * <p>
+	 * This method is identical to calling <code>getClassMeta(Class.<jk>class</jk>)</code> but uses
+	 * 	a cached copy to avoid a hashmap lookup.
+	 *
+	 * @return The {@link ClassMeta} object associated with the <code>String</code> class.
+	 */
+	public ClassMeta<Class> _class() {
+		return cmClass;
+	}
+
+	/**
+	 * Casts the specified value into the specified type.
+	 * <p>
+	 * 	If the value isn't an instance of the specified type, then converts
+	 * 	the value if possible.<br>
+	 * <p>
+	 * 	The following conversions are valid:
+	 * 	<table class='styled'>
+	 * 		<tr><th>Convert to type</th><th>Valid input value types</th><th>Notes</th></tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				A class that is the normal type of a registered {@link PojoTransform}.
+	 * 			</td>
+	 * 			<td>
+	 * 				A value whose class matches the transformed type of that registered {@link PojoTransform}.
+	 * 			</td>
+	 * 			<td>&nbsp;</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				A class that is the transformed type of a registered {@link PojoTransform}.
+	 * 			</td>
+	 * 			<td>
+	 * 				A value whose class matches the normal type of that registered {@link PojoTransform}.
+	 * 			</td>
+	 * 			<td>&nbsp;</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				{@code Number} (e.g. {@code Integer}, {@code Short}, {@code Float},...)<br>
+	 * 				<code>Number.<jsf>TYPE</jsf></code> (e.g. <code>Integer.<jsf>TYPE</jsf></code>, <code>Short.<jsf>TYPE</jsf></code>, <code>Float.<jsf>TYPE</jsf></code>,...)
+	 * 			</td>
+	 * 			<td>
+	 * 				{@code Number}, {@code String}, <jk>null</jk>
+	 * 			</td>
+	 * 			<td>
+	 * 				For primitive {@code TYPES}, <jk>null</jk> returns the JVM default value for that type.
+	 * 			</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				{@code Map} (e.g. {@code Map}, {@code HashMap}, {@code TreeMap}, {@code ObjectMap})
+	 * 			</td>
+	 * 			<td>
+	 * 				{@code Map}
+	 * 			</td>
+	 * 			<td>
+	 * 				If {@code Map} is not constructible, a {@code ObjectMap} is created.
+	 * 			</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 			{@code Collection} (e.g. {@code List}, {@code LinkedList}, {@code HashSet}, {@code ObjectList})
+	 * 			</td>
+	 * 			<td>
+	 * 				{@code Collection<Object>}<br>
+	 * 				{@code Object[]}
+	 * 			</td>
+	 * 			<td>
+	 * 				If {@code Collection} is not constructible, a {@code ObjectList} is created.
+	 * 			</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				{@code X[]} (array of any type X)<br>
+	 * 			</td>
+	 * 			<td>
+	 * 				{@code List<X>}<br>
+	 * 			</td>
+	 * 			<td>&nbsp;</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				{@code X[][]} (multi-dimensional arrays)<br>
+	 * 			</td>
+	 * 			<td>
+	 * 				{@code List<List<X>>}<br>
+	 * 				{@code List<X[]>}<br>
+	 * 				{@code List[]<X>}<br>
+	 * 			</td>
+	 * 			<td>&nbsp;</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				{@code Enum}<br>
+	 * 			</td>
+	 * 			<td>
+	 * 				{@code String}<br>
+	 * 			</td>
+	 * 			<td>&nbsp;</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				Bean<br>
+	 * 			</td>
+	 * 			<td>
+	 * 				{@code Map}<br>
+	 * 			</td>
+	 * 			<td>&nbsp;</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				{@code String}<br>
+	 * 			</td>
+	 * 			<td>
+	 * 				Anything<br>
+	 * 			</td>
+	 * 			<td>
+	 * 				Arrays are converted to JSON arrays<br>
+	 * 			</td>
+	 * 		</tr>
+	 * 		<tr>
+	 * 			<td>
+	 * 				Anything with one of the following methods:<br>
+	 * 				<code><jk>public static</jk> T fromString(String)</code><br>
+	 * 				<code><jk>public static</jk> T valueOf(String)</code><br>
+	 * 				<code><jk>public</jk> T(String)</code><br>
+	 * 			</td>
+	 * 			<td>
+	 * 				<code>String</code><br>
+	 * 			</td>
+	 * 			<td>
+	 * 				<br>
+	 * 			</td>
+	 * 		</tr>
+	 * 	</table>
+	 *
+	 * @param <T> The class type to convert the value to.
+	 * @param value The value to be converted.
+	 * @param type The target object type.
+	 * @return The converted type.
+	 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
+	 */
+	public <T> T convertToType(Object value, ClassMeta<T> type) throws InvalidDataConversionException {
+		return convertToType(null, value, type);
+	}
+
+	/**
+	 * Same as {@link #convertToType(Object, ClassMeta)}, except used for instantiating inner member classes that must
+	 * 	be instantiated within another class instance.
+	 *
+	 * @param <T> The class type to convert the value to.
+	 * @param outer If class is a member class, this is the instance of the containing class.
+	 * 	Should be <jk>null</jk> if not a member class.
+	 * @param value The value to convert.
+	 * @param type The class type to convert the value to.
+	 * @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
+	 * @return The converted value.
+	 */
+	public <T> T convertToType(Object outer, Object value, ClassMeta<T> type) throws InvalidDataConversionException {
+		if (type == null)
+			type = (ClassMeta<T>)object();
+
+		try {
+			// Handle the case of a null value.
+			if (value == null) {
+
+				// If it's a primitive, then use the converters to get the default value for the primitive type.
+				if (type.isPrimitive())
+					return type.getPrimitiveDefault();
+
+				// Otherwise, just return null.
+				return null;
+			}
+
+			Class<T> tc = type.getInnerClass();
+
+			// If no conversion needed, then just return the value.
+			// Don't include maps or collections, because child elements may need conversion.
+			if (tc.isInstance(value))
+				if (! ((type.isMap() && type.getValueType().isNotObject()) || (type.isCollection() && type.getElementType().isNotObject())))
+					return (T)value;
+
+			if (tc == Class.class)
+				return (T)(classLoader.loadClass(value.toString()));
+
+			if (type.getPojoTransform() != null) {
+				PojoTransform f = type.getPojoTransform();
+				Class<?> nc = f.getNormalClass(), fc = f.getTransformedClass();
+				if (isParentClass(nc, tc) && isParentClass(fc, value.getClass()))
+					return (T)f.normalize(value, type);
+			}
+
+			ClassMeta<?> vt = getClassMetaForObject(value);
+			if (vt.getPojoTransform() != null) {
+				PojoTransform f = vt.getPojoTransform();
+				Class<?> nc = f.getNormalClass(), fc = f.getTransformedClass();
+				if (isParentClass(nc, vt.getInnerClass()) && isParentClass(fc, tc))
+					return (T)f.transform(value);
+			}
+
+			if (type.isPrimitive()) {
+				if (value.toString().isEmpty())
+					return type.getPrimitiveDefault();
+
+				if (type.isNumber()) {
+					if (value instanceof Number) {
+						Number n = (Number)value;
+						if (tc == Integer.TYPE)
+							return (T)Integer.valueOf(n.intValue());
+						if (tc == Short.TYPE)
+							return (T)Short.valueOf(n.shortValue());
+						if (tc == Long.TYPE)
+							return (T)Long.valueOf(n.longValue());
+						if (tc == Float.TYPE)
+							return (T)Float.valueOf(n.floatValue());
+						if (tc == Double.TYPE)
+							return (T)Double.valueOf(n.doubleValue());
+						if (tc == Byte.TYPE)
+							return (T)Byte.valueOf(n.byteValue());
+					} else {
+						String n = null;
+						if (value instanceof Boolean)
+							n = ((Boolean)value).booleanValue() ? "1" : "0";
+						else
+							n = value.toString();
+						if (tc == Integer.TYPE)
+							return (T)Integer.valueOf(n);
+						if (tc == Short.TYPE)
+							return (T)Short.valueOf(n);
+						if (tc == Long.TYPE)
+							return (T)Long.valueOf(n);
+						if (tc == Float.TYPE)
+							return (T)new Float(n);
+						if (tc == Double.TYPE)
+							return (T)new Double(n);
+						if (tc == Byte.TYPE)
+							return (T)Byte.valueOf(n);
+					}
+				} else if (type.isChar()) {
+					String s = value.toString();
+					return (T)Character.valueOf(s.length() == 0 ? 0 : s.charAt(0));
+				} else if (type.isBoolean()) {
+					if (value instanceof Number) {
+						int i = ((Number)value).intValue();
+						return (T)(i == 0 ? Boolean.FALSE : Boolean.TRUE);
+					}
+					return (T)Boolean.valueOf(value.toString());
+				}
+			}
+
+			if (type.isNumber()) {
+				if (value instanceof Number) {
+					Number n = (Number)value;
+					if (tc == Integer.class)
+						return (T)Integer.valueOf(n.intValue());
+					if (tc == Short.class)
+						return (T)Short.valueOf(n.shortValue());
+					if (tc == Long.class)
+						return (T)Long.valueOf(n.longValue());
+					if (tc == Float.class)
+						return (T)Float.valueOf(n.floatValue());
+					if (tc == Double.class)
+						return (T)Double.valueOf(n.doubleValue());
+					if (tc == Byte.class)
+						return (T)Byte.valueOf(n.byteValue());
+					if (tc == Byte.class)
+						return (T)Byte.valueOf(n.byteValue());
+					if (tc == AtomicInteger.class)
+						return (T)new AtomicInteger(n.intValue());
+					if (tc == AtomicLong.class)
+						return (T)new AtomicLong(n.intValue());
+				} else {
+					if (value.toString().isEmpty())
+						return null;
+					String n = null;
+					if (value instanceof Boolean)
+						n = ((Boolean)value).booleanValue() ? "1" : "0";
+					else
+						n = value.toString();
+					if (tc == Integer.class)
+						return (T)Integer.valueOf(n);
+					if (tc == Short.class)
+						return (T)Short.valueOf(n);
+					if (tc == Long.class)
+						return (T)Long.valueOf(n);
+					if (tc == Float.class)
+						return (T)new Float(n);
+					if (tc == Double.class)
+						return (T)new Double(n);
+					if (tc == Byte.class)
+						return (T)Byte.valueOf(n);
+					if (tc == AtomicInteger.class)
+						return (T)new AtomicInteger(Integer.valueOf(n));
+					if (tc == AtomicLong.class)
+						return (T)new AtomicLong(Long.valueOf(n));
+				}
+			}
+
+			if (type.isChar()) {
+				String s = value.toString();
+				return (T)Character.valueOf(s.length() == 0 ? 0 : s.charAt(0));
+			}
+
+			// Handle setting of array properties
+			if (type.isArray()) {
+				if (vt.isCollection())
+					return (T)toArray(type, (Collection)value);
+				else if (vt.isArray())
+					return (T)toArray(type, Arrays.asList((Object[])value));
+				else if (StringUtils.startsWith(value.toString(), '['))
+					return (T)toArray(type, new ObjectList(value.toString()).setBeanContext(this));
+			}
+
+			// Target type is some sort of Map that needs to be converted.
+			if (type.isMap()) {
+				try {
+					if (value instanceof Map) {
+						Map m = type.canCreateNewInstance(outer) ? (Map)type.newInstance(outer) : new ObjectMap(this);
+						ClassMeta keyType = type.getKeyType(), valueType = type.getValueType();
+						for (Map.Entry e : (Set<Map.Entry>)((Map)value).entrySet()) {
+							Object k = e.getKey();
+							if (keyType.isNotObject()) {
+								if (keyType.isString() && k.getClass() != Class.class)
+									k = k.toString();
+								else
+									k = convertToType(m, k, keyType);
+							}
+							Object v = e.getValue();
+							if (valueType.isNotObject())
+								v = convertToType(m, v, valueType);
+							m.put(k, v);
+						}
+						return (T)m;
+					} else if (!type.canCreateNewInstanceFromString(outer)) {
+						ObjectMap m = new ObjectMap(value.toString(), defaultParser);
+						return convertToType(outer, m, type);
+					}
+				} catch (Exception e) {
+					throw new InvalidDataConversionException(value.getClass(), type, e);
+				}
+			}
+
+			// Target type is some sort of Collection
+			if (type.isCollection()) {
+				try {
+					Collection l = type.canCreateNewInstance(outer) ? (Collection)type.newInstance(outer) : new ObjectList(this);
+					ClassMeta elementType = type.getElementType();
+
+					if (value.getClass().isArray())
+						for (Object o : (Object[])value)
+							l.add(elementType.isObject() ? o : convertToType(l, o, elementType));
+					else if (value instanceof Collection)
+						for (Object o : (Collection)value)
+							l.add(elementType.isObject() ? o : convertToType(l, o, elementType));
+					else if (value instanceof Map)
+						l.add(elementType.isObject() ? value : convertToType(l, value, elementType));
+					else if (! value.toString().isEmpty())
+						throw new InvalidDataConversionException(value.getClass(), type, null);
+					return (T)l;
+				} catch (InvalidDataConversionException e) {
+					throw e;
+				} catch (Exception e) {
+					throw new InvalidDataConversionException(value.getClass(), type, e);
+				}
+			}
+
+			if (type.isEnum()) {
+				if (type.canCreateNewInstanceFromString(outer))
+					return type.newInstanceFromString(outer, value.toString());
+				return (T)Enum.valueOf((Class<? extends Enum>)tc, value.toString());
+			}
+
+			if (type.isString()) {
+				if (vt.isArray() || vt.isMap() || vt.isCollection() || vt.isBean()) {
+					if (JsonSerializer.DEFAULT_LAX != null)
+						return (T)JsonSerializer.DEFAULT_LAX.serialize(value);
+				} else if (vt.isClass()) {
+					return (T)ClassUtils.getReadableClassName((Class<?>)value);
+				}
+				return (T)value.toString();
+			}
+
+			if (type.isCharSequence()) {
+				Class<?> c = value.getClass();
+				if (c.isArray()) {
+					if (c.getComponentType().isPrimitive()) {
+						ObjectList l = new ObjectList(this);
+						int size = Array.getLength(value);
+						for (int i = 0; i < size; i++)
+							l.add(Array.get(value, i));
+						value = l;
+					}
+					value = new ObjectList((Object[])value).setBeanContext(this);
+				}
+
+				return type.newInstanceFromString(outer, value.toString());
+			}
+
+			if (type.isBoolean()) {
+				if (value instanceof Number)
+					return (T)(Boolean.valueOf(((Number)value).intValue() != 0));
+				return (T)Boolean.valueOf(value.toString());
+			}
+
+			// It's a bean being initialized with a Map
+			if (type.isBean() && value instanceof Map)
+				return newBeanMap(tc).load((Map<?,?>) value).getBean();
+
+			if (type.canCreateNewInstanceFromObjectMap(outer) && value instanceof ObjectMap)
+				return type.newInstanceFromObjectMap(outer, (ObjectMap)value);
+
+			if (type.canCreateNewInstanceFromNumber(outer) && value instanceof Number)
+				return type.newInstanceFromNumber(outer, (Number)value);
+
+			if (type.canCreateNewInstanceFromString(outer))
+				return type.newInstanceFromString(outer, value.toString());
+
+			if (type.isBean())
+				return newBeanMap(type.getInnerClass()).load(value.toString()).getBean();
+
+		} catch (Exception e) {
+			throw new InvalidDataConversionException(value, type, e);
+		}
+
+		throw new InvalidDataConversionException(value, type, null);
+	}
+
+	/**
+	 * Converts the contents of the specified list into an array.
+	 * <p>
+	 * 	Works on both object and primitive arrays.
+	 * <p>
+	 * 	In the case of multi-dimensional arrays, the incoming list must
+	 * 	contain elements of type n-1 dimension.  i.e. if {@code type} is <code><jk>int</jk>[][]</code>
+	 * 	then {@code list} must have entries of type <code><jk>int</jk>[]</code>.
+	 *
+	 * @param type The type to convert to.  Must be an array type.
+	 * @param list The contents to populate the array with.
+	 * @return A new object or primitive array.
+	 */
+	public Object toArray(ClassMeta<?> type, Collection<?> list) {
+		if (list == null)
+			return null;
+		ClassMeta<?> componentType = type.getElementType();
+		Object array = Array.newInstance(componentType.getInnerClass(), list.size());
+		int i = 0;
+		for (Object o : list) {
+			if (! type.getInnerClass().isInstance(o)) {
+				if (componentType.isArray() && o instanceof Collection)
+					o = toArray(componentType, (Collection<?>)o);
+				else if (o == null && componentType.isPrimitive())
+					o = componentType.getPrimitiveDefault();
+				else
+					o = convertToType(null, o, componentType);
+			}
+			try {
+				Array.set(array, i++, o);
+			} catch (IllegalArgumentException e) {
+				e.printStackTrace();
+				throw e;
+			}
+		}
+		return array;
+	}
+
+
+	/**
+	 * Returns the classloader associated with this bean context.
+	 * @return The classloader associated with this bean context.
+	 */
+	public ClassLoader getClassLoader() {
+		return classLoader;
+	}
+
+	@Override /* Object */
+	public int hashCode() {
+		return hashCode;
+	}
+
+	@Override /* Object */
+	public boolean equals(Object o) {
+		if (o instanceof BeanContext)
+			return ((BeanContext)o).cmCache == cmCache;
+		return false;
+	}
+
+	@Override /* Object */
+	public String toString() {
+		ObjectMap m = new ObjectMap()
+			.append("id", System.identityHashCode(this))
+			.append("beansRequireDefaultConstructor", beansRequireDefaultConstructor)
+			.append("beansRequireSerializable", beansRequireSerializable)
+			.append("beansRequireSettersForGetters", beansRequireSettersForGetters)
+			.append("beansRequireSomeProperties", beansRequireSomeProperties)
+			.append("beanMapPutReturnsOldValue", beanMapPutReturnsOldValue)
+			.append("beanConstructorVisibility", beanConstructorVisibility)
+			.append("beanClassVisibility", beanClassVisibility)
+			.append("beanMethodVisibility", beanMethodVisibility)
+			.append("beanFieldVisibility", beanFieldVisibility)
+			.append("useInterfaceProxies", useInterfaceProxies)
+			.append("ignoreUnknownBeanProperties", ignoreUnknownBeanProperties)
+			.append("ignoreUnknownNullBeanProperties", ignoreUnknownNullBeanProperties)
+			.append("ignorePropertiesWithoutSetters", ignorePropertiesWithoutSetters)
+			.append("ignoreInvocationExceptionsOnGetters", ignoreInvocationExceptionsOnGetters)
+			.append("ignoreInvocationExceptionsOnSetters", ignoreInvocationExceptionsOnSetters)
+			.append("useJavaBeanIntrospector", useJavaBeanIntrospector)
+			.append("beanTransforms", beanTransforms)
+			.append("pojoTransforms", pojoTransforms)
+			.append("notBeanClasses", notBeanClasses)
+			.append("implClasses", implClasses)
+			.append("sortProperties", sortProperties);
+		try {
+			return m.toString(JsonSerializer.DEFAULT_LAX_READABLE);
+		} catch (SerializeException e) {
+			return e.getLocalizedMessage();
+		}
+	}
+}