OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 17 | Rev 156 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.utils;
15
 
16
import java.lang.reflect.Array;
144 ilm 17
import java.lang.reflect.Constructor;
17 ilm 18
import java.lang.reflect.GenericArrayType;
19
import java.lang.reflect.ParameterizedType;
20
import java.lang.reflect.Type;
21
import java.lang.reflect.TypeVariable;
22
import java.util.ArrayList;
23
import java.util.Arrays;
24
import java.util.HashMap;
25
import java.util.LinkedHashMap;
26
import java.util.List;
27
import java.util.Map;
28
 
29
public final class ReflectUtils {
30
 
144 ilm 31
    /**
32
     * Find a constructor with compatible parameters.
33
     *
34
     * @param cls the class to find a constructor for, not <code>null</code>.
35
     * @param parameterTypes types of parameters the constructor must accept.
36
     * @return the first matching constructor, <code>null</code> if no matching constructor found.
37
     */
38
    static public final <T> Constructor<T> getMatchingConstructor(Class<T> cls, Class<?>... parameterTypes) {
39
        try {
40
            return cls.getConstructor(parameterTypes);
41
        } catch (NoSuchMethodException e) {
42
            for (final Constructor<?> c : cls.getConstructors()) {
43
                final Class<?>[] ctorParameters = c.getParameterTypes();
44
                if (ctorParameters.length == parameterTypes.length) {
45
                    boolean ok = true;
46
                    for (int i = 0; ok && i < ctorParameters.length; i++) {
47
                        ok &= ctorParameters[i].isAssignableFrom(parameterTypes[i]);
48
                    }
49
                    if (ok) {
50
                        @SuppressWarnings("unchecked")
51
                        final Constructor<T> res = (Constructor<T>) c;
52
                        assert c.getDeclaringClass() == cls;
53
                        return res;
54
                    }
55
                }
56
            }
57
            return null;
58
        }
59
    }
60
 
17 ilm 61
    static private Map<Type, Type> resolveTypes(Class<?> c, Class<?> raw) {
62
        final Map<Type, Type> res = new HashMap<Type, Type>();
63
        if (!raw.isAssignableFrom(c))
64
            return res;
65
 
66
        // c : ListDeString implements List<String>
67
        final List<Type> types = new ArrayList<Type>(Arrays.asList(c.getGenericInterfaces()));
68
        types.add(c.getGenericSuperclass());
69
        for (final Type t : types) {
70
            if (t instanceof ParameterizedType) {
71
                // eg List<String>
72
                final ParameterizedType pt = (ParameterizedType) t;
73
                if (raw.isAssignableFrom((Class) pt.getRawType())) {
74
                    // eg List.class (List<E>)
75
                    final Class<?> rawType = (Class) pt.getRawType();
76
                    // eg [String.class]
77
                    final Type[] actualTypeArguments = pt.getActualTypeArguments();
78
                    // eg [E]
79
                    final TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
80
                    for (int i = 0; i < actualTypeArguments.length; i++) {
81
                        res.put(typeParameters[i], actualTypeArguments[i]);
82
                    }
83
                }
84
            }
85
            final Class<?> tc = getClass(t);
86
            if (tc != null) {
87
                res.putAll(resolveTypes(tc, raw));
88
            }
89
        }
90
        return res;
91
    }
92
 
93
    /**
94
     * The map of type arguments of baseClass to actual type for childClass.
95
     *
96
     * @param <T> the type of the baseClass.
97
     * @param childClass the class to test, eg Props.class with Props<V> extends Map<String, V>.
98
     * @param baseClass the generic superclass, eg Map.class.
99
     * @return a the map, eg {K => String.class, V => null}.
100
     */
101
    public static <T> Map<TypeVariable<Class<T>>, Class<?>> getTypeArgumentsMap(Class<? extends T> childClass, Class<T> baseClass) {
102
        final TypeVariable<Class<T>>[] actualTypeArguments = baseClass.getTypeParameters();
103
        if (actualTypeArguments.length == 0)
104
            throw new IllegalArgumentException(baseClass + " is not generic");
105
 
106
        final Map<TypeVariable<Class<T>>, Class<?>> res = new LinkedHashMap<TypeVariable<Class<T>>, Class<?>>();
107
        final Map<Type, Type> resolvedTypes = resolveTypes(childClass, baseClass);
108
        // for each actual type argument provided to baseClass, determine (if possible)
109
        // the raw class for that type argument.
110
        // resolve types by chasing down type variables.
111
        for (final TypeVariable<Class<T>> baseType : actualTypeArguments) {
112
            Type currentType = baseType;
113
            while (resolvedTypes.containsKey(currentType)) {
114
                currentType = resolvedTypes.get(currentType);
115
            }
116
            res.put(baseType, getClass(currentType));
117
        }
118
 
119
        return res;
120
    }
121
 
122
    /**
123
     * Search for the list of class used to extend/implement a generic class/interface.
124
     *
125
     * @param <T> the type of the baseClass.
126
     * @param childClass the class to test, eg Props.class with Props<V> extends Map<String, V>.
127
     * @param baseClass the generic superclass, eg Map.class.
128
     * @return the list of actual classes w/o the possible nulls (if childClass is generic), never
129
     *         <code>null</code>, eg [Boolean.class].
130
     */
131
    public static <T> List<Class<?>> getTypeArguments(Class<? extends T> childClass, Class<T> baseClass) {
132
        final ArrayList<Class<?>> res = new ArrayList<Class<?>>();
133
        // ok since getTypeArgumentsMap returns a LinkedHashMap
134
        for (final Class<?> c : getTypeArgumentsMap(childClass, baseClass).values()) {
135
            if (c != null)
136
                res.add(c);
137
        }
138
        return res;
139
    }
140
 
141
    static public <U> List<Class<?>> getTypeArguments(U o, Class<U> raw) {
142
        return getTypeArguments(o.getClass().asSubclass(raw), raw);
143
    }
144
 
145
    /**
146
     * Whether o can be casted to raw&lt;typeArgs&gt;.
147
     *
148
     * @param <U> type of the superclass.
149
     * @param o the instance to check, eg new MapOfInt2Boolean().
150
     * @param raw the generic superclass, eg Map.class.
151
     * @param typeArgs arguments to <code>raw</code>, eg Integer.class, Boolean.class.
144 ilm 152
     * @return whether o is a raw&lt;typeArgs&gt;, eg <code>true</code> : new MapOfInt2Boolean() is
153
     *         a Map&lt;Integer, Boolean&gt;.
17 ilm 154
     */
155
    static public <U> boolean isCastable(U o, Class<U> raw, Class... typeArgs) {
156
        return getTypeArguments(o, raw).equals(Arrays.asList(typeArgs));
157
    }
158
 
159
    // *** pasted from http://www.artima.com/weblogs/viewpost.jsp?thread=208860
160
 
161
    /**
162
     * Get the underlying class for a type, or null if the type is a variable type.
163
     *
164
     * @param type the type
165
     * @return the underlying class
166
     */
167
    private static Class<?> getClass(Type type) {
168
        if (type instanceof Class) {
169
            return (Class) type;
170
        } else if (type instanceof ParameterizedType) {
171
            return getClass(((ParameterizedType) type).getRawType());
172
        } else if (type instanceof GenericArrayType) {
173
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
174
            Class<?> componentClass = getClass(componentType);
175
            if (componentClass != null) {
176
                return Array.newInstance(componentClass, 0).getClass();
177
            } else {
178
                return null;
179
            }
180
        } else {
181
            return null;
182
        }
183
    }
184
 
185
}