/*
* aitools utilities
* Copyright (C) 2006 Noel Bush
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.aitools.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import org.aitools.util.runtime.DeveloperError;
import org.aitools.util.runtime.UserError;
/**
* Contains utilities related to manipulating classes.
*
* @author <a href="mailto:noel@aitools.org">Noel Bush</a>
* @since 4.5
*/
public class Classes {
/**
* Returns an instance of the given class, instantiated using a constructor that takes the arguments given.
*
* @param <T> the type of the class
* @param theClass the class to instantiate
* @param description a short (one word or so) description of the desired class
* @param constructorArgs the arguments to the constructor (actual arguments, not types)
* @return the desired class
*/
@SuppressWarnings("unchecked")
public static <T> T getNewInstance(Class<T> theClass, String description, Object... constructorArgs) {
// Get the types of the arguments.
int argCount = constructorArgs.length;
ArrayList<Class<T>> argumentTypes = new ArrayList<Class<T>>(argCount);
for (int index = 0; index < argCount; index++) {
try {
argumentTypes.add((Class<T>) constructorArgs[index].getClass());
}
catch (ClassCastException e) {
throw new DeveloperError(String.format("Invalid arguments provided for constructor to create new %s.",
description), e);
}
}
// Get the constructor that takes the given argument types.
Constructor<T> constructor = null;
try {
constructor = theClass.getConstructor(argumentTypes.toArray(new Class[] {}));
}
catch (NoSuchMethodException e) {
throw new DeveloperError(String.format("Developer specified an invalid constructor for %s.", description), e);
}
catch (SecurityException e) {
throw new DeveloperError(String.format("Permission denied to create new %s with specified constructor.",
description), e);
}
// Get a new instance of the class.
try {
return constructor.newInstance(constructorArgs);
}
catch (IllegalAccessException e) {
throw new DeveloperError(String.format("Underlying constructor for %s is inaccessible.", description), e);
}
catch (InstantiationException e) {
throw new DeveloperError(String.format("Could not instantiate %s.", description), e);
}
catch (IllegalArgumentException e) {
throw new DeveloperError(String.format("Illegal argument exception when creating %s.", description), e);
}
catch (InvocationTargetException e) {
throw new DeveloperError(String.format("Constructor threw an exception when getting a %s instance from it.",
description), e.getTargetException());
}
}
/**
* Convenience wrapper for {@link Classes#getSubclassInstance(Class, String, String, Object...)} that does not require
* a description and assumes zero constructor arguments.
*
* @param classname the classname to instantiate
* @param <T> the class of which the instantiated class must be a subclass
* @param baseType the base class type
* @return the desired class
*/
public static <T> T getSubclassInstance(Class<T> baseType, String classname) {
return getSubclassInstance(baseType, classname, "[no description]", new Object[] {});
}
/**
* Returns the class which is a subclass of <code>T</code>, instantiated using a constructor that takes the arguments
* given.
*
* @param classname the classname to instantiate
* @param <T> the class of which the instantiated class must be a subclass
* @param baseType the base class type
* @param description a short (one word or so) description of the desired class
* @param constructorArgs the arguments to the constructor (actual arguments, not types)
* @return the desired class
*/
@SuppressWarnings("unchecked")
public static <T> T getSubclassInstance(Class<T> baseType, String classname, String description,
Object... constructorArgs) {
// Get the subclass.
Class<? extends T> subclass = null;
try {
subclass = (Class<? extends T>) Class.forName(classname);
}
catch (ClassNotFoundException e) {
throw new UserError(String.format("Specified %s (\"%s\") could not be found.", description, classname), e);
}
catch (ClassCastException e) {
throw new UserError(String.format("\"%s\" is not a %s subclass.", classname, description), e);
}
return getNewInstance(subclass, description, constructorArgs);
}
/**
* Verify that a given classname is available. Use the provided
* description to explain what went wrong if the class is not found.
*
* @param classname
* @param description
*/
public static void verifyAvailable(String classname, String description) {
try {
Class.forName(classname);
}
catch (ClassNotFoundException e) {
throw new UserError(String.format("Could not find %s class.", description), e);
}
}
}