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/05/15 17:43:41 UTC

[1/2] incubator-juneau git commit: Add ConfigFile.getSectionAsInterface() method.

Repository: incubator-juneau
Updated Branches:
  refs/heads/master c68cc34d5 -> ca59d8a4e


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
index d84242e..da2e09f 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFile.java
@@ -57,9 +57,24 @@ public abstract class ConfigFile implements Map<String,Section> {
 	 * @param value The new value.
 	 * @param encoded
 	 * @return The previous value, or <jk>null</jk> if the section or key did not previously exist.
+	 * @throws SerializeException If the object value could not be converted to a JSON string for some reason.
 	 * @throws UnsupportedOperationException If config file is read only.
 	 */
-	public abstract String put(String sectionName, String sectionKey, Object value, boolean encoded);
+	public abstract String put(String sectionName, String sectionKey, Object value, boolean encoded) throws SerializeException;
+
+	/**
+	 * Identical to {@link #put(String, String, Object, boolean)} except used when the value is a simple string
+	 * to avoid having to catch a {@link SerializeException}.
+	 *
+	 * @param sectionName The section name.  Must not be <jk>null</jk>.
+	 * @param sectionKey The section key.  Must not be <jk>null</jk>.
+	 * @param value The new value.
+	 * @param encoded
+	 * @return The previous value, or <jk>null</jk> if the section or key did not previously exist.
+	 * @throws UnsupportedOperationException If config file is read only.
+	 */
+	public abstract String put(String sectionName, String sectionKey, String value, boolean encoded);
+
 
 	/**
 	 * Removes an antry from this config file.
@@ -158,20 +173,38 @@ public abstract class ConfigFile implements Map<String,Section> {
 	public abstract ConfigFile clearHeaderComments(String section);
 
 	/**
-	 * Returns the serializer in use for this config file.
+	 * Returns the reusable bean session associated with this config file.
+	 * <p>
+	 * Used for performing simple datatype conversions.
 	 *
-	 * @return This object (for method chaining).
-	 * @throws SerializeException If no serializer is defined on this config file.
+	 * @return The reusable bean session associated with this config file.
 	 */
-	protected abstract WriterSerializer getSerializer() throws SerializeException;
+	protected abstract BeanSession getBeanSession();
 
 	/**
-	 * Returns the parser in use for this config file.
+	 * Converts the specified object to a string.
+	 * <p>
+	 * The serialized output is identical to LAX JSON (JSON with unquoted attributes) except for the following exceptions:
+	 * <ul>
+	 * 	<li>Top level strings are not quoted.
+	 * </ul>
 	 *
-	 * @return This object (for method chaining).
-	 * @throws ParseException If no parser is defined on this config file.
+	 * @param o The object to serialize.
+	 * @return The serialized object.
+	 * @throws SerializeException
 	 */
-	protected abstract ReaderParser getParser() throws ParseException;
+	protected abstract String serialize(Object o) throws SerializeException;
+
+	/**
+	 * Converts the specified string to an object of the specified type.
+	 *
+	 * @param s The string to parse.
+	 * @param type The data type to create.
+	 * @param args The generic type arguments if the type is a {@link Collection} or {@link Map}
+	 * @return The parsed object.
+	 * @throws ParseException
+	 */
+	protected abstract <T> T parse(String s, Type type, Type...args) throws ParseException;
 
 	/**
 	 * Places a read lock on this config file.
@@ -222,81 +255,173 @@ public abstract class ConfigFile implements Map<String,Section> {
 	 * 	<li><js>"section/key"</js> - A value from the specified section.
 	 * </ul>
 	 * <p>
-	 * If the class type is an array, the value is split on commas and converted individually.
+	 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
+	 *
+	 * <h5 class='section'>Examples:</h5>
+	 * <p class='bcode'>
+	 * 	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+	 *
+	 * 	<jc>// Parse into a linked-list of strings.</jc>
+	 * 	List l = cf.getObject(<js>"MySection/myListOfStrings"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a linked-list of beans.</jc>
+	 * 	List l = cf.getObject(<js>"MySection/myListOfBeans"</js>, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a linked-list of linked-lists of strings.</jc>
+	 * 	List l = cf.getObject(<js>"MySection/my2dListOfStrings"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a map of string keys/values.</jc>
+	 * 	Map m = cf.getObject(<js>"MySection/myMap"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a map containing string keys and values of lists containing beans.</jc>
+	 * 	Map m = cf.getObject(<js>"MySection/myMapOfListsOfBeans"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
+	 * </p>
+	 * <p>
+	 * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type.
+	 * <p>
+	 * <code>Map</code> classes are assumed to be followed by zero or two meta objects indicating the key and value types.
 	 * <p>
-	 * If you specify a primitive element type using this method (e.g. <code><jk>int</jk>.<jk>class</jk></code>,
-	 * 	you will get an array of wrapped objects (e.g. <code>Integer[].<jk>class</jk></code>.
+	 * The array can be arbitrarily long to indicate arbitrarily complex data structures.
+	 * <p>
+	 * <h5 class='section'>Notes:</h5>
+	 * <ul>
+	 * 	<li>Use the {@link #getObject(String, Class)} method instead if you don't need a parameterized map/collection.
+	 * </ul>
 	 *
-	 * @param c The class to convert the value to.
 	 * @param key The key.  See {@link #getString(String)} for a description of the key.
+	 * @param type The object type to create.
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+	 * @param args The type arguments of the class if it's a collection or map.
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+	 * 	<br>Ignored if the main type is not a map or collection.
+	 *
 	 * @throws ParseException If parser could not parse the value or if a parser is not registered with this config file.
 	 * @return The value, or <jk>null</jk> if the section or key does not exist.
 	 */
-	@SuppressWarnings("unchecked")
-	public final <T> T getObject(Class<T> c, String key) throws ParseException {
-		assertFieldNotNull(c, "c");
-		return getObject(c, key, c.isArray() ? (T)Array.newInstance(c.getComponentType(), 0) : null);
+	public final <T> T getObject(String key, Type type, Type...args) throws ParseException {
+		assertFieldNotNull(key, "key");
+		assertFieldNotNull(type, "type");
+		return parse(getString(key), type, args);
 	}
 
 	/**
-	 * Gets the entry with the specified key and converts it to the specified value..
+	 * Same as {@link #getObject(String, Type, Type...)} except optimized for a non-parameterized class.
 	 * <p>
-	 * The key can be in one of the following formats...
-	 * <ul class='spaced-list'>
-	 * 	<li><js>"key"</js> - A value in the default section (i.e. defined above any <code>[section]</code> header).
-	 * 	<li><js>"section/key"</js> - A value from the specified section.
-	 * </ul>
+	 * This is the preferred parse method for simple types since you don't need to cast the results.
+	 * <h5 class='section'>Examples:</h5>
+	 * <p class='bcode'>
+	 * 	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+	 *
+	 * 	<jc>// Parse into a string.</jc>
+	 * 	String s = cf.getObject(<js>"MySection/mySimpleString"</js>, String.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a bean.</jc>
+	 * 	MyBean b = cf.getObject(<js>"MySection/myBean"</js>, MyBean.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a bean array.</jc>
+	 * 	MyBean[] ba = cf.getObject(<js>"MySection/myBeanArray"</js>, MyBean[].<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a linked-list of objects.</jc>
+	 * 	List l = cf.getObject(<js>"MySection/myList"</js>, LinkedList.<jk>class</jk>);
+	 *
+	 * 	<jc>// Parse into a map of object keys/values.</jc>
+	 * 	Map m = cf.getObject(<js>"MySection/myMap"</js>, TreeMap.<jk>class</jk>);
+	 * </p>
+	 *
+	 * @param <T> The class type of the object being created.
+	 * @param key The key.  See {@link #getString(String)} for a description of the key.
+	 * @param type The object type to create.
+	 * @return The parsed object.
+	 * @throws ParseException If the input contains a syntax error or is malformed, or is not valid for the specified type.
+	 * @see BeanSession#getClassMeta(Type,Type...) for argument syntax for maps and collections.
+	 */
+	public final <T> T getObject(String key, Class<T> type) throws ParseException {
+		assertFieldNotNull(key, "key");
+		assertFieldNotNull(type, "c");
+		return parse(getString(key), type);
+	}
+
+
+	/**
+	 * Gets the entry with the specified key and converts it to the specified value.
+	 * <p>
+	 * Same as {@link #getObject(String, Class)}, but with a default value.
 	 *
-	 * @param c The class to convert the value to.
 	 * @param key The key.  See {@link #getString(String)} for a description of the key.
 	 * @param def The default value if section or key does not exist.
+	 * @param type The class to convert the value to.
+	 *
 	 * @throws ParseException If parser could not parse the value or if a parser is not registered with this config file.
 	 * @return The value, or <jk>null</jk> if the section or key does not exist.
 	 */
-	public final <T> T getObject(Class<T> c, String key, T def) throws ParseException {
-		assertFieldNotNull(c, "c");
+	public final <T> T getObjectWithDefault(String key, T def, Class<T> type) throws ParseException {
 		assertFieldNotNull(key, "key");
-		return getObject(c, getSectionName(key), getSectionKey(key), def);
+		assertFieldNotNull(type, "c");
+		T t = parse(getString(key), type);
+		return (t == null ? def : t);
 	}
 
 	/**
-	 * Same as {@link #getObject(Class, String, Object)}, but value is referenced through section name and key instead of full key.
+	 * Gets the entry with the specified key and converts it to the specified value.
+	 * <p>
+	 * Same as {@link #getObject(String, Type, Type...)}, but with a default value.
+	 *
+	 * @param key The key.  See {@link #getString(String)} for a description of the key.
+	 * @param def The default value if section or key does not exist.
+	 * @param type The object type to create.
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+	 * @param args The type arguments of the class if it's a collection or map.
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+	 * 	<br>Ignored if the main type is not a map or collection.
+	 *
+	 * @throws ParseException If parser could not parse the value or if a parser is not registered with this config file.
+	 * @return The value, or <jk>null</jk> if the section or key does not exist.
+	 */
+	public final <T> T getObjectWithDefault(String key, T def, Type type, Type...args) throws ParseException {
+		assertFieldNotNull(key, "key");
+		assertFieldNotNull(type, "type");
+		T t = parse(getString(key), type, args);
+		return (t == null ? def : t);
+	}
+
+	/**
+	 * Gets the entry with the specified key and converts it to the specified value.
+	 * <p>
+	 * Same as {@link #getObject(String, Class)}, but used when key is already broken into section/key.
 	 *
-	 * @param c The class to convert the value to.
 	 * @param sectionName The section name.  Must not be <jk>null</jk>.
 	 * @param sectionKey The section key.  Must not be <jk>null</jk>.
-	 * @param def The default value if section or key does not exist.
+	 * @param c The class to convert the value to.
+	 *
 	 * @throws ParseException If parser could not parse the value or if a parser is not registered with this config file.
 	 * @return The value, or the default value if the section or value doesn't exist.
 	 */
-	@SuppressWarnings("unchecked")
-	public <T> T getObject(Class<T> c, String sectionName, String sectionKey, T def) throws ParseException {
-		String s = get(sectionName, sectionKey);
-		if (s == null)
-			return def;
-		if (c == String.class)
-			return (T)s;
-		if (c == Integer.class || c == int.class)
-			return (T)(StringUtils.isEmpty(s) ? def : Integer.valueOf(parseIntWithSuffix(s)));
-		if (c == Boolean.class || c == boolean.class)
-			return (T)(StringUtils.isEmpty(s) ? def : Boolean.valueOf(Boolean.parseBoolean(s)));
-		if (c == String[].class) {
-			String[] r = StringUtils.isEmpty(s) ? new String[0] : StringUtils.split(s, ',');
-			return (T)(r.length == 0 ? def : r);
-		}
-		if (c.isArray()) {
-			Class<?> ce = c.getComponentType();
-			if (StringUtils.isEmpty(s))
-				return def;
-			String[] r = StringUtils.split(s, ',');
-			Object o = Array.newInstance(ce, r.length);
-			for (int i = 0; i < r.length; i++)
-				Array.set(o, i, getParser().parse(r[i], ce));
-			return (T)o;
-		}
-		if (StringUtils.isEmpty(s))
-			return def;
-		return getParser().parse(s, c);
+	public final <T> T getObject(String sectionName, String sectionKey, Class<T> c) throws ParseException {
+		assertFieldNotNull(sectionName, "sectionName");
+		assertFieldNotNull(sectionKey, "sectionKey");
+		return parse(get(sectionName, sectionKey), c);
+	}
+
+	/**
+	 * Gets the entry with the specified key and converts it to the specified value.
+	 * <p>
+	 * Same as {@link #getObject(String, Type, Type...)}, but used when key is already broken into section/key.
+	 *
+	 * @param sectionName The section name.  Must not be <jk>null</jk>.
+	 * @param sectionKey The section key.  Must not be <jk>null</jk>.
+	 * @param type The object type to create.
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+	 * @param args The type arguments of the class if it's a collection or map.
+	 * 	<br>Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType}, {@link GenericArrayType}
+	 * 	<br>Ignored if the main type is not a map or collection.
+	 *
+	 * @throws ParseException If parser could not parse the value or if a parser is not registered with this config file.
+	 * @return The value, or <jk>null</jk> if the section or key does not exist.
+	 */
+	public final <T> T getObject(String sectionName, String sectionKey, Type type, Type...args) throws ParseException {
+		assertFieldNotNull(sectionName, "sectionName");
+		assertFieldNotNull(sectionKey, "sectionKey");
+		return parse(get(sectionName, sectionKey), type, args);
 	}
 
 	/**
@@ -430,33 +555,7 @@ public abstract class ConfigFile implements Map<String,Section> {
 	 */
 	public final String put(String key, Object value, boolean encoded) throws SerializeException {
 		assertFieldNotNull(key, "key");
-		if (value == null)
-			value = "";
-		Class<?> c = value.getClass();
-		if (isSimpleType(c))
-			return put(getSectionName(key), getSectionKey(key), value.toString(), encoded);
-		if (c.isAssignableFrom(Collection.class)) {
-			Collection<?> c2 = (Collection<?>)value;
-			String[] r = new String[c2.size()];
-			int i = 0;
-			for (Object o2 : c2) {
-				boolean isSimpleType = o2 == null ? true : isSimpleType(o2.getClass());
-				r[i++] = (isSimpleType ? Array.get(value, i).toString() : getSerializer().toString(Array.get(value, i)));
-			}
-			return put(getSectionName(key), getSectionKey(key), StringUtils.join(r, ','), encoded);
-		} else if (c.isArray()) {
-			boolean isSimpleType = isSimpleType(c.getComponentType());
-			String[] r = new String[Array.getLength(value)];
-			for (int i = 0; i < r.length; i++) {
-				r[i] = (isSimpleType ? Array.get(value, i).toString() : getSerializer().toString(Array.get(value, i)));
-			}
-			return put(getSectionName(key), getSectionKey(key), StringUtils.join(r, ','), encoded);
-		}
-		return put(getSectionName(key), getSectionKey(key), getSerializer().toString(value), encoded);
-	}
-
-	private static boolean isSimpleType(Class<?> c) {
-		return (c == String.class || c.isPrimitive() || c.isAssignableFrom(Number.class) || c == Boolean.class);
+		return put(getSectionName(key), getSectionKey(key), serialize(value), encoded);
 	}
 
 	/**
@@ -510,7 +609,7 @@ public abstract class ConfigFile implements Map<String,Section> {
 					Class<?> pt = m.getParameterTypes()[0];
 					if (permittedPropertyTypes == null || permittedPropertyTypes.length == 0 || ArrayUtils.contains(pt, permittedPropertyTypes)) {
 						String propName = Introspector.decapitalize(m.getName().substring(3));
-						Object value = getObject(pt, sectionName, propName, null);
+						Object value = getObject(sectionName, propName, pt);
 						if (value != null) {
 							m.invoke(bean, value);
 							om.put(propName, value);
@@ -528,7 +627,7 @@ public abstract class ConfigFile implements Map<String,Section> {
 	}
 
 	/**
-	 * Shortcut for calling <code>asBean(sectionName, c, <jk>false</jk>)</code>.
+	 * Shortcut for calling <code>getSectionAsBean(sectionName, c, <jk>false</jk>)</code>.
 	 *
 	 * @param sectionName The section name to write from.
 	 * @param c The bean class to create.
@@ -541,6 +640,33 @@ public abstract class ConfigFile implements Map<String,Section> {
 
 	/**
 	 * Converts this config file section to the specified bean instance.
+	 * <p>
+	 * Key/value pairs in the config file section get copied as bean property values to the specified bean class.
+	 * <p>
+	 * <h6 class='figure'>Example config file</h6>
+	 * <p class='bcode'>
+	 * 	<cs>[MyAddress]</cs>
+	 * 	<ck>name</ck> = <cv>John Smith</cv>
+	 * 	<ck>street</ck> = <cv>123 Main Street</cv>
+	 * 	<ck>city</ck> = <cv>Anywhere</cv>
+	 * 	<ck>state</ck> = <cv>NY</cv>
+	 * 	<ck>zip</ck> = <cv>12345</cv>
+	 * </p>
+	 *
+	 * <h6 class='figure'>Example bean</h6>
+	 * <p class='bcode'>
+	 * 	<jk>public class</jk> Address {
+	 * 		public String name, street, city;
+	 * 		public StateEnum state;
+	 * 		public int zip;
+	 * 	}
+	 * </p>
+	 *
+	 * <h6 class='figure'>Example usage</h6>
+	 * <p class='bcode'>
+	 * 	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+	 * 	Address myAddress = cf.getSectionAsBean(<js>"MySection"</js>, Address.<jk>class</jk>);
+	 * </p>
 	 *
 	 * @param sectionName The section name to write from.
 	 * @param c The bean class to create.
@@ -553,14 +679,14 @@ public abstract class ConfigFile implements Map<String,Section> {
 		assertFieldNotNull(c, "c");
 		readLock();
 		try {
-			BeanMap<T> bm = getParser().getBeanContext().createSession().newBeanMap(c);
+			BeanMap<T> bm = getBeanSession().newBeanMap(c);
 			for (String k : getSectionKeys(sectionName)) {
 				BeanPropertyMeta bpm = bm.getPropertyMeta(k);
 				if (bpm == null) {
 					if (! ignoreUnknownProperties)
 						throw new ParseException("Unknown property {0} encountered", k);
 				} else {
-					bm.put(k, getObject(bpm.getClassMeta().getInnerClass(), sectionName + '/' + k));
+					bm.put(k, getObject(sectionName + '/' + k, bpm.getClassMeta().getInnerClass()));
 				}
 			}
 			return bm.getBean();
@@ -570,6 +696,88 @@ public abstract class ConfigFile implements Map<String,Section> {
 	}
 
 	/**
+	 * Wraps a config file section inside a Java interface so that values in the section can be read and
+	 * write using getters and setters.
+	 * <p>
+	 * <h6 class='figure'>Example config file</h6>
+	 * <p class='bcode'>
+	 * 	<cs>[MySection]</cs>
+	 * 	<ck>string</ck> = <cv>foo</cv>
+	 * 	<ck>int</ck> = <cv>123</cv>
+	 * 	<ck>enum</ck> = <cv>ONE</cv>
+	 * 	<ck>bean</ck> = <cv>{foo:'bar',baz:123}</cv>
+	 * 	<ck>int3dArray</ck> = <cv>[[[123,null],null],null]</cv>
+	 * 	<ck>bean1d3dListMap</ck> = <cv>{key:[[[[{foo:'bar',baz:123}]]]]}</cv>
+	 * </p>
+	 *
+	 * <h6 class='figure'>Example interface</h6>
+	 * <p class='bcode'>
+	 * 	<jk>public interface</jk> MyConfigInterface {
+	 *
+	 * 		String getString();
+	 * 		<jk>void</jk> setString(String x);
+	 *
+	 * 		<jk>int</jk> getInt();
+	 * 		<jk>void</jk> setInt(<jk>int</jk> x);
+	 *
+	 * 		MyEnum getEnum();
+	 * 		<jk>void</jk> setEnum(MyEnum x);
+	 *
+	 * 		MyBean getBean();
+	 * 		<jk>void</jk> setBean(MyBean x);
+	 *
+	 * 		<jk>int</jk>[][][] getInt3dArray();
+	 * 		<jk>void</jk> setInt3dArray(<jk>int</jk>[][][] x);
+	 *
+	 * 		Map&lt;String,List&lt;MyBean[][][]&gt;&gt; getBean1d3dListMap();
+	 * 		<jk>void</jk> setBean1d3dListMap(Map&lt;String,List&lt;MyBean[][][]&gt;&gt; x);
+	 * 	}
+	 * </p>
+	 *
+	 * <h6 class='figure'>Example usage</h6>
+	 * <p class='bcode'>
+	 * 	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+	 *
+	 * 	MyConfigInterface ci = cf.getSectionAsInterface(<js>"MySection"</js>, MyConfigInterface.<jk>class</jk>);
+	 *
+	 * 	<jk>int</jk> myInt = ci.getInt();
+	 *
+	 * 	ci.setBean(<jk>new</jk> MyBean());
+	 *
+	 * 	cf.save();
+	 * </p>
+	 *
+	 * @param sectionName The section name to retrieve as an interface proxy.
+	 * @param c The proxy interface class.
+	 * @return The proxy interface.
+	 */
+	@SuppressWarnings("unchecked")
+	public final <T> T getSectionAsInterface(final String sectionName, final Class<T> c) {
+		assertFieldNotNull(c, "c");
+
+		if (! c.isInterface())
+			throw new UnsupportedOperationException("Class passed to getSectionAsInterface is not an interface.");
+
+		InvocationHandler h = new InvocationHandler() {
+
+			@Override
+			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+				BeanInfo bi = Introspector.getBeanInfo(c, null);
+				for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
+					Method rm = pd.getReadMethod(), wm = pd.getWriteMethod();
+					if (method.equals(rm))
+						return ConfigFile.this.getObject(sectionName, pd.getName(), rm.getGenericReturnType());
+					if (method.equals(wm))
+						return ConfigFile.this.put(sectionName, pd.getName(), args[0], false);
+				}
+				throw new UnsupportedOperationException("Unsupported interface method.  method=[ " + method + " ]");
+			}
+		};
+
+		return (T)Proxy.newProxyInstance(c.getClassLoader(), new Class[] { c }, h);
+	}
+
+	/**
 	 * Returns <jk>true</jk> if this section contains the specified key and the key has a non-blank value.
 	 *
 	 * @param key The key.  See {@link #getString(String)} for a description of the key.

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java
index a699b4f..5d44f10 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileBuilder.java
@@ -69,7 +69,7 @@ public class ConfigFileBuilder {
 	}
 
 	/**
-	 * Specify the parser to use for parsing POJOs when using {@link ConfigFile#getObject(Class,String)}.
+	 * Specify the parser to use for parsing POJOs when using {@link ConfigFile#getObject(String,Class)}.
 	 * <p>
 	 * The default value for this setting is {@link JsonParser#DEFAULT}
 	 *

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
index f1ef0eb..dc784b5 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileImpl.java
@@ -16,6 +16,7 @@ import static org.apache.juneau.ini.ConfigUtils.*;
 import static org.apache.juneau.internal.ThrowableUtils.*;
 
 import java.io.*;
+import java.lang.reflect.*;
 import java.nio.charset.*;
 import java.util.*;
 import java.util.concurrent.locks.*;
@@ -37,6 +38,7 @@ public final class ConfigFileImpl extends ConfigFile {
 	private final Encoder encoder;
 	private final WriterSerializer serializer;
 	private final ReaderParser parser;
+	private final BeanSession pBeanSession;
 	private final Charset charset;
 	final List<ConfigFileListener> listeners = Collections.synchronizedList(new ArrayList<ConfigFileListener>());
 
@@ -66,7 +68,7 @@ public final class ConfigFileImpl extends ConfigFile {
 	 * If <jk>null</jk>, defaults to {@link XorEncoder#INSTANCE}.
 	 * @param serializer The serializer to use for serializing POJOs in the {@link #put(String, Object)} method.
 	 * If <jk>null</jk>, defaults to {@link JsonSerializer#DEFAULT}.
-	 * @param parser The parser to use for parsing POJOs in the {@link #getObject(Class,String)} method.
+	 * @param parser The parser to use for parsing POJOs in the {@link #getObject(String,Class)} method.
 	 * If <jk>null</jk>, defaults to {@link JsonParser#DEFAULT}.
 	 * @param charset The charset on the files.
 	 * If <jk>null</jk>, defaults to {@link Charset#defaultCharset()}.
@@ -85,6 +87,7 @@ public final class ConfigFileImpl extends ConfigFile {
 			for (Section s : sections.values())
 				s.setReadOnly();
 		}
+		this.pBeanSession = this.parser.getBeanContext().createSession();
 	}
 
 	/**
@@ -193,6 +196,43 @@ public final class ConfigFileImpl extends ConfigFile {
 		return this;
 	}
 
+	@Override /* ConfigFile */
+	protected String serialize(Object value) throws SerializeException {
+		if (value == null)
+			return "";
+		Class<?> c = value.getClass();
+		if (isSimpleType(c))
+			return value.toString();
+		String s = serializer.toString(value);
+		if (s.startsWith("'"))
+			return s.substring(1, s.length()-1);
+		return s;
+	}
+
+	@Override /* ConfigFile */
+	@SuppressWarnings({ "unchecked" })
+	protected <T> T parse(String s, Type type, Type...args) throws ParseException {
+
+		if (StringUtils.isEmpty(s))
+			return null;
+
+		if (isSimpleType(type))
+			return (T)pBeanSession.convertToType(s, (Class<?>)type);
+
+		char s1 = StringUtils.charAt(s, 0);
+		if (s1 != '[' && s1 != '{' && ! "null".equals(s))
+			s = '\'' + s + '\'';
+
+		return parser.parse(s, type, args);
+	}
+
+	private static boolean isSimpleType(Type t) {
+		if (! (t instanceof Class))
+			return false;
+		Class<?> c = (Class<?>)t;
+		return (c == String.class || c.isPrimitive() || c.isAssignableFrom(Number.class) || c == Boolean.class || c.isEnum());
+	}
+
 
 	//--------------------------------------------------------------------------------
 	// Map methods
@@ -429,10 +469,17 @@ public final class ConfigFileImpl extends ConfigFile {
 	}
 
 	@Override /* ConfigFile */
-	public String put(String sectionName, String sectionKey, Object value, boolean encoded) {
+	public String put(String sectionName, String sectionKey, Object value, boolean encoded) throws SerializeException {
 		assertFieldNotNull(sectionKey, "sectionKey");
 		Section s = getSection(sectionName, true);
-		return s.put(sectionKey, value.toString(), encoded);
+		return s.put(sectionKey, serialize(value), encoded);
+	}
+
+	@Override /* ConfigFile */
+	public String put(String sectionName, String sectionKey, String value, boolean encoded) {
+		assertFieldNotNull(sectionKey, "sectionKey");
+		Section s = getSection(sectionName, true);
+		return s.put(sectionKey, value, encoded);
 	}
 
 	@Override /* ConfigFile */
@@ -657,17 +704,8 @@ public final class ConfigFileImpl extends ConfigFile {
 	}
 
 	@Override /* ConfigFile */
-	protected WriterSerializer getSerializer() throws SerializeException {
-		if (serializer == null)
-			throw new SerializeException("Serializer not defined on config file.");
-		return serializer;
-	}
-
-	@Override /* ConfigFile */
-	protected ReaderParser getParser() throws ParseException {
-		if (parser == null)
-			throw new ParseException("Parser not defined on config file.");
-		return parser;
+	protected BeanSession getBeanSession() {
+		return pBeanSession;
 	}
 
 	@Override /* ConfigFile */

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
index 62f3899..5407c0f 100644
--- a/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
+++ b/juneau-core/src/main/java/org/apache/juneau/ini/ConfigFileWrapped.java
@@ -15,6 +15,7 @@ package org.apache.juneau.ini;
 import static org.apache.juneau.internal.ThrowableUtils.*;
 
 import java.io.*;
+import java.lang.reflect.*;
 import java.util.*;
 
 import org.apache.juneau.*;
@@ -233,13 +234,8 @@ public final class ConfigFileWrapped extends ConfigFile {
 	}
 
 	@Override /* ConfigFile */
-	protected WriterSerializer getSerializer() throws SerializeException {
-		return cf.getSerializer();
-	}
-
-	@Override /* ConfigFile */
-	protected ReaderParser getParser() throws ParseException {
-		return cf.getParser();
+	protected BeanSession getBeanSession() {
+		return cf.getBeanSession();
 	}
 
 	@Override /* ConfigFile */
@@ -251,7 +247,12 @@ public final class ConfigFileWrapped extends ConfigFile {
 	}
 
 	@Override /* ConfigFile */
-	public String put(String sectionName, String sectionKey, Object value, boolean encoded) {
+	public String put(String sectionName, String sectionKey, String value, boolean encoded) {
+		return cf.put(sectionName, sectionKey, value, encoded);
+	}
+
+	@Override /* ConfigFile */
+	public String put(String sectionName, String sectionKey, Object value, boolean encoded) throws SerializeException {
 		return cf.put(sectionName, sectionKey, value, encoded);
 	}
 
@@ -274,4 +275,14 @@ public final class ConfigFileWrapped extends ConfigFile {
 	protected void readUnlock() {
 		cf.readUnlock();
 	}
+
+	@Override /* ConfigFile */
+	protected String serialize(Object o) throws SerializeException {
+		return cf.serialize(o);
+	}
+
+	@Override /* ConfigFile */
+	protected <T> T parse(String s, Type type, Type... args) throws ParseException {
+		return cf.parse(s, type, args);
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
index 92ab945..490eed1 100644
--- a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
+++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java
@@ -590,7 +590,7 @@ public class JsonParser extends ReaderParser {
 						break;
 					}
 				} else {
-					if (c == ',' || c == '}' || session.isWhitespace(c)) {
+					if (c == ',' || c == '}' || c == ']' || session.isWhitespace(c)) {
 						s = r.getMarked(0, -1);
 						r.unread();
 						break;

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/javadoc/overview.html
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html
index 62ed843..980b84a 100644
--- a/juneau-core/src/main/javadoc/overview.html
+++ b/juneau-core/src/main/javadoc/overview.html
@@ -1373,14 +1373,14 @@
 	<cc># Default section</cc>
 	<ck>key1</ck> = <cv>1</cv>
 	<ck>key2</ck> = <cv>true</cv>
-	<ck>key3</ck> = <cv>1,2,3</cv>
+	<ck>key3</ck> = <cv>[1,2,3]</cv>
 	<ck>key4</ck> = <cv>http://foo</cv>
 	
 	<cc># Section 1</cc>
 	<cs>[Section1]</cs>
 	<ck>key1</ck> = <cv>2</cv>
 	<ck>key2</ck> = <cv>false</cv>
-	<ck>key3</ck> = <cv>4,5,6</cv>
+	<ck>key3</ck> = <cv>[4,5,6]</cv>
 	<ck>key4</ck> = <cv>http://bar</cv>
 		</p>
 		<p>
@@ -1393,7 +1393,7 @@
 	URL key4;
 	
 	<jc>// Load our config file</jc>
-	ConfigFile f = <jk>new</jk> ConfigFileBuilder().build(<js>"MyIniFile.cfg"</js>);
+	ConfigFile f = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
 	
 	<jc>// Read values from default section</jc>
 	key1 = f.getInt(<js>"key1"</js>);
@@ -1412,12 +1412,12 @@
 		</p>
 		<p class='bcode'>
 	<jc>// Construct the sample INI file programmatically</jc>
-	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyIniFile.cfg"</js>)
+	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>)
 		.addLines(<jk>null</jk>,
 			<js>"# Default section"</js>,
 			<js>"key1 = 1"</js>,
 			<js>"key2 = true"</js>,
-			<js>"key3 = 1,2,3"</js>,
+			<js>"key3 = [1,2,3]"</js>,
 			<js>"key4 = http://foo"</js>,
 			<js>""</js>)
 		.addHeaderComments(<js>"Section1"</js>,
@@ -1425,7 +1425,7 @@
 		.addLines(<js>"Section1"</js>,
 			<js>"key1 = 2"</js>,
 			<js>"key2 = false"</js>,
-			<js>"key3 = 4,5,6"</js>,
+			<js>"key3 = [4,5,6]"</js>,
 			<js>"key4 = http://bar"</js>)
 		.save();
 		</p>
@@ -1434,7 +1434,7 @@
 		</p>
 		<p class='bcode'>
 	<jc>// Construct the sample INI file programmatically</jc>
-	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyIniFile.cfg"</js>)
+	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>)
 		.addLines(<jk>null</jk>,
 			<js>"# Default section"</js>)
 		.addHeaderComments(<js>"section1"</js>,
@@ -1450,11 +1450,14 @@
 	cf.save();
 		</p>
 		<p>
+			Values are LAX JSON (i.e. unquoted attributes, single quotes) except for top-level strings which are left unquoted.  
+			Any parsable object types are supported as values (e.g. arrays, collections, beans, swappable objects, enums, etc...).
+		</p>
+		<p>
 			The config file looks deceptively simple, the config file API is a very powerful feature with many capabilities, including:
 		</p>
 		<ul class='spaced-list'>
 			<li>The ability to use variables to reference environment variables, system properties, other config file entries, and a host of other types.
-			<li>The ability to store and retrieve POJOs as JSON.
 			<li>APIs for updating, modifying, and saving configuration files without losing comments or formatting.
 			<li>Extensive listener APIs.
 		</ul>
@@ -1472,7 +1475,7 @@
 	<ck>aBoolean</ck> = <cv>true</cv>
 	
 	<cc># An int array</cc>
-	<ck>anIntArray</ck> = <cv>1,2,3</cv>
+	<ck>anIntArray</ck> = <cv>[1,2,3]</cv>
 	
 	<cc># A POJO that can be converted from a String</cc>
 	<ck>aURL</ck> = <cv>http://foo </cv>
@@ -1521,6 +1524,75 @@
 	String myArg = cf.getString(<js>"MySection/myArg"</js>); 
 	String firstArg = cf.getString(<js>"MySection/firstArg"</js>); 
 		</p>
+		<p>
+			Config files can also be used to directly populate beans using the {@link org.apache.juneau.ini.ConfigFile#getSectionAsBean(String,Class,boolean)}:
+		</p>
+		<p class='bcode'>
+	<jc>// Example config file</jc>
+	<cs>[MyAddress]</cs>
+	<ck>name</ck> = <cv>John Smith</cv>
+	<ck>street</ck> = <cv>123 Main Street</cv>
+	<ck>city</ck> = <cv>Anywhere</cv>
+	<ck>state</ck> = <cv>NY</cv>
+	<ck>zip</ck> = <cv>12345</cv>
+
+	<jc>// Example bean</jc>
+	<jk>public class</jk> Address {
+		public String name, street, city;
+		public StateEnum state;
+		public int zip;
+	}
+
+	<jc>// Example usage</jc>
+	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+	Address myAddress = cf.getSectionAsBean(<js>"MySection"</js>, Address.<jk>class</jk>);
+		</p>
+		<p>
+			Config file sections can also be accessed via interface proxies using {@link org.apache.juneau.ini.ConfigFile#getSectionAsInterface(String,Class)}:
+		</p>
+		<p class='bcode'>
+	<jc>// Example config file</jc>
+	<cs>[MySection]</cs>
+	<ck>string</ck> = <cv>foo</cv>
+	<ck>int</ck> = <cv>123</cv>
+	<ck>enum</ck> = <cv>ONE</cv>
+	<ck>bean</ck> = <cv>{foo:'bar',baz:123}</cv>
+	<ck>int3dArray</ck> = <cv>[[[123,null],null],null]</cv>
+	<ck>bean1d3dListMap</ck> = <cv>{key:[[[[{foo:'bar',baz:123}]]]]}</cv>
+
+	<jc>// Example interface</jc>
+	<jk>public interface</jk> MyConfigInterface {
+
+		String getString();
+		<jk>void</jk> setString(String x);
+
+		<jk>int</jk> getInt();
+		<jk>void</jk> setInt(<jk>int</jk> x);
+
+		MyEnum getEnum();
+		<jk>void</jk> setEnum(MyEnum x);
+
+		MyBean getBean();
+		<jk>void</jk> setBean(MyBean x);
+
+		<jk>int</jk>[][][] getInt3dArray();
+		<jk>void</jk> setInt3dArray(<jk>int</jk>[][][] x);
+		
+		Map&lt;String,List&lt;MyBean[][][]&gt;&gt; getBean1d3dListMap();
+		<jk>void</jk> setBean1d3dListMap(Map&lt;String,List&lt;MyBean[][][]&gt;&gt; x);
+	}
+	
+	<jc>// Example usage</jc>
+	ConfigFile cf = <jk>new</jk> ConfigFileBuilder().build(<js>"MyConfig.cfg"</js>);
+	MyConfigInterface ci = cf.getSectionAsInterface(<js>"MySection"</js>, MyConfigInterface.<jk>class</jk>);
+	<jk>int</jk> myInt = ci.getInt();
+	ci.setBean(<jk>new</jk> MyBean());
+	cf.save();
+		</p>
+			
+		
+		
+		
 		
 		<h6 class='topic'>Additional Information</h6>
 		<ul class='javahierarchy'>
@@ -5973,6 +6045,23 @@
 			<li>New package:  {@link org.apache.juneau.http}.
 			<li>Support for dynamic beans.  See {@link org.apache.juneau.annotation.BeanProperty#name() @BeanProperty.name()}.
 			<li>New doc: <a class='doclink' href='#Core.JacksonComparison'>2.12 - Comparison with Jackson</a>
+			<li>All parsers now allow for numeric types with <js>'K'</js>/<js>'M'</js>/<js>'G'</js> suffixes to represent
+				kilobytes, megabytes, and gigabytes.
+				<p class='bcode'>
+	<jc>// Example</jc>
+	<jk>int</jk> i = JsonParser.<jsf>DEFAULT</jsf>.parse(<js>"123M"</js>);  <jc>// 123MB</jc>
+				</p>
+			<li>New/modified methods on {@link org.apache.juneau.ini.ConfigFile}:
+				<ul>
+					<li>{@link org.apache.juneau.ini.ConfigFile#put(String,String,Object,boolean) put(String,String,Object,boolean)} 
+					<li>{@link org.apache.juneau.ini.ConfigFile#getObject(String,Type,Type...) getObject(String,Type,Type...)} 
+					<li>{@link org.apache.juneau.ini.ConfigFile#getObject(String,Class) getObject(String,Class)} 
+					<li>{@link org.apache.juneau.ini.ConfigFile#getObject(String,String,Type,Type...) getObject(String,String,Type,Type...)} 
+					<li>{@link org.apache.juneau.ini.ConfigFile#getObject(String,String,Class) getObject(String,String,Class)} 
+					<li>{@link org.apache.juneau.ini.ConfigFile#getObjectWithDefault(String,Object,Type,Type...) getObjectWithDefault(String,Object,Type,Type)} 
+					<li>{@link org.apache.juneau.ini.ConfigFile#getObjectWithDefault(String,Object,Class) getObjectWithDefault(String,Object,Class)} 
+				</ul>
+			<li>New ability to interact with config file sections with proxy interfaces with new method {@link org.apache.juneau.ini.ConfigFile#getSectionAsInterface(String,Class)}.
 		</ul>
 
 		<h6 class='topic'>org.apache.juneau.rest</h6>

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-examples-rest/examples.cfg
----------------------------------------------------------------------
diff --git a/juneau-examples-rest/examples.cfg b/juneau-examples-rest/examples.cfg
index e41894e..352533f 100755
--- a/juneau-examples-rest/examples.cfg
+++ b/juneau-examples-rest/examples.cfg
@@ -23,10 +23,10 @@
 
 resources = org.apache.juneau.examples.rest.RootResources
 
-# Ports to try.
+# Array of ports to try.
 # 0 means try a random port.
 # 3 0's means try 3 random ports.
-port = 10000, 0, 0, 0
+port = [10000, 0, 0, 0]
 
 # Authentication:  NONE, BASIC.
 authType = NONE

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-microservice-template/microservice.cfg
----------------------------------------------------------------------
diff --git a/juneau-microservice-template/microservice.cfg b/juneau-microservice-template/microservice.cfg
index 82ac437..c3eb1d0 100755
--- a/juneau-microservice-template/microservice.cfg
+++ b/juneau-microservice-template/microservice.cfg
@@ -27,9 +27,10 @@ REST = org.apache.juneau.microservice.rest.RestApplication
 #================================================================================
 [REST]
 
-# The HTTP port number to use.
-# Default is Rest-Port setting in manifest file, or 8000.
-port = 10000
+# Array of ports to try for hosting the REST interface.
+# 0 means try a random port.
+# 3 0's means try 3 random ports.
+port = [10000, 0, 0, 0]
 
 # A JSON map of servlet paths to servlet classes.
 # Example:  

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
----------------------------------------------------------------------
diff --git a/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java b/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
index 050ec1a..9c05035 100755
--- a/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
+++ b/juneau-microservice/src/main/java/org/apache/juneau/microservice/RestMicroservice.java
@@ -269,7 +269,7 @@ public class RestMicroservice extends Microservice {
 			ch.setFormatter(new LogEntryFormatter(format, dateFormat, false));
 			logger.addHandler(ch);
 		}
-		ObjectMap loggerLevels = cf.getObject(ObjectMap.class, "Logging/levels");
+		ObjectMap loggerLevels = cf.getObject("Logging/levels", ObjectMap.class);
 		if (loggerLevels != null)
 		for (String l : loggerLevels.keySet())
 			Logger.getLogger(l).setLevel(loggerLevels.get(Level.class, l));
@@ -339,7 +339,7 @@ public class RestMicroservice extends Microservice {
 		ConfigFile cf = getConfig();
 		ObjectMap mf = getManifest();
 		
-		int[] ports = cf.getObject(int[].class, "REST/port", mf.get(int[].class, "Rest-Port", new int[]{8000}));
+		int[] ports = cf.getObjectWithDefault("REST/port", mf.get(int[].class, "Rest-Port", new int[]{8000}), int[].class);
 
 		port = findOpenPort(ports);
 		if (port == 0) {
@@ -474,7 +474,7 @@ public class RestMicroservice extends Microservice {
 		ObjectMap mf = getManifest();
 		Map<String,Class<? extends Servlet>> rm = new LinkedHashMap<String,Class<? extends Servlet>>();
 
-		ObjectMap resourceMap = cf.getObject(ObjectMap.class, "REST/resourceMap");
+		ObjectMap resourceMap = cf.getObject("REST/resourceMap", ObjectMap.class);
 		String[] resources = cf.getStringArray("REST/resources", mf.getStringArray("Rest-Resources"));
 
 		if (resourceMap != null && ! resourceMap.isEmpty()) {

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-rest-test/juneau-rest-test.cfg
----------------------------------------------------------------------
diff --git a/juneau-rest-test/juneau-rest-test.cfg b/juneau-rest-test/juneau-rest-test.cfg
index e6dd8f3..ec9628a 100644
--- a/juneau-rest-test/juneau-rest-test.cfg
+++ b/juneau-rest-test/juneau-rest-test.cfg
@@ -26,7 +26,7 @@ resources = org.apache.juneau.rest.test.Root
 # Ports to try.
 # 0 means try a random port.
 # 3 0's means try 3 random ports.
-port = 10001, 0, 0, 0
+port = [10001, 0, 0, 0]
 
 # Authentication:  NONE, BASIC.
 authType = NONE
@@ -104,11 +104,11 @@ consoleLevel = WARNING
 
 [Test]
 int1 = 1
-int2 = 1,2,3
+int2 = [1,2,3]
 int3 = $C{Test/int1, -1}
 int4 = $C{Test/int3, -1}
 int5 = $C{XXX, -1}
 boolean1 = true
-boolean2 = true,true
+boolean2 = [true,true]
 path = $E{PATH}
 testManifestEntry = $MF{Test-Entry}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
index e8f84cc..d766290 100644
--- a/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
+++ b/juneau-rest-test/src/main/java/org/apache/juneau/rest/test/ConfigResource.java
@@ -33,6 +33,6 @@ public class ConfigResource extends Resource {
 
 	@RestMethod(name="GET", path="/{key}/{class}")
 	public Object test2(RestRequest req, @Path("key") String key, @Path("class") Class<?> c) throws Exception {
-		return req.getConfigFile().getObject(c, key);
+		return req.getConfigFile().getObject(key, c);
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
index 22ebaff..d52425d 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/ConfigTest.java
@@ -33,17 +33,16 @@ public class ConfigTest extends RestTestcase {
 
 		ConfigFile cf = c.doGet(URL).getResponse(ConfigFileImpl.class);
 
-		assertObjectEquals("{int1:'1',int2:'1,2,3',int3:'$C{Test/int1, -1}',int4:'$C{Test/int3, -1}',int5:'$C{XXX, -1}',boolean1:'true',boolean2:'true,true',path:'$E{PATH}',testManifestEntry:'$MF{Test-Entry}'}", cf.get("Test"));
+		assertObjectEquals("{int1:'1',int2:'[1,2,3]',int3:'$C{Test/int1, -1}',int4:'$C{Test/int3, -1}',int5:'$C{XXX, -1}',boolean1:'true',boolean2:'[true,true]',path:'$E{PATH}',testManifestEntry:'$MF{Test-Entry}'}", cf.get("Test"));
 
 		assertEquals("'1'", c.doGet(URL + "/Test%2Fint1/" + getName(String.class)).getResponseAsString());
-		assertEquals("['1']", c.doGet(URL + "/Test%2Fint1/" + getName(String[].class)).getResponseAsString());
-		assertEquals("'1,2,3'", c.doGet(URL + "/Test%2Fint2/" + getName(String.class)).getResponseAsString());
+		assertEquals("'[1,2,3]'", c.doGet(URL + "/Test%2Fint2/" + getName(String.class)).getResponseAsString());
 		assertEquals("['1','2','3']", c.doGet(URL + "/Test%2Fint2/" + getName(String[].class)).getResponseAsString());
 		assertEquals("[1,2,3]", c.doGet(URL + "/Test%2Fint2/" + getName(int[].class)).getResponseAsString());
 		assertEquals("[1,2,3]", c.doGet(URL + "/Test%2Fint2/" + getName(Integer[].class)).getResponseAsString());
-		assertEquals("[1]", c.doGet(URL + "/Test%2Fint3/" + getName(int[].class)).getResponseAsString());
-		assertEquals("[1]", c.doGet(URL + "/Test%2Fint4/" + getName(int[].class)).getResponseAsString());
-		assertEquals("[-1]", c.doGet(URL + "/Test%2Fint5/" + getName(int[].class)).getResponseAsString());
+		assertEquals("1", c.doGet(URL + "/Test%2Fint3/" + getName(Integer.class)).getResponseAsString());
+		assertEquals("1", c.doGet(URL + "/Test%2Fint4/" + getName(Integer.class)).getResponseAsString());
+		assertEquals("-1", c.doGet(URL + "/Test%2Fint5/" + getName(Integer.class)).getResponseAsString());
 		assertEquals("true", c.doGet(URL + "/Test%2Fboolean1/" + getName(Boolean.class)).getResponseAsString());
 		assertEquals("[true,true]", c.doGet(URL + "/Test%2Fboolean2/" + getName(Boolean[].class)).getResponseAsString());
 		assertTrue(c.doGet(URL + "/Test%2Fpath/" + getName(String.class)).getResponseAsString().length() > 10);

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java
----------------------------------------------------------------------
diff --git a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java
index d8a0b1b..eb2f247 100644
--- a/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java
+++ b/juneau-rest-test/src/test/java/org/apache/juneau/rest/test/_TestSuite.java
@@ -38,6 +38,7 @@ import org.junit.runners.Suite.*;
 	GroupsTest.class,
 	GzipTest.class,
 	HeadersTest.class,
+	HtmlPropertiesTest.class,
 	InheritanceTest.class,
 	InterfaceProxyTest.class,
 	JacocoDummyTest.class,
@@ -54,6 +55,7 @@ import org.junit.runners.Suite.*;
 	ParsersTest.class,
 	PathsTest.class,
 	PathTest.class,
+	PathVariableTest.class,
 	PropertiesTest.class,
 	QueryTest.class,
 	RestClientTest.class,



[2/2] incubator-juneau git commit: Add ConfigFile.getSectionAsInterface() method.

Posted by ja...@apache.org.
Add ConfigFile.getSectionAsInterface() method.

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

Branch: refs/heads/master
Commit: ca59d8a4e7461857f8bf498029d2d49a31a39a43
Parents: c68cc34
Author: JamesBognar <ja...@apache.org>
Authored: Mon May 15 13:43:37 2017 -0400
Committer: JamesBognar <ja...@apache.org>
Committed: Mon May 15 13:43:37 2017 -0400

----------------------------------------------------------------------
 .../juneau/ini/ConfigFileBuilderTest.java       |  10 +-
 .../juneau/ini/ConfigFileInterfaceTest.java     | 601 +++++++++++++++++++
 .../org/apache/juneau/ini/ConfigFileTest.java   | 190 +++---
 .../org/apache/juneau/test/pojos/ABean.java     |  24 +
 .../org/apache/juneau/test/pojos/Constants.java |  25 +
 .../juneau/test/pojos/ImplicitSwappedPojo.java  |  35 ++
 .../apache/juneau/test/pojos/SwappedPojo.java   |  20 +
 .../juneau/test/pojos/SwappedPojoSwap.java      |  35 ++
 .../org/apache/juneau/test/pojos/TestEnum.java  |  17 +
 .../org/apache/juneau/test/pojos/TypedBean.java |  17 +
 .../apache/juneau/test/pojos/TypedBeanImpl.java |  25 +
 .../java/org/apache/juneau/BeanSession.java     |  92 ++-
 .../java/org/apache/juneau/ini/ConfigFile.java  | 386 +++++++++---
 .../apache/juneau/ini/ConfigFileBuilder.java    |   2 +-
 .../org/apache/juneau/ini/ConfigFileImpl.java   |  66 +-
 .../apache/juneau/ini/ConfigFileWrapped.java    |  27 +-
 .../java/org/apache/juneau/json/JsonParser.java |   2 +-
 juneau-core/src/main/javadoc/overview.html      | 107 +++-
 juneau-examples-rest/examples.cfg               |   4 +-
 juneau-microservice-template/microservice.cfg   |   7 +-
 .../juneau/microservice/RestMicroservice.java   |   6 +-
 juneau-rest-test/juneau-rest-test.cfg           |   6 +-
 .../apache/juneau/rest/test/ConfigResource.java |   2 +-
 .../org/apache/juneau/rest/test/ConfigTest.java |  11 +-
 .../org/apache/juneau/rest/test/_TestSuite.java |   2 +
 25 files changed, 1451 insertions(+), 268 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
index 7a3501e..3d34086 100755
--- a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileBuilderTest.java
@@ -139,13 +139,13 @@ public class ConfigFileBuilderTest {
 		try { cf.addListener(new ConfigFileListener(){}); fail(); } catch (UnsupportedOperationException e) {}
 
 		// All these should succeed.
-		cf.getObject(String.class, "A");
-		cf.getObject(String.class, "A", "a");
+		cf.getObject("A", String.class);
+		cf.getObject("A", "a", String.class);
 		cf.getString("A");
 		cf.getString("A","a");
-		cf.getObject(String.class, "A");
-		cf.getObject(String.class, "A", "a");
-		cf.getObject(String[].class, "A");
+		cf.getObject("A", String.class);
+		cf.getObject("A", "a", String.class);
+		cf.getObject("A", String[].class);
 		cf.getStringArray("A");
 		cf.getStringArray("A", null);
 		cf.getInt("A");

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
new file mode 100644
index 0000000..4d34568
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileInterfaceTest.java
@@ -0,0 +1,601 @@
+// ***************************************************************************************************************************
+// * 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.ini;
+
+import static org.junit.Assert.*;
+import static org.apache.juneau.TestUtils.*;
+
+import java.util.*;
+
+import org.apache.juneau.test.pojos.*;
+import org.apache.juneau.utils.*;
+import org.junit.*;
+
+@SuppressWarnings("javadoc")
+public class ConfigFileInterfaceTest {
+
+	ConfigFile cf;
+	ConfigInterface proxy;
+	
+	public ConfigFileInterfaceTest() throws Exception {
+		ConfigFileBuilder configFileBuilder = new ConfigFileBuilder();
+		cf = configFileBuilder.build();
+		proxy = cf.getSectionAsInterface("A", ConfigInterface.class);
+	}
+	
+	
+	//====================================================================================================
+	// getSectionAsInterface(String,Class)
+	//====================================================================================================
+	
+	@Test
+	public void testString() throws Exception {
+		proxy.setString("foo");
+		assertEquals("foo", proxy.getString());
+		assertEquals("foo", cf.get("A", "string"));
+	}
+	
+	@Test
+	public void testInt() throws Exception {
+		proxy.setInt(1);
+		assertEquals(1, proxy.getInt());
+		assertEquals("1", cf.get("A", "int"));
+	}
+	
+	@Test
+	public void testInteger() throws Exception {
+		proxy.setInteger(2);
+		assertEquals(2, proxy.getInteger().intValue());
+		assertEquals("2", cf.get("A", "integer"));
+		assertType(Integer.class, proxy.getInteger());
+	}
+
+	@Test
+	public void testBoolean() throws Exception {
+		proxy.setBoolean(true);
+		assertEquals(true, proxy.isBoolean());
+		assertEquals("true", cf.get("A", "boolean"));
+	}
+	
+	@Test
+	public void testBooleanObject() throws Exception {
+		proxy.setBooleanObject(true);
+		assertEquals(true, proxy.getBooleanObject().booleanValue());
+		assertEquals("true", cf.get("A", "booleanObject"));
+		assertType(Boolean.class, proxy.getBooleanObject());
+	}
+
+	@Test
+	public void testFloat() throws Exception {
+		proxy.setFloat(1f);
+		assertTrue(1f == proxy.getFloat());
+		assertEquals("1.0", cf.get("A", "float"));
+	}
+	
+	@Test
+	public void testFloatObject() throws Exception {
+		proxy.setFloatObject(1f);
+		assertTrue(1f == proxy.getFloatObject().floatValue());
+		assertEquals("1.0", cf.get("A", "floatObject"));
+		assertType(Float.class, proxy.getFloatObject());
+	}
+
+	@Test
+	public void testInt3dArray() throws Exception {
+		proxy.setInt3dArray(new int[][][]{{{1,2},null},null});
+		assertEquals("[[[1,2],null],null]", cf.get("A", "int3dArray"));
+		assertObjectEquals("[[[1,2],null],null]", proxy.getInt3dArray());
+		assertType(int[][][].class, proxy.getInt3dArray());
+	}
+
+	@Test
+	public void testInteger3dArray() throws Exception {
+		proxy.setInteger3dArray(new Integer[][][]{{{1,null},null},null});
+		assertObjectEquals("[[[1,null],null],null]", proxy.getInteger3dArray());
+		assertEquals("[[[1,null],null],null]", cf.get("A", "integer3dArray"));
+		assertType(Integer.class, proxy.getInteger3dArray()[0][0][0]);
+	}
+
+	@Test
+	public void testString3dArray() throws Exception {
+		proxy.setString3dArray(new String[][][]{{{"foo",null},null},null});
+		assertObjectEquals("[[['foo',null],null],null]", proxy.getString3dArray());
+		assertEquals("[[['foo',null],null],null]", cf.get("A", "string3dArray"));
+	}
+
+	@Test
+	public void testIntegerList() throws Exception {
+		proxy.setIntegerList(new AList<Integer>().append(1).append(null));
+		assertObjectEquals("[1,null]", proxy.getIntegerList());
+		assertEquals("[1,null]", cf.get("A", "integerList"));
+		assertType(Integer.class, proxy.getIntegerList().get(0));
+	}
+	
+	@Test
+	public void testInteger3dList() throws Exception {
+		proxy.setInteger3dList(
+			new AList<List<List<Integer>>>()
+			.append(
+				new AList<List<Integer>>()
+				.append(new AList<Integer>().append(1).append(null))
+				.append(null)
+			)
+			.append(null)
+		);
+		assertObjectEquals("[[[1,null],null],null]", proxy.getInteger3dList());
+		assertEquals("[[[1,null],null],null]", cf.get("A", "integer3dList"));
+		assertType(Integer.class, proxy.getInteger3dList().get(0).get(0).get(0));
+	}
+
+	@Test
+	public void testInteger1d3dList() throws Exception {
+		proxy.setInteger1d3dList(new AList<Integer[][][]>().append(new Integer[][][]{{{1,null},null},null}).append(null));
+		assertObjectEquals("[[[[1,null],null],null],null]", proxy.getInteger1d3dList());
+		assertEquals("[[[[1,null],null],null],null]", cf.get("A", "integer1d3dList"));
+		assertType(Integer.class, proxy.getInteger1d3dList().get(0)[0][0][0]);
+	}
+	
+	@Test
+	public void testInt1d3dList() throws Exception {
+		proxy.setInt1d3dList(new AList<int[][][]>().append(new int[][][]{{{1,2},null},null}).append(null));
+		assertObjectEquals("[[[[1,2],null],null],null]", proxy.getInt1d3dList());
+		assertEquals("[[[[1,2],null],null],null]", cf.get("A", "int1d3dList"));
+		assertType(int[][][].class, proxy.getInt1d3dList().get(0));
+	}
+	
+	@Test
+	public void testStringList() throws Exception {
+		proxy.setStringList(Arrays.asList("foo","bar",null));
+		assertObjectEquals("['foo','bar',null]", proxy.getStringList());
+		assertEquals("['foo','bar',null]", cf.get("A", "stringList"));
+	}
+
+	// Beans
+	
+	@Test
+	public void testBean() throws Exception {
+		proxy.setBean(new ABean().init());
+		assertObjectEquals("{a:1,b:'foo'}", proxy.getBean());
+		assertEquals("{a:1,b:'foo'}", cf.get("A", "bean"));
+		assertType(ABean.class, proxy.getBean());
+	}
+
+	@Test
+	public void testBean3dArray() throws Exception {
+		proxy.setBean3dArray(new ABean[][][]{{{new ABean().init(),null},null},null});
+		assertObjectEquals("[[[{a:1,b:'foo'},null],null],null]", proxy.getBean3dArray());
+		assertEquals("[[[{a:1,b:'foo'},null],null],null]", cf.get("A", "bean3dArray"));
+		assertType(ABean.class, proxy.getBean3dArray()[0][0][0]);
+	}
+
+	@Test
+	public void testBeanList() throws Exception {
+		proxy.setBeanList(Arrays.asList(new ABean().init()));
+		assertObjectEquals("[{a:1,b:'foo'}]", proxy.getBeanList());
+		assertEquals("[{a:1,b:'foo'}]", cf.get("A", "beanList"));
+		assertType(ABean.class, proxy.getBeanList().get(0));
+	}
+
+	@Test
+	public void testBean1d3dList() throws Exception {
+		proxy.setBean1d3dList(new AList<ABean[][][]>().append(new ABean[][][]{{{new ABean().init(),null},null},null}).append(null));
+		assertObjectEquals("[[[[{a:1,b:'foo'},null],null],null],null]", proxy.getBean1d3dList());
+		assertEquals("[[[[{a:1,b:'foo'},null],null],null],null]", cf.get("A", "bean1d3dList"));
+		assertType(ABean.class, proxy.getBean1d3dList().get(0)[0][0][0]);
+	}
+
+	@Test
+	public void testBeanMap() throws Exception {
+		proxy.setBeanMap(new AMap<String,ABean>().append("foo",new ABean().init()));
+		assertObjectEquals("{foo:{a:1,b:'foo'}}", proxy.getBeanMap());
+		assertEquals("{foo:{a:1,b:'foo'}}", cf.get("A", "beanMap"));
+		assertType(ABean.class, proxy.getBeanMap().get("foo"));
+	}
+
+	@Test
+	public void testBeanListMap() throws Exception {
+		proxy.setBeanListMap(new AMap<String,List<ABean>>().append("foo",Arrays.asList(new ABean().init())));
+		assertObjectEquals("{foo:[{a:1,b:'foo'}]}", proxy.getBeanListMap());
+		assertEquals("{foo:[{a:1,b:'foo'}]}", cf.get("A", "beanListMap"));
+		assertType(ABean.class, proxy.getBeanListMap().get("foo").get(0));
+	}
+
+	@Test
+	public void testBean1d3dListMap() throws Exception {
+		proxy.setBean1d3dListMap(new AMap<String,List<ABean[][][]>>().append("foo",new AList<ABean[][][]>().append(new ABean[][][]{{{new ABean().init(),null},null},null}).append(null)));
+		assertObjectEquals("{foo:[[[[{a:1,b:'foo'},null],null],null],null]}", proxy.getBean1d3dListMap());
+		assertEquals("{foo:[[[[{a:1,b:'foo'},null],null],null],null]}", cf.get("A", "bean1d3dListMap"));
+		assertType(ABean.class, proxy.getBean1d3dListMap().get("foo").get(0)[0][0][0]);
+	}
+
+	@Test
+	public void testBeanListMapIntegerKeys() throws Exception {
+		proxy.setBeanListMapIntegerKeys(new AMap<Integer,List<ABean>>().append(1,Arrays.asList(new ABean().init())));
+		assertObjectEquals("{'1':[{a:1,b:'foo'}]}", proxy.getBeanListMapIntegerKeys());
+		assertEquals("{'1':[{a:1,b:'foo'}]}", cf.get("A", "beanListMapIntegerKeys"));
+		assertType(ABean.class, proxy.getBeanListMapIntegerKeys().get(1).get(0));
+	}
+
+	// Typed beans
+	
+	@Test
+	public void testTypedBean() throws Exception {
+		proxy.setTypedBean(new TypedBeanImpl().init());
+		assertObjectEquals("{_type:'TypedBeanImpl',a:1,b:'foo'}", proxy.getTypedBean());
+		assertEquals("{_type:'TypedBeanImpl',a:1,b:'foo'}", cf.get("A", "typedBean"));
+		assertType(TypedBeanImpl.class, proxy.getTypedBean());
+	}
+
+	@Test
+	public void testTypedBean3dArray() throws Exception {
+		proxy.setTypedBean3dArray(new TypedBean[][][]{{{new TypedBeanImpl().init(),null},null},null});
+		assertObjectEquals("[[[{_type:'TypedBeanImpl',a:1,b:'foo'},null],null],null]", proxy.getTypedBean3dArray());
+		assertEquals("[[[{_type:'TypedBeanImpl',a:1,b:'foo'},null],null],null]", cf.get("A", "typedBean3dArray"));
+		assertType(TypedBeanImpl.class, proxy.getTypedBean3dArray()[0][0][0]);
+	}
+
+	@Test
+	public void testTypedBeanList() throws Exception {
+		proxy.setTypedBeanList(Arrays.asList((TypedBean)new TypedBeanImpl().init()));
+		assertObjectEquals("[{_type:'TypedBeanImpl',a:1,b:'foo'}]", proxy.getTypedBeanList());
+		assertEquals("[{_type:'TypedBeanImpl',a:1,b:'foo'}]", cf.get("A", "typedBeanList"));
+		assertType(TypedBeanImpl.class, proxy.getTypedBeanList().get(0));
+	}
+
+	@Test
+	public void testTypedBean1d3dList() throws Exception {
+		proxy.setTypedBean1d3dList(new AList<TypedBean[][][]>().append(new TypedBean[][][]{{{new TypedBeanImpl().init(),null},null},null}).append(null));
+		assertObjectEquals("[[[[{_type:'TypedBeanImpl',a:1,b:'foo'},null],null],null],null]", proxy.getTypedBean1d3dList());
+		assertEquals("[[[[{_type:'TypedBeanImpl',a:1,b:'foo'},null],null],null],null]", cf.get("A", "typedBean1d3dList"));
+		assertType(TypedBeanImpl.class, proxy.getTypedBean1d3dList().get(0)[0][0][0]);
+	}
+
+	@Test
+	public void testTypedBeanMap() throws Exception {
+		proxy.setTypedBeanMap(new AMap<String,TypedBean>().append("foo",new TypedBeanImpl().init()));
+		assertObjectEquals("{foo:{_type:'TypedBeanImpl',a:1,b:'foo'}}", proxy.getTypedBeanMap());
+		assertEquals("{foo:{_type:'TypedBeanImpl',a:1,b:'foo'}}", cf.get("A", "typedBeanMap"));
+		assertType(TypedBeanImpl.class, proxy.getTypedBeanMap().get("foo"));
+	}
+
+	@Test
+	public void testTypedBeanListMap() throws Exception {
+		proxy.setTypedBeanListMap(new AMap<String,List<TypedBean>>().append("foo",Arrays.asList((TypedBean)new TypedBeanImpl().init())));
+		assertObjectEquals("{foo:[{_type:'TypedBeanImpl',a:1,b:'foo'}]}", proxy.getTypedBeanListMap());
+		assertEquals("{foo:[{_type:'TypedBeanImpl',a:1,b:'foo'}]}", cf.get("A", "typedBeanListMap"));
+		assertType(TypedBeanImpl.class, proxy.getTypedBeanListMap().get("foo").get(0));
+	}
+
+	@Test
+	public void testTypedBean1d3dListMap() throws Exception {
+		proxy.setTypedBean1d3dListMap(new AMap<String,List<TypedBean[][][]>>().append("foo",new AList<TypedBean[][][]>().append(new TypedBean[][][]{{{new TypedBeanImpl().init(),null},null},null}).append(null)));
+		assertObjectEquals("{foo:[[[[{_type:'TypedBeanImpl',a:1,b:'foo'},null],null],null],null]}", proxy.getTypedBean1d3dListMap());
+		assertEquals("{foo:[[[[{_type:'TypedBeanImpl',a:1,b:'foo'},null],null],null],null]}", cf.get("A", "typedBean1d3dListMap"));
+		assertType(TypedBeanImpl.class, proxy.getTypedBean1d3dListMap().get("foo").get(0)[0][0][0]);
+	}
+
+	@Test
+	public void testTypedBeanListMapIntegerKeys() throws Exception {
+		proxy.setTypedBeanListMapIntegerKeys(new AMap<Integer,List<TypedBean>>().append(1,Arrays.asList((TypedBean)new TypedBeanImpl().init())));
+		assertObjectEquals("{'1':[{_type:'TypedBeanImpl',a:1,b:'foo'}]}", proxy.getTypedBeanListMapIntegerKeys());
+		assertEquals("{'1':[{_type:'TypedBeanImpl',a:1,b:'foo'}]}", cf.get("A", "typedBeanListMapIntegerKeys"));
+		assertType(TypedBeanImpl.class, proxy.getTypedBeanListMapIntegerKeys().get(1).get(0));
+	}
+
+	// Swapped POJOs
+	
+	@Test
+	public void testSwappedPojo() throws Exception {
+		proxy.setSwappedPojo(new SwappedPojo());
+		assertObjectEquals("'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/'", proxy.getSwappedPojo());
+		assertEquals("swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/", cf.get("A", "swappedPojo"));
+		assertType(SwappedPojo.class, proxy.getSwappedPojo());
+	}
+
+	@Test
+	public void testSwappedPojo3dArray() throws Exception {
+		proxy.setSwappedPojo3dArray(new SwappedPojo[][][]{{{new SwappedPojo(),null},null},null});
+		assertObjectEquals("[[['swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/',null],null],null]", proxy.getSwappedPojo3dArray());
+		assertEquals("[[['swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/',null],null],null]", cf.get("A", "swappedPojo3dArray"));
+		assertType(SwappedPojo.class, proxy.getSwappedPojo3dArray()[0][0][0]);
+	}
+
+	@Test
+	public void testSwappedPojoMap() throws Exception {
+		proxy.setSwappedPojoMap(new AMap<SwappedPojo,SwappedPojo>().append(new SwappedPojo(), new SwappedPojo()));
+		assertObjectEquals("{'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/':'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/'}", proxy.getSwappedPojoMap());
+		assertEquals("{'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/':'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/'}", cf.get("A", "swappedPojoMap"));
+		assertType(SwappedPojo.class, proxy.getSwappedPojoMap().keySet().iterator().next());
+		assertType(SwappedPojo.class, proxy.getSwappedPojoMap().values().iterator().next());
+	}
+
+	@Test
+	public void testSwappedPojo3dMap() throws Exception {
+		proxy.setSwappedPojo3dMap(new AMap<SwappedPojo,SwappedPojo[][][]>().append(new SwappedPojo(), new SwappedPojo[][][]{{{new SwappedPojo(),null},null},null}));
+		assertObjectEquals("{'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/':[[['swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/',null],null],null]}", proxy.getSwappedPojo3dMap());
+		assertEquals("{'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/':[[['swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/',null],null],null]}", cf.get("A", "swappedPojo3dMap"));
+		assertType(SwappedPojo.class, proxy.getSwappedPojo3dMap().keySet().iterator().next());
+		assertType(SwappedPojo.class, proxy.getSwappedPojo3dMap().values().iterator().next()[0][0][0]);
+	}
+
+	// Implicit swapped POJOs
+	
+	@Test
+	public void testImplicitSwappedPojo() throws Exception {
+		proxy.setImplicitSwappedPojo(new ImplicitSwappedPojo());
+		assertObjectEquals("'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/'", proxy.getImplicitSwappedPojo());
+		assertEquals("swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/", cf.get("A", "implicitSwappedPojo"));
+		assertType(ImplicitSwappedPojo.class, proxy.getImplicitSwappedPojo());
+	}
+
+	@Test
+	public void testImplicitSwappedPojo3dArray() throws Exception {
+		proxy.setImplicitSwappedPojo3dArray(new ImplicitSwappedPojo[][][]{{{new ImplicitSwappedPojo(),null},null},null});
+		assertObjectEquals("[[['swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/',null],null],null]", proxy.getImplicitSwappedPojo3dArray());
+		assertEquals("[[['swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/',null],null],null]", cf.get("A", "implicitSwappedPojo3dArray"));
+		assertType(ImplicitSwappedPojo.class, proxy.getImplicitSwappedPojo3dArray()[0][0][0]);
+	}
+
+	@Test
+	public void testImplicitSwappedPojoMap() throws Exception {
+		proxy.setImplicitSwappedPojoMap(new AMap<ImplicitSwappedPojo,ImplicitSwappedPojo>().append(new ImplicitSwappedPojo(), new ImplicitSwappedPojo()));
+		assertObjectEquals("{'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/':'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/'}", proxy.getImplicitSwappedPojoMap());
+		assertEquals("{'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/':'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/'}", cf.get("A", "implicitSwappedPojoMap"));
+		assertType(ImplicitSwappedPojo.class, proxy.getImplicitSwappedPojoMap().keySet().iterator().next());
+		assertType(ImplicitSwappedPojo.class, proxy.getImplicitSwappedPojoMap().values().iterator().next());
+	}
+
+	@Test
+	public void testImplicitSwappedPojo3dMap() throws Exception {
+		proxy.setImplicitSwappedPojo3dMap(new AMap<ImplicitSwappedPojo,ImplicitSwappedPojo[][][]>().append(new ImplicitSwappedPojo(), new ImplicitSwappedPojo[][][]{{{new ImplicitSwappedPojo(),null},null},null}));
+		assertObjectEquals("{'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/':[[['swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/',null],null],null]}", proxy.getImplicitSwappedPojo3dMap());
+		assertEquals("{'swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/':[[['swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/',null],null],null]}", cf.get("A", "implicitSwappedPojo3dMap"));
+		assertType(ImplicitSwappedPojo.class, proxy.getImplicitSwappedPojo3dMap().keySet().iterator().next());
+		assertType(ImplicitSwappedPojo.class, proxy.getImplicitSwappedPojo3dMap().values().iterator().next()[0][0][0]);
+	}
+
+	// Enums
+	
+	@Test
+	public void testEnum() throws Exception {
+		proxy.setEnum(TestEnum.TWO);
+		assertObjectEquals("'TWO'", proxy.getEnum());
+		assertEquals("TWO", cf.get("A", "enum"));
+		assertType(TestEnum.class, proxy.getEnum());
+	}
+
+	@Test
+	public void testEnum3d() throws Exception {
+		proxy.setEnum3d(new TestEnum[][][]{{{TestEnum.TWO,null},null},null});
+		assertObjectEquals("[[['TWO',null],null],null]", proxy.getEnum3d());
+		assertEquals("[[['TWO',null],null],null]", cf.get("A", "enum3d"));
+		assertType(TestEnum.class, proxy.getEnum3d()[0][0][0]);
+	}
+
+	@Test
+	public void testEnumList() throws Exception {
+		proxy.setEnumList(new AList<TestEnum>().append(TestEnum.TWO).append(null));
+		assertObjectEquals("['TWO',null]", proxy.getEnumList());
+		assertEquals("['TWO',null]", cf.get("A", "enumList"));
+		assertType(TestEnum.class, proxy.getEnumList().get(0));
+	}
+
+	@Test
+	public void testEnum3dList() throws Exception {
+		proxy.setEnum3dList(
+			new AList<List<List<TestEnum>>>()
+			.append(
+				new AList<List<TestEnum>>()
+				.append(
+					new AList<TestEnum>().append(TestEnum.TWO).append(null)
+				)
+				.append(null)
+			.append(null)
+			)
+		);
+		assertObjectEquals("[[['TWO',null],null,null]]", proxy.getEnum3dList());
+		assertEquals("[[['TWO',null],null,null]]", cf.get("A", "enum3dList"));
+		assertType(TestEnum.class, proxy.getEnum3dList().get(0).get(0).get(0));
+	}
+
+	@Test
+	public void testEnum1d3dList() throws Exception {
+		proxy.setEnum1d3dList(new AList<TestEnum[][][]>().append(new TestEnum[][][]{{{TestEnum.TWO,null},null},null}).append(null));
+		assertObjectEquals("[[[['TWO',null],null],null],null]", proxy.getEnum1d3dList());
+		assertEquals("[[[['TWO',null],null],null],null]", cf.get("A", "enum1d3dList"));
+		assertType(TestEnum.class, proxy.getEnum1d3dList().get(0)[0][0][0]);
+	}
+
+	@Test
+	public void testEnumMap() throws Exception {
+		proxy.setEnumMap(new AMap<TestEnum,TestEnum>().append(TestEnum.ONE,TestEnum.TWO));
+		assertObjectEquals("{ONE:'TWO'}", proxy.getEnumMap());
+		assertEquals("{ONE:'TWO'}", cf.get("A", "enumMap"));
+		assertType(TestEnum.class, proxy.getEnumMap().keySet().iterator().next());
+		assertType(TestEnum.class, proxy.getEnumMap().values().iterator().next());
+	}
+
+	@Test
+	public void testEnum3dArrayMap() throws Exception {
+		proxy.setEnum3dArrayMap(new AMap<TestEnum,TestEnum[][][]>().append(TestEnum.ONE, new TestEnum[][][]{{{TestEnum.TWO,null},null},null}));
+		assertObjectEquals("{ONE:[[['TWO',null],null],null]}", proxy.getEnum3dArrayMap());
+		assertEquals("{ONE:[[['TWO',null],null],null]}", cf.get("A", "enum3dArrayMap"));
+		assertType(TestEnum.class, proxy.getEnum3dArrayMap().keySet().iterator().next());
+		assertType(TestEnum.class, proxy.getEnum3dArrayMap().values().iterator().next()[0][0][0]);
+	}
+
+	@Test
+	public void testEnum1d3dListMap() throws Exception {
+		proxy.setEnum1d3dListMap(new AMap<TestEnum,List<TestEnum[][][]>>().append(TestEnum.ONE, new AList<TestEnum[][][]>().append(new TestEnum[][][]{{{TestEnum.TWO,null},null},null}).append(null)));
+		assertObjectEquals("{ONE:[[[['TWO',null],null],null],null]}", proxy.getEnum1d3dListMap());
+		assertEquals("{ONE:[[[['TWO',null],null],null],null]}", cf.get("A", "enum1d3dListMap"));
+		assertType(TestEnum.class, proxy.getEnum1d3dListMap().keySet().iterator().next());
+		assertType(TestEnum.class, proxy.getEnum1d3dListMap().values().iterator().next().get(0)[0][0][0]);
+	}
+
+	public static interface ConfigInterface {
+
+		// Various primitives
+
+		public String getString();
+		public void setString(String x);
+		
+		public int getInt();
+		public void setInt(int x);
+		
+		public Integer getInteger();
+		public void setInteger(Integer x);
+
+		public boolean isBoolean();
+		public void setBoolean(boolean x);
+		
+		public Boolean getBooleanObject();
+		public void setBooleanObject(Boolean x);
+
+		public float getFloat();
+		public void setFloat(float x);
+		
+		public Float getFloatObject();
+		public void setFloatObject(Float x);
+
+		public int[][][] getInt3dArray();
+		public void setInt3dArray(int[][][] x);
+
+		public Integer[][][] getInteger3dArray();
+		public void setInteger3dArray(Integer[][][] x);
+
+		public String[][][] getString3dArray();
+		public void setString3dArray(String[][][] x);
+
+		public List<Integer> getIntegerList();
+		public void setIntegerList(List<Integer> x);
+		
+		public List<List<List<Integer>>> getInteger3dList();
+		public void setInteger3dList(List<List<List<Integer>>> x);
+
+		public List<Integer[][][]> getInteger1d3dList();
+		public void setInteger1d3dList(List<Integer[][][]> x);
+		
+		public List<int[][][]> getInt1d3dList();
+		public void setInt1d3dList(List<int[][][]> x);
+		
+		public List<String> getStringList();
+		public void setStringList(List<String> x);		
+
+		// Beans
+		
+		public ABean getBean();
+		public void setBean(ABean x);		
+
+		public ABean[][][] getBean3dArray();
+		public void setBean3dArray(ABean[][][] x);		
+
+		public List<ABean> getBeanList();
+		public void setBeanList(List<ABean> x);		
+
+		public List<ABean[][][]> getBean1d3dList();
+		public void setBean1d3dList(List<ABean[][][]> x);		
+
+		public Map<String,ABean> getBeanMap();
+		public void setBeanMap(Map<String,ABean> x);		
+
+		public Map<String,List<ABean>> getBeanListMap();
+		public void setBeanListMap(Map<String,List<ABean>> x);		
+
+		public Map<String,List<ABean[][][]>> getBean1d3dListMap();
+		public void setBean1d3dListMap(Map<String,List<ABean[][][]>> x);		
+
+		public Map<Integer,List<ABean>> getBeanListMapIntegerKeys();
+		public void setBeanListMapIntegerKeys(Map<Integer,List<ABean>> x);		
+
+		// Typed beans
+		
+		public TypedBean getTypedBean();
+		public void setTypedBean(TypedBean x);		
+
+		public TypedBean[][][] getTypedBean3dArray();
+		public void setTypedBean3dArray(TypedBean[][][] x);		
+
+		public List<TypedBean> getTypedBeanList();
+		public void setTypedBeanList(List<TypedBean> x);		
+
+		public List<TypedBean[][][]> getTypedBean1d3dList();
+		public void setTypedBean1d3dList(List<TypedBean[][][]> x);		
+
+		public Map<String,TypedBean> getTypedBeanMap();
+		public void setTypedBeanMap(Map<String,TypedBean> x);		
+
+		public Map<String,List<TypedBean>> getTypedBeanListMap();
+		public void setTypedBeanListMap(Map<String,List<TypedBean>> x);		
+
+		public Map<String,List<TypedBean[][][]>> getTypedBean1d3dListMap();
+		public void setTypedBean1d3dListMap(Map<String,List<TypedBean[][][]>> x);		
+
+		public Map<Integer,List<TypedBean>> getTypedBeanListMapIntegerKeys();
+		public void setTypedBeanListMapIntegerKeys(Map<Integer,List<TypedBean>> x);		
+
+		// Swapped POJOs
+		
+		public SwappedPojo getSwappedPojo();
+		public void setSwappedPojo(SwappedPojo x);		
+
+		public SwappedPojo[][][] getSwappedPojo3dArray();
+		public void setSwappedPojo3dArray(SwappedPojo[][][] x);		
+
+		public Map<SwappedPojo,SwappedPojo> getSwappedPojoMap();
+		public void setSwappedPojoMap(Map<SwappedPojo,SwappedPojo> x);		
+
+		public Map<SwappedPojo,SwappedPojo[][][]> getSwappedPojo3dMap();
+		public void setSwappedPojo3dMap(Map<SwappedPojo,SwappedPojo[][][]> x);		
+
+		// Implicit swapped POJOs
+		
+		public ImplicitSwappedPojo getImplicitSwappedPojo();
+		public void setImplicitSwappedPojo(ImplicitSwappedPojo x);		
+
+		public ImplicitSwappedPojo[][][] getImplicitSwappedPojo3dArray();
+		public void setImplicitSwappedPojo3dArray(ImplicitSwappedPojo[][][] x);		
+
+		public Map<ImplicitSwappedPojo,ImplicitSwappedPojo> getImplicitSwappedPojoMap();
+		public void setImplicitSwappedPojoMap(Map<ImplicitSwappedPojo,ImplicitSwappedPojo> x);		
+
+		public Map<ImplicitSwappedPojo,ImplicitSwappedPojo[][][]> getImplicitSwappedPojo3dMap();
+		public void setImplicitSwappedPojo3dMap(Map<ImplicitSwappedPojo,ImplicitSwappedPojo[][][]> x);		
+
+		// Enums
+		
+		public TestEnum getEnum();
+		public void setEnum(TestEnum x);		
+
+		public TestEnum[][][] getEnum3d();
+		public void setEnum3d(TestEnum[][][] x);		
+
+		public List<TestEnum> getEnumList();
+		public void setEnumList(List<TestEnum> x);		
+
+		public List<List<List<TestEnum>>> getEnum3dList();
+		public void setEnum3dList(List<List<List<TestEnum>>> x);		
+
+		public List<TestEnum[][][]> getEnum1d3dList();
+		public void setEnum1d3dList(List<TestEnum[][][]> x);		
+
+		public Map<TestEnum,TestEnum> getEnumMap();
+		public void setEnumMap(Map<TestEnum,TestEnum> x);		
+
+		public Map<TestEnum,TestEnum[][][]> getEnum3dArrayMap();
+		public void setEnum3dArrayMap(Map<TestEnum,TestEnum[][][]> x);		
+
+		public Map<TestEnum,List<TestEnum[][][]>> getEnum1d3dListMap();
+		public void setEnum1d3dListMap(Map<TestEnum,List<TestEnum[][][]>> x);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java
index 21c8287..c4b77b3 100755
--- a/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java
+++ b/juneau-core-test/src/test/java/org/apache/juneau/ini/ConfigFileTest.java
@@ -260,34 +260,34 @@ public class ConfigFileTest {
 	public void testExampleInConfigFile() throws Exception {
 
 		ConfigFile cf = configFileBuilder.build()
-			.addLines(null, "# Default section", "key1 = 1", "key2 = true", "key3 = 1,2,3", "key4 = 'http://foo'", "")
+			.addLines(null, "# Default section", "key1 = 1", "key2 = true", "key3 = [1,2,3]", "key4 = http://foo", "")
 			.addHeaderComments("section1", "# Section 1")
-			.addLines("section1", "key1 = 2", "key2 = false", "key3 = 4,5,6", "key4 = 'http://bar'");
+			.addLines("section1", "key1 = 2", "key2 = false", "key3 = [4,5,6]", "key4 = http://bar");
 		ConfigFile cfw = cf.getResolving(VarResolver.DEFAULT);
 
 		assertEquals(1, cf.getInt("key1"));
 		assertEquals(true, cf.getBoolean("key2"));
-		assertEquals(3, cf.getObject(int[].class, "key3")[2]);
-		assertEquals(6, cf.getObject(int[].class, "xkey3", new int[]{4,5,6})[2]);
-		assertEquals(6, cf.getObject(int[].class, "X/key3", new int[]{4,5,6})[2]);
-		assertEquals(new URL("http://foo").toString(), cf.getObject(URL.class, "key4").toString());
+		assertEquals(3, cf.getObject("key3", int[].class)[2]);
+		assertEquals(6, cf.getObjectWithDefault("xkey3", new int[]{4,5,6}, int[].class)[2]);
+		assertEquals(6, cf.getObjectWithDefault("X/key3", new int[]{4,5,6}, int[].class)[2]);
+		assertEquals(new URL("http://foo").toString(), cf.getObject("key4", URL.class).toString());
 
 		assertEquals(1, cfw.getInt("key1"));
 		assertEquals(true, cfw.getBoolean("key2"));
-		assertEquals(3, cfw.getObject(int[].class, "key3")[2]);
-		assertEquals(6, cfw.getObject(int[].class, "xkey3", new int[]{4,5,6})[2]);
-		assertEquals(6, cfw.getObject(int[].class, "X/key3", new int[]{4,5,6})[2]);
-		assertEquals(new URL("http://foo").toString(), cfw.getObject(URL.class, "key4").toString());
+		assertEquals(3, cfw.getObject("key3", int[].class)[2]);
+		assertEquals(6, cfw.getObjectWithDefault("xkey3", new int[]{4,5,6}, int[].class)[2]);
+		assertEquals(6, cfw.getObjectWithDefault("X/key3", new int[]{4,5,6}, int[].class)[2]);
+		assertEquals(new URL("http://foo").toString(), cfw.getObject("key4", URL.class).toString());
 
 		assertEquals(2, cf.getInt("section1/key1"));
 		assertEquals(false, cf.getBoolean("section1/key2"));
-		assertEquals(6, cf.getObject(int[].class, "section1/key3")[2]);
-		assertEquals(new URL("http://bar").toString(), cf.getObject(URL.class, "section1/key4").toString());
+		assertEquals(6, cf.getObject("section1/key3", int[].class)[2]);
+		assertEquals(new URL("http://bar").toString(), cf.getObject("section1/key4", URL.class).toString());
 
 		assertEquals(2, cfw.getInt("section1/key1"));
 		assertEquals(false, cfw.getBoolean("section1/key2"));
-		assertEquals(6, cfw.getObject(int[].class, "section1/key3")[2]);
-		assertEquals(new URL("http://bar").toString(), cfw.getObject(URL.class, "section1/key4").toString());
+		assertEquals(6, cfw.getObject("section1/key3", int[].class)[2]);
+		assertEquals(new URL("http://bar").toString(), cfw.getObject("section1/key4", URL.class).toString());
 
 		cf = configFileBuilder.build(getFreshFile())
 			.addLines(null, "# Default section")
@@ -308,23 +308,23 @@ public class ConfigFileTest {
 
 		assertEquals(1, cf.getInt("key1"));
 		assertEquals(true, cf.getBoolean("key2"));
-		assertEquals(3, cf.getObject(int[].class, "key3")[2]);
-		assertEquals(new URL("http://foo").toString(), cf.getObject(URL.class, "key4").toString());
+		assertEquals(3, cf.getObject("key3", int[].class)[2]);
+		assertEquals(new URL("http://foo").toString(), cf.getObject("key4", URL.class).toString());
 
 		assertEquals(1, cfw.getInt("key1"));
 		assertEquals(true, cfw.getBoolean("key2"));
-		assertEquals(3, cfw.getObject(int[].class, "key3")[2]);
-		assertEquals(new URL("http://foo").toString(), cfw.getObject(URL.class, "key4").toString());
+		assertEquals(3, cfw.getObject("key3", int[].class)[2]);
+		assertEquals(new URL("http://foo").toString(), cfw.getObject("key4", URL.class).toString());
 
 		assertEquals(2, cf.getInt("section1/key1"));
 		assertEquals(false, cf.getBoolean("section1/key2"));
-		assertEquals(6, cf.getObject(int[].class, "section1/key3")[2]);
-		assertEquals(new URL("http://bar").toString(), cf.getObject(URL.class, "section1/key4").toString());
+		assertEquals(6, cf.getObject("section1/key3", int[].class)[2]);
+		assertEquals(new URL("http://bar").toString(), cf.getObject("section1/key4", URL.class).toString());
 
 		assertEquals(2, cfw.getInt("section1/key1"));
 		assertEquals(false, cfw.getBoolean("section1/key2"));
-		assertEquals(6, cfw.getObject(int[].class, "section1/key3")[2]);
-		assertEquals(new URL("http://bar").toString(), cfw.getObject(URL.class, "section1/key4").toString());
+		assertEquals(6, cfw.getObject("section1/key3", int[].class)[2]);
+		assertEquals(new URL("http://bar").toString(), cfw.getObject("section1/key4", URL.class).toString());
 
 		cfw.put("key1", 2);
 		cfw.put("key2", false);
@@ -340,23 +340,23 @@ public class ConfigFileTest {
 
 		assertEquals(2, cf.getInt("key1"));
 		assertEquals(false, cf.getBoolean("key2"));
-		assertEquals(6, cf.getObject(int[].class, "key3")[2]);
-		assertEquals(new URL("http://bar").toString(), cf.getObject(URL.class, "key4").toString());
+		assertEquals(6, cf.getObject("key3", int[].class)[2]);
+		assertEquals(new URL("http://bar").toString(), cf.getObject("key4", URL.class).toString());
 
 		assertEquals(2, cfw.getInt("key1"));
 		assertEquals(false, cfw.getBoolean("key2"));
-		assertEquals(6, cfw.getObject(int[].class, "key3")[2]);
-		assertEquals(new URL("http://bar").toString(), cfw.getObject(URL.class, "key4").toString());
+		assertEquals(6, cfw.getObject("key3", int[].class)[2]);
+		assertEquals(new URL("http://bar").toString(), cfw.getObject("key4", URL.class).toString());
 
 		assertEquals(3, cf.getInt("section1/key1"));
 		assertEquals(true, cf.getBoolean("section1/key2"));
-		assertEquals(9, cf.getObject(int[].class, "section1/key3")[2]);
-		assertEquals(new URL("http://baz").toString(), cf.getObject(URL.class, "section1/key4").toString());
+		assertEquals(9, cf.getObject("section1/key3", int[].class)[2]);
+		assertEquals(new URL("http://baz").toString(), cf.getObject("section1/key4", URL.class).toString());
 
 		assertEquals(3, cfw.getInt("section1/key1"));
 		assertEquals(true, cfw.getBoolean("section1/key2"));
-		assertEquals(9, cfw.getObject(int[].class, "section1/key3")[2]);
-		assertEquals(new URL("http://baz").toString(), cfw.getObject(URL.class, "section1/key4").toString());
+		assertEquals(9, cfw.getObject("section1/key3", int[].class)[2]);
+		assertEquals(new URL("http://baz").toString(), cfw.getObject("section1/key4", URL.class).toString());
 	}
 
 	//====================================================================================================
@@ -365,17 +365,17 @@ public class ConfigFileTest {
 	@Test
 	public void testEnum() throws Exception {
 		ConfigFile cf = configFileBuilder.build(getFreshFile())
-			.addLines(null, "key1 = 'MINUTES'");
+			.addLines(null, "key1 = MINUTES");
 		ConfigFile cfw = cf.getResolving(VarResolver.DEFAULT);
 
-		assertEquals(TimeUnit.MINUTES, cf.getObject(TimeUnit.class, "key1"));
-		assertEquals(TimeUnit.MINUTES, cfw.getObject(TimeUnit.class, "key1"));
+		assertEquals(TimeUnit.MINUTES, cf.getObject("key1", TimeUnit.class));
+		assertEquals(TimeUnit.MINUTES, cfw.getObject("key1", TimeUnit.class));
 
 		cf.save();
 		cf.load();
 
-		assertEquals(TimeUnit.MINUTES, cf.getObject(TimeUnit.class, "key1"));
-		assertEquals(TimeUnit.MINUTES, cfw.getObject(TimeUnit.class, "key1"));
+		assertEquals(TimeUnit.MINUTES, cf.getObject("key1", TimeUnit.class));
+		assertEquals(TimeUnit.MINUTES, cfw.getObject("key1", TimeUnit.class));
 	}
 
 	//====================================================================================================
@@ -1143,41 +1143,41 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testGetObjectArray() throws Exception {
-		ConfigFile cf = configFileBuilder.build().addLines("A", "a1=1,2,3");
+		ConfigFile cf = configFileBuilder.build().addLines("A", "a1=[1,2,3]");
 		ConfigFile cfw = cf.getResolving();
-		assertObjectEquals("[1,2,3]", cf.getObject(Integer[].class, "A/a1"));
-		assertObjectEquals("[1,2,3]", cfw.getObject(Integer[].class, "A/a1"));
-		assertObjectEquals("[4,5,6]", cf.getObject(Integer[].class, "A/a2", new Integer[]{4,5,6}));
-		assertObjectEquals("[4,5,6]", cfw.getObject(Integer[].class, "A/a2", new Integer[]{4,5,6}));
-		assertObjectEquals("[7,8,9]", cf.getObject(Integer[].class, "B/a1", new Integer[]{7,8,9}));
-		assertObjectEquals("[7,8,9]", cfw.getObject(Integer[].class, "B/a1", new Integer[]{7,8,9}));
-		assertObjectEquals("[]", cf.getObject(Integer[].class, "B/a1"));
-		assertObjectEquals("[]", cfw.getObject(Integer[].class, "B/a1"));
-
-		cf = configFileBuilder.build().addLines("A", "a1 = 1 ,\n\t2 ,\n\t3 ");
-		assertObjectEquals("[1,2,3]", cf.getObject(Integer[].class, "A/a1"));
-		assertObjectEquals("[1,2,3]", cfw.getObject(Integer[].class, "A/a1"));
+		assertObjectEquals("[1,2,3]", cf.getObject("A/a1", Integer[].class));
+		assertObjectEquals("[1,2,3]", cfw.getObject("A/a1", Integer[].class));
+		assertObjectEquals("[4,5,6]", cf.getObjectWithDefault("A/a2", new Integer[]{4,5,6}, Integer[].class));
+		assertObjectEquals("[4,5,6]", cfw.getObjectWithDefault("A/a2", new Integer[]{4,5,6}, Integer[].class));
+		assertObjectEquals("[7,8,9]", cf.getObjectWithDefault("B/a1", new Integer[]{7,8,9}, Integer[].class));
+		assertObjectEquals("[7,8,9]", cfw.getObjectWithDefault("B/a1", new Integer[]{7,8,9}, Integer[].class));
+		assertNull(cf.getObject("B/a1", Integer[].class));
+		assertNull(cfw.getObject("B/a1", Integer[].class));
+
+		cf = configFileBuilder.build().addLines("A", "a1 = [1 ,\n\t2 ,\n\t3] ");
+		assertObjectEquals("[1,2,3]", cf.getObject("A/a1", Integer[].class));
+		assertObjectEquals("[1,2,3]", cfw.getObject("A/a1", Integer[].class));
 
 		// We cannot cast primitive arrays to Object[], so the following throws exceptions.
-		assertObjectEquals("[1,2,3]", cf.getObject(int[].class, "A/a1"));
-		assertEquals("int", cf.getObject(int[].class, "A/a1").getClass().getComponentType().getSimpleName());
-		assertObjectEquals("[]", cf.getObject(int[].class, "B/a1"));
-		assertEquals("int", cf.getObject(int[].class, "B/a1").getClass().getComponentType().getSimpleName());
-		assertObjectEquals("[]", cf.getObject(int[].class, "A/a2"));
-		assertEquals("int", cf.getObject(int[].class, "A/a2").getClass().getComponentType().getSimpleName());
-
-		assertObjectEquals("[1,2,3]", cf.getObject(int[].class, "A/a1", new int[]{4}));
-		assertEquals("int", cf.getObject(int[].class, "A/a1", new int[]{4}).getClass().getComponentType().getSimpleName());
-		assertObjectEquals("[4]", cf.getObject(int[].class, "B/a1", new int[]{4}));
-		assertEquals("int", cf.getObject(int[].class, "B/a1", new int[]{4}).getClass().getComponentType().getSimpleName());
-		assertObjectEquals("[4]", cf.getObject(int[].class, "A/a2", new int[]{4}));
-		assertEquals("int", cf.getObject(int[].class, "A/a2", new int[]{4}).getClass().getComponentType().getSimpleName());
-
-		System.setProperty("X", "4,5,6");
-		cf = configFileBuilder.build().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}").addLines("A", "a1=1,2,3").getResolving();
-		assertObjectEquals("[1,2,3]", cf.getObject(int[].class, "x1", new int[]{9}));
-		assertObjectEquals("[4,5,6]", cf.getObject(int[].class, "x2", new int[]{9}));
-		assertObjectEquals("[9]", cf.getObject(int[].class, "x3", new int[]{9}));
+		assertObjectEquals("[1,2,3]", cf.getObject("A/a1", int[].class));
+		assertEquals("int", cf.getObject("A/a1", int[].class).getClass().getComponentType().getSimpleName());
+		assertNull(cf.getObject("B/a1", int[].class));
+		assertEquals("int", cf.getObjectWithDefault("B/a1", new int[0], int[].class).getClass().getComponentType().getSimpleName());
+		assertNull(cf.getObject("A/a2", int[].class));
+		assertEquals("int", cf.getObjectWithDefault("A/a2", new int[0], int[].class).getClass().getComponentType().getSimpleName());
+
+		assertObjectEquals("[1,2,3]", cf.getObjectWithDefault("A/a1", new int[]{4}, int[].class));
+		assertEquals("int", cf.getObjectWithDefault("A/a1", new int[]{4}, int[].class).getClass().getComponentType().getSimpleName());
+		assertObjectEquals("[4]", cf.getObjectWithDefault("B/a1", new int[]{4}, int[].class));
+		assertEquals("int", cf.getObjectWithDefault("B/a1", new int[]{4}, int[].class).getClass().getComponentType().getSimpleName());
+		assertObjectEquals("[4]", cf.getObjectWithDefault("A/a2", new int[]{4}, int[].class));
+		assertEquals("int", cf.getObjectWithDefault("A/a2", new int[]{4}, int[].class).getClass().getComponentType().getSimpleName());
+
+		System.setProperty("X", "[4,5,6]");
+		cf = configFileBuilder.build().addLines(null, "x1=$C{A/a1}", "x2=$S{X}", "x3=$S{Y}").addLines("A", "a1=[1,2,3]").getResolving();
+		assertObjectEquals("[1,2,3]", cf.getObjectWithDefault("x1", new int[]{9}, int[].class));
+		assertObjectEquals("[4,5,6]", cf.getObjectWithDefault("x2", new int[]{9}, int[].class));
+		assertObjectEquals("[9]", cf.getObjectWithDefault("x3", new int[]{9}, int[].class));
 		System.clearProperty("X");
 	}
 
@@ -1627,34 +1627,34 @@ public class ConfigFileTest {
 	//====================================================================================================
 	@Test
 	public void testGetObject() throws Exception {
-		ConfigFile cf = configFileBuilder.build().addLines("A", "a1=1,2,3", "a2=1", "a3=true", "a4=1.2", "a5=1.2,3.4");
+		ConfigFile cf = configFileBuilder.build().addLines("A", "a1=[1,2,3]", "a2=1", "a3=true", "a4=1.2", "a5=[1.2,3.4]");
 		ConfigFile cfw = cf.getResolving();
 
-		assertObjectEquals("['1','2','3']", cf.getObject(String[].class, "A/a1"));
-		assertObjectEquals("'1,2,3'", cf.getObject(String.class, "A/a1"));
-		assertObjectEquals("'foobar'", cf.getObject(String.class, "X/a1", "foobar"));
-		assertObjectEquals("1", cf.getObject(int.class, "A/a2"));
-		assertObjectEquals("1", cf.getObject(Integer.class, "A/a2"));
-		assertObjectEquals("true", cf.getObject(boolean.class, "A/a3"));
-		assertObjectEquals("true", cf.getObject(Boolean.class, "A/a3"));
-		assertObjectEquals("1.2", cf.getObject(Float.class, "A/a4"));
-		assertObjectEquals("[1.2,3.4]", cf.getObject(Float[].class, "A/a5"));
-		assertObjectEquals("1.2", cf.getObject(float.class, "A/a4"));
-		assertObjectEquals("[1.2,3.4]", cf.getObject(float[].class, "A/a5"));
-		assertNull(cf.getObject(String.class, "B/a4"));
-
-		assertObjectEquals("['1','2','3']", cfw.getObject(String[].class, "A/a1"));
-		assertObjectEquals("'1,2,3'", cfw.getObject(String.class, "A/a1"));
-		assertObjectEquals("'foobar'", cfw.getObject(String.class, "X/a1", "foobar"));
-		assertObjectEquals("1", cfw.getObject(int.class, "A/a2"));
-		assertObjectEquals("1", cfw.getObject(Integer.class, "A/a2"));
-		assertObjectEquals("true", cfw.getObject(boolean.class, "A/a3"));
-		assertObjectEquals("true", cfw.getObject(Boolean.class, "A/a3"));
-		assertObjectEquals("1.2", cfw.getObject(Float.class, "A/a4"));
-		assertObjectEquals("[1.2,3.4]", cfw.getObject(Float[].class, "A/a5"));
-		assertObjectEquals("1.2", cfw.getObject(float.class, "A/a4"));
-		assertObjectEquals("[1.2,3.4]", cfw.getObject(float[].class, "A/a5"));
-		assertNull(cfw.getObject(String.class, "B/a4"));
+		assertObjectEquals("['1','2','3']", cf.getObject("A/a1", String[].class));
+		assertObjectEquals("'[1,2,3]'", cf.getObject("A/a1", String.class));
+		assertObjectEquals("'foobar'", cf.getObjectWithDefault("X/a1", "foobar", String.class));
+		assertObjectEquals("1", cf.getObject("A/a2", int.class));
+		assertObjectEquals("1", cf.getObject("A/a2", Integer.class));
+		assertObjectEquals("true", cf.getObject("A/a3", boolean.class));
+		assertObjectEquals("true", cf.getObject("A/a3", Boolean.class));
+		assertObjectEquals("1.2", cf.getObject("A/a4", Float.class));
+		assertObjectEquals("[1.2,3.4]", cf.getObject("A/a5", Float[].class));
+		assertObjectEquals("1.2", cf.getObject("A/a4", float.class));
+		assertObjectEquals("[1.2,3.4]", cf.getObject("A/a5", float[].class));
+		assertNull(cf.getObject("B/a4", String.class));
+
+		assertObjectEquals("['1','2','3']", cfw.getObject("A/a1", String[].class));
+		assertObjectEquals("'[1,2,3]'", cfw.getObject("A/a1", String.class));
+		assertObjectEquals("'foobar'", cfw.getObjectWithDefault("X/a1", "foobar", String.class));
+		assertObjectEquals("1", cfw.getObject("A/a2", int.class));
+		assertObjectEquals("1", cfw.getObject("A/a2", Integer.class));
+		assertObjectEquals("true", cfw.getObject("A/a3", boolean.class));
+		assertObjectEquals("true", cfw.getObject("A/a3", Boolean.class));
+		assertObjectEquals("1.2", cfw.getObject("A/a4", Float.class));
+		assertObjectEquals("[1.2,3.4]", cfw.getObject("A/a5", Float[].class));
+		assertObjectEquals("1.2", cfw.getObject("A/a4", float.class));
+		assertObjectEquals("[1.2,3.4]", cfw.getObject("A/a5", float[].class));
+		assertNull(cfw.getObject("B/a4", String.class));
 	}
 
 	//====================================================================================================
@@ -1909,13 +1909,13 @@ public class ConfigFileTest {
 				assertEquals("Field 'key' cannot be null.", e.getLocalizedMessage());
 			}
 			try {
-				cf.getObject(Object.class, null);
+				cf.getObject(null, Object.class);
 				fail();
 			} catch (IllegalArgumentException e) {
 				assertEquals("Field 'key' cannot be null.", e.getLocalizedMessage());
 			}
 			try {
-				cf.getObject(null, "");
+				cf.getObject("", null);
 				fail();
 			} catch (IllegalArgumentException e) {
 				assertEquals("Field 'c' cannot be null.", e.getLocalizedMessage());

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/ABean.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/ABean.java b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/ABean.java
new file mode 100644
index 0000000..aa6a724
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/ABean.java
@@ -0,0 +1,24 @@
+// ***************************************************************************************************************************
+// * 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.test.pojos;
+
+public class ABean {
+	public int a;
+	public String b;
+
+	public ABean init() {
+		this.a = 1;
+		this.b = "foo";
+		return this;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/Constants.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/Constants.java b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/Constants.java
new file mode 100644
index 0000000..ccfef31
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/Constants.java
@@ -0,0 +1,25 @@
+// ***************************************************************************************************************************
+// * 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.test.pojos;
+
+/**
+ * Description.
+ * <p>
+ *
+ * @author James Bognar (james.bognar@salesforce.com)
+ */
+public class Constants {
+
+	public static final String SWAP = "swap-~!@#$%^&*()_+`-={}[]|:;\"<,>.?/";
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/ImplicitSwappedPojo.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/ImplicitSwappedPojo.java b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/ImplicitSwappedPojo.java
new file mode 100644
index 0000000..cd1bc0d
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/ImplicitSwappedPojo.java
@@ -0,0 +1,35 @@
+// ***************************************************************************************************************************
+// * 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.test.pojos;
+
+import static org.apache.juneau.test.pojos.Constants.*;
+
+import org.apache.juneau.annotation.*;
+
+@BeanIgnore
+public class ImplicitSwappedPojo {
+	public boolean wasUnswapped;
+
+	@Override
+	public String toString() {
+		return SWAP;
+	}
+
+	public ImplicitSwappedPojo() {}
+
+
+	public ImplicitSwappedPojo(String fromString) {
+		if (fromString.equals(SWAP))
+			wasUnswapped = true;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/SwappedPojo.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/SwappedPojo.java b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/SwappedPojo.java
new file mode 100644
index 0000000..605fd25
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/SwappedPojo.java
@@ -0,0 +1,20 @@
+// ***************************************************************************************************************************
+// * 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.test.pojos;
+
+import org.apache.juneau.annotation.*;
+
+@Pojo(swap=SwappedPojoSwap.class)
+public class SwappedPojo {
+	public boolean wasUnswapped;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/SwappedPojoSwap.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/SwappedPojoSwap.java b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/SwappedPojoSwap.java
new file mode 100644
index 0000000..6284898
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/SwappedPojoSwap.java
@@ -0,0 +1,35 @@
+// ***************************************************************************************************************************
+// * 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.test.pojos;
+
+import static org.apache.juneau.test.pojos.Constants.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transform.*;
+
+public class SwappedPojoSwap extends PojoSwap<SwappedPojo,String> {
+	@Override
+	public String swap(BeanSession session, SwappedPojo c) throws SerializeException {
+		return SWAP;
+	}
+
+	@Override
+	public SwappedPojo unswap(BeanSession session, String f, ClassMeta<?> hint) throws ParseException {
+		SwappedPojo c = new SwappedPojo();
+		if (f.equals(SWAP))
+			c.wasUnswapped = true;
+		return c;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TestEnum.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TestEnum.java b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TestEnum.java
new file mode 100644
index 0000000..0f4a39a
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TestEnum.java
@@ -0,0 +1,17 @@
+// ***************************************************************************************************************************
+// * 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.test.pojos;
+
+public enum TestEnum {
+	ONE,TWO,THREE
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TypedBean.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TypedBean.java b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TypedBean.java
new file mode 100644
index 0000000..2a50d98
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TypedBean.java
@@ -0,0 +1,17 @@
+// ***************************************************************************************************************************
+// * 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.test.pojos;
+
+@org.apache.juneau.annotation.Bean(beanDictionary={TypedBeanImpl.class})
+public interface TypedBean {
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TypedBeanImpl.java
----------------------------------------------------------------------
diff --git a/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TypedBeanImpl.java b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TypedBeanImpl.java
new file mode 100644
index 0000000..7b00644
--- /dev/null
+++ b/juneau-core-test/src/test/java/org/apache/juneau/test/pojos/TypedBeanImpl.java
@@ -0,0 +1,25 @@
+// ***************************************************************************************************************************
+// * 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.test.pojos;
+
+@org.apache.juneau.annotation.Bean(typeName="TypedBeanImpl", sort=true)
+public class TypedBeanImpl implements TypedBean {
+	public int a;
+	public String b;
+
+	public TypedBeanImpl init() {
+		this.a = 1;
+		this.b = "foo";
+		return this;
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/ca59d8a4/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanSession.java b/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
index 525f0e3..1a2baca 100644
--- a/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanSession.java
@@ -372,18 +372,31 @@ public class BeanSession extends Session {
 							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);
+
+						int multiplier = (tc == Integer.TYPE || tc == Short.TYPE || tc == Long.TYPE) ? getMultiplier(n) : 1;
+						if (multiplier != 1) {
+							n = n.substring(0, n.length()-1).trim();
+							Long l = Long.valueOf(n) * multiplier;
+							if (tc == Integer.TYPE)
+								return (T)Integer.valueOf(l.intValue());
+							if (tc == Short.TYPE)
+								return (T)Short.valueOf(l.shortValue());
+							if (tc == Long.TYPE)
+								return (T)Long.valueOf(l.longValue());
+						} else {
+							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();
@@ -426,22 +439,35 @@ public class BeanSession extends Session {
 						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));
+
+					int multiplier = (tc == Integer.class || tc == Short.class || tc == Long.class) ? getMultiplier(n) : 1;
+					if (multiplier != 1) {
+						n = n.substring(0, n.length()-1).trim();
+						Long l = Long.valueOf(n) * multiplier;
+						if (tc == Integer.TYPE)
+							return (T)Integer.valueOf(l.intValue());
+						if (tc == Short.TYPE)
+							return (T)Short.valueOf(l.shortValue());
+						if (tc == Long.TYPE)
+							return (T)Long.valueOf(l.longValue());
+					} else {
+						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));
+					}
 				}
 			}
 
@@ -581,6 +607,16 @@ public class BeanSession extends Session {
 		throw new InvalidDataConversionException(value, type, null);
 	}
 
+	private static int getMultiplier(String s) {
+		if (s.endsWith("G"))
+			return 1024*1024*1024;
+		if (s.endsWith("M"))
+			return 1024*1024;
+		if (s.endsWith("K"))
+			return 1024;
+		return 1;
+	}
+
 	/**
 	 * Converts the contents of the specified list into an array.
 	 * <p>