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 ([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 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 ([email protected]) + */ +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 ([email protected]) + * @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
