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

burningwave / reflection / #68

18 Oct 2023 11:19AM UTC coverage: 74.528% (+5.4%) from 69.107%
#68

push

Roberto-Gentili
Bug fix

790 of 1060 relevant lines covered (74.53%)

0.75 hits per line

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

67.65
/src/main/java/org/burningwave/reflection/Methods.java
1
/*
2
 * This file is part of Burningwave Reflection.
3
 *
4
 * Author: Roberto Gentili
5
 *
6
 * Hosted at: https://github.com/burningwave/reflection
7
 *
8
 * --
9
 *
10
 * The MIT License (MIT)
11
 *
12
 * Copyright (c) 2022 Roberto Gentili
13
 *
14
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
15
 * documentation files (the "Software"), to deal in the Software without restriction, including without
16
 * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
17
 * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
18
 * conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all copies or substantial
21
 * portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25
 * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
27
 * OR OTHER DEALINGS IN THE SOFTWARE.
28
 */
29
package org.burningwave.reflection;
30

31

32
import java.lang.invoke.MethodHandle;
33
import java.lang.invoke.MethodHandles;
34
import java.lang.invoke.MethodType;
35
import java.lang.reflect.Method;
36
import java.lang.reflect.Modifier;
37
import java.util.ArrayList;
38
import java.util.Arrays;
39
import java.util.Collection;
40
import java.util.List;
41
import java.util.function.Predicate;
42
import java.util.function.Supplier;
43
import java.util.stream.Collectors;
44

45
import org.burningwave.Classes;
46
import org.burningwave.Executor;
47
import org.burningwave.Strings;
48
import org.burningwave.Throwables;
49
import org.burningwave.ThrowingFunction;
50

51
@SuppressWarnings("unchecked")
52
public class Methods extends Members.Handler.OfExecutable<Method, MethodCriteria> {
53

54
        public final static Methods INSTANCE;
55

56
        static {
57
                INSTANCE = new Methods();
1✔
58
        }
1✔
59

60
        private Methods(){}
1✔
61

62
        public Collection<Method> findAllAndMakeThemAccessible(
63
                Class<?> targetClass
64
        ) {
65
                String cacheKey = getCacheKey(targetClass, Members.ALL_FOR_CLASS);
1✔
66
                Collection<Method> members = Cache.INSTANCE.uniqueKeyForAllMethods.getOrUploadIfAbsent(
1✔
67
                        cacheKey, () -> {
68
                                return findAllAndMakeThemAccessible(
1✔
69
                                        MethodCriteria.forEntireClassHierarchy(), targetClass
1✔
70
                                );
71
                        }
72
                );
73
                return members;
1✔
74
        }
75

76
        public Collection<Method> findAllByExactNameAndMakeThemAccessible(
77
                Class<?> targetClass,
78
                String methodName,
79
                Class<?>... inputParameterTypesOrSubTypes
80
        ) {
81
                return findAllByNamePredicateAndMakeThemAccessible(targetClass, "equals " + methodName, methodName::equals, inputParameterTypesOrSubTypes);
1✔
82
        }
83

84
        public Collection<Method> findAllByMatchedNameAndMakeThemAccessible(
85
                Class<?> targetClass,
86
                String regEx,
87
                Class<?>... inputParameterTypesOrSubTypes
88
        ) {
89
                return findAllByNamePredicateAndMakeThemAccessible(
×
90
                        targetClass, "match " + regEx,
91
                        name -> name.matches(regEx),
×
92
                        inputParameterTypesOrSubTypes
93
                );
94
        }
95

96
        public MethodHandle findDirectHandle(Class<?> targetClass, String methodName, Class<?>... inputParameterTypesOrSubTypes) {
97
                return findDirectHandleBox(targetClass, methodName, inputParameterTypesOrSubTypes).getHandler();
×
98
        }
99

100
        public Method findFirstAndMakeItAccessible(Class<?> targetClass, String memberName, Class<?>... inputParameterTypesOrSubTypes) {
101
                Collection<Method> members = findAllByExactNameAndMakeThemAccessible(targetClass, memberName, inputParameterTypesOrSubTypes);
1✔
102
                if (members.size() == 1) {
1✔
103
                        return members.stream().findFirst().get();
1✔
104
                } else if (members.size() > 1) {
1✔
105
                        Collection<Method> membersThatMatch = searchForExactMatch(members, inputParameterTypesOrSubTypes);
1✔
106
                        if (!membersThatMatch.isEmpty()) {
1✔
107
                                return membersThatMatch.stream().findFirst().get();
1✔
108
                        }
109
                        return members.stream().findFirst().get();
1✔
110
                }
111
                return null;
×
112
        }
113

114

115
        public Method findOneAndMakeItAccessible(Class<?> targetClass, String memberName, Class<?>... inputParameterTypesOrSubTypes) {
116
                Collection<Method> members = findAllByExactNameAndMakeThemAccessible(targetClass, memberName, inputParameterTypesOrSubTypes);
×
117
                if (members.size() == 1) {
×
118
                        return members.stream().findFirst().get();
×
119
                } else if (members.size() > 1) {
×
120
                        Collection<Method> membersThatMatch = searchForExactMatch(members, inputParameterTypesOrSubTypes);
×
121
                        if (membersThatMatch.size() == 1) {
×
122
                                return membersThatMatch.stream().findFirst().get();
×
123
                        }
124
                        Throwables.INSTANCE.throwException(
×
125
                                new IllegalArgumentException(
126
                                        Strings.INSTANCE.compile(
×
127
                                                "Found more than one of method named {} with argument types {} in {} hierarchy",
128
                                                memberName,
129
                                                String.join(", ", Arrays.asList(inputParameterTypesOrSubTypes).stream().map(cls -> cls.getName()).collect(Collectors.toList())),
×
130
                                                targetClass.getName()
×
131
                                        )
132
                                )
133
                        );
134
                }
135
                return null;
×
136
        }
137

138
        public <T> T invoke(Object target, Method method, Object... params) {
139
                return Facade.INSTANCE.invoke(target, method, params);
1✔
140
        }
141

142
        public <T> T invoke(Object target, String methodName, Object... arguments) {
143
                return Executor.getFirst(() ->
1✔
144
                        (T)invokeDirect(
1✔
145
                                Classes.INSTANCE.retrieveFrom(target),
1✔
146
                                target, methodName, () -> {
147
                                        List<Object> argumentList = new ArrayList<>();
1✔
148
                                        argumentList.add(target);
1✔
149
                                        return argumentList;
1✔
150
                                },
151
                                arguments
152
                        ), () ->
153
                                invoke(
1✔
154
                                        Classes.INSTANCE.retrieveFrom(target),
1✔
155
                                        null, methodName, method ->
156
                                                invoke(
1✔
157
                                                        target, method, getArgumentArray(
1✔
158
                                                        method,
159
                                                        this::getArgumentListWithArrayForVarArgs,
160
                                                        ArrayList::new,
161
                                                        arguments
162
                                                )
163
                                        ),
164
                                        arguments
165
                                )
166
                );
167
        }
168

169
        public         <T> T invokeStatic(Class<?> targetClass, String methodName, Object... arguments) {
170
                return Executor.getFirst(
1✔
171
                        () ->
172
                                (T)invokeDirect(targetClass, null, methodName, ArrayList::new, arguments),
1✔
173
                        () ->
174
                                invoke(
×
175
                                        targetClass, null, methodName, method ->
176
                                                (T)invoke(null,
×
177
                                                        method,
178
                                                        getArgumentArray(
×
179
                                                                method,
180
                                                                this::getArgumentListWithArrayForVarArgs,
181
                                                                ArrayList::new,
182
                                                                arguments
183
                                                        )
184
                                                ),
185
                                        arguments
186
                                )
187
                );
188
        }
189

190

191
        String createGetterMethodNameByFieldPath(String fieldPath) {
192
                String methodName =
×
193
                        "get" + Strings.INSTANCE.capitalizeFirstCharacter(fieldPath);
×
194
                return methodName;
×
195
        }
196

197
        String createSetterMethodNameByFieldPath(String fieldPath) {
198
                String methodName =
×
199
                        "set" + Strings.INSTANCE.capitalizeFirstCharacter(fieldPath);
×
200
                return methodName;
×
201
        }
202

203
        @Override
204
        MethodHandle retrieveMethodHandle(MethodHandles.Lookup consulter, Method method) throws java.lang.NoSuchMethodException, IllegalAccessException {
205
                Class<?> methodDeclaringClass = method.getDeclaringClass();
1✔
206
                return !Modifier.isStatic(method.getModifiers())?
1✔
207
                        consulter.findSpecial(
1✔
208
                                methodDeclaringClass, retrieveNameForCaching(method),
1✔
209
                                MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
1✔
210
                                methodDeclaringClass
211
                        ):
212
                        consulter.findStatic(
1✔
213
                                methodDeclaringClass, retrieveNameForCaching(method),
1✔
214
                                MethodType.methodType(method.getReturnType(), method.getParameterTypes())
1✔
215
                        );
216
        }
217

218
        @Override
219
        String retrieveNameForCaching(Method method) {
220
                return method.getName();
1✔
221
        }
222

223
        private Collection<Method> findAllByNamePredicateAndMakeThemAccessible(
224
                Class<?> targetClass,
225
                String cacheKeyPrefix,
226
                Predicate<String> namePredicate,
227
                Class<?>... inputParameterTypesOrSubTypes
228
        ) {
229
                String cacheKey = getCacheKey(targetClass, cacheKeyPrefix, inputParameterTypesOrSubTypes);
1✔
230
                return Cache.INSTANCE.uniqueKeyForAllMethods.getOrUploadIfAbsent(cacheKey, () -> {
1✔
231
                        MethodCriteria criteria = MethodCriteria.forEntireClassHierarchy()
1✔
232
                                .name(namePredicate)
1✔
233
                                .and().parameterTypesAreAssignableFrom(inputParameterTypesOrSubTypes);
1✔
234
                        if (inputParameterTypesOrSubTypes != null && inputParameterTypesOrSubTypes.length == 0) {
1✔
235
                                criteria = criteria.or(MethodCriteria.forEntireClassHierarchy().name(namePredicate).and().parameter((parameters, idx) -> parameters.length == 1 && parameters[0].isVarArgs()));
1✔
236
                        }
237
                        MethodCriteria finalCriteria = criteria;
1✔
238
                        return Cache.INSTANCE.uniqueKeyForAllMethods.getOrUploadIfAbsent(cacheKey, () ->
1✔
239
                                findAllAndApply(
1✔
240
                                                finalCriteria, targetClass, (member) -> {
241
                                                setAccessible(member, true);
1✔
242
                                        }
1✔
243
                                )
244
                        );
245
                });
246
        }
247

248
        private Members.Handler.OfExecutable.Box<Method> findDirectHandleBox(Class<?> targetClass, String methodName, Class<?>... inputParameterTypesOrSubTypes) {
249
                String cacheKey = getCacheKey(targetClass, "equals " + methodName, inputParameterTypesOrSubTypes);
1✔
250
                Members.Handler.OfExecutable.Box<Method> entry =
1✔
251
                        (Box<Method>)Cache.INSTANCE.uniqueKeyForExecutableAndMethodHandle.get(cacheKey);
1✔
252
                if (entry == null) {
1✔
253
                        Method method = findFirstAndMakeItAccessible(targetClass, methodName, inputParameterTypesOrSubTypes);
1✔
254
                        if (method == null) {
1✔
255
                                Throwables.INSTANCE.throwException(
×
256
                                        new NoSuchMethodException(
257
                                                Strings.INSTANCE.compile(
×
258
                                                        "Method {} not found in {} hierarchy", methodName, targetClass.getName()
×
259
                                                )
260
                                        )
261
                                );
262
                        }
263
                        entry = findDirectHandleBox(
1✔
264
                                method, cacheKey
265
                        );
266
                }
267
                return entry;
1✔
268
        }
269

270
        private <T> T invoke(Class<?> targetClass, Object target, String methodName, ThrowingFunction<Method, T, Throwable> methodInvoker, Object... arguments) {
271
                return Executor.get(() -> {
1✔
272
                        Method method = findFirstAndMakeItAccessible(targetClass, methodName, Classes.INSTANCE.retrieveFrom(arguments));
1✔
273
                        if (method == null) {
1✔
274
                                Throwables.INSTANCE.throwException(
×
275
                                        new NoSuchMethodException(
276
                                                Strings.INSTANCE.compile(
×
277
                                                        "Method {} not found in {} hierarchy", methodName, targetClass.getName()
×
278
                                                )
279
                                        )
280
                                );
281
                        }
282
                        return methodInvoker.apply(method);
1✔
283
                });
284
        }
285

286
        private <T> T invokeDirect(Class<?> targetClass, Object target, String methodName, Supplier<List<Object>> listSupplier,  Object... arguments) {
287
                Class<?>[] argsType = Classes.INSTANCE.retrieveFrom(arguments);
1✔
288
                Members.Handler.OfExecutable.Box<Method> methodHandleBox = findDirectHandleBox(targetClass, methodName, argsType);
1✔
289
                return Executor.get(() -> {
1✔
290
                                Method method = methodHandleBox.getExecutable();
1✔
291
                                List<Object> argumentList = getFlatArgumentList(method, listSupplier, arguments);
1✔
292
                                return (T)methodHandleBox.getHandler().invokeWithArguments(argumentList);
1✔
293
                        }
294
                );
295
        }
296

297
        public static class NoSuchMethodException extends RuntimeException {
298

299
                private static final long serialVersionUID = -2912826056405333039L;
300

301
                public NoSuchMethodException(String message) {
302
                        super(message);
×
303
                }
×
304

305
        }
306

307
}
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

© 2026 Coveralls, Inc