001/* 002 GRANITE DATA SERVICES 003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S. 004 005 This file is part of Granite Data Services. 006 007 Granite Data Services is free software; you can redistribute it and/or modify 008 it under the terms of the GNU Library General Public License as published by 009 the Free Software Foundation; either version 2 of the License, or (at your 010 option) any later version. 011 012 Granite Data Services is distributed in the hope that it will be useful, but 013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License 015 for more details. 016 017 You should have received a copy of the GNU Library General Public License 018 along with this library; if not, see <http://www.gnu.org/licenses/>. 019*/ 020 021package org.granite.util; 022 023import java.lang.annotation.Annotation; 024import java.lang.reflect.AnnotatedElement; 025import java.lang.reflect.Array; 026import java.lang.reflect.Constructor; 027import java.lang.reflect.Field; 028import java.lang.reflect.GenericArrayType; 029import java.lang.reflect.InvocationTargetException; 030import java.lang.reflect.Member; 031import java.lang.reflect.Method; 032import java.lang.reflect.Modifier; 033import java.lang.reflect.ParameterizedType; 034import java.lang.reflect.Type; 035import java.lang.reflect.TypeVariable; 036import java.lang.reflect.WildcardType; 037import java.net.MalformedURLException; 038import java.net.URL; 039import java.util.ArrayList; 040import java.util.Collections; 041import java.util.List; 042import java.util.Map; 043import java.util.Set; 044 045/** 046 * @author Franck WOLFF 047 */ 048public abstract class TypeUtil { 049 050 public static Object newInstance(String type) 051 throws ClassNotFoundException, InstantiationException, IllegalAccessException { 052 return forName(type).newInstance(); 053 } 054 055 public static <T> T newInstance(String type, Class<T> cast) 056 throws ClassNotFoundException, InstantiationException, IllegalAccessException { 057 return forName(type, cast).newInstance(); 058 } 059 060 public static Object newInstance(String type, Class<?>[] argsClass, Object[] argsValues) 061 throws ClassNotFoundException, InstantiationException, IllegalAccessException { 062 return newInstance(forName(type), argsClass, argsValues); 063 } 064 065 @SuppressWarnings("unchecked") 066 public static <T> T newInstance(Class<?> type, Class<T> cast) 067 throws InstantiationException, IllegalAccessException { 068 return (T)type.newInstance(); 069 } 070 071 public static <T> T newInstance(Class<T> type, Class<?>[] argsClass, Object[] argsValues) 072 throws InstantiationException, IllegalAccessException { 073 T instance = null; 074 try { 075 Constructor<T> constructorDef = type.getConstructor(argsClass); 076 instance = constructorDef.newInstance(argsValues); 077 } catch (SecurityException e) { 078 throw new InstantiationException(e.getMessage()); 079 } catch (NoSuchMethodException e) { 080 throw new InstantiationException(e.getMessage()); 081 } catch (IllegalArgumentException e) { 082 throw new InstantiationException(e.getMessage()); 083 } catch (InvocationTargetException e) { 084 throw new InstantiationException(e.getMessage()); 085 } 086 return instance; 087 } 088 089 public static Class<?> forName(String type) throws ClassNotFoundException { 090 try { 091 return TypeUtil.class.getClassLoader().loadClass(type); 092 } 093 catch (ClassNotFoundException e) { 094 return Thread.currentThread().getContextClassLoader().loadClass(type); 095 } 096 } 097 098 @SuppressWarnings("unchecked") 099 public static <T> Class<T> forName(String type, Class<T> cast) throws ClassNotFoundException { 100 try { 101 return (Class<T>)TypeUtil.class.getClassLoader().loadClass(type); 102 } 103 catch (ClassNotFoundException e) { 104 return (Class<T>)Thread.currentThread().getContextClassLoader().loadClass(type); 105 } 106 } 107 108 public static Constructor<?> getConstructor(String type, Class<?>[] paramTypes) 109 throws ClassNotFoundException, NoSuchMethodException { 110 return getConstructor(forName(type), paramTypes); 111 } 112 113 public static <T> Constructor<T> getConstructor(Class<T> type, Class<?>[] paramTypes) 114 throws NoSuchMethodException { 115 return type.getConstructor(paramTypes); 116 } 117 118 public static <T> List<T> emptyList(Class<T> type) { 119 return Collections.emptyList(); 120 } 121 122 public static <T> Set<T> emptySet(Class<T> type) { 123 return Collections.emptySet(); 124 } 125 126 public static <T, U> Map<T, U> emptyMap(Class<T> keyType, Class<U> valueType) { 127 return Collections.emptyMap(); 128 } 129 130 public static boolean isPrimitive(Type type) { 131 return type instanceof Class<?> && ((Class<?>)type).isPrimitive(); 132 } 133 134 public static Class<?> classOfType(Type type) { 135 if (type instanceof Class<?>) 136 return (Class<?>)type; 137 if (type instanceof ParameterizedType) 138 return (Class<?>)((ParameterizedType)type).getRawType(); 139 if (type instanceof WildcardType) { 140 // Forget lower bounds and only deal with first upper bound... 141 Type[] ubs = ((WildcardType)type).getUpperBounds(); 142 if (ubs.length > 0) 143 return classOfType(ubs[0]); 144 } 145 if (type instanceof GenericArrayType) { 146 Class<?> ct = classOfType(((GenericArrayType)type).getGenericComponentType()); 147 return (ct != null ? Array.newInstance(ct, 0).getClass() : Object[].class); 148 } 149 if (type instanceof TypeVariable<?>) { 150 // Only deal with first (upper) bound... 151 Type[] ubs = ((TypeVariable<?>)type).getBounds(); 152 if (ubs.length > 0) 153 return classOfType(ubs[0]); 154 } 155 // Should never append... 156 return Object.class; 157 } 158 159 public static Type getBoundType(TypeVariable<?> typeVariable) { 160 Type[] ubs = typeVariable.getBounds(); 161 if (ubs.length > 0) 162 return ubs[0]; 163 164 // should never happen... 165 if (typeVariable.getGenericDeclaration() instanceof Type) 166 return (Type)typeVariable.getGenericDeclaration(); 167 return typeVariable; 168 } 169 170 public static ParameterizedType[] getDeclaringTypes(Class<?> type) { 171 List<ParameterizedType> supertypes = new ArrayList<ParameterizedType>(); 172 173 Type stype = type.getGenericSuperclass(); 174 Class<?> sclass = type.getSuperclass(); 175 while (sclass != null && sclass != Object.class) { 176 if (stype instanceof ParameterizedType) 177 supertypes.add((ParameterizedType)stype); 178 stype = sclass.getGenericSuperclass(); 179 sclass = sclass.getSuperclass(); 180 } 181 182 collectGenericInterfaces(type.getGenericInterfaces(), supertypes); 183 184 return supertypes.isEmpty() ? null : supertypes.toArray(new ParameterizedType[supertypes.size()]); 185 } 186 187 private static void collectGenericInterfaces(Type[] types, List<ParameterizedType> supertypes) { 188 if (types == null) 189 return; 190 for (Type t : types) { 191 if (t instanceof ParameterizedType) 192 supertypes.add((ParameterizedType)t); 193 else 194 collectGenericInterfaces(((Class<?>)t).getGenericInterfaces(), supertypes); 195 } 196 } 197 198 public static Type resolveTypeVariable(Type genericType, Class<?> declaringClass, ParameterizedType[] declaringTypes) { 199 if (genericType instanceof TypeVariable && declaringTypes != null) { 200 int index = -1; 201 TypeVariable<?> typeVariable = (TypeVariable<?>)genericType; 202 ParameterizedType declaringType = null; 203 for (int j = 0; j < declaringClass.getTypeParameters().length; j++) { 204 Type typeParameter = declaringClass.getTypeParameters()[j]; 205 if (typeParameter == typeVariable) 206 index = j; 207 else if (typeVariable.getBounds() != null) { 208 for (Type t : typeVariable.getBounds()) { 209 if (typeParameter == t) { 210 index = j; 211 break; 212 } 213 } 214 } 215 if (index >= 0) { 216 for (ParameterizedType t : declaringTypes) { 217 if (declaringClass.isAssignableFrom(classOfType(t))) { 218 declaringType = t; 219 break; 220 } 221 } 222 break; 223 } 224 } 225 if (declaringType != null && index >= 0 && index < declaringType.getActualTypeArguments().length) 226 return declaringType.getActualTypeArguments()[index]; 227 } 228 return genericType; 229 } 230 public static String getPackageName(Class<?> clazz) { 231 return clazz.getPackage() != null ? clazz.getPackage().getName() : ""; 232 } 233 234 public static PropertyDescriptor[] getProperties(Class<?> clazz) { 235 try { 236 PropertyDescriptor[] properties = Introspector.getPropertyDescriptors(clazz); 237 Field[] fields = clazz.getDeclaredFields(); 238 for (Field field : fields) { 239 if (Boolean.class.equals(field.getType())) { 240 boolean found = false; 241 for (PropertyDescriptor property : properties) { 242 if (property.getName().equals(field.getName())) { 243 found = true; 244 if (property.getReadMethod() == null) { 245 try { 246 Method readMethod = clazz.getDeclaredMethod(getIsMethodName(field.getName())); 247 if (Modifier.isPublic(readMethod.getModifiers()) && !Modifier.isStatic(readMethod.getModifiers())) 248 property.setReadMethod(readMethod); 249 } 250 catch (NoSuchMethodException e) { 251 } 252 } 253 break; 254 } 255 } 256 if (!found) { 257 try { 258 Method readMethod = clazz.getDeclaredMethod(getIsMethodName(field.getName())); 259 if (Modifier.isPublic(readMethod.getModifiers()) && !Modifier.isStatic(readMethod.getModifiers())) { 260 PropertyDescriptor[] propertiesTmp = new PropertyDescriptor[properties.length + 1]; 261 System.arraycopy(properties, 0, propertiesTmp, 0, properties.length); 262 propertiesTmp[properties.length] = new PropertyDescriptor(field.getName(), readMethod, null); 263 properties = propertiesTmp; 264 } 265 } 266 catch (NoSuchMethodException e) { 267 } 268 } 269 } 270 } 271 return properties; 272 } catch (Exception e) { 273 throw new RuntimeException("Could not introspect properties of class: " + clazz, e); 274 } 275 } 276 277 private static String getIsMethodName(String name) { 278 return "is" + name.substring(0, 1).toUpperCase() + name.substring(1); 279 } 280 281 public static ClassLoader getClassLoader(Class<?> clazz) { 282 return (clazz.getClassLoader() != null ? clazz.getClassLoader() : ClassLoader.getSystemClassLoader()); 283 } 284 285 public static URL findResource(Class<?> clazz) { 286 while (clazz.isArray()) 287 clazz = clazz.getComponentType(); 288 if (clazz.isPrimitive()) 289 return null; 290 URL url = getClassLoader(clazz).getResource(toResourceName(clazz)); 291 String path = url.toString(); 292 if (path.indexOf(' ') != -1) { 293 try { 294 url = new URL(path.replace(" ", "%20")); 295 } catch (MalformedURLException e) { 296 // should never happen... 297 } 298 } 299 return url; 300 } 301 302 public static String toResourceName(Class<?> clazz) { 303 return clazz.getName().replace('.', '/').concat(".class"); 304 } 305 306 public static String getMethodSignature(Method method) { 307 StringBuilder sb = new StringBuilder(); 308 sb.append(method.getName()).append('('); 309 Class<?>[] params = method.getParameterTypes(); 310 for (int i = 0; i < params.length; i++) { 311 if (i > 0) 312 sb.append(','); 313 sb.append(getTypeSignature(params[i])); 314 } 315 sb.append(')'); 316 return sb.toString(); 317 } 318 319 public static String getTypeSignature(Class<?> type) { 320 if (type.isArray()) { 321 try { 322 int dimensions = 1; 323 Class<?> clazz = type.getComponentType(); 324 while (clazz.isArray()) { 325 dimensions++; 326 clazz = clazz.getComponentType(); 327 } 328 329 StringBuffer sb = new StringBuffer(clazz.getName()); 330 while (dimensions-- > 0) 331 sb.append("[]"); 332 return sb.toString(); 333 } catch (Throwable e) { 334 // fallback... 335 } 336 } 337 return type.getName(); 338 } 339 340 public static Method getMethod(Class<?> clazz, String signature) throws NoSuchMethodException { 341 signature = StringUtil.removeSpaces(signature); 342 343 if (!signature.endsWith(")")) 344 signature += "()"; 345 346 for (Method method : clazz.getMethods()) { 347 if (signature.equals(getMethodSignature(method))) 348 return method; 349 } 350 351 throw new NoSuchMethodException("Could not find method: " + signature + " in class: " + clazz); 352 } 353 354 public static boolean isAnnotationPresent(AnnotatedElement elmt, Class<? extends Annotation> annotationClass) { 355 return getAnnotation(elmt, annotationClass) != null; 356 } 357 358 public static <T extends Annotation> DeclaredAnnotation<T> getAnnotation(AnnotatedElement elmt, Class<T> annotationClass) { 359 T annotation = elmt.getAnnotation(annotationClass); 360 361 if (annotation != null) { 362 Class<?> declaringClass = (elmt instanceof Member ? ((Member)elmt).getDeclaringClass() : (Class<?>)elmt); 363 return new DeclaredAnnotation<T>(annotation, elmt, declaringClass); 364 } 365 366 if (elmt instanceof Field) 367 return null; 368 369 if (elmt instanceof Method) { 370 Method m = (Method)elmt; 371 return getMethodAnnotation(m.getDeclaringClass(), m.getName(), m.getParameterTypes(), annotationClass); 372 } 373 374 if (elmt instanceof Constructor) { 375 Constructor<?> c = (Constructor<?>)elmt; 376 return getConstructorAnnotation(c.getDeclaringClass(), annotationClass); 377 } 378 379 if (elmt instanceof Class) { 380 Class<?> c = (Class<?>)elmt; 381 return getClassAnnotation(c.getDeclaringClass(), annotationClass); 382 } 383 384 throw new RuntimeException("Unsupported annotated element: " + elmt); 385 } 386 387 public static <T extends Annotation> DeclaredAnnotation<T> getMethodAnnotation(Class<?> clazz, String name, Class<?>[] parameterTypes, Class<T> annotationClass) { 388 DeclaredAnnotation<T> declaredAnnotation = null; 389 390 try { 391 Method method = clazz.getDeclaredMethod(name, parameterTypes); 392 T annotation = clazz.getDeclaredMethod(name, parameterTypes).getAnnotation(annotationClass); 393 if (annotation != null) 394 declaredAnnotation = new DeclaredAnnotation<T>(annotation, method, clazz); 395 } 396 catch (NoSuchMethodException e) { 397 // fallback... 398 } 399 400 if (declaredAnnotation == null && clazz.getSuperclass() != null) 401 declaredAnnotation = getMethodAnnotation(clazz.getSuperclass(), name, parameterTypes, annotationClass); 402 403 if (declaredAnnotation == null) { 404 for (Class<?> interfaze : clazz.getInterfaces()) { 405 declaredAnnotation = getMethodAnnotation(interfaze, name, parameterTypes, annotationClass); 406 if (declaredAnnotation != null) 407 break; 408 } 409 } 410 411 return declaredAnnotation; 412 } 413 414 public static <T extends Annotation> DeclaredAnnotation<T> getConstructorAnnotation(Class<?> clazz, Class<T> annotationClass) { 415 DeclaredAnnotation<T> declaredAnnotation = null; 416 417 for (Constructor<?> constructor : clazz.getDeclaredConstructors()) { 418 T annotation = constructor.getAnnotation(annotationClass); 419 if (annotation != null) { 420 declaredAnnotation = new DeclaredAnnotation<T>(annotation, constructor, clazz); 421 break; 422 } 423 } 424 425 if (declaredAnnotation == null && clazz.getSuperclass() != null) 426 declaredAnnotation = getConstructorAnnotation(clazz.getSuperclass(), annotationClass); 427 428 return declaredAnnotation; 429 } 430 431 public static <T extends Annotation> DeclaredAnnotation<T> getClassAnnotation(Class<?> clazz, Class<T> annotationClass) { 432 DeclaredAnnotation<T> declaredAnnotation = null; 433 434 T annotation = clazz.getAnnotation(annotationClass); 435 if (annotation != null) 436 declaredAnnotation = new DeclaredAnnotation<T>(annotation, clazz, clazz); 437 else { 438 if (clazz.getSuperclass() != null) 439 declaredAnnotation = getClassAnnotation(clazz.getSuperclass(), annotationClass); 440 441 if (declaredAnnotation == null) { 442 for (Class<?> interfaze : clazz.getInterfaces()) { 443 declaredAnnotation = getClassAnnotation(interfaze, annotationClass); 444 if (declaredAnnotation != null) 445 break; 446 } 447 } 448 } 449 450 return declaredAnnotation; 451 } 452 453 public static class DeclaredAnnotation<T extends Annotation> { 454 455 public final T annotation; 456 public final AnnotatedElement annotatedElement; 457 public final Class<?> declaringClass; 458 459 public DeclaredAnnotation(T annotation, AnnotatedElement annotatedElement, Class<?> declaringClass) { 460 this.annotation = annotation; 461 this.annotatedElement = annotatedElement; 462 this.declaringClass = declaringClass; 463 } 464 465 @Override 466 public String toString() { 467 return getClass().getName() + "{annotation=" + annotation + ", annotatedElement=" + annotatedElement + ", declaringClass=" + declaringClass + "}"; 468 } 469 } 470}