http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/BeanMap.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMap.java 
b/juneau-core/src/main/java/org/apache/juneau/BeanMap.java
new file mode 100644
index 0000000..28bee46
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanMap.java
@@ -0,0 +1,504 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.transform.*;
+import org.apache.juneau.xml.annotation.*;
+
+/**
+ * Java bean wrapper class.
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ *     A wrapper that wraps Java bean instances inside of a {@link Map} 
interface that allows
+ *     properties on the wrapped object can be accessed using the {@link 
Map#get(Object) get()} and {@link Map#put(Object,Object) put()} methods.
+ * <p>
+ *     Use the {@link BeanContext} class to create instances of this class.
+ *
+ *
+ * <h6 class='topic'>Bean property order</h6>
+ * <p>
+ *     The order of the properties returned by the {@link Map#keySet() 
keySet()} and {@link Map#entrySet() entrySet()} methods are as follows:
+ *     <ul class='spaced-list'>
+ *             <li>If {@link Bean @Bean} annotation is specified on class, 
then the order is the same as the list of properties in the annotation.
+ *             <li>If {@link Bean @Bean} annotation is not specified on the 
class, then the order is the same as that returned
+ *                     by the {@link java.beans.BeanInfo} class (i.e. ordered 
by definition in the class).
+ *     </ul>
+ *     <br>
+ *     The order can also be overridden through the use of a {@link 
BeanTransform}.
+ *
+ *
+ * <h6 class='topic'>POJO transforms</h6>
+ * <p>
+ *     If {@link PojoTransform PojoTransforms} are defined on the class types 
of the properties of this bean or the bean properties themselves, the
+ *     {@link #get(Object)} and {@link #put(String, Object)} methods will 
automatically
+ *     transform the property value to and from the serialized form.
+ *
+ * @author Barry M. Caceres
+ * @author James Bognar ([email protected])
+ * @param <T> Specifies the type of object that this map encapsulates.
+ */
+public class BeanMap<T> extends AbstractMap<String,Object> implements 
Delegate<T> {
+
+       /** The wrapped object. */
+       protected T bean;
+
+       /** Temporary holding cache for beans with read-only properties.  
Normally null. */
+       protected Map<String,Object> propertyCache;
+
+       /** Temporary holding cache for bean properties of array types when the 
add() method is being used. */
+       protected Map<String,List<?>> arrayPropertyCache;
+
+       /** The BeanMeta associated with the class of the object. */
+       protected BeanMeta<T> meta;
+
+       /**
+        * Instance of this class are instantiated through the BeanContext 
class.
+        *
+        * @param bean The bean to wrap inside this map.
+        * @param meta The metadata associated with the bean class.
+        */
+       protected BeanMap(T bean, BeanMeta<T> meta) {
+               this.bean = bean;
+               this.meta = meta;
+               if (meta.constructorArgs.length > 0)
+                       propertyCache = new TreeMap<String,Object>();
+       }
+
+       /**
+        * Returns the metadata associated with this bean map.
+        *
+        * @return The metadata associated with this bean map.
+        */
+       public BeanMeta<T> getMeta() {
+               return meta;
+       }
+
+       /**
+        * Returns the wrapped bean object.
+        * Triggers bean creation if bean has read-only properties set through 
a constructor
+        *      defined by the {@link BeanConstructor} annotation.
+        *
+        * @return The inner bean object.
+        */
+       public T getBean() {
+               T b = getBean(true);
+
+               // If we have any arrays that need to be constructed, do it now.
+               if (arrayPropertyCache != null) {
+                       for (Map.Entry<String,List<?>> e : 
arrayPropertyCache.entrySet()) {
+                               String key = e.getKey();
+                               List<?> value = e.getValue();
+                               BeanPropertyMeta<T> bpm = getPropertyMeta(key);
+                               try {
+                                       bpm.setArray(b, value);
+                               } catch (Exception e1) {
+                                       throw new RuntimeException(e1);
+                               }
+                       }
+                       arrayPropertyCache = null;
+               }
+               return b;
+       }
+
+       /**
+        * Returns the wrapped bean object.
+        * <p>
+        * If <code>create</code> is <jk>false</jk>, then this method may 
return <jk>null</jk>
+        *      if the bean has read-only properties set through a constructor
+        *      defined by the {@link BeanConstructor} annotation.
+        * <p>
+        * This method does NOT always return the bean in it's final state.
+        *      Array properties temporary stored as ArrayLists are not 
finalized
+        *      until the {@link #getBean()} method is called.
+        *
+        * @param create If bean hasn't been instantiated yet, then instantiate 
it.
+        * @return The inner bean object.
+        */
+       public T getBean(boolean create) {
+               /** If this is a read-only bean, then we need to create it. */
+               if (bean == null && create && meta.constructorArgs.length > 0) {
+                       String[] props = meta.constructorArgs;
+                       Constructor<T> c = meta.constructor;
+                       Object[] args = new Object[props.length];
+                       for (int i = 0; i < props.length; i++)
+                               args[i] = propertyCache.remove(props[i]);
+                       try {
+                               bean = c.newInstance(args);
+                               for (Map.Entry<String,Object> e : 
propertyCache.entrySet())
+                                       put(e.getKey(), e.getValue());
+                               propertyCache = null;
+                       } catch (Exception e) {
+                               throw new BeanRuntimeException(e);
+                       }
+               }
+               return bean;
+       }
+
+       /**
+        * Returns the value of the property identified as the URI property 
(annotated with {@link BeanProperty#beanUri()} as <jk>true</jk>).
+        *
+        * @return The URI value, or <jk>null</jk> if no URI property exists on 
this bean.
+        */
+       public Object getBeanUri() {
+               BeanMeta<T> bm = getMeta();
+               return bm.hasBeanUriProperty() ? 
bm.getBeanUriProperty().get(this) : null;
+       }
+
+       /**
+        * Sets the bean URI property if the bean has a URI property.
+        * Ignored otherwise.
+        *
+        * @param o The bean URI object.
+        * @return If the bean context setting {@code 
beanMapPutReturnsOldValue} is <jk>true</jk>, then the old value of the property 
is returned.
+        *              Otherwise, this method always returns <jk>null</jk>.
+        */
+       public Object putBeanUri(Object o) {
+               BeanMeta<T> bm = getMeta();
+               return bm.hasBeanUriProperty() ? 
bm.getBeanUriProperty().set(this, o) : null;
+       }
+
+       /**
+        * Sets a property on the bean.
+        * <p>
+        * If there is a {@link PojoTransform} associated with this bean 
property or bean property type class, then
+        *      you must pass in a transformed value.
+        * For example, if the bean property type class is a {@link Date} and 
the bean property has the
+        *      {@link org.apache.juneau.transforms.DateTransform.ISO8601DT} 
transform associated with it through the
+        *      {@link BeanProperty#transform() @BeanProperty.transform()} 
annotation, the value being passed in must be
+        *      a String containing an ISO8601 date-time string value.
+        *
+        * <dl>
+        *      <dt>Example:</dt>
+        *      <dd>
+        * <p class='bcode'>
+        *      <jc>// Construct a bean with a 'birthDate' Date field</jc>
+        *      Person p = <jk>new</jk> Person();
+        *
+        *      <jc>// Create a bean context and add the ISO8601 date-time 
transform</jc>
+        *      BeanContext beanContext = <jk>new</jk> 
BeanContext().addTransform(DateTransform.ISO8601DT.<jk>class</jk>);
+        *
+        *      <jc>// Wrap our bean in a bean map</jc>
+        *      BeanMap&lt;Person&gt; b = beanContext.forBean(p);
+        *
+        *      <jc>// Set the field</jc>
+        *      myBeanMap.put(<js>"birthDate"</js>, 
<js>"'1901-03-03T04:05:06-5000'"</js>);
+        * </p>
+        *      </dd>
+        * </dl>
+        *
+        * @param property The name of the property to set.
+        * @param value The value to set the property to.
+        * @return If the bean context setting {@code 
beanMapPutReturnsOldValue} is <jk>true</jk>, then the old value of the property 
is returned.
+        *              Otherwise, this method always returns <jk>null</jk>.
+        * @throws RuntimeException if any of the following occur.
+        *      <ul class='spaced-list'>
+        *              <li>BeanMapEntry does not exist on the underlying 
object.
+        *              <li>Security settings prevent access to the underlying 
object setter method.
+        *              <li>An exception occurred inside the setter method.
+        *      </ul>
+        */
+       @Override /* Map */
+       public Object put(String property, Object value) {
+               BeanPropertyMeta<T> p = meta.properties.get(property);
+               if (p == null) {
+                       if (meta.ctx.ignoreUnknownBeanProperties)
+                               return null;
+                       if (property.equals("<uri>") && meta.uriProperty != 
null)
+                               return meta.uriProperty.set(this, value);
+
+                       // If this bean has subtypes, and we haven't set the 
subtype yet,
+                       // store the property in a temporary cache until the 
bean can be instantiated.
+                       // This eliminates the need for requiring that the sub 
type attribute be provided first.
+                       if (meta.subTypeIdProperty != null) {
+                               if (propertyCache == null)
+                                       propertyCache = new 
TreeMap<String,Object>();
+                               return propertyCache.put(property, value);
+                       }
+
+                       throw new BeanRuntimeException(meta.c, "Bean property 
''{0}'' not found.", property);
+               }
+               if (meta.transform != null)
+                       if (meta.transform.writeProperty(this.bean, property, 
value))
+                               return null;
+               return p.set(this, value);
+       }
+
+       /**
+        * Add a value to a collection or array property.
+        * <p>
+        *      As a general rule, adding to arrays is not recommended since 
the array must be recreate each time
+        *      this method is called.
+        *
+        * @param property Property name or child-element name (if {@link 
Xml#childName()} is specified).
+        * @param value The value to add to the collection or array.
+        */
+       public void add(String property, Object value) {
+               BeanPropertyMeta<T> p = meta.properties.get(property);
+               if (p == null) {
+                       if (meta.ctx.ignoreUnknownBeanProperties)
+                               return;
+                       throw new BeanRuntimeException(meta.c, "Bean property 
''{0}'' not found.", property);
+               }
+               p.add(this, value);
+       }
+
+
+       /**
+        * Gets a property on the bean.
+        * <p>
+        * If there is a {@link PojoTransform} associated with this bean 
property or bean property type class, then
+        *      this method will return the transformed value.
+        * For example, if the bean property type class is a {@link Date} and 
the bean property has the
+        *      {@link org.apache.juneau.transforms.DateTransform.ISO8601DT} 
transform associated with it through the
+        *      {@link BeanProperty#transform() @BeanProperty.transform()} 
annotation, this method will return a String
+        *      containing an ISO8601 date-time string value.
+        *
+        * <dl>
+        *      <dt>Example:</dt>
+        *      <dd>
+        * <p class='bcode'>
+        *      <jc>// Construct a bean with a 'birthDate' Date field</jc>
+        *      Person p = <jk>new</jk> Person();
+        *      p.setBirthDate(<jk>new</jk> Date(1, 2, 3, 4, 5, 6));
+        *
+        *      <jc>// Create a bean context and add the ISO8601 date-time 
transform</jc>
+        *      BeanContext beanContext = <jk>new</jk> 
BeanContext().addTransform(DateTransform.ISO8601DT.<jk>class</jk>);
+        *
+        *      <jc>// Wrap our bean in a bean map</jc>
+        *      BeanMap&lt;Person&gt; b = beanContext.forBean(p);
+        *
+        *      <jc>// Get the field as a string (i.e. 
"'1901-03-03T04:05:06-5000'")</jc>
+        *      String s = myBeanMap.get(<js>"birthDate"</js>);
+        * </p>
+        *      </dd>
+        * </dl>
+        *
+        * @param property The name of the property to get.
+        * @throws RuntimeException if any of the following occur.
+        *      <ol>
+        *              <li>BeanMapEntry does not exist on the underlying 
object.
+        *              <li>Security settings prevent access to the underlying 
object getter method.
+        *              <li>An exception occurred inside the getter method.
+        *      </ol>
+        */
+       @Override /* Map */
+       public Object get(Object property) {
+               BeanPropertyMeta<T> p = meta.properties.get(property);
+               if (p == null)
+                       return null;
+               if (meta.transform != null && property != null)
+                       return meta.transform.readProperty(this.bean, 
property.toString(), p.get(this));
+               return p.get(this);
+       }
+
+       /**
+        * Convenience method for setting multiple property values by passing 
in JSON (or other) text.
+        * <p>
+        *      Typically the input is going to be JSON, although the actual 
data type
+        *      depends on the default parser specified by the {@link 
BeanContext#BEAN_defaultParser} property
+        *      value on the config that created the context that created this 
map.
+        *
+        * <dl>
+        *      <dt>Example:</dt>
+        *      <dd>
+        * <p class='bcode'>
+        *      aPersonBean.load(<js>"{name:'John Smith',age:21}"</js>)
+        * </p>
+        *      </dd>
+        * </dl>
+        *
+        * @param input The text that will get parsed into a map and then added 
to this map.
+        * @return This object (for method chaining).
+        * @throws ParseException If the input contains a syntax error or is 
malformed.
+        */
+       public BeanMap<T> load(String input) throws ParseException {
+               putAll(new ObjectMap(input, this.meta.ctx.defaultParser));
+               return this;
+       }
+
+       /**
+        * Convenience method for setting multiple property values by passing 
in a reader.
+        *
+        * @param r The text that will get parsed into a map and then added to 
this map.
+        * @param p The parser to use to parse the text.
+        * @return This object (for method chaining).
+        * @throws ParseException If the input contains a syntax error or is 
malformed.
+        * @throws IOException Thrown by <code>Reader</code>.
+        */
+       public BeanMap<T> load(Reader r, ReaderParser p) throws ParseException, 
IOException {
+               putAll(new ObjectMap(r, p));
+               return this;
+       }
+
+       /**
+        * Convenience method for loading this map with the contents of the 
specified map.
+        * <p>
+        * Identical to {@link #putAll(Map)} except as a fluent-style method.
+        *
+        * @param entries The map containing the entries to add to this map.
+        * @return This object (for method chaining).
+        */
+       @SuppressWarnings({"unchecked","rawtypes"})
+       public BeanMap<T> load(Map entries) {
+               putAll(entries);
+               return this;
+       }
+
+       /**
+        * Returns the names of all properties associated with the bean.
+        * <p>
+        *      The returned set is unmodifiable.
+        */
+       @Override /* Map */
+       public Set<String> keySet() {
+               return meta.properties.keySet();
+       }
+
+       /**
+        * Returns the specified property on this bean map.
+        * <p>
+        *      Allows you to get and set an individual property on a bean 
without having a
+        *      handle to the bean itself by using the {@link 
BeanMapEntry#getValue()}
+        *      and {@link BeanMapEntry#setValue(Object)} methods.
+        * <p>
+        *      This method can also be used to get metadata on a property by
+        *      calling the {@link BeanMapEntry#getMeta()} method.
+        *
+        * @param propertyName The name of the property to look up.
+        * @return The bean property, or null if the bean has no such property.
+        */
+       public BeanMapEntry<T> getProperty(String propertyName) {
+               BeanPropertyMeta<T> p = meta.properties.get(propertyName);
+               if (p == null)
+                       return null;
+               return new BeanMapEntry<T>(this, p);
+       }
+
+       /**
+        * Returns the metadata on the specified property.
+        *
+        * @param propertyName The name of the bean property.
+        * @return Metadata on the specified property, or <jk>null</jk> if that 
property does not exist.
+        */
+       public BeanPropertyMeta<T> getPropertyMeta(String propertyName) {
+               return meta.properties.get(propertyName);
+       }
+
+       /**
+        * Returns the {@link ClassMeta} of the wrapped bean.
+        *
+        * @return The class type of the wrapped bean.
+        */
+       @Override /* Delagate */
+       public ClassMeta<T> getClassMeta() {
+               return this.meta.getClassMeta();
+       }
+
+       /**
+        * Invokes all the getters on this bean and return the values as a list 
of {@link BeanPropertyValue} objects.
+        * <p>
+        * This allows a snapshot of all values to be grabbed from a bean in 
one call.
+        *
+        * @param addClassAttr Add a <jk>"_class"</jk> bean property to the 
returned list.
+        * @param ignoreNulls Don't return properties whose values are null.
+        * @return The list of all bean property values.
+        */
+       public List<BeanPropertyValue> getValues(final boolean addClassAttr, 
final boolean ignoreNulls) {
+               Collection<BeanPropertyMeta<T>> properties = getProperties();
+               int capacity = (ignoreNulls && properties.size() > 10) ? 10 : 
properties.size() + (addClassAttr ? 1 : 0);
+               List<BeanPropertyValue> l = new 
ArrayList<BeanPropertyValue>(capacity);
+               if (addClassAttr)
+                       l.add(new BeanPropertyValue(meta.getClassProperty(), 
meta.c.getName(), null));
+               for (BeanPropertyMeta<T> bpm : properties) {
+                       try {
+                               Object val = bpm.get(this);
+                               if (val != null || ! ignoreNulls)
+                                       l.add(new BeanPropertyValue(bpm, val, 
null));
+                       } catch (Error e) {
+                               // Errors should always be uncaught.
+                               throw e;
+                       } catch (Throwable t) {
+                               l.add(new BeanPropertyValue(bpm, null, t));
+                       }
+               }
+               return l;
+       }
+
+       /**
+        * Returns a simple collection of properties for this bean map.
+        * @return A simple collection of properties for this bean map.
+        */
+       protected Collection<BeanPropertyMeta<T>> getProperties() {
+               return meta.properties.values();
+       }
+
+       /**
+        * Returns all the properties associated with the bean.
+        * @return A new set.
+        */
+       @Override
+       public Set<Entry<String,Object>> entrySet() {
+
+               // Construct our own anonymous set to implement this function.
+               Set<Entry<String,Object>> s = new 
AbstractSet<Entry<String,Object>>() {
+
+                       // Get the list of properties from the meta object.
+                       // Note that the HashMap.values() method caches 
results, so this collection
+                       // will really only be constructed once per bean type 
since the underlying
+                       // map never changes.
+                       final Collection<BeanPropertyMeta<T>> pSet = 
getProperties();
+
+                       @Override /* Set */
+                       public Iterator<java.util.Map.Entry<String, Object>> 
iterator() {
+
+                               // Construct our own anonymous iterator that 
uses iterators against the meta.properties
+                               // map to maintain position.  This prevents us 
from having to construct any of our own
+                               // collection objects.
+                               return new Iterator<Entry<String,Object>>() {
+
+                                       final Iterator<BeanPropertyMeta<T>> 
pIterator = pSet.iterator();
+
+                                       @Override /* Iterator */
+                                       public boolean hasNext() {
+                                               return pIterator.hasNext();
+                                       }
+
+                                       @Override /* Iterator */
+                                       public Map.Entry<String, Object> next() 
{
+                                               return new 
BeanMapEntry<T>(BeanMap.this, pIterator.next());
+                                       }
+
+                                       @Override /* Iterator */
+                                       public void remove() {
+                                               throw new 
UnsupportedOperationException("Cannot remove item from iterator.");
+                                       }
+                               };
+                       }
+
+                       @Override /* Set */
+                       public int size() {
+                               return pSet.size();
+                       }
+               };
+
+               return s;
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/BeanMapEntry.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMapEntry.java 
b/juneau-core/src/main/java/org/apache/juneau/BeanMapEntry.java
new file mode 100644
index 0000000..34aad38
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanMapEntry.java
@@ -0,0 +1,125 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau;
+
+import java.util.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.transform.*;
+
+/**
+ * Represents a single entry in a bean map.
+ * <p>
+ *     This class can be used to get and set property values on a bean, or to 
get metadata on a property.
+ *
+ * <h6 class='topic'>Examples</h6>
+ * <p class='bcode'>
+ *     <jc>// Construct a new bean</jc>
+ *     Person p = <jk>new</jk> Person();
+ *
+ *     <jc>// Wrap it in a bean map</jc>
+ *     BeanMap&lt;Person&gt; b = BeanContext.<jsf>DEFAULT</jsf>.forBean(p);
+ *
+ *     <jc>// Get a reference to the birthDate property</jc>
+ *     BeanMapEntry birthDate = b.getProperty(<js>"birthDate"</js>);
+ *
+ *     <jc>// Set the property value</jc>
+ *     birthDate.setValue(<jk>new</jk> Date(1, 2, 3, 4, 5, 6));
+ *
+ *     <jc>// Or if the DateTransform.DEFAULT_ISO8601DT is registered with the 
bean context, set a transformed value</jc>
+ *     birthDate.setValue(<js>"'1901-03-03T04:05:06-5000'"</js>);
+ * </p>
+ *
+ * @author James Bognar ([email protected])
+ *
+ * @param <T> The bean type.
+ */
+public class BeanMapEntry<T> implements Map.Entry<String,Object> {
+       private final BeanMap<T> beanMap;
+       private final BeanPropertyMeta<T> meta;
+
+       /**
+        * Constructor.
+        *
+        * @param beanMap The bean map that this entry belongs to.
+        * @param property The bean property.
+        */
+       protected BeanMapEntry(BeanMap<T> beanMap, BeanPropertyMeta<T> 
property) {
+               this.beanMap = beanMap;
+               this.meta = property;
+       }
+
+       @Override /* Map.Entry */
+       public String getKey() {
+               return meta.getName();
+       }
+
+       /**
+        * Returns the value of this property.
+        * <p>
+        * If there is a {@link PojoTransform} associated with this bean 
property or bean property type class, then
+        *      this method will return the transformed value.
+        * For example, if the bean property type class is a {@link Date} and 
the bean property has the
+        *      {@link org.apache.juneau.transforms.DateTransform.ISO8601DT} 
transform associated with it through the
+        *      {@link BeanProperty#transform() @BeanProperty.transform()} 
annotation, this method will return a String
+        *      containing an ISO8601 date-time string value.
+        */
+       @Override /* Map.Entry */
+       public Object getValue() {
+               return meta.get(this.beanMap);
+       }
+
+       /**
+        * Sets the value of this property.
+        * <p>
+        * If the property is an array of type {@code X}, then the value can be 
a {@code Collection<X>} or {@code X[]} or {@code Object[]}.
+        * <p>
+        * If the property is a bean type {@code X}, then the value can either 
be an {@code X} or a {@code Map}.
+        * <p>
+        * If there is a {@link PojoTransform} associated with this bean 
property or bean property type class, then
+        *      you must pass in a transformed value.
+        * For example, if the bean property type class is a {@link Date} and 
the bean property has the
+        *      {@link org.apache.juneau.transforms.DateTransform.ISO8601DT} 
transform associated with it through the
+        *      {@link BeanProperty#transform() @BeanProperty.transform()} 
annotation, the value being passed in must be
+        *      a String containing an ISO8601 date-time string value.
+        *
+        * @return  The set value after it's been converted.
+        */
+       @Override /* Map.Entry */
+       public Object setValue(Object value) {
+               return meta.set(this.beanMap, value);
+       }
+
+       /**
+        * Returns the bean map that contains this property.
+        *
+        * @return The bean map that contains this property.
+        */
+       public BeanMap<T> getBeanMap() {
+               return this.beanMap;
+       }
+
+       /**
+        * Returns the metadata about this bean property.
+        *
+        * @return Metadata about this bean property.
+        */
+       public BeanPropertyMeta<T> getMeta() {
+               return this.meta;
+       }
+
+       @Override /* Object */
+       public String toString() {
+               return this.getKey() + "=" + this.getValue();
+       }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java 
b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
new file mode 100644
index 0000000..8d4d631
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java
@@ -0,0 +1,767 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau;
+
+import static org.apache.juneau.Visibility.*;
+import static org.apache.juneau.internal.ClassUtils.*;
+
+import java.beans.*;
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.Map.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.html.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.jena.*;
+import org.apache.juneau.transform.*;
+import org.apache.juneau.xml.*;
+
+
+/**
+ * Encapsulates all access to the properties of a bean class (like a souped-up 
{@link java.beans.BeanInfo}).
+ *
+ *
+ * <h6 class='topic'>Description</h6>
+ * <p>
+ *     Uses introspection to find all the properties associated with this 
class.  If the {@link Bean @Bean} annotation
+ *     is present on the class, or the class has a {@link BeanTransform} 
registered with it in the bean context,
+ *     then that information is used to determine the properties on the class.
+ *     Otherwise, the {@code BeanInfo} functionality in Java is used to 
determine the properties on the class.
+ *
+ *
+ * <h6 class='topic'>Bean property ordering</h6>
+ * <p>
+ *     The order of the properties are as follows:
+ *     <ul class='spaced-list'>
+ *             <li>If {@link Bean @Bean} annotation is specified on class, 
then the order is the same as the list of properties in the annotation.
+ *             <li>If {@link Bean @Bean} annotation is not specified on the 
class, then the order is based on the following.
+ *                     <ul>
+ *                             <li>Public fields (same order as {@code 
Class.getFields()}).
+ *                             <li>Properties returned by {@code 
BeanInfo.getPropertyDescriptors()}.
+ *                             <li>Non-standard getters/setters with {@link 
BeanProperty @BeanProperty} annotation defined on them.
+ *                     </ul>
+ *     </ul>
+ *     <br>
+ *     The order can also be overridden through the use of an {@link 
BeanTransform}.
+ *
+ *
+ * @param <T> The class type that this metadata applies to.
+ * @author Barry M. Caceres
+ * @author James Bognar ([email protected])
+ */
+public class BeanMeta<T> {
+
+       /** The target class type that this meta object describes. */
+       protected ClassMeta<T> classMeta;
+
+       /** The target class that this meta object describes. */
+       protected Class<T> c;
+
+       /** The properties on the target class. */
+       protected Map<String,BeanPropertyMeta<T>> properties;
+
+       /** The getter properties on the target class. */
+       protected Map<Method,String> getterProps = new HashMap<Method,String>();
+
+       /** The setter properties on the target class. */
+       protected Map<Method,String> setterProps = new HashMap<Method,String>();
+
+       /** The bean context that created this metadata object. */
+       protected BeanContext ctx;
+
+       /** Optional bean transform associated with the target class. */
+       protected BeanTransform<? extends T> transform;
+
+       /** Type variables implemented by this bean. */
+       protected Map<Class<?>,Class<?>[]> typeVarImpls;
+
+       /** The constructor for this bean. */
+       protected Constructor<T> constructor;
+
+       /** For beans with constructors with BeanConstructor annotation, this 
is the list of constructor arg properties. */
+       protected String[] constructorArgs = new String[0];
+
+       /** XML-related metadata */
+       protected XmlBeanMeta<T> xmlMeta;
+
+       // Other fields
+       BeanPropertyMeta<T> uriProperty;                                 // The 
property identified as the URI for this bean (annotated with 
@BeanProperty.beanUri).
+       BeanPropertyMeta<T> subTypeIdProperty;                           // The 
property indentified as the sub type differentiator property (identified by 
@Bean.subTypeProperty annotation).
+       PropertyNamer propertyNamer;                                     // 
Class used for calculating bean property names.
+       BeanPropertyMeta<T> classProperty;                               // 
"_class" mock bean property.
+
+       BeanMeta() {}
+
+       /**
+        * Constructor.
+        *
+        * @param classMeta The target class.
+        * @param ctx The bean context that created this object.
+        * @param transform Optional bean transform associated with the target 
class.  Can be <jk>null</jk>.
+        */
+       protected BeanMeta(final ClassMeta<T> classMeta, BeanContext ctx, 
org.apache.juneau.transform.BeanTransform<? extends T> transform) {
+               this.classMeta = classMeta;
+               this.ctx = ctx;
+               this.transform = transform;
+               this.classProperty = new BeanPropertyMeta<T>(this, "_class", 
ctx.string());
+               this.c = classMeta.getInnerClass();
+       }
+
+       /**
+        * Returns the {@link ClassMeta} of this bean.
+        *
+        * @return The {@link ClassMeta} of this bean.
+        */
+       @BeanIgnore
+       public ClassMeta<T> getClassMeta() {
+               return classMeta;
+       }
+
+       /**
+        * Initializes this bean meta, and returns an error message if the 
specified class is not
+        * a bean for any reason.
+        *
+        * @return Reason why this class isn't a bean, or <jk>null</jk> if no 
problems detected.
+        * @throws BeanRuntimeException If unexpected error occurs such as 
invalid annotations on the bean class.
+        */
+       @SuppressWarnings("unchecked")
+       protected String init() throws BeanRuntimeException {
+
+               try {
+                       Visibility
+                               conVis = ctx.beanConstructorVisibility,
+                               cVis = ctx.beanClassVisibility,
+                               mVis = ctx.beanMethodVisibility,
+                               fVis = ctx.beanFieldVisibility;
+
+                       // If @Bean.interfaceClass is specified on the parent 
class, then we want
+                       // to use the properties defined on that class, not the 
subclass.
+                       Class<?> c2 = (transform != null && 
transform.getInterfaceClass() != null ? transform.getInterfaceClass() : c);
+
+                       Class<?> stopClass = (transform != null ? 
transform.getStopClass() : Object.class);
+                       if (stopClass == null)
+                               stopClass = Object.class;
+
+                       Map<String,BeanPropertyMeta<T>> normalProps = new 
LinkedHashMap<String,BeanPropertyMeta<T>>();
+
+                       /// See if this class matches one the patterns in the 
exclude-class list.
+                       if (ctx.isNotABean(c))
+                               return "Class matches exclude-class list";
+
+                       if (! cVis.isVisible(c.getModifiers()))
+                               return "Class is not public";
+
+                       if (c.isAnnotationPresent(BeanIgnore.class))
+                               return "Class is annotated with @BeanIgnore";
+
+                       // Make sure it's serializable.
+                       if (transform == null && ctx.beansRequireSerializable 
&& ! isParentClass(Serializable.class, c))
+                               return "Class is not serializable";
+
+                       // Look for @BeanConstructor constructor.
+                       for (Constructor<?> x : c.getConstructors()) {
+                               if 
(x.isAnnotationPresent(BeanConstructor.class)) {
+                                       if (constructor != null)
+                                               throw new 
BeanRuntimeException(c, "Multiple instances of '@BeanConstructor' found.");
+                                       constructor = (Constructor<T>)x;
+                                       constructorArgs = 
x.getAnnotation(BeanConstructor.class).properties();
+                                       if (constructorArgs.length != 
x.getParameterTypes().length)
+                                               throw new 
BeanRuntimeException(c, "Number of properties defined in '@BeanConstructor' 
annotation does not match number of parameters in constructor.");
+                                       if (! setAccessible(constructor))
+                                               throw new 
BeanRuntimeException(c, "Could not set accessibility to true on method with 
@BeanConstructor annotation.  Method=''{0}''", constructor.getName());
+                               }
+                       }
+
+                       // If this is an interface, look for impl classes 
defined in the context.
+                       if (constructor == null)
+                               constructor = 
(Constructor<T>)ctx.getImplClassConstructor(c, conVis);
+
+                       if (constructor == null)
+                               constructor = 
(Constructor<T>)ClassMeta.findNoArgConstructor(c, conVis);
+
+                       if (constructor == null && transform == null && 
ctx.beansRequireDefaultConstructor)
+                               return "Class does not have the required no-arg 
constructor";
+
+                       if (! setAccessible(constructor))
+                               throw new BeanRuntimeException(c, "Could not 
set accessibility to true on no-arg constructor");
+
+                       // Explicitly defined property names in @Bean 
annotation.
+                       Set<String> fixedBeanProps = new 
LinkedHashSet<String>();
+
+                       if (transform != null) {
+
+                               // Get the 'properties' attribute if specified.
+                               if (transform.getProperties() != null)
+                                       for (String p : 
transform.getProperties())
+                                               fixedBeanProps.add(p);
+
+                               if (transform.getPropertyNamer() != null)
+                                       propertyNamer = 
transform.getPropertyNamer().newInstance();
+                       }
+
+                       if (propertyNamer == null)
+                               propertyNamer = new PropertyNamerDefault();
+
+                       // First populate the properties with those specified 
in the bean annotation to
+                       // ensure that ordering first.
+                       for (String name : fixedBeanProps)
+                               normalProps.put(name, new 
BeanPropertyMeta<T>(this, name));
+
+                       if (ctx.useJavaBeanIntrospector) {
+                               BeanInfo bi = null;
+                               if (! c2.isInterface())
+                                       bi = Introspector.getBeanInfo(c2, 
stopClass);
+                               else
+                                       bi = Introspector.getBeanInfo(c2, null);
+                               if (bi != null) {
+                                       for (PropertyDescriptor pd : 
bi.getPropertyDescriptors()) {
+                                               String name = pd.getName();
+                                               if (! 
normalProps.containsKey(name))
+                                                       normalProps.put(name, 
new BeanPropertyMeta<T>(this, name));
+                                               
normalProps.get(name).setGetter(pd.getReadMethod()).setSetter(pd.getWriteMethod());
+                                       }
+                               }
+
+                       } else /* Use 'better' introspection */ {
+
+                               for (Field f : findBeanFields(c2, stopClass, 
fVis)) {
+                                       String name = findPropertyName(f, 
fixedBeanProps);
+                                       if (name != null) {
+                                               if (! 
normalProps.containsKey(name))
+                                                       normalProps.put(name, 
new BeanPropertyMeta<T>(this, name));
+                                               
normalProps.get(name).setField(f);
+                                       }
+                               }
+
+                               List<BeanMethod> bms = findBeanMethods(c2, 
stopClass, mVis, fixedBeanProps, propertyNamer);
+
+                               // Iterate through all the getters.
+                               for (BeanMethod bm : bms) {
+                                       String pn = bm.propertyName;
+                                       Method m = bm.method;
+                                       if (! normalProps.containsKey(pn))
+                                               normalProps.put(pn, new 
BeanPropertyMeta<T>(this, pn));
+                                       BeanPropertyMeta<?> bpm = 
normalProps.get(pn);
+                                       if (! bm.isSetter)
+                                               bpm.setGetter(m);
+                               }
+
+                               // Now iterate through all the setters.
+                               for (BeanMethod bm : bms) {
+                                       if (bm.isSetter) {
+                                               BeanPropertyMeta<?> bpm = 
normalProps.get(bm.propertyName);
+                                               if (bm.matchesPropertyType(bpm))
+                                                       
bpm.setSetter(bm.method);
+                                       }
+                               }
+                       }
+
+                       typeVarImpls = new HashMap<Class<?>,Class<?>[]>();
+                       findTypeVarImpls(c, typeVarImpls);
+                       if (typeVarImpls.isEmpty())
+                               typeVarImpls = null;
+
+                       // Eliminate invalid properties, and set the contents 
of getterProps and setterProps.
+                       for (Iterator<BeanPropertyMeta<T>> i = 
normalProps.values().iterator(); i.hasNext();) {
+                               BeanPropertyMeta<T> p = i.next();
+                               try {
+                                       if (p.validate()) {
+
+                                               if (p.getGetter() != null)
+                                                       
getterProps.put(p.getGetter(), p.getName());
+
+                                               if (p.getSetter() != null)
+                                                       
setterProps.put(p.getSetter(), p.getName());
+
+                                               if (p.isBeanUri())
+                                                       uriProperty = p;
+
+                                       } else {
+                                               i.remove();
+                                       }
+                               } catch (ClassNotFoundException e) {
+                                       throw new BeanRuntimeException(c, 
e.getLocalizedMessage());
+                               }
+                       }
+
+                       // Check for missing properties.
+                       for (String fp : fixedBeanProps)
+                               if (! normalProps.containsKey(fp))
+                                       throw new BeanRuntimeException(c, "The 
property ''{0}'' was defined on the @Bean(properties=X) annotation but was not 
found on the class definition.", fp);
+
+                       // Mark constructor arg properties.
+                       for (String fp : constructorArgs) {
+                               BeanPropertyMeta<T> m = normalProps.get(fp);
+                               if (m == null)
+                                       throw new BeanRuntimeException(c, "The 
property ''{0}'' was defined on the @BeanConstructor(properties=X) annotation 
but was not found on the class definition.", fp);
+                               m.setAsConstructorArg();
+                       }
+
+                       // Make sure at least one property was found.
+                       if (transform == null && ctx.beansRequireSomeProperties 
&& normalProps.size() == 0)
+                               return "No properties detected on bean class";
+
+                       boolean sortProperties = (ctx.sortProperties || 
(transform != null && transform.isSortProperties())) && 
fixedBeanProps.isEmpty();
+
+                       properties = sortProperties ? new 
TreeMap<String,BeanPropertyMeta<T>>() : new 
LinkedHashMap<String,BeanPropertyMeta<T>>();
+
+                       if (transform != null && transform.getSubTypeProperty() 
!= null) {
+                               String subTypeProperty = 
transform.getSubTypeProperty();
+                               this.subTypeIdProperty = new 
SubTypePropertyMeta(subTypeProperty, transform.getSubTypes(), 
normalProps.remove(subTypeProperty));
+                               properties.put(subTypeProperty, 
this.subTypeIdProperty);
+                       }
+
+                       properties.putAll(normalProps);
+
+                       // If a transform is defined, look for inclusion and 
exclusion lists.
+                       if (transform != null) {
+
+                               // Eliminated excluded properties if 
BeanTransform.excludeKeys is specified.
+                               String[] includeKeys = 
transform.getProperties();
+                               String[] excludeKeys = 
transform.getExcludeProperties();
+                               if (excludeKeys != null) {
+                                       for (String k : excludeKeys)
+                                               properties.remove(k);
+
+                               // Only include specified properties if 
BeanTransform.includeKeys is specified.
+                               // Note that the order must match includeKeys.
+                               } else if (includeKeys != null) {
+                                       Map<String,BeanPropertyMeta<T>> 
properties2 = new LinkedHashMap<String,BeanPropertyMeta<T>>();
+                                       for (String k : includeKeys) {
+                                               if (properties.containsKey(k))
+                                                       properties2.put(k, 
properties.get(k));
+                                       }
+                                       properties = properties2;
+                               }
+                       }
+
+                       xmlMeta = new XmlBeanMeta<T>(this, null);
+
+                       // We return this through the Bean.keySet() interface, 
so make sure it's not modifiable.
+                       properties = Collections.unmodifiableMap(properties);
+
+               } catch (BeanRuntimeException e) {
+                       throw e;
+               } catch (Exception e) {
+                       return "Exception:  " + StringUtils.getStackTrace(e);
+               }
+
+               return null;
+       }
+
+       /**
+        * Returns the subtype ID property of this bean if it has one.
+        * <p>
+        * The subtype id is specified using the {@link Bean#subTypeProperty()} 
annotation.
+        *
+        * @return The meta property for the sub type property, or 
<jk>null</jk> if no subtype is defined for this bean.
+        */
+       public BeanPropertyMeta<T> getSubTypeIdProperty() {
+               return subTypeIdProperty;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this bean has subtypes associated with it.
+        * Subtypes are defined using the {@link Bean#subTypes()} annotation.
+        *
+        * @return <jk>true</jk> if this bean has subtypes associated with it.
+        */
+       public boolean isSubTyped() {
+               return subTypeIdProperty != null;
+       }
+
+       /**
+        * Returns <jk>true</jk> if one of the properties on this bean is 
annotated with {@link BeanProperty#beanUri()} as <jk>true</jk>
+        *
+        * @return <jk>true</jk> if this bean has subtypes associated with it. 
<jk>true</jk> if there is a URI property associated with this bean.
+        */
+       public boolean hasBeanUriProperty() {
+               return uriProperty != null;
+       }
+
+       /**
+        * Returns the bean property marked as the URI for the bean (annotated 
with {@link BeanProperty#beanUri()} as <jk>true</jk>).
+        *
+        * @return The URI property, or <jk>null</jk> if no URI property exists 
on this bean.
+        */
+       public BeanPropertyMeta<T> getBeanUriProperty() {
+               return uriProperty;
+       }
+
+       /**
+        * Returns a mock bean property that resolves to the name 
<js>"_class"</js> and whose value always resolves
+        *      to the class name of the bean.
+        *
+        * @return The class name property.
+        */
+       public BeanPropertyMeta<T> getClassProperty() {
+               return classProperty;
+       }
+
+       /*
+        * Temporary getter/setter method struct.
+        */
+       private static class BeanMethod {
+               String propertyName;
+               boolean isSetter;
+               Method method;
+               Class<?> type;
+
+               BeanMethod(String propertyName, boolean isSetter, Method 
method) {
+                       this.propertyName = propertyName;
+                       this.isSetter = isSetter;
+                       this.method = method;
+                       if (isSetter)
+                               this.type = method.getParameterTypes()[0];
+                       else
+                               this.type = method.getReturnType();
+               }
+
+               /*
+                * Returns true if this method matches the class type of the 
specified property.
+                * Only meant to be used for setters.
+                */
+               boolean matchesPropertyType(BeanPropertyMeta<?> b) {
+                       if (b == null)
+                               return false;
+
+                       // Get the bean property type from the getter/field.
+                       Class<?> pt = null;
+                       if (b.getGetter() != null)
+                               pt = b.getGetter().getReturnType();
+                       else if (b.getField() != null)
+                               pt = b.getField().getType();
+
+                       // Doesn't match if no getter/field defined.
+                       if (pt == null)
+                               return false;
+
+                       // Doesn't match if not same type or super type as 
getter/field.
+                       if (! isParentClass(type, pt))
+                               return false;
+
+                       // If a setter was previously set, only use this setter 
if it's a closer
+                       // match (e.g. prev type is a superclass of this type).
+                       if (b.getSetter() == null)
+                               return true;
+
+                       Class<?> prevType = 
b.getSetter().getParameterTypes()[0];
+                       return isParentClass(prevType, type, true);
+               }
+
+               @Override /* Object */
+               public String toString() {
+                       return method.toString();
+               }
+       }
+
+       /*
+        * Find all the bean methods on this class.
+        *
+        * @param c The transformed class.
+        * @param stopClass Don't look above this class in the hierarchy.
+        * @param v The minimum method visibility.
+        * @param fixedBeanProps Only include methods whose properties are in 
this list.
+        * @param pn Use this property namer to determine property names from 
the method names.
+        */
+       private static List<BeanMethod> findBeanMethods(Class<?> c, Class<?> 
stopClass, Visibility v, Set<String> fixedBeanProps, PropertyNamer pn) {
+               List<BeanMethod> l = new LinkedList<BeanMethod>();
+
+               for (Class<?> c2 : findClasses(c, stopClass)) {
+                       for (Method m : c2.getDeclaredMethods()) {
+                               int mod = m.getModifiers();
+                               if (Modifier.isStatic(mod) || 
Modifier.isTransient(mod))
+                                       continue;
+                               if (m.isAnnotationPresent(BeanIgnore.class))
+                                       continue;
+                               if (m.isBridge())   // This eliminates methods 
with covariant return types from parent classes on child classes.
+                                       continue;
+                               if (! (v.isVisible(m) || 
m.isAnnotationPresent(BeanProperty.class)))
+                                       continue;
+                               String n = m.getName();
+                               Class<?>[] pt = m.getParameterTypes();
+                               Class<?> rt = m.getReturnType();
+                               boolean isGetter = false, isSetter = false;
+                               if (pt.length == 1 && n.startsWith("set") && 
(isParentClass(rt, c) || rt.equals(Void.TYPE))) {
+                                       isSetter = true;
+                                       n = n.substring(3);
+                               } else if (pt.length == 0 && 
n.startsWith("get") && (! rt.equals(Void.TYPE))) {
+                                       isGetter = true;
+                                       n = n.substring(3);
+                               } else if (pt.length == 0 && n.startsWith("is") 
&& (rt.equals(Boolean.TYPE) || rt.equals(Boolean.class))) {
+                                       isGetter = true;
+                                       n = n.substring(2);
+                               }
+                               n = pn.getPropertyName(n);
+                               if (isGetter || isSetter) {
+                                       BeanProperty bp = 
m.getAnnotation(BeanProperty.class);
+                                       if (bp != null && ! 
bp.name().equals("")) {
+                                               n = bp.name();
+                                               if (! fixedBeanProps.isEmpty())
+                                                       if (! 
fixedBeanProps.contains(n))
+                                                               throw new 
BeanRuntimeException(c, "Method property ''{0}'' identified in @BeanProperty, 
but missing from @Bean", n);
+                                       }
+                                       l.add(new BeanMethod(n, isSetter, m));
+                               }
+                       }
+               }
+               return l;
+       }
+
+       private static Collection<Field> findBeanFields(Class<?> c, Class<?> 
stopClass, Visibility v) {
+               List<Field> l = new LinkedList<Field>();
+               for (Class<?> c2 : findClasses(c, stopClass)) {
+                       for (Field f : c2.getDeclaredFields()) {
+                               int m = f.getModifiers();
+                               if (Modifier.isStatic(m) || 
Modifier.isTransient(m))
+                                       continue;
+                               if (f.isAnnotationPresent(BeanIgnore.class))
+                                       continue;
+                               if (! (v.isVisible(f) || 
f.isAnnotationPresent(BeanProperty.class)))
+                                       continue;
+                               l.add(f);
+                       }
+               }
+               return l;
+       }
+
+       private static List<Class<?>> findClasses(Class<?> c, Class<?> 
stopClass) {
+               LinkedList<Class<?>> l = new LinkedList<Class<?>>();
+               findClasses(c, l, stopClass);
+               return l;
+       }
+
+       private static void findClasses(Class<?> c, LinkedList<Class<?>> l, 
Class<?> stopClass) {
+               while (c != null && stopClass != c) {
+                       l.addFirst(c);
+                       for (Class<?> ci : c.getInterfaces())
+                               findClasses(ci, l, stopClass);
+                       c = c.getSuperclass();
+               }
+       }
+
+       /**
+        * Returns the metadata on all properties associated with this bean.
+        *
+        * @return Metadata on all properties associated with this bean.
+        */
+       public Collection<BeanPropertyMeta<T>> getPropertyMetas() {
+               return this.properties.values();
+       }
+
+       /**
+        * Returns the metadata on the specified list of properties.
+        *
+        * @param pNames The list of properties to retrieve.  If <jk>null</jk>, 
returns all properties.
+        * @return The metadata on the specified list of properties.
+        */
+       public Collection<BeanPropertyMeta<T>> getPropertyMetas(final 
String...pNames) {
+               if (pNames == null)
+                       return getPropertyMetas();
+               List<BeanPropertyMeta<T>> l = new 
ArrayList<BeanPropertyMeta<T>>(pNames.length);
+               for (int i = 0; i < pNames.length; i++)
+                       l.add(getPropertyMeta(pNames[i]));
+               return l;
+       }
+
+       /**
+        * Returns XML related metadata for this bean type.
+        *
+        * @return The XML metadata for this bean type.
+        */
+       public XmlBeanMeta<T> getXmlMeta() {
+               return xmlMeta;
+       }
+
+       /**
+        * Returns metadata about the specified property.
+        *
+        * @param name The name of the property on this bean.
+        * @return The metadata about the property, or <jk>null</jk> if no such 
property exists
+        *      on this bean.
+        */
+       public BeanPropertyMeta<T> getPropertyMeta(String name) {
+               return this.properties.get(name);
+       }
+
+       /**
+        * Creates a new instance of this bean.
+        *
+        * @param outer The outer object if bean class is a non-static inner 
member class.
+        * @return A new instance of this bean if possible, or <jk>null</jk> if 
not.
+        * @throws IllegalArgumentException Thrown by constructor.
+        * @throws InstantiationException Thrown by constructor.
+        * @throws IllegalAccessException Thrown by constructor.
+        * @throws InvocationTargetException Thrown by constructor.
+        */
+       @SuppressWarnings("unchecked")
+       protected T newBean(Object outer) throws IllegalArgumentException, 
InstantiationException, IllegalAccessException, InvocationTargetException {
+               if (classMeta.isMemberClass) {
+                       if (constructor != null)
+                               return constructor.newInstance(outer);
+               } else {
+                       if (constructor != null)
+                               return constructor.newInstance((Object[])null);
+                       InvocationHandler h = 
classMeta.getProxyInvocationHandler();
+                       if (h != null) {
+                               ClassLoader cl = 
classMeta.beanContext.classLoader;
+                               if (cl == null)
+                                       cl = this.getClass().getClassLoader();
+                               return (T)Proxy.newProxyInstance(cl, new 
Class[] { classMeta.innerClass, java.io.Serializable.class }, h);
+                       }
+               }
+               return null;
+       }
+
+       /*
+        * Returns the property name of the specified field if it's a valid 
property.
+        * Returns null if the field isn't a valid property.
+        */
+       private String findPropertyName(Field f, Set<String> fixedBeanProps) {
+               BeanProperty bp = f.getAnnotation(BeanProperty.class);
+               if (bp != null && ! bp.name().equals("")) {
+                       String name = bp.name();
+                       if (fixedBeanProps.isEmpty() || 
fixedBeanProps.contains(name))
+                               return name;
+                       throw new BeanRuntimeException(c, "Method property 
''{0}'' identified in @BeanProperty, but missing from @Bean", name);
+               }
+               String name = propertyNamer.getPropertyName(f.getName());
+               if (fixedBeanProps.isEmpty() || fixedBeanProps.contains(name))
+                       return name;
+               return null;
+       }
+
+       /**
+        * Recursively determines the classes represented by parameterized 
types in the class hierarchy of
+        * the specified type, and puts the results in the specified map.<br>
+        * <p>
+        *      For example, given the following classes...
+        * <p class='bcode'>
+        *      public static class BeanA&lt;T> {
+        *              public T x;
+        *      }
+        *      public static class BeanB extends BeanA&lt;Integer>} {...}
+        * <p>
+        *      ...calling this method on {@code BeanB.class} will load the 
following data into {@code m} indicating
+        *      that the {@code T} parameter on the BeanA class is implemented 
with an {@code Integer}:
+        * <p class='bcode'>
+        *      {BeanA.class:[Integer.class]}
+        * <p>
+        *      TODO:  This code doesn't currently properly handle the 
following situation:
+        * <p class='bcode'>
+        *      public static class BeanB&ltT extends Number> extends 
BeanA&ltT>;
+        *      public static class BeanC extends BeanB&ltInteger>;
+        * <p>
+        *      When called on {@code BeanC}, the variable will be detected as 
a {@code Number}, not an {@code Integer}.<br>
+        *      If anyone can figure out a better way of doing this, please do 
so!
+        *
+        * @param t The type we're recursing.
+        * @param m Where the results are loaded.
+        */
+       private static void findTypeVarImpls(Type t, Map<Class<?>,Class<?>[]> 
m) {
+               if (t instanceof Class) {
+                       Class<?> c = (Class<?>)t;
+                       findTypeVarImpls(c.getGenericSuperclass(), m);
+                       for (Type ci : c.getGenericInterfaces())
+                               findTypeVarImpls(ci, m);
+               } else if (t instanceof ParameterizedType) {
+                       ParameterizedType pt = (ParameterizedType)t;
+                       Type rt = pt.getRawType();
+                       if (rt instanceof Class) {
+                               Type[] gImpls = pt.getActualTypeArguments();
+                               Class<?>[] gTypes = new Class[gImpls.length];
+                               for (int i = 0; i < gImpls.length; i++) {
+                                       Type gt = gImpls[i];
+                                       if (gt instanceof Class)
+                                               gTypes[i] = (Class<?>)gt;
+                                       else if (gt instanceof TypeVariable) {
+                                               TypeVariable<?> tv = 
(TypeVariable<?>)gt;
+                                               for (Type upperBound : 
tv.getBounds())
+                                                       if (upperBound 
instanceof Class)
+                                                               gTypes[i] = 
(Class<?>)upperBound;
+                                       }
+                               }
+                               m.put((Class<?>)rt, gTypes);
+                               findTypeVarImpls(pt.getRawType(), m);
+                       }
+               }
+       }
+
+       /*
+        * Bean property for getting and setting bean subtype.
+        */
+       @SuppressWarnings({"rawtypes","unchecked"})
+       private class SubTypePropertyMeta extends BeanPropertyMeta<T> {
+
+               private Map<Class<?>,String> subTypes;
+               private BeanPropertyMeta<T> realProperty;  // Bean property if 
bean actually has a real subtype field.
+
+               SubTypePropertyMeta(String subTypeAttr, Map<Class<?>,String> 
subTypes, BeanPropertyMeta<T> realProperty) {
+                       super(BeanMeta.this, subTypeAttr, ctx.string());
+                       this.subTypes = subTypes;
+                       this.realProperty = realProperty;
+                       this.htmlMeta = new HtmlBeanPropertyMeta<T>(this);
+                       this.xmlMeta = new XmlBeanPropertyMeta<T>(this);
+                       this.rdfMeta = new RdfBeanPropertyMeta<T>(this);
+               }
+
+               /*
+                * Setting this bean property causes the inner bean to be set 
to the subtype implementation.
+                */
+               @Override /* BeanPropertyMeta */
+               public Object set(BeanMap<T> m, Object value) throws 
BeanRuntimeException {
+                       if (value == null)
+                               throw new BeanRuntimeException("Attempting to 
set bean subtype property to null.");
+                       String subTypeId = value.toString();
+                       for (Entry<Class<?>,String> e : subTypes.entrySet()) {
+                               if (e.getValue().equals(subTypeId)) {
+                                       Class subTypeClass = e.getKey();
+                                       m.meta = ctx.getBeanMeta(subTypeClass);
+                                       try {
+                                               m.bean = 
(T)subTypeClass.newInstance();
+                                               if (realProperty != null)
+                                                       realProperty.set(m, 
value);
+                                               // If subtype attribute wasn't 
specified first, set them again from the temporary cache.
+                                               if (m.propertyCache != null)
+                                                       for 
(Map.Entry<String,Object> me : m.propertyCache.entrySet())
+                                                               
m.put(me.getKey(), me.getValue());
+                                       } catch (Exception e1) {
+                                               throw new 
BeanRuntimeException(e1);
+                                       }
+                                       return null;
+                               }
+                       }
+                       throw new BeanRuntimeException(c, "Unknown subtype ID 
''{0}''", subTypeId);
+               }
+
+               @Override /* BeanPropertyMeta */
+               public Object get(BeanMap<T> m) throws BeanRuntimeException {
+                       String subTypeId = transform.getSubTypes().get(c);
+                       if (subTypeId == null)
+                               throw new BeanRuntimeException(c, "Unmapped sub 
type class");
+                       return subTypeId;
+               }
+       }
+
+       @Override /* Object */
+       public String toString() {
+               StringBuilder sb = new StringBuilder(c.getName());
+               sb.append(" {\n");
+               for (BeanPropertyMeta<?> pm : this.properties.values())
+                       sb.append('\t').append(pm.toString()).append(",\n");
+               sb.append('}');
+               return sb.toString();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/BeanMetaFiltered.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMetaFiltered.java 
b/juneau-core/src/main/java/org/apache/juneau/BeanMetaFiltered.java
new file mode 100644
index 0000000..09b0d0d
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanMetaFiltered.java
@@ -0,0 +1,74 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau;
+
+import java.util.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.xml.*;
+
+/**
+ * Sames as {@link BeanMeta}, except the list of bean properties are limited
+ * by a {@link BeanProperty#properties()} annotation.
+ *
+ * @param <T> The class type that this metadata applies to.
+ * @author James Bognar ([email protected])
+ */
+public final class BeanMetaFiltered<T> extends BeanMeta<T> {
+
+       private final BeanMeta<T> innerMeta;
+
+       /**
+        * Wrapper constructor.
+        *
+        * @param innerMeta The untransformed bean meta of the bean property.
+        * @param pNames The list of transformed property names.
+        */
+       public BeanMetaFiltered(BeanMeta<T> innerMeta, String[] pNames) {
+               this.innerMeta = innerMeta;
+               this.properties = new 
LinkedHashMap<String,BeanPropertyMeta<T>>();
+               for (String p : pNames)
+                       properties.put(p, innerMeta.getPropertyMeta(p));
+               this.xmlMeta = new XmlBeanMeta<T>(innerMeta, pNames);
+       }
+
+       /**
+        * Wrapper constructor.
+        *
+        * @param innerMeta The untransformed bean meta of the bean property.
+        * @param pNames The list of transformed property names.
+        */
+       public BeanMetaFiltered(BeanMeta<T> innerMeta, Collection<String> 
pNames) {
+               this(innerMeta, pNames.toArray(new String[pNames.size()]));
+       }
+
+       @Override /* Delagate */
+       public ClassMeta<T> getClassMeta() {
+               return innerMeta.classMeta;
+       }
+
+       @Override /* BeanMeta */
+       public Collection<BeanPropertyMeta<T>> getPropertyMetas() {
+               return properties.values();
+       }
+
+       @Override /* BeanMeta */
+       public BeanPropertyMeta<T> getPropertyMeta(String name) {
+               return properties.get(name);
+       }
+
+       @Override /* Object */
+       public String toString() {
+               return innerMeta.c.getName();
+       }
+}

http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java 
b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
new file mode 100644
index 0000000..48332cf
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/BeanPropertyMeta.java
@@ -0,0 +1,813 @@
+/***************************************************************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information regarding copyright 
ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the "License"); you may not 
use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 
or implied.  See the License for the
+ * specific language governing permissions and limitations under the License.
+ 
***************************************************************************************************************************/
+package org.apache.juneau;
+
+import static org.apache.juneau.Visibility.*;
+import static org.apache.juneau.internal.ClassUtils.*;
+import static org.apache.juneau.internal.CollectionUtils.*;
+import static org.apache.juneau.internal.ReflectionUtils.*;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.net.URI;
+import java.util.*;
+
+import org.apache.juneau.annotation.*;
+import org.apache.juneau.html.*;
+import org.apache.juneau.internal.*;
+import org.apache.juneau.jena.*;
+import org.apache.juneau.parser.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.transform.*;
+import org.apache.juneau.xml.*;
+
+/**
+ * Contains metadata about a bean property.
+ * <p>
+ *     Contains information such as type of property (e.g. 
field/getter/setter), class type of property value,
+ *     and whether any transforms are associated with this property.
+ * <p>
+ *     Developers will typically not need access to this class.  The 
information provided by it is already
+ *     exposed through several methods on the {@link BeanMap} API.
+ *
+ * @param <T> The class type of the bean that this metadata applies to.
+ * @author James Bognar ([email protected])
+ */
+@SuppressWarnings({ "rawtypes", "unchecked" })
+public class BeanPropertyMeta<T> {
+
+       private Field field;
+       private Method getter, setter;
+       private boolean isConstructorArg, isBeanUri, isUri;
+
+       private final BeanMeta<T> beanMeta;
+
+       private String name;
+       private ClassMeta<?>
+               rawTypeMeta,                           // The real class type 
of the bean property.
+               typeMeta;                              // The transformed class 
type of the bean property.
+       private String[] properties;
+       private PojoTransform transform;      // PojoTransform defined only via 
@BeanProperty annotation.
+
+       /** HTML related metadata on this bean property. */
+       protected HtmlBeanPropertyMeta<T> htmlMeta;
+
+       /** XML related metadata on this bean property. */
+       protected XmlBeanPropertyMeta<T> xmlMeta;
+
+       /** RDF related metadata on this bean property. */
+       protected RdfBeanPropertyMeta<T> rdfMeta;  //
+
+       /**
+        * Constructor.
+        *
+        * @param beanMeta The metadata of the bean containing this property.
+        * @param name This property name.
+        */
+       protected BeanPropertyMeta(BeanMeta<T> beanMeta, String name) {
+               this.beanMeta = beanMeta;
+               this.name = name;
+       }
+
+       BeanPropertyMeta(BeanMeta<T> beanMeta, String name, ClassMeta<?> 
rawTypeMeta) {
+               this(beanMeta, name);
+               this.rawTypeMeta = rawTypeMeta;
+       }
+
+       BeanPropertyMeta(BeanMeta<T> beanMeta, String name, Method getter, 
Method setter) {
+               this(beanMeta, name);
+               setGetter(getter);
+               setSetter(setter);
+       }
+
+       /**
+        * Returns the name of this bean property.
+        *
+        * @return The name of the bean property.
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * Returns the bean meta that this property belongs to.
+        *
+        * @return The bean meta that this property belongs to.
+        */
+       @BeanIgnore
+       public BeanMeta<T> getBeanMeta() {
+               return beanMeta;
+       }
+
+       /**
+        * Returns the getter method for this property.
+        *
+        * @return The getter method for this bean property, or <jk>null</jk> 
if there is no getter method.
+        */
+       public Method getGetter() {
+               return getter;
+       }
+
+       /**
+        * Returns the setter method for this property.
+        *
+        * @return The setter method for this bean property, or <jk>null</jk> 
if there is no setter method.
+        */
+       public Method getSetter() {
+               return setter;
+       }
+
+       /**
+        * Returns the field for this property.
+        *
+        * @return The field for this bean property, or <jk>null</jk> if there 
is no field associated with this bean property.
+        */
+       public Field getField() {
+               return field;
+       }
+
+       /**
+        * Returns the {@link ClassMeta} of the class of this property.
+        * <p>
+        * If this property or the property type class has a {@link 
PojoTransform} associated with it, this
+        *      method returns the transformed class meta.
+        * This matches the class type that is used by the {@link 
#get(BeanMap)} and {@link #set(BeanMap, Object)} methods.
+        *
+        * @return The {@link ClassMeta} of the class of this property.
+        */
+       public ClassMeta<?> getClassMeta() {
+               if (typeMeta == null)
+                       typeMeta = (transform != null ? 
transform.getTransformedClassMeta() : rawTypeMeta == null ? 
beanMeta.ctx.object() : rawTypeMeta.getTransformedClassMeta());
+               return typeMeta;
+       }
+
+       /**
+        * Sets the getter method for this property.
+        *
+        * @param getter The getter method to associate with this property.
+        * @return This object (for method chaining).
+        */
+       BeanPropertyMeta<T> setGetter(Method getter) {
+               setAccessible(getter);
+               this.getter = getter;
+               return this;
+       }
+
+       /**
+        * Sets the setter method for this property.
+        *
+        * @param setter The setter method to associate with this property.
+        * @return This object (for method chaining).
+        */
+       BeanPropertyMeta<T> setSetter(Method setter) {
+               setAccessible(setter);
+               this.setter = setter;
+               return this;
+       }
+
+       /**
+        * Sets the field for this property.
+        *
+        * @param field The field to associate with this property.
+        * @return This object (for method chaining).
+        */
+       BeanPropertyMeta<T> setField(Field field) {
+               setAccessible(field);
+               this.field = field;
+               return this;
+       }
+
+       /**
+        * Marks this property as only settable through a constructor arg.
+        *
+        * @return This object (for method chaining).
+        */
+       BeanPropertyMeta<T> setAsConstructorArg() {
+               this.isConstructorArg = true;
+               return this;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this bean property is marked with {@link 
BeanProperty#beanUri()} as <jk>true</jk>.
+        *
+        * @return <jk>true</jk> if this bean property is marked with {@link 
BeanProperty#beanUri()} as <jk>true</jk>.
+        */
+       public boolean isBeanUri() {
+               return isBeanUri;
+       }
+
+       /**
+        * Returns <jk>true</jk> if this bean property is a URI.
+        * <p>
+        * A bean property can be considered a URI if any of the following are 
true:
+        * <ul class='spaced-list'>
+        *      <li>Property class type is {@link URL} or {@link URI}.
+        *      <li>Property class type is annotated with {@link 
org.apache.juneau.annotation.URI}.
+        *      <li>Property getter, setter, or field is annotated with {@link 
org.apache.juneau.annotation.URI}.
+        * </ul>
+        *
+        * @return <jk>true</jk> if this bean property is a URI.
+        */
+       public boolean isUri() {
+               return isUri;
+       }
+
+       /**
+        * Returns the override list of properties defined through a {@link 
BeanProperty#properties()} annotation
+        *  on this property.
+        *
+        * @return The list of override properties, or <jk>null</jk> if 
annotation not specified.
+        */
+       public String[] getProperties() {
+               return properties;
+       }
+
+       /**
+        * Returns the HTML-related metadata on this bean property.
+        *
+        * @return The HTML-related metadata on this bean property.  Never 
<jk>null</jk>/.
+        */
+       public HtmlBeanPropertyMeta<T> getHtmlMeta() {
+               return htmlMeta;
+       }
+
+       /**
+        * Returns the XML-related metadata on this bean property.
+        *
+        * @return The XML-related metadata on this bean property.  Never 
<jk>null</jk>/.
+        */
+       public XmlBeanPropertyMeta<T> getXmlMeta() {
+               return xmlMeta;
+       }
+
+       /**
+        * Returns the RDF-related metadata on this bean property.
+        *
+        * @return The RDF-related metadata on this bean property.  Never 
<jk>null</jk>/.
+        */
+       public RdfBeanPropertyMeta<T> getRdfMeta() {
+               return rdfMeta;
+       }
+
+       boolean validate() throws Exception {
+
+               BeanContext f = beanMeta.ctx;
+               Map<Class<?>,Class<?>[]> typeVarImpls = beanMeta.typeVarImpls;
+
+               if (field == null && getter == null)
+                       return false;
+
+               if (field == null && setter == null && 
f.beansRequireSettersForGetters && ! isConstructorArg)
+                       return false;
+
+               if (field != null) {
+                       BeanProperty p = 
field.getAnnotation(BeanProperty.class);
+                       rawTypeMeta = f.getClassMeta(p, field.getGenericType(), 
typeVarImpls);
+                       isUri |= (rawTypeMeta.isUri() || 
field.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
+                       if (p != null) {
+                               transform = getPropertyPojoTransform(p);
+                               if (p.properties().length != 0)
+                                       properties = p.properties();
+                               isBeanUri |= p.beanUri();
+                       }
+               }
+
+               if (getter != null) {
+                       BeanProperty p = 
getter.getAnnotation(BeanProperty.class);
+                       if (rawTypeMeta == null)
+                               rawTypeMeta = f.getClassMeta(p, 
getter.getGenericReturnType(), typeVarImpls);
+                       isUri |= (rawTypeMeta.isUri() || 
getter.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
+                       if (p != null) {
+                               if (transform == null)
+                                       transform = getPropertyPojoTransform(p);
+                               if (properties != null && p.properties().length 
!= 0)
+                                       properties = p.properties();
+                               isBeanUri |= p.beanUri();
+                       }
+               }
+
+               if (setter != null) {
+                       BeanProperty p = 
setter.getAnnotation(BeanProperty.class);
+                       if (rawTypeMeta == null)
+                               rawTypeMeta = f.getClassMeta(p, 
setter.getGenericParameterTypes()[0], typeVarImpls);
+                       isUri |= (rawTypeMeta.isUri() || 
setter.isAnnotationPresent(org.apache.juneau.annotation.URI.class));
+                       if (p != null) {
+                       if (transform == null)
+                               transform = getPropertyPojoTransform(p);
+                               if (properties != null && p.properties().length 
!= 0)
+                                       properties = p.properties();
+                               isBeanUri |= p.beanUri();
+                       }
+               }
+
+               if (rawTypeMeta == null)
+                       return false;
+
+               // Do some annotation validation.
+               Class<?> c = rawTypeMeta.getInnerClass();
+               if (getter != null && ! isParentClass(getter.getReturnType(), 
c))
+                       return false;
+               if (setter != null && ! 
isParentClass(setter.getParameterTypes()[0], c))
+                       return false;
+               if (field != null && ! isParentClass(field.getType(), c))
+                       return false;
+
+               htmlMeta = new HtmlBeanPropertyMeta(this);
+               xmlMeta = new XmlBeanPropertyMeta(this);
+               rdfMeta = new RdfBeanPropertyMeta(this);
+
+               return true;
+       }
+
+       private PojoTransform getPropertyPojoTransform(BeanProperty p) throws 
Exception {
+               Class<? extends PojoTransform> c = p.transform();
+               if (c == PojoTransform.NULL.class)
+                       return null;
+               try {
+                       PojoTransform f = c.newInstance();
+                       f.setBeanContext(this.beanMeta.ctx);
+                       return f;
+               } catch (Exception e) {
+                       throw new BeanRuntimeException(this.beanMeta.c, "Could 
not instantiate PojoTransform ''{0}'' for bean property ''{1}''", c.getName(), 
this.name).initCause(e);
+               }
+       }
+
+       /**
+        * Equivalent to calling {@link BeanMap#get(Object)}, but is faster 
since it avoids looking up the property meta.
+        *
+        * @param m The bean map to get the transformed value from.
+        * @return The property value.
+        */
+       public Object get(BeanMap<T> m) {
+               try {
+                       // Read-only beans have their properties stored in a 
cache until getBean() is called.
+                       Object bean = m.bean;
+                       if (bean == null)
+                               return m.propertyCache.get(name);
+
+                       Object o = null;
+
+                       if (getter == null && field == null)
+                               throw new BeanRuntimeException(beanMeta.c, 
"Getter or public field not defined on property ''{0}''", name);
+
+                       if (getter != null)
+                               o = getter.invoke(bean, (Object[])null);
+
+                       else if (field != null)
+                               o = field.get(bean);
+
+                       o = transform(o);
+                       if (o == null)
+                               return null;
+                       if (properties != null) {
+                               if (rawTypeMeta.isArray()) {
+                                       Object[] a = (Object[])o;
+                                       List l = new ArrayList(a.length);
+                                       ClassMeta childType = 
rawTypeMeta.getElementType();
+                                       for (Object c : a)
+                                               
l.add(applyChildPropertiesFilter(childType, c));
+                                       return l;
+                               } else if (rawTypeMeta.isCollection()) {
+                                       Collection c = (Collection)o;
+                                       List l = new ArrayList(c.size());
+                                       ClassMeta childType = 
rawTypeMeta.getElementType();
+                                       for (Object cc : c)
+                                               
l.add(applyChildPropertiesFilter(childType, cc));
+                                       return l;
+                               } else {
+                                       return 
applyChildPropertiesFilter(rawTypeMeta, o);
+                               }
+                       }
+                       return o;
+               } catch (SerializeException e) {
+                       throw new BeanRuntimeException(e);
+               } catch (Throwable e) {
+                       if (beanMeta.ctx.ignoreInvocationExceptionsOnGetters) {
+                               if (rawTypeMeta.isPrimitive())
+                                       return 
rawTypeMeta.getPrimitiveDefault();
+                               return null;
+                       }
+                       throw new BeanRuntimeException(beanMeta.c, "Exception 
occurred while getting property ''{0}''", name).initCause(e);
+               }
+       }
+
+       /**
+        * Equivalent to calling {@link BeanMap#put(String, Object)}, but is 
faster since it avoids
+        *      looking up the property meta.
+        *
+        * @param m The bean map to set the property value on.
+        * @param value The value to set.
+        * @return The previous property value.
+        * @throws BeanRuntimeException If property could not be set.
+        */
+       public Object set(BeanMap<T> m, Object value) throws 
BeanRuntimeException {
+               try {
+                       // Comvert to raw form.
+                       value = normalize(value);
+                       BeanContext bc = this.beanMeta.ctx;
+
+               if (m.bean == null) {
+
+                       // If this bean has subtypes, and we haven't set the 
subtype yet,
+                       // store the property in a temporary cache until the 
bean can be instantiated.
+                       if (m.meta.subTypeIdProperty != null && m.propertyCache 
== null)
+                               m.propertyCache = new TreeMap<String,Object>();
+
+                       // Read-only beans get their properties stored in a 
cache.
+                       if (m.propertyCache != null)
+                               return m.propertyCache.put(name, value);
+
+                       throw new BeanRuntimeException("Non-existent bean 
instance on bean.");
+               }
+
+                       boolean isMap = rawTypeMeta.isMap();
+                       boolean isCollection = rawTypeMeta.isCollection();
+
+               if (field == null && setter == null && ! (isMap || 
isCollection)) {
+                       if ((value == null && 
bc.ignoreUnknownNullBeanProperties) || bc.ignorePropertiesWithoutSetters)
+                               return null;
+                       throw new BeanRuntimeException(beanMeta.c, "Setter or 
public field not defined on property ''{0}''", name);
+               }
+
+               Object bean = m.getBean(true);  // Don't use getBean() because 
it triggers array creation!
+
+               try {
+
+                       Object r = beanMeta.ctx.beanMapPutReturnsOldValue || 
isMap || isCollection ? get(m) : null;
+                               Class<?> propertyClass = 
rawTypeMeta.getInnerClass();
+
+                       if (value == null && (isMap || isCollection)) {
+                               if (setter != null) {
+                                       setter.invoke(bean, new Object[] { null 
});
+                                       return r;
+                               } else if (field != null) {
+                                       field.set(bean, null);
+                                       return r;
+                               }
+                               throw new BeanRuntimeException(beanMeta.c, 
"Cannot set property ''{0}'' to null because no setter or public field is 
defined", name);
+                       }
+
+                       if (isMap) {
+
+                               if (! (value instanceof Map)) {
+                                       if (value instanceof CharSequence)
+                                               value = new 
ObjectMap((CharSequence)value).setBeanContext(beanMeta.ctx);
+                                       else
+                                               throw new 
BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' 
to object of type ''{2}''", name, propertyClass.getName(), 
findClassName(value));
+                               }
+
+                               Map valueMap = (Map)value;
+                               Map propMap = (Map)r;
+                                       ClassMeta<?> valueType = 
rawTypeMeta.getValueType();
+
+                               // If the property type is abstract, then we 
either need to reuse the existing
+                               // map (if it's not null), or try to assign the 
value directly.
+                                       if (! 
rawTypeMeta.canCreateNewInstance()) {
+                                       if (propMap == null) {
+                                               if (setter == null && field == 
null)
+                                                       throw new 
BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' 
to object of type ''{2}'' because no setter or public field is defined, and the 
current value is null", name, propertyClass.getName(), findClassName(value));
+
+                                               if 
(propertyClass.isInstance(valueMap)) {
+                                                       if (! 
valueType.isObject()) {
+                                                               for (Map.Entry 
e : (Set<Map.Entry>)valueMap.entrySet()) {
+                                                                       Object 
v = e.getValue();
+                                                                       if (v 
!= null && ! valueType.getInnerClass().isInstance(v))
+                                                                               
throw new BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type 
''{1}'' to object of type ''{2}'' because the value types in the assigned map 
do not match the specified ''elementClass'' attribute on the property, and the 
property value is currently null", name, propertyClass.getName(), 
findClassName(value));
+                                                               }
+                                                       }
+                                                       if (setter != null)
+                                                               
setter.invoke(bean, valueMap);
+                                                       else
+                                                               field.set(bean, 
valueMap);
+                                                       return r;
+                                               }
+                                               throw new 
BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{2}'' 
to object of type ''{2}'' because the assigned map cannot be converted to the 
specified type because the property type is abstract, and the property value is 
currently null", name, propertyClass.getName(), findClassName(value));
+                                       }
+                               } else {
+                                       if (propMap == null) {
+                                               propMap = 
(Map)propertyClass.newInstance();
+                                               if (setter != null)
+                                                       setter.invoke(bean, 
propMap);
+                                               else if (field != null)
+                                                       field.set(bean, 
propMap);
+                                               else
+                                                       throw new 
BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' 
to object of type ''{2}'' because no setter or public field is defined on this 
property, and the existing property value is null", name, 
propertyClass.getName(), findClassName(value));
+                                       } else {
+                                               propMap.clear();
+                                       }
+                               }
+
+                               // Set the values.
+                               for (Map.Entry e : 
(Set<Map.Entry>)valueMap.entrySet()) {
+                                       Object k = e.getKey();
+                                       Object v = e.getValue();
+                                       if (! valueType.isObject())
+                                               v = 
beanMeta.ctx.convertToType(v, valueType);
+                                       propMap.put(k, v);
+                               }
+
+                       } else if (isCollection) {
+
+                               if (! (value instanceof Collection)) {
+                                       if (value instanceof CharSequence)
+                                               value = new 
ObjectList((CharSequence)value).setBeanContext(beanMeta.ctx);
+                                       else
+                                               throw new 
BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' 
to object of type ''{2}''", name, propertyClass.getName(), 
findClassName(value));
+                               }
+
+                               Collection valueList = (Collection)value;
+                               Collection propList = (Collection)r;
+                                       ClassMeta elementType = 
rawTypeMeta.getElementType();
+
+                               // If the property type is abstract, then we 
either need to reuse the existing
+                               // collection (if it's not null), or try to 
assign the value directly.
+                                       if (! 
rawTypeMeta.canCreateNewInstance()) {
+                                       if (propList == null) {
+                                               if (setter == null && field == 
null)
+                                                       throw new 
BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' 
to object of type ''{2}'' because no setter or public field is defined, and the 
current value is null", name, propertyClass.getName(), findClassName(value));
+
+                                               if 
(propertyClass.isInstance(valueList)) {
+                                                       if (! 
elementType.isObject()) {
+                                                                       List l 
= new ObjectList(valueList);
+                                                                       for 
(ListIterator<Object> i = l.listIterator(); i.hasNext(); ) {
+                                                                               
Object v = i.next();
+                                                                               
if (v != null && (! elementType.getInnerClass().isInstance(v))) {
+                                                                               
        i.set(bc.convertToType(v, elementType));
+                                                                               
}
+                                                                       }
+                                                                       
valueList = l;
+                                                               }
+                                                       if (setter != null)
+                                                               
setter.invoke(bean, valueList);
+                                                       else
+                                                               field.set(bean, 
valueList);
+                                                       return r;
+                                               }
+                                               throw new 
BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' 
to object of type ''{2}'' because the assigned map cannot be converted to the 
specified type because the property type is abstract, and the property value is 
currently null", name, propertyClass.getName(), findClassName(value));
+                                       }
+                                       propList.clear();
+                               } else {
+                                       if (propList == null) {
+                                               propList = 
(Collection)propertyClass.newInstance();
+                                               if (setter != null)
+                                                       setter.invoke(bean, 
propList);
+                                               else if (field != null)
+                                                       field.set(bean, 
propList);
+                                               else
+                                                       throw new 
BeanRuntimeException(beanMeta.c, "Cannot set property ''{0}'' of type ''{1}'' 
to object of type ''{2}'' because no setter is defined on this property, and 
the existing property value is null", name, propertyClass.getName(), 
findClassName(value));
+                                       } else {
+                                               propList.clear();
+                                       }
+                               }
+
+                               // Set the values.
+                               for (Object v : valueList) {
+                                       if (! elementType.isObject())
+                                               v = 
beanMeta.ctx.convertToType(v, elementType);
+                                       propList.add(v);
+                               }
+
+                       } else {
+                               if (transform != null && value != null && 
isParentClass(transform.getTransformedClass(), value.getClass())) {
+                                               value = 
transform.normalize(value, rawTypeMeta);
+                               } else {
+                                               value = 
beanMeta.ctx.convertToType(value, rawTypeMeta);
+                                       }
+                               if (setter != null)
+                                       setter.invoke(bean, new Object[] { 
value });
+                               else if (field != null)
+                                       field.set(bean, value);
+                       }
+
+                       return r;
+
+               } catch (BeanRuntimeException e) {
+                       throw e;
+               } catch (Exception e) {
+                       if (beanMeta.ctx.ignoreInvocationExceptionsOnSetters) {
+                                       if (rawTypeMeta.isPrimitive())
+                                               return 
rawTypeMeta.getPrimitiveDefault();
+                               return null;
+                       }
+                       throw new BeanRuntimeException(beanMeta.c, "Error 
occurred trying to set property ''{0}''", name).initCause(e);
+               }
+               } catch (ParseException e) {
+                       throw new BeanRuntimeException(e);
+               }
+       }
+
+       /**
+        * Sets an array field on this bean.
+        * Works on both <code>Object</code> and primitive arrays.
+        *
+        * @param bean The bean of the field.
+        * @param l The collection to use to set the array field.
+        * @throws IllegalArgumentException Thrown by method invocation.
+        * @throws IllegalAccessException Thrown by method invocation.
+        * @throws InvocationTargetException Thrown by method invocation.
+        */
+       protected void setArray(T bean, List l) throws 
IllegalArgumentException, IllegalAccessException, InvocationTargetException {
+               Object array = ArrayUtils.toArray(l, 
this.rawTypeMeta.getElementType().getInnerClass());
+               if (setter != null)
+                       setter.invoke(bean, array);
+               else if (field != null)
+                       field.set(bean, array);
+               else
+                       throw new BeanRuntimeException(beanMeta.c, "Attempt to 
initialize array property ''{0}'', but no setter or field defined.", name);
+       }
+
+       /**
+        * Adds a value to a {@link Collection} or array property.
+        * Note that adding values to an array property is inefficient for large
+        * arrays since it must copy the array into a larger array on each 
operation.
+        *
+        * @param m The bean of the field being set.
+        * @param value The value to add to the field.
+        * @throws BeanRuntimeException If field is not a collection or array.
+        */
+       public void add(BeanMap<T> m, Object value) throws BeanRuntimeException 
{
+
+               BeanContext bc = beanMeta.ctx;
+
+               // Read-only beans get their properties stored in a cache.
+               if (m.bean == null) {
+                       if (! m.propertyCache.containsKey(name))
+                               m.propertyCache.put(name, new ObjectList(bc));
+                       ((ObjectList)m.propertyCache.get(name)).add(value);
+                       return;
+               }
+
+               boolean isCollection = rawTypeMeta.isCollection();
+               boolean isArray = rawTypeMeta.isArray();
+
+               if (! (isCollection || isArray))
+                       throw new BeanRuntimeException(beanMeta.c, "Attempt to 
add element to property ''{0}'' which is not a collection or array", name);
+
+               Object bean = m.getBean(true);
+
+               ClassMeta<?> elementType = rawTypeMeta.getElementType();
+
+               try {
+                       Object v = bc.convertToType(value, elementType);
+
+                       if (isCollection) {
+                               Collection c = null;
+                               if (getter != null) {
+                                       c = (Collection)getter.invoke(bean, 
(Object[])null);
+                               } else if (field != null) {
+                                       c = (Collection)field.get(bean);
+                               } else {
+                                       throw new 
BeanRuntimeException(beanMeta.c, "Attempt to append to collection property 
''{0}'', but no getter or field defined.", name);
+                               }
+
+                               if (c != null) {
+                                       c.add(v);
+                                       return;
+                               }
+
+                               if (rawTypeMeta.canCreateNewInstance())
+                                       c = 
(Collection)rawTypeMeta.newInstance();
+                               else
+                                       c = new ObjectList(bc);
+
+                               c.add(v);
+
+                               if (setter != null)
+                                       setter.invoke(bean, c);
+                               else if (field != null)
+                                       field.set(bean, c);
+                               else
+                                       throw new 
BeanRuntimeException(beanMeta.c, "Attempt to initialize collection property 
''{0}'', but no setter or field defined.", name);
+
+                       } else /* isArray() */ {
+
+                               if (m.arrayPropertyCache == null)
+                                       m.arrayPropertyCache = new 
TreeMap<String,List<?>>();
+
+                               List l = m.arrayPropertyCache.get(name);
+                               if (l == null) {
+                                       l = new LinkedList();  // ArrayLists 
and LinkLists appear to perform equally.
+                                       m.arrayPropertyCache.put(name, l);
+
+                                       // Copy any existing array values into 
the temporary list.
+                                       Object oldArray;
+                               if (getter != null)
+                                               oldArray = getter.invoke(bean, 
(Object[])null);
+                               else if (field != null)
+                                               oldArray = field.get(bean);
+                               else
+                                       throw new 
BeanRuntimeException(beanMeta.c, "Attempt to append to array property ''{0}'', 
but no getter or field defined.", name);
+                                       ArrayUtils.copyToList(oldArray, l);
+                               }
+
+                               // Add new entry to our array.
+                               l.add(v);
+                       }
+
+               } catch (BeanRuntimeException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new BeanRuntimeException(e);
+               }
+       }
+
+       /**
+        * Returns all instances of the specified annotation in the hierarchy 
of this bean property.
+        * <p>
+        * Searches through the class hierarchy (e.g. superclasses, interfaces, 
packages) for all
+        * instances of the specified annotation.
+        *
+        * @param a The class to find annotations for.
+        * @return A list of annotations ordered in child-to-parent order.  
Never <jk>null</jk>.
+        */
+       public <A extends Annotation> List<A> findAnnotations(Class<A> a) {
+               List<A> l = new LinkedList<A>();
+               if (field != null) {
+                       addIfNotNull(l, field.getAnnotation(a));
+                       appendAnnotations(a, field.getType(), l);
+               }
+               if (getter != null) {
+                       addIfNotNull(l, getter.getAnnotation(a));
+                       appendAnnotations(a, getter.getReturnType(), l);
+               }
+               if (setter != null) {
+                       addIfNotNull(l, setter.getAnnotation(a));
+                       appendAnnotations(a, setter.getReturnType(), l);
+               }
+               appendAnnotations(a, 
this.getBeanMeta().getClassMeta().getInnerClass(), l);
+               return l;
+       }
+
+       private Object transform(Object o) throws SerializeException {
+               // First use transform defined via @BeanProperty.
+               if (transform != null)
+                       return transform.transform(o);
+               if (o == null)
+                       return null;
+               // Otherwise, look it up via bean context.
+               if (rawTypeMeta.hasChildPojoTransforms()) {
+                       Class c = o.getClass();
+                       ClassMeta<?> cm = rawTypeMeta.innerClass == c ? 
rawTypeMeta : beanMeta.ctx.getClassMeta(c);
+                       PojoTransform f = cm.getPojoTransform();
+                       if (f != null)
+                               return f.transform(o);
+               }
+               return o;
+       }
+
+       private Object normalize(Object o) throws ParseException {
+               if (transform != null)
+                       return transform.normalize(o, rawTypeMeta);
+               if (o == null)
+                       return null;
+               if (rawTypeMeta.hasChildPojoTransforms()) {
+                       Class c = o.getClass();
+                       ClassMeta<?> cm = rawTypeMeta.innerClass == c ? 
rawTypeMeta : beanMeta.ctx.getClassMeta(c);
+                       PojoTransform f = cm.getPojoTransform();
+                       if (f != null)
+                               return f.normalize(o, rawTypeMeta);
+               }
+               return o;
+       }
+
+       private Object applyChildPropertiesFilter(ClassMeta cm, Object o) {
+               if (o == null)
+                       return null;
+               if (cm.isBean())
+                       return new BeanMap(o, new 
BeanMetaFiltered(cm.getBeanMeta(), properties));
+               if (cm.isMap())
+                       return new FilteredMap((Map)o, properties);
+               if (cm.isObject()) {
+                       if (o instanceof Map)
+                               return new FilteredMap((Map)o, properties);
+                       BeanMeta bm = 
this.getBeanMeta().ctx.getBeanMeta(o.getClass());
+                       if (bm != null)
+                               return new BeanMap(o, new 
BeanMetaFiltered(cm.getBeanMeta(), properties));
+               }
+               return o;
+       }
+
+       private String findClassName(Object o) {
+               if (o == null)
+                       return null;
+               if (o instanceof Class)
+                       return ((Class<?>)o).getName();
+               return o.getClass().getName();
+       }
+
+       @Override /* Object */
+       public String toString() {
+               return name + ": " + this.rawTypeMeta.getInnerClass().getName() 
+ ", field=["+field+"], getter=["+getter+"], setter=["+setter+"]";
+       }
+}

Reply via email to