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}