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

link-intersystems / lis-commons / #282

05 Nov 2023 05:04PM UTC coverage: 89.848% (-0.2%) from 90.032%
#282

Pull #10

renelink
Improved Readme.
Pull Request #10: feature/beans record

77 of 80 new or added lines in 11 files covered. (96.25%)

1 existing line in 1 file now uncovered.

7461 of 8304 relevant lines covered (89.85%)

0.9 hits per line

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

93.52
/lis-commons-lang/src/main/java/com/link_intersystems/lang/reflect/Class2.java
1
/**
2
 * Copyright 2011 Link Intersystems GmbH <rene.link@link-intersystems.com>
3
 * <p>
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
 * <p>
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 * <p>
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 com.link_intersystems.lang.reflect;
17

18
import com.link_intersystems.lang.ClassLoaderContextAware;
19
import com.link_intersystems.lang.Signature;
20
import com.link_intersystems.lang.reflect.PotentiallyApplicableMemberStrategy.PotentiallyApplicableCriteria;
21
import com.link_intersystems.lang.reflect.PotentiallyApplicableMemberStrategy.PotentionallyApplicableConstructorCriteria;
22
import com.link_intersystems.lang.reflect.PotentiallyApplicableMemberStrategy.PotentionallyApplicableMethodCriteria;
23

24
import java.io.Serializable;
25
import java.lang.reflect.*;
26
import java.util.*;
27
import java.util.function.Function;
28

29
import static java.util.Objects.requireNonNull;
30

31
/**
32
 * An extension to the {@link Class} that encapsulates complex logic like
33
 * finding an applicable method or constructor and handling generic types.
34
 *
35
 *
36
 * <h2>Generic type handling</h2>
37
 * <p>
38
 * Suppose the following hierarchy
39
 * </p>
40
 *
41
 * <pre>
42
 *
43
 *                             +-----------------+                               +-------------------+
44
 *                             |  AInterface&lt;A&gt;  |                               | BInterface&lt;B, BB&gt; |
45
 *                             +-----------------+                               +-------------------+
46
 *                                      ^                                                  ^
47
 *                                      |                                                  |
48
 *                 +-----------------------------------------+       +-------------------------------------------+
49
 *                 |  AClass&lt;A&gt; implements AInterface&lt;Long&gt;  |       | CInterface&lt;C,CC&gt; extends BInterface&lt;CC,C&gt; |
50
 *                 +-----------------------------------------+       +-------------------------------------------+
51
 *                                      ^                                                  ^
52
 *                                      |                                                  |
53
 *                +---------------------+--------------------------------------------------+
54
 *                |                                                                        |
55
 * +------------------------------+          +-------------------------------------------------------------------+
56
 * |BClass extends AClass&lt;Integer&gt;|          | CClass extends AClass&lt;Float&gt; implements CInterface&lt;Byte, Boolean&gt; |
57
 * +------------------------------+          +-------------------------------------------------------------------+
58
 *
59
 * </pre>
60
 * <p>
61
 * In the previous described hierarchy the following code's assertions will be
62
 * true
63
 *
64
 * <pre>
65
 * Class2 aClass = Class2.forClass(AClass.class);
66
 * Class2 bClass = Class2.forClass(BClass.class);
67
 * Class2 cClass = Class2.forClass(CClass.class);
68
 *
69
 * TypeVariable&lt;?&gt; aClassTypeVariable = aClass.getTypeVariable(&quot;A&quot;);
70
 * Type bBoundType = bClass.getBoundType(aClassTypeVariable);
71
 * assertEqual(Integer.class, bBoundType);
72
 *
73
 * Type cBoundType = cClass.getBoundType(aClassTypeVariable);
74
 * assertEqual(Float.class, cBoundType);
75
 *
76
 * // bounded types are also resolved in a complex hierarchy,
77
 * // even if the bounded types got swapped in
78
 * // the hierarchy like in CInterface&lt;C,CC&gt; extends BInterface&lt;CC,C&gt;
79
 * Class2 bInterface = Class2.forClass(BInterface);
80
 * TypeVariable&lt;?&gt; bInterfaceTypeVariable = bInterface.getTypeVariable(&quot;BB&quot;);
81
 * cBoundType = cClass.getBoundType(bInterfaceTypeVariable);
82
 * assertEqual(Byte.class, cBoundType);
83
 * </pre>
84
 * <p>
85
 *
86
 * </p>
87
 *
88
 * @author René Link <a
89
 * href="mailto:rene.link@link-intersystems.com">[rene.link@link-
90
 * intersystems.com]</a>
91
 * @since 1.0.0;
92
 */
93
public class Class2<T> implements Serializable {
94

95
    /**
96
     *
97
     */
98
    private static final long serialVersionUID = -1718130720537907850L;
99

100
    private final Class<T> clazz;
101

102
    private Class2<T[]> arrayType2;
103

104
    private List<Method2> declaredMethods;
105

106
    private List<Constructor2<T>> declaredConstructors;
107

108
    /**
109
     * internal cache for type variables. I expect that a "normal" generic type
110
     * will not have more than 3 type variables. Therefore I use the Flat3Map
111
     * that can grow but is very fast for up to 3 entries.
112
     */
113
    private transient Map<String, TypeVariable<?>> typeVariableCache;
114

115
    private static final Object TYPE_VARIABLE_CACHE_SYNCHRONIZATION = new Object();
1✔
116

117
    private static final Map<Class<?>, Class2<?>> CLASS_TO_CLASS2 = new HashMap<>();
1✔
118

119
    private static final PotentiallyApplicableMemberStrategy POTENTIALLY_APPLICABLE_STRATEGY = new PotentiallyApplicableMemberStrategy();
1✔
120

121
    private static final ChooseMostSpecificMemberStrategy<Member2<?>> CHOOSE_POTENTIAL_APPLICABLE = new ChooseMostSpecificMemberStrategy<>();
1✔
122

123
    private ArrayType<T> arrayType;
124

125
    @SuppressWarnings("unchecked")
126
    private static <RT extends Member2<?>> ChooseMostSpecificMemberStrategy<RT> getChooseMostSpecificStrategy() {
127
        return (ChooseMostSpecificMemberStrategy<RT>) CHOOSE_POTENTIAL_APPLICABLE;
1✔
128
    }
129

130
    /**
131
     * @return a {@link Class2} object that represents the {@link Class} defined
132
     * by the full qualified class name.
133
     * @since 1.0.0;
134
     * @deprecated use {@link #get(String)} instead.
135
     */
136
    public static <T> Class2<T> forName(String className)
137
            throws ClassNotFoundException {
138
        return get(className);
×
139
    }
140

141
    /**
142
     * @return a {@link Class2} object that represents the {@link Class} defined
143
     * by the full qualified class name.
144
     */
145
    @SuppressWarnings("unchecked")
146
    public static <T> Class2<T> get(String className)
147
            throws ClassNotFoundException {
148
        Class<T> classForName = (Class<T>) Class.forName(className);
1✔
149
        return get(classForName);
1✔
150
    }
151

152
    /**
153
     * @return a {@link Class2} for the given {@link Class}.
154
     * @since 1.0.0;
155
     * @deprecated use {@link #get(Class)} instead.
156
     */
157
    public static <T> Class2<T> forClass(Class<T> clazz) {
158
        return get(clazz);
×
159
    }
160

161
    /**
162
     * @return a {@link Class2} for the given {@link Class}.
163
     * @since 1.2.0;
164
     */
165
    @SuppressWarnings("unchecked")
166
    public static <T> Class2<T> get(Class<T> clazz) {
167
        requireNonNull(clazz);
1✔
168
        Class2<T> class2 = (Class2<T>) CLASS_TO_CLASS2.get(clazz);
1✔
169
        if (class2 == null) {
1✔
170
            class2 = new Class2<>(clazz);
1✔
171
            CLASS_TO_CLASS2.put(clazz, class2);
1✔
172
        }
173
        return class2;
1✔
174
    }
175

176
    /**
177
     * Constructs a new {@link Class2} for class clazz.
178
     *
179
     * @param clazz the class to get a {@link Class2} for.
180
     */
181
    protected Class2(Class<T> clazz) {
1✔
182
        this.clazz = requireNonNull(clazz);
1✔
183
    }
1✔
184

185
    /**
186
     * @return the {@link ClassLoaderContextAware} that uses this class's class
187
     * loader. If this class has no class loader (java system classes)
188
     * it returns a {@link ClassLoaderContextAware} that uses the
189
     * {@link ClassLoader#getSystemClassLoader()}.
190
     * @since 1.2.0;
191
     */
192
    public ClassLoaderContextAware getClassLoaderContextAware() {
193
        ClassLoader classLoader = clazz.getClassLoader();
1✔
194
        ClassLoaderContextAware classLoaderContext = ClassLoaderContextAware
1✔
195
                .forClassLoader(classLoader);
1✔
196
        return classLoaderContext;
1✔
197
    }
198

199
    /**
200
     * @return the {@link Package2} that this {@link Class2} belongs to.
201
     * @since 1.2.0;
202
     */
203
    public Package2 getPackage() {
204
        return Package2.get(getType().getPackage());
1✔
205
    }
206

207
    /**
208
     * Constructs a new instance of the type described by this {@link Class2}
209
     * with the given arguments.
210
     *
211
     * @param constructorArgs the arguments for creating an instance of this {@link Class2}
212
     *                        's type.
213
     * @return a new instance of the type represented by this {@link Class2}.
214
     * @throws Exception if one of the declared exceptions (if any) of the constructor
215
     *                   is thrown.
216
     * @since 1.2.0;
217
     */
218
    public T newInstance(Object... constructorArgs) throws Exception {
219
        Constructor2<T> applicableConstructor = getApplicableConstructor(constructorArgs);
×
220
        T newInstance = applicableConstructor.newInstance(constructorArgs);
×
221
        return newInstance;
×
222
    }
223

224
    /**
225
     * @return the {@link Class} object that this {@link Class2} is based on.
226
     * @since 1.0.0;
227
     */
228
    public Class<T> getType() {
229
        return clazz;
1✔
230
    }
231

232
    /**
233
     * Returns the array type of this {@link Class2}'s type, e.g. if this
234
     * <code>Class2</code>'s type is <code>Object</code> the returned type is
235
     * <code>Object[]</code>. This method is a way to dynamically create an
236
     * array type.
237
     *
238
     * @return the array type of this {@link Class2}'s type, e.g. if this
239
     * <code>Class2</code>'s type is <code>Object</code> the returned
240
     * type is <code>Object[]</code>. This method is a way to
241
     * dynamically create an array type.
242
     * @since 1.2.0;
243
     */
244
    public ArrayType<T> getArrayType() {
245
        if (arrayType == null) {
1✔
246
            arrayType = new ArrayType<>(getType());
1✔
247
        }
248
        return arrayType;
1✔
249
    }
250

251

252
    /**
253
     * Identifies the {@link Constructor2} of this {@link Class2} that is
254
     * applicable for the invocation parameters. Uses the same search algorithm
255
     * as {@link #getApplicableMethod(String, AccessType[], Class...)} . If the
256
     * {@link Class} represented by this {@link Class2} is an interface this
257
     * method does never return a {@link Constructor2} always null.
258
     *
259
     * @return the applicable constructor or null if no applicable constructor
260
     * could be found.
261
     * @since 1.0.0;
262
     */
263
    @SuppressWarnings({"unchecked", "rawtypes"})
264
    public Constructor2<T> getApplicableConstructor(AccessType[] accessTypes,
265
                                                    Class<?>... invocationParameters) {
266
        PotentionallyApplicableConstructorCriteria potentionallyApplicableConstructorCriteria = new PotentionallyApplicableConstructorCriteria(
1✔
267
                accessTypes, invocationParameters);
268
        return getApplicableConstructor(potentionallyApplicableConstructorCriteria);
1✔
269
    }
270

271
    private Constructor2<T> getApplicableConstructor(PotentionallyApplicableConstructorCriteria potentionallyApplicableConstructorCriteria) {
272
        List<Constructor2<T>> constructorsInternal = getConstructors();
1✔
273
        List<Constructor2<T>> potentiallyApplicable = getPotentiallyApplicable(constructorsInternal, potentionallyApplicableConstructorCriteria);
1✔
274
        return chooseApplicableMember(potentiallyApplicable);
1✔
275
    }
276

277
    /**
278
     * Identifies the {@link Constructor2} of this {@link Class2} that is
279
     * applicable for the invocation parameters and {@link AccessType}s. If this
280
     * class represents an interface null is returned.
281
     *
282
     * @param accessTypes the {@link AccessType} that the selected constructor must
283
     *                    have.
284
     * @since 1.0.0;
285
     */
286
    public Constructor2<T> getApplicableConstructor(AccessType[] accessTypes,
287
                                                    Object... invocationArguments) {
288
        PotentionallyApplicableConstructorCriteria potentionallyApplicableConstructorCriteria = new PotentionallyApplicableConstructorCriteria(
1✔
289
                accessTypes, invocationArguments);
290
        return getApplicableConstructor(potentionallyApplicableConstructorCriteria);
1✔
291
    }
292

293
    private <C extends Member2<?>> List<C> getPotentiallyApplicable(
294
            List<C> candidates,
295
            PotentiallyApplicableCriteria<C> potentiallyApplicableCriteria) {
296
        return POTENTIALLY_APPLICABLE_STRATEGY
1✔
297
                .getPotentialApplicable(candidates,
1✔
298
                        potentiallyApplicableCriteria);
299
    }
300

301
    private <RT extends Member2<?>> RT chooseApplicableMember(
302
            List<RT> potentiallyApplicable) {
303
        ChooseMostSpecificMemberStrategy<RT> chooseMostSpecificStrategy = getChooseMostSpecificStrategy();
1✔
304
        RT mostSpecific = chooseMostSpecificStrategy
1✔
305
                .chooseMostSpecific(potentiallyApplicable);
1✔
306
        return mostSpecific;
1✔
307
    }
308

309
    /**
310
     * Returns the declaring method2 object using the same logic as
311
     * {@link Class#getDeclaredMethod(String, Class...)}.
312
     *
313
     * @param name           the name of the method.
314
     * @param parameterTypes the exact parameter types that the declaring method must have.
315
     * @return the {@link Method2} that represents the declaring method with the
316
     * name and Assert.
317
     * @throws NoSuchMethodException if no method matches the parameters.
318
     * @since 1.0.0;
319
     */
320
    public Method2 getDeclaringMethod2(String name, Class<?>... parameterTypes)
321
            throws NoSuchMethodException {
322
        requireNonNull(parameterTypes);
1✔
323
        List<Method2> declaredMethods = getDeclaredMethods();
1✔
324
        for (Method2 method2 : declaredMethods) {
1✔
325
            Class<?>[] parameterTypes2 = method2.getParameterTypes();
1✔
326
            if (Arrays.equals(parameterTypes, parameterTypes2)) {
1✔
327
                return method2;
1✔
328
            }
UNCOV
329
        }
×
330

331
        throw new NoSuchMethodException("No method named " + name + "( )");
×
332
    }
333

334
    /**
335
     * Returns the {@link Method2} of this {@link Class2} that matches the
336
     * signature or null if no method matches.
337
     *
338
     * @param signature the {@link Signature} of the requested method
339
     * @return the {@link Method2} of this {@link Class2} that matches the
340
     * signature or null if no method matches.
341
     * @since 1.0.0;
342
     */
343
    public Method2 getMethod2(Signature signature) {
344
        List<Method2> declaredMethods = getDeclaredMethods();
1✔
345
        for (Method2 method2 : declaredMethods) {
1✔
346
            if (method2.getSignature().equals(signature)) {
1✔
347
                return method2;
1✔
348
            }
349
        }
1✔
350
        return null;
1✔
351
    }
352

353
    /**
354
     * @return the {@link Method2} for the given {@link Method}. The
355
     * {@link Method} must be declared on this {@link Class2}'s
356
     * {@link Class}.
357
     * @throws NoSuchMethodException if the method is not declared on this {@link Class2}'s
358
     *                               {@link Class}.
359
     * @since 1.0.0;
360
     */
361
    Method2 getMethod2(Method method) throws NoSuchMethodException {
362
        requireNonNull(method);
1✔
363
        List<Method2> declaredMethods = getDeclaredMethods();
1✔
364
        for (Method2 method2 : declaredMethods) {
1✔
365
            if (method.equals(method2.getMember())) {
1✔
366
                return method2;
1✔
367
            }
368
        }
1✔
369
        throw new NoSuchMethodException(method + " must be a method of this "
×
370
                + clazz);
371
    }
372

373
    /**
374
     * Identifies the {@link Constructor2} of this {@link Class2} that is
375
     * applicable for the invocation parameters regardless to the access type.
376
     * If this class represents an interface null is returned.
377
     *
378
     * @return the applicable constructor or null if no applicable constructor
379
     * could be found.
380
     * @see #getApplicableConstructor(AccessType[], Class...)
381
     * @since 1.0.0;
382
     */
383
    public Constructor2<T> getApplicableConstructor(Class<?>... paramTypes) {
384
        return getApplicableConstructor(AccessType.values(), paramTypes);
1✔
385
    }
386

387
    /**
388
     * {@inheritDoc}
389
     *
390
     * @param args
391
     * @return
392
     */
393
    public Constructor2<T> getApplicableConstructor(Object... args) {
394
        return getApplicableConstructor(AccessType.values(), args);
1✔
395
    }
396

397
    /**
398
     * Identifies the {@link Method2} of this {@link Class2} that is applicable
399
     * for the name and invocation parameters regardless to the access type.
400
     *
401
     * @return the applicable method or null if no applicable method could be
402
     * found.
403
     * @see #getApplicableMethod(String, AccessType[], Class...)
404
     * @since 1.0.0;
405
     */
406
    public Method2 getApplicableMethod(String name, Class<?>... paramTypes) {
407
        return getApplicableMethod(name, AccessType.values(), paramTypes);
1✔
408
    }
409

410
    public Method2 getApplicableMethod(String name, Object... args) {
411
        return getApplicableMethod(name, AccessType.values(), args);
1✔
412

413
    }
414

415
    /**
416
     * Identifies the {@link Method2} of this {@link Class2} that is applicable
417
     * for the name and invocation parameters, including those methods declared
418
     * by this class or interface and those inherited from superclasses and
419
     * superinterfaces. Uses the search algorithm defined by the java language
420
     * specification - 15.12.2.1 Identify Potentially Applicable Methods.
421
     *
422
     * <pre>
423
     *  <h2>15.12.2.1 Identify Potentially Applicable Methods.</h2>
424
     *  A member method is <i>potentially applicable</i> to a method invocation if and only if all of
425
     *  the following are true:
426
     *  <ul>
427
     *  <li>The name of the member is identical to the name of the method in the method invocation.</li>
428
     *  <li>The member is accessible (§6.6) to the class or interface in which the method
429
     * invocation appears.</li>
430
     *  <li>The arity of the member is lesser or equal to the arity of the method invocation.</li>
431
     *  <li>If the member is a variable arity method with arity <i>n</i>, the arity of the method
432
     *  invocation is greater or equal to <i>n</i>-1.</li>
433
     *  <li>If the member is a fixed arity method with arity <i>n</i>, the arity of the method
434
     *  invocation is equal to <i>n</i></li>
435
     *  <li>If the method invocation includes explicit type parameters, and the member is a generic
436
     *  method, then the number of actual type parameters is equal to the number of formal type
437
     *  parameters.</li>
438
     *  </ul>
439
     * </pre>
440
     *
441
     * @param name       the method's name.
442
     * @param paramTypes the parameter classes.
443
     * @return the applicable method or null if no applicable method exists in
444
     * this class nor in it's superclasses.
445
     * @since 1.0.0;
446
     */
447
    public Method2 getApplicableMethod(String name, AccessType[] accessTypes,
448
                                       Class<?>... paramTypes) {
449
        Method2 applicableMethod = getDeclaredApplicableMethod(name,
1✔
450
                accessTypes, paramTypes);
451
        if (applicableMethod == null) {
1✔
452
            Class2<? super T> superclass2 = getSuperclass2();
1✔
453
            if (superclass2 != null) {
1✔
454
                applicableMethod = superclass2.getApplicableMethod(name,
1✔
455
                        accessTypes, paramTypes);
456
            }
457
        }
458
        return applicableMethod;
1✔
459
    }
460

461
    /**
462
     * Identifies the {@link Method2} of this {@link Class2} that is applicable
463
     * for the name and invocation parameters, but excludes inherited methods.
464
     * Uses the search algorithm defined by the java language specification -
465
     * 15.12.2.1 Identify Potentially Applicable Methods.
466
     *
467
     * <pre>
468
     *  <h2>15.12.2.1 Identify Potentially Applicable Methods.</h2>
469
     *  A member method is <i>potentially applicable</i> to a method invocation if and only if all of
470
     *  the following are true:
471
     *  <ul>
472
     *  <li>The name of the member is identical to the name of the method in the method invocation.</li>
473
     *  <li>The member is accessible (§6.6) to the class or interface in which the method
474
     * invocation appears.</li>
475
     *  <li>The arity of the member is lesser or equal to the arity of the method invocation.</li>
476
     *  <li>If the member is a variable arity method with arity <i>n</i>, the arity of the method
477
     *  invocation is greater or equal to <i>n</i>-1.</li>
478
     *  <li>If the member is a fixed arity method with arity <i>n</i>, the arity of the method
479
     *  invocation is equal to <i>n</i></li>
480
     *  <li>If the method invocation includes explicit type parameters, and the member is a generic
481
     *  method, then the number of actual type parameters is equal to the number of formal type
482
     *  parameters.</li>
483
     *  </ul>
484
     * </pre>
485
     *
486
     * @param name       the method's name.
487
     * @param paramTypes the parameter classes.
488
     * @return the applicable method or null if no applicable method exists in
489
     * this class.
490
     * @since 1.2.0;
491
     */
492
    public Method2 getDeclaredApplicableMethod(String name,
493
                                               AccessType[] accessTypes, Class<?>... paramTypes) {
494
        List<Method2> declaredMethods = getDeclaredMethods();
1✔
495
        PotentionallyApplicableMethodCriteria potentionallyApplicableMethodCriteria = new PotentionallyApplicableMethodCriteria(
1✔
496
                name, accessTypes, paramTypes);
497

498
        List<Method2> potentiallyApplicable = getPotentiallyApplicable(
1✔
499
                declaredMethods, potentionallyApplicableMethodCriteria);
500
        return chooseApplicableMember(potentiallyApplicable);
1✔
501
    }
502

503
    /**
504
     * Same behavior as
505
     * {@link #getApplicableMethod(String, AccessType[], Class...)}, but uses
506
     * invocation parameter objects. E.g. finds a methods that is applicable to
507
     * be invoked with the invocation parameter objects.
508
     *
509
     * @param name the name of the method.
510
     * @param args the invocation parameters.
511
     * @return the applicable method or null if no applicable method exists in
512
     * this class nor in it's superclasses.
513
     * @since 1.2.0;
514
     */
515
    public Method2 getApplicableMethod(String name, AccessType[] accessTypes,
516
                                       Object... args) {
517
        Method2 applicableMethod = getDeclaredApplicableMethod(name,
1✔
518
                accessTypes, args);
519
        if (applicableMethod == null) {
1✔
520
            Class2<? super T> superclass2 = getSuperclass2();
1✔
521
            if (superclass2 != null) {
1✔
522
                applicableMethod = superclass2.getApplicableMethod(name,
1✔
523
                        accessTypes, args);
524
            }
525
        }
526
        return applicableMethod;
1✔
527
    }
528

529
    /**
530
     * Same behavior as
531
     * {@link #getDeclaredApplicableMethod(String, AccessType[], Class...)}, but
532
     * uses invocation parameter objects. Finds a methods that is applicable to
533
     * be invoked with the invocation parameter objects.
534
     *
535
     * @param name the name of the method.
536
     * @param args the invocation parameters.
537
     * @return the applicable method or null if no applicable method exists in
538
     * this class.
539
     * @since 1.2.0;
540
     */
541
    public Method2 getDeclaredApplicableMethod(String name,
542
                                               AccessType[] accessTypes, Object... args) {
543
        List<Method2> declaredMethods = getDeclaredMethods();
1✔
544
        PotentionallyApplicableMethodCriteria potentionallyApplicableMethodCriteria = new PotentionallyApplicableMethodCriteria(
1✔
545
                name, accessTypes, args);
546

547
        List<Method2> potentiallyApplicable = getPotentiallyApplicable(
1✔
548
                declaredMethods, potentionallyApplicableMethodCriteria);
549
        return chooseApplicableMember(potentiallyApplicable);
1✔
550
    }
551

552
    /**
553
     * Convenience method to get a {@link Class2} instance of this class's
554
     * superclass.
555
     *
556
     * @return the {@link Class2} instance that represents this class's
557
     * superclass if any. If this class represents the java
558
     * {@link Object} class null is returned.
559
     * @since 1.2.0;
560
     */
561
    public Class2<? super T> getSuperclass2() {
562
        Class<? super T> superclass = clazz.getSuperclass();
1✔
563
        if (superclass == null) {
1✔
564
            return null;
1✔
565
        }
566
        Class2<? super T> superclass2 = Class2.get(superclass);
1✔
567
        return superclass2;
1✔
568
    }
569

570
    /**
571
     * @return the {@link Method2}s for this {@link Class2}. Supposed to be used
572
     * by sub classes. Subclasses must not modify the returned array.
573
     * @since 1.0.0;
574
     */
575
    @SuppressWarnings({"unchecked", "rawtypes"})
576
    private List<Constructor2<T>> getConstructors() {
577
        if (declaredConstructors == null) {
1✔
578
            if (clazz.isInterface()) {
1✔
579
                declaredConstructors = Collections.emptyList();
×
580
            } else {
581
                Constructor<?>[] declaredConstructors = clazz
1✔
582
                        .getDeclaredConstructors();
1✔
583
                List<Constructor2<T>> constructors = new ArrayList<>();
1✔
584
                for (Constructor<?> declaredConstructor : declaredConstructors) {
1✔
585
                    constructors.add(new Constructor2(declaredConstructor));
1✔
586
                }
587
                this.declaredConstructors = Collections
1✔
588
                        .unmodifiableList(constructors);
1✔
589
            }
590
        }
591
        return declaredConstructors;
1✔
592
    }
593

594
    /**
595
     * @return the {@link Method2}s for this {@link Class2}. Supposed to be used
596
     * by sub classes.
597
     * @since 1.0.0;
598
     */
599
    private List<Method2> getDeclaredMethods() {
600
        if (declaredMethods == null) {
1✔
601
            List<Method2> declaredMethods = new ArrayList<>();
1✔
602
            Method[] declaredMethodArray = clazz.getDeclaredMethods();
1✔
603
            for (Method method : declaredMethodArray) {
1✔
604
                declaredMethods.add(new Method2(method));
1✔
605
            }
606
            this.declaredMethods = Collections
1✔
607
                    .unmodifiableList(declaredMethods);
1✔
608
        }
609
        return declaredMethods;
1✔
610
    }
611

612
    /**
613
     * The generic type variables defined at this class.
614
     *
615
     * @since 1.2.0;
616
     */
617
    public TypeVariable<?>[] getTypeVariables() {
618
        return clazz.getTypeParameters();
1✔
619
    }
620

621
    /**
622
     * @return the {@link TypeVariable} for the given name that is defined on
623
     * this generic type or null if no {@link TypeVariable} is
624
     * defined with that name. This method only looks at the current
625
     * type represented by this generic object and not on super
626
     * types.
627
     * @since 1.2.0;
628
     */
629
    public TypeVariable<?> getTypeVariable(String typeVarName) {
630
        requireNonNull(typeVarName);
1✔
631
        TypeVariable<?> typeVariable = getTypeVariableCache().get(typeVarName);
1✔
632
        if (typeVariable == null) {
1✔
633
            typeVarName = typeVarName.trim();
1✔
634
            TypeVariable<?>[] typeParams = clazz.getTypeParameters();
1✔
635
            for (TypeVariable<?> typeParameter : typeParams) {
1✔
636
                if (typeVarName.equals(typeParameter.getName())) {
1✔
637
                    typeVariable = typeParameter;
1✔
638
                    getTypeVariableCache().put(typeVarName, typeVariable);
1✔
639
                    break;
1✔
640
                }
641
            }
642
        }
643
        return typeVariable;
1✔
644
    }
645

646
    private Map<String, TypeVariable<?>> getTypeVariableCache() {
647
        synchronized (TYPE_VARIABLE_CACHE_SYNCHRONIZATION) {
1✔
648
            if (typeVariableCache == null) {
1✔
649
                typeVariableCache = new HashMap<>();
1✔
650
            }
651
            return typeVariableCache;
1✔
652
        }
653
    }
654

655
    /**
656
     * @param typeVariable the {@link TypeVariable} to get the bounded type for.
657
     * @return the type that is bound on this generic type for the given
658
     * {@link TypeVariable} or null if the type is not bound on this
659
     * generic's type. If the bound type is a generic type the
660
     * raw type will be returned.
661
     * @since 1.2.0;
662
     */
663
    public Type getBoundType(TypeVariable<?> typeVariable) {
664
        requireNonNull(typeVariable);
1✔
665
        Type type = doGetBoundType(typeVariable, clazz);
1✔
666
        if (type instanceof TypeVariable<?>) {
1✔
667
            typeVariable = (TypeVariable<?>) type;
1✔
668
            type = getBoundType(typeVariable);
1✔
669
        }
670
        return type;
1✔
671
    }
672

673
    /**
674
     * Convenience method that returns the bound class of the first type
675
     * variable occurrence in the class's hierarchy. Every class in the
676
     * hierarchy will be traversed. If a class doesn't define a type variable
677
     * with the required name that class's interfaces gets traversed too. If
678
     * also no interface has a type variable with the required name the next
679
     * class in the hierarchy will be traversed.
680
     *
681
     * @return the class bound to this class's type variable with the
682
     * typeVarName. Searches the class hierarchy for the first
683
     * occurrence of a type variable with the given name.
684
     * @throws IllegalArgumentException if no type variable definition could be found in this class's
685
     *                                  hierarchy.
686
     * @since 1.2.0;
687
     */
688
    public <C> Class<C> getBoundClass(String typeVarName) {
689
        class BoundClassIterator implements Iterator<Class<?>> {
690

691
            private Queue<Class<?>> types = new LinkedList<>();
1✔
692

693
            public BoundClassIterator(Class<T> type) {
1✔
694
                types.add(type);
1✔
695
            }
1✔
696

697
            @Override
698
            public boolean hasNext() {
699
                return !types.isEmpty();
1✔
700
            }
701

702
            @Override
703
            public Class<?> next() {
704
                Class<?> nextType = types.poll();
1✔
705

706
                Class<?> superclass = nextType.getSuperclass();
1✔
707
                if (superclass != null) {
1✔
708
                    types.add(superclass);
1✔
709
                }
710
                Class<?>[] interfaces = nextType.getInterfaces();
1✔
711

712
                types.addAll(Arrays.asList(interfaces));
1✔
713

714
                return nextType;
1✔
715
            }
716
        }
717

718
        BoundClassIterator boundClassIterator = new BoundClassIterator(getType());
1✔
719

720
        TypeVariable<?> typeVariable = null;
1✔
721
        while (boundClassIterator.hasNext()) {
1✔
722
            Class<?> clazzInHierarchy = boundClassIterator.next();
1✔
723
            Class2<?> class2InHierarchy = Class2.get(clazzInHierarchy);
1✔
724
            typeVariable = class2InHierarchy.getTypeVariable(typeVarName);
1✔
725
            if (typeVariable != null) {
1✔
726
                break;
1✔
727
            }
728
        }
1✔
729

730
        if (typeVariable == null) {
1✔
731
            throw new IllegalArgumentException("No type variable named "
1✔
732
                    + typeVarName + " was found in the hierarchy of "
733
                    + getType());
1✔
734
        }
735
        return getBoundClass(typeVariable);
1✔
736
    }
737

738
    /**
739
     * @return the class that is bound on this generic type for the
740
     * given {@link TypeVariable} or null if the type bound is not a
741
     * Class<?>. If the bound type resolved for the {@link TypeVariable}
742
     * is itself a ( {@link ParameterizedType} ) the raw type will be
743
     * returned.
744
     */
745
    @SuppressWarnings("unchecked")
746
    public <C> Class<C> getBoundClass(TypeVariable<?> typeVariable) {
747
        Type boundType = getBoundType(typeVariable);
1✔
748
        if (boundType == null) {
1✔
749
            return null;
1✔
750
        }
751
        if (boundType instanceof ParameterizedType) {
1✔
752
            ParameterizedType parameterizedType = (ParameterizedType) boundType;
1✔
753
            boundType = parameterizedType.getRawType();
1✔
754
        }
755

756
        if (boundType instanceof GenericArrayType) {
1✔
757
            GenericArrayType genericArrayType = GenericArrayType.class.cast(boundType);
×
758
            Type genericComponentType = genericArrayType
×
759
                    .getGenericComponentType();
×
760
            Class<?> componentType = Class.class.cast(genericComponentType);
×
761
            Object array = Array.newInstance(componentType, 0);
×
762
            boundType = array.getClass();
×
763
        }
764

765
        return (Class<C>) boundType;
1✔
766
    }
767

768
    /**
769
     * @param <TI>            the expected type so that clients must not cast.
770
     * @param typeVariable    the type variable that defines the class to be instantiated.
771
     * @param constructorArgs the arguments to use when constructing the bound type.
772
     * @return an instance of the bound type defined by the typeVariable -
773
     * constructing it with the constructorArgs. The right constructor
774
     * is determined by the constructorArgs object's types.
775
     * @throws IllegalStateException    in case of an {@link InstantiationException},
776
     *                                  {@link IllegalAccessException} or
777
     *                                  {@link InvocationTargetException} that arises when trying to
778
     *                                  instantiate the class bound to the type variable.
779
     * @throws IllegalArgumentException if the type bound to the type variable is an interface or the
780
     *                                  constructor that is called to instantiate the class bound to
781
     *                                  the type variable itself throws an
782
     *                                  {@link IllegalArgumentException} or. The original exception
783
     *                                  is wrapped to provide more information.
784
     */
785
    public <TI> TI getBoundInstance(TypeVariable<?> typeVariable,
786
                                    Object... constructorArgs) {
787
        Class<TI> typeClass = getBoundClass(typeVariable);
1✔
788
        if (typeClass.isInterface()) {
1✔
789
            throw new IllegalArgumentException("The type variable's ("
1✔
790
                    + typeVariable + ") bound type (" + typeClass
791
                    + ") is an interface.");
792
        }
793
        Class2<TI> class2 = Class2.get(typeClass);
1✔
794
        Class<?>[] constructorArgClasses = new Class<?>[constructorArgs.length];
1✔
795
        for (int i = 0; i < constructorArgs.length; i++) {
1✔
796
            Object arg = constructorArgs[i];
1✔
797
            if (arg != null) {
1✔
798
                constructorArgClasses[i] = arg.getClass();
1✔
799
            }
800
        }
801

802
        Constructor2<?> constructor2 = class2
1✔
803
                .getApplicableConstructor(constructorArgClasses);
1✔
804
        if (constructor2 == null) {
1✔
805
            throw new IllegalArgumentException(
1✔
806
                    "Type variable's ("
807
                            + typeVariable
808
                            + ") bound type "
809
                            + typeClass
810
                            + " doesn't have a constructor applicable for the argument types "
811
                            + Arrays.asList(constructorArgClasses));
1✔
812
        }
813
        try {
814
            TI t = constructor2.getInvokable().invoke(constructorArgs);
1✔
815
            return t;
1✔
816
        } catch (Exception e) {
×
817
            throw new IllegalStateException(
×
818
                    "Unable to instantiate an object of " + typeClass
819
                            + " using constructor " + constructor2.getMember()
×
820
                            + " with arguments "
821
                            + Arrays.asList(constructorArgs), e);
×
822
        }
823
    }
824

825
    /**
826
     * Same as {@link #toString(String...)} with parameter "java.lang".
827
     *
828
     * @return a string representation of this {@link Class2};
829
     * @since 1.0.0;
830
     */
831
    public String toString() {
832
        return toString("java.lang");
1✔
833
    }
834

835
    /**
836
     * The string representation of a {@link Class2}.
837
     *
838
     * <ul>
839
     * <li>
840
     * Types are represented by their canonical name. If a type is a
841
     * &quot;well-known&quot; type (all types in java.lang) the type's simple
842
     * name is used. E.g. String - java.util.List.</li>
843
     * <ul>
844
     * <li>
845
     * Arrays are represented by their type and appended by []. E.g. int[]
846
     * String[] java.beans.PropertyDescriptor[].</li>
847
     *
848
     * @param wellKnownPackages packages that are &quot;well known&quot; will not be printed
849
     *                          in the string representation. E.g. if java.lang is defined as
850
     *                          well known the Class2 that represents a String class will be
851
     *                          printed just as &quot;String&quot; and not java.lang.String.
852
     * @return a string representation of this {@link Class2};
853
     * @since 1.0.0;
854
     */
855
    public String toString(String... wellKnownPackages) {
856
        requireNonNull(wellKnownPackages);
1✔
857
        StringBuilder toStringBuilder = new StringBuilder();
1✔
858
        Class<?> clazz = getType();
1✔
859
        boolean isArray = clazz.isArray();
1✔
860
        if (isArray) {
1✔
861
            clazz = clazz.getComponentType();
1✔
862
        }
863
        Package clazzPackage = clazz.getPackage();
1✔
864
        String packageName = "";
1✔
865
        if (clazzPackage != null) {
1✔
866
            packageName = clazzPackage.getName();
1✔
867
        }
868

869
        boolean isWellKnownPackage = Arrays.binarySearch(wellKnownPackages,
1✔
870
                packageName) > -1;
871

872
        if (isWellKnownPackage) {
1✔
873
            toStringBuilder.append(clazz.getSimpleName());
1✔
874
        } else {
875
            toStringBuilder.append(clazz.getCanonicalName());
1✔
876
        }
877

878
        TypeVariable<?>[] typeParameters = clazz.getTypeParameters();
1✔
879
        String typeParametersToString = typeParametersToString(typeParameters);
1✔
880
        toStringBuilder.append(typeParametersToString);
1✔
881

882
        if (isArray) {
1✔
883
            toStringBuilder.append("[]");
1✔
884
        }
885
        return toStringBuilder.toString();
1✔
886
    }
887

888
    private String typeParametersToString(TypeVariable<?>[] typeParameters) {
889
        StringBuilder toStringBuilder = new StringBuilder();
1✔
890
        if (typeParameters.length > 0) {
1✔
891
            toStringBuilder.append("<");
1✔
892

893
            String[] typeParametersAsStrings = transform(typeParameters,
1✔
894
                    TypeVariableToStringTransformer.INSTANCE);
895

896
            String typeParametersToString = String.join(",", typeParametersAsStrings);
1✔
897
            toStringBuilder.append(typeParametersToString);
1✔
898

899
            toStringBuilder.append(">");
1✔
900
        }
901
        return toStringBuilder.toString();
1✔
902
    }
903

904
    @SuppressWarnings("SameParameterValue")
905
    private String[] transform(TypeVariable<?>[] typeVariables, Function<TypeVariable<?>, String> objectTransformer) {
906
        String[] toStringList = new String[typeVariables.length];
1✔
907
        for (int i = 0; i < typeVariables.length; i++) {
1✔
908
            TypeVariable<?> typeVariable = typeVariables[i];
1✔
909
            Object transformedObject = objectTransformer.apply(typeVariable);
1✔
910
            toStringList[i] = transformedObject.toString();
1✔
911
        }
912
        return toStringList;
1✔
913
    }
914

915
    private Type doGetBoundType(TypeVariable<?> typeVariable, Type... types) {
916
        Type boundType = null;
1✔
917
        for (Type type : types) {
1✔
918
            if (!(type instanceof Class<?>)) {
1✔
919
                continue;
1✔
920
            }
921

922
            Class<?> typeClass = Class.class.cast(type);
1✔
923
            boundType = findBoundTypeInGenericSuperclasses(typeClass,
1✔
924
                    typeVariable);
925
            if (boundType != null) {
1✔
926
                break;
1✔
927
            }
928

929
            boundType = findBoundTypeInGenericInterfaces(typeClass,
1✔
930
                    typeVariable);
931
            if (boundType != null) {
1✔
932
                break;
1✔
933
            }
934

935
            boundType = findBoundTypeInSuperclasses(typeClass, typeVariable);
1✔
936
            if (boundType != null) {
1✔
937
                break;
1✔
938
            }
939

940
            boundType = findBoundTypeInInterfaces(typeClass, typeVariable);
1✔
941
            if (boundType != null) {
1✔
942
                break;
1✔
943
            }
944
        }
945
        return boundType;
1✔
946
    }
947

948
    private Type findBoundTypeInGenericSuperclasses(Class<?> typeClass,
949
                                                    TypeVariable<?> typeVariable) {
950
        Type boundType = null;
1✔
951

952
        Type genericSuperclass = typeClass.getGenericSuperclass();
1✔
953
        if (genericSuperclass instanceof ParameterizedType) {
1✔
954
            ParameterizedType parameterizedType = ParameterizedType.class.cast(genericSuperclass);
1✔
955
            boundType = doGetBoundType(parameterizedType, typeVariable);
1✔
956
        }
957

958
        return boundType;
1✔
959
    }
960

961
    private Type findBoundTypeInGenericInterfaces(Class<?> typeClass,
962
                                                  TypeVariable<?> typeVariable) {
963
        Type boundType = null;
1✔
964

965
        Type[] genericInterfaces = typeClass.getGenericInterfaces();
1✔
966
        for (Type genericInterface : genericInterfaces) {
1✔
967
            if (genericInterface instanceof ParameterizedType) {
1✔
968
                ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
1✔
969
                boundType = doGetBoundType(parameterizedType, typeVariable);
1✔
970
                if (boundType != null) {
1✔
971
                    break;
1✔
972
                }
973
            }
974
        }
975

976
        return boundType;
1✔
977
    }
978

979
    private Type findBoundTypeInSuperclasses(Class<?> typeClass,
980
                                             TypeVariable<?> typeVariable) {
981
        Class<?> superclass = typeClass.getSuperclass();
1✔
982
        Type boundType = doGetBoundType(typeVariable, superclass);
1✔
983
        return boundType;
1✔
984
    }
985

986
    private Type findBoundTypeInInterfaces(Class<?> typeClass,
987
                                           TypeVariable<?> typeVariable) {
988
        Class<?>[] interfaces = typeClass.getInterfaces();
1✔
989
        Type boundType = doGetBoundType(typeVariable, interfaces);
1✔
990
        return boundType;
1✔
991
    }
992

993
    private Type doGetBoundType(ParameterizedType parameterizedType,
994
                                TypeVariable<?> typeVariable) {
995
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
1✔
996
        Type rawType = parameterizedType.getRawType();
1✔
997
        if (rawType instanceof Class<?>) {
1✔
998
            Class<?> rawTypeClass = (Class<?>) rawType;
1✔
999
            TypeVariable<?>[] typeParameters = rawTypeClass.getTypeParameters();
1✔
1000
            for (int i = 0; i < typeParameters.length; i++) {
1✔
1001
                TypeVariable<?> typeParameter = typeParameters[i];
1✔
1002
                if (typeParameter.equals(typeVariable)) {
1✔
1003
                    return actualTypeArguments[i];
1✔
1004
                }
1005
            }
1006
        }
1007
        return null;
1✔
1008
    }
1009

1010
    /**
1011
     * Nullsafe {@link ClassLoader} resolution. If this {@link Class2}
1012
     * represents a system class the {@link ClassLoader#getSystemClassLoader()}
1013
     * will be returned.
1014
     *
1015
     * @return the class loader of this {@link Class2}.
1016
     * @since 1.2.0;
1017
     */
1018
    public ClassLoader getClassLoader() {
1019
        ClassLoader classLoader = getType().getClassLoader();
1✔
1020
        if (classLoader == null) {
1✔
1021
            classLoader = ClassLoader.getSystemClassLoader();
1✔
1022
        }
1023
        return classLoader;
1✔
1024
    }
1025

1026
    @Override
1027
    public int hashCode() {
1028
        final int prime = 31;
1✔
1029
        int result = 1;
1✔
1030
        result = prime * result + clazz.hashCode();
1✔
1031
        return result;
1✔
1032
    }
1033

1034
    @SuppressWarnings("rawtypes")
1035
    @Override
1036
    public boolean equals(Object obj) {
1037
        if (this == obj)
1✔
1038
            return true;
1✔
1039
        if (obj == null)
1✔
1040
            return false;
1✔
1041
        if (getClass() != obj.getClass())
1✔
1042
            return false;
1✔
1043
        Class2 other = (Class2) obj;
1✔
1044
        return clazz.equals(other.clazz);
1✔
1045
    }
1046

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