All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.ClassMeta Maven / Gradle / Ivy

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * 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.ClassFlags.*;
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.Date;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

import org.apache.juneau.annotation.*;
import org.apache.juneau.http.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
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.
 *
 * 

* Instances of this class can be created through the {@link BeanContext#getClassMeta(Class)} method. * *

* The {@link BeanContext} class will cache and reuse instances of this class except for the following class types: *

    *
  • Arrays *
  • Maps with non-Object key/values. *
  • Collections with non-Object key/values. *
* *

* This class is tied to the {@link BeanContext} class because it's that class that makes the determination of what is * a bean. * * @param The class type of the wrapped class. */ @Bean(properties="innerClass,classCategory,elementType,keyType,valueType,notABeanReason,initException,beanMeta") public final class ClassMeta implements Type { /** Class categories. */ enum ClassCategory { MAP, COLLECTION, CLASS, METHOD, NUMBER, DECIMAL, BOOLEAN, CHAR, DATE, ARRAY, ENUM, OTHER, CHARSEQ, STR, OBJ, URI, BEANMAP, READER, INPUTSTREAM, VOID, ARGS } final Class innerClass; // The class being wrapped. private final Class 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 noArgConstructor; // The no-arg constructor for this class (if it has one). private final Constructor 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). exampleMethod; // The example() or @Example-annotated method (if it has one). private final Field exampleField; // The @Example-annotated field (if it has one). private final Setter 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 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,PojoSwap> childSwapMap, // Maps normal subclasses to PojoSwaps. childUnswapMap; // Maps swap subclasses to PojoSwaps. private final PojoSwap[] pojoSwaps; // The object POJO swaps associated with this bean (if it has any). private final BeanFilter beanFilter; // The bean filter associated with this bean (if it has one). private final BuilderSwap builderSwap; // The builder swap 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 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 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 final Object example; // Example object. private final Map,Transform> fromTransforms = new ConcurrentHashMap<>(); private final Map,Transform> toTransforms = new ConcurrentHashMap<>(); private final Transform readerTransform; private final Transform inputStreamTransform; private final Transform stringTransform; private ReadWriteLock lock = new ReentrantReadWriteLock(false); private Lock rLock = lock.readLock(), wLock = lock.writeLock(); /** * 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 null. * @param beanFilter * The {@link BeanFilter} programmatically associated with this class. * Can be null. * @param pojoSwap * The {@link PojoSwap} programmatically associated with this class. * Can be null. * @param childPojoSwap * The child {@link PojoSwap PojoSwaps} programmatically associated with this class. * These are the PojoSwaps that have normal classes that are subclasses of this class. * Can be null. * @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 innerClass, BeanContext beanContext, Class implClass, BeanFilter beanFilter, PojoSwap[] pojoSwaps, PojoSwap[] childPojoSwaps, Object example) { this.innerClass = innerClass; this.beanContext = beanContext; wLock.lock(); try { // 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 builder = new ClassMetaBuilder(innerClass, beanContext, implClass, beanFilter, pojoSwaps, childPojoSwaps, example); 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.beanFilter = beanFilter; this.pojoSwaps = builder.pojoSwaps.isEmpty() ? null : builder.pojoSwaps.toArray(new PojoSwap[builder.pojoSwaps.size()]); this.builderSwap = builder.builderSwap; 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.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.exampleMethod = builder.exampleMethod; this.exampleField = builder.exampleField; this.example = builder.example; this.args = null; this.readerTransform = builder.readerTransform; this.inputStreamTransform = builder.inputStreamTransform; this.stringTransform = builder.stringTransform; } finally { wLock.unlock(); } } /** * Causes thread to wait until constructor has exited. */ final void waitForInit() { rLock.lock(); rLock.unlock(); } /** * Copy constructor. * *

* Used for creating Map and Collection class metas that shouldn't be cached. */ ClassMeta(ClassMeta 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.publicMethods = mainType.publicMethods; this.beanContext = mainType.beanContext; 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.pojoSwaps = mainType.pojoSwaps; this.builderSwap = mainType.builderSwap; this.beanFilter = mainType.beanFilter; this.extMeta = mainType.extMeta; this.initException = mainType.initException; this.beanRegistry = mainType.beanRegistry; this.exampleMethod = mainType.exampleMethod; this.exampleField = mainType.exampleField; this.example = mainType.example; this.args = null; this.readerTransform = mainType.readerTransform; this.inputStreamTransform = mainType.inputStreamTransform; this.stringTransform = mainType.stringTransform; } /** * Constructor for args-arrays. */ @SuppressWarnings("unchecked") ClassMeta(ClassMeta[] args) { this.innerClass = (Class) 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.publicMethods = null; this.beanContext = null; 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.pojoSwaps = null; this.builderSwap = null; this.beanFilter = null; this.extMeta = new MetadataMap(); this.initException = null; this.beanRegistry = null; this.exampleMethod = null; this.exampleField = null; this.example = null; this.readerTransform = null; this.inputStreamTransform = null; this.stringTransform = null; } @SuppressWarnings({"unchecked","rawtypes","hiding"}) private final class ClassMetaBuilder { Class innerClass; Class implClass; BeanContext beanContext; ClassCategory cc = ClassCategory.OTHER; boolean isDelegate = false, isMemberClass = false, isAbstract = false; Method fromStringMethod = null, swapMethod = null, unswapMethod = null; Setter parentPropertyMethod = null, namePropertyMethod = null; Constructor noArgConstructor = null, stringConstructor = null, swapConstructor = null, numberConstructor = null; Class swapMethodType = null, numberConstructorType = null; Object primitiveDefault = null; Map publicMethods = new LinkedHashMap<>(); ClassMeta keyType = null, valueType = null, elementType = null; String typePropertyName = null, notABeanReason = null, dictionaryName = null; Throwable initException = null; BeanMeta beanMeta = null; List pojoSwaps = new ArrayList<>(); BuilderSwap builderSwap; InvocationHandler invocationHandler = null; BeanRegistry beanRegistry = null; PojoSwap[] childPojoSwaps; ConcurrentHashMap,PojoSwap> childSwapMap, childUnswapMap; Method exampleMethod; Field exampleField; Object example; Transform readerTransform; Transform inputStreamTransform; Transform stringTransform; ClassMetaBuilder(Class innerClass, BeanContext beanContext, Class implClass, BeanFilter beanFilter, PojoSwap[] pojoSwaps, PojoSwap[] childPojoSwaps, Object example) { this.innerClass = innerClass; this.beanContext = beanContext; this.implClass = implClass; this.childPojoSwaps = childPojoSwaps; this.childSwapMap = childPojoSwaps == null ? null : new ConcurrentHashMap,PojoSwap>(); this.childUnswapMap = childPojoSwaps == null ? null : new ConcurrentHashMap,PojoSwap>(); Class 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(Method.class, c)) cc = METHOD; 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","fromValue","valueOf","parse","parseString","forName","forString"}) { if (fromStringMethod == null) { for (Method m : c.getMethods()) { if (isAll(m, STATIC, PUBLIC, NOT_DEPRECATED) && hasName(m, methodName) && hasReturnType(m, c) && hasArgs(m, String.class)) { fromStringMethod = m; break; } } } } // TODO - should use transforms for above code. // 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 (isAll(m, PUBLIC, NOT_DEPRECATED, NOT_STATIC) && hasName(m, "swap") && hasFuzzyArgs(m, BeanSession.class)) { swapMethod = m; swapMethodType = m.getReturnType(); break; } } // Find unswap() method if present. if (swapMethod != null) { for (Method m : c.getMethods()) { if (isAll(m, PUBLIC, NOT_DEPRECATED, STATIC) &&hasName(m, "unswap") && hasFuzzyArgs(m, BeanSession.class, swapMethodType)) { unswapMethod = m; break; } } } // Find example() method if present. for (Method m : c.getMethods()) { if (isAll(m, PUBLIC, NOT_DEPRECATED, STATIC) && hasName(m, "example") && hasFuzzyArgs(m, BeanSession.class)) { exampleMethod = m; break; } } for (Field f : getAllFields(c, true)) { if (f.isAnnotationPresent(ParentProperty.class)) { if (isStatic(f)) throw new ClassMetaRuntimeException("@ParentProperty used on invalid field ''{0}''", f); setAccessible(f, false); parentPropertyMethod = new Setter.FieldSetter(f); } if (f.isAnnotationPresent(NameProperty.class)) { if (isStatic(f)) throw new ClassMetaRuntimeException("@NameProperty used on invalid field ''{0}''", f); setAccessible(f, false); namePropertyMethod = new Setter.FieldSetter(f); } } for (Field f : c.getDeclaredFields()) { if (f.isAnnotationPresent(Example.class)) { if (! (isStatic(f) && isParentClass(innerClass, f.getType()))) throw new ClassMetaRuntimeException("@Example used on invalid field ''{0}''", f); setAccessible(f, false); exampleField = f; } } // Find @NameProperty and @ParentProperty methods if present. for (Method m : getAllMethods(c, true)) { if (m.isAnnotationPresent(ParentProperty.class)) { if (isStatic(m) || ! hasNumArgs(m, 1)) throw new ClassMetaRuntimeException("@ParentProperty used on invalid method ''{0}''", m); setAccessible(m, false); parentPropertyMethod = new Setter.MethodSetter(m); } if (m.isAnnotationPresent(NameProperty.class)) { if (isStatic(m) || ! hasNumArgs(m, 1)) throw new ClassMetaRuntimeException("@NameProperty used on invalid method ''{0}''", m); setAccessible(m, false); namePropertyMethod = new Setter.MethodSetter(m); } } for (Method m : c.getDeclaredMethods()) { if (m.isAnnotationPresent(Example.class)) { if (! (isStatic(m) && hasFuzzyArgs(m, BeanSession.class) && isParentClass(innerClass, m.getReturnType()))) throw new ClassMetaRuntimeException("@Example used on invalid method ''{0}''", m); setAccessible(m, false); exampleMethod = m; } } // Note: Primitive types are normally abstract. isAbstract = ClassUtils.isAbstract(c) && ! c.isPrimitive(); // Find constructor(String) method if present. for (Constructor cs : c.getConstructors()) { if (isPublic(cs) && isNotDeprecated(cs)) { Class[] pt = cs.getParameterTypes(); if (pt.length == (isMemberClass ? 1 : 0) && c != Object.class && ! isAbstract) { noArgConstructor = cs; } else if (pt.length == (isMemberClass ? 2 : 1)) { Class arg = pt[(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 = getWrapperIfPrimitive(arg); } } } } primitiveDefault = ClassUtils.getPrimitiveDefault(c); for (Method m : c.getMethods()) if (isAll(m, PUBLIC, NOT_DEPRECATED)) publicMethods.put(getMethodSignature(m), m); if (innerClass != Object.class) { noArgConstructor = (Constructor)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 fSwapConstructor = swapConstructor; this.pojoSwaps.add( new PojoSwap(c, swapMethod.getReturnType()) { @Override public Object swap(BeanSession session, Object o) throws SerializeException { try { return fSwapMethod.invoke(o, getMatchingArgs(fSwapMethod.getParameterTypes(), 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, getMatchingArgs(fSwapMethod.getParameterTypes(), session, f)); if (fSwapConstructor != null) return fSwapConstructor.newInstance(f); return super.unswap(session, f, hint); } catch (Exception e) { throw new ParseException(e); } } } ); } if (pojoSwaps != null) this.pojoSwaps.addAll(Arrays.asList(pojoSwaps)); if (beanContext != null) this.builderSwap = BuilderSwap.findSwapFromPojoClass(c, beanContext.getBeanConstructorVisibility(), beanContext.getBeanMethodVisibility()); findPojoSwaps(this.pojoSwaps); 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) 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) 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(); if (beanMeta != null && beanContext != null && beanContext.isUseInterfaceProxies() && innerClass.isInterface()) invocationHandler = new BeanProxyInvocationHandler(beanMeta); Bean b = c.getAnnotation(Bean.class); if (b != null) { if (b.beanDictionary().length != 0) beanRegistry = new BeanRegistry(beanContext, null, b.beanDictionary()); // This could be a non-bean POJO with a type name. if (dictionaryName == null && ! b.typeName().isEmpty()) dictionaryName = b.typeName(); } Example e = c.getAnnotation(Example.class); if (example == null && e != null && ! e.value().isEmpty()) example = e.value(); if (example == null) { switch(cc) { case BOOLEAN: example = true; break; case CHAR: example = 'a'; break; case CHARSEQ: case STR: example = "foo"; break; case DECIMAL: if (isFloat()) example = new Float(1f); else if (isDouble()) example = new Double(1d); break; case ENUM: Iterator i = EnumSet.allOf((Class)c).iterator(); if (i.hasNext()) example = i.next(); break; case NUMBER: if (isShort()) example = new Short((short)1); else if (isInteger()) example = new Integer(1); else if (isLong()) example = new Long(1l); break; case URI: case ARGS: case ARRAY: case BEANMAP: case CLASS: case COLLECTION: case DATE: case INPUTSTREAM: case MAP: case METHOD: case OBJ: case OTHER: case READER: case VOID: break; } } this.example = example; this.readerTransform = TransformCache.get(Reader.class, c); this.inputStreamTransform = TransformCache.get(InputStream.class, c); this.stringTransform = TransformCache.get(String.class, c); } private BeanFilter findBeanFilter() { try { Map,Bean> ba = getAnnotationsMap(Bean.class, innerClass); if (! ba.isEmpty()) return new AnnotationBeanFilterBuilder(innerClass, ba).build(); } catch (Exception e) { throw new RuntimeException(e); } return null; } private void findPojoSwaps(List l) { Swap swap = innerClass.getAnnotation(Swap.class); if (swap != null) l.add(createPojoSwap(swap)); Swaps swaps = innerClass.getAnnotation(Swaps.class); if (swaps != null) for (Swap s : swaps.value()) l.add(createPojoSwap(s)); } private PojoSwap createPojoSwap(Swap s) { Class c = s.value(); if (c == Null.class) c = s.impl(); if (isParentClass(PojoSwap.class, c)) { PojoSwap ps = beanContext.newInstance(PojoSwap.class, c); if (s.mediaTypes().length > 0) ps.forMediaTypes(MediaType.forStrings(s.mediaTypes())); if (! s.template().isEmpty()) ps.withTemplate(s.template()); return ps; } if (isParentClass(Surrogate.class, c)) { List> l = SurrogateSwap.findPojoSwaps(c); if (! l.isEmpty()) return (PojoSwap)l.iterator().next(); } throw new ClassMetaRuntimeException("Invalid swap class ''{0}'' specified. Must extend from PojoSwap or Surrogate.", c); } private ClassMeta findClassMeta(Class c) { return beanContext.getClassMeta(c, false); } private ClassMeta[] findParameters() { return beanContext.findParameters(innerClass, innerClass); } } /** * Returns the type property name associated with this class and subclasses. * *

* If null, "_type" should be assumed. * * @return * The type property name associated with this bean class, or null 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. * *

* The lexical name is defined by {@link Bean#typeName() @Bean(typeName)}. * * @return * The type name associated with this bean class, or null 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. * *

* This bean registry contains names specified in the {@link Bean#beanDictionary() @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 null 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 true if this class is a superclass of or the same as the specified class. * * @param c The comparison class. * @return true if this class is a superclass of or the same as the specified class. */ public boolean isAssignableFrom(Class c) { return isParentClass(innerClass, c); } /** * Returns true if this class is a subclass of or the same as the specified class. * * @param c The comparison class. * @return true if this class is a subclass of or the same as the specified class. */ public boolean isInstanceOf(Class c) { return isParentClass(c, innerClass); } /** * Returns true if this class or any child classes has a {@link PojoSwap} associated with it. * *

* 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 true 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 null 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 null 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 null. * 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 null if no no-arg constructor exists with the required visibility. */ @SuppressWarnings({"rawtypes","unchecked"}) protected static Constructor findNoArgConstructor(Class c, Visibility v) { if (ClassUtils.isAbstract(c)) return null; boolean isMemberClass = c.isMemberClass() && ! isStatic(c); for (Constructor cc : c.getConstructors()) { if (hasNumArgs(cc, isMemberClass ? 1 : 0) && v.isVisible(cc.getModifiers()) && 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 getInnerClass() { return innerClass; } /** * Returns the serialized (swapped) form of this class if there is an {@link PojoSwap} associated with it. * * @param session * The bean session. *
Required because the swap used may depend on the media type being serialized or parsed. * @return The serialized class type, or this object if no swap is associated with the class. */ @BeanIgnore public ClassMeta getSerializedClassMeta(BeanSession session) { PojoSwap ps = getPojoSwap(session); return (ps == null ? this : ps.getSwapClassMeta(session)); } /** * Returns the example of this class. * * @param session * The bean session. *
Required because the example method may take it in as a parameter. * @return The serialized class type, or this object if no swap is associated with the class. */ @SuppressWarnings({"unchecked","rawtypes"}) @BeanIgnore public T getExample(BeanSession session) { try { if (example != null) { if (isInstance(example)) return (T)example; if (example instanceof String) { if (isCharSequence()) return (T)example; String s = example.toString(); if (isMapOrBean() && StringUtils.isObjectMap(s, false)) return JsonParser.DEFAULT.parse(s, this); if (isCollectionOrArray() && StringUtils.isObjectList(s, false)) return JsonParser.DEFAULT.parse(s, this); } if (example instanceof Map && isMapOrBean()) { return JsonParser.DEFAULT.parse(SimpleJsonSerializer.DEFAULT_READABLE.toString(example), this); } if (example instanceof Collection && isCollectionOrArray()) { return JsonParser.DEFAULT.parse(SimpleJsonSerializer.DEFAULT_READABLE.serialize(example), this); } } if (exampleMethod != null) return (T)invokeMethodFuzzy(exampleMethod, null, session); if (exampleField != null) return (T)exampleField.get(null); if (isCollection()) { Object etExample = getElementType().getExample(session); if (etExample != null) { if (canCreateNewInstance()) { Collection c = (Collection)newInstance(); c.add(etExample); return (T)c; } return (T)Collections.singleton(etExample); } } else if (isArray()) { Object etExample = getElementType().getExample(session); if (etExample != null) { Object o = Array.newInstance(getElementType().innerClass, 1); Array.set(o, 0, etExample); return (T)o; } } else if (isMap()) { Object vtExample = getValueType().getExample(session); Object ktExample = getKeyType().getExample(session); if (ktExample != null && vtExample != null) { if (canCreateNewInstance()) { Map m = (Map)newInstance(); m.put(ktExample, vtExample); return (T)m; } return (T)Collections.singletonMap(ktExample, vtExample); } } return null; } catch (Exception e) { throw new ClassMetaRuntimeException(e); } } /** * 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 null 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 null 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 null if this class is not a Map. */ public ClassMeta getValueType() { return valueType; } /** * Returns true if this class implements {@link Delegate}, meaning it's a representation of some other * object. * * @return true if this class implements {@link Delegate}. */ public boolean isDelegate() { return isDelegate; } /** * Returns true if this class is a subclass of {@link Map}. * * @return true if this class is a subclass of {@link Map}. */ public boolean isMap() { return cc == MAP || cc == BEANMAP; } /** * Returns true if this class is a subclass of {@link Map} or it's a bean. * * @return true 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 true if this class is a subclass of {@link BeanMap}. * * @return true if this class is a subclass of {@link BeanMap}. */ public boolean isBeanMap() { return cc == BEANMAP; } /** * Returns true if this class is a subclass of {@link Collection}. * * @return true if this class is a subclass of {@link Collection}. */ public boolean isCollection() { return cc == COLLECTION; } /** * Returns true if this class is a subclass of {@link Collection} or is an array. * * @return true if this class is a subclass of {@link Collection} or is an array. */ public boolean isCollectionOrArray() { return cc == COLLECTION || cc == ARRAY; } /** * Returns true if this class extends from {@link Set}. * * @return true if this class extends from {@link Set}. */ public boolean isSet() { return cc == COLLECTION && isParentClass(Set.class, innerClass); } /** * Returns true if this class extends from {@link List}. * * @return true if this class extends from {@link List}. */ public boolean isList() { return cc == COLLECTION && isParentClass(List.class, innerClass); } /** * Returns true if this class is byte[]. * * @return true if this class is byte[]. */ public boolean isByteArray() { return cc == ARRAY && this.innerClass == byte[].class; } /** * Returns true if this class is {@link Class}. * * @return true if this class is {@link Class}. */ public boolean isClass() { return cc == CLASS; } /** * Returns true if this class is {@link Method}. * * @return true if this class is {@link Method}. */ public boolean isMethod() { return cc == METHOD; } /** * Returns true if this class is an {@link Enum}. * * @return true if this class is an {@link Enum}. */ public boolean isEnum() { return cc == ENUM; } /** * Returns true if this class is an array. * * @return true if this class is an array. */ public boolean isArray() { return cc == ARRAY; } /** * Returns true if this class is a bean. * * @return true if this class is a bean. */ public boolean isBean() { return beanMeta != null; } /** * Returns true if this class is {@link Object}. * * @return true if this class is {@link Object}. */ public boolean isObject() { return cc == OBJ; } /** * Returns true if this class is not {@link Object}. * * @return true if this class is not {@link Object}. */ public boolean isNotObject() { return cc != OBJ; } /** * Returns true if this class is a subclass of {@link Number}. * * @return true if this class is a subclass of {@link Number}. */ public boolean isNumber() { return cc == NUMBER || cc == DECIMAL; } /** * Returns true if this class is a subclass of {@link Float} or {@link Double}. * * @return true if this class is a subclass of {@link Float} or {@link Double}. */ public boolean isDecimal() { return cc == DECIMAL; } /** * Returns true if this class is either {@link Float} or float. * * @return true if this class is either {@link Float} or float. */ public boolean isFloat() { return innerClass == Float.class || innerClass == float.class; } /** * Returns true if this class is either {@link Double} or double. * * @return true if this class is either {@link Double} or double. */ public boolean isDouble() { return innerClass == Double.class || innerClass == double.class; } /** * Returns true if this class is either {@link Short} or short. * * @return true if this class is either {@link Short} or short. */ public boolean isShort() { return innerClass == Short.class || innerClass == short.class; } /** * Returns true if this class is either {@link Integer} or int. * * @return true if this class is either {@link Integer} or int. */ public boolean isInteger() { return innerClass == Integer.class || innerClass == int.class; } /** * Returns true if this class is either {@link Long} or long. * * @return true if this class is either {@link Long} or long. */ public boolean isLong() { return innerClass == Long.class || innerClass == long.class; } /** * Returns true if this metadata represents the specified type. * * @param c The class to test against. * @return true if this metadata represents the specified type. */ public boolean isType(Class c) { return isParentClass(c, innerClass); } /** * Returns true if this class is a {@link Boolean}. * * @return true if this class is a {@link Boolean}. */ public boolean isBoolean() { return cc == BOOLEAN; } /** * Returns true if this class is a subclass of {@link CharSequence}. * * @return true if this class is a subclass of {@link CharSequence}. */ public boolean isCharSequence() { return cc == STR || cc == CHARSEQ; } /** * Returns true if this class is a {@link String}. * * @return true if this class is a {@link String}. */ public boolean isString() { return cc == STR; } /** * Returns true if this class is a {@link Character}. * * @return true if this class is a {@link Character}. */ public boolean isChar() { return cc == CHAR; } /** * Returns true if this class is a primitive. * * @return true if this class is a primitive. */ public boolean isPrimitive() { return innerClass.isPrimitive(); } /** * Returns true if this class is a {@link Date} or {@link Calendar}. * * @return true if this class is a {@link Date} or {@link Calendar}. */ public boolean isDateOrCalendar() { return cc == DATE; } /** * Returns true if this class is a {@link Date}. * * @return true if this class is a {@link Date}. */ public boolean isDate() { return cc == DATE && ClassUtils.isParentClass(Date.class, innerClass); } /** * Returns true if this class is a {@link Calendar}. * * @return true if this class is a {@link Calendar}. */ public boolean isCalendar() { return cc == DATE && ClassUtils.isParentClass(Calendar.class, innerClass); } /** * Returns true if this class is a {@link URI} or {@link URL}. * * @return true if this class is a {@link URI} or {@link URL}. */ public boolean isUri() { return cc == URI; } /** * Returns true if this class is a {@link Reader}. * * @return true if this class is a {@link Reader}. */ public boolean isReader() { return cc == READER; } /** * Returns true if this class is an {@link InputStream}. * * @return true if this class is an {@link InputStream}. */ public boolean isInputStream() { return cc == INPUTSTREAM; } /** * Returns true if this class is {@link Void} or void. * * @return true if this class is {@link Void} or void. */ public boolean isVoid() { return cc == VOID; } /** * Returns true if this metadata represents an array of argument types. * * @return true 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 null 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 null. * @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 true if instance of this object can be null. * *

* Objects can be null, but primitives cannot, except for chars which can be represented by * (char)0. * * @return true if instance of this class can be null. */ public boolean isNullable() { if (innerClass.isPrimitive()) return cc == CHAR; return true; } /** * Returns true if this class is abstract. * * @return true if this class is abstract. */ public boolean isAbstract() { return isAbstract; } /** * Returns true if this class is an inner class. * * @return true if this class is an inner class. */ public boolean isMemberClass() { return isMemberClass; } /** * All public methods on this class including static methods. * *

* Keys are method signatures. * * @return The public methods on this class. */ public Map getPublicMethods() { return publicMethods; } /** * Returns the {@link PojoSwap} associated with this class that's the best match for the specified session. * * @param session * The current bean session. *
If multiple swaps are associated with a class, only the first one with a matching media type will * be returned. * @return * The {@link PojoSwap} associated with this class, or null if there are no POJO swaps associated with * this class. */ public PojoSwap getPojoSwap(BeanSession session) { if (pojoSwaps != null) { int matchQuant = 0, matchIndex = -1; for (int i = 0; i < pojoSwaps.length; i++) { int q = pojoSwaps[i].match(session); if (q > matchQuant) { matchQuant = q; matchIndex = i; } } if (matchIndex > -1) return pojoSwaps[matchIndex]; } return null; } /** * Returns the builder swap associated with this class. * * @param session The current bean session. * @return The builder swap associated with this class, or null if it doesn't exist. */ public BuilderSwap getBuilderSwap(BeanSession session) { return builderSwap; } /** * Returns the {@link BeanMeta} associated with this class. * * @return * The {@link BeanMeta} associated with this class, or null if there is no bean meta associated with * this class. */ public BeanMeta getBeanMeta() { return beanMeta; } /** * Returns the no-arg constructor for this class. * * @return The no-arg constructor for this class, or null if it does not exist. */ public Constructor 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 null. */ public M getExtendedMeta(Class c) { return extMeta.get(c, this); } /** * Returns the interface proxy invocation handler for this class. * * @return The interface proxy invocation handler, or null if it does not exist. */ public InvocationHandler getProxyInvocationHandler() { return invocationHandler; } /** * Returns true if this class has a no-arg constructor or invocation handler. * * @return true 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 true if this class has a no-arg constructor or invocation handler. * Returns false 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 null for non-member or static classes. * @return * true 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 && hasArgs(noArgConstructor, outer.getClass()); return canCreateNewInstance(); } /** * Returns true if this class can be instantiated as a bean. * Returns false 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 null for non-member or static classes. * @return * true 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 && hasArgs(beanMeta.constructor, outer.getClass()); return true; } /** * Returns true if this class can call the {@link #newInstanceFromString(Object, String)} method. * * @param outer * The outer class object for non-static member classes. * Can be null for non-member or static classes. * @return true 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 && hasArgs(stringConstructor, outer.getClass(), String.class); return true; } return false; } /** * Returns true if this class can call the {@link #newInstanceFromString(Object, String)} method. * * @param outer * The outer class object for non-static member classes. * Can be null for non-member or static classes. * @return true if this class has a no-arg constructor or invocation handler. */ public boolean canCreateNewInstanceFromNumber(Object outer) { if (numberConstructor != null) { if (isMemberClass) return outer != null && hasArgs(numberConstructor, 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 null if no such constructor exists. */ @SuppressWarnings("unchecked") public Class getNewInstanceFromNumberClass() { return (Class) numberConstructorType; } /** * Returns the method or field annotated with {@link NameProperty @NameProperty}. * * @return * The method or field annotated with {@link NameProperty @NameProperty} or null if method does not * exist. */ public Setter getNameProperty() { return namePropertyMethod; } /** * Returns the method or field annotated with {@link ParentProperty @ParentProperty}. * * @return * The method or field annotated with {@link ParentProperty @ParentProperty} or null if method does not * exist. */ public Setter getParentProperty() { return parentPropertyMethod; } /** * Returns the reason why this class is not a bean, or null if it is a bean. * * @return The reason why this class is not a bean, or null if it is a bean. */ public synchronized String getNotABeanReason() { return notABeanReason; } /** * Returns any exception that was throw in the init() 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 int or Integer. * * @return The default value, or null if this class type is not a primitive. */ @SuppressWarnings("unchecked") public T getPrimitiveDefault() { return (T)primitiveDefault; } /** * Converts the specified object to a string. * * @param t The object to convert. * @return The object converted to a string, or null if the object was null. */ public String toString(Object t) { if (t == null) return null; if (isEnum() && beanContext.isUseEnumNames()) return ((Enum)t).name(); return t.toString(); } /** * Create a new instance of the main class of this declared type from a String input. * *

* In order to use this method, the class must have one of the following methods: *

    *
  • public static T valueOf(String in); *
  • public static T fromString(String in); *
  • public T(String in); *
* * @param outer * The outer class object for non-static member classes. Can be null for non-member or static classes. * @param arg The input argument value. * @return A new instance of the object, or null if there is no string constructor on the object. * @throws IllegalAccessException * If the Constructor 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", "rawtypes" }) public T newInstanceFromString(Object outer, String arg) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException { if (isEnum() && beanContext.isUseEnumNames()) return (T)Enum.valueOf((Class)this.innerClass, arg); Method m = fromStringMethod; if (m != null) return (T)m.invoke(null, arg); Constructor 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 Number input. * *

* In order to use this method, the class must have one of the following methods: *

    *
  • public T(Number in); *
* * @param session The current bean session. * @param outer * The outer class object for non-static member classes. * Can be null for non-member or static classes. * @param arg The input argument value. * @return A new instance of the object, or null if there is no numeric constructor on the object. * @throws IllegalAccessException * If the Constructor 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 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 null if there is no no-arg constructor on the object. * @throws IllegalAccessException * If the Constructor object enforces Java language access control and the underlying constructor is * inaccessible. * @throws IllegalArgumentException * If one of the following occurs: *
    *
  • * The number of actual and formal parameters differ. *
  • * An unwrapping conversion for primitive arguments fails. *
  • * A parameter value cannot be converted to the corresponding formal parameter type by a method invocation * conversion. *
  • * The constructor pertains to an enum type. *
* @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 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 null if instantiating a non-member or static class. * @return A new instance of the object, or null if there is no no-arg constructor on the object. * @throws IllegalAccessException * If the Constructor object enforces Java language access control and the underlying constructor is * inaccessible. * @throws IllegalArgumentException * If one of the following occurs: *
    *
  • * The number of actual and formal parameters differ. *
  • * An unwrapping conversion for primitive arguments fails. *
  • * A parameter value cannot be converted to the corresponding formal parameter type by a method invocation * conversion. *
  • * The constructor pertains to an enum type. *
* @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 true 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. boolean == Boolean). * * @param cm The class meta to compare to. * @return true 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)+">"); return sb.append(n); } /** * Returns true 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 true if the specified object is an instance of this class. */ public boolean isInstance(Object o) { if (o != null) return isParentClass(this.innerClass, o.getClass()) || (isPrimitive() && getPrimitiveWrapper(this.innerClass) == o.getClass()); return false; } /** * Returns a readable name for this class (e.g. "java.lang.String", "boolean[]"). * * @return The readable name for this class. */ public String getReadableName() { return getReadableClassName(this.innerClass); } /** * Shortcut for calling {@link Class#getName()} on the inner class of this metadata. * * @return The name of the inner class. */ public String getName() { return innerClass.getName(); } /** * Shortcut for calling {@link Class#getSimpleName()} on the inner class of this metadata. * * @return The simple name of the inner class. */ public String getSimpleName() { return innerClass.getSimpleName(); } 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(); } /** * Returns true if this class has a transform associated with it that allows it to be created from a Reader. * * @return true if this class has a transform associated with it that allows it to be created from a Reader. */ public boolean hasReaderTransform() { return hasTransformFrom(Reader.class); } /** * Returns the transform for this class for creating instances from a Reader. * * @return The transform, or null if no such transform exists. */ public Transform getReaderTransform() { return getFromTransform(Reader.class); } /** * Returns true if this class has a transform associated with it that allows it to be created from an InputStream. * * @return true if this class has a transform associated with it that allows it to be created from an InputStream. */ public boolean hasInputStreamTransform() { return hasTransformFrom(InputStream.class); } /** * Returns the transform for this class for creating instances from an InputStream. * * @return The transform, or null if no such transform exists. */ public Transform getInputStreamTransform() { return getFromTransform(InputStream.class); } /** * Returns true if this class has a transform associated with it that allows it to be created from a String. * * @return true if this class has a transform associated with it that allows it to be created from a String. */ public boolean hasStringTransform() { return stringTransform != null; } /** * Returns the transform for this class for creating instances from a String. * * @return The transform, or null if no such transform exists. */ public Transform getStringTransform() { return stringTransform; } /** * Returns true if this class can be instantiated from the specified type. * * @param c The class type to convert from. * @return true if this class can be instantiated from the specified type. */ public boolean hasTransformFrom(Class c) { return getFromTransform(c) != null; } /** * Returns true if this class can be instantiated from the specified type. * * @param c The class type to convert from. * @return true if this class can be instantiated from the specified type. */ public boolean hasTransformFrom(ClassMeta c) { return getFromTransform(c.getInnerClass()) != null; } /** * Returns true if this class can be transformed to the specified type. * * @param c The class type to convert from. * @return true if this class can be transformed to the specified type. */ public boolean hasTransformTo(Class c) { return getToTransform(c) != null; } /** * Returns true if this class can be transformed to the specified type. * * @param c The class type to convert from. * @return true if this class can be transformed to the specified type. */ public boolean hasTransformTo(ClassMeta c) { return getToTransform(c.getInnerClass()) != null; } /** * Transforms the specified object into an instance of this class. * * @param o The object to transform. * @return The transformed object. */ @SuppressWarnings({"unchecked","rawtypes"}) public T transformFrom(Object o) { Transform t = getFromTransform(o.getClass()); return (T)(t == null ? null : t.transform(o)); } /** * Transforms the specified object into an instance of this class. * * @param o The object to transform. * @param c The class * @return The transformed object. */ @SuppressWarnings({"unchecked","rawtypes"}) public O transformTo(Object o, Class c) { Transform t = getToTransform(c); return (O)(t == null ? null : t.transform(o)); } /** * Transforms the specified object into an instance of this class. * * @param o The object to transform. * @param c The class * @return The transformed object. */ public O transformTo(Object o, ClassMeta c) { return transformTo(o, c.getInnerClass()); } /** * Returns the transform for this class for creating instances from other object types. * * @param c The transform-from class. * @return The transform, or null if no such transform exists. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public Transform getFromTransform(Class c) { Transform t = fromTransforms.get(c); if (t == TransformCache.NULL) return null; if (t == null) { t = TransformCache.get(c, innerClass); if (t == null) t = TransformCache.NULL; fromTransforms.put(c, t); } return t == TransformCache.NULL ? null : t; } /** * Returns the transform for this class for creating instances from other object types. * * @param c The transform-from class. * @return The transform, or null if no such transform exists. */ @SuppressWarnings({ "rawtypes", "unchecked" }) public Transform getToTransform(Class c) { Transform t = toTransforms.get(c); if (t == TransformCache.NULL) return null; if (t == null) { t = TransformCache.get(innerClass, c); if (t == null) t = TransformCache.NULL; toTransforms.put(c, t); } return t == TransformCache.NULL ? null : t; } }