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