package org.test4j.tools.commons;
import static ext.test4j.apache.commons.io.IOUtils.closeQuietly;
import static java.lang.reflect.Modifier.isStatic;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import mockit.MockUp;
import org.test4j.module.Test4JException;
import org.test4j.module.core.utility.MessageHelper;
import org.test4j.tools.datagen.ConstructorArgsGenerator;
import org.test4j.tools.exception.NewInstanceException;
import org.test4j.tools.reflector.MethodAccessor;
import ext.test4j.apache.commons.io.IOUtils;
import ext.test4j.objenesis.ObjenesisHelper;
@SuppressWarnings({ "rawtypes", "unchecked" })
public class ClazzHelper {
private static final Map<String, Boolean> clazAvailableCache = new HashMap<String, Boolean>();
/**
* Utility method that verifies whether the class with the given fully
* qualified classname is available in the classpath.
*
* @param className The name of the class
* @return True if the class with the given name is available
*/
public static boolean isClassAvailable(String className) {
Boolean isAvailable = clazAvailableCache.get(className);
if (isAvailable != null) {
return isAvailable;
}
try {
Thread.currentThread().getContextClassLoader().loadClass(className);
clazAvailableCache.put(className, true);
return true;
} catch (ClassNotFoundException e) {
clazAvailableCache.put(className, false);
return false;
}
}
/**
* Gets the class for the given name. An Test4JException is thrown when the
* class could not be loaded.
*
* @param className The name of the class, not null
* @return The class, not null
*/
public static <T> Class<T> getClazz(String className) {
try {
return (Class<T>) Class.forName(className);
} catch (Throwable t) {
throw new Test4JException("Could not load class with name " + className, t);
}
}
/**
* @param className The name of the class to check, not null
* @return True if the classfile exists in the classpath
*/
public static boolean classFileExistsInClasspath(String className) {
String classFileName = className.replace('.', '/') + ".class";
return ClazzHelper.class.getClassLoader().getResource(classFileName) != null;
}
/**
* 类路径中是否有 org.hibernate.tool.hbm2ddl.SchemaExport class
*
* @return
*/
public final static boolean doesImportSchemaExport() {
try {
Class.forName("org.hibernate.tool.hbm2ddl.SchemaExport");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
public final static String getPackFromClassName(String clazzName) {
int index = clazzName.lastIndexOf(".");
String pack = "";
if (index > 0) {
pack = clazzName.substring(0, index);
}
return pack;
}
/**
* 根据类名获得package的路径<br>
* a.b.c.ImplClazz 返回 a/b/c
*
* @param clazzName
* @return
*/
public final static String getPathFromPath(String clazzName) {
String pack = getPackFromClassName(clazzName);
return pack.replace(".", "/");
}
/**
* 根据类名获得package的路径<br>
* a.b.c.ImplClazz 返回 "a/b/c"<br>
* if clazz==null, 返回 ""
*
* @param clazzName
* @return
*/
public final static String getPathFromPath(Class clazz) {
if (clazz == null) {
return "";
} else {
return getPathFromPath(clazz.getName());
}
}
public final static void generateClazz(String clazzName, byte[] r) throws IOException {
// debug,调试生成的字节码
FileOutputStream file = new FileOutputStream("d:/" + clazzName.substring(clazzName.lastIndexOf('.') + 1)
+ ".class");
file.write(r);
file.flush();
file.close();
}
/**
* package名称有效字符表达式
*/
public final static String VALID_PACK_REGULAR = "[^\\.]*";
/**
* 将带有星号的package名称转换为正则表达式
*
* @param regexPackage 带有星号的package名称,比如 com.**.service.*Impl
* @return
*/
public static String getPackageRegex(String regexPackage) {
String _interfaceKey = regexPackage.replace('*', '#');// 先把*替换成不会被使用的字符#,避免后面的正则表达式替换错误
String regex = _interfaceKey.replace(".", "\\.");
regex = regex.replace("\\.##\\.", "(\\.|\\..*\\.)");
regex = regex.replace("##\\.", ".*\\.");
regex = regex.replace("#", VALID_PACK_REGULAR);
return regex;
}
/**
* 判断类型是否是接口或者抽象类
*
* @param type
* @return
*/
public static boolean isInterfaceOrAbstract(Class type) {
if (type == null) {
throw new RuntimeException("type can't be null.");
}
if (type.isInterface()) {
return true;
} else if (Modifier.isAbstract(type.getModifiers())) {
return true;
} else {
return false;
}
}
/**
* 从void setBeanName(Type instance)方法中解析出beanName
*
* @param method
* @return
*/
public static String exactBeanName(Method method) {
String methodname = method.getName();
if (methodname.equalsIgnoreCase("set") || methodname.startsWith("set") == false) {
return null;
}
String beanName = methodname.substring(3);
beanName = beanName.substring(0, 1).toLowerCase() + beanName.substring(1);
return beanName;
}
/**
* 创建className的实例<br>
* Creates an instance of the class with the given name. The class's no
* argument constructor is used to create an instance.
*
* @param className The name of the class, not null
* @return An instance of this class
* @throws Test4JException if the class could not be found or no instance
* could be created
*/
public static <T> T createInstanceOfType(String className) {
try {
Class type = Class.forName(className);
return (T) newInstance(type);
} catch (ClassCastException e) {
throw new Test4JException("Class " + className + " is not of expected type.", e);
} catch (NoClassDefFoundError e) {
throw new Test4JException("Unable to load class " + className, e);
} catch (ClassNotFoundException e) {
throw new Test4JException("Class " + className + " not found", e);
} catch (Test4JException e) {
throw e;
} catch (Throwable e) {
throw new Test4JException("Error while instantiating class " + className, e);
}
}
/**
* Checks whether the given fromType is assignable to the given toType, also
* taking into account possible auto-boxing.<br>
* Check if the right-hand side type may be assigned to the left-hand side
* type following the Java generics rules.
*
* @param fromType The from type, not null
* @param toType The to type, not null
* @return True if assignable
*/
public static boolean isAssignable(Type fromType, Type toType) {
if (fromType instanceof Class && toType instanceof Class) {
Class fromClass = (Class) fromType;
Class toClass = (Class) toType;
// handle auto boxing types
if (boolean.class.equals(fromClass) && Boolean.class.isAssignableFrom(toClass)
|| boolean.class.equals(toClass) && Boolean.class.isAssignableFrom(fromClass)) {
return true;
}
if (char.class.equals(fromClass) && Character.class.isAssignableFrom(toClass) || char.class.equals(toClass)
&& Character.class.isAssignableFrom(fromClass)) {
return true;
}
if (int.class.equals(fromClass) && Integer.class.isAssignableFrom(toClass) || int.class.equals(toClass)
&& Integer.class.isAssignableFrom(fromClass)) {
return true;
}
if (long.class.equals(fromClass) && Long.class.isAssignableFrom(toClass) || long.class.equals(toClass)
&& Long.class.isAssignableFrom(fromClass)) {
return true;
}
if (float.class.equals(fromClass) && Float.class.isAssignableFrom(toClass) || float.class.equals(toClass)
&& Float.class.isAssignableFrom(fromClass)) {
return true;
}
if (double.class.equals(fromClass) && Double.class.isAssignableFrom(toClass)
|| double.class.equals(toClass) && Double.class.isAssignableFrom(fromClass)) {
return true;
}
return toClass.isAssignableFrom(fromClass);
} else {
if (toType.equals(fromType)) {
return true;
}
if (toType instanceof Class && fromType instanceof Class) {
return ((Class) toType).isAssignableFrom((Class) fromType);
}
if (toType instanceof ParameterizedType && fromType instanceof ParameterizedType) {
return isAssignable((ParameterizedType) toType, (ParameterizedType) fromType);
}
if (toType instanceof WildcardType) {
return isAssignable((WildcardType) toType, fromType);
}
return false;
}
}
private static boolean isAssignable(ParameterizedType lhsType, ParameterizedType rhsType) {
if (lhsType.equals(rhsType)) {
return true;
}
Type[] lhsTypeArguments = lhsType.getActualTypeArguments();
Type[] rhsTypeArguments = rhsType.getActualTypeArguments();
if (lhsTypeArguments.length != rhsTypeArguments.length) {
return false;
}
for (int size = lhsTypeArguments.length, i = 0; i < size; ++i) {
Type lhsArg = lhsTypeArguments[i];
Type rhsArg = rhsTypeArguments[i];
if (!lhsArg.equals(rhsArg)
&& !(lhsArg instanceof WildcardType && isAssignable((WildcardType) lhsArg, rhsArg))) {
return false;
}
}
return true;
}
private static boolean isAssignable(WildcardType lhsType, Type rhsType) {
Type[] upperBounds = lhsType.getUpperBounds();
Type[] lowerBounds = lhsType.getLowerBounds();
for (int size = upperBounds.length, i = 0; i < size; ++i) {
if (!isAssignable(upperBounds[i], rhsType)) {
return false;
}
}
for (int size = lowerBounds.length, i = 0; i < size; ++i) {
if (!isAssignable(rhsType, lowerBounds[i])) {
return false;
}
}
return true;
}
/**
* 根据字符串获取枚举值<br>
* Gets the enum value that has the given name.
*
* @param enumClass The enum class, not null
* @param enumValueName The name of the enum value, not null
* @return The actual enum value, not null
* @throws Test4JException if no value could be found with the given name
*/
public static <T extends Enum<?>> T getEnumValue(Class<T> enumClass, String enumValueName) {
T[] enumValues = enumClass.getEnumConstants();
for (T enumValue : enumValues) {
if (enumValueName.equalsIgnoreCase(enumValue.name())) {
return enumValue;
}
}
throw new Test4JException("Unable to find a enum value in enum: " + enumClass + ", with value name: "
+ enumValueName);
}
/**
* 获得泛型字段的参数类类型<br>
* Gets the T from a Class<T> field declaration. An exception is raised if
* the field type is not generic or has more than 1 generic type
*
* @param field The field to get the type from, not null
* @return The declared generic type
*/
public static Type getGenericType(Field field) {
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
Type[] argumentTypes = ((ParameterizedType) type).getActualTypeArguments();
if (argumentTypes.length == 1) {
return argumentTypes[0];
}
throw new Test4JException("Unable to determine unique generic type for field: " + field
+ ". The field type declares more than one generic type: " + type);
}
throw new Test4JException("Unable to determine unique generic type for field: " + field
+ ". Field type is not a generic type: " + type);
}
/**
* Gets the class instance for the given type instance.
*
* @param type The type to get a class instance for, not null
* @return The class instance, not null
*/
public static <T> Class<T> getClassForType(Type type) {
if (type instanceof Class) {
return (Class<T>) type;
}
if (type instanceof ParameterizedType) {
return (Class<T>) ((ParameterizedType) type).getRawType();
}
throw new Test4JException("Unable to convert Type instance " + type + " to a Class instance.");
}
/**
* 读取class的字节码内容byte[]
*
* @param clazz
* @return
*/
public static byte[] getBytes(Class clazz) {
String name = clazz.getName().replace('.', '/') + ".class";
InputStream iStream = clazz.getClassLoader().getResourceAsStream(name);
try {
ByteArrayOutputStream oStream = new ByteArrayOutputStream();
IOUtils.copy(iStream, oStream);
return oStream.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
closeQuietly(iStream);
}
}
/**
* 创建claz对象实例<br>
* 不是通过 new Construction()形式
*
* @param <T>
* @param claz
* @return
*/
public static <T> T newInstance(Class<T> claz) {
if (claz.isMemberClass() && !isStatic(claz.getModifiers())) {
throw new NewInstanceException(
"Creation of an instance of a non-static innerclass is not possible using reflection. The type "
+ claz.getSimpleName()
+ " is only known in the context of an instance of the enclosing class "
+ claz.getEnclosingClass().getSimpleName()
+ ". Declare the innerclass as static to make construction possible.");
}
try {
return innerNewInstance(claz);
} catch (NewInstanceException ne) {
throw ne;
} catch (Throwable e) {
throw new NewInstanceException(e);
}
}
private static <T> T innerNewInstance(Class<T> claz) {
if (claz.isInterface()) {
return (T) new MockUp(claz) {
}.getMockInstance();
}
int modifiers = claz.getModifiers();
if (Modifier.isAbstract(modifiers)) {
throw new NewInstanceException("unsupport abstract class!");
}
try {
Constructor constructor = claz.getDeclaredConstructor();
if (constructor != null) {
boolean isAccessor = constructor.isAccessible();
constructor.setAccessible(true);
Object o = constructor.newInstance();
constructor.setAccessible(isAccessor);
return (T) o;
}
} catch (Throwable e) {
MessageHelper.warn("new instance[" + claz.getName() + "] error", e);
return (T) ObjenesisHelper.newInstance(claz);
}
return (T) ObjenesisHelper.newInstance(claz);
}
public static <T> T newInstance(Class<T> claz, ConstructorArgsGenerator argGenerator) {
Constructor constructor = null;
try {
constructor = claz.getDeclaredConstructor();
} catch (Exception e) {
constructor = claz.getDeclaredConstructors()[0];
}
if (constructor == null) {
constructor = claz.getConstructors()[0];
}
try {
Object[] args = argGenerator.generate(constructor);
boolean isAccessor = constructor.isAccessible();
constructor.setAccessible(true);
Object o = constructor.newInstance(args);
constructor.setAccessible(isAccessor);
return (T) o;
} catch (Exception e) {
MessageHelper.warn("new instance[" + claz.getName() + "] error.", e);
return (T) ObjenesisHelper.newInstance(claz);
}
}
/**
* 返回可以class中定义的字段(包含父类定义的)<br>
*
* @param clazz
* @param filters 需要过滤掉的字段
* @param includeStatic 是否包含static修饰的字段
* @param includeFinal 是否包含final修饰的字段
* @param includeTransient 是否包含transient修饰的字段
* @return
*/
public static final List<Field> getAllFields(Class clazz, Collection<String> filters, boolean includeStatic,
boolean includeFinal, boolean includeTransient) {
List<Field> jsonFields = new ArrayList<Field>();
List<Field> fields = FieldHelper.getAllFields(clazz);
for (Field field : fields) {
String fieldname = field.getName();
if (filters != null && filters.contains(fieldname)) {
continue;
}
int modifier = field.getModifiers();
if (!includeStatic && Modifier.isStatic(modifier)) {
continue;
}
if (!includeFinal && Modifier.isFinal(modifier)) {
continue;
}
if (!includeTransient && Modifier.isTransient(modifier)) {
continue;
}
if (jsonFields.contains(field)) {
continue;
}
jsonFields.add(field);
}
return jsonFields;
}
private final static String Proxy_Type_Pattern = ".*\\$[Pp]roxy\\d+.*";
/**
* 返回非代理类的类型
*
* @param type
* @return
*/
public static Class getUnProxyType(Class clazz) {
if (Proxy.isProxyClass(clazz)) {
return Object.class;
}
Class type = clazz;
while (type.isAnonymousClass() || type.getName().matches(Proxy_Type_Pattern)) {
type = type.getSuperclass();
}
return type;
}
private static MethodAccessor springModuleHelper;
private static MethodAccessor getSpringModuleHelper() throws ClassNotFoundException {
if (springModuleHelper == null) {
Class claz = Class.forName("org.test4j.module.spring.utility.SpringModuleHelper");
springModuleHelper = new MethodAccessor(claz, "getAdvisedObject", Object.class);
}
return springModuleHelper;
}
/**
* 如果是spring代理对象,获得被代理的目标对象
*
* @param target
* @return
*/
public static Object getProxiedObject(Object target) {
try {
if (ClazzHelper.isClassAvailable("org.springframework.aop.framework.Advised")) {
Object o = getSpringModuleHelper().invokeStatic(new Object[] { target });
return o;
} else {
return target;
}
} catch (Exception e) {
MessageHelper.warn("get proxy object error.", e);
return target;
}
}
/**
* 是否是class文件
*
* @param classFileName
* @return
*/
public static boolean isClassFile(String classFileName) {
return classFileName.endsWith(".class");
}
/**
* 是否是内部类
*
* @param className
* @return
*/
public static boolean isInnerClass(String className) {
return className.contains("$");
}
/**
* 将class的全路径替换为带.的class名称<br>
* ex: convert /a/b.class to a.b
*
* @param clazzPath
* @return
*/
public static String replaceFileSeparators(String clazzPath) {
String clazzName = clazzPath.replace(File.separatorChar, '.');
if (File.separatorChar != '/') {
// In Jar-Files it's always '/'
clazzName = clazzName.replace('/', '.');
}
if (clazzName.startsWith(".")) {
return clazzName.substring(1);
} else {
return clazzName;
}
}
/**
* 是否是抽象类且非primitive类型
*
* @param clazz
* @return
*/
public static boolean isAbstract(Class clazz) {
boolean isAbstract = Modifier.isAbstract(clazz.getModifiers());
boolean isPrimitive = PrimitiveHelper.isPrimitiveType(clazz);
return isAbstract && !isPrimitive;
}
}