001/** 002 * Copyright (c) 2007-2008, Regents of the University of Colorado 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without 006 * modification, are permitted provided that the following conditions are met: 007 * 008 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 009 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 010 * Neither the name of the University of Colorado at Boulder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 011 * 012 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 013 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 014 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 015 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 016 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 017 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 018 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 019 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 020 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 021 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 022 * POSSIBILITY OF SUCH DAMAGE. 023 */ 024package org.cleartk.util; 025 026import java.lang.reflect.InvocationTargetException; 027import java.lang.reflect.ParameterizedType; 028import java.lang.reflect.Type; 029import java.lang.reflect.TypeVariable; 030import java.util.Map; 031import java.util.TreeMap; 032 033import org.apache.uima.resource.ResourceInitializationException; 034 035/** 036 * <br> 037 * Copyright (c) 2007-2008, Regents of the University of Colorado <br> 038 * All rights reserved. 039 */ 040public class ReflectionUtil { 041 042 /** 043 * Perform an unchecked cast based on a type parameter. 044 * 045 * @param <T> 046 * The type to which the object should be cast. 047 * @param o 048 * The object. 049 * @return The object, cast to the given type. 050 */ 051 @SuppressWarnings("unchecked") 052 public static <T> T uncheckedCast(Object o) { 053 return (T) o; 054 } 055 056 public static interface TypeArgumentDelegator { 057 public Map<String, Type> getTypeArguments(Class<?> genericType); 058 } 059 060 public static <T> Type getTypeArgument(Class<T> genericType, String typeParameterName, T obj) { 061 Map<String, Type> typeArguments = getTypeArguments(genericType, obj); 062 return typeArguments == null ? null : typeArguments.get(typeParameterName); 063 } 064 065 /** 066 * Try to find the instantiation of all of genericTypes type parameters in objs class. 067 * 068 * @param genericType 069 * the generic supertype of objs class 070 * @param obj 071 * an instantiation of a subclass of genericType. All of genericTypes type parameters 072 * must have been instantiated in the inheritance hierarchy. 073 * @return a map of genericTypes type parameters (their name in the source code) to the type they 074 * are instantiated as in obj 075 */ 076 public static Map<String, Type> getTypeArguments(Class<?> genericType, Object obj) { 077 if (obj instanceof TypeArgumentDelegator) { 078 return ((TypeArgumentDelegator) obj).getTypeArguments(genericType); 079 } 080 Map<String, Type> typeMap = new TreeMap<String, Type>(); 081 return getTypeArguments(genericType, obj.getClass(), typeMap); 082 } 083 084 public static boolean isAssignableFrom(Type type1, Type type2) { 085 if (type1 instanceof Class<?> && type2 instanceof Class<?>) { 086 return ((Class<?>) type1).isAssignableFrom((Class<?>) type2); 087 } else { 088 return type1.equals(type2); 089 } 090 } 091 092 private static Map<String, Type> getTypeArguments( 093 Class<?> genericType, 094 Type type, 095 Map<String, Type> typeMap) { 096 if (type instanceof ParameterizedType) { 097 return getTypeArguments(genericType, (ParameterizedType) type, typeMap); 098 } else if (type instanceof Class<?>) { 099 return getTypeArguments(genericType, (Class<?>) type, typeMap); 100 } else { 101 throw new IllegalArgumentException("type must be a ParameterizedType or Class"); 102 } 103 } 104 105 private static Map<String, Type> getTypeArguments( 106 Class<?> genericType, 107 Class<?> classType, 108 Map<String, Type> typeMap) { 109 if (genericType.isInterface()) { 110 for (Type interfaceType : classType.getGenericInterfaces()) { 111 Map<String, Type> result = getTypeArguments(genericType, interfaceType, typeMap); 112 if (result != null) 113 return result; 114 } 115 } 116 117 Type superType = classType.getGenericSuperclass(); 118 if (superType != null) { 119 return getTypeArguments(genericType, superType, typeMap); 120 } 121 122 return null; 123 } 124 125 private static Map<String, Type> getTypeArguments( 126 Class<?> genericType, 127 ParameterizedType paramType, 128 Map<String, Type> typeMap) { 129 Class<?> rawType = (Class<?>) paramType.getRawType(); 130 if (rawType == genericType) { 131 // found it! 132 TypeVariable<?> typeVars[] = rawType.getTypeParameters(); 133 Type actualTypes[] = paramType.getActualTypeArguments(); 134 Map<String, Type> result = new TreeMap<String, Type>(); 135 for (int i = 0; i < actualTypes.length; i++) { 136 while (actualTypes[i] != null && actualTypes[i] instanceof TypeVariable<?>) { 137 String key = typevarString((TypeVariable<?>) actualTypes[i]); 138 if (typeMap.containsKey(key)) 139 actualTypes[i] = typeMap.get(key); 140 else 141 actualTypes[i] = null; 142 } 143 result.put(typeVars[i].getName(), actualTypes[i]); 144 } 145 return result; 146 } else { 147 TypeVariable<?> typeVars[] = rawType.getTypeParameters(); 148 Type actualTypes[] = paramType.getActualTypeArguments(); 149 for (int i = 0; i < typeVars.length; i++) 150 typeMap.put(typevarString(typeVars[i]), actualTypes[i]); 151 return getTypeArguments(genericType, paramType.getRawType(), typeMap); 152 } 153 } 154 155 private static String typevarString(TypeVariable<?> tv) { 156 return tv.getGenericDeclaration().toString() + " " + tv.getName(); 157 } 158 159 /** 160 * Checks that the given type parameters of the given objects are compatible. 161 * 162 * Type parameters are identified by providing the class in which the type parameter is defined, 163 * and the declared name of the type parameter. 164 * 165 * Throws a ResourceInitializationException if the type parameters are not compatible. 166 * 167 * @param <T> 168 * Type of the class declaring the first type parameter 169 * @param <U> 170 * Type of the class declaring the second type parameter 171 * @param paramDefiningClass1 172 * The class declaring the first type parameter 173 * @param paramName1 174 * The declared name of the first type parameter 175 * @param object1 176 * The target object 177 * @param paramDefiningClass2 178 * The class declaring the second type parameter 179 * @param paramName2 180 * The declared name of the second type parameter 181 * @param object2 182 * The source object 183 */ 184 public static <T, U> void checkTypeParameterIsAssignable( 185 Class<T> paramDefiningClass1, 186 String paramName1, 187 T object1, 188 Class<U> paramDefiningClass2, 189 String paramName2, 190 U object2) throws ResourceInitializationException { 191 192 // get the type arguments from the objects 193 java.lang.reflect.Type type1 = ReflectionUtil.getTypeArgument( 194 paramDefiningClass1, 195 paramName1, 196 object1); 197 java.lang.reflect.Type type2 = ReflectionUtil.getTypeArgument( 198 paramDefiningClass2, 199 paramName2, 200 object2); 201 202 // both arguments missing is compatible 203 if (type1 == null && type2 == null) { 204 return; 205 } 206 207 // if the second type is not assignable to the first, raise an exception 208 if (type1 == null || type2 == null || !ReflectionUtil.isAssignableFrom(type1, type2)) { 209 throw CleartkInitializationException.incompatibleTypeParameters( 210 object1, 211 paramName1, 212 type1, 213 object2, 214 paramName2, 215 type2); 216 } 217 } 218 219 /** 220 * Checks that the given type parameters of the given objects are exactly equal. 221 * 222 * Type parameters are identified by providing the class in which the type parameter is defined, 223 * and the declared name of the type parameter. 224 * 225 * Throws an instance of the given exception class if type parameters are not exactly equal. 226 * 227 * @param <T> 228 * Type of the class declaring the first type parameter 229 * @param <U> 230 * Type of the class declaring the second type parameter 231 * @param paramDefiningClass1 232 * The class declaring the first type parameter 233 * @param paramName1 234 * The declared name of the first type parameter 235 * @param object1 236 * The target object 237 * @param paramDefiningClass2 238 * The class declaring the second type parameter 239 * @param paramName2 240 * The declared name of the second type parameter 241 * @param object2 242 * The source object 243 */ 244 public static <T, U, E extends Exception> void checkTypeParametersAreEqual( 245 Class<T> paramDefiningClass1, 246 String paramName1, 247 T object1, 248 Class<U> paramDefiningClass2, 249 String paramName2, 250 U object2, 251 Class<E> exceptionClass) throws E { 252 253 // get the type arguments from the objects 254 java.lang.reflect.Type type1 = ReflectionUtil.getTypeArgument( 255 paramDefiningClass1, 256 paramName1, 257 object1); 258 java.lang.reflect.Type type2 = ReflectionUtil.getTypeArgument( 259 paramDefiningClass2, 260 paramName2, 261 object2); 262 263 // both arguments missing is equal 264 if (type1 == null && type2 == null) { 265 return; 266 } 267 268 // if the second type is not equal to the first, raise an exception 269 if (type1 == null || type2 == null || !type1.equals(type2)) { 270 try { 271 throw exceptionClass.getConstructor(String.class).newInstance( 272 String.format( 273 "%s with %s %s is not equal to %s with %s %s", 274 object1.getClass().getSimpleName(), 275 paramName1, 276 type1, 277 object2.getClass().getSimpleName(), 278 paramName2, 279 type2)); 280 } catch (InstantiationException e) { 281 throw new RuntimeException(e); 282 } catch (IllegalAccessException e) { 283 throw new RuntimeException(e); 284 } catch (InvocationTargetException e) { 285 throw new RuntimeException(e); 286 } catch (NoSuchMethodException e) { 287 throw new RuntimeException(e); 288 } 289 } 290 } 291 292}