OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 144 | 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
 
156 ilm 16
import java.beans.Expression;
17 ilm 17
import java.lang.reflect.Array;
144 ilm 18
import java.lang.reflect.Constructor;
17 ilm 19
import java.lang.reflect.GenericArrayType;
20
import java.lang.reflect.ParameterizedType;
21
import java.lang.reflect.Type;
22
import java.lang.reflect.TypeVariable;
23
import java.util.ArrayList;
24
import java.util.Arrays;
25
import java.util.HashMap;
26
import java.util.LinkedHashMap;
27
import java.util.List;
28
import java.util.Map;
29
 
30
public final class ReflectUtils {
31
 
144 ilm 32
    /**
156 ilm 33
     * Find a constructor with compatible parameters. NOTE: This follow a simpler algorithm than the
34
     * one in the JLS (<a href=
35
     * "https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.12.2.5">15.12.2.5.
36
     * Choosing the Most Specific Method</a>).
144 ilm 37
     *
38
     * @param cls the class to find a constructor for, not <code>null</code>.
39
     * @param parameterTypes types of parameters the constructor must accept.
40
     * @return the first matching constructor, <code>null</code> if no matching constructor found.
41
     */
42
    static public final <T> Constructor<T> getMatchingConstructor(Class<T> cls, Class<?>... parameterTypes) {
43
        try {
44
            return cls.getConstructor(parameterTypes);
45
        } catch (NoSuchMethodException e) {
46
            for (final Constructor<?> c : cls.getConstructors()) {
47
                final Class<?>[] ctorParameters = c.getParameterTypes();
48
                if (ctorParameters.length == parameterTypes.length) {
49
                    boolean ok = true;
50
                    for (int i = 0; ok && i < ctorParameters.length; i++) {
51
                        ok &= ctorParameters[i].isAssignableFrom(parameterTypes[i]);
52
                    }
53
                    if (ok) {
54
                        @SuppressWarnings("unchecked")
55
                        final Constructor<T> res = (Constructor<T>) c;
56
                        assert c.getDeclaringClass() == cls;
57
                        return res;
58
                    }
59
                }
60
            }
61
            return null;
62
        }
63
    }
64
 
156 ilm 65
    @SuppressWarnings("unchecked")
66
    static public final <T> T createInstance(final Class<T> clazz, Object... args) throws Exception {
67
        // too difficult to order superclasses and all implemented interfaces of args in order to
68
        // find the most specific constructor
69
        return (T) new Expression(clazz, "new", args).getValue();
70
    }
71
 
72
    /**
73
     * Return the class with the passed name if it is a subclass of <code>clazz</code>.
74
     *
75
     * @param name the name of the class, not <code>null</code>.
76
     * @param clazz the superclass, not <code>null</code>.
77
     * @return the requested class, <code>null</code> if {@link ClassNotFoundException not found} or
78
     *         not a {@link Class#asSubclass(Class) subclass} of <code>clazz</code>.
79
     */
80
    static public final <T> Class<? extends T> getSubclass(final String name, final Class<T> clazz) {
81
        return getSubclass(name, clazz, clazz.getClassLoader());
82
    }
83
 
84
    static public final <T> Class<? extends T> getSubclass(final String name, final Class<T> clazz, final ClassLoader cl) {
85
        final Class<?> res;
86
        try {
87
            res = Class.forName(name, true, cl);
88
        } catch (ClassNotFoundException e) {
89
            return null;
90
        }
91
        return clazz.isAssignableFrom(res) ? res.asSubclass(clazz) : null;
92
    }
93
 
17 ilm 94
    static private Map<Type, Type> resolveTypes(Class<?> c, Class<?> raw) {
95
        final Map<Type, Type> res = new HashMap<Type, Type>();
96
        if (!raw.isAssignableFrom(c))
97
            return res;
98
 
99
        // c : ListDeString implements List<String>
100
        final List<Type> types = new ArrayList<Type>(Arrays.asList(c.getGenericInterfaces()));
101
        types.add(c.getGenericSuperclass());
102
        for (final Type t : types) {
103
            if (t instanceof ParameterizedType) {
104
                // eg List<String>
105
                final ParameterizedType pt = (ParameterizedType) t;
106
                if (raw.isAssignableFrom((Class) pt.getRawType())) {
107
                    // eg List.class (List<E>)
108
                    final Class<?> rawType = (Class) pt.getRawType();
109
                    // eg [String.class]
110
                    final Type[] actualTypeArguments = pt.getActualTypeArguments();
111
                    // eg [E]
112
                    final TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
113
                    for (int i = 0; i < actualTypeArguments.length; i++) {
114
                        res.put(typeParameters[i], actualTypeArguments[i]);
115
                    }
116
                }
117
            }
118
            final Class<?> tc = getClass(t);
119
            if (tc != null) {
120
                res.putAll(resolveTypes(tc, raw));
121
            }
122
        }
123
        return res;
124
    }
125
 
126
    /**
127
     * The map of type arguments of baseClass to actual type for childClass.
128
     *
129
     * @param <T> the type of the baseClass.
130
     * @param childClass the class to test, eg Props.class with Props<V> extends Map<String, V>.
131
     * @param baseClass the generic superclass, eg Map.class.
132
     * @return a the map, eg {K => String.class, V => null}.
133
     */
134
    public static <T> Map<TypeVariable<Class<T>>, Class<?>> getTypeArgumentsMap(Class<? extends T> childClass, Class<T> baseClass) {
135
        final TypeVariable<Class<T>>[] actualTypeArguments = baseClass.getTypeParameters();
136
        if (actualTypeArguments.length == 0)
137
            throw new IllegalArgumentException(baseClass + " is not generic");
138
 
139
        final Map<TypeVariable<Class<T>>, Class<?>> res = new LinkedHashMap<TypeVariable<Class<T>>, Class<?>>();
140
        final Map<Type, Type> resolvedTypes = resolveTypes(childClass, baseClass);
141
        // for each actual type argument provided to baseClass, determine (if possible)
142
        // the raw class for that type argument.
143
        // resolve types by chasing down type variables.
144
        for (final TypeVariable<Class<T>> baseType : actualTypeArguments) {
145
            Type currentType = baseType;
146
            while (resolvedTypes.containsKey(currentType)) {
147
                currentType = resolvedTypes.get(currentType);
148
            }
149
            res.put(baseType, getClass(currentType));
150
        }
151
 
152
        return res;
153
    }
154
 
155
    /**
156
     * Search for the list of class used to extend/implement a generic class/interface.
157
     *
158
     * @param <T> the type of the baseClass.
159
     * @param childClass the class to test, eg Props.class with Props<V> extends Map<String, V>.
160
     * @param baseClass the generic superclass, eg Map.class.
161
     * @return the list of actual classes w/o the possible nulls (if childClass is generic), never
162
     *         <code>null</code>, eg [Boolean.class].
163
     */
164
    public static <T> List<Class<?>> getTypeArguments(Class<? extends T> childClass, Class<T> baseClass) {
165
        final ArrayList<Class<?>> res = new ArrayList<Class<?>>();
166
        // ok since getTypeArgumentsMap returns a LinkedHashMap
167
        for (final Class<?> c : getTypeArgumentsMap(childClass, baseClass).values()) {
168
            if (c != null)
169
                res.add(c);
170
        }
171
        return res;
172
    }
173
 
174
    static public <U> List<Class<?>> getTypeArguments(U o, Class<U> raw) {
175
        return getTypeArguments(o.getClass().asSubclass(raw), raw);
176
    }
177
 
178
    /**
179
     * Whether o can be casted to raw&lt;typeArgs&gt;.
180
     *
181
     * @param <U> type of the superclass.
182
     * @param o the instance to check, eg new MapOfInt2Boolean().
183
     * @param raw the generic superclass, eg Map.class.
184
     * @param typeArgs arguments to <code>raw</code>, eg Integer.class, Boolean.class.
144 ilm 185
     * @return whether o is a raw&lt;typeArgs&gt;, eg <code>true</code> : new MapOfInt2Boolean() is
186
     *         a Map&lt;Integer, Boolean&gt;.
17 ilm 187
     */
188
    static public <U> boolean isCastable(U o, Class<U> raw, Class... typeArgs) {
189
        return getTypeArguments(o, raw).equals(Arrays.asList(typeArgs));
190
    }
191
 
192
    // *** pasted from http://www.artima.com/weblogs/viewpost.jsp?thread=208860
193
 
194
    /**
195
     * Get the underlying class for a type, or null if the type is a variable type.
196
     *
197
     * @param type the type
198
     * @return the underlying class
199
     */
200
    private static Class<?> getClass(Type type) {
201
        if (type instanceof Class) {
202
            return (Class) type;
203
        } else if (type instanceof ParameterizedType) {
204
            return getClass(((ParameterizedType) type).getRawType());
205
        } else if (type instanceof GenericArrayType) {
206
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
207
            Class<?> componentClass = getClass(componentType);
208
            if (componentClass != null) {
209
                return Array.newInstance(componentClass, 0).getClass();
210
            } else {
211
                return null;
212
            }
213
        } else {
214
            return null;
215
        }
216
    }
217
 
218
}