/**
* $Id: ConstructorUtils.java 61 2009-09-25 11:14:16Z azeckoski $
* $URL: http://reflectutils.googlecode.com/svn/trunk/src/main/java/org/azeckoski/reflectutils/ConstructorUtils.java $
* FieldUtils.java - genericdao - May 19, 2008 10:10:15 PM - azeckoski
**************************************************************************
* Copyright (c) 2008 Aaron Zeckoski
* Licensed under the Apache License, Version 2.0
*
* A copy of the Apache License has been included in this
* distribution and is available at: http://www.apache.org/licenses/LICENSE-2.0.txt
*
* Aaron Zeckoski (azeckoski @ gmail.com) (aaronz @ vt.edu) (aaron @ caret.cam.ac.uk)
*/
package org.azeckoski.reflectutils;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Serializable;
import java.io.Writer;
import java.lang.ref.SoftReference;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import org.azeckoski.reflectutils.map.ArrayOrderedMap;
/**
* Class which provides methods for dealing with class constructors,
* also provides access to all the public constructors for a class
*
* @author Aaron Zeckoski (azeckoski @ gmail.com)
*/
public class ConstructorUtils {
/**
* Empty constructor
* <br/>
* <b>WARNING:</b> use the {@link #getInstance()} method to get this rather than recreating it over and over
*/
public ConstructorUtils() {
ConstructorUtils.setInstance(this);
}
protected ClassDataCacher getClassDataCacher() {
return ClassDataCacher.getInstance();
}
// init all the maps when class inits
static {
makeImmutableDefaultsMap();
makePrimitiveDefaultsMap();
makePrimitiveWrapperMap();
makeWTPMap();
}
/*
* Some code below derived from BeanCloner
* http://www.coderslog.com/Main_Page
* Copyright 2007 CodersLog.com
* Licensed under the Apache License, Version 2.0 (the "License");
*/
private static Set<Class<?>> immutableTypes = null;
/**
* @return a set of all known immutable types
*/
public static synchronized Set<Class<?>> getImmutableTypes() {
if (immutableTypes == null
|| immutableTypes.isEmpty()) {
makeDefaultImmuatableSet();
}
return immutableTypes;
}
private static void makeDefaultImmuatableSet() {
immutableTypes = getImmutableDefaults().keySet();
}
private static Map<Class<?>, Object> immutableDefaults = null;
/**
* @return the map of all immutable types -> the default values for those types
*/
public static synchronized Map<Class<?>, Object> getImmutableDefaults() {
if (immutableDefaults == null ||
immutableDefaults.isEmpty()) {
makeImmutableDefaultsMap();
}
return immutableDefaults;
}
private static void makeImmutableDefaultsMap() {
immutableDefaults = new HashMap<Class<?>, Object>();
immutableDefaults.put(BigDecimal.class, new BigDecimal(0));
immutableDefaults.put(BigInteger.class, BigInteger.valueOf(0l));
immutableDefaults.put(Boolean.class, Boolean.FALSE);
immutableDefaults.put(Byte.class, Byte.valueOf((byte)0));
immutableDefaults.put(Character.class, (Character)c);
immutableDefaults.put(Date.class, new Date(0));
immutableDefaults.put(Double.class, Double.valueOf(0));
immutableDefaults.put(Float.class, Float.valueOf(0));
immutableDefaults.put(Long.class, Long.valueOf(0));
immutableDefaults.put(Integer.class, Integer.valueOf(0));
immutableDefaults.put(String.class, "");
immutableDefaults.put(Short.class, Short.valueOf((short)0));
immutableDefaults.put(Timestamp.class, new Timestamp(0));
}
private static Map<Class<?>, Class<?>> primitiveToWrapper = null;
/**
* @return the map of all primitive types -> wrapper types
*/
public static synchronized Map<Class<?>, Class<?>> getPrimitiveToWrapper() {
if (primitiveToWrapper == null ||
primitiveToWrapper.isEmpty()) {
makePrimitiveWrapperMap();
}
return primitiveToWrapper;
}
private static void makePrimitiveWrapperMap() {
primitiveToWrapper = new HashMap<Class<?>, Class<?>>();
primitiveToWrapper.put(boolean.class, Boolean.class);
primitiveToWrapper.put(byte.class, Byte.class);
primitiveToWrapper.put(char.class, Character.class);
primitiveToWrapper.put(double.class, Double.class);
primitiveToWrapper.put(float.class, Float.class);
primitiveToWrapper.put(int.class, Integer.class);
primitiveToWrapper.put(long.class, Long.class);
primitiveToWrapper.put(short.class, Short.class);
primitiveToWrapper.put(boolean[].class, Boolean[].class);
primitiveToWrapper.put(byte[].class, Byte[].class);
primitiveToWrapper.put(char[].class, Character[].class);
primitiveToWrapper.put(double[].class, Double[].class);
primitiveToWrapper.put(float[].class, Float[].class);
primitiveToWrapper.put(int[].class, Integer[].class);
primitiveToWrapper.put(long[].class, Long[].class);
primitiveToWrapper.put(short[].class, Short[].class);
}
private static Map<Class<?>, Class<?>> wrapperToPrimitive = null;
/**
* @return the map of all wrapper types -> primitive types
*/
public static synchronized Map<Class<?>, Class<?>> getWrapperToPrimitive() {
if (wrapperToPrimitive == null ||
wrapperToPrimitive.isEmpty()) {
makeWTPMap();
}
return wrapperToPrimitive;
}
private static void makeWTPMap() {
wrapperToPrimitive = new HashMap<Class<?>, Class<?>>();
wrapperToPrimitive.put(Boolean.class, boolean.class);
wrapperToPrimitive.put(Byte.class, byte.class);
wrapperToPrimitive.put(Character.class, char.class);
wrapperToPrimitive.put(Double.class, double.class);
wrapperToPrimitive.put(Float.class, float.class);
wrapperToPrimitive.put(Integer.class, int.class);
wrapperToPrimitive.put(Long.class, long.class);
wrapperToPrimitive.put(Short.class, short.class);
wrapperToPrimitive.put(Boolean[].class, boolean[].class);
wrapperToPrimitive.put(Byte[].class, byte[].class);
wrapperToPrimitive.put(Character[].class, char[].class);
wrapperToPrimitive.put(Double[].class, double[].class);
wrapperToPrimitive.put(Float[].class, float[].class);
wrapperToPrimitive.put(Integer[].class, int[].class);
wrapperToPrimitive.put(Long[].class, long[].class);
wrapperToPrimitive.put(Short[].class, short[].class);
}
private static Map<Class<?>, Object> primitiveDefaults = null;
/**
* @return the map of all primitive types -> the default values for those types
*/
public static synchronized Map<Class<?>, Object> getPrimitiveDefaults() {
if (primitiveDefaults == null ||
primitiveDefaults.isEmpty()) {
makePrimitiveDefaultsMap();
}
return primitiveDefaults;
}
private static char c;
private static void makePrimitiveDefaultsMap() {
primitiveDefaults = new HashMap<Class<?>, Object>();
primitiveDefaults.put(boolean.class, (Boolean)false);
primitiveDefaults.put(byte.class, (Byte)(byte)0);
primitiveDefaults.put(char.class, (Character)c);
primitiveDefaults.put(double.class, (Double)0.0D);
primitiveDefaults.put(float.class, (Float)0.0F);
primitiveDefaults.put(int.class, (Integer)0);
primitiveDefaults.put(long.class, (Long)0L);
primitiveDefaults.put(short.class, (Short)(short)0);
}
private static void checkNull(Class<?> type) {
if (type == null) {
throw new IllegalArgumentException("class type cannot be null to check the type");
}
}
/**
* Get the default value for for a type if one is available OR null if there is no default (since null sorta is the default)
* @param <T>
* @param type any class type including primitives
* @return the default value OR null if there is no default
*/
@SuppressWarnings("unchecked")
public static <T> T getDefaultValue(Class<T> type) {
T val = null;
if (getPrimitiveDefaults().containsKey(type)) {
val = (T) getPrimitiveDefaults().get(type);
} else if (getImmutableDefaults().containsKey(type)) {
val = (T) getImmutableDefaults().get(type);
}
return val;
}
/**
* @param type any class
* @return true if this class is a primitive or other simple class (like String or immutable)
*/
public static boolean isClassSimple(Class<?> type) {
checkNull(type);
boolean simple = false;
if ( isClassPrimitive(type)
|| getImmutableTypes().contains(type)) {
simple = true;
}
return simple;
}
/**
* Indicates that this class is a special type which we should not attempt to reflect over,
* especially which doing deep copies or clones,
* reflection over special types is generally slow or extremely costly or unpredictable
*
* @param type any class
* @return true if this is a special type which is non-reflectable
*/
public static boolean isClassSpecial(Class<?> type) {
boolean special = false;
if (Class.class.isAssignableFrom(type)) {
special = true;
} else if (Type.class.isAssignableFrom(type)) {
special = true;
} else if (Package.class.isAssignableFrom(type)) {
special = true;
} else if (ClassLoader.class.isAssignableFrom(type)) {
special = true;
} else if (InputStream.class.isAssignableFrom(type)) {
special = true;
} else if (OutputStream.class.isAssignableFrom(type)) {
special = true;
} else if (InputStream.class.isAssignableFrom(type)) {
special = true;
} else if (Writer.class.isAssignableFrom(type)) {
special = true;
} else if (Reader.class.isAssignableFrom(type)) {
special = true;
}
return special;
}
/**
* @param type any class
* @return true if this class is a bean of some kind (i.e. not primitive, immutable, or a holder like a map)
*/
public static boolean isClassBean(Class<?> type) {
checkNull(type);
boolean bean = true;
if ( isClassSimple(type) || isClassObjectHolder(type) ) {
bean = false;
}
return bean;
}
/**
* @param type any class
* @return true if this class is an array (e.g. int[].class, {@link Integer}[] )
*/
public static boolean isClassArray(Class<?> type) {
checkNull(type);
boolean array = false;
if (type.isArray()) {
array = true;
}
return array;
}
/**
* @param type any class
* @return true if this class is a primitive (e.g. int.class, boolean.class)
*/
public static boolean isClassPrimitive(Class<?> type) {
checkNull(type);
boolean primitive = false;
if (type.isPrimitive()) {
primitive = true;
}
return primitive;
}
/**
* @param type any class
* @return true if this class is a list (e.g. {@link List}, {@link ArrayList})
*/
public static boolean isClassList(Class<?> type) {
checkNull(type);
boolean list = false;
if (List.class.isAssignableFrom(type)) {
list = true;
}
return list;
}
/**
* @param type any class
* @return true if this class is a collection (e.g. {@link Collection}, {@link HashSet}, {@link Vector})
*/
public static boolean isClassCollection(Class<?> type) {
checkNull(type);
boolean collection = false;
if (Collection.class.isAssignableFrom(type)) {
collection = true;
}
return collection;
}
/**
* @param type any class
* @return true if this class is a map (e.g. {@link Map}, {@link HashMap})
*/
public static boolean isClassMap(Class<?> type) {
checkNull(type);
boolean collection = false;
if (Map.class.isAssignableFrom(type)) {
collection = true;
}
return collection;
}
/**
* @param type any class
* @return true if this is a collection, map, or array,
* something that holds a bunch of objects (e.g. {@link Map}, {@link Set}, {@link List}, array)
*/
public static boolean isClassObjectHolder(Class<?> type) {
checkNull(type);
boolean holder = false;
if ( isClassArray(type) || isClassCollection(type) || isClassMap(type) ) {
holder = true;
}
return holder;
}
/**
* @param type any class
* @return the type of the array elements if this is an array or just the type if it is not an array
*/
public static Class<?> getTypeFromArray(Class<?> type) {
Class<?> toType = type;
if (type.isArray()) {
toType = type.getComponentType();
}
return toType;
}
/**
* Checks for the special cases of the inner collections in {@link Collections} and {@link Arrays}
* @param type any class type
* @return the equivalent of the inner collection type or the original type if this is not one
*/
public static Class<?> getTypeFromInnerCollection(Class<?> type) {
// check for the special cases of collections which cannot be constructed
if (type != null) {
Class<?> parent = type.getEnclosingClass();
if (parent != null) {
if (Collections.class.equals(parent)) {
// unmodifiable collections
List<Class<?>> l = getInterfacesForClass(type);
if (l.size() > 0) {
for (Class<?> iface : l) {
if (Collection.class.isAssignableFrom(iface)) {
if ( List.class.isAssignableFrom(iface)
|| Set.class.isAssignableFrom(iface)) {
type = iface;
} else {
type = Collection.class;
}
break;
} else if (Map.class.isAssignableFrom(iface)) {
type = iface;
break;
}
}
} else {
type = Collection.class;
}
} else if (Arrays.class.equals(parent)) {
// Arrays#ArrayList special case
type = List.class;
}
}
}
return type;
}
/**
* Gets a valid class which can be constructed from an interface or special cases which cannot be constructed
* @param <T>
* @param type any class
* @return the type which implements this interface if one can be found
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> getClassFromInterface(Class<T> type) {
Class<T> toType = type;
// check for the special cases of collections which cannot be constructed
type = (Class<T>) ConstructorUtils.getTypeFromInnerCollection(type);
// now check for interfaces
if (type.isInterface()) {
if (SortedSet.class.isAssignableFrom(type)) {
toType = (Class<T>) TreeSet.class;
} else if (SortedMap.class.isAssignableFrom(type)) {
toType = (Class<T>) TreeMap.class;
} else if ( isClassList(type) ) {
// we use the thread safe version of list by default
toType = (Class<T>) Vector.class;
} else if (Set.class.isAssignableFrom(type)) {
toType = (Class<T>) HashSet.class;
} else if ( isClassMap(type) ) {
toType = (Class<T>) ArrayOrderedMap.class;
} else if ( isClassCollection(type) ) {
toType = (Class<T>) Vector.class;
// Serializable should stay at the end
} else if (Serializable.class.isAssignableFrom(type)) {
// if it is serializable then it is probably a string right?
toType = (Class<T>) String.class;
} else {
// TODO try to find the interface implementation in the ClassLoader (not actually possible without real hackery)
}
}
return toType;
}
/**
* A simple but efficient method for getting the interfaces for a class type,
* this has some shortcuts for the common types like maps, lists, etc.<br/>
* Only returns the interfaces for the current type and not for all nested types
*
* @param type any class type
* @return the list of interfaces (empty if none)
*/
public static List<Class<?>> getInterfacesForClass(Class<?> type) {
ArrayList<Class<?>> interfaces = new ArrayList<Class<?>>();
// find the actual interfaces from the class itself
for (Class<?> iface : type.getInterfaces()) {
interfaces.add(iface);
}
// add in the collection interface if this is a collection
if ( isClassCollection(type) ) {
if ( isClassList(type) ) {
interfaces.add(List.class);
} else if ( Set.class.isAssignableFrom(type)) {
interfaces.add(Set.class);
}
interfaces.add(Collection.class);
} else if ( isClassMap(type) ) {
interfaces.add(Map.class);
}
return interfaces;
}
/**
* Adds the class which this class extends (if there is one) to the list of interfaces
* @see #getInterfacesForClass(Class)
* @param type any class type
* @return the list of interfaces and the class this extends (empty if none)
*/
public static List<Class<?>> getExtendAndInterfacesForClass(Class<?> type) {
ArrayList<Class<?>> l = new ArrayList<Class<?>>();
Class<?> superClass = type.getSuperclass();
if (superClass != null) {
l.add(superClass);
}
l.addAll( getInterfacesForClass(type) );
return l;
}
/**
* Get the wrapper class for this class if there is one
* @param beanClass any class
* @return the wrapper class if there is one OR just returns the given class
*/
public static Class<?> getWrapper(final Class<?> beanClass) {
Class<?> wrapper = null;
if (beanClass != null) {
if ( isClassPrimitive(beanClass) ) {
wrapper = getPrimitiveToWrapper().get(beanClass);
} else if ( isClassArray(beanClass) && beanClass.getComponentType().isPrimitive()) {
wrapper = getPrimitiveToWrapper().get(beanClass);
} else {
wrapper = beanClass;
}
if (wrapper == null) {
wrapper = beanClass;
}
}
return wrapper;
}
/**
* Will compare 2 classes for equality which will make a friendly comparison of types
* and will happily compare primitive types with wrappers and say they are equal
* @param c1 any class
* @param c2 any class
* @return true if the classes are equivalent, false otherwise
*/
public static boolean classEquals(final Class<?> c1, final Class<?> c2) {
boolean equals = false;
if (c1 == null || c2 == null) {
equals = false;
} else {
if (c1.isArray() && c2.isArray()) {
// both arrays
if (c1.getComponentType().isPrimitive() == c2.getComponentType().isPrimitive()) {
equals = c1.equals(c2);
} else {
// mixed primitive/wrappers so make all wrappers
Class<?> c1W = getWrapper(c1);
Class<?> c2W = getWrapper(c2);
equals = c1W.equals(c2W);
}
} else {
if (c1.isArray() || c2.isArray()) {
// one array and the other is not so cannot be equals
equals = false;
} else if (c1.isPrimitive() == c2.isPrimitive()) {
equals = c1.equals(c2);
} else {
// mixed primitive/wrappers so make all wrappers
Class<?> c1W = getWrapper(c1);
Class<?> c2W = getWrapper(c2);
equals = c1W.equals(c2W);
}
}
}
return equals;
}
/**
* Checks if assignFrom is assignable to assignTo (i.e. this is OK: assignFrom b; assignTo a = (assignTo) b;) <br/>
* An example of this is: Integer b; Object a = (Object) b; <br/>
* Another example of this is: ExtendedThing b; Thing a = (Thing) b; <br/>
* This works like {@link #classEquals(Class, Class)} and will convert primitive class types for comparison automatically
*
* @param assignFrom any class
* @param assignTo any class
* @return true if the class is assignable or equal OR false otherwise
*/
public static boolean classAssignable(final Class<?> assignFrom, final Class<?> assignTo) {
boolean assignable = false;
if (assignTo == null || assignFrom == null) {
assignable = false;
} else {
if ( Object.class.equals(assignTo) ) {
// anything can assign to an object
assignable = true;
} else if ( classEquals(assignTo, assignFrom) ) {
assignable = true;
} else {
if (assignTo.isAssignableFrom(assignFrom)) {
assignable = true;
} else {
// make everything wrappers
Class<?> c1W = getWrapper(assignTo);
Class<?> c2W = getWrapper(assignFrom);
assignable = c1W.isAssignableFrom(c2W);
}
}
}
return assignable;
}
/*
* Some code below derived from BeanUtilsBean and PropertyUtilsbean
* http://commons.apache.org/beanutils/
* Licensed under the Apache License, Version 2.0 (the "License");
*/
/**
* Construct an object for the class of the given type regardless of whether it has a default constructor,
* this will construct anything which has a valid class type including primitives,
* arrays, collections and even classes without default constructors,
* this will attempt to use the default constructor first if available though,
* It must be possible to construct the class without knowing something about it beforehand,
* (i.e. classes with only constructors which require non-null arguments will not be able
* to be constructed)
*
* @param <T>
* @param type any object class
* @return the newly constructed object of the given class type
* (if primitive then a wrapped object will be returned which java will unwrap automatically)
* @throws IllegalArgumentException if the class is null or the class cannot be constructed
*/
@SuppressWarnings("unchecked")
public <T> T constructClass(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Cannot construct class when beanClass is null");
}
// make sure we are not trying to construct an interface
type = ConstructorUtils.getClassFromInterface(type);
T newC = null;
if ( isClassPrimitive(type) ) {
if (getPrimitiveDefaults().containsKey(type)) {
newC = (T) getPrimitiveDefaults().get(type);
}
} else if ( isClassArray(type) ) {
Class<?> componentType = getTypeFromArray(type);
try {
newC = (T) Array.newInstance(componentType, 0);
} catch (RuntimeException e) {
throw new IllegalArgumentException("Could not construct array of type: " + componentType + " for: " + type.getName());
}
}
if (newC == null) {
try {
// this should work 99% of the time
newC = (T) type.newInstance();
} catch (Exception e) {
// now we will try to use the various constructors by giving them null values to construct the object
List<Constructor<T>> constructors = null;
if ( ConstructorUtils.isClassBean(type) ) {
// get bean constructors
constructors = getClassDataCacher().getClassData(type).getConstructors();
} else {
// simpler type
try {
Constructor<?>[] c = type.getConstructors();
constructors = Arrays.asList((Constructor<T>[])c);
} catch (SecurityException e1) {
throw new IllegalArgumentException("Could not construct object for class (" + type.getName() + "): " + e1.getMessage(), e1);
}
}
for (Constructor<T> constructor : constructors) {
Object[] params = new Object[ constructor.getParameterTypes().length ];
try {
newC = (T) constructor.newInstance(params);
break;
} catch (IllegalArgumentException e1) {
// oh well
} catch (InstantiationException e1) {
// tough cookies
} catch (IllegalAccessException e1) {
// them's the breaks
} catch (InvocationTargetException e1) {
// life's tough
} catch (ExceptionInInitializerError e1) {
// meh
}
// ignore any exceptions and keep trying
}
if (newC == null) {
// all attempts failed
throw new IllegalArgumentException("Could not construct object for class (" + type.getName() + ") using newInstance or using any of the constructors: " + e.getMessage(), e);
}
}
}
return newC;
}
/**
* Construct an object for the class of the given type with the given params (arguments),
* arguments must match or the construction will fail
*
* @param <T>
* @param type any object class
* @param params the parameters (args) for the constructor
* @return the newly constructed object of the given class type OR fails if the params cannot be matched
* @throws IllegalArgumentException if the class is null or the class cannot be constructed
*/
@SuppressWarnings("unchecked")
public <T> T constructClass(Class<T> type, Object[] params) {
if (type == null) {
throw new IllegalArgumentException("beanClass cannot be null");
}
T newC = null;
if (params == null || params.length == 0) {
newC = constructClass(type);
} else {
int paramsCount = params.length;
// get all the constructors
List<Constructor<T>> constructors = null;
try {
constructors = getClassDataCacher().getClassData(type).getConstructors();
} catch (IllegalArgumentException e) {
try {
Constructor<?>[] c = type.getConstructors();
constructors = Arrays.asList((Constructor<T>[])c);
} catch (SecurityException e1) {
throw new IllegalArgumentException("Could not construct object for class (" + type.getName() + ")", e1);
}
}
// make a list of the param type arrays
List<Class<?>[]> constParamTypesList = new ArrayList<Class<?>[]>();
for (Constructor<T> constructor : constructors) {
constParamTypesList.add( constructor.getParameterTypes() );
}
// make an array of the input params types
Class<?>[] paramTypes = new Class<?>[params.length];
for (int i = 0; i < params.length; i++) {
if (params[i] == null) {
// handle nulls as any object
paramTypes[i] = Object.class;
} else {
Class<?> c = params[i].getClass();
paramTypes[i] = c;
}
}
// now see if any are a match
Constructor<T> matched = null;
Object[] args = null;
// try to find exact match by size and order
for (int i = 0; i < constParamTypesList.size(); i++) {
Class<?>[] cParamTypes = constParamTypesList.get(i);
if (cParamTypes.length == paramsCount) {
// found matching number of params
boolean matching = false;
for (int j = 0; j < cParamTypes.length; j++) {
if ( classEquals(paramTypes[j], cParamTypes[j]) ) {
matching = true;
} else {
matching = false;
break;
}
}
if (matching) {
// found exact match
matched = constructors.get(i);
args = params;
break;
}
}
}
// try to find exact match by size and order (but not the exact class types, just assignable)
if (matched == null) {
for (int i = 0; i < constParamTypesList.size(); i++) {
Class<?>[] cParamTypes = constParamTypesList.get(i);
if (cParamTypes.length == paramsCount) {
// found matching number of params
boolean matching = false;
for (int j = 0; j < cParamTypes.length; j++) {
if ( classAssignable(cParamTypes[j], paramTypes[j]) ) {
// assignable (near) match
matching = true;
} else {
matching = false;
break;
}
}
if (matching) {
// found nearly exact match
matched = constructors.get(i);
args = params;
break;
}
}
}
}
// TODO try to find near match by size (same number and types)
// TODO try to find near match by order (same order but with extra junk nulls on the end)
// TODO try to make any possible match
// now try to construct if we got a match
if (matched != null) {
try {
newC = matched.newInstance(args);
} catch (Exception e) {
throw new IllegalArgumentException("Failure constructing object for class (" + type.getName() + ") with the given params: " + ArrayUtils.arrayToString(params), e);
}
}
}
if (newC == null) {
throw new IllegalArgumentException("Could not construct object for class (" + type.getName() + ") with the given params: " + ArrayUtils.arrayToString(params));
}
return newC;
}
@Override
public String toString() {
return "Construct:"+getClassDataCacher();
}
// STATIC access
protected static SoftReference<ConstructorUtils> instanceStorage;
/**
* Get a singleton instance of this class to work with (stored statically) <br/>
* <b>WARNING</b>: do not hold onto this object or cache it yourself, call this method again if you need it again
* @return a singleton instance of this class
*/
public static ConstructorUtils getInstance() {
ConstructorUtils instance = (instanceStorage == null ? null : instanceStorage.get());
if (instance == null) {
instance = ConstructorUtils.setInstance(null);
}
return instance;
}
/**
* Set the singleton instance of the class which will be stored statically
* @param instance the instance to use as the singleton instance
*/
public static ConstructorUtils setInstance(ConstructorUtils newInstance) {
ConstructorUtils instance = newInstance;
if (instance == null) {
instance = new ConstructorUtils();
instance.singleton = true;
}
ConstructorUtils.timesCreated++;
instanceStorage = new SoftReference<ConstructorUtils>(instance);
return instance;
}
private static int timesCreated = 0;
public static int getTimesCreated() {
return timesCreated;
}
private boolean singleton = false;
/**
* @return true if this object is the singleton
*/
public boolean isSingleton() {
return singleton;
}
}