blob: 4c1a3338d5ad7f65ed036a7e875f63bcbbf77c3e [file] [log] [blame]
// ***************************************************************************************************************************
// * 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.ClassMeta.ClassCategory.*;
import static org.apache.juneau.internal.ClassUtils.*;
import java.io.*;
import java.lang.reflect.*;
import java.lang.reflect.Proxy;
import java.net.*;
import java.net.URI;
import java.util.*;
import java.util.concurrent.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.remoteable.*;
import org.apache.juneau.serializer.*;
import org.apache.juneau.transform.*;
import org.apache.juneau.utils.*;
/**
* 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.
*
* @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, OTHER, CHARSEQ, STR, OBJ, URI, BEANMAP, READER, INPUTSTREAM, VOID, ARGS
}
final Class<T> innerClass; // The class being wrapped.
private final Class<? extends T> implClass; // The implementation class to use if this is an interface.
private final ClassCategory cc; // The class category.
private final Method fromStringMethod; // The static valueOf(String) or fromString(String) or forString(String) method (if it has one).
private final Constructor<? extends T>
noArgConstructor; // The no-arg constructor for this class (if it has one).
private 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).
private final Class<?>
swapMethodType, // The class type of the object in the number constructor.
numberConstructorType;
private final Method
swapMethod, // The swap() method (if it has one).
unswapMethod, // The unswap() 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).
private 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.
private final Object primitiveDefault; // Default value for primitive type classes.
private final Map<String,Method>
remoteableMethods, // Methods annotated with @RemoteMethod.
publicMethods; // All public methods, including static methods.
private final PojoSwap<?,?>[] childPojoSwaps; // Any PojoSwaps where the normal type is a subclass of this class.
private final ConcurrentHashMap<Class<?>,PojoSwap<?,?>>
childSwapMap, // Maps normal subclasses to PojoSwaps.
childUnswapMap; // Maps swap subclasses to PojoSwaps.
private final PojoSwap<T,?> pojoSwap; // The object POJO swap associated with this bean (if it has one).
private final BeanFilter beanFilter; // The bean filter associated with this bean (if it has one).
private final MetadataMap extMeta; // Extended metadata
private final BeanContext beanContext; // The bean context that created this object.
private final ClassMeta<?>
serializedClassMeta, // The transformed class type (if class has swap associated with it).
elementType, // If ARRAY or COLLECTION, the element class type.
keyType, // If MAP, the key class type.
valueType; // If MAP, the value class type.
private final BeanMeta<T> beanMeta; // The bean meta for this bean class (if it's a bean).
private final String
typePropertyName, // The property name of the _type property for this class and subclasses.
notABeanReason, // If this isn't a bean, the reason why.
dictionaryName; // The dictionary name of this class if it has one.
private final Throwable initException; // Any exceptions thrown in the init() method.
private final InvocationHandler invocationHandler; // The invocation handler for this class (if it has one).
private final BeanRegistry beanRegistry; // The bean registry of this class meta (if it has one).
private final ClassMeta<?>[] args; // Arg types if this is an array of args.
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;
/**
* 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 implClass For interfaces and abstract classes, this represents the "real" class to instantiate.
* Can be <jk>null</jk>.
* @param beanFilter The {@link BeanFilter} programmatically associated with this class.
* Can be <jk>null</jk>.
* @param pojoSwap The {@link PojoSwap} programmatically associated with this class.
* Can be <jk>null</jk>.
* @param childPojoSwap The child {@link PojoSwap PojoSwaps} programmatically associated with this class.
* These are the <code>PojoSwaps</code> that have normal classes that are subclasses of this class.
* Can be <jk>null</jk>.
* @param delayedInit Don't call init() in constructor.
* Used for delayed initialization when the possibility of class reference loops exist.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
ClassMeta(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?> pojoSwap, PojoSwap<?,?>[] childPojoSwaps) {
this.innerClass = innerClass;
this.beanContext = beanContext;
// We always immediately add this class meta to the bean context cache so that we can resolve recursive references.
if (beanContext != null && beanContext.cmCache != null)
beanContext.cmCache.put(innerClass, this);
ClassMetaBuilder<T> builder = new ClassMetaBuilder(innerClass, beanContext, implClass, beanFilter, pojoSwap, childPojoSwaps);
this.cc = builder.cc;
this.isDelegate = builder.isDelegate;
this.fromStringMethod = builder.fromStringMethod;
this.swapMethod = builder.swapMethod;
this.unswapMethod = builder.unswapMethod;
this.swapMethodType = builder.swapMethodType;
this.parentPropertyMethod = builder.parentPropertyMethod;
this.namePropertyMethod = builder.namePropertyMethod;
this.noArgConstructor = builder.noArgConstructor;
this.stringConstructor = builder.stringConstructor;
this.swapConstructor = builder.swapConstructor;
this.numberConstructor = builder.numberConstructor;
this.numberConstructorType = builder.numberConstructorType;
this.primitiveDefault = builder.primitiveDefault;
this.publicMethods = builder.publicMethods;
this.remoteableMethods = builder.remoteableMethods;
this.beanFilter = beanFilter;
this.pojoSwap = builder.pojoSwap;
this.extMeta = new MetadataMap();
this.keyType = builder.keyType;
this.valueType = builder.valueType;
this.elementType = builder.elementType;
this.notABeanReason = builder.notABeanReason;
this.beanMeta = builder.beanMeta;
this.initException = builder.initException;
this.typePropertyName = builder.typePropertyName;
this.dictionaryName = builder.dictionaryName;
this.serializedClassMeta = builder.serializedClassMeta;
this.invocationHandler = builder.invocationHandler;
this.beanRegistry = builder.beanRegistry;
this.isMemberClass = builder.isMemberClass;
this.isAbstract = builder.isAbstract;
this.implClass = builder.implClass;
this.childUnswapMap = builder.childUnswapMap;
this.childSwapMap = builder.childSwapMap;
this.childPojoSwaps = builder.childPojoSwaps;
this.args = null;
}
/**
* Copy constructor.
* Used for creating Map and Collection class metas that shouldn't be cached.
*/
ClassMeta(ClassMeta<T> mainType, ClassMeta<?> keyType, ClassMeta<?> valueType, ClassMeta<?> elementType) {
this.innerClass = mainType.innerClass;
this.implClass = mainType.implClass;
this.childPojoSwaps = mainType.childPojoSwaps;
this.childSwapMap = mainType.childSwapMap;
this.childUnswapMap = mainType.childUnswapMap;
this.cc = mainType.cc;
this.fromStringMethod = mainType.fromStringMethod;
this.noArgConstructor = mainType.noArgConstructor;
this.stringConstructor = mainType.stringConstructor;
this.numberConstructor = mainType.numberConstructor;
this.swapConstructor = mainType.swapConstructor;
this.swapMethodType = mainType.swapMethodType;
this.numberConstructorType = mainType.numberConstructorType;
this.swapMethod = mainType.swapMethod;
this.unswapMethod = mainType.unswapMethod;
this.namePropertyMethod = mainType.namePropertyMethod;
this.parentPropertyMethod = mainType.parentPropertyMethod;
this.isDelegate = mainType.isDelegate;
this.isAbstract = mainType.isAbstract;
this.isMemberClass = mainType.isMemberClass;
this.primitiveDefault = mainType.primitiveDefault;
this.remoteableMethods = mainType.remoteableMethods;
this.publicMethods = mainType.publicMethods;
this.beanContext = mainType.beanContext;
this.serializedClassMeta = this;
this.elementType = elementType;
this.keyType = keyType;
this.valueType = valueType;
this.invocationHandler = mainType.invocationHandler;
this.beanMeta = mainType.beanMeta;
this.typePropertyName = mainType.typePropertyName;
this.dictionaryName = mainType.dictionaryName;
this.notABeanReason = mainType.notABeanReason;
this.pojoSwap = mainType.pojoSwap;
this.beanFilter = mainType.beanFilter;
this.extMeta = mainType.extMeta;
this.initException = mainType.initException;
this.beanRegistry = mainType.beanRegistry;
this.args = null;
}
/**
* Constructor for args-arrays.
*/
@SuppressWarnings("unchecked")
ClassMeta(ClassMeta<?>[] args) {
this.innerClass = (Class<T>) Object[].class;
this.args = args;
this.implClass = null;
this.childPojoSwaps = null;
this.childSwapMap = null;
this.childUnswapMap = null;
this.cc = ARGS;
this.fromStringMethod = null;
this.noArgConstructor = null;
this.stringConstructor = null;
this.numberConstructor = null;
this.swapConstructor = null;
this.swapMethodType = null;
this.numberConstructorType = null;
this.swapMethod = null;
this.unswapMethod = null;
this.namePropertyMethod = null;
this.parentPropertyMethod = null;
this.isDelegate = false;
this.isAbstract = false;
this.isMemberClass = false;
this.primitiveDefault = null;
this.remoteableMethods = null;
this.publicMethods = null;
this.beanContext = null;
this.serializedClassMeta = this;
this.elementType = null;
this.keyType = null;
this.valueType = null;
this.invocationHandler = null;
this.beanMeta = null;
this.typePropertyName = null;
this.dictionaryName = null;
this.notABeanReason = null;
this.pojoSwap = null;
this.beanFilter = null;
this.extMeta = new MetadataMap();
this.initException = null;
this.beanRegistry = null;
}
@SuppressWarnings({"unchecked","rawtypes","hiding"})
private class ClassMetaBuilder<T> {
Class<T> innerClass;
Class<? extends T> implClass;
BeanContext beanContext;
ClassCategory cc = ClassCategory.OTHER;
boolean
isDelegate = false,
isMemberClass = false,
isAbstract = false;
Method
fromStringMethod = null,
swapMethod = null,
unswapMethod = null,
parentPropertyMethod = null,
namePropertyMethod = null;
Constructor<T>
noArgConstructor = null,
stringConstructor = null,
swapConstructor = null,
numberConstructor = null;
Class<?>
swapMethodType = null,
numberConstructorType = null;
Object primitiveDefault = null;
Map<String,Method>
publicMethods = new LinkedHashMap<String,Method>(),
remoteableMethods = new LinkedHashMap<String,Method>();
ClassMeta<?>
keyType = null,
valueType = null,
elementType = null,
serializedClassMeta = null;
String
typePropertyName = null,
notABeanReason = null,
dictionaryName = null;
Throwable initException = null;
BeanMeta beanMeta = null;
PojoSwap pojoSwap = null;
InvocationHandler invocationHandler = null;
BeanRegistry beanRegistry = null;
PojoSwap<?,?>[] childPojoSwaps;
ConcurrentHashMap<Class<?>,PojoSwap<?,?>>
childSwapMap,
childUnswapMap;
private ClassMetaBuilder(Class<T> innerClass, BeanContext beanContext, Class<? extends T> implClass, BeanFilter beanFilter, PojoSwap<T,?> pojoSwap, PojoSwap<?,?>[] childPojoSwaps) {
this.innerClass = innerClass;
this.beanContext = beanContext;
this.implClass = implClass;
this.childPojoSwaps = childPojoSwaps;
this.childSwapMap = childPojoSwaps == null ? null : new ConcurrentHashMap<Class<?>,PojoSwap<?,?>>();
this.childUnswapMap = childPojoSwaps == null ? null : new ConcurrentHashMap<Class<?>,PojoSwap<?,?>>();
Class<T> c = innerClass;
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 (c == void.class || c == Void.class)
cc = VOID;
} 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 swap() method if present.
for (Method m : c.getMethods()) {
if (isPublic(m) && isNotDeprecated(m) && ! isStatic(m)) {
String mName = m.getName();
if (mName.equals("swap")) {
Class<?>[] pt = m.getParameterTypes();
if (pt.length == 1 && pt[0] == BeanSession.class) {
swapMethod = m;
swapMethodType = m.getReturnType();
break;
}
}
}
}
// Find unswap() method if present.
if (swapMethod != null) {
for (Method m : c.getMethods()) {
if (isPublic(m) && isNotDeprecated(m) && isStatic(m)) {
String mName = m.getName();
if (mName.equals("unswap")) {
Class<?>[] pt = m.getParameterTypes();
if (pt.length == 2 && pt[0] == BeanSession.class && pt[1] == swapMethodType) {
unswapMethod = 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 (swapMethodType != null && swapMethodType.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 = 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);
Map<Class<?>,Remoteable> remoteableMap = ReflectionUtils.findAnnotationsMap(Remoteable.class, c);
if (! remoteableMap.isEmpty()) {
Map.Entry<Class<?>,Remoteable> e = remoteableMap.entrySet().iterator().next(); // Grab the first one.
Class<?> ic = e.getKey();
Remoteable r = e.getValue();
String methodPaths = r.methodPaths();
String expose = r.expose();
for (Method m : "DECLARED".equals(expose) ? ic.getDeclaredMethods() : ic.getMethods()) {
if (isPublic(m)) {
RemoteMethod rm = m.getAnnotation(RemoteMethod.class);
if (rm != null || ! "ANNOTATED".equals(expose)) {
String path = "NAME".equals(methodPaths) ? m.getName() : ClassUtils.getMethodSignature(m);
remoteableMethods.put(path, m);
}
}
}
}
if (innerClass != Object.class) {
noArgConstructor = (Constructor<T>)findNoArgConstructor(implClass == null ? innerClass : implClass, Visibility.PUBLIC);
}
if (beanFilter == null)
beanFilter = findBeanFilter();
if (swapMethod != null) {
final Method fSwapMethod = swapMethod;
final Method fUnswapMethod = unswapMethod;
final Constructor<T> fSwapConstructor = swapConstructor;
this.pojoSwap = new PojoSwap<T,Object>(c, swapMethod.getReturnType()) {
@Override
public Object swap(BeanSession session, Object o) throws SerializeException {
try {
return fSwapMethod.invoke(o, session);
} catch (Exception e) {
throw new SerializeException(e);
}
}
@Override
public T unswap(BeanSession session, Object f, ClassMeta<?> hint) throws ParseException {
try {
if (fUnswapMethod != null)
return (T)fUnswapMethod.invoke(null, session, f);
if (fSwapConstructor != null)
return fSwapConstructor.newInstance(f);
return super.unswap(session, f, hint);
} catch (Exception e) {
throw new ParseException(e);
}
}
};
}
if (this.pojoSwap == null)
this.pojoSwap = findPojoSwap();
if (this.pojoSwap == null)
this.pojoSwap = pojoSwap;
try {
// If this is an array, get the element type.
if (cc == ARRAY)
elementType = findClassMeta(innerClass.getComponentType());
// If this is a MAP, see if it's parameterized (e.g. AddressBook extends HashMap<String,Person>)
else if (cc == MAP) {
ClassMeta[] parameters = findParameters();
if (parameters != null && parameters.length == 2) {
keyType = parameters[0];
valueType = parameters[1];
} else {
keyType = findClassMeta(Object.class);
valueType = findClassMeta(Object.class);
}
}
// If this is a COLLECTION, see if it's parameterized (e.g. AddressBook extends LinkedList<Person>)
else if (cc == COLLECTION) {
ClassMeta[] parameters = findParameters();
if (parameters != null && parameters.length == 1) {
elementType = parameters[0];
} else {
elementType = findClassMeta(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 (cc == OTHER) {
BeanMeta newMeta = null;
try {
newMeta = new BeanMeta(ClassMeta.this, beanContext, beanFilter, null);
notABeanReason = newMeta.notABeanReason;
// Always get these even if it's not a bean:
beanRegistry = newMeta.beanRegistry;
typePropertyName = newMeta.typePropertyName;
} catch (RuntimeException e) {
notABeanReason = e.getMessage();
throw e;
}
if (notABeanReason == null)
beanMeta = newMeta;
}
} catch (NoClassDefFoundError e) {
initException = e;
} catch (RuntimeException e) {
initException = e;
throw e;
}
if (beanMeta != null)
dictionaryName = beanMeta.getDictionaryName();
serializedClassMeta = (this.pojoSwap == null ? ClassMeta.this : findClassMeta(this.pojoSwap.getSwapClass()));
if (serializedClassMeta == null)
serializedClassMeta = ClassMeta.this;
if (beanMeta != null && beanContext != null && beanContext.useInterfaceProxies && innerClass.isInterface())
invocationHandler = new BeanProxyInvocationHandler<T>(beanMeta);
Bean b = c.getAnnotation(Bean.class);
if (b != null && b.beanDictionary().length != 0)
beanRegistry = new BeanRegistry(beanContext, null, b.beanDictionary());
}
private BeanFilter findBeanFilter() {
try {
Map<Class<?>,Bean> ba = ReflectionUtils.findAnnotationsMap(Bean.class, innerClass);
if (! ba.isEmpty())
return new AnnotationBeanFilterBuilder(innerClass, ba).build();
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
private PojoSwap<T,?> findPojoSwap() {
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();
throw new RuntimeException("TODO - Surrogate classes not yet supported.");
}
}
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private ClassMeta<?> findClassMeta(Class<?> c) {
return beanContext.getClassMeta(c);
}
private ClassMeta<?>[] findParameters() {
return beanContext.findParameters(innerClass, innerClass);
}
}
/**
* Returns the type property name associated with this class and subclasses.
* <p>
* If <jk>null</jk>, <js>"_type"</js> should be assumed.
*
* @return The type property name associated with this bean class, or <jk>null</jk> if there is no explicit type property name defined or this isn't a bean.
*/
public String getBeanTypePropertyName() {
return typePropertyName;
}
/**
* Returns the bean dictionary name associated with this class.
* <p>
* The lexical name is defined by {@link Bean#typeName()}.
*
* @return The type name associated with this bean class, or <jk>null</jk> if there is no type name defined or this isn't a bean.
*/
public String getDictionaryName() {
return dictionaryName;
}
/**
* Returns the bean registry for this class.
* <p>
* This bean registry contains names specified in the {@link Bean#beanDictionary()} annotation defined
* on the class, regardless of whether the class is an actual bean.
* This allows interfaces to define subclasses with type names.
*
* @return The bean registry for this class, or <jk>null</jk> if no bean registry is associated with it.
*/
public BeanRegistry getBeanRegistry() {
return beanRegistry;
}
/**
* Returns the category of this class.
*
* @return The category of this class.
*/
public ClassCategory getClassCategory() {
return cc;
}
/**
* 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 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
* that no transforms are associated with a bean property.
*
* @return <jk>true</jk> if this class or any child classes has a {@link PojoSwap} associated with it.
*/
protected boolean hasChildPojoSwaps() {
return childPojoSwaps != null;
}
/**
* Returns the {@link PojoSwap} where the specified class is the same/subclass of the normal class of
* one of the child pojo swaps associated with this class.
*
* @param normalClass The normal class being resolved.
* @return The resolved {@link PojoSwap} or <jk>null</jk> if none were found.
*/
protected PojoSwap<?,?> getChildPojoSwapForSwap(Class<?> normalClass) {
if (childSwapMap != null) {
PojoSwap<?,?> s = childSwapMap.get(normalClass);
if (s == null) {
for (PojoSwap<?,?> f : childPojoSwaps)
if (s == null && isParentClass(f.getNormalClass(), normalClass))
s = f;
if (s == null)
s = PojoSwap.NULL;
PojoSwap<?,?> s2 = childSwapMap.putIfAbsent(normalClass, s);
if (s2 != null)
s = s2;
}
if (s == PojoSwap.NULL)
return null;
return s;
}
return null;
}
/**
* Returns the {@link PojoSwap} where the specified class is the same/subclass of the swap class of
* one of the child pojo swaps associated with this class.
*
* @param swapClass The swap class being resolved.
* @return The resolved {@link PojoSwap} or <jk>null</jk> if none were found.
*/
protected PojoSwap<?,?> getChildPojoSwapForUnswap(Class<?> swapClass) {
if (childUnswapMap != null) {
PojoSwap<?,?> s = childUnswapMap.get(swapClass);
if (s == null) {
for (PojoSwap<?,?> f : childPojoSwaps)
if (s == null && isParentClass(f.getSwapClass(), swapClass))
s = f;
if (s == null)
s = PojoSwap.NULL;
PojoSwap<?,?> s2 = childUnswapMap.putIfAbsent(swapClass, s);
if (s2 != null)
s = s2;
}
if (s == PojoSwap.NULL)
return null;
return s;
}
return null;
}
/**
* 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<?> 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;
}
/**
* 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.
*/
@BeanIgnore
public ClassMeta<?> getSerializedClassMeta() {
return serializedClassMeta;
}
/**
* 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 cc == MAP || cc == 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 cc == MAP || cc == BEANMAP || beanMeta != null;
}
/**
* 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 a bean.
*
* @return <jk>true</jk> if this class is a bean.
*/
public boolean isBean() {
return beanMeta != null;
}
/**
* 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 this class is {@link Void} or <jk>void</jk>.
*
* @return <jk>true</jk> if this class is {@link Void} or <jk>void</jk>.
*/
public boolean isVoid() {
return cc == VOID;
}
/**
* Returns <jk>true</jk> if this metadata represents an array of argument types.
*
* @return <jk>true</jk> if this metadata represents an array of argument types.
*/
public boolean isArgs() {
return cc == ARGS;
}
/**
* Returns the argument types of this meta.
*
* @return The argument types of this meta, or <jk>null</jk> if this isn't an array of argument types.
*/
public ClassMeta<?>[] getArgs() {
return args;
}
/**
* Returns the argument metadata at the specified index if this is an args metadata object.
*
* @param index The argument index.
* @return The The argument metadata. Never <jk>null</jk>.
* @throws BeanRuntimeException If this metadata object is not a list of arguments, or the index is out of range.
*/
public ClassMeta<?> getArg(int index) {
if (args != null && index >= 0 && index < args.length)
return args[index];
throw new BeanRuntimeException("Invalid argument index specified: {0}. Only {1} arguments are defined.", index, args == null ? 0 : args.length);
}
/**
* 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;
}
/**
* Returns <jk>true</jk> if this class is abstract.
* @return <jk>true</jk> if this class is abstract.
*/
public boolean isAbstract() {
return isAbstract;
}
/**
* Returns <jk>true</jk> if this class is an inner class.
* @return <jk>true</jk> if this class is an inner class.
*/
public boolean isMemberClass() {
return isMemberClass;
}
/**
* 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 the {@link PojoSwap} associated with this class.
*
* @return The {@link PojoSwap} associated with this class, or <jk>null</jk> if there is no POJO swap
* associated with this class.
*/
public PojoSwap<T,?> getPojoSwap() {
return pojoSwap;
}
/**
* 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 language-specified extended metadata on this class.
*
* @param c 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);
}
/**
* 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() {
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;
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 (fromStringMethod != 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 canCreateNewInstanceFromNumber(Object outer) {
if (numberConstructor != null) {
if (isMemberClass)
return outer != null && numberConstructor.getParameterTypes()[0] == outer.getClass();
return true;
}
return false;
}
/**
* Returns the class type of the parameter of the numeric constructor.
*
* @return The class type of the numeric constructor, or <jk>null</jk> if no such constructor exists.
*/
@SuppressWarnings("unchecked")
public Class<? extends Number> getNewInstanceFromNumberClass() {
return (Class<? extends Number>) numberConstructorType;
}
/**
* 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;
}
/**
* 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 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 string 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 = fromStringMethod;
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 a <code>Number</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(Number in);</code>
* </ul>
*
* @param session The current bean session.
* @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 numeric 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.
*/
public T newInstanceFromNumber(BeanSession session, Object outer, Number arg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<T> c = numberConstructor;
if (c != null) {
Object arg2 = session.convertToType(arg, numberConstructor.getParameterTypes()[0]);
if (isMemberClass)
return c.newInstance(outer, arg2);
return c.newInstance(arg2);
}
throw new InstantiationError("No string constructor or valueOf(Number) 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 class='spaced-list'>
* <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 class='spaced-list'>
* <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();
}
/**
* Similar to {@link #equals(Object)} except primitive and Object types that are similar
* are considered the same. (e.g. <jk>boolean</jk> == <code>Boolean</code>).
*
* @param cm The class meta to compare to.
* @return <jk>true</jk> if the specified class-meta is equivalent to this one.
*/
public boolean same(ClassMeta<?> cm) {
if (equals(cm))
return true;
return (isPrimitive() && cc == cm.cc);
}
@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 n = innerClass.getName();
if (simple) {
int i = n.lastIndexOf('.');
n = n.substring(i == -1 ? 0 : i+1).replace('$', '.');
}
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 {
private static Method forLanguageTagMethod;
static {
try {
forLanguageTagMethod = Locale.class.getMethod("forLanguageTag", String.class);
} catch (NoSuchMethodException e) {}
}
@SuppressWarnings("unused")
public static final Locale fromString(String localeString) {
if (forLanguageTagMethod != null) {
if (localeString.indexOf('_') != -1)
localeString = localeString.replace('_', '-');
try {
return (Locale)forLanguageTagMethod.invoke(null, localeString);
} catch (Exception e) {
throw new BeanRuntimeException(e);
}
}
String[] v = localeString.toString().split("[\\-\\_]");
if (v.length == 1)
return new Locale(v[0]);
else if (v.length == 2)
return new Locale(v[0], v[1]);
else if (v.length == 3)
return new Locale(v[0], v[1], v[2]);
throw new BeanRuntimeException("Could not convert string ''{0}'' to a Locale.", localeString);
}
}
@Override /* Object */
public int hashCode() {
return super.hashCode();
}
}