http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.java 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.java
new file mode 100755
index 0000000..1a70876
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.java
@@ -0,0 +1,1282 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ 
*******************************************************************************/
+package com.ibm.juno.core;
+
+import static com.ibm.juno.core.utils.ClassUtils.*;
+
+import java.io.*;
+import java.util.*;
+
+import com.ibm.juno.core.filter.*;
+import com.ibm.juno.core.filters.*;
+import com.ibm.juno.core.json.*;
+import com.ibm.juno.core.parser.*;
+import com.ibm.juno.core.serializer.*;
+import com.ibm.juno.core.utils.*;
+
+/**
+ * Java implementation of a JSON object.
+ * <p>
+ *     An extension of {@link LinkedHashMap}, so all methods available in that 
class are also available
+ *     to this class.
+ * <p>
+ *     Note that the use of this class is optional.  The serializers will 
accept any objects that implement
+ *     the {@link java.util.Map} interface.  But this class provides some 
useful additional functionality
+ *     when working with JSON models constructed from Java Collections 
Framework objects.  For example, a
+ *     constructor is provided for converting a JSON object string directly 
into a {@link Map}.  It also contains
+ *     accessor methods for to avoid common typecasting when accessing 
elements in a list.
+ *
+ * <h6 class='topic'>Examples</h6>
+ * <p class='bcode'>
+ *     <jc>// Construct an empty Map</jc>
+ *     Map m = <jk>new</jk> ObjectMap();
+ *
+ *     <jc>// Construct a Map from JSON</jc>
+ *     String json = <js>"{a:'A',b:{c:'C',d:123}}"</js>;
+ *     m = <jk>new</jk> ObjectMap(json);
+ *
+ *     <jc>// Construct a Map using the append method</jc>
+ *     m = <jk>new</jk> 
ObjectMap().append(<js>"foo"</js>,<js>"x"</js>).append(<js>"bar"</js>,123).append(<js>"baz"</js>,<jk>true</jk>);
+ *
+ *     <jc>// Construct a Map from XML generated by XmlSerializer</jc>
+ *     String xml = <js>"&lt;object&gt;&lt;a type='string'&gt;A&lt;/a&gt;&lt;b 
type='object'&gt;&lt;c type='string'&gt;C&lt;/c&gt;&lt;d 
type='number'&gt;123&lt;/d&gt;&lt;/b&gt;&lt;/object&gt;"</js>;
+ *     m = <jk>new</jk> ObjectMap(xml, DataFormat.<jsf>XML</jsf>);
+ *     m = (Map)XmlParser.<jsf>DEFAULT</jsf>.parse(xml); <jc>// Equivalent</jc>
+ *     m = (Map)XmlParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, 
xml); <jc>// Equivalent</jc>
+ *     m = XmlParser.<jsf>DEFAULT</jsf>.parse(Map.<jk>class</jk>, xml); <jc>// 
Equivalent</jc>
+ *     m = XmlParser.<jsf>DEFAULT</jsf>.parse(ObjectMap.<jk>class</jk>, xml); 
<jc>// Equivalent</jc>
+ *
+ *     <jc>// Construct a Map from a URL GET parameter string generated by 
UrlEncodingParser</jc>
+ *     String urlParams = <js>"?a='A'&amp;b={c:'C',d:123}"</js>;
+ *     m = <jk>new</jk> ObjectMap(urlParams, DataFormat.<jsf>URLPARAM</jsf>);
+ *     m = 
(Map)UrlEncodingParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, xml); 
<jc>// Equivalent</jc>
+ *     m = UrlEncodingParser.<jsf>DEFAULT</jsf>.parse(Map.<jk>class</jk>, 
xml); <jc>// Equivalent</jc>
+ *     m = 
UrlEncodingParser.<jsf>DEFAULT</jsf>.parse(ObjectMap.<jk>class</jk>, xml); 
<jc>// Equivalent</jc>
+ *
+ *     <jc>// Construct JSON from ObjectMap</jc>
+ *     m = <jk>new</jk> ObjectMap(<js>"{foo:'bar'},{baz:[123,true]}"</js>);
+ *     json = m.toString();  <jc>// Produces 
"{foo:'bar'},{baz:[123,true]}"</jc>
+ *     json = m.toString(JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>);  <jc>// 
Equivalent</jc>
+ *     json = JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>.serialize(m);  
<jc>// Equivalent</jc>
+ *
+ *     <jc>// Get a map entry as an Integer</jc>
+ *     m = <jk>new</jk> ObjectMap(<js>"{foo:123}"</js>);
+ *     Integer i = m.getInt(<js>"foo"</js>);
+ *     i = m.get(Integer.<jk>class</jk>, <js>"foo"</js>);  <jc>// 
Equivalent</jc>
+ *
+ *     <jc>// Get a map entry as a Float</jc>
+ *     m = <jk>new</jk> ObjectMap(<js>"{foo:123}"</js>);
+ *     Float f = m.getFloat(<js>"foo"</js>);
+ *     f = m.get(Float.<jk>class</jk>, <js>"foo"</js>);  <jc>// Equivalent</jc>
+ *
+ *     <jc>// Same as above, except converted to a String</jc>
+ *     m = <jk>new</jk> ObjectMap(<js>"{foo:123}"</js>);
+ *     String s = m.getString(<js>"foo"</js>); <jc>// Returns "123"</jc>
+ *     s = m.get(String.<jk>class</jk>, <js>"foo"</js>);  <jc>// 
Equivalent</jc>
+ *
+ *     <jc>// Get one of the entries in the list as a bean (converted to a 
bean if it isn't already one)</jc>
+ *     m = <jk>new</jk> ObjectMap(<js>"{person:{name:'John 
Smith',age:45}}"</js>);
+ *     Person p = m.get(Person.<jk>class</jk>, <js>"person"</js>);
+ *
+ *     <jc>// Add an inner map</jc>
+ *     ObjectMap m1 = <jk>new</jk> ObjectMap(<js>"{a:1}"</js>);
+ *     ObjectMap m2 = <jk>new</jk> ObjectMap(<js>"{b:2}"</js>).setInner(m1);
+ *     <jk>int</jk> a = m2.getInt(<js>"a"</js>);  <jc>// a == 1 </jc>
+ * </p>
+ *
+ * @author James Bognar ([email protected])
+ */
+public class ObjectMap extends LinkedHashMap<String,Object> {
+       private static final long serialVersionUID = 1L;
+
+       private transient BeanContext beanContext = BeanContext.DEFAULT;
+       private ObjectMap inner;
+
+       /**
+        * An empty read-only ObjectMap.
+        */
+       public static final ObjectMap EMPTY_MAP = new ObjectMap() {
+
+               private static final long serialVersionUID = 1L;
+
+               @Override /* Map */
+               @SuppressWarnings("unchecked")
+               public Set<Map.Entry<String,Object>> entrySet() {
+                       return Collections.EMPTY_MAP.entrySet();
+               }
+
+               @Override /* Map */
+               @SuppressWarnings("unchecked")
+               public Set<String> keySet() {
+                       return Collections.EMPTY_MAP.keySet();
+               }
+
+               @Override /* Map */
+               public Object put(String key, Object value) {
+                       throw new UnsupportedOperationException();
+               }
+
+               @Override /* Map */
+               public Object remove(Object key) {
+                       throw new UnsupportedOperationException();
+               }
+
+               @Override /* Map */
+               public Collection<Object> values() {
+                       return Collections.emptyMap().values();
+               }
+       };
+
+       /**
+        * Construct an ObjectMap directly from a string using the specified 
parser.
+        *
+        * @param s The string being parsed.
+        * @param p The parser to use to parse the input.
+        * @throws ParseException If the input contains a syntax error or is 
malformed.
+        */
+       public ObjectMap(CharSequence s, ReaderParser p) throws ParseException {
+               this(p == null ? BeanContext.DEFAULT : p.getBeanContext());
+               try {
+                       if (p == null)
+                               p = JsonParser.DEFAULT;
+                       if (s != null)
+                               p.parseIntoMap(new CharSequenceReader(s), 
s.length(), this, beanContext.string(), beanContext.object());
+               } catch (IOException e) {
+                       throw new ParseException(e);
+               }
+       }
+
+       /**
+        * Shortcut for <code><jk>new</jk> 
ObjectMap(string,JsonParser.<jsf>DEFAULT</jsf>);</code>
+        *
+        * @param s The JSON text to parse.
+        * @throws ParseException If the input contains a syntax error or is 
malformed.
+        */
+       public ObjectMap(CharSequence s) throws ParseException {
+               this(s, null);
+       }
+
+       /**
+        * Construct an ObjectMap directly from a reader using the specified 
parser.
+        *
+        * @param r The reader to read from.  The reader will be wrapped in a 
{@link BufferedReader} if it isn't already.
+        * @param p The parser to use to parse the input.
+        * @throws ParseException If the input contains a syntax error or is 
malformed.
+        * @throws IOException If a problem occurred trying to read from the 
reader.
+        */
+       public ObjectMap(Reader r, ReaderParser p) throws ParseException, 
IOException {
+               parseReader(r, p);
+       }
+
+       /**
+        * Shortcut for <code><jk>new</jk> ObjectMap(reader, 
JsonParser.<jsf>DEFAULT</jsf>)</code>.
+        *
+        * @param r The reader to read from.  The reader will be wrapped in a 
{@link BufferedReader} if it isn't already.
+        * @throws ParseException If the input contains a syntax error or is 
malformed.
+        * @throws IOException If a problem occurred trying to read from the 
reader.
+        */
+       public ObjectMap(Reader r) throws ParseException, IOException {
+               parseReader(r, JsonParser.DEFAULT);
+       }
+
+       private void parseReader(Reader r, ReaderParser p) throws IOException, 
ParseException {
+               if (p == null)
+                       p = JsonParser.DEFAULT;
+               p.parseIntoMap(r, -1, this, beanContext.string(), 
beanContext.object());
+       }
+
+       /**
+        * Construct an empty JSON object (i.e. an empty {@link LinkedHashMap}).
+        */
+       public ObjectMap() {
+               this(BeanContext.DEFAULT);
+       }
+
+       /**
+        * Construct an empty JSON object (i.e. an empty {@link LinkedHashMap}) 
with the specified bean context.
+        *
+        * @param beanContext The bean context to use for creating beans.
+        */
+       public ObjectMap(BeanContext beanContext) {
+               super();
+               this.beanContext = beanContext;
+       }
+
+       /**
+        * Construct a JSON object and fill it with the contents from the 
specified {@link Map}.
+        *
+        * @param m The map whose entries will be copied into this map.
+        */
+       public ObjectMap(Map<?,?> m) {
+               super();
+               for (Map.Entry<?,?> e : m.entrySet())
+                       put(e.getKey().toString(), e.getValue());
+       }
+
+       /**
+        * Set an inner map in this map to allow for chained get calls.
+        * <p>
+        * If {@link #get(Object)} returns <jk>null</jk>, then {@link 
#get(Object)} will be called on the inner map.
+        * <p>
+        * In addition to providing the ability to chain maps, this method also 
provides the ability
+        * to wrap an existing map inside another map so that you can add 
entries to the outer
+        * map without affecting the values on the inner map.
+        * <p class='bcode'>
+        *      ObjectMap m1 = <jk>new</jk> ObjectMap(<js>"{foo:1}"</js>);
+        *      ObjectMap m2 = <jk>new</jk> ObjectMap().setInner(m1);
+        *      m2.put(<js>"foo"</js>, 2);                      <jc>// 
Overwrite the entry</jc>
+        *      <jk>int</jk> foo1 = m1.getInt(<js>"foo"</js>);           <jc>// 
foo1 == 1 </jc>
+        *      <jk>int</jk> foo2 = m2.getInt(<js>"foo"</js>);           <jc>// 
foo2 == 2 </jc>
+        * </p>
+        *
+        * @param inner The inner map.
+        *      Can be <jk>null</jk> to remove the inner map from an existing 
map.
+        * @return This object (for method chaining).
+        */
+       public ObjectMap setInner(ObjectMap inner) {
+               this.inner = inner;
+               return this;
+       }
+
+       /**
+        * Searches for the specified key in this map ignoring case.
+        *
+        * @param key The key to search for.  For performance reasons, it's 
preferrable that the key be all lowercase.
+        * @return The key, or <jk>null</jk> if map does not contain this key.
+        */
+       public String findKeyIgnoreCase(String key) {
+               for (String k : keySet())
+                       if (key.equalsIgnoreCase(k))
+                               return k;
+               return null;
+       }
+
+
+       /**
+        * Returns the inner map if one was set through {@link 
#setInner(ObjectMap)}.
+        *
+        * @return The inner map if one was set through {@link 
#setInner(ObjectMap)}, or <jk>null</jk> if no inner map is present.
+        */
+       public ObjectMap getInner() {
+               return inner;
+       }
+
+       /**
+        * Override the default bean context used for converting POJOs.
+        * <p>
+        * Default is {@link BeanContext#DEFAULT}, which is sufficient in most 
cases.
+        * <p>
+        * Useful if you're serializing/parsing beans with filters defined.
+        *
+        * @param beanContext The new bean context.
+        * @return This object (for method chaining).
+        */
+       public ObjectMap setBeanContext(BeanContext beanContext) {
+               this.beanContext = beanContext;
+               return this;
+       }
+
+       /**
+        * Returns the {@link BeanContext} currently associated with this map.
+        *
+        * @return The {@link BeanContext} currently associated with this map.
+        */
+       public BeanContext getBeanContext() {
+               return beanContext;
+       }
+
+       /**
+        * Convenience method for adding multiple objects to this map.
+        * <p>
+        *      Equivalent to calling {@code put(key, value)}, but returns
+        *      this map so that the method can be chained.
+        *
+        * @param key The key.
+        * @param value The value.
+        * @return This object (for method chaining).
+        */
+       public ObjectMap append(String key, Object value) {
+               put(key, value);
+               return this;
+       }
+
+       @Override /* Map */
+       public Object get(Object key) {
+               Object o = super.get(key);
+               if (o == null && inner != null)
+                       o = inner.get(key);
+               return o;
+       }
+
+       /**
+        * Same as {@link Map#get(Object) get()}, but returns the default value 
if the key
+        * could not be found.
+        *
+        * @param key The key.
+        * @param def The default value if the entry doesn't exist.
+        * @return The value, or the default value if the entry doesn't exist.
+        */
+       public Object get(String key, Object def) {
+               Object o = get(key);
+               return (o == null ? def : o);
+       }
+
+       /**
+        * Same as {@link Map#get(Object) get()}, but casts or converts the 
value to the specified class type.
+        * <p>
+        *      See {@link BeanContext#convertToType(Object, ClassMeta)} for 
the list of valid data conversions.
+        *
+        * @param <T> The class type.
+        * @param type The class type.
+        * @param key The key.
+        * @return The value, or <jk>null</jk> if the entry doesn't exist.
+        */
+       public <T> T get(Class<T> type, String key) {
+               return get(type, key, null);
+       }
+
+       /**
+        * Same as {@link Map#get(Object) get()}, but converts the raw value to 
the specified class type using the specified filter.
+        *
+        * @param <T> The filtered class type.
+        * @param filter The filter class used to convert the raw type to a 
filtered type.
+        * @param key The key.
+        * @return The value, or <jk>null</jk> if the entry doesn't exist.
+        * @throws ParseException Thrown by the filter if a problem occurred 
trying to parse the value.
+        */
+       @SuppressWarnings({ "rawtypes", "unchecked" })
+       public <T> T get(PojoFilter<T,?> filter, String key) throws 
ParseException {
+               Object o = super.get(key);
+               if (o == null)
+                       return null;
+               PojoFilter f = filter;
+               return (T)f.unfilter(o, null);
+       }
+
+       /**
+        * Same as {@link Map#get(Object) get()}, but casts or converts the 
value to the specified class type.
+        * <p>
+        *      See {@link BeanContext#convertToType(Object, ClassMeta)} for 
the list of valid data conversions.
+        *
+        * @param <T> The class type.
+        * @param type The class type.
+        * @param key The key.
+        * @param def The default value if the entry doesn't exist.
+        * @return The value, or the default value if the entry doesn't exist.
+        */
+       public <T> T get(Class<T> type, String key, T def) {
+               Object o = get(key);
+               if (o == null)
+                       return def;
+               T t = beanContext.convertToType(o, type);
+               if (t == null)
+                       return def;
+               return t;
+       }
+
+       /**
+        * Same as {@link Map#get(Object) get()}, but casts or converts the 
value to the specified class type.
+        * <p>
+        *      See {@link BeanContext#convertToType(Object, ClassMeta)} for 
the list of valid data conversions.
+        *
+        * @param <T> The class type.
+        * @param type The class type.
+        * @param key The key.
+        * @return The value, or the default value if the entry doesn't exist.
+        */
+       public <T> T get(ClassMeta<T> type, String key) {
+               return get(type, key, null);
+       }
+
+       /**
+        * Same as {@link Map#get(Object) get()}, but casts or converts the 
value to the specified class type.
+        * <p>
+        *      See {@link BeanContext#convertToType(Object, ClassMeta)} for 
the list of valid data conversions.
+        *
+        * @param <T> The class type.
+        * @param type The class type.
+        * @param key The key.
+        * @param def The default value if the entry doesn't exist.
+        * @return The value, or the default value if the entry doesn't exist.
+        */
+       public <T> T get(ClassMeta<T> type, String key, T def) {
+               Object o = get(key);
+               if (o == null)
+                       return def;
+               return beanContext.convertToType(o, type);
+       }
+
+       /**
+        * Returns the value for the first key in the list that has an entry in 
this map.
+        *
+        * @param keys The keys to look up in order.
+        * @return The value of the first entry whose key exists, or 
<jk>null</jk> if none of the keys exist in this map.
+        */
+       public Object find(String...keys) {
+               for (String key : keys)
+                       if (containsKey(key))
+                               return get(key);
+               return null;
+       }
+
+       /**
+        * Returns the value for the first key in the list that has an entry in 
this map.
+        * <p>
+        *      Casts or converts the value to the specified class type.
+        * <p>
+        *      See {@link BeanContext#convertToType(Object, ClassMeta)} for 
the list of valid data conversions.
+        *
+        * @param type The class type to convert the value to.
+        * @param <T> The class type to convert the value to.
+        * @param keys The keys to look up in order.
+        * @return The value of the first entry whose key exists, or 
<jk>null</jk> if none of the keys exist in this map.
+        */
+       public <T> T find(Class<T> type, String...keys) {
+               for (String key : keys)
+                       if (containsKey(key))
+                               return get(type, key);
+               return null;
+       }
+
+       /**
+        * Convenience method for inserting JSON directly into an attribute on 
this object.
+        * <p>
+        *      The JSON text can be an object (i.e. <js>"{...}"</js>) or an 
array (i.e. <js>"[...]"</js>).
+        *
+        * @param key The key.
+        * @param json The JSON text that will be parsed into an Object and 
then inserted into this map.
+        * @throws ParseException If the input contains a syntax error or is 
malformed.
+        */
+       public void putJson(String key, String json) throws ParseException {
+               this.put(key, JsonParser.DEFAULT.parse(json, Object.class));
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link String}.
+        * <p>
+        *      Shortcut for <code>get(String.<jk>class</jk>, key)</code>.
+        *
+        * @param key The key.
+        * @return The converted value, or <jk>null</jk> if the map contains no 
mapping for this key.
+        */
+       public String getString(String key) {
+               return get(String.class, key);
+       }
+
+       /**
+        * Specialized method that calls {@link #getString(String)} and splits 
the
+        *      results as a simple comma-delimited list.
+        *
+        * @param key the key.
+        * @return A list of tokens, trimmed of whitespace.  An empty list if 
entry not found.  Never <jk>null</jk>.
+        */
+       public String[] getStringArray(String key) {
+               String s = get(String.class, key);
+               return (s == null ? new String[0] : StringUtils.split(s, ','));
+       }
+
+       /**
+        * Same as {@link #getStringArray(String)} but returns a default value 
if the value cannot be found.
+        *
+        * @param key The map key.
+        * @param def The default value if value is not found.
+        * @return The value converted to a string array.
+        */
+       public String[] getStringArray(String key, String[] def) {
+               String s = get(String.class, key);
+               String[] r = (s == null ? new String[0] : StringUtils.split(s, 
','));
+               return (r.length == 0 ? def : r);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link String}.
+        * <p>
+        *      Shortcut for <code>get(String.<jk>class</jk>, key, 
defVal)</code>.
+        *
+        * @param key The key.
+        * @param defVal The default value if the map doesn't contain the 
specified mapping.
+        * @return The converted value, or the default value if the map 
contains no mapping for this key.
+        */
+       public String getString(String key, String defVal) {
+               return get(String.class, key, defVal);
+       }
+
+       /**
+        * Returns the specified entry value converted to an {@link Integer}.
+        * <p>
+        *      Shortcut for <code>get(Integer.<jk>class</jk>, key)</code>.
+        *
+        * @param key The key.
+        * @return The converted value, or <jk>null</jk> if the map contains no 
mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Integer getInt(String key) {
+               return get(Integer.class, key);
+       }
+
+       /**
+        * Returns the specified entry value converted to an {@link Integer}.
+        * <p>
+        *      Shortcut for <code>get(Integer.<jk>class</jk>, key, 
defVal)</code>.
+        *
+        * @param key The key.
+        * @param defVal The default value if the map doesn't contain the 
specified mapping.
+        * @return The converted value, or the default value if the map 
contains no mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Integer getInt(String key, Integer defVal) {
+               return get(Integer.class, key, defVal);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link Long}.
+        * <p>
+        *      Shortcut for <code>get(Long.<jk>class</jk>, key)</code>.
+        *
+        * @param key The key.
+        * @return The converted value, or <jk>null</jk> if the map contains no 
mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Long getLong(String key) {
+               return get(Long.class, key);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link Long}.
+        * <p>
+        *      Shortcut for <code>get(Long.<jk>class</jk>, key, defVal)</code>.
+        *
+        * @param key The key.
+        * @param defVal The default value if the map doesn't contain the 
specified mapping.
+        * @return The converted value, or the default value if the map 
contains no mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Long getLong(String key, Long defVal) {
+               return get(Long.class, key, defVal);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link Boolean}.
+        * <p>
+        *      Shortcut for <code>get(Boolean.<jk>class</jk>, key)</code>.
+        *
+        * @param key The key.
+        * @return The converted value, or <jk>null</jk> if the map contains no 
mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Boolean getBoolean(String key) {
+               return get(Boolean.class, key);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link Boolean}.
+        * <p>
+        *      Shortcut for <code>get(Boolean.<jk>class</jk>, key, 
defVal)</code>.
+        *
+        * @param key The key.
+        * @param defVal The default value if the map doesn't contain the 
specified mapping.
+        * @return The converted value, or the default value if the map 
contains no mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Boolean getBoolean(String key, Boolean defVal) {
+               return get(Boolean.class, key, defVal);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link Map}.
+        * <p>
+        *      Shortcut for <code>get(Map.<jk>class</jk>, key)</code>.
+        *
+        * @param key The key.
+        * @return The converted value, or <jk>null</jk> if the map contains no 
mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Map<?,?> getMap(String key) {
+               return get(Map.class, key);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link Map}.
+        * <p>
+        *      Shortcut for <code>get(Map.<jk>class</jk>, key, defVal)</code>.
+        *
+        * @param key The key.
+        * @param defVal The default value if the map doesn't contain the 
specified mapping.
+        * @return The converted value, or the default value if the map 
contains no mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Map<?,?> getMap(String key, Map<?,?> defVal) {
+               return get(Map.class, key, defVal);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link List}.
+        * <p>
+        *      Shortcut for <code>get(List.<jk>class</jk>, key)</code>.
+        *
+        * @param key The key.
+        * @return The converted value, or <jk>null</jk> if the map contains no 
mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public List<?> getList(String key) {
+               return get(List.class, key);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link List}.
+        * <p>
+        *      Shortcut for <code>get(List.<jk>class</jk>, key, defVal)</code>.
+        *
+        * @param key The key.
+        * @param defVal The default value if the map doesn't contain the 
specified mapping.
+        * @return The converted value, or the default value if the map 
contains no mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public List<?> getList(String key, List<?> defVal) {
+               return get(List.class, key, defVal);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link Map}.
+        * <p>
+        *      Shortcut for <code>get(ObjectMap.<jk>class</jk>, key)</code>.
+        *
+        * @param key The key.
+        * @return The converted value, or <jk>null</jk> if the map contains no 
mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public ObjectMap getObjectMap(String key) {
+               return get(ObjectMap.class, key);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link ObjectMap}.
+        * <p>
+        *      Shortcut for <code>get(ObjectMap.<jk>class</jk>, key, 
defVal)</code>.
+        *
+        * @param key The key.
+        * @param defVal The default value if the map doesn't contain the 
specified mapping.
+        * @return The converted value, or the default value if the map 
contains no mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public ObjectMap getObjectMap(String key, ObjectMap defVal) {
+               return get(ObjectMap.class, key, defVal);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link ObjectList}.
+        * <p>
+        *      Shortcut for <code>get(ObjectList.<jk>class</jk>, key)</code>.
+        *
+        * @param key The key.
+        * @return The converted value, or <jk>null</jk> if the map contains no 
mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public ObjectList getObjectList(String key) {
+               return get(ObjectList.class, key);
+       }
+
+       /**
+        * Returns the specified entry value converted to a {@link ObjectList}.
+        * <p>
+        *      Shortcut for <code>get(ObjectList.<jk>class</jk>, key, 
defVal)</code>.
+        *
+        * @param key The key.
+        * @param defVal The default value if the map doesn't contain the 
specified mapping.
+        * @return The converted value, or the default value if the map 
contains no mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public ObjectList getObjectList(String key, ObjectList defVal) {
+               return get(ObjectList.class, key, defVal);
+       }
+
+       /**
+        * Returns the first entry that exists converted to a {@link String}.
+        * <p>
+        *      Shortcut for <code>find(String.<jk>class</jk>, keys)</code>.
+        *
+        * @param keys The list of keys to look for.
+        * @return The converted value of the first key in the list that has an 
entry in this map,
+        *      or <jk>null</jk> if the map contains no mapping for any of the 
keys.
+        */
+       public String findString(String... keys) {
+               return find(String.class, keys);
+       }
+
+       /**
+        * Returns the first entry that exists converted to an {@link Integer}.
+        * <p>
+        *      Shortcut for <code>find(Integer.<jk>class</jk>, keys)</code>.
+        *
+        * @param keys The list of keys to look for.
+        * @return The converted value of the first key in the list that has an 
entry in this map,
+        *      or <jk>null</jk> if the map contains no mapping for any of the 
keys.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Integer findInt(String... keys) {
+               return find(Integer.class, keys);
+       }
+
+       /**
+        * Returns the first entry that exists converted to a {@link Long}.
+        * <p>
+        *      Shortcut for <code>find(Long.<jk>class</jk>, keys)</code>.
+        *
+        * @param keys The list of keys to look for.
+        * @return The converted value of the first key in the list that has an 
entry in this map,
+        *      or <jk>null</jk> if the map contains no mapping for any of the 
keys.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Long findLong(String... keys) {
+               return find(Long.class, keys);
+       }
+
+       /**
+        * Returns the first entry that exists converted to a {@link Boolean}.
+        * <p>
+        *      Shortcut for <code>find(Boolean.<jk>class</jk>, keys)</code>.
+        *
+        * @param keys The list of keys to look for.
+        * @return The converted value of the first key in the list that has an 
entry in this map,
+        *      or <jk>null</jk> if the map contains no mapping for any of the 
keys.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Boolean findBoolean(String... keys) {
+               return find(Boolean.class, keys);
+       }
+
+       /**
+        * Returns the first entry that exists converted to a {@link Map}.
+        * <p>
+        *      Shortcut for <code>find(Map.<jk>class</jk>, keys)</code>.
+        *
+        * @param keys The list of keys to look for.
+        * @return The converted value of the first key in the list that has an 
entry in this map,
+        *      or <jk>null</jk> if the map contains no mapping for any of the 
keys.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public Map<?,?> findMap(String... keys) {
+               return find(Map.class, keys);
+       }
+
+       /**
+        * Returns the first entry that exists converted to a {@link List}.
+        * <p>
+        *      Shortcut for <code>find(List.<jk>class</jk>, keys)</code>.
+        *
+        * @param keys The list of keys to look for.
+        * @return The converted value of the first key in the list that has an 
entry in this map,
+        *      or <jk>null</jk> if the map contains no mapping for any of the 
keys.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public List<?> findList(String... keys) {
+               return find(List.class, keys);
+       }
+
+       /**
+        * Returns the first entry that exists converted to a {@link ObjectMap}.
+        * <p>
+        *      Shortcut for <code>find(ObjectMap.<jk>class</jk>, keys)</code>.
+        *
+        * @param keys The list of keys to look for.
+        * @return The converted value of the first key in the list that has an 
entry in this map,
+        *      or <jk>null</jk> if the map contains no mapping for any of the 
keys.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public ObjectMap findObjectMap(String... keys) {
+               return find(ObjectMap.class, keys);
+       }
+
+       /**
+        * Returns the first entry that exists converted to a {@link 
ObjectList}.
+        * <p>
+        *      Shortcut for <code>find(ObjectList.<jk>class</jk>, keys)</code>.
+        *
+        * @param keys The list of keys to look for.
+        * @return The converted value of the first key in the list that has an 
entry in this map,
+        *      or <jk>null</jk> if the map contains no mapping for any of the 
keys.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public ObjectList findObjectList(String... keys) {
+               return find(ObjectList.class, keys);
+       }
+
+       /**
+        * Returns the first key in the map.
+        *
+        * @return The first key in the map, or <jk>null</jk> if the map is 
empty.
+        */
+       public String getFirstKey() {
+               return isEmpty() ? null : keySet().iterator().next();
+       }
+
+       /**
+        * Returns the class type of the object at the specified index.
+        *
+        * @param key The key into this map.
+        * @return The data type of the object at the specified key, or 
<jk>null</jk> if the value is null or does not exist.
+        */
+       public ClassMeta<?> getClassMeta(String key) {
+               return beanContext.getClassMetaForObject(get(key));
+       }
+
+       /**
+        * Equivalent to calling <code>get(class,key,def)</code> followed by 
<code>remove(key);</code>
+        *
+        * @param <T> The class type.
+        * @param type The class type.
+        * @param key The key.
+        * @param defVal The default value if the map doesn't contain the 
specified mapping.
+        * @return The converted value, or the default value if the map 
contains no mapping for this key.
+        * @throws InvalidDataConversionException If value cannot be converted.
+        */
+       public <T> T remove(Class<T> type, String key, T defVal) {
+               T t = get(type, key, defVal);
+               remove(key);
+               return t;
+       }
+
+
+       /**
+        * Convenience method for removing several keys at once.
+        *
+        * @param keys The list of keys to remove.
+        */
+       public void removeAll(Collection<String> keys) {
+               for (String k : keys)
+                       remove(k);
+       }
+
+       /**
+        * Convenience method for removing several keys at once.
+        *
+        * @param keys The list of keys to remove.
+        */
+       public void removeAll(String... keys) {
+               for (String k : keys)
+                       remove(k);
+       }
+
+       @Override /* Map */
+       public boolean containsKey(Object key) {
+               if (super.containsKey(key))
+                       return true;
+               if (inner != null)
+                       return inner.containsKey(key);
+               return false;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this map contains the specified key, 
ignoring
+        *      the inner map if it exists.
+        *
+        * @param key The key to look up.
+        * @return <jk>true</jk> if this map contains the specified key.
+        */
+       public boolean containsOuterKey(Object key) {
+               return super.containsKey(key);
+       }
+
+       /**
+        * Returns a copy of this <code>ObjectMap</code> with only the 
specified keys.
+        *
+        * @param keys The keys of the entries to copy.
+        * @return A new map with just the keys and values from this map.
+        */
+       public ObjectMap include(String...keys) {
+               ObjectMap m2 = new ObjectMap();
+               for (Map.Entry<String,Object> e : this.entrySet())
+                       for (String k : keys)
+                               if (k.equals(e.getKey()))
+                                       m2.put(k, e.getValue());
+               return m2;
+       }
+
+       /**
+        * Returns a copy of this <code>ObjectMap</code> without the specified 
keys.
+        *
+        * @param keys The keys of the entries not to copy.
+        * @return A new map without the keys and values from this map.
+        */
+       public ObjectMap exclude(String...keys) {
+               ObjectMap m2 = new ObjectMap();
+               for (Map.Entry<String,Object> e : this.entrySet()) {
+                       boolean exclude = false;
+                       for (String k : keys)
+                               if (k.equals(e.getKey()))
+                                       exclude = true;
+                       if (! exclude)
+                               m2.put(e.getKey(), e.getValue());
+               }
+               return m2;
+       }
+
+       /**
+        * Sets a value in this map if the entry does not exist or the value is 
<jk>null</jk>.
+        *
+        * @param key The map key.
+        * @param val The value to set if the current value does not exist or 
is <jk>null</jk>.
+        * @return This object (for method chaining).
+        */
+       public ObjectMap putIfNull(String key, Object val) {
+               Object o = get(key);
+               if (o == null)
+                       put(key, val);
+               return this;
+       }
+
+       /**
+        * Sets a value in this map if the entry does not exist or the value is 
<jk>null</jk> or an empty string.
+        *
+        * @param key The map key.
+        * @param val The value to set if the current value does not exist or 
is <jk>null</jk> or an empty string.
+        * @return This object (for method chaining).
+        */
+       public ObjectMap putIfEmpty(String key, Object val) {
+               Object o = get(key);
+               if (o == null || o.toString().isEmpty())
+                       put(key, val);
+               return this;
+       }
+
+       /**
+        * Converts this map into the class type specified by the 
<js>"_class"</js> entry value.
+        * <p>
+        *      This method can be used to convert <code>ObjectMap</code> 
objects to a variety of POJO types.
+        *
+        * <dl>
+        *      <dt>Example of valid class types:</dt>
+        *      <dd>
+        * <p>
+        * An object map can be converted to a bean.
+        *              </p>
+        * <p class='bcode'>
+        *      {
+        *              _class: <js>'com.ibm.sample.addressBook.Person'</js>,
+        *              name: <js>'John Smith'</js>,
+        *              ...
+        *      }
+        * </p>
+        * <p>
+        * It can also be converted into another map type.
+        *              </p>
+        * <p class='bcode'>
+        *      <jc>// Generic TreeMap (String keys, Object values)</jc>
+        *      {
+        *              _class: <js>'java.util.TreeMap'</js>,
+        *              name: <js>'John Smith'</js>,
+        *              ...
+        *      }
+        *      <jc>// TreeMap where values are forced to be strings.</jc>
+        *      {
+        *              _class: 
<js>'java.util.TreeMap&lt;java.lang.String,java.lang.String&gt;'</js>,
+        *              name: <js>'John Smith'</js>,
+        *              ...
+        *      }
+        * </p>
+        * <p>
+        * It can also be converted to Collections objects if map defines an 
<code>items</code> entry of type array.
+        *              </p>
+        * <p class='bcode'>
+        *      <jc>// LinkedList of strings</jc>
+        *      {
+        *              _class: <js>'java.util.LinkedList'</js>,
+        *              items: [ <js>'John Smith'</js>, ... ]
+        *      }
+        *      <jc>// LinkedList of beans</jc>
+        *      {
+        *              _class: 
<js>'java.util.LinkedList&lt;com.ibm.sample.addressBook.Person&gt;'</js>,
+        *              items: [ { name: <js>'John Smith'</js>, ... }, ... ]
+        *      }
+        * </p>
+        * <p>
+        * It can also be converted to arrays.
+        *              </p>
+        * <p class='bcode'>
+        *      <jc>// Array of strings</jc>
+        *      {
+        *              _class: <js>'java.lang.String[]'</js>,
+        *              items: [ <js>'John Smith'</js>, ... ]
+        *      }
+        *      <jc>// Array of beans</jc>
+        *      {
+        *              _class: <js>'com.ibm.sample.addressBook.Person[]'</js>,
+        *              items: [ { name: <js>'John Smith'</js>, ... }, ... ]
+        *      }
+        * </p>
+        * <p>
+        * It can also be converted to any type that can be handled by the 
{@link BeanContext#convertToType(Object, Class)} method.
+        * In this case, the value is specified by an <code>value</code> entry 
of any type.
+        *                      For example, if the bean context has a {@link 
CalendarFilter} associated with it, it can convert a string value to a calendar.
+        * <p class='bcode'>
+        *      {
+        *              _class: <js>'java.util.GregorianCalendar'</js>,
+        *              value: <js>'2001-07-04T15:30:45-05:00'</js>
+        *      }
+        * </p>
+        *      </dd>
+        *      <dt>Notes:</dt>
+        *      <dd>
+        * <ul>
+        *      <li>This method is recursive.  It will also recursively convert 
any descendant entries to POJOs.
+        * </ul>
+        *      </dd>
+        * </dl>
+        *
+        * @return The new Java object of type specified by the 
<js>"_class"</js> entry value, or this
+        *      same object if entry does not exist.
+        */
+       public Object cast() {
+               String c = (String)get("_class");
+               if (c == null) {
+                       if (containsKey("_value"))
+                               return get("_value");
+                       return this;
+               }
+               return cast2(beanContext.getClassMetaFromString(c));
+       }
+
+       /**
+        * Converts this map into an object of the specified type.
+        * <p>
+        * The rules are the same as those specified in {@link #cast()}.
+        * <p>
+        * If this map contains a <js>"_class"</js> entry, it must be the same 
as or a subclass
+        *      of the <code>type</code>.
+        *
+        * @param <T> The class type to convert this map object to.
+        * @param type The class type to convert this map object to.
+        * @return The new object.
+        * @throws ClassCastException If the <js>"_class"</js> entry is present 
and not assignable
+        *      from <code>type</code>
+        */
+       @SuppressWarnings("unchecked")
+       public <T> T cast(Class<T> type) {
+               ClassMeta<?> c1 = 
beanContext.getClassMetaFromString((String)get("_class"));
+               ClassMeta<?> c2 = beanContext.getClassMeta(type);
+               ClassMeta<?> c = narrowClassMeta(c1, c2);
+               return (T)cast2(c);
+       }
+
+       /**
+        * Same as {@link #cast(Class)}, except allows you to specify a {@link 
ClassMeta} parameter.
+        *
+        * @param <T> The class type to convert this map object to.
+        * @param cm The class type to convert this map object to.
+        * @return The new object.
+        * @throws ClassCastException If the <js>"_class"</js> entry is present 
and not assignable
+        *      from <code>type</code>
+        */
+       @SuppressWarnings({"unchecked"})
+       public <T> T cast(ClassMeta<T> cm) {
+               ClassMeta<?> c1 = 
beanContext.getClassMetaFromString((String)get("_class"));
+               ClassMeta<?> c = narrowClassMeta(c1, cm);
+               return (T)cast2(c);
+       }
+
+       /*
+        * Combines the class specified by a "_class" attribute with the 
ClassMeta
+        * passed in through the cast(ClassMeta) method.
+        * The rule is that child classes superceed parent classes, and c2 
superceeds c1
+        * if one isn't the parent of another.
+        */
+       @SuppressWarnings("unchecked")
+       private ClassMeta<?> narrowClassMeta(ClassMeta<?> c1, ClassMeta<?> c2) {
+               if (c1 == null)
+                       return c2;
+               ClassMeta<?> c = getNarrowedClassMeta(c1, c2);
+               if (c1.isMap()) {
+                       ClassMeta<?> k = getNarrowedClassMeta(c1.getKeyType(), 
c2.getKeyType());
+                       ClassMeta<?> v = 
getNarrowedClassMeta(c1.getValueType(), c2.getValueType());
+                       return beanContext.getMapClassMeta((Class<? extends 
Map<?,?>>)c.getInnerClass(), k, v);
+               }
+               if (c1.isCollection()) {
+                       ClassMeta<?> e = 
getNarrowedClassMeta(c1.getElementType(), c2.getElementType());
+                       return beanContext.getCollectionClassMeta((Class<? 
extends Collection<?>>)c.getInnerClass(), e);
+               }
+               return c;
+       }
+
+       /*
+        * If c1 is a child of c2 or the same as c2, returns c1.
+        * Otherwise, returns c2.
+        */
+       private ClassMeta<?> getNarrowedClassMeta(ClassMeta<?> c1, ClassMeta<?> 
c2) {
+               if (isParentClass(c2.getInnerClass(), c1.getInnerClass()))
+                       return c1;
+               return c2;
+       }
+
+       /*
+        * Converts this map to the specified class type.
+        */
+       @SuppressWarnings({"unchecked","rawtypes"})
+       private <T> T cast2(ClassMeta<T> cm) {
+
+               try {
+                       Object value = get("value");
+
+                       if (cm.isMap()) {
+                               Map m2 = (cm.canCreateNewInstance() ? 
(Map)cm.newInstance() : new ObjectMap(beanContext));
+                               ClassMeta<?> kType = cm.getKeyType(), vType = 
cm.getValueType();
+                               for (Map.Entry<String,Object> e : entrySet()) {
+                                       Object k = e.getKey();
+                                       Object v = e.getValue();
+                                       if (! k.equals("_class")) {
+
+                                               // Attempt to recursively cast 
child maps.
+                                               if (v instanceof ObjectMap)
+                                                       v = 
((ObjectMap)v).cast();
+
+                                               k = (kType.isString() ? k : 
beanContext.convertToType(k, kType));
+                                               v = (vType.isObject() ? v : 
beanContext.convertToType(v, vType));
+
+                                               m2.put(k, v);
+                                       }
+                               }
+                               return (T)m2;
+
+                       } else if (cm.isBean()) {
+                               BeanMap<? extends T> bm = 
beanContext.newBeanMap(cm.getInnerClass());
+
+                               // Iterate through all the entries in the map 
and set the individual field values.
+                               for (Map.Entry<String,Object> e : entrySet()) {
+                                       String k = e.getKey();
+                                       Object v = e.getValue();
+                                       if (! k.equals("_class")) {
+
+                                               // Attempt to recursively cast 
child maps.
+                                               if (v instanceof ObjectMap)
+                                                       v = 
((ObjectMap)v).cast();
+
+                                               bm.put(k, v);
+                                       }
+                               }
+
+                               return bm.getBean();
+
+                       } else if (cm.isArray() || cm.isCollection()) {
+                               List items = (List)get("items");
+                               return beanContext.convertToType(items, cm);
+
+                       } else if (value != null) {
+                               return beanContext.convertToType(value, cm);
+                       }
+
+               } catch (Exception e) {
+                       throw new BeanRuntimeException(cm.innerClass, "Error 
occurred attempting to cast to an object of type ''{0}''", 
cm.innerClass.getName()).initCause(e);
+               }
+
+               throw new BeanRuntimeException(cm.innerClass, "Cannot convert 
to class type ''{0}''.  Only beans and maps can be converted using this 
method.", cm.innerClass.getName());
+       }
+
+
+       /**
+        * Serialize this object into a string using the specified serializer.
+        *
+        * @param serializer The serializer to use to convert this object to a 
string.
+        * @return This object serialized as a string.
+        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        */
+       public String toString(WriterSerializer serializer) throws 
SerializeException {
+               return serializer.serialize(this);
+       }
+
+       /**
+        * Serialize this object into a JSON string using the {@link 
JsonSerializer#DEFAULT} serializer.
+        */
+       @Override /* Object */
+       public String toString() {
+               try {
+                       return this.toString(JsonSerializer.DEFAULT_LAX);
+               } catch (SerializeException e) {
+                       return e.getLocalizedMessage();
+               }
+       }
+
+       /**
+        * Convenience method for serializing this map to the specified 
<code>Writer</code> using
+        * the {@link JsonSerializer#DEFAULT} serializer.
+        *
+        * @param w The writer to serialize this object to.
+        * @return This object (for method chaining).
+        * @throws IOException If a problem occurred trying to write to the 
writer.
+        * @throws SerializeException If a problem occurred trying to convert 
the output.
+        */
+       public ObjectMap serializeTo(Writer w) throws IOException, 
SerializeException {
+               JsonSerializer.DEFAULT.serialize(this);
+               return this;
+       }
+
+       @Override /* Map */
+       public Set<String> keySet() {
+               if (inner == null)
+                       return super.keySet();
+               LinkedHashSet<String> s = new LinkedHashSet<String>();
+               s.addAll(inner.keySet());
+               s.addAll(super.keySet());
+               return s;
+       }
+
+       @Override /* Map */
+       public Set<Map.Entry<String,Object>> entrySet() {
+               if (inner == null)
+                       return super.entrySet();
+
+               final Set<String> keySet = keySet();
+               final Iterator<String> keys = keySet.iterator();
+
+               return new AbstractSet<Map.Entry<String,Object>>() {
+
+                       @Override /* Iterable */
+                       public Iterator<Map.Entry<String,Object>> iterator() {
+
+                               return new Iterator<Map.Entry<String,Object>>() 
{
+
+                                       @Override /* Iterator */
+                                       public boolean hasNext() {
+                                               return keys.hasNext();
+                                       }
+
+                                       @Override /* Iterator */
+                                       public Map.Entry<String,Object> next() {
+                                               return new 
Map.Entry<String,Object>() {
+                                                       String key = 
keys.next();
+
+                                                       @Override /* Map.Entry 
*/
+                                                       public String getKey() {
+                                                               return key;
+                                                       }
+
+                                                       @Override /* Map.Entry 
*/
+                                                       public Object 
getValue() {
+                                                               return get(key);
+                                                       }
+
+                                                       @Override /* Map.Entry 
*/
+                                                       public Object 
setValue(Object object) {
+                                                               return put(key, 
object);
+                                                       }
+                                               };
+                                       }
+
+                                       @Override /* Iterator */
+                                       public void remove() {
+                                               throw new 
UnsupportedOperationException();
+                                       }
+                               };
+                       }
+
+                       @Override /* Set */
+                       public int size() {
+                               return keySet.size();
+                       }
+               };
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.class 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.class
new file mode 100755
index 0000000..2154b91
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.java 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.java
new file mode 100755
index 0000000..a4c26d3
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ 
*******************************************************************************/
+package com.ibm.juno.core;
+
+import com.ibm.juno.core.annotation.*;
+
+/**
+ * Defines an API for converting conventional bean property names to some 
other form.
+ * <p>
+ * For example, given the bean property <js>"fooBarURL"</js>, the {@link 
PropertyNamerDashedLC}
+ *     property namer will convert this to <js>"foo-bar-url"</js>.
+ * <p>
+ * Property namers are associated with beans through the {@link 
Bean#propertyNamer} annotation.
+ *
+ * @author James Bognar ([email protected])
+ */
+public interface PropertyNamer {
+
+       /**
+        * Convert the specified default property name to some other value.
+        * @param name The original bean property name.
+        * @return The converted property name.
+        */
+       public String getPropertyName(String name);
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.class
new file mode 100755
index 0000000..752a35e
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.class
 differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.java
new file mode 100755
index 0000000..4d93c7e
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ 
*******************************************************************************/
+package com.ibm.juno.core;
+
+/**
+ * Converts property names to dashed-lower-case format.
+ * <p>
+ *     Examples:
+ * <ul>
+ *     <li><js>"fooBar"</js> -&gt; <js>"foo-bar"</js>
+ *     <li><js>"fooBarURL"</js> -&gt; <js>"foo-bar-url"</js>
+ *     <li><js>"FooBarURL"</js> -&gt; <js>"foo-bar-url"</js>
+ * </ul>
+ *
+ * @author James Bognar ([email protected])
+ */
+public final class PropertyNamerDashedLC implements PropertyNamer {
+
+       @Override /* PropertyNamer */
+       public String getPropertyName(String name) {
+               if (name == null || name.isEmpty())
+                       return name;
+
+               int numUCs = 0;
+               boolean isPrevUC = Character.isUpperCase(name.charAt(0));
+               for (int i = 1; i < name.length(); i++) {
+                       char c = name.charAt(i);
+                       if (Character.isUpperCase(c)) {
+                               if (! isPrevUC)
+                                       numUCs++;
+                               isPrevUC = true;
+                       } else {
+                               isPrevUC = false;
+                       }
+               }
+
+               char[] name2 = new char[name.length() + numUCs];
+               isPrevUC = Character.isUpperCase(name.charAt(0));
+               name2[0] = Character.toLowerCase(name.charAt(0));
+               int ni = 0;
+               for (int i = 0; i < name.length(); i++) {
+                       char c = name.charAt(i);
+                       if (Character.isUpperCase(c)) {
+                               if (! isPrevUC)
+                                       name2[ni++] = '-';
+                               isPrevUC = true;
+                               name2[ni++] = Character.toLowerCase(c);
+                       } else {
+                               isPrevUC = false;
+                               name2[ni++] = c;
+                       }
+               }
+
+               return new String(name2);
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.class
new file mode 100755
index 0000000..3a6cb52
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.class
 differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.java 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.java
new file mode 100755
index 0000000..c534d76
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ 
*******************************************************************************/
+package com.ibm.juno.core;
+
+import java.beans.*;
+
+/**
+ * Default property namer.
+ * <p>
+ *     Examples:
+ * <ul>
+ *     <li><js>"fooBar"</js> -&gt; <js>"fooBar"</js>
+ *     <li><js>"fooBarURL"</js> -&gt; <js>"fooBarURL"</js>
+ *     <li><js>"FooBarURL"</js> -&gt; <js>"fooBarURL"</js>
+ *     <li><js>"URL"</js> -&gt; <js>"URL"</js>
+ * </ul>
+ * <p>
+ *     See {@link Introspector#decapitalize(String)} for exact rules.
+ *
+ * @author James Bognar ([email protected])
+ */
+public final class PropertyNamerDefault implements PropertyNamer {
+
+       @Override /* PropertyNamer */
+       public String getPropertyName(String name) {
+               return Introspector.decapitalize(name);
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.class 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.class
new file mode 100755
index 0000000..7c7b7a5
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.java 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.java
new file mode 100755
index 0000000..4dc8f2f
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2015. All Rights Reserved.
+ *
+ * Note to U.S. Government Users Restricted Rights:  Use,
+ * duplication or disclosure restricted by GSA ADP Schedule
+ * Contract with IBM Corp.
+ 
*******************************************************************************/
+package com.ibm.juno.core;
+
+import java.io.*;
+
+/**
+ * Interface that identifies that an object can be serialized directly to an 
output stream.
+ * <p>
+ *     Instances must identify the media type of the content by implementing 
the
+ *     {@link #getMediaType()} method.
+ * </p>
+ *
+ * @author James Bognar ([email protected])
+ */
+public interface Streamable {
+
+       /**
+        * Serialize this object to the specified output stream.
+        *
+        * @param os The output stream to stream to.
+        * @throws IOException
+        */
+       void streamTo(OutputStream os) throws IOException;
+
+       /**
+        * Returns the serialized media type for this resource (e.g. 
<js>"text/html"</js>).
+        *
+        * @return The media type, or <jk>null</jk> if the media type is not 
known.
+        */
+       String getMediaType();
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility$1.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility$1.class 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility$1.class
new file mode 100755
index 0000000..2fb5745
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility$1.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.class 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.class
new file mode 100755
index 0000000..3bd640f
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.java 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.java
new file mode 100755
index 0000000..0687d39
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * Note to U.S. Government Users Restricted Rights:  Use,
+ * duplication or disclosure restricted by GSA ADP Schedule
+ * Contract with IBM Corp.
+ 
*******************************************************************************/
+package com.ibm.juno.core;
+
+import java.lang.reflect.*;
+
+/**
+ * Defines class/field/method visibilities.
+ * <p>
+ * Used to specify minimum levels of visibility when detecting bean classes, 
methods, and fields.
+ * Used in conjunction with the following bean context properties:
+ * <ul>
+ *     <li>{@link BeanContextProperties#BEAN_beanConstructorVisibility}
+ *     <li>{@link BeanContextProperties#BEAN_beanClassVisibility}
+ *     <li>{@link BeanContextProperties#BEAN_beanFieldVisibility}
+ *     <li>{@link BeanContextProperties#BEAN_methodVisibility}
+ * </ul>
+ *
+ * @author James Bognar ([email protected])
+ */
+public enum Visibility {
+
+       /** Ignore all */
+       NONE,
+
+       /** Include only <jk>public</jk> classes/fields/methods. */
+       PUBLIC,
+
+       /** Include only <jk>public</jk> or <jk>protected</jk> 
classes/fields/methods. */
+       PROTECTED,
+
+       /** Include all but <jk>private</jk> classes/fields/methods. */
+       DEFAULT,
+
+       /** Include all classes/fields/methods. */
+       PRIVATE;
+
+       /**
+        * Identifies if the specified mod matches this visibility.
+        * Example:
+        * <code>
+        *      
<jsf>PUBLIC</jsf>.isVisible(MyPublicClass.<jk>class</jk>.getModifiers()); 
<jc>//true</jk>
+        *      
<jsf>PUBLIC</jsf>.isVisible(MyPrivateClass.<jk>class</jk>.getModifiers()); 
<jc>//false</jk>
+        *      
<jsf>PRIVATE</jsf>.isVisible(MyPrivateClass.<jk>class</jk>.getModifiers()); 
<jc>//true</jk>
+        *      
<jsf>NONE</jsf>.isVisible(MyPublicClass.<jk>class</jk>.getModifiers()); 
<jc>//false</jk>
+        * </code>
+        *
+        * @param mod The modifier from the object being tested (e.g. results 
from {@link Class#getModifiers()}.
+        * @return <jk>true</jk> if this visibility matches the specified 
modifier attribute.
+        */
+       public boolean isVisible(int mod) {
+               switch(this) {
+                       case NONE: return false;
+                       case PRIVATE: return true;
+                       case DEFAULT: return ! Modifier.isPrivate(mod);
+                       case PROTECTED: return Modifier.isProtected(mod) || 
Modifier.isPublic(mod);
+                       default: return Modifier.isPublic(mod);
+               }
+       }
+
+       /**
+        * Shortcut for <code>isVisible(x.getModifiers());</code>
+        *
+        * @param x The constructor to check.
+        * @return <jk>true</jk> if the constructor is at least as visible as 
this object.
+        */
+       public boolean isVisible(Constructor<?> x) {
+               return isVisible(x.getModifiers());
+       }
+
+       /**
+        * Shortcut for <code>isVisible(x.getModifiers());</code>
+        *
+        * @param x The method to check.
+        * @return <jk>true</jk> if the method is at least as visible as this 
object.
+        */
+       public boolean isVisible(Method x) {
+               return isVisible(x.getModifiers());
+       }
+
+       /**
+        * Shortcut for <code>isVisible(x.getModifiers());</code>
+        *
+        * @param x The field to check.
+        * @return <jk>true</jk> if the field is at least as visible as this 
object.
+        */
+       public boolean isVisible(Field x) {
+               return isVisible(x.getModifiers());
+       }
+
+       /**
+        * Makes constructor accessible if it matches the visibility 
requirements, or returns <jk>null</jk> if it doesn't.
+        * Security exceptions thrown on the call to {@link 
Constructor#setAccessible(boolean)} are quietly ignored.
+        *
+        * @param x The constructor.
+        * @return The same constructor if visibility requirements met, or 
<jk>null</jk> if visibility requirement not
+        *      met or call to {@link Constructor#setAccessible(boolean)} 
throws a security exception.
+        */
+       public <T> Constructor<T> filter(Constructor<T> x) {
+               if (x == null)
+                       return null;
+               if (isVisible(x))
+                       if (! setAccessible(x))
+                               return null;
+               return x;
+       }
+
+       /**
+        * Makes method accessible if it matches the visibility requirements, 
or returns <jk>null</jk> if it doesn't.
+        * Security exceptions thrown on the call to {@link 
Method#setAccessible(boolean)} are quietly ignored.
+        *
+        * @param x The method.
+        * @return The same method if visibility requirements met, or 
<jk>null</jk> if visibility requirement not
+        *      met or call to {@link Method#setAccessible(boolean)} throws a 
security exception.
+        */
+       public <T> Method filter(Method x) {
+               if (x == null)
+                       return null;
+               if (isVisible(x))
+                       if (! setAccessible(x))
+                               return null;
+               return x;
+       }
+
+       /**
+        * Makes field accessible if it matches the visibility requirements, or 
returns <jk>null</jk> if it doesn't.
+        * Security exceptions thrown on the call to {@link 
Field#setAccessible(boolean)} are quietly ignored.
+        *
+        * @param x The field.
+        * @return The same field if visibility requirements met, or 
<jk>null</jk> if visibility requirement not
+        *      met or call to {@link Field#setAccessible(boolean)} throws a 
security exception.
+        */
+       public Field filter(Field x) {
+               if (x == null)
+                       return null;
+               if (isVisible(x))
+                       if (! setAccessible(x))
+                               return null;
+               return x;
+       }
+
+       /**
+        * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and 
quietly ignores security exceptions.
+        *
+        * @param x The constructor.
+        * @return <jk>true</jk> if call was successful.
+        */
+       public static boolean setAccessible(Constructor<?> x) {
+               try {
+                       if (! (x == null || x.isAccessible()))
+                               x.setAccessible(true);
+                       return true;
+               } catch (SecurityException e) {
+                       return false;
+               }
+       }
+
+       /**
+        * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and 
quietly ignores security exceptions.
+        *
+        * @param x The method.
+        * @return <jk>true</jk> if call was successful.
+        */
+       public static boolean setAccessible(Method x) {
+               try {
+                       if (! (x == null || x.isAccessible()))
+                               x.setAccessible(true);
+                       return true;
+               } catch (SecurityException e) {
+                       return false;
+               }
+       }
+
+       /**
+        * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and 
quietly ignores security exceptions.
+        *
+        * @param x The field.
+        * @return <jk>true</jk> if call was successful.
+        */
+       public static boolean setAccessible(Field x) {
+               try {
+                       if (! (x == null || x.isAccessible()))
+                               x.setAccessible(true);
+                       return true;
+               } catch (SecurityException e) {
+                       return false;
+               }
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.class 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.class
new file mode 100755
index 0000000..ceb79f5
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.class differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.java 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.java
new file mode 100755
index 0000000..0c7cbd2
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2015. All Rights Reserved.
+ *
+ * Note to U.S. Government Users Restricted Rights:  Use,
+ * duplication or disclosure restricted by GSA ADP Schedule
+ * Contract with IBM Corp.
+ 
*******************************************************************************/
+package com.ibm.juno.core;
+
+import java.io.*;
+
+/**
+ * Interface that identifies that an object can be serialized directly to a 
writer.
+ * <p>
+ *     Instances must identify the media type of the content by implementing 
the
+ *     {@link #getMediaType()} method.
+ * </p>
+ *
+ * @author James Bognar ([email protected])
+ */
+public interface Writable {
+
+       /**
+        * Serialize this object to the specified writer.
+        *
+        * @param w The writer to write to.
+        * @throws IOException
+        */
+       void writeTo(Writer w) throws IOException;
+
+       /**
+        * Returns the serialized media type for this resource (e.g. 
<js>"text/html"</js>)
+        *
+        * @return The media type, or <jk>null</jk> if the media type is not 
known.
+        */
+       String getMediaType();
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.class 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.class
new file mode 100755
index 0000000..a9f3acd
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.class 
differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.java 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.java
new file mode 100755
index 0000000..ccdb7f7
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.java
@@ -0,0 +1,216 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2011, 2014. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ 
*******************************************************************************/
+package com.ibm.juno.core.annotation;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.beans.*;
+import java.lang.annotation.*;
+
+import com.ibm.juno.core.*;
+import com.ibm.juno.core.filter.*;
+
+/**
+ * Used to tailor how beans get interpreted by the framework.
+ * <p>
+ *     Can be used to do the following:
+ * <ul>
+ *     <li>Explicitly specify the set and order of properties on a bean.
+ *     <li>Associate a {@link PropertyNamer} with a class.
+ *     <li>Specify subtypes of a bean differentiated by a sub type property.
+ * </ul>
+ * <p>
+ *     This annotation can be applied to classes and interfaces.
+ *
+ * @author James Bognar ([email protected])
+ */
+@Documented
+@Target(TYPE)
+@Retention(RUNTIME)
+@Inherited
+public @interface Bean {
+
+       /**
+        * The set and order of names of properties associated with a bean 
class.
+        * <p>
+        *      The order specified is the same order that the entries will be 
returned by the {@link BeanMap#entrySet()} and related methods.
+        * <p>
+        *      This annotation is an alternative to using the {@link 
BeanFilter} class with an implemented {@link BeanFilter#getProperties()} method.
+        *
+        * <dl>
+        *      <dt>Example:</dt>
+        *      <dd>
+        * <p class='bcode'>
+        *      <jc>// Address class with only street/city/state properties (in 
that order).</jc>
+        *      <jc>// All other properties are ignored.</jc>
+        *      
<ja>@Bean</ja>(properties={<js>"street"</js>,<js>"city"</js>,<js>"state"</js>})
+        *      <jk>public class</jk> Address {
+        *              ...
+        * </p>
+        *      </dd>
+        * </dl>
+        */
+       String[] properties() default {};
+
+       /**
+        * Specifies a list of properties that should be excluded from {@link 
BeanMap#entrySet()}.
+        * <p>
+        *      This annotation is an alternative to using the {@link 
BeanFilter} class with an implemented {@link BeanFilter#getExcludeProperties()} 
method.
+        *
+        * <dl>
+        *      <dt>Example:</dt>
+        *      <dd>
+        * <p class='bcode'>
+        *      <jc>// Address class with only street/city/state properties (in 
that order).</jc>
+        *      <jc>// All other properties are ignored.</jc>
+        *      
<ja>@Bean</ja>(excludeProperties={<js>"city"</js>,<js>"state"</js>})
+        *      <jk>public class</jk> Address {
+        *              ...
+        * </p>
+        *      </dd>
+        * </dl>
+        */
+       String[] excludeProperties() default {};
+
+       /**
+        * Associates a {@link PropertyNamer} with this bean to tailor the 
names of the bean properties.
+        * <p>
+        *      Property namers are used to transform bean property names from 
standard form to some other form.
+        *      For example, the {@link PropertyNamerDashedLC} will convert 
property names to dashed-lowercase, and
+        *              these will be used as attribute names in JSON, and 
element names in XML.
+        * <p>
+        *      This annotation is an alternative to using the {@link 
BeanFilter} class with an implemented {@link BeanFilter#getPropertyNamer()} 
method.
+        *
+        * <dl>
+        *      <dt>Example:</dt>
+        *      <dd>
+        * <p class='bcode'>
+        *      <jc>// Define a class with dashed-lowercase property names.</jc>
+        *      
<ja>@Bean</ja>(propertyNamer=PropertyNamerDashedLC.<jk>class</jk>)
+        *      <jk>public class</jk> MyClass {
+        *              ...
+        *      }
+        * </p>
+        *      </dd>
+        * </dl>
+        */
+       Class<? extends PropertyNamer> propertyNamer() default 
PropertyNamerDefault.class;
+
+       /**
+        * Defines a virtual property on a superclass that identifies bean 
subtype classes.
+        * <p>
+        *      In the following example, the abstract class has two subclasses 
that are differentiated
+        *              by a property called <code>subType</code>
+        * <p class='bcode'>
+        *      <jc>// Abstract superclass</jc>
+        *      <ja>@Bean</ja>(
+        *              subTypeProperty=<js>"subType"</js>,
+        *              subTypes={
+        *                      <ja>@BeanSubType</ja>(type=A1.<jk>class</jk>, 
id=<js>"A1"</js>),
+        *                      <ja>@BeanSubType</ja>(type=A2.<jk>class</jk>, 
id=<js>"A2"</js>)
+        *              }
+        *      )
+        *      <jk>public class</jk> A {
+        *              <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>;
+        *      }
+        *
+        *      <jc>// Subclass 1</jc>
+        *      <jk>public class</jk> A1 <jk>extends</jk> A {
+        *              <jk>public</jk> String <jf>f1</jf>;
+        *      }
+        *
+        *      <jc>// Subclass 2</jc>
+        *      <jk>public class</jk> A2 <jk>extends</jk> A {
+        *              <jk>public</jk> String <jf>f2</jf>;
+        *      }
+        * </p>
+        * <p>
+        *      The following shows what happens when serializing a subclassed 
object to JSON:
+        * <p>
+        * <p class='bcode'>
+        *      JsonSerializer s = JsonSerializer.<jsf>DEFAULT_LAX</jsf>;
+        *      A1 a1 = <jk>new</jk> A1();
+        *      a1.<jf>f1</jf> = <js>"f1"</js>;
+        *      String r = s.serialize(a1);
+        *      
<jsm>assertEquals</jsm>(<js>"{subType:'A1',f1:'f1',f0:'f0'}"</js>, r);
+        * </p>
+        * <p>
+        *      The following shows what happens when parsing back into the 
original object.
+        * <p>
+        * <p class='bcode'>
+        *      JsonParser p = JsonParser.<jsf>DEFAULT</jsf>;
+        *      A a = p.parse(r, A.<jk>class</jk>);
+        *      <jsm>assertTrue</jsm>(a <jk>instanceof</jk> A1);
+        * </p>
+        * <p>
+        *      This annotation is an alternative to using the {@link 
BeanFilter} class with an implemented {@link BeanFilter#getSubTypeProperty()} 
method.
+        */
+       String subTypeProperty() default "";
+
+       /**
+        * Used in conjunction with {@link #subTypeProperty()} to set up bean 
subtypes.
+        */
+       BeanSubType[] subTypes() default {};
+
+       /**
+        * Identifies a class to be used as the interface class for this and 
all subclasses.
+        * <p>
+        *      When specified, only the list of properties defined on the 
interface class will be used during serialization.
+        *      Additional properties on subclasses will be ignored.
+        * <p class='bcode'>
+        *      <jc>// Parent class</jc>
+        *      <ja>@Bean</ja>(interfaceClass=A.<jk>class</jk>)
+        *      <jk>public abstract class</jk> A {
+        *              <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>;
+        *      }
+        *
+        *      <jc>// Sub class</jc>
+        *      <jk>public class</jk> A1 <jk>extends</jk> A {
+        *              <jk>public</jk> String <jf>f1</jf> = <js>"f1"</js>;
+        *      }
+        *
+        *      JsonSerializer s = JsonSerializer.<jsf>DEFAULT_LAX</jsf>;
+        *      A1 a1 = <jk>new</jk> A1();
+        *      String r = s.serialize(a1);
+        *      <jsm>assertEquals</jsm>(<js>"{f0:'f0'}"</js>, r);  // Note f1 
is not serialized.
+        * </p>
+        * <p>
+        *      Note that this annotation can be used on the parent class so 
that it filters to all child classes,
+        *              or can be set individually on the child classes.
+        * <p>
+        *      This annotation is an alternative to using the {@link 
BeanFilter} class with an implemented {@link BeanFilter#getInterfaceClass()} 
method.
+        */
+       Class<?> interfaceClass() default Object.class;
+
+       /**
+        * Identifies a stop class for the annotated class.
+        * <p>
+        * Identical in purpose to the stop class specified by {@link 
Introspector#getBeanInfo(Class, Class)}.
+        * Any properties in the stop class or in its baseclasses will be 
ignored during analysis.
+        * <p>
+        * For example, in the following class hierarchy, instances of 
<code>C3</code> will include property <code>p3</code>, but
+        *      not <code>p1</code> or <code>p2</code>.
+        * <p class='bcode'>
+        *      <jk>public class</jk> C1 {
+        *              <jk>public int</jk> getP1();
+        *      }
+        *
+        *      <jk>public class</jk> C2 <jk>extends</jk> C1 {
+        *              <jk>public int</jk> getP2();
+        *      }
+        *
+        *      <ja>@Bean</ja>(stopClass=C2.<jk>class</jk>)
+        *      <jk>public class</jk> C3 <jk>extends</jk> C2 {
+        *              <jk>public int</jk> getP3();
+        *      }
+        * </p>
+        */
+       Class<?> stopClass() default Object.class;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.class
new file mode 100755
index 0000000..3a5d88f
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.class
 differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.java
new file mode 100755
index 0000000..b9153e9
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2011, 2014. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ 
*******************************************************************************/
+package com.ibm.juno.core.annotation;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+import com.ibm.juno.core.*;
+
+/**
+ * Maps constructor arguments to property names on beans with read-only 
properties.
+ * <p>
+ *     This annotation can be used in the case of beans with properties whose 
values can only be set by passing
+ *     them in through a constructor on the class.<br>
+ *     Since method parameter names are lost during compilation, this 
annotation essentially redefines them
+ *     so that they are available at runtime.
+ * <p>
+ *     The definition of a read-only bean is a bean with properties with only 
getters, like shown below...
+ * <p class='bcode'>
+ *     <jk>public class</jk> Person {
+ *             <jk>private final</jk> String <jf>name</jf>;
+ *             <jk>private final int</jk> <jf>age</jf>;
+ *
+ *             
<ja>@BeanConstructor</ja>(properties={<js>"name"</js>,<js>"age"</js>})
+ *             <jk>public</jk> Person(String name, <jk>int</jk> age) {
+ *                     <jk>this</jk>.<jf>name</jf> = name;
+ *                     <jk>this</jk>.<jf>age</jf> = age;
+ *             }
+ *
+ *             <jc>// Read only properties.</jc>
+ *
+ *             <jk>public</jk> String getName() {
+ *                     <jk>return</jk> <jf>name</jf>;
+ *             }
+ *
+ *             <jk>public int</jk> getAge() {
+ *                     <jk>return</jk> <jf>age</jf>;
+ *             }
+ *     }
+ *
+ *     String json = <js>"{name:'John Smith',age:45}"</js>;
+ *     Person p = JsonParser.<jsf>DEFAULT</jsf>.parse(json);
+ *     String name = p.getName();  <jc>// "John Smith"</jc>
+ *     <jk>int</jk> age = p.getAge();   <jc>// 45</jc>
+ * </p>
+ * <p>
+ *     This annotation can only be applied to constructors and can only be 
applied to one constructor per class.
+ * <p>
+ *     When present, bean instantiation is delayed until the call to {@link 
BeanMap#getBean()}.
+ *     Until then, bean property values are stored in a local cache until 
<code>getBean()</code> is called.
+ *     Because of this additional caching step, parsing into read-only beans 
tends to be slower and use
+ *     more memory than parsing into beans with writable properties.
+ * <p>
+ *     Attempting to call {@link BeanMap#put(String,Object)} on a read-only 
property after calling {@link BeanMap#getBean()}
+ *     will result in a {@link BeanRuntimeException} being thrown.
+ *     Multiple calls to {@link BeanMap#getBean()} will return the same bean 
instance.
+ * <p>
+ *     Beans can be defined with a combination of read-only and read-write 
properties.
+ *
+ * @author James Bognar ([email protected])
+ */
+@Documented
+@Target(CONSTRUCTOR)
+@Retention(RUNTIME)
+@Inherited
+public @interface BeanConstructor {
+
+       /**
+        * The names of the properties of the constructor arguments.
+        * <p>
+        *      The number of properties listed must match the number of 
arguments in the constructor.
+        */
+       String[] properties() default {};
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.class
new file mode 100755
index 0000000..4e1f137
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.class
 differ

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.java
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.java
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.java
new file mode 100755
index 0000000..b71b3f8
--- /dev/null
+++ 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2011, 2014. All Rights Reserved.
+ *
+ *  The source code for this program is not published or otherwise
+ *  divested of its trade secrets, irrespective of what has been
+ *  deposited with the U.S. Copyright Office.
+ 
*******************************************************************************/
+package com.ibm.juno.core.annotation;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.*;
+
+/**
+ * Ignore classes, fields, and methods from being interpreted as bean or bean 
components.
+ * <p>
+ *     Applied to classes that may look like beans, but you want to be treated 
as non-beans.
+ *     For example, if you want to force a bean to be converted to a string 
using the <code>toString()</code>
+ *             method, use this annoation on the class.
+ * <p>
+ *     Applies to fields that should not be interpreted as bean property 
fields.
+ * <p>
+ *     Applies to getters or setters that should not be interpreted as bean 
property getters or setters.
+ *
+ * @author James Bognar ([email protected])
+ */
+@Documented
+@Target({FIELD,METHOD,TYPE})
+@Retention(RUNTIME)
+@Inherited
+public @interface BeanIgnore {}
+

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanProperty.class
----------------------------------------------------------------------
diff --git 
a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanProperty.class
 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanProperty.class
new file mode 100755
index 0000000..49c9274
Binary files /dev/null and 
b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanProperty.class
 differ

Reply via email to