• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LearnLib / automatalib / 13138848026

04 Feb 2025 02:53PM UTC coverage: 92.108% (+2.2%) from 89.877%
13138848026

push

github

mtf90
[maven-release-plugin] prepare release automatalib-0.12.0

16609 of 18032 relevant lines covered (92.11%)

1.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.45
/commons/util/src/main/java/net/automatalib/common/util/ReflectUtil.java
1
/* Copyright (C) 2013-2025 TU Dortmund University
2
 * This file is part of AutomataLib <https://automatalib.net>.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.automatalib.common.util;
17

18
import java.lang.reflect.Constructor;
19
import java.lang.reflect.Method;
20
import java.util.HashMap;
21
import java.util.Map;
22
import java.util.Objects;
23

24
import org.checkerframework.checker.nullness.qual.Nullable;
25

26
/**
27
 * Utility methods for using Java reflection.
28
 */
29
public final class ReflectUtil {
30

31
    private static final Map<Class<?>, Class<?>> CLASS_TO_PRIMITIVE;
32

33
    static {
34
        CLASS_TO_PRIMITIVE = new HashMap<>();
2✔
35
        CLASS_TO_PRIMITIVE.put(Boolean.class, boolean.class);
2✔
36
        CLASS_TO_PRIMITIVE.put(Byte.class, byte.class);
2✔
37
        CLASS_TO_PRIMITIVE.put(Character.class, char.class);
2✔
38
        CLASS_TO_PRIMITIVE.put(Double.class, double.class);
2✔
39
        CLASS_TO_PRIMITIVE.put(Float.class, float.class);
2✔
40
        CLASS_TO_PRIMITIVE.put(Integer.class, int.class);
2✔
41
        CLASS_TO_PRIMITIVE.put(Long.class, long.class);
2✔
42
        CLASS_TO_PRIMITIVE.put(Short.class, short.class);
2✔
43
        CLASS_TO_PRIMITIVE.put(Void.class, void.class);
2✔
44
    }
2✔
45

46
    private ReflectUtil() {}
47

48
    /**
49
     * Tries to find a constructor that is able to accept parameters of the given types. First tries to find the
50
     * constructor matching the exact parameter types. If such a constructor does not exist, tries to find any (the
51
     * first match of arbitrary order) constructor that is able to accept the parameter types by means of auto-boxing,
52
     * i.e. a {@code Constructor(int)} would be returned for the parameters class {@code Integer.class}.
53
     * <p>
54
     * Returns {@code null} if no such constructor could be found.
55
     *
56
     * @param clazz
57
     *         the class which should be scanned for constructors
58
     * @param params
59
     *         the types of the constructor arguments
60
     * @param <T>
61
     *         the class type
62
     *
63
     * @return A constructor that is able of accepting parameters of the specified types, {@code null} if such a
64
     * constructor could not be found.
65
     */
66
    public static <T> @Nullable Constructor<T> findConstructor(Class<T> clazz, Class<?>... params) {
67
        try {
68
            return clazz.getConstructor(params);
2✔
69
        } catch (NoSuchMethodException e) {
2✔
70
            @SuppressWarnings("unchecked")
71
            Constructor<T>[] ctors = (Constructor<T>[]) clazz.getConstructors();
2✔
72

73
            for (Constructor<T> candidate : ctors) {
2✔
74
                if (w2pEquals(candidate.getParameterTypes(), params)) {
2✔
75
                    return candidate;
2✔
76
                }
77
            }
78

79
            return null;
2✔
80
        }
81
    }
82

83
    /**
84
     * Tries to find a method of the given name that is able to accept parameters of the given types. First tries to
85
     * find the method matching the exact parameter types. If such a method does not exist, tries to find any (the first
86
     * match of arbitrary order) method that is able to accept the parameter types by means of auto-boxing, i.e. a
87
     * {@code Method(int)} would be returned for the parameters class {@code Integer.class}.
88
     * <p>
89
     * Returns {@code null} if no such method could be found.
90
     *
91
     * @param clazz
92
     *         the class which should be scanned for methods
93
     * @param name
94
     *         the name of the method
95
     * @param params
96
     *         the types of the method arguments
97
     *
98
     * @return A method that is able to accept parameters of the specified types, {@code null} if such a method could
99
     * not be found.
100
     */
101
    public static @Nullable Method findMethod(Class<?> clazz, String name, Class<?>... params) {
102
        try {
103
            return clazz.getMethod(name, params);
2✔
104
        } catch (NoSuchMethodException e) {
2✔
105
            Method[] methods = clazz.getMethods();
2✔
106

107
            for (Method candidate : methods) {
2✔
108
                if (candidate.getName().equals(name) && w2pEquals(candidate.getParameterTypes(), params)) {
2✔
109
                    return candidate;
2✔
110
                }
111
            }
112

113
            return null;
2✔
114
        }
115
    }
116

117
    /**
118
     * See {@link #findMethod(Class, String, Class...)}. This variation does not require the types of input parameters,
119
     * but can handle the actual objects, which should be passed to the method.
120
     *
121
     * @param clazz
122
     *         the class which should be scanned for methods
123
     * @param name
124
     *         the name of the method
125
     * @param args
126
     *         the objects that should be passed to the method, may contain {@code null}s
127
     *
128
     * @return A method that is able to accept of the specified objects, {@code null} if such a method could not be
129
     * found.
130
     */
131
    public static @Nullable Method findMatchingMethod(Class<?> clazz, String name, @Nullable Object... args) {
132
        for (Method m : clazz.getMethods()) {
2✔
133
            if (!m.getName().equals(name)) {
2✔
134
                continue;
2✔
135
            }
136

137
            if (isMatch(m.getParameterTypes(), args)) {
2✔
138
                return m;
2✔
139
            }
140
        }
141

142
        return null;
2✔
143
    }
144

145
    /**
146
     * See {@link #findMethod(Class, String, Class...)}. This variation allows to additionally narrow the method by
147
     * specifying its return type.
148
     *
149
     * @param clazz
150
     *         the class which should be scanned for methods
151
     * @param name
152
     *         the name of the method
153
     * @param returnType
154
     *         the type of the returned object, if {@code null}, the return type will be ignored
155
     * @param params
156
     *         the types of the method arguments
157
     *
158
     * @return A method that is able to accept of the specified objects, {@code null} if such a method could not be
159
     * found.
160
     */
161
    public static @Nullable Method findMethodRT(Class<?> clazz,
162
                                                String name,
163
                                                @Nullable Class<?> returnType,
164
                                                Class<?>... params) {
165
        Method m = findMethod(clazz, name, params);
2✔
166

167
        if (m == null) {
2✔
168
            return null;
2✔
169
        } else if (returnType == null) {
2✔
170
            return m;
2✔
171
        }
172

173
        Class<?> rt = m.getReturnType();
2✔
174

175
        if (w2pEquals(rt, returnType) || returnType.isAssignableFrom(rt)) {
2✔
176
            return m;
2✔
177
        }
178

179
        return null;
2✔
180
    }
181

182
    private static boolean w2pEquals(Class<?>[] a, Class<?>... b) {
183
        if (a.length != b.length) {
2✔
184
            return false;
2✔
185
        }
186

187
        for (int i = 0; i < a.length; i++) {
2✔
188
            if (!w2pEquals(a[i], b[i])) {
2✔
189
                return false;
×
190
            }
191
        }
192

193
        return true;
2✔
194
    }
195

196
    private static boolean w2pEquals(Class<?> a, Class<?> b) {
197
        final Class<?> primA = CLASS_TO_PRIMITIVE.getOrDefault(a, a);
2✔
198
        final Class<?> primB = CLASS_TO_PRIMITIVE.getOrDefault(b, b);
2✔
199
        return Objects.equals(primA, primB);
2✔
200
    }
201

202
    private static boolean isMatch(Class<?>[] paramTypes, @Nullable Object... args) {
203
        if (paramTypes.length != args.length) {
2✔
204
            return false;
2✔
205
        }
206

207
        for (int i = 0; i < paramTypes.length; i++) {
2✔
208
            Class<?> paramType = paramTypes[i];
2✔
209
            Object arg = args[i];
2✔
210
            if (paramType.isPrimitive()) {
2✔
211
                if (arg == null) {
2✔
212
                    return false;
2✔
213
                }
214
                Class<?> argType = arg.getClass();
2✔
215
                if (paramType != CLASS_TO_PRIMITIVE.get(argType)) {
2✔
216
                    return false;
×
217
                }
218
            } else {
2✔
219
                if (arg != null) {
2✔
220
                    Class<?> argType = arg.getClass();
2✔
221
                    if (!paramType.isAssignableFrom(argType)) {
2✔
222
                        return false;
×
223
                    }
224
                }
225
            }
226
        }
227

228
        return true;
2✔
229
    }
230
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc