Repository: incubator-juneau Updated Branches: refs/heads/master 74a90ffa7 -> dba656f35
JUNEAU-32 - Improvements to BeanMeta class Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/dba656f3 Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/dba656f3 Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/dba656f3 Branch: refs/heads/master Commit: dba656f354ce59bdd423479ba15df1fd2e949b7d Parents: 74a90ff Author: JamesBognar <jamesbog...@apache.org> Authored: Thu Feb 2 21:03:29 2017 -0500 Committer: JamesBognar <jamesbog...@apache.org> Committed: Thu Feb 2 21:03:29 2017 -0500 ---------------------------------------------------------------------- .../java/org/apache/juneau/BeanContext.java | 4 +- .../main/java/org/apache/juneau/BeanMeta.java | 2 +- .../main/java/org/apache/juneau/ClassMeta.java | 736 ++----------------- .../java/org/apache/juneau/ClassMetaSimple.java | 662 +++++++++++++++++ .../org/apache/juneau/internal/ClassUtils.java | 34 +- 5 files changed, 751 insertions(+), 687 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/dba656f3/juneau-core/src/main/java/org/apache/juneau/BeanContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java index a699825..06e1f8f 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanContext.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanContext.java @@ -1834,11 +1834,11 @@ public class BeanContext extends Context { while (cc != null) { Class implClass = implClasses.get(cc); if (implClass != null) - return ClassMeta.findNoArgConstructor(implClass, v); + return findNoArgConstructor(implClass, v); for (Class ic : cc.getInterfaces()) { implClass = implClasses.get(ic); if (implClass != null) - return ClassMeta.findNoArgConstructor(implClass, v); + return findNoArgConstructor(implClass, v); } cc = cc.getSuperclass(); } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/dba656f3/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java index 6b1b63d..e1b3d65 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java @@ -210,7 +210,7 @@ public class BeanMeta<T> { constructor = (Constructor<T>)ctx.getImplClassConstructor(c, conVis); if (constructor == null) - constructor = (Constructor<T>)ClassMeta.findNoArgConstructor(c, conVis); + constructor = (Constructor<T>)findNoArgConstructor(c, conVis); if (constructor == null && beanFilter == null && ctx.beansRequireDefaultConstructor) return "Class does not have the required no-arg constructor"; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/dba656f3/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java index d7f112b..42d5542 100644 --- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java @@ -12,14 +12,10 @@ // *************************************************************************************************************************** package org.apache.juneau; -import static org.apache.juneau.ClassMeta.ClassCategory.*; -import static org.apache.juneau.internal.ClassUtils.*; +import static org.apache.juneau.ClassMetaSimple.ClassCategory.*; -import java.io.*; import java.lang.reflect.*; import java.lang.reflect.Proxy; -import java.net.*; -import java.net.URI; import java.util.*; import org.apache.juneau.annotation.*; @@ -49,16 +45,9 @@ import org.apache.juneau.utils.*; * @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, CLASS, NUMBER, DECIMAL, BOOLEAN, CHAR, DATE, ARRAY, ENUM, BEAN, UNKNOWN, OTHER, CHARSEQ, STR, OBJ, URI, BEANMAP, READER, INPUTSTREAM - } +public final class ClassMeta<T> extends ClassMetaSimple<T> { 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<?> serializedClassMeta, // The transformed class type (if class has swap associated with it). elementType = null, // If ARRAY or COLLECTION, the element class type. @@ -67,40 +56,13 @@ public final class ClassMeta<T> implements Type { InvocationHandler invocationHandler; // The invocation handler for this class (if it has one). BeanMeta<T> beanMeta; // The bean meta for this bean class (if it's a bean). String dictionaryName, resolvedDictionaryName; // The dictionary name of this class if it has one. - Method fromStringMethod; // The static valueOf(String) or fromString(String) or forString(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> numberConstructor; // The X(Number) constructor (if it has one). - Constructor<T> swapConstructor; // The X(Swappable) constructor (if it has one). - Class<? extends Number> numberConstructorType; // The class type of the object in the number constructor. - Constructor<T> objectMapConstructor; // The X(ObjectMap) constructor (if it has one). - Method toObjectMapMethod; // The toObjectMap() method (if it has one). - Method swapMethod; // The swap() 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. PojoSwap<T,?> pojoSwap; // The object POJO swap associated with this bean (if it has one). BeanFilter 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 MetadataMap extMeta = new MetadataMap(); // Extended metadata private Throwable initException; // Any exceptions thrown in the init() method. private boolean hasChildPojoSwaps; // True if this class or any subclass of this class has a PojoSwap 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>. @@ -118,7 +80,7 @@ public final class ClassMeta<T> implements Type { * Used for delayed initialization when the possibility of class reference loops exist. */ ClassMeta(Class<T> innerClass, BeanContext beanContext, boolean delayedInit) { - this.innerClass = innerClass; + super(innerClass); this.beanContext = beanContext; if (! delayedInit) init(); @@ -132,161 +94,14 @@ public final class ClassMeta<T> implements Type { beanFilter = findBeanFilter(beanContext); pojoSwap = findPojoSwap(beanContext); - serializedClassMeta = (pojoSwap == null ? this : beanContext.getClassMeta(pojoSwap.getSwapClass())); - if (serializedClassMeta == null) - serializedClassMeta = this; - - if (innerClass != Object.class) { - this.noArgConstructor = beanContext.getImplClassConstructor(innerClass, Visibility.PUBLIC); - if (noArgConstructor == null) - noArgConstructor = findNoArgConstructor(innerClass, Visibility.PUBLIC); + if (innerClass != Object.class && this.noArgConstructor == null) { + this.noArgConstructor = (Constructor<T>) beanContext.getImplClassConstructor(innerClass, Visibility.PUBLIC); } this.hasChildPojoSwaps = beanContext.hasChildPojoSwaps(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 (c.equals(Class.class)) - classCategory = CLASS; - 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(org.apache.juneau.annotation.URI.class)) - classCategory = URI; - else if (isParentClass(Reader.class, c)) - classCategory = READER; - else if (isParentClass(InputStream.class, c)) - classCategory = INPUTSTREAM; - } - - isMemberClass = c.isMemberClass() && ! isStatic(c); - - // 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","forString"}) { - if (this.fromStringMethod == null) { - for (Method m : c.getMethods()) { - if (isStatic(m) && isPublic(m) && isNotDeprecated(m)) { - String mName = m.getName(); - if (mName.equals(methodName) && m.getReturnType() == innerClass) { - Class<?>[] args = m.getParameterTypes(); - if (args.length == 1 && args[0] == String.class) { - this.fromStringMethod = m; - break; - } - } - } - } - } - } - - // Special cases - try { - if (c == TimeZone.class) - this.fromStringMethod = c.getMethod("getTimeZone", String.class); - else if (c == Locale.class) - this.fromStringMethod = LocaleAsString.class.getMethod("fromString", String.class); - } catch (NoSuchMethodException e1) {} - - // Find toObjectMap() method if present. - for (Method m : c.getMethods()) { - if (isPublic(m) && isNotDeprecated(m) && ! isStatic(m)) { - String mName = m.getName(); - if (mName.equals("toObjectMap")) { - if (m.getParameterTypes().length == 0 && m.getReturnType() == ObjectMap.class) { - this.toObjectMapMethod = m; - break; - } - } else if (mName.equals("swap")) { - if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0] == BeanSession.class) { - this.swapMethod = 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()) { - if (isPublic(cs) && isNotDeprecated(cs)) { - 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; - else if (swapMethod != null && swapMethod.getReturnType().getClass().isAssignableFrom(arg)) - this.swapConstructor = cs; - else if (classCategory != NUMBER && (Number.class.isAssignableFrom(arg) || (arg.isPrimitive() && (arg == int.class || arg == short.class || arg == long.class || arg == float.class || arg == double.class)))) { - this.numberConstructor = cs; - this.numberConstructorType = (Class<? extends Number>)ClassUtils.getWrapperIfPrimitive(arg); - } - } - } - } - if (swapMethod != null) { - this.pojoSwap = new PojoSwap<T,Object>(c, swapMethod.getReturnType()) { + this.pojoSwap = new PojoSwap<T,Object>(innerClass, swapMethod.getReturnType()) { @Override public Object swap(BeanSession session, Object o) throws SerializeException { try { @@ -308,15 +123,16 @@ public final class ClassMeta<T> implements Type { }; } - // Note: Primitive types are normally abstract. - isAbstract = Modifier.isAbstract(c.getModifiers()) && ! isPrimitive(); + serializedClassMeta = (pojoSwap == null ? this : beanContext.getClassMeta(pojoSwap.getSwapClass())); + if (serializedClassMeta == null) + serializedClassMeta = this; // If this is an array, get the element type. - if (classCategory == ARRAY) + if (cc == 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) { + else if (cc == MAP) { ClassMeta[] parameters = beanContext.findParameters(innerClass, innerClass); if (parameters != null && parameters.length == 2) { keyType = parameters[0]; @@ -328,7 +144,7 @@ public final class ClassMeta<T> implements Type { } // If this is a COLLECTION, see if it's parameterized (e.g. AddressBook extends LinkedList<Person>) - else if (classCategory == COLLECTION) { + else if (cc == COLLECTION) { ClassMeta[] parameters = beanContext.findParameters(innerClass, innerClass); if (parameters != null && parameters.length == 1) { elementType = parameters[0]; @@ -339,7 +155,7 @@ public final class ClassMeta<T> implements Type { // 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) { + else if (cc == OTHER) { BeanMeta newMeta = null; try { @@ -349,49 +165,11 @@ public final class ClassMeta<T> implements Type { notABeanReason = e.getMessage(); throw e; } - if (notABeanReason != null) - classCategory = OTHER; - else { + if (notABeanReason == null) { 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) { @@ -399,20 +177,6 @@ public final class ClassMeta<T> implements Type { 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); - if (isBean()) dictionaryName = resolvedDictionaryName = getBeanMeta().getDictionaryName(); @@ -449,25 +213,6 @@ public final class ClassMeta<T> implements Type { } /** - * 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}. * * @return <jk>true</jk> if this class has subtypes. @@ -477,16 +222,6 @@ public final class ClassMeta<T> implements Type { } /** - * 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 PojoSwap} associated with it. * <p> * Used when transforming bean properties to prevent having to look up transforms if we know for certain @@ -519,10 +254,10 @@ public final class ClassMeta<T> implements Type { try { Pojo p = innerClass.getAnnotation(Pojo.class); if (p != null) { - Class<?> c = p.swap(); - if (c != Null.class) { - if (ClassUtils.isParentClass(PojoSwap.class, c)) - return (PojoSwap<T,?>)c.newInstance(); + Class<?> swapClass = p.swap(); + if (swapClass != Null.class) { + if (ClassUtils.isParentClass(PojoSwap.class, swapClass)) + return (PojoSwap<T,?>)swapClass.newInstance(); throw new RuntimeException("TODO - Surrogate classes not yet supported."); } } @@ -536,30 +271,6 @@ public final class ClassMeta<T> implements Type { } /** - * 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() && ! isStatic(c); - for (Constructor cc : c.getConstructors()) { - mod = cc.getModifiers(); - if (cc.getParameterTypes().length == (isMemberClass ? 1 : 0) && v.isVisible(mod) && isNotDeprecated(cc)) - return v.transform(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. @@ -593,15 +304,6 @@ public final class ClassMeta<T> implements Type { } /** - * Returns the {@link Class} object that this class type wraps. - * - * @return The wrapped class object. - */ - public Class<T> getInnerClass() { - return innerClass; - } - - /** * Returns the serialized (swapped) form of this class if there is an {@link PojoSwap} associated with it. * * @return The serialized class type, or this object if no swap is associated with the class. @@ -639,263 +341,21 @@ public final class ClassMeta<T> implements Type { } /** - * 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 Map} or it's a bean. - * - * @return <jk>true</jk> if this class is a subclass of {@link Map} or it's a bean. - */ - public boolean isMapOrBean() { - return classCategory == MAP || classCategory == BEANMAP || classCategory == BEAN; - } - - /** - * 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 a subclass of {@link Collection} or is an array. - * - * @return <jk>true</jk> if this class is a subclass of {@link Collection} or is an array. - */ - public boolean isCollectionOrArray() { - return classCategory == COLLECTION || classCategory == ARRAY; - } - - /** - * Returns <jk>true</jk> if this class is {@link Class}. - * - * @return <jk>true</jk> if this class is {@link Class}. - */ - public boolean isClass() { - return classCategory == CLASS; - } - - /** - * 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; + return beanMeta != null; } /** - * 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. - * - * @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. + * Returns <jk>true</jk> if this class is a subclass of {@link Map} or it's a bean. * - * @return The public methods on this class. + * @return <jk>true</jk> if this class is a subclass of {@link Map} or it's a bean. */ - public Map<String,Method> getPublicMethods() { - if (publicMethods == null) { - synchronized(this) { - Map<String,Method> map = new LinkedHashMap<String,Method>(); - for (Method m : innerClass.getMethods()) - if (isPublic(m) && isNotDeprecated(m)) - map.put(ClassUtils.getMethodSignature(m), m); - publicMethods = Collections.unmodifiableMap(map); - } - } - return publicMethods; + public boolean isMapOrBean() { + return cc == MAP || cc == BEANMAP || beanMeta != null; } /** @@ -930,11 +390,11 @@ public final class ClassMeta<T> implements Type { /** * Returns the language-specified extended metadata on this class. * - * @param c The name of the metadata class to create. + * @param extMetaClass The name of the metadata class to create. * @return Extended metadata on this class. Never <jk>null</jk>. */ - public <M extends ClassMetaExtended> M getExtendedMeta(Class<M> c) { - return extMeta.get(c, this); + public <M extends ClassMetaExtended> M getExtendedMeta(Class<M> extMetaClass) { + return extMeta.get(extMetaClass, this); } /** @@ -1057,50 +517,6 @@ public final class ClassMeta<T> implements Type { } /** - * 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. @@ -1136,16 +552,6 @@ public final class ClassMeta<T> implements Type { } /** - * 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: @@ -1169,11 +575,11 @@ public final class ClassMeta<T> implements Type { Method m = fromStringMethod; if (m != null) return (T)m.invoke(null, arg); - Constructor<T> c = stringConstructor; - if (c != null) { + Constructor<T> con = stringConstructor; + if (con != null) { if (isMemberClass) - return c.newInstance(outer, arg); - return c.newInstance(arg); + return con.newInstance(outer, arg); + return con.newInstance(arg); } throw new InstantiationError("No string constructor or valueOf(String) method found for class '"+getInnerClass().getName()+"'"); } @@ -1197,12 +603,12 @@ public final class ClassMeta<T> implements Type { * @throws InvocationTargetException If the underlying constructor throws an exception. */ public T newInstanceFromNumber(BeanSession session, Object outer, Number arg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException { - Constructor<T> c = numberConstructor; - if (c != null) { + Constructor<T> con = numberConstructor; + if (con != null) { Object arg2 = session.convertToType(arg, numberConstructor.getParameterTypes()[0]); if (isMemberClass) - return c.newInstance(outer, arg2); - return c.newInstance(arg2); + return con.newInstance(outer, arg2); + return con.newInstance(arg2); } throw new InstantiationError("No string constructor or valueOf(Number) method found for class '"+getInnerClass().getName()+"'"); } @@ -1225,11 +631,11 @@ public final class ClassMeta<T> implements Type { * @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) { + Constructor<T> con = objectMapConstructor; + if (con != null) { if (isMemberClass) - return c.newInstance(outer, arg); - return c.newInstance(arg); + return con.newInstance(outer, arg); + return con.newInstance(arg); } throw new InstantiationError("No map constructor method found for class '"+getInnerClass().getName()+"'"); } @@ -1253,9 +659,9 @@ public final class ClassMeta<T> implements Type { 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); + Constructor<? extends T> con = getConstructor(); + if (con != null) + return con.newInstance((Object[])null); InvocationHandler h = getProxyInvocationHandler(); if (h != null) return (T)Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] { getInnerClass(), java.io.Serializable.class }, h); @@ -1310,7 +716,7 @@ public final class ClassMeta<T> implements Type { public boolean same(ClassMeta<?> cm) { if (equals(cm)) return true; - return (isPrimitive() && classCategory == cm.classCategory); + return (isPrimitive() && cc == cm.cc); } @Override /* Object */ @@ -1341,51 +747,26 @@ public final class ClassMeta<T> implements Type { int i = n.lastIndexOf('.'); n = n.substring(i == -1 ? 0 : i+1).replace('$', '.'); } - switch(classCategory) { - case ARRAY: - return elementType.toString(sb, simple).append('[').append(']'); - case MAP: - return sb.append(n).append(keyType.isObject() && valueType.isObject() ? "" : "<"+keyType.toString(simple)+","+valueType.toString(simple)+">"); - case BEANMAP: - return sb.append(BeanMap.class.getName()).append('<').append(n).append('>'); - case COLLECTION: - return sb.append(n).append(elementType.isObject() ? "" : "<"+elementType.toString(simple)+">"); - case OTHER: - if (simple) - return sb.append(n); - sb.append("OTHER-").append(n).append(",notABeanReason=").append(notABeanReason); - if (initException != null) - sb.append(",initException=").append(initException); - return sb; - default: + if (cc == ARRAY) + return elementType.toString(sb, simple).append('[').append(']'); + if (cc == MAP) + return sb.append(n).append(keyType.isObject() && valueType.isObject() ? "" : "<"+keyType.toString(simple)+","+valueType.toString(simple)+">"); + if (cc == BEANMAP) + return sb.append(BeanMap.class.getName()).append('<').append(n).append('>'); + if (cc == COLLECTION) + return sb.append(n).append(elementType.isObject() ? "" : "<"+elementType.toString(simple)+">"); + if (cc == OTHER && beanMeta == null) { + if (simple) return sb.append(n); + sb.append("OTHER-").append(n).append(",notABeanReason=").append(notABeanReason); + if (initException != null) + sb.append(",initException=").append(initException); + return sb; } + return sb.append(n); } - /** - * 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); - } - - private static class LocaleAsString { + static class LocaleAsString { private static Method forLanguageTagMethod; static { try { @@ -1393,7 +774,6 @@ public final class ClassMeta<T> implements Type { } catch (NoSuchMethodException e) {} } - @SuppressWarnings("unused") public static final Locale fromString(String localeString) { if (forLanguageTagMethod != null) { if (localeString.indexOf('_') != -1) http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/dba656f3/juneau-core/src/main/java/org/apache/juneau/ClassMetaSimple.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMetaSimple.java b/juneau-core/src/main/java/org/apache/juneau/ClassMetaSimple.java new file mode 100644 index 0000000..b0de937 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/ClassMetaSimple.java @@ -0,0 +1,662 @@ +// *************************************************************************************************************************** +// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * +// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * +// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * +// * with the License. You may obtain a copy of the License at * +// * * +// * http://www.apache.org/licenses/LICENSE-2.0 * +// * * +// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * +// * specific language governing permissions and limitations under the License. * +// *************************************************************************************************************************** +package org.apache.juneau; + +import static org.apache.juneau.ClassMetaSimple.ClassCategory.*; +import static org.apache.juneau.internal.ClassUtils.*; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.net.URI; +import java.util.*; + +import org.apache.juneau.ClassMeta.*; +import org.apache.juneau.annotation.*; +import org.apache.juneau.internal.*; + +/** + * Information about a class that can be gathered through reflection alone. + * + * @param <T> The class type of the wrapped class. + */ +public class ClassMetaSimple<T> implements Type { + + /** Class categories. */ + enum ClassCategory { + MAP, COLLECTION, CLASS, NUMBER, DECIMAL, BOOLEAN, CHAR, DATE, ARRAY, ENUM, OTHER, CHARSEQ, STR, OBJ, URI, BEANMAP, READER, INPUTSTREAM + } + + final Class<T> innerClass; // The class being wrapped. + final ClassCategory cc; // The class category. + final Method fromStringMethod; // The static valueOf(String) or fromString(String) or forString(String) method (if it has one). + Constructor<T> + noArgConstructor; // The no-arg constructor for this class (if it has one). + final Constructor<T> + stringConstructor, // The X(String) constructor (if it has one). + numberConstructor, // The X(Number) constructor (if it has one). + swapConstructor, // The X(Swappable) constructor (if it has one). + objectMapConstructor; // The X(ObjectMap) constructor (if it has one). + final Class<? extends Number> numberConstructorType; // The class type of the object in the number constructor. + final Method + toObjectMapMethod, // The toObjectMap() method (if it has one). + swapMethod, // The swap() method (if it has one). + namePropertyMethod, // The method to set the name on an object (if it has one). + parentPropertyMethod; // The method to set the parent on an object (if it has one). + final 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. + final Object primitiveDefault; // Default value for primitive type classes. + final 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; + + + @SuppressWarnings({"unchecked","rawtypes"}) + ClassMetaSimple(Class<T> innerClass) { + this.innerClass = innerClass; + + Class<T> c = innerClass; + ClassCategory _cc = ClassCategory.OTHER; + boolean _isDelegate = false; + Method + _fromStringMethod = null, + _toObjectMapMethod = null, + _swapMethod = null, + _parentPropertyMethod = null, + _namePropertyMethod = null; + Constructor<T> + _noArgConstructor = null, + _stringConstructor = null, + _objectMapConstructor = null, + _swapConstructor = null, + _numberConstructor = null; + Class<? extends Number> _numberConstructorType = null; + Object _primitiveDefault = null; + Map<String,Method> + _publicMethods = new LinkedHashMap<String,Method>(), + _remoteableMethods = null; + + if (c.isPrimitive()) { + if (c == Boolean.TYPE) + _cc = 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) + _cc = DECIMAL; + else + _cc = NUMBER; + } + else if (c == Character.TYPE) + _cc = CHAR; + } else { + if (isParentClass(Delegate.class, c)) + _isDelegate = true; + if (c == Object.class) + _cc = OBJ; + else if (c.isEnum()) + _cc = ENUM; + else if (c.equals(Class.class)) + _cc = CLASS; + else if (isParentClass(CharSequence.class, c)) { + if (c.equals(String.class)) + _cc = STR; + else + _cc = CHARSEQ; + } + else if (isParentClass(Number.class, c)) { + if (isParentClass(Float.class, c) || isParentClass(Double.class, c)) + _cc = DECIMAL; + else + _cc = NUMBER; + } + else if (isParentClass(Collection.class, c)) + _cc = COLLECTION; + else if (isParentClass(Map.class, c)) { + if (isParentClass(BeanMap.class, c)) + _cc = BEANMAP; + else + _cc = MAP; + } + else if (c == Character.class) + _cc = CHAR; + else if (c == Boolean.class) + _cc = BOOLEAN; + else if (isParentClass(Date.class, c) || isParentClass(Calendar.class, c)) + _cc = DATE; + else if (c.isArray()) + _cc = ARRAY; + else if (isParentClass(URL.class, c) || isParentClass(URI.class, c) || c.isAnnotationPresent(org.apache.juneau.annotation.URI.class)) + _cc = URI; + else if (isParentClass(Reader.class, c)) + _cc = READER; + else if (isParentClass(InputStream.class, c)) + _cc = INPUTSTREAM; + } + + isMemberClass = c.isMemberClass() && ! isStatic(c); + + // 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","forString"}) { + if (_fromStringMethod == null) { + for (Method m : c.getMethods()) { + if (isStatic(m) && isPublic(m) && isNotDeprecated(m)) { + String mName = m.getName(); + if (mName.equals(methodName) && m.getReturnType() == c) { + Class<?>[] args = m.getParameterTypes(); + if (args.length == 1 && args[0] == String.class) { + _fromStringMethod = m; + break; + } + } + } + } + } + } + + // Special cases + try { + if (c == TimeZone.class) + _fromStringMethod = c.getMethod("getTimeZone", String.class); + else if (c == Locale.class) + _fromStringMethod = LocaleAsString.class.getMethod("fromString", String.class); + } catch (NoSuchMethodException e1) {} + + // Find toObjectMap() method if present. + for (Method m : c.getMethods()) { + if (isPublic(m) && isNotDeprecated(m) && ! isStatic(m)) { + String mName = m.getName(); + if (mName.equals("toObjectMap")) { + if (m.getParameterTypes().length == 0 && m.getReturnType() == ObjectMap.class) { + _toObjectMapMethod = m; + break; + } + } else if (mName.equals("swap")) { + if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0] == BeanSession.class) { + _swapMethod = 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; + } + } + + // Note: Primitive types are normally abstract. + isAbstract = Modifier.isAbstract(c.getModifiers()) && ! c.isPrimitive(); + + // Find constructor(String) method if present. + for (Constructor cs : c.getConstructors()) { + if (isPublic(cs) && isNotDeprecated(cs)) { + Class<?>[] args = cs.getParameterTypes(); + if (args.length == (isMemberClass ? 1 : 0) && c != Object.class && ! isAbstract) { + _noArgConstructor = cs; + } else if (args.length == (isMemberClass ? 2 : 1)) { + Class<?> arg = args[(isMemberClass ? 1 : 0)]; + if (arg == String.class) + _stringConstructor = cs; + else if (ObjectMap.class.isAssignableFrom(arg)) + _objectMapConstructor = cs; + else if (_swapMethod != null && _swapMethod.getReturnType().isAssignableFrom(arg)) + _swapConstructor = cs; + else if (_cc != NUMBER && (Number.class.isAssignableFrom(arg) || (arg.isPrimitive() && (arg == int.class || arg == short.class || arg == long.class || arg == float.class || arg == double.class)))) { + _numberConstructor = cs; + _numberConstructorType = (Class<? extends Number>)ClassUtils.getWrapperIfPrimitive(arg); + } + } + } + } + + + 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; + } + + for (Method m : c.getMethods()) + if (isPublic(m) && isNotDeprecated(m)) + _publicMethods.put(ClassUtils.getMethodSignature(m), m); + + if (c.getAnnotation(Remoteable.class) != null) { + _remoteableMethods = _publicMethods; + } else { + for (Method m : c.getMethods()) { + if (m.getAnnotation(Remoteable.class) != null) { + if (_remoteableMethods == null) + _remoteableMethods = new LinkedHashMap<String,Method>(); + _remoteableMethods.put(ClassUtils.getMethodSignature(m), m); + } + } + } + + this.cc = _cc; + this.isDelegate = _isDelegate; + this.fromStringMethod = _fromStringMethod; + this.toObjectMapMethod = _toObjectMapMethod; + this.swapMethod = _swapMethod; + this.parentPropertyMethod = _parentPropertyMethod; + this.namePropertyMethod =_namePropertyMethod; + this.noArgConstructor = _noArgConstructor; + this.stringConstructor = _stringConstructor; + this.objectMapConstructor =_objectMapConstructor; + this.swapConstructor = _swapConstructor; + this.numberConstructor = _numberConstructor; + this.numberConstructorType = _numberConstructorType; + this.primitiveDefault = _primitiveDefault; + this.publicMethods = _publicMethods; + this.remoteableMethods = _remoteableMethods; + } + + /** + * Returns the category of this class. + * + * @return The category of this class. + */ + public final ClassCategory getClassCategory() { + return cc; + } + + /** + * Returns <jk>true</jk> if this class is a superclass of or the same as the specified class. + * + * @param child The comparison class. + * @return <jk>true</jk> if this class is a superclass of or the same as the specified class. + */ + public final boolean isAssignableFrom(Class<?> child) { + return isParentClass(this.innerClass, child); + } + + /** + * Returns <jk>true</jk> if this class is a subclass of or the same as the specified class. + * + * @param parent The comparison class. + * @return <jk>true</jk> if this class is a subclass of or the same as the specified class. + */ + public final boolean isInstanceOf(Class<?> parent) { + return isParentClass(parent, this.innerClass); + } + + /** + * Returns the {@link Class} object that this class type wraps. + * + * @return The wrapped class object. + */ + public final Class<T> getInnerClass() { + return innerClass; + } + + /** + * 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 final 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 final boolean isMap() { + return cc == MAP || cc == 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 cc == 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 cc == COLLECTION; + } + + /** + * Returns <jk>true</jk> if this class is a subclass of {@link Collection} or is an array. + * + * @return <jk>true</jk> if this class is a subclass of {@link Collection} or is an array. + */ + public boolean isCollectionOrArray() { + return cc == COLLECTION || cc == ARRAY; + } + + /** + * Returns <jk>true</jk> if this class is {@link Class}. + * + * @return <jk>true</jk> if this class is {@link Class}. + */ + public boolean isClass() { + return cc == CLASS; + } + + /** + * 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 cc == 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 cc == ARRAY; + } + + /** + * Returns <jk>true</jk> if this class is {@link Object}. + * + * @return <jk>true</jk> if this class is {@link Object}. + */ + public boolean isObject() { + return cc == 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 cc != 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 cc == NUMBER || cc == 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 cc == 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 cc == 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 cc == STR || cc == 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 cc == 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 cc == 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 cc == 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 cc == 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 cc == 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 cc == 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 cc == 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. + * + * @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. + * + * @return The public methods on this class. + */ + public Map<String,Method> getPublicMethods() { + return publicMethods; + } + + /** + * 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 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; + } + + /** + * 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); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/dba656f3/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java index 49c1083..c4fb6b6 100644 --- a/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java +++ b/juneau-core/src/main/java/org/apache/juneau/internal/ClassUtils.java @@ -316,19 +316,43 @@ public final class ClassUtils { return Modifier.isPublic(c.getModifiers()); } + + /** + * 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"}) + public static final <T> Constructor<T> findNoArgConstructor(Class<T> c, Visibility v) { + int mod = c.getModifiers(); + if (Modifier.isAbstract(mod)) + return null; + boolean isMemberClass = c.isMemberClass() && ! isStatic(c); + for (Constructor cc : c.getConstructors()) { + mod = cc.getModifiers(); + if (cc.getParameterTypes().length == (isMemberClass ? 1 : 0) && v.isVisible(mod) && isNotDeprecated(cc)) + return v.transform(cc); + } + return null; + } + /** * Finds the real parameter type of the specified class. * * @param c The class containing the parameters (e.g. PojoSwap<T,S>) * @param index The zero-based index of the parameter to resolve. - * @param o The object we're trying to resolve the parameter type for. + * @param oc The class we're trying to resolve the parameter type for. * @return The resolved real class. */ - public static Class<?> resolveParameterType(Class<?> c, int index, Object o) { + public static Class<?> resolveParameterType(Class<?> c, int index, Class<?> oc) { // We need to make up a mapping of type names. Map<Type,Type> typeMap = new HashMap<Type,Type>(); - Class<?> oc = o.getClass(); while (c != oc.getSuperclass()) { extractTypes(typeMap, oc); oc = oc.getSuperclass(); @@ -352,9 +376,7 @@ public final class ClassUtils { List<Class<?>> nestedOuterTypes = new LinkedList<Class<?>>(); for (Class<?> ec = oc.getEnclosingClass(); ec != null; ec = ec.getEnclosingClass()) { try { - Field this$0 = oc.getDeclaredField("this$0"); - Object outerInstance = this$0.get(o); - Class<?> outerClass = outerInstance.getClass(); + Class<?> outerClass = oc.getClass(); nestedOuterTypes.add(outerClass); Map<Type,Type> outerTypeMap = new HashMap<Type,Type>(); extractTypes(outerTypeMap, outerClass);