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    }