/******************************************************************************* * Copyright (c) 2006-2010 eBay Inc. All Rights Reserved. * 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 *******************************************************************************/ package org.ebayopensource.turmeric.tools.codegen.util; 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.WildcardType; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.ebayopensource.turmeric.tools.codegen.JTypeTable; import org.ebayopensource.turmeric.tools.codegen.exception.PreProcessFailedException; /** * Provides reflection utility methods for code generation tools. * * * @author rmandapati */ public class IntrospectUtil { private static Set<Method> s_objectClassMethods = new HashSet<Method>(); static { Method[] allMethods = Object.class.getMethods(); for (int i = 0; i < allMethods.length; i++) { s_objectClassMethods.add(allMethods[i]); } } private static Set<String> s_javaPackages = new HashSet<String>(); static { s_javaPackages.add("java."); //s_javaPackages.add("javax."); //s_javaPackages.add("sun."); } private static Set<Class<?>> s_collectionTypes = new HashSet<Class<?>>(); static { s_collectionTypes.add(java.util.Collection.class); s_collectionTypes.add(java.util.Iterator.class); s_collectionTypes.add(java.util.Map.class); } private static Map<String, Class<?>> s_primitiveToWrapperMap = new HashMap<String, Class<?>>(); static { //s_primitiveToWrapperMap.put("void", Void.class); s_primitiveToWrapperMap.put("boolean", Boolean.class); s_primitiveToWrapperMap.put("byte", Byte.class); s_primitiveToWrapperMap.put("short", Short.class); s_primitiveToWrapperMap.put("char", Character.class); s_primitiveToWrapperMap.put("int", Integer.class); s_primitiveToWrapperMap.put("float", Float.class); s_primitiveToWrapperMap.put("long", Long.class); s_primitiveToWrapperMap.put("double", Double.class); } public static Class<?> getWrapperType(String primitiveType) { Class<?> wrapperType = s_primitiveToWrapperMap.get(primitiveType); if (wrapperType != null) { return wrapperType; } throw new IllegalArgumentException("Unknown primitive type : " + primitiveType); } public static JTypeTable initializeJType(String fullyQualifiedClassName) throws PreProcessFailedException { Class<?> clazz = null; try { clazz = loadClass(fullyQualifiedClassName); } catch (ClassNotFoundException ex) { throw new PreProcessFailedException("Failed to load class : " + fullyQualifiedClassName, ex); } JTypeTable jTypeTable = new JTypeTable(clazz); List<Method> methodList = getMethods(clazz); jTypeTable.setMethods(methodList); Set<Class<?>> typesReferred = getTypesReferred(methodList); jTypeTable.setTypesReferred(typesReferred); return jTypeTable; } public static JTypeTable initializeAsyncInterfaceJType(String fullyQualifiedClassName) throws PreProcessFailedException { Class<?> clazz = null; try { ClassLoader cl = Thread.currentThread().getContextClassLoader(); clazz = Class.forName(fullyQualifiedClassName, true, cl); } catch (ClassNotFoundException ex) { throw new PreProcessFailedException("Failed to load class : " + fullyQualifiedClassName, ex); } JTypeTable jTypeTable = new JTypeTable(clazz); List<Method> methodList = getMethods(clazz); jTypeTable.setMethods(methodList); return jTypeTable; } public static void addAllTypesReferred(Class<?> type, Set<String> typeNameSet) { // If Type is already processed or being processed // then don't process it again, which might cause // infinite loop // ex: A class referring itself (Linked list node class) if (type == null || typeNameSet.contains(type.getName())) { return; } else if (type == Void.TYPE || type.isPrimitive()) { return; } else if (type.getName().startsWith("java")) { return; } else if (type.isArray()) { addAllTypesReferred(type.getComponentType(), typeNameSet); return; } typeNameSet.add(type.getName()); Method[] allMethods = type.getMethods(); for (Method method : allMethods) { Type genReturnType = method.getGenericReturnType(); Type[] genParamTypes = method.getGenericParameterTypes(); Type[] methodInOutTypes = new Type[genParamTypes.length + 1]; methodInOutTypes[0] = genReturnType; System.arraycopy(genParamTypes, 0, methodInOutTypes, 1, genParamTypes.length); for (Type inOutType : methodInOutTypes) { if (!isParameterizedType(inOutType) && (inOutType instanceof Class)) { addAllTypesReferred((Class<?>) inOutType, typeNameSet); } else { List<Class<?>> actualTypes = new ArrayList<Class<?>>(2); actualTypes = getAllActualTypes(inOutType, actualTypes); for (Class<?> typeClass : actualTypes) { addAllTypesReferred(typeClass, typeNameSet); } } } } Field[] publicFields = type.getFields(); for (Field field : publicFields) { Type genType = field.getGenericType(); if (!isParameterizedType(genType)) { addAllTypesReferred((Class<?>) genType, typeNameSet); } else { List<Class<?>> allTypes = new ArrayList<Class<?>>(2); allTypes = getAllActualTypes(field.getGenericType(), allTypes); for (Class<?> typeClass : allTypes) { addAllTypesReferred(typeClass, typeNameSet); } } } } public static boolean hasAttachmentTypeRef(Class<?> type, Set<String> typeNameSet) { // If Type is already processed or being processed // then don't process it again, which might cause // infinite loop // ex: A class referring itself (Linked list node class) if (type == null || typeNameSet.contains(type.getName())) { return false; } typeNameSet.add(type.getName()); if (type == Void.TYPE || type.isPrimitive()) { return false; } else if (type.isArray()) { return hasAttachmentTypeRef(type.getComponentType(), typeNameSet); } else if (javax.activation.DataHandler.class.isAssignableFrom(type)) { return true; } else if (type.getPackage() != null && type.getPackage().getName().startsWith("java")) { return false; } else { boolean hasAttachmentType = false; Method[] allMethods = type.getMethods(); for (Method method : allMethods) { Type genReturnType = method.getGenericReturnType(); Type[] genParamTypes = method.getGenericParameterTypes(); Type[] methodInOutTypes = new Type[genParamTypes.length + 1]; methodInOutTypes[0] = genReturnType; System.arraycopy(genParamTypes, 0, methodInOutTypes, 1, genParamTypes.length); for (Type inOutType : methodInOutTypes) { if (!isParameterizedType(inOutType) && (inOutType instanceof Class)) { hasAttachmentType = hasAttachmentTypeRef((Class<?>) inOutType, typeNameSet); if (hasAttachmentType) { return true; } } else { List<Class<?>> actualTypes = new ArrayList<Class<?>>(2); actualTypes = getAllActualTypes(inOutType, actualTypes); for (Class<?> typeClass : actualTypes) { hasAttachmentType = hasAttachmentTypeRef(typeClass, typeNameSet); if (hasAttachmentType) { return true; } } } } } // None of the method's Param / Return type is of Attachment type // So, check whether any PUBLIC field is an Attachment type Field[] publicFields = type.getFields(); for (Field field : publicFields) { Type genType = field.getGenericType(); if (!isParameterizedType(genType)) { hasAttachmentType = hasAttachmentTypeRef((Class<?>) genType, typeNameSet); if (hasAttachmentType) { return true; } } else { List<Class<?>> allTypes = new ArrayList<Class<?>>(2); allTypes = getAllActualTypes(field.getGenericType(), allTypes); for (Class<?> typeClass : allTypes) { hasAttachmentType = hasAttachmentTypeRef(typeClass, typeNameSet); if (hasAttachmentType) { return true; } } } } return false; } } private static List<Class<?>> getAllActualTypes(Type type, List<Class<?>> allTypes) { if (isParameterizedType(type)) { Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments(); for (Type actualType : actualTypes) { getAllActualTypes(actualType, allTypes); } } else if (type instanceof Class) { allTypes.add((Class<?>) type); } return allTypes; } public static boolean isParameterizedType(Type type) { return (type instanceof ParameterizedType); } public static boolean isWildCardType(Type type) { return (type instanceof WildcardType); } public static boolean isGenericArrayType(Type type) { return (type instanceof GenericArrayType); } public static Method getMethodWithSignature( Class<?> clazz, String methodName, Class<?>[] params) { Method resultMethod = null; Method[] allMethods = clazz.getDeclaredMethods(); if (params == null) { params = new Class[0]; } for (Method method : allMethods) { if (method.getName().equals(methodName)) { Class<?> methodParams[] = method.getParameterTypes(); if (methodParams.length != params.length) { continue; } int paramIndex = 0; for (Class<?> paramType : methodParams) { if (paramType != params[paramIndex]) { break; } paramIndex++; } if (methodParams.length == paramIndex) { resultMethod = method; } } } return resultMethod; } public static boolean hasCollectionType(Class<?>[] types) { if (types == null || types.length == 0) { return false; } boolean hasCollectionType = false; for (Class<?> typeClazz : types) { if (isCollectionType(typeClazz)) { hasCollectionType = true; break; } } return hasCollectionType; } public static boolean isCollectionType(Class<?> typeClazz) { if (typeClazz == null || typeClazz.isPrimitive()) { return false; } boolean isCollectionType = false; for (Class<?> collectionClazz : s_collectionTypes) { if (collectionClazz.isAssignableFrom(typeClazz)) { isCollectionType = true; break; } } return isCollectionType; } public static Class<?> loadClass(String fullyQualifiedClassName) throws ClassNotFoundException { try { return Class.forName(fullyQualifiedClassName); } catch( final ClassNotFoundException ignore ) { return ContextClassLoaderUtil.loadClass(fullyQualifiedClassName); } } public static Method[] getPublicInstanceMethods(Class<?> clazz) { List<Method> publicMethods = new ArrayList<Method>(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (isPublicInstanceMethod(method)) { publicMethods.add(method); } } return publicMethods.toArray(new Method[0]); } public static Method[] getMatchedMethods(Class<?> clazz, Set<String> methodNameSet) { Method[] methods = clazz.getDeclaredMethods(); List<Method> matchedMethods = new ArrayList<Method>(); for (Method method : methods) { if (methodNameSet.contains(method.getName())) { matchedMethods.add(method); } } return matchedMethods.toArray(new Method[0]); } public static boolean isPublicInstanceMethod(Method method) { int modifiers = method.getModifiers(); return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers); } private static boolean isFilteredMethod(Method method, Set<Method> methodFilterSet) { return (methodFilterSet.contains(method)); } private static boolean isFilteredPackage(Class<?> clazz, Set<String> pkgFilterSet) { boolean isFilteredPackage = false; String pkgName = clazz.getPackage().getName(); for (String filteredPkg : pkgFilterSet) { if (pkgName.startsWith(filteredPkg)) { isFilteredPackage = true; break; } } return isFilteredPackage; } public static List<Method> getMethods(Class<?> clazz) { return getMethods(clazz, s_objectClassMethods); } private static List<Method> getMethods(Class<?> clazz, Set<Method> methodFilterSet) { Method[] allMethods = clazz.getMethods(); int methodCount = allMethods.length; List<Method> methodList = new ArrayList<Method>(); for (int i = 0; i < methodCount; i++) { if (!isFilteredMethod(allMethods[i], methodFilterSet)) { methodList.add(allMethods[i]); } } return methodList; } private static Set<Class<?>> getTypesReferred(List<Method> methodList) { return getTypesReferred(methodList, s_javaPackages); } private static Set<Class<?>> getTypesReferred( List<Method> methodList, Set<String> pkgFilterSet) { Set<Class<?>> typesReferred = new HashSet<Class<?>>(); for (Method method : methodList) { Type[] allTypes = typesReferred(method); addTypesReferred(allTypes, typesReferred, pkgFilterSet); } return typesReferred; } private static Type[] typesReferred(Method method) { Type[] paramTypes = method.getGenericParameterTypes(); Type returnType = method.getGenericReturnType(); Type[] allTypes = new Type[paramTypes.length+1]; System.arraycopy(paramTypes, 0, allTypes, 0, paramTypes.length); allTypes[allTypes.length-1] = returnType; return allTypes; } private static void addTypesReferred( Type[] types, Set<Class<?>> typesReferred, Set<String> pkgFilterSet) { for (Type typeClazz : types) { if (isParameterizedType(typeClazz)) { ParameterizedType pType = (ParameterizedType) typeClazz; addTypesReferred( pType.getActualTypeArguments(), typesReferred, pkgFilterSet); } else if(isWildCardType(typeClazz)){ /* WildcardType wType = (WildcardType)typeClazz; wType.getClass() addTypesReferred( wType.getLowerBounds(), typesReferred, pkgFilterSet); addTypesReferred( wType.getUpperBounds(), typesReferred, pkgFilterSet); */ } else { Class<?> clazz = (Class<?>) typeClazz; if (clazz.isArray()) { clazz = clazz.getComponentType(); } if (!clazz.isPrimitive() && !isFilteredPackage(clazz, pkgFilterSet)) { typesReferred.add(clazz); } } } } }