You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/01 00:08:19 UTC
[30/51] [partial] incubator-juneau git commit: Initial Juno contents
from IBM JazzHub repo
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanPropertyMeta.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanPropertyMeta.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanPropertyMeta.java
new file mode 100755
index 0000000..ea9fc3c
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanPropertyMeta.java
@@ -0,0 +1,803 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import static com.ibm.juno.core.Visibility.*;
+import static com.ibm.juno.core.utils.ClassUtils.*;
+import static com.ibm.juno.core.utils.CollectionUtils.*;
+import static com.ibm.juno.core.utils.ReflectionUtils.*;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.net.*;
+import java.util.*;
+
+import com.ibm.juno.core.annotation.*;
+import com.ibm.juno.core.annotation.URI;
+import com.ibm.juno.core.filter.*;
+import com.ibm.juno.core.html.*;
+import com.ibm.juno.core.jena.*;
+import com.ibm.juno.core.parser.*;
+import com.ibm.juno.core.serializer.*;
+import com.ibm.juno.core.utils.*;
+import com.ibm.juno.core.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 filters 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 (jbognar@us.ibm.com)
+ */
+@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 filtered class type of the bean property.
+ private String[] properties;
+ private PojoFilter filter; // PojoFilter 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; //
+
+ 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 PojoFilter} associated with it, this
+ * method returns the filtered 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 = (filter != null ? filter.getFilteredClassMeta() : rawTypeMeta.getFilteredClassMeta());
+ 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>
+ * <li>Property class type is {@link URL} or {@link URI}.
+ * <li>Property class type is annotated with {@link com.ibm.juno.core.annotation.URI}.
+ * <li>Property getter, setter, or field is annotated with {@link com.ibm.juno.core.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(com.ibm.juno.core.annotation.URI.class));
+ if (p != null) {
+ filter = getPropertyPojoFilter(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(com.ibm.juno.core.annotation.URI.class));
+ if (p != null) {
+ if (filter == null)
+ filter = getPropertyPojoFilter(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(com.ibm.juno.core.annotation.URI.class));
+ if (p != null) {
+ if (filter == null)
+ filter = getPropertyPojoFilter(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 PojoFilter getPropertyPojoFilter(BeanProperty p) throws Exception {
+ Class<? extends PojoFilter> c = p.filter();
+ if (c == PojoFilter.NULL.class)
+ return null;
+ try {
+ PojoFilter f = c.newInstance();
+ f.setBeanContext(this.beanMeta.ctx);
+ return f;
+ } catch (Exception e) {
+ throw new BeanRuntimeException(this.beanMeta.c, "Could not instantiate PojoFilter ''{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 filtered 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 = filter(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(Object, 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 = unfilter(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 (filter != null && value != null && isParentClass(filter.getFilteredClass(), value.getClass())) {
+ value = filter.unfilter(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 filter(Object o) throws SerializeException {
+ // First use filter defined via @BeanProperty.
+ if (filter != null)
+ return filter.filter(o);
+ if (o == null)
+ return null;
+ // Otherwise, look it up via bean context.
+ if (rawTypeMeta.hasChildPojoFilters()) {
+ Class c = o.getClass();
+ ClassMeta<?> cm = rawTypeMeta.innerClass == c ? rawTypeMeta : beanMeta.ctx.getClassMeta(c);
+ PojoFilter f = cm.getPojoFilter();
+ if (f != null)
+ return f.filter(o);
+ }
+ return o;
+ }
+
+ private Object unfilter(Object o) throws ParseException {
+ if (filter != null)
+ return filter.unfilter(o, rawTypeMeta);
+ if (o == null)
+ return null;
+ if (rawTypeMeta.hasChildPojoFilters()) {
+ Class c = o.getClass();
+ ClassMeta<?> cm = rawTypeMeta.innerClass == c ? rawTypeMeta : beanMeta.ctx.getClassMeta(c);
+ PojoFilter f = cm.getPojoFilter();
+ if (f != null)
+ return f.unfilter(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+"]";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanProxyInvocationHandler.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanProxyInvocationHandler.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanProxyInvocationHandler.class
new file mode 100755
index 0000000..4b8f78d
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanProxyInvocationHandler.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanProxyInvocationHandler.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanProxyInvocationHandler.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanProxyInvocationHandler.java
new file mode 100755
index 0000000..3e628aa
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanProxyInvocationHandler.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+/**
+ * Provides an {@link InvocationHandler} for creating beans from bean interfaces.
+ * <p>
+ * If the {@code useInterfaceProxies} setting is enabled in {@link BeanContext}, this
+ * is the class that creates instances of beans from interfaces.
+ *
+ * @author Barry M. Caceres
+ * @param <T> The interface class
+ */
+public class BeanProxyInvocationHandler<T> implements InvocationHandler {
+
+ private final BeanMeta<T> meta; // The BeanMeta for this instance
+ private Map<String, Object> beanProps; // The map of property names to bean property values.
+
+ /**
+ * Constructs with the specified {@link BeanMeta}.
+ *
+ * @param meta The bean meta data.
+ */
+ public BeanProxyInvocationHandler(BeanMeta<T> meta) {
+ this.meta = meta;
+ this.beanProps = new HashMap<String, Object>();
+ }
+
+ /**
+ * Implemented to handle the method called.
+ */
+ @Override /* InvocationHandler */
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ Class<?>[] paramTypes = method.getParameterTypes();
+ if (method.getName().equals("equals") && (paramTypes.length == 1) && (paramTypes[0] == java.lang.Object.class)) {
+ Object arg = args[0];
+ if (arg == null)
+ return false;
+ if (proxy == arg)
+ return true;
+ if (proxy.getClass() == arg.getClass()) {
+ InvocationHandler ih = Proxy.getInvocationHandler(arg);
+ if (ih instanceof BeanProxyInvocationHandler) {
+ return this.beanProps.equals(((BeanProxyInvocationHandler<?>)ih).beanProps);
+ }
+ }
+ BeanMap<Object> bean = this.meta.ctx.forBean(arg);
+ return this.beanProps.equals(bean);
+ }
+
+ if (method.getName().equals("hashCode") && (paramTypes.length == 0))
+ return Integer.valueOf(this.beanProps.hashCode());
+
+ if (method.getName().equals("toString") && (paramTypes.length == 0))
+ return this.beanProps.toString();
+
+ String prop = this.meta.getterProps.get(method);
+ if (prop != null)
+ return this.beanProps.get(prop);
+
+ prop = this.meta.setterProps.get(method);
+ if (prop != null) {
+ this.beanProps.put(prop, args[0]);
+ return null;
+ }
+
+ throw new UnsupportedOperationException("Unsupported bean method. method=[ " + method + " ]");
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanRuntimeException.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanRuntimeException.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanRuntimeException.class
new file mode 100755
index 0000000..e3247f1
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanRuntimeException.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanRuntimeException.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanRuntimeException.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanRuntimeException.java
new file mode 100755
index 0000000..f931e98
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/BeanRuntimeException.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * Note to U.S. Government Users Restricted Rights: Use,
+ * duplication or disclosure restricted by GSA ADP Schedule
+ * Contract with IBM Corp.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import java.text.*;
+
+/**
+ * General bean runtime operation exception.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ */
+public final class BeanRuntimeException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor.
+ *
+ * @param message The error message.
+ */
+ public BeanRuntimeException(String message) {
+ super(message);
+ }
+
+ /**
+ * Shortcut for calling <code><jk>new</jk> BeanRuntimeException(String.format(c.getName() + <js>": "</js> + message, args));</code>
+ *
+ * @param c The class name of the bean that caused the exception.
+ * @param message The error message.
+ * @param args Arguments passed in to the {@code String.format()} method.
+ */
+ public BeanRuntimeException(Class<?> c, String message, Object... args) {
+ this(c.getName() + ": " + (args.length == 0 ? message : MessageFormat.format(message, args)));
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param cause The initial cause of the exception.
+ */
+ public BeanRuntimeException(Throwable cause) {
+ super(cause == null ? null : cause.getLocalizedMessage());
+ initCause(cause);
+ }
+
+ /**
+ * Sets the inner cause for this exception.
+ *
+ * @param cause The inner cause.
+ * @return This object (for method chaining).
+ */
+ @Override /* Throwable */
+ public synchronized BeanRuntimeException initCause(Throwable cause) {
+ super.initCause(cause);
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta$1.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta$1.class
new file mode 100755
index 0000000..6ff209f
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta$1.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta$ClassCategory.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta$ClassCategory.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta$ClassCategory.class
new file mode 100755
index 0000000..28f91ed
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta$ClassCategory.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta.class
new file mode 100755
index 0000000..7e0e41a
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta.class differ
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta.java
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta.java
new file mode 100755
index 0000000..a95bfdb
--- /dev/null
+++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ClassMeta.java
@@ -0,0 +1,1262 @@
+/*******************************************************************************
+ * Licensed Materials - Property of IBM
+ * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved.
+ *
+ * The source code for this program is not published or otherwise
+ * divested of its trade secrets, irrespective of what has been
+ * deposited with the U.S. Copyright Office.
+ *******************************************************************************/
+package com.ibm.juno.core;
+
+import static com.ibm.juno.core.ClassMeta.ClassCategory.*;
+import static com.ibm.juno.core.ClassMeta.ClassCategory.URI;
+import static com.ibm.juno.core.utils.ClassUtils.*;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.lang.reflect.Proxy;
+import java.net.*;
+import java.net.URI;
+import java.util.*;
+
+import com.ibm.juno.core.annotation.*;
+import com.ibm.juno.core.filter.*;
+import com.ibm.juno.core.filter.Filter;
+import com.ibm.juno.core.html.*;
+import com.ibm.juno.core.jena.*;
+import com.ibm.juno.core.json.*;
+import com.ibm.juno.core.urlencoding.*;
+import com.ibm.juno.core.utils.*;
+import com.ibm.juno.core.xml.*;
+
+/**
+ * A wrapper class around the {@link Class} object that provides cached information
+ * about that class.
+ *
+ * <p>
+ * Instances of this class can be created through the {@link BeanContext#getClassMeta(Class)} method.
+ * <p>
+ * The {@link BeanContext} class will cache and reuse instances of this class except for the following class types:
+ * <ul>
+ * <li>Arrays
+ * <li>Maps with non-Object key/values.
+ * <li>Collections with non-Object key/values.
+ * </ul>
+ * <p>
+ * This class is tied to the {@link BeanContext} class because it's that class that makes the determination
+ * of what is a bean.
+ *
+ * @author James Bognar (jbognar@us.ibm.com)
+ * @param <T> The class type of the wrapped class.
+ */
+@Bean(properties={"innerClass","classCategory","elementType","keyType","valueType","notABeanReason","initException","beanMeta"})
+public final class ClassMeta<T> implements Type {
+
+ /** Class categories. */
+ enum ClassCategory {
+ MAP, COLLECTION, NUMBER, DECIMAL, BOOLEAN, CHAR, DATE, ARRAY, ENUM, BEAN, UNKNOWN, OTHER, CHARSEQ, STR, OBJ, URI, BEANMAP, READER, INPUTSTREAM
+ }
+
+ final BeanContext beanContext; // The bean context that created this object.
+ ClassCategory classCategory = UNKNOWN; // The class category.
+ final Class<T> innerClass; // The class being wrapped.
+ ClassMeta<?>
+ filteredClassMeta, // The filtered class type (in class has filter associated with it.
+ elementType = null, // If ARRAY or COLLECTION, the element class type.
+ keyType = null, // If MAP, the key class type.
+ valueType = null; // If MAP, the value class type.
+ InvocationHandler invocationHandler; // The invocation handler for this class (if it has one).
+ volatile BeanMeta<T> beanMeta; // The bean meta for this bean class (if it's a bean).
+ Method valueOfMethod; // The static valueOf(String) or fromString(String) method (if it has one).
+ Constructor<? extends T> noArgConstructor; // The no-arg constructor for this class (if it has one).
+ Constructor<T> stringConstructor; // The X(String) constructor (if it has one).
+ Constructor<T> objectMapConstructor; // The X(ObjectMap) constructor (if it has one).
+ Method toObjectMapMethod; // The toObjectMap() method (if it has one).
+ Method namePropertyMethod; // The method to set the name on an object (if it has one).
+ Method parentPropertyMethod; // The method to set the parent on an object (if it has one).
+ String notABeanReason; // If this isn't a bean, the reason why.
+ PojoFilter<T,?> pojoFilter; // The object filter associated with this bean (if it has one).
+ BeanFilter<? extends T> beanFilter; // The bean filter associated with this bean (if it has one).
+ boolean
+ isDelegate, // True if this class extends Delegate.
+ isAbstract, // True if this class is abstract.
+ isMemberClass; // True if this is a non-static member class.
+
+ private XmlClassMeta xmlMeta; // Class-related metadata from the @Xml annotation found on this class or parent class.
+ private JsonClassMeta jsonMeta; // Class-related metadata from the @Json annotation found on this class or parent class.
+ private HtmlClassMeta htmlMeta; // Class-related metadata from the @Html annotation found on this class or parent class.
+ private UrlEncodingClassMeta urlEncodingMeta; // Class-related metadata from the @UrlEncoding annotation found on this class or parent class.
+ private RdfClassMeta rdfMeta; // Class-related metadata from the @Rdf annotation found on this class or parent class.
+
+ private Throwable initException; // Any exceptions thrown in the init() method.
+ private boolean hasChildPojoFilters; // True if this class or any subclass of this class has a PojoFilter associated with it.
+ private Object primitiveDefault; // Default value for primitive type classes.
+ private Map<String,Method> remoteableMethods, // Methods annotated with @Remoteable. Contains all public methods if class is annotated with @Remotable.
+ publicMethods; // All public methods, including static methods.
+
+ private static final Boolean BOOLEAN_DEFAULT = false;
+ private static final Character CHARACTER_DEFAULT = (char)0;
+ private static final Short SHORT_DEFAULT = (short)0;
+ private static final Integer INTEGER_DEFAULT = 0;
+ private static final Long LONG_DEFAULT = 0l;
+ private static final Float FLOAT_DEFAULT = 0f;
+ private static final Double DOUBLE_DEFAULT = 0d;
+ private static final Byte BYTE_DEFAULT = (byte)0;
+
+ /**
+ * Shortcut for calling <code>ClassMeta(innerClass, beanContext, <jk>false</jk>)</code>.
+ */
+ ClassMeta(Class<T> innerClass, BeanContext beanContext) {
+ this(innerClass, beanContext, false);
+ }
+
+ /**
+ * Construct a new {@code ClassMeta} based on the specified {@link Class}.
+ *
+ * @param innerClass The class being wrapped.
+ * @param beanContext The bean context that created this object.
+ * @param delayedInit Don't call init() in constructor.
+ * Used for delayed initialization when the possibility of class reference loops exist.
+ */
+ ClassMeta(Class<T> innerClass, BeanContext beanContext, boolean delayedInit) {
+ this.innerClass = innerClass;
+ this.beanContext = beanContext;
+ if (! delayedInit)
+ init();
+ }
+
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ ClassMeta init() {
+
+ try {
+ Filter filter = findFilter(beanContext);
+ if (filter != null) {
+ if (filter.getType() == Filter.FilterType.BEAN)
+ beanFilter = (BeanFilter)filter;
+ else
+ pojoFilter = (PojoFilter)filter;
+ filteredClassMeta = (pojoFilter == null ? this : beanContext.getClassMeta(pojoFilter.getFilteredClass()));
+ } else {
+ filteredClassMeta = this;
+ }
+
+ if (innerClass != Object.class) {
+ this.noArgConstructor = beanContext.getImplClassConstructor(innerClass, Visibility.PUBLIC);
+ if (noArgConstructor == null)
+ noArgConstructor = findNoArgConstructor(innerClass, Visibility.PUBLIC);
+ }
+
+ this.hasChildPojoFilters = beanContext.hasChildPojoFilters(innerClass);
+
+ this.xmlMeta = new XmlClassMeta(innerClass);
+ this.jsonMeta = new JsonClassMeta(innerClass);
+ this.htmlMeta = new HtmlClassMeta(innerClass);
+ this.urlEncodingMeta = new UrlEncodingClassMeta(innerClass);
+ this.rdfMeta = new RdfClassMeta(innerClass);
+
+ Class c = innerClass;
+
+ if (c.isPrimitive()) {
+ if (c == Boolean.TYPE)
+ classCategory = BOOLEAN;
+ else if (c == Byte.TYPE || c == Short.TYPE || c == Integer.TYPE || c == Long.TYPE || c == Float.TYPE || c == Double.TYPE) {
+ if (c == Float.TYPE || c == Double.TYPE)
+ classCategory = DECIMAL;
+ else
+ classCategory = NUMBER;
+ }
+ else if (c == Character.TYPE)
+ classCategory = CHAR;
+ } else {
+ if (isParentClass(Delegate.class, c))
+ isDelegate = true;
+ if (c == Object.class)
+ classCategory = OBJ;
+ else if (c.isEnum())
+ classCategory = ENUM;
+ else if (isParentClass(CharSequence.class, c)) {
+ if (c.equals(String.class))
+ classCategory = STR;
+ else
+ classCategory = CHARSEQ;
+ }
+ else if (isParentClass(Number.class, c)) {
+ if (isParentClass(Float.class, c) || isParentClass(Double.class, c))
+ classCategory = DECIMAL;
+ else
+ classCategory = NUMBER;
+ }
+ else if (isParentClass(Collection.class, c))
+ classCategory = COLLECTION;
+ else if (isParentClass(Map.class, c)) {
+ if (isParentClass(BeanMap.class, c))
+ classCategory = BEANMAP;
+ else
+ classCategory = MAP;
+ }
+ else if (c == Character.class)
+ classCategory = CHAR;
+ else if (c == Boolean.class)
+ classCategory = BOOLEAN;
+ else if (isParentClass(Date.class, c) || isParentClass(Calendar.class, c))
+ classCategory = DATE;
+ else if (c.isArray())
+ classCategory = ARRAY;
+ else if (isParentClass(URL.class, c) || isParentClass(URI.class, c) || c.isAnnotationPresent(com.ibm.juno.core.annotation.URI.class))
+ classCategory = URI;
+ else if (isParentClass(Reader.class, c))
+ classCategory = READER;
+ else if (isParentClass(InputStream.class, c))
+ classCategory = INPUTSTREAM;
+ }
+
+ isMemberClass = c.isMemberClass() && ! Modifier.isStatic(c.getModifiers());
+
+ // Find static fromString(String) or equivalent method.
+ // fromString() must be checked before valueOf() so that Enum classes can create their own
+ // specialized fromString() methods to override the behavior of Enum.valueOf(String).
+ // valueOf() is used by enums.
+ // parse() is used by the java logging Level class.
+ // forName() is used by Class and Charset
+ for (String methodName : new String[]{"fromString","valueOf","parse","parseString","forName"}) {
+ if (this.valueOfMethod == null) {
+ for (Method m : c.getMethods()) {
+ int mod = m.getModifiers();
+ if (Modifier.isStatic(mod) && Modifier.isPublic(mod)) {
+ String mName = m.getName();
+ if (mName.equals(methodName) && m.getReturnType() == innerClass) {
+ Class<?>[] args = m.getParameterTypes();
+ if (args.length == 1 && args[0] == String.class) {
+ this.valueOfMethod = m;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Find toObjectMap() method if present.
+ for (Method m : c.getMethods()) {
+ int mod = m.getModifiers();
+ if (Modifier.isPublic(mod) && ! Modifier.isStatic(mod)) {
+ String mName = m.getName();
+ if (mName.equals("toObjectMap")) {
+ if (m.getParameterTypes().length == 0 && m.getReturnType() == ObjectMap.class) {
+ this.toObjectMapMethod = m;
+ break;
+ }
+ }
+ }
+ }
+
+ // Find @NameProperty and @ParentProperty methods if present.
+ for (Method m : c.getDeclaredMethods()) {
+ if (m.isAnnotationPresent(ParentProperty.class) && m.getParameterTypes().length == 1) {
+ m.setAccessible(true);
+ parentPropertyMethod = m;
+ }
+ if (m.isAnnotationPresent(NameProperty.class) && m.getParameterTypes().length == 1) {
+ m.setAccessible(true);
+ namePropertyMethod = m;
+ }
+ }
+
+ // Find constructor(String) method if present.
+ for (Constructor cs : c.getConstructors()) {
+ int mod = cs.getModifiers();
+ if (Modifier.isPublic(mod)) {
+ Class<?>[] args = cs.getParameterTypes();
+ if (args.length == (isMemberClass ? 2 : 1)) {
+ Class<?> arg = args[(isMemberClass ? 1 : 0)];
+ if (arg == String.class)
+ this.stringConstructor = cs;
+ else if (ObjectMap.class.isAssignableFrom(arg))
+ this.objectMapConstructor = cs;
+ }
+ }
+ }
+
+ // Note: Primitive types are normally abstract.
+ isAbstract = Modifier.isAbstract(c.getModifiers()) && ! isPrimitive();
+
+ // If this is an array, get the element type.
+ if (classCategory == ARRAY)
+ elementType = beanContext.getClassMeta(innerClass.getComponentType());
+
+ // If this is a MAP, see if it's parameterized (e.g. AddressBook extends HashMap<String,Person>)
+ else if (classCategory == MAP) {
+ ClassMeta[] parameters = beanContext.findParameters(innerClass, innerClass);
+ if (parameters != null && parameters.length == 2) {
+ keyType = parameters[0];
+ valueType = parameters[1];
+ } else {
+ keyType = beanContext.getClassMeta(Object.class);
+ valueType = beanContext.getClassMeta(Object.class);
+ }
+ }
+
+ // If this is a COLLECTION, see if it's parameterized (e.g. AddressBook extends LinkedList<Person>)
+ else if (classCategory == COLLECTION) {
+ ClassMeta[] parameters = beanContext.findParameters(innerClass, innerClass);
+ if (parameters != null && parameters.length == 1) {
+ elementType = parameters[0];
+ } else {
+ elementType = beanContext.getClassMeta(Object.class);
+ }
+ }
+
+ // If the category is unknown, see if it's a bean.
+ // Note that this needs to be done after all other initialization has been done.
+ else if (classCategory == UNKNOWN) {
+
+ BeanMeta newMeta = new BeanMeta(this, beanContext, beanFilter);
+ try {
+ notABeanReason = newMeta.init();
+ } catch (RuntimeException e) {
+ notABeanReason = e.getMessage();
+ throw e;
+ }
+ if (notABeanReason != null)
+ classCategory = OTHER;
+ else {
+ beanMeta = newMeta;
+ classCategory = BEAN;
+ }
+ }
+
+ if (c.isPrimitive()) {
+ if (c == Boolean.TYPE)
+ primitiveDefault = BOOLEAN_DEFAULT;
+ else if (c == Character.TYPE)
+ primitiveDefault = CHARACTER_DEFAULT;
+ else if (c == Short.TYPE)
+ primitiveDefault = SHORT_DEFAULT;
+ else if (c == Integer.TYPE)
+ primitiveDefault = INTEGER_DEFAULT;
+ else if (c == Long.TYPE)
+ primitiveDefault = LONG_DEFAULT;
+ else if (c == Float.TYPE)
+ primitiveDefault = FLOAT_DEFAULT;
+ else if (c == Double.TYPE)
+ primitiveDefault = DOUBLE_DEFAULT;
+ else if (c == Byte.TYPE)
+ primitiveDefault = BYTE_DEFAULT;
+ } else {
+ if (c == Boolean.class)
+ primitiveDefault = BOOLEAN_DEFAULT;
+ else if (c == Character.class)
+ primitiveDefault = CHARACTER_DEFAULT;
+ else if (c == Short.class)
+ primitiveDefault = SHORT_DEFAULT;
+ else if (c == Integer.class)
+ primitiveDefault = INTEGER_DEFAULT;
+ else if (c == Long.class)
+ primitiveDefault = LONG_DEFAULT;
+ else if (c == Float.class)
+ primitiveDefault = FLOAT_DEFAULT;
+ else if (c == Double.class)
+ primitiveDefault = DOUBLE_DEFAULT;
+ else if (c == Byte.class)
+ primitiveDefault = BYTE_DEFAULT;
+ }
+ } catch (NoClassDefFoundError e) {
+ this.initException = e;
+ } catch (RuntimeException e) {
+ this.initException = e;
+ throw e;
+ }
+
+ if (innerClass.getAnnotation(Remoteable.class) != null) {
+ remoteableMethods = getPublicMethods();
+ } else {
+ for (Method m : innerClass.getMethods()) {
+ if (m.getAnnotation(Remoteable.class) != null) {
+ if (remoteableMethods == null)
+ remoteableMethods = new LinkedHashMap<String,Method>();
+ remoteableMethods.put(ClassUtils.getMethodSignature(m), m);
+ }
+ }
+ }
+ if (remoteableMethods != null)
+ remoteableMethods = Collections.unmodifiableMap(remoteableMethods);
+
+ return this;
+ }
+
+ /**
+ * Returns the category of this class.
+ *
+ * @return The category of this class.
+ */
+ public ClassCategory getClassCategory() {
+ return classCategory;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a superclass of or the same as the specified class.
+ *
+ * @param c The comparison class.
+ * @return <jk>true</jk> if this class is a superclass of or the same as the specified class.
+ */
+ public boolean isAssignableFrom(Class<?> c) {
+ return isParentClass(innerClass, c);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class as subtypes defined through {@link Bean#subTypes} or {@link BeanFilter#getSubTypes()}.
+ *
+ * @return <jk>true</jk> if this class has subtypes.
+ */
+ public boolean hasSubTypes() {
+ return beanFilter != null && beanFilter.getSubTypeProperty() != null;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a subclass of or the same as the specified class.
+ *
+ * @param c The comparison class.
+ * @return <jk>true</jk> if this class is a subclass of or the same as the specified class.
+ */
+ public boolean isInstanceOf(Class<?> c) {
+ return isParentClass(c, innerClass);
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class or any child classes has a {@link PojoFilter} associated with it.
+ * <p>
+ * Used when filtering bean properties to prevent having to look up filters if we know for certain
+ * that no filters are associated with a bean property.
+ *
+ * @return <jk>true</jk> if this class or any child classes has a {@link PojoFilter} associated with it.
+ */
+ public boolean hasChildPojoFilters() {
+ return hasChildPojoFilters;
+ }
+
+ private Filter findFilter(BeanContext context) {
+ try {
+ com.ibm.juno.core.annotation.Filter b = innerClass.getAnnotation(com.ibm.juno.core.annotation.Filter.class);
+ if (b != null) {
+ Class<? extends Filter> c = b.value();
+ if (c != Filter.NULL.class) {
+ Filter f = c.newInstance();
+ f.setBeanContext(context);
+ return f;
+ }
+ }
+ if (context == null)
+ return null;
+ Filter f = context.findBeanFilter(innerClass);
+ if (f != null)
+ return f;
+ f = context.findPojoFilter(innerClass);
+ if (f != null)
+ return f;
+ List<Bean> ba = ReflectionUtils.findAnnotations(Bean.class, innerClass);
+ if (! ba.isEmpty())
+ f = new AnnotationBeanFilter<T>(innerClass, ba);
+ return f;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Locates the no-arg constructor for the specified class.
+ * Constructor must match the visibility requirements specified by parameter 'v'.
+ * If class is abstract, always returns <jk>null</jk>.
+ * Note that this also returns the 1-arg constructor for non-static member classes.
+ *
+ * @param c The class from which to locate the no-arg constructor.
+ * @param v The minimum visibility.
+ * @return The constructor, or <jk>null</jk> if no no-arg constructor exists with the required visibility.
+ */
+ @SuppressWarnings({"rawtypes","unchecked"})
+ protected static <T> Constructor<? extends T> findNoArgConstructor(Class<T> c, Visibility v) {
+ int mod = c.getModifiers();
+ if (Modifier.isAbstract(mod))
+ return null;
+ boolean isMemberClass = c.isMemberClass() && ! Modifier.isStatic(mod);
+ for (Constructor cc : c.getConstructors()) {
+ mod = cc.getModifiers();
+ if (cc.getParameterTypes().length == (isMemberClass ? 1 : 0) && v.isVisible(mod))
+ return v.filter(cc);
+ }
+ return null;
+ }
+
+ /**
+ * Set element type on non-cached <code>Collection</code> types.
+ *
+ * @param elementType The class type for elements in the collection class represented by this metadata.
+ * @return This object (for method chaining).
+ */
+ protected ClassMeta<T> setElementType(ClassMeta<?> elementType) {
+ this.elementType = elementType;
+ return this;
+ }
+
+ /**
+ * Set key type on non-cached <code>Map</code> types.
+ *
+ * @param keyType The class type for keys in the map class represented by this metadata.
+ * @return This object (for method chaining).
+ */
+ protected ClassMeta<T> setKeyType(ClassMeta<?> keyType) {
+ this.keyType = keyType;
+ return this;
+ }
+
+ /**
+ * Set value type on non-cached <code>Map</code> types.
+ *
+ * @param valueType The class type for values in the map class represented by this metadata.
+ * @return This object (for method chaining).
+ */
+ protected ClassMeta<T> setValueType(ClassMeta<?> valueType) {
+ this.valueType = valueType;
+ return this;
+ }
+
+ /**
+ * Returns the {@link Class} object that this class type wraps.
+ *
+ * @return The wrapped class object.
+ */
+ public Class<T> getInnerClass() {
+ return innerClass;
+ }
+
+ /**
+ * Returns the generalized form of this class if there is an {@link PojoFilter} associated with it.
+ *
+ * @return The filtered class type, or this object if no filter is associated with the class.
+ */
+ @BeanIgnore
+ public ClassMeta<?> getFilteredClassMeta() {
+ return filteredClassMeta;
+ }
+
+ /**
+ * For array and {@code Collection} types, returns the class type of the components of the array or {@code Collection}.
+ *
+ * @return The element class type, or <jk>null</jk> if this class is not an array or Collection.
+ */
+ public ClassMeta<?> getElementType() {
+ return elementType;
+ }
+
+ /**
+ * For {@code Map} types, returns the class type of the keys of the {@code Map}.
+ *
+ * @return The key class type, or <jk>null</jk> if this class is not a Map.
+ */
+ public ClassMeta<?> getKeyType() {
+ return keyType;
+ }
+
+ /**
+ * For {@code Map} types, returns the class type of the values of the {@code Map}.
+ *
+ * @return The value class type, or <jk>null</jk> if this class is not a Map.
+ */
+ public ClassMeta<?> getValueType() {
+ return valueType;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class implements {@link Delegate}, meaning
+ * it's a representation of some other object.
+ *
+ * @return <jk>true</jk> if this class implements {@link Delegate}.
+ */
+ public boolean isDelegate() {
+ return isDelegate;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a subclass of {@link Map}.
+ *
+ * @return <jk>true</jk> if this class is a subclass of {@link Map}.
+ */
+ public boolean isMap() {
+ return classCategory == MAP || classCategory == BEANMAP;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a subclass of {@link BeanMap}.
+ *
+ * @return <jk>true</jk> if this class is a subclass of {@link BeanMap}.
+ */
+ public boolean isBeanMap() {
+ return classCategory == BEANMAP;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a subclass of {@link Collection}.
+ *
+ * @return <jk>true</jk> if this class is a subclass of {@link Collection}.
+ */
+ public boolean isCollection() {
+ return classCategory == COLLECTION;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is an {@link Enum}.
+ *
+ * @return <jk>true</jk> if this class is an {@link Enum}.
+ */
+ public boolean isEnum() {
+ return classCategory == ENUM;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is an array.
+ *
+ * @return <jk>true</jk> if this class is an array.
+ */
+ public boolean isArray() {
+ return classCategory == ARRAY;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a bean.
+ *
+ * @return <jk>true</jk> if this class is a bean.
+ */
+ public boolean isBean() {
+ return classCategory == BEAN;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is {@link Object}.
+ *
+ * @return <jk>true</jk> if this class is {@link Object}.
+ */
+ public boolean isObject() {
+ return classCategory == OBJ;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is not {@link Object}.
+ *
+ * @return <jk>true</jk> if this class is not {@link Object}.
+ */
+ public boolean isNotObject() {
+ return classCategory != OBJ;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a subclass of {@link Number}.
+ *
+ * @return <jk>true</jk> if this class is a subclass of {@link Number}.
+ */
+ public boolean isNumber() {
+ return classCategory == NUMBER || classCategory == DECIMAL;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a subclass of {@link Float} or {@link Double}.
+ *
+ * @return <jk>true</jk> if this class is a subclass of {@link Float} or {@link Double}.
+ */
+ public boolean isDecimal() {
+ return classCategory == DECIMAL;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a {@link Boolean}.
+ *
+ * @return <jk>true</jk> if this class is a {@link Boolean}.
+ */
+ public boolean isBoolean() {
+ return classCategory == BOOLEAN;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a subclass of {@link CharSequence}.
+ *
+ * @return <jk>true</jk> if this class is a subclass of {@link CharSequence}.
+ */
+ public boolean isCharSequence() {
+ return classCategory == STR || classCategory == CHARSEQ;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a {@link String}.
+ *
+ * @return <jk>true</jk> if this class is a {@link String}.
+ */
+ public boolean isString() {
+ return classCategory == STR;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a {@link Character}.
+ *
+ * @return <jk>true</jk> if this class is a {@link Character}.
+ */
+ public boolean isChar() {
+ return classCategory == CHAR;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a primitive.
+ *
+ * @return <jk>true</jk> if this class is a primitive.
+ */
+ public boolean isPrimitive() {
+ return innerClass.isPrimitive();
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a {@link Date} or {@link Calendar}.
+ *
+ * @return <jk>true</jk> if this class is a {@link Date} or {@link Calendar}.
+ */
+ public boolean isDate() {
+ return classCategory == DATE;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a {@link URI} or {@link URL}.
+ *
+ * @return <jk>true</jk> if this class is a {@link URI} or {@link URL}.
+ */
+ public boolean isUri() {
+ return classCategory == URI;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is a {@link Reader}.
+ *
+ * @return <jk>true</jk> if this class is a {@link Reader}.
+ */
+ public boolean isReader() {
+ return classCategory == READER;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is an {@link InputStream}.
+ *
+ * @return <jk>true</jk> if this class is an {@link InputStream}.
+ */
+ public boolean isInputStream() {
+ return classCategory == INPUTSTREAM;
+ }
+
+ /**
+ * Returns <jk>true</jk> if instance of this object can be <jk>null</jk>.
+ * <p>
+ * Objects can be <jk>null</jk>, but primitives cannot, except for chars which can be represented
+ * by <code>(<jk>char</jk>)0</code>.
+ *
+ * @return <jk>true</jk> if instance of this class can be null.
+ */
+ public boolean isNullable() {
+ if (innerClass.isPrimitive())
+ return classCategory == CHAR;
+ return true;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class or one of it's methods are annotated with {@link Remoteable @Remotable}.
+ *
+ * @return <jk>true</jk> if this class is remoteable.
+ */
+ public boolean isRemoteable() {
+ return remoteableMethods != null;
+ }
+
+ /**
+ * All methods on this class annotated with {@link Remoteable @Remotable}, or all public methods if class is annotated.
+ * Keys are method signatures (see {@link ClassUtils#getMethodSignature(Method)})
+ *
+ * @return All remoteable methods on this class.
+ */
+ public Map<String,Method> getRemoteableMethods() {
+ return remoteableMethods;
+ }
+
+ /**
+ * All public methods on this class including static methods.
+ * Keys are method signatures (see {@link ClassUtils#getMethodSignature(Method)}).
+ *
+ * @return The public methods on this class.
+ */
+ public Map<String,Method> getPublicMethods() {
+ if (publicMethods == null) {
+ synchronized(this) {
+ Map<String,Method> map = new LinkedHashMap<String,Method>();
+ for (Method m : innerClass.getMethods())
+ if (Modifier.isPublic(m.getModifiers()))
+ map.put(ClassUtils.getMethodSignature(m), m);
+ publicMethods = Collections.unmodifiableMap(map);
+ }
+ }
+ return publicMethods;
+ }
+
+ /**
+ * Returns the {@link PojoFilter} associated with this class.
+ *
+ * @return The {@link PojoFilter} associated with this class, or <jk>null</jk> if there is no POJO filter
+ * associated with this class.
+ */
+ public PojoFilter<T,?> getPojoFilter() {
+ return pojoFilter;
+ }
+
+ /**
+ * Returns the {@link BeanMeta} associated with this class.
+ *
+ * @return The {@link BeanMeta} associated with this class, or <jk>null</jk> if there is no bean meta
+ * associated with this class.
+ */
+ public BeanMeta<T> getBeanMeta() {
+ return beanMeta;
+ }
+
+ /**
+ * Returns the no-arg constructor for this class.
+ *
+ * @return The no-arg constructor for this class, or <jk>null</jk> if it does not exist.
+ */
+ public Constructor<? extends T> getConstructor() {
+ return noArgConstructor;
+ }
+
+ /**
+ * Returns the <ja>@Xml</ja> annotation defined on this class, superclass, or interface of this class.
+ *
+ * @return XML metadata on this class. Never <jk>null</jk>.
+ */
+ public XmlClassMeta getXmlMeta() {
+ return xmlMeta;
+ }
+
+ /**
+ * Returns metadata from the <ja>@Json</ja> annotation defined on this class, superclass, or interface of this class.
+ *
+ * @return JSON metadata on this class. Never <jk>null</jk>.
+ */
+ public JsonClassMeta getJsonMeta() {
+ return jsonMeta;
+ }
+
+ /**
+ * Returns metadata from the <ja>@Html</ja> annotation defined on this class, superclass, or interface of this class.
+ *
+ * @return HTML metadata on this class. Never <jk>null</jk>.
+ */
+ public HtmlClassMeta getHtmlMeta() {
+ return htmlMeta;
+ }
+
+ /**
+ * Returns metadata from the <ja>@UrlEncoding</ja> annotation defined on this class, superclass, or interface of this class.
+ *
+ * @return URL-Encoding metadata on this class. Never <jk>null</jk>.
+ */
+ public UrlEncodingClassMeta getUrlEncodingMeta() {
+ return urlEncodingMeta;
+ }
+
+ /**
+ * Returns metadata from the <ja>@Rdf</ja> annotation defined on this class, superclass, or interface of this class.
+ *
+ * @return RDF metadata on this class. Never <jk>null</jk>.
+ */
+ public RdfClassMeta getRdfMeta() {
+ return rdfMeta;
+ }
+
+ /**
+ * Returns the interface proxy invocation handler for this class.
+ *
+ * @return The interface proxy invocation handler, or <jk>null</jk> if it does not exist.
+ */
+ public InvocationHandler getProxyInvocationHandler() {
+ if (invocationHandler == null && beanMeta != null && beanContext.useInterfaceProxies && innerClass.isInterface())
+ invocationHandler = new BeanProxyInvocationHandler<T>(beanMeta);
+ return invocationHandler;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class has a no-arg constructor or invocation handler.
+ *
+ * @return <jk>true</jk> if a new instance of this class can be constructed.
+ */
+ public boolean canCreateNewInstance() {
+ if (isMemberClass)
+ return false;
+ if (noArgConstructor != null)
+ return true;
+ if (getProxyInvocationHandler() != null)
+ return true;
+ if (isArray() && elementType.canCreateNewInstance())
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class has a no-arg constructor or invocation handler.
+ * Returns <jk>false</jk> if this is a non-static member class and the outer object does not match
+ * the class type of the defining class.
+ *
+ * @param outer The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes.
+ * @return <jk>true</jk> if a new instance of this class can be created within the context of the specified outer object.
+ */
+ public boolean canCreateNewInstance(Object outer) {
+ if (isMemberClass)
+ return outer != null && noArgConstructor != null && noArgConstructor.getParameterTypes()[0] == outer.getClass();
+ return canCreateNewInstance();
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class can be instantiated as a bean.
+ * Returns <jk>false</jk> if this is a non-static member class and the outer object does not match
+ * the class type of the defining class.
+ *
+ * @param outer The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes.
+ * @return <jk>true</jk> if a new instance of this bean can be created within the context of the specified outer object.
+ */
+ public boolean canCreateNewBean(Object outer) {
+ if (beanMeta == null)
+ return false;
+ // Beans with filters with subtype properties are assumed to be constructable.
+ if (beanFilter != null && beanFilter.getSubTypeProperty() != null)
+ return true;
+ if (beanMeta.constructor == null)
+ return false;
+ if (isMemberClass)
+ return outer != null && beanMeta.constructor.getParameterTypes()[0] == outer.getClass();
+ return true;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class can call the {@link #newInstanceFromString(Object, String)} method.
+ *
+ * @param outer The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes.
+ * @return <jk>true</jk> if this class has a no-arg constructor or invocation handler.
+ */
+ public boolean canCreateNewInstanceFromString(Object outer) {
+ if (valueOfMethod != null)
+ return true;
+ if (stringConstructor != null) {
+ if (isMemberClass)
+ return outer != null && stringConstructor.getParameterTypes()[0] == outer.getClass();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class can call the {@link #newInstanceFromString(Object, String)} method.
+ *
+ * @param outer The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes.
+ * @return <jk>true</jk> if this class has a no-arg constructor or invocation handler.
+ */
+ public boolean canCreateNewInstanceFromObjectMap(Object outer) {
+ if (objectMapConstructor != null) {
+ if (isMemberClass)
+ return outer != null && objectMapConstructor.getParameterTypes()[0] == outer.getClass();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class has an <code>ObjectMap toObjectMap()</code> method.
+ *
+ * @return <jk>true</jk> if class has a <code>toObjectMap()</code> method.
+ */
+ public boolean hasToObjectMapMethod() {
+ return toObjectMapMethod != null;
+ }
+
+ /**
+ * Returns the method annotated with {@link NameProperty @NameProperty}.
+ *
+ * @return The method annotated with {@link NameProperty @NameProperty} or <jk>null</jk> if method does not exist.
+ */
+ public Method getNameProperty() {
+ return namePropertyMethod;
+ }
+
+ /**
+ * Returns the method annotated with {@link ParentProperty @ParentProperty}.
+ *
+ * @return The method annotated with {@link ParentProperty @ParentProperty} or <jk>null</jk> if method does not exist.
+ */
+ public Method getParentProperty() {
+ return parentPropertyMethod;
+ }
+
+ /**
+ * Converts an instance of this class to an {@link ObjectMap}.
+ *
+ * @param t The object to convert to a map.
+ * @return The converted object, or <jk>null</jk> if method does not have a <code>toObjectMap()</code> method.
+ * @throws BeanRuntimeException Thrown by <code>toObjectMap()</code> method invocation.
+ */
+ public ObjectMap toObjectMap(Object t) throws BeanRuntimeException {
+ try {
+ if (toObjectMapMethod != null)
+ return (ObjectMap)toObjectMapMethod.invoke(t);
+ return null;
+ } catch (Exception e) {
+ throw new BeanRuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns the reason why this class is not a bean, or <jk>null</jk> if it is a bean.
+ *
+ * @return The reason why this class is not a bean, or <jk>null</jk> if it is a bean.
+ */
+ public synchronized String getNotABeanReason() {
+ return notABeanReason;
+ }
+
+ /**
+ * Returns <jk>true</jk> if this class is abstract.
+ * @return <jk>true</jk> if this class is abstract.
+ */
+ public boolean isAbstract() {
+ return isAbstract;
+ }
+
+ /**
+ * Returns any exception that was throw in the <code>init()</code> method.
+ *
+ * @return The cached exception.
+ */
+ public Throwable getInitException() {
+ return initException;
+ }
+
+ /**
+ * Returns the {@link BeanContext} that created this object.
+ *
+ * @return The bean context.
+ */
+ public BeanContext getBeanContext() {
+ return beanContext;
+ }
+
+ /**
+ * Returns the default value for primitives such as <jk>int</jk> or <jk>Integer</jk>.
+ *
+ * @return The default value, or <jk>null</jk> if this class type is not a primitive.
+ */
+ @SuppressWarnings("unchecked")
+ public T getPrimitiveDefault() {
+ return (T)primitiveDefault;
+ }
+
+ /**
+ * Create a new instance of the main class of this declared type from a <code>String</code> input.
+ * <p>
+ * In order to use this method, the class must have one of the following methods:
+ * <ul>
+ * <li><code><jk>public static</jk> T valueOf(String in);</code>
+ * <li><code><jk>public static</jk> T fromString(String in);</code>
+ * <li><code><jk>public</jk> T(String in);</code>
+ * </ul>
+ *
+ * @param outer The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes.
+ * @param arg The input argument value.
+ * @return A new instance of the object, or <jk>null</jk> if there is no no-arg constructor on the object.
+ * @throws IllegalAccessException If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is inaccessible.
+ * @throws IllegalArgumentException If the parameter type on the method was invalid.
+ * @throws InstantiationException If the class that declares the underlying constructor represents an abstract class, or
+ * does not have one of the methods described above.
+ * @throws InvocationTargetException If the underlying constructor throws an exception.
+ */
+ @SuppressWarnings("unchecked")
+ public T newInstanceFromString(Object outer, String arg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
+ Method m = valueOfMethod;
+ if (m != null)
+ return (T)m.invoke(null, arg);
+ Constructor<T> c = stringConstructor;
+ if (c != null) {
+ if (isMemberClass)
+ return c.newInstance(outer, arg);
+ return c.newInstance(arg);
+ }
+ throw new InstantiationError("No string constructor or valueOf(String) method found for class '"+getInnerClass().getName()+"'");
+ }
+
+ /**
+ * Create a new instance of the main class of this declared type from an <code>ObjectMap</code> input.
+ * <p>
+ * In order to use this method, the class must have one of the following methods:
+ * <ul>
+ * <li><code><jk>public</jk> T(ObjectMap in);</code>
+ * </ul>
+ *
+ * @param outer The outer class object for non-static member classes. Can be <jk>null</jk> for non-member or static classes.
+ * @param arg The input argument value.
+ * @return A new instance of the object.
+ * @throws IllegalAccessException If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is inaccessible.
+ * @throws IllegalArgumentException If the parameter type on the method was invalid.
+ * @throws InstantiationException If the class that declares the underlying constructor represents an abstract class, or
+ * does not have one of the methods described above.
+ * @throws InvocationTargetException If the underlying constructor throws an exception.
+ */
+ public T newInstanceFromObjectMap(Object outer, ObjectMap arg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
+ Constructor<T> c = objectMapConstructor;
+ if (c != null) {
+ if (isMemberClass)
+ return c.newInstance(outer, arg);
+ return c.newInstance(arg);
+ }
+ throw new InstantiationError("No map constructor method found for class '"+getInnerClass().getName()+"'");
+ }
+
+ /**
+ * Create a new instance of the main class of this declared type.
+ *
+ * @return A new instance of the object, or <jk>null</jk> if there is no no-arg constructor on the object.
+ * @throws IllegalAccessException If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is inaccessible.
+ * @throws IllegalArgumentException If one of the following occurs:
+ * <ul>
+ * <li>The number of actual and formal parameters differ.
+ * <li>An unwrapping conversion for primitive arguments fails.
+ * <li>A parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion.
+ * <li>The constructor pertains to an enum type.
+ * </ul>
+ * @throws InstantiationException If the class that declares the underlying constructor represents an abstract class.
+ * @throws InvocationTargetException If the underlying constructor throws an exception.
+ */
+ @SuppressWarnings("unchecked")
+ public T newInstance() throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
+ if (isArray())
+ return (T)Array.newInstance(getInnerClass().getComponentType(), 0);
+ Constructor<? extends T> c = getConstructor();
+ if (c != null)
+ return c.newInstance((Object[])null);
+ InvocationHandler h = getProxyInvocationHandler();
+ if (h != null)
+ return (T)Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] { getInnerClass(), java.io.Serializable.class }, h);
+ if (isArray())
+ return (T)Array.newInstance(this.elementType.innerClass,0);
+ return null;
+ }
+
+ /**
+ * Same as {@link #newInstance()} except for instantiating non-static member classes.
+ *
+ * @param outer The instance of the owning object of the member class instance. Can be <jk>null</jk> if instantiating a non-member or static class.
+ * @return A new instance of the object, or <jk>null</jk> if there is no no-arg constructor on the object.
+ * @throws IllegalAccessException If the <code>Constructor</code> object enforces Java language access control and the underlying constructor is inaccessible.
+ * @throws IllegalArgumentException If one of the following occurs:
+ * <ul>
+ * <li>The number of actual and formal parameters differ.
+ * <li>An unwrapping conversion for primitive arguments fails.
+ * <li>A parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion.
+ * <li>The constructor pertains to an enum type.
+ * </ul>
+ * @throws InstantiationException If the class that declares the underlying constructor represents an abstract class.
+ * @throws InvocationTargetException If the underlying constructor throws an exception.
+ */
+ public T newInstance(Object outer) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
+ if (isMemberClass)
+ return noArgConstructor.newInstance(outer);
+ return newInstance();
+ }
+
+ /**
+ * Checks to see if the specified class type is the same as this one.
+ *
+ * @param t The specified class type.
+ * @return <jk>true</jk> if the specified class type is the same as the class for this type.
+ */
+ @Override /* Object */
+ public boolean equals(Object t) {
+ if (t == null || ! (t instanceof ClassMeta))
+ return false;
+ ClassMeta<?> t2 = (ClassMeta<?>)t;
+ return t2.getInnerClass() == this.getInnerClass();
+ }
+
+ @Override /* Object */
+ public String toString() {
+ return toString(false);
+ }
+
+ /**
+ * Same as {@link #toString()} except use simple class names.
+ *
+ * @param simple Print simple class names only (no package).
+ * @return A new string.
+ */
+ public String toString(boolean simple) {
+ return toString(new StringBuilder(), simple).toString();
+ }
+
+ /**
+ * Appends this object as a readable string to the specified string builder.
+ *
+ * @param sb The string builder to append this object to.
+ * @param simple Print simple class names only (no package).
+ * @return The same string builder passed in (for method chaining).
+ */
+ protected StringBuilder toString(StringBuilder sb, boolean simple) {
+ String name = innerClass.getName();
+ if (simple) {
+ int i = name.lastIndexOf('.');
+ name = name.substring(i == -1 ? 0 : i+1).replace('$', '.');
+ }
+ switch(classCategory) {
+ case ARRAY:
+ return elementType.toString(sb, simple).append('[').append(']');
+ case MAP:
+ return sb.append(name).append(keyType.isObject() && valueType.isObject() ? "" : "<"+keyType.toString(simple)+","+valueType.toString(simple)+">");
+ case BEANMAP:
+ return sb.append(BeanMap.class.getName()).append("<").append(name).append(">");
+ case COLLECTION:
+ return sb.append(name).append(elementType.isObject() ? "" : "<"+elementType.toString(simple)+">");
+ case OTHER:
+ if (simple)
+ return sb.append(name);
+ sb.append("OTHER-").append(name).append(",notABeanReason=").append(notABeanReason);
+ if (initException != null)
+ sb.append(",initException=").append(initException);
+ return sb;
+ default:
+ return sb.append(name);
+ }
+ }
+
+ /**
+ * Returns <jk>true</jk> if the specified object is an instance of this class.
+ * This is a simple comparison on the base class itself and not on
+ * any generic parameters.
+ *
+ * @param o The object to check.
+ * @return <jk>true</jk> if the specified object is an instance of this class.
+ */
+ public boolean isInstance(Object o) {
+ if (o != null)
+ return ClassUtils.isParentClass(this.innerClass, o.getClass());
+ return false;
+ }
+
+ /**
+ * Returns a readable name for this class (e.g. <js>"java.lang.String"</js>, <js>"boolean[]"</js>).
+ *
+ * @return The readable name for this class.
+ */
+ public String getReadableName() {
+ return ClassUtils.getReadableClassName(this.innerClass);
+ }
+
+ @Override /* Object */
+ public int hashCode() {
+ return super.hashCode();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/CoreApi.class
----------------------------------------------------------------------
diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/CoreApi.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/CoreApi.class
new file mode 100755
index 0000000..44730ed
Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/CoreApi.class differ