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 */
022 package org.granite.util;
023
024 import java.lang.annotation.Annotation;
025 import java.lang.reflect.AnnotatedElement;
026 import java.lang.reflect.Array;
027 import java.lang.reflect.Constructor;
028 import java.lang.reflect.Field;
029 import java.lang.reflect.GenericArrayType;
030 import java.lang.reflect.InvocationTargetException;
031 import java.lang.reflect.Member;
032 import java.lang.reflect.Method;
033 import java.lang.reflect.Modifier;
034 import java.lang.reflect.ParameterizedType;
035 import java.lang.reflect.Type;
036 import java.lang.reflect.TypeVariable;
037 import java.lang.reflect.WildcardType;
038 import java.net.MalformedURLException;
039 import java.net.URL;
040 import java.util.ArrayList;
041 import java.util.Collection;
042 import java.util.Collections;
043 import java.util.List;
044 import java.util.Map;
045 import java.util.Set;
046
047 /**
048 * @author Franck WOLFF
049 */
050 public 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 }