/* * Copyright 2011 cruxframework.org. * * Licensed 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.cruxframework.crux.core.utils; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * @author Thiago da Rosa de Bustamante * @author Samuel Almeida Cardoso * */ public class ClassUtils { private static Set<String> simpleTypes = new HashSet<String>(); static { simpleTypes.add(Integer.class.getCanonicalName()); simpleTypes.add(Short.class.getCanonicalName()); simpleTypes.add(Byte.class.getCanonicalName()); simpleTypes.add(Long.class.getCanonicalName()); simpleTypes.add(Double.class.getCanonicalName()); simpleTypes.add(Float.class.getCanonicalName()); simpleTypes.add(Boolean.class.getCanonicalName()); simpleTypes.add(Character.class.getCanonicalName()); simpleTypes.add(Integer.TYPE.getCanonicalName()); simpleTypes.add(Short.TYPE.getCanonicalName()); simpleTypes.add(Byte.TYPE.getCanonicalName()); simpleTypes.add(Long.TYPE.getCanonicalName()); simpleTypes.add(Double.TYPE.getCanonicalName()); simpleTypes.add(Float.TYPE.getCanonicalName()); simpleTypes.add(Boolean.TYPE.getCanonicalName()); simpleTypes.add(Character.TYPE.getCanonicalName()); simpleTypes.add(String.class.getCanonicalName()); simpleTypes.add(Date.class.getCanonicalName()); simpleTypes.add(java.sql.Date.class.getCanonicalName()); simpleTypes.add(BigInteger.class.getCanonicalName()); simpleTypes.add(BigDecimal.class.getCanonicalName()); } static boolean isSimpleType(String className) { return simpleTypes.contains(className); } /** * @param rawType * @return */ public static boolean hasCharacterConstructor(Class<?> rawType) { return rawType.getCanonicalName().equals(Character.class.getCanonicalName()); } /** * @param type * @return */ public static boolean hasCharacterConstructor(Type type) { Class<?> rawType = getRawType(type); return hasCharacterConstructor(rawType); } /** * @param rawType * @return */ public static boolean hasStringConstructor(Class<?> rawType) { return !hasCharacterConstructor(rawType); } /** * @param type * @return */ public static boolean hasStringConstructor(Type type) { Class<?> rawType = getRawType(type); return hasStringConstructor(rawType); } public static boolean isAnnotationPresent(Class<?> targetClass, Class<? extends Annotation> annotationClass) { if (targetClass.isAnnotationPresent(annotationClass)) { return true; } Class<?>[] interfaces = targetClass.getInterfaces(); if (interfaces != null) { for (Class<?> intf : interfaces) { if (isAnnotationPresent(intf, annotationClass)) { return true; } } } Class<?> superclass = targetClass.getSuperclass(); if (superclass != null && isAnnotationPresent(superclass, annotationClass)) { return true; } return false; } /** * * @param type * @return */ public static boolean isSimpleType(Class<?> rawType) { return rawType.isEnum() || isSimpleType(rawType.getCanonicalName()); } /** * * @param type * @return */ public static boolean isSimpleType(Type type) { Class<?> rawType = getRawType(type); return isSimpleType(rawType); } public static Type getGenericReturnTypeOfGenericInterfaceMethod(Class<?> clazz, Method method) { if (!method.getDeclaringClass().isInterface()) { return method.getGenericReturnType(); } try { Method tmp = clazz.getMethod(method.getName(), method.getParameterTypes()); return tmp.getGenericReturnType(); } catch (NoSuchMethodException e) { } return method.getGenericReturnType(); } /** * * @param propertyName * @return */ public static String getSetterMethod(String propertyName) { if (propertyName == null || propertyName.length() == 0) { return null; } String result = "set" + Character.toUpperCase(propertyName.charAt(0)); if (propertyName.length() > 1) { result += propertyName.substring(1); } return result; } /** * * @param propertyName * @param baseClass * @return */ public static String getGetterMethod(String propertyName, Class<?> baseClass) { if (propertyName == null || propertyName.length() == 0) { return null; } String result = "" + Character.toUpperCase(propertyName.charAt(0)); result += propertyName.substring(1); if (propertyName.length() > 1) { try { baseClass.getMethod("get" + result, new Class<?>[] {}); result = "get" + result; } catch (Exception e) { try { baseClass.getMethod("is" + result, new Class<?>[] {}); result = "is" + result; } catch (Exception e1) { result = null; } } } return result; } /** * @param propertyName * @return */ public static String getGetterMethod(String propertyName) { if (propertyName == null || propertyName.length() == 0) { return null; } String result = "" + Character.toUpperCase(propertyName.charAt(0)); result += propertyName.substring(1); result = "get" + result; return result; } /** * * @param baseType * @param methodName * @return */ public static boolean hasMethod(Class<?> baseType, String methodName) { return hasMethod(baseType, methodName, new Class<?>[]{}); } /** * * @param baseType * @param methodName * @param parameters * @return */ public static boolean hasMethod(Class<?> baseType, String methodName, Class<?>[] parameters) { try { if (baseType.getMethod(methodName, parameters) != null) { return true; } } catch (Exception e) { } return false; } /** * * @param widgetType * @param setterMethod * @return */ public static boolean hasValidSetter(Class<?> widgetType, String setterMethod, Class<?> attrType) { try { if (widgetType.getMethod(setterMethod, new Class<?>[] { attrType }) != null) { return true; } } catch (Exception e) { try { if (attrType.isPrimitive()) { Class<?> wrapperType = getBoxedClassForPrimitive(attrType); if (widgetType.getMethod(setterMethod, new Class<?>[] { wrapperType }) != null) { return true; } } else { Class<?> primitiveType = getPrimitiveFromWrapper(attrType); if (primitiveType != null && widgetType.getMethod(setterMethod, new Class<?>[] { primitiveType }) != null) { return true; } } } catch (Exception e1) { // Do nothing... try superclass } if (attrType.getSuperclass() != null) { return hasValidSetter(widgetType, setterMethod, attrType.getSuperclass()); } } return false; } /** * workaround for JVM BUG - http://codereligion.com/post/28703017143/beware-of-java-beans-introspector * @author Thiago da Rosa de Bustamante * */ public static class PropertyInfo { private final String name; private final Type type; private final Method readMethod; private final Method writeMethod; public PropertyInfo(String name, Type type, Method readMethod, Method writeMethod) { this.name = name; this.type = type; this.readMethod = readMethod; this.writeMethod = writeMethod; } public String getName() { return name; } public Type getType() { return type; } public Method getReadMethod() { return readMethod; } public Method getWriteMethod() { return writeMethod; } } public static boolean isValidSetterMethod(Method method) { return (Modifier.isPublic(method.getModifiers()) && method.getName().startsWith("set") && method.getName().length() >3 && method.getParameterTypes().length == 1); } /** * * @param method * @return */ public static boolean isValidGetterMethod(Method method) { return Modifier.isPublic(method.getModifiers()) && ((method.getName().startsWith("get") && method.getName().length() >3 && method.getParameterTypes().length == 0) && !method.getName().equals("getClass") || (method.getName().startsWith("is") && method.getName().length() >2 && method.getParameterTypes().length == 0) && (method.getReturnType().equals(Boolean.TYPE) || method.getReturnType().equals(Boolean.class)) ); } public static List<Method> getSetterMethods(Class<?> objectType) { List<Method> result = new ArrayList<Method>(); Method[] methods = objectType.getMethods(); for (Method method : methods) { if (isValidSetterMethod(method)) { result.add(method); } } return result; } /** * * @param objectType * @return */ public static List<Method> getGetterMethods(Class<?> objectType) { List<Method> result = new ArrayList<Method>(); Method[] methods = objectType.getMethods(); for (Method method : methods) { if (isValidGetterMethod(method)) { result.add(method); } } return result; } public static String getPropertyForGetterOrSetterMethod(Method method) { String name = method.getName(); if (name.startsWith("get") || name.startsWith("set")) { name = name.substring(3); } else if (name.startsWith("is")) { name = name.substring(2); } name = Character.toLowerCase(name.charAt(0))+ name.substring(1); return name; } /** * workaround for JVM BUG - http://codereligion.com/post/28703017143/beware-of-java-beans-introspector * @param type * @return */ public static PropertyInfo[] extractBeanPropertiesInfo(Type type) { Class<?> rawType = getRawType(type); List<PropertyInfo> result = new ArrayList<PropertyInfo>(); List<Method> getterMethods = getGetterMethods(rawType); List<Method> setterMethods = getSetterMethods(rawType); try { for (Method setterMethod : setterMethods) { String setterProperty = getPropertyForGetterOrSetterMethod(setterMethod); for (Method getterMethod : getterMethods) { String getterProperty = getPropertyForGetterOrSetterMethod(getterMethod); if (getterProperty.equals(setterProperty)) { Type returnType = getterMethod.getGenericReturnType(); Type propertyType = getPropertyType(returnType, type, rawType); result.add(new PropertyInfo(setterProperty, propertyType, getterMethod, setterMethod)); break; } } } } catch (Exception e) { throw new RuntimeException("Unable to determine properties for bean: " + rawType.getCanonicalName(), e); } return result.toArray(new PropertyInfo[result.size()]); } public static Type resolveGenericTypeOnMethod(Type genericParameterType, Class<?> baseClass, Method method) { Class<?> declaringClass = method.getDeclaringClass(); Type baseType = getGenericDeclaringType(baseClass, declaringClass); if (baseType != null) { Type result = getPropertyType(genericParameterType, baseType, getRawType(baseType)); return result; } return getRawType(genericParameterType); } /** * @param primitiveType * @return */ public static Class<?> getBoxedClassForPrimitive(Class<?> primitiveType) { if (primitiveType.equals(Integer.TYPE)) { return Integer.class; } else if (primitiveType.equals(Short.TYPE)) { return Short.class; } else if (primitiveType.equals(Byte.TYPE)) { return Byte.class; } else if (primitiveType.equals(Long.TYPE)) { return Long.class; } else if (primitiveType.equals(Float.TYPE)) { return Float.class; } else if (primitiveType.equals(Double.TYPE)) { return Double.class; } else if (primitiveType.equals(Boolean.TYPE)) { return Boolean.class; } else if (primitiveType.equals(Character.TYPE)) { return Character.class; } return null; } /** * @param method * @return */ public static String getMethodDescription(Method method) { StringBuilder str = new StringBuilder(); str.append(method.getDeclaringClass().getCanonicalName()); str.append("."); str.append(method.getName()); str.append("("); boolean needsComma = false; for (Class<?> type : method.getParameterTypes()) { if (needsComma) { str.append(","); } needsComma = true; str.append(type.getCanonicalName()); } str.append(")"); return str.toString(); } /** * * @param type * @param field * @return */ public static boolean isPropertyVisibleToWrite(Class<?> type, Field field) { return Modifier.isPublic(field.getModifiers()) || ClassUtils.hasValidSetter(type, ClassUtils.getSetterMethod(field.getName()), field.getType()); } public static Object stringToPrimitiveBoxType(Class<?> primitiveType, String value) { if (primitiveType.equals(String.class)) { return value; } if (primitiveType.equals(Boolean.TYPE)) { if (value == null) return Boolean.FALSE; return Boolean.valueOf(value); } else if (value == null) { value = "0"; } if (primitiveType.equals(Integer.TYPE)) { return Integer.valueOf(value); } else if (primitiveType.equals(Long.TYPE)) { return Long.valueOf(value); } else if (primitiveType.equals(Double.TYPE)) { return Double.valueOf(value); } else if (primitiveType.equals(Float.TYPE)) { return Float.valueOf(value); } else if (primitiveType.equals(Byte.TYPE)) { return Byte.valueOf(value); } else if (primitiveType.equals(Short.TYPE)) { return Short.valueOf(value); } else if (primitiveType.equals(Character.TYPE)) { if (value != null && value.length() > 0) { return Character.valueOf(value.charAt(0)); } } return null; } public static Type getCollectionBaseType(Class<?> type, Type genericType) { if (genericType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericType; Type componentGenericType = parameterizedType.getActualTypeArguments()[0]; return componentGenericType; } else if (genericType instanceof GenericArrayType) { final GenericArrayType genericArrayType = (GenericArrayType) genericType; Type componentGenericType = genericArrayType.getGenericComponentType(); return componentGenericType; } else if (type.isArray()) { return type.getComponentType(); } return null; } public static boolean isCollection(Class<?> type) { return (type.isArray() || Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)); } public static Class<?> getRawType(Type type) { if (type instanceof Class<?>) { // type is a normal class. return (Class<?>) type; } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; Type rawType = parameterizedType.getRawType(); return (Class<?>) rawType; } else if (type instanceof GenericArrayType) { final GenericArrayType genericArrayType = (GenericArrayType) type; final Class<?> componentRawType = getRawType(genericArrayType.getGenericComponentType()); return Array.newInstance(componentRawType, 0).getClass(); } else if (type instanceof TypeVariable) { final TypeVariable<?> typeVar = (TypeVariable<?>) type; if (typeVar.getBounds() != null && typeVar.getBounds().length > 0) { return getRawType(typeVar.getBounds()[0]); } } throw new RuntimeException("Unable to determine base class from Type"); } @SuppressWarnings("unchecked") public static <T> T findAnnotation(Annotation[] searchList, Class<T> annotation) { if (searchList == null) { return null; } for (Annotation ann : searchList) { if (ann.annotationType().equals(annotation)) { return (T) ann; } } return null; } public static Type getActualValueOfTypeVariable(Class<?> clazz, TypeVariable<?> typeVariable) { if (typeVariable.getGenericDeclaration() instanceof Class<?>) { Class<?> classDeclaringTypeVariable = (Class<?>) typeVariable.getGenericDeclaration(); // find the generic version of classDeclaringTypeVariable Type fromInterface = getTypeVariableViaGenericInterface(clazz, classDeclaringTypeVariable, typeVariable); if (fromInterface != null) { return fromInterface; } while (clazz.getSuperclass() != null) { if (clazz.getSuperclass().equals(classDeclaringTypeVariable)) { // found it ParameterizedType parameterizedSuperclass = (ParameterizedType) clazz.getGenericSuperclass(); for (int i = 0; i < classDeclaringTypeVariable.getTypeParameters().length; i++) { TypeVariable<?> tv = classDeclaringTypeVariable.getTypeParameters()[i]; if (tv.equals(typeVariable)) { return parameterizedSuperclass.getActualTypeArguments()[i]; } } } clazz = clazz.getSuperclass(); } } throw new RuntimeException("Unable to determine value of type parameter " + typeVariable); } private static Type getGenericDeclaringType(Class<?> baseClass, Class<?> declaringClass) { if (baseClass.equals(declaringClass)) { return baseClass; } if (declaringClass.isInterface()) { Type[] interfaces = baseClass.getGenericInterfaces(); for (Type type : interfaces) { if (type instanceof ParameterizedType) { if (((ParameterizedType)type).getRawType().equals(declaringClass)) { return type; } } } } else { Class<?> superclass = baseClass.getSuperclass(); if (superclass != null && superclass.equals(declaringClass)) { return baseClass.getGenericSuperclass(); } } return null; } private static Type getPropertyType(final Type propertyType, final Type baseClass, final Class<?> baseRawType) { Type result = null; if (propertyType instanceof Class) { result = propertyType; } else if (propertyType instanceof ParameterizedType) { return new ParameterizedType() { @Override public Type getRawType() { return ((ParameterizedType)propertyType).getRawType(); } @Override public Type getOwnerType() { return ((ParameterizedType)propertyType).getOwnerType(); } @Override public Type[] getActualTypeArguments() { Type[] origActualTypeArguments = ((ParameterizedType)propertyType).getActualTypeArguments(); Type[] actualTypeArguments = new Type[origActualTypeArguments.length]; int i=0; for (Type type : origActualTypeArguments) { actualTypeArguments[i++] = getPropertyType(type, baseClass, baseRawType); } return actualTypeArguments; } }; } else if (propertyType instanceof GenericArrayType) { final GenericArrayType genericArrayType = (GenericArrayType) propertyType; final Class<?> componentRawType = getRawType(getPropertyType(genericArrayType.getGenericComponentType(), baseClass, baseRawType)); result = Array.newInstance(componentRawType, 0).getClass(); } else if (propertyType instanceof TypeVariable) { Type[] typeArguments = ((ParameterizedType)baseClass).getActualTypeArguments(); TypeVariable<?>[] typeParameters = baseRawType.getTypeParameters(); String parameterName = ((TypeVariable<?>)propertyType).getName(); int i=0; for (TypeVariable<?> typeVariable : typeParameters) { if (parameterName.equals(typeVariable.getName())) { result = typeArguments[i]; break; } i++; } if (result == null) { throw new RuntimeException("Unable to determine property types for bean: " + baseRawType.getCanonicalName()); } } else { throw new RuntimeException("Unable to determine property types for bean: " + baseRawType.getCanonicalName() + ". Type is not supported: " + propertyType); } return result; } /** * @param attrType * @return */ private static Class<?> getPrimitiveFromWrapper(Class<?> attrType) { if (attrType.equals(Integer.class)) { return Integer.TYPE; } else if (attrType.equals(Short.class)) { return Short.TYPE; } else if (attrType.equals(Long.class)) { return Long.TYPE; } else if (attrType.equals(Byte.class)) { return Byte.TYPE; } else if (attrType.equals(Float.class)) { return Float.TYPE; } else if (attrType.equals(Double.class)) { return Double.TYPE; } else if (attrType.equals(Boolean.class)) { return Boolean.TYPE; } else if (attrType.equals(Character.class)) { return Character.TYPE; } return null; } private static Type getTypeVariableViaGenericInterface(Class<?> clazz, Class<?> classDeclaringTypeVariable, TypeVariable<?> typeVariable) { for (Type genericInterface : clazz.getGenericInterfaces()) { if (genericInterface instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericInterface; for (int i = 0; i < classDeclaringTypeVariable.getTypeParameters().length; i++) { TypeVariable<?> tv = classDeclaringTypeVariable.getTypeParameters()[i]; if (tv.equals(typeVariable)) { return parameterizedType.getActualTypeArguments()[i]; } } } else if (genericInterface instanceof Class) { return getTypeVariableViaGenericInterface((Class<?>) genericInterface, classDeclaringTypeVariable, typeVariable); } } return null; } }