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