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

hazendaz / jmockit1 / 496

15 Nov 2025 05:33PM UTC coverage: 72.192% (-0.008%) from 72.2%
496

push

github

web-flow
Merge pull request #412 from hazendaz/renovate/major-spring-core

Update spring core to v7 (major)

5677 of 8360 branches covered (67.91%)

Branch coverage included in aggregate %.

11922 of 16018 relevant lines covered (74.43%)

0.74 hits per line

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

98.65
/main/src/main/java/mockit/internal/reflection/GenericTypeReflection.java
1
/*
2
 * MIT License
3
 * Copyright (c) 2006-2025 JMockit developers
4
 * See LICENSE file for full license text.
5
 */
6
package mockit.internal.reflection;
7

8
import edu.umd.cs.findbugs.annotations.NonNull;
9
import edu.umd.cs.findbugs.annotations.Nullable;
10

11
import java.lang.reflect.GenericArrayType;
12
import java.lang.reflect.GenericDeclaration;
13
import java.lang.reflect.Member;
14
import java.lang.reflect.ParameterizedType;
15
import java.lang.reflect.Type;
16
import java.lang.reflect.TypeVariable;
17
import java.lang.reflect.WildcardType;
18
import java.util.ArrayList;
19
import java.util.Collections;
20
import java.util.HashMap;
21
import java.util.List;
22
import java.util.Map;
23
import java.util.Map.Entry;
24

25
@SuppressWarnings("OverlyComplexClass")
26
public final class GenericTypeReflection {
27
    @NonNull
28
    private final Map<String, Type> typeParametersToTypeArguments;
29
    @NonNull
30
    private final Map<String, String> typeParametersToTypeArgumentNames;
31
    private final boolean withSignatures;
32

33
    public GenericTypeReflection(@NonNull Class<?> ownerClass, @Nullable Type genericType) {
34
        this(ownerClass, genericType, true);
1✔
35
    }
1✔
36

37
    public GenericTypeReflection(@NonNull Class<?> ownerClass, @Nullable Type genericType, boolean withSignatures) {
1✔
38
        typeParametersToTypeArguments = new HashMap<>(4);
1✔
39
        typeParametersToTypeArgumentNames = withSignatures ? new HashMap<>(4) : Collections.<String, String> emptyMap();
1✔
40
        this.withSignatures = withSignatures;
1✔
41
        discoverTypeMappings(ownerClass, genericType);
1✔
42
    }
1✔
43

44
    private void discoverTypeMappings(@NonNull Class<?> rawType, @Nullable Type genericType) {
45
        if (genericType instanceof ParameterizedType) {
1✔
46
            addMappingsFromTypeParametersToTypeArguments(rawType, (ParameterizedType) genericType);
1✔
47
        }
48

49
        addGenericTypeMappingsForSuperTypes(rawType);
1✔
50
    }
1✔
51

52
    private void addGenericTypeMappingsForSuperTypes(@NonNull Class<?> rawType) {
53
        Type superType = rawType;
1✔
54

55
        while (superType != null && superType != Object.class) {
1✔
56
            Class<?> superClass = (Class<?>) superType;
1✔
57
            superType = superClass.getGenericSuperclass();
1✔
58

59
            if (superType != null && superType != Object.class) {
1✔
60
                superClass = addGenericTypeMappingsIfParameterized(superType);
1✔
61
                superType = superClass;
1✔
62
            }
63

64
            addGenericTypeMappingsForInterfaces(superClass);
1✔
65
        }
1✔
66
    }
1✔
67

68
    @NonNull
69
    private Class<?> addGenericTypeMappingsIfParameterized(@NonNull Type superType) {
70
        if (superType instanceof ParameterizedType) {
1✔
71
            ParameterizedType genericSuperType = (ParameterizedType) superType;
1✔
72
            Class<?> rawType = (Class<?>) genericSuperType.getRawType();
1✔
73
            addMappingsFromTypeParametersToTypeArguments(rawType, genericSuperType);
1✔
74
            return rawType;
1✔
75
        }
76

77
        return (Class<?>) superType;
1✔
78
    }
79

80
    private void addGenericTypeMappingsForInterfaces(@NonNull Class<?> classOrInterface) {
81
        for (Type implementedInterface : classOrInterface.getGenericInterfaces()) {
1✔
82
            Class<?> implementedType = addGenericTypeMappingsIfParameterized(implementedInterface);
1✔
83
            addGenericTypeMappingsForInterfaces(implementedType);
1✔
84
        }
85
    }
1✔
86

87
    private void addMappingsFromTypeParametersToTypeArguments(@NonNull Class<?> rawType,
88
            @NonNull ParameterizedType genericType) {
89
        String ownerTypeDesc = getOwnerClassDesc(rawType);
1✔
90
        TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
1✔
91
        Type[] typeArguments = genericType.getActualTypeArguments();
1✔
92

93
        for (int i = 0, n = typeParameters.length; i < n; i++) {
1✔
94
            TypeVariable<?> typeParam = typeParameters[i];
1✔
95
            String typeVarName = typeParam.getName();
1✔
96

97
            if (typeParametersToTypeArguments.containsKey(ownerTypeDesc + ':' + typeVarName)) {
1✔
98
                continue;
1✔
99
            }
100

101
            Type typeArg = typeArguments[i];
1✔
102

103
            if (typeArg instanceof Class<?>) {
1✔
104
                addMappingForClassType(ownerTypeDesc, typeVarName, typeArg);
1✔
105
            } else if (typeArg instanceof TypeVariable<?>) {
1✔
106
                addMappingForTypeVariable(ownerTypeDesc, typeVarName, typeArg);
1✔
107
            } else if (typeArg instanceof ParameterizedType) {
1✔
108
                addMappingForParameterizedType(ownerTypeDesc, typeVarName, typeArg);
1✔
109
            } else if (typeArg instanceof GenericArrayType) {
1✔
110
                addMappingForArrayType(ownerTypeDesc, typeVarName, typeArg);
1✔
111
            } else {
112
                addMappingForFirstTypeBound(ownerTypeDesc, typeParam);
1✔
113
            }
114
        }
115

116
        Type outerType = genericType.getOwnerType();
1✔
117

118
        if (outerType instanceof ParameterizedType) {
1✔
119
            ParameterizedType parameterizedOuterType = (ParameterizedType) outerType;
1✔
120
            Class<?> rawOuterType = (Class<?>) parameterizedOuterType.getRawType();
1✔
121
            addMappingsFromTypeParametersToTypeArguments(rawOuterType, parameterizedOuterType);
1✔
122
        }
123
    }
1✔
124

125
    private void addMappingForClassType(@NonNull String ownerTypeDesc, @NonNull String typeName,
126
            @NonNull Type typeArg) {
127
        String mappedTypeArgName = null;
1✔
128

129
        if (withSignatures) {
1✔
130
            Class<?> classArg = (Class<?>) typeArg;
1✔
131
            String ownerClassDesc = getOwnerClassDesc(classArg);
1✔
132
            mappedTypeArgName = classArg.isArray() ? ownerClassDesc : 'L' + ownerClassDesc;
1✔
133
        }
134

135
        addTypeMapping(ownerTypeDesc, typeName, typeArg, mappedTypeArgName);
1✔
136
    }
1✔
137

138
    private void addMappingForTypeVariable(@NonNull String ownerTypeDesc, @NonNull String typeName,
139
            @NonNull Type typeArg) {
140
        @Nullable
141
        String mappedTypeArgName = null;
1✔
142

143
        if (withSignatures) {
1✔
144
            TypeVariable<?> typeVar = (TypeVariable<?>) typeArg;
1✔
145
            String ownerClassDesc = getOwnerClassDesc(typeVar);
1✔
146
            String intermediateTypeArg = ownerClassDesc + ":T" + typeVar.getName();
1✔
147
            mappedTypeArgName = typeParametersToTypeArgumentNames.get(intermediateTypeArg);
1✔
148
        }
149

150
        addTypeMapping(ownerTypeDesc, typeName, typeArg, mappedTypeArgName);
1✔
151
    }
1✔
152

153
    private void addMappingForParameterizedType(@NonNull String ownerTypeDesc, @NonNull String typeName,
154
            @NonNull Type typeArg) {
155
        String mappedTypeArgName = getMappedTypeArgName(typeArg);
1✔
156
        addTypeMapping(ownerTypeDesc, typeName, typeArg, mappedTypeArgName);
1✔
157
    }
1✔
158

159
    @Nullable
160
    private String getMappedTypeArgName(@NonNull Type typeArg) {
161
        if (withSignatures) {
1✔
162
            Class<?> classType = getClassType(typeArg);
1✔
163
            return 'L' + getOwnerClassDesc(classType);
1✔
164
        }
165

166
        return null;
1✔
167
    }
168

169
    private void addMappingForArrayType(@NonNull String ownerTypeDesc, @NonNull String typeName,
170
            @NonNull Type typeArg) {
171
        String mappedTypeArgName = null;
1✔
172

173
        if (withSignatures) {
1✔
174
            mappedTypeArgName = getMappedTypeArgName((GenericArrayType) typeArg);
1✔
175
        }
176

177
        addTypeMapping(ownerTypeDesc, typeName, typeArg, mappedTypeArgName);
1✔
178
    }
1✔
179

180
    private void addMappingForFirstTypeBound(@NonNull String ownerTypeDesc, @NonNull TypeVariable<?> typeParam) {
181
        Type typeArg = typeParam.getBounds()[0];
1✔
182
        String mappedTypeArgName = getMappedTypeArgName(typeArg);
1✔
183
        addTypeMapping(ownerTypeDesc, typeParam.getName(), typeArg, mappedTypeArgName);
1✔
184
    }
1✔
185

186
    @NonNull
187
    private static String getOwnerClassDesc(@NonNull Class<?> rawType) {
188
        return rawType.getName().replace('.', '/');
1✔
189
    }
190

191
    @NonNull
192
    private Class<?> getClassType(@NonNull Type type) {
193
        if (type instanceof ParameterizedType) {
1✔
194
            ParameterizedType parameterizedType = (ParameterizedType) type;
1✔
195
            return (Class<?>) parameterizedType.getRawType();
1✔
196
        }
197

198
        if (type instanceof TypeVariable<?>) {
1✔
199
            TypeVariable<?> typeVar = (TypeVariable<?>) type;
1✔
200
            String typeVarKey = getTypeVariableKey(typeVar);
1✔
201
            @Nullable
202
            Type typeArg = typeParametersToTypeArguments.get(typeVarKey);
1✔
203

204
            if (typeArg == null) {
1!
205
                throw new IllegalArgumentException("Unable to resolve type variable \"" + typeVar.getName() + '"');
×
206
            }
207

208
            // noinspection TailRecursion
209
            return getClassType(typeArg);
1✔
210
        }
211

212
        return (Class<?>) type;
1✔
213
    }
214

215
    @NonNull
216
    private String getMappedTypeArgName(@NonNull GenericArrayType arrayType) {
217
        StringBuilder argName = new StringBuilder(20);
1✔
218
        argName.append('[');
1✔
219

220
        while (true) {
221
            Type componentType = arrayType.getGenericComponentType();
1✔
222

223
            if (!(componentType instanceof GenericArrayType)) {
1✔
224
                Class<?> classType = getClassType(componentType);
1✔
225
                argName.append('L').append(getOwnerClassDesc(classType));
1✔
226
                return argName.toString();
1✔
227
            }
228
            argName.append('[');
1✔
229
            // noinspection AssignmentToMethodParameter
230
            arrayType = (GenericArrayType) componentType;
1✔
231
        }
1✔
232
    }
233

234
    private void addTypeMapping(@NonNull String ownerTypeDesc, @NonNull String typeVarName, @NonNull Type mappedTypeArg,
235
            @Nullable String mappedTypeArgName) {
236
        typeParametersToTypeArguments.put(ownerTypeDesc + ':' + typeVarName, mappedTypeArg);
1✔
237

238
        if (mappedTypeArgName != null) {
1✔
239
            addTypeMapping(ownerTypeDesc, typeVarName, mappedTypeArgName);
1✔
240
        }
241
    }
1✔
242

243
    private void addTypeMapping(@NonNull String ownerTypeDesc, @NonNull String typeVarName,
244
            @NonNull String mappedTypeArgName) {
245
        String typeMappingKey = ownerTypeDesc + ":T" + typeVarName;
1✔
246
        typeParametersToTypeArgumentNames.put(typeMappingKey, mappedTypeArgName);
1✔
247
    }
1✔
248

249
    public final class GenericSignature {
250
        private final List<String> parameters = new ArrayList<>();
1✔
251
        private final String parameterTypeDescs;
252
        private final int lengthOfParameterTypeDescs;
253
        private int currentPos;
254

255
        GenericSignature(@NonNull String signature) {
1✔
256
            int p = signature.indexOf('(');
1✔
257
            int q = signature.lastIndexOf(')');
1✔
258
            parameterTypeDescs = signature.substring(p + 1, q);
1✔
259
            lengthOfParameterTypeDescs = parameterTypeDescs.length();
1✔
260
            addTypeDescsToList();
1✔
261
        }
1✔
262

263
        private void addTypeDescsToList() {
264
            while (currentPos < lengthOfParameterTypeDescs) {
1✔
265
                addNextParameter();
1✔
266
            }
267
        }
1✔
268

269
        private void addNextParameter() {
270
            int startPos = currentPos;
1✔
271
            int endPos;
272
            char c = parameterTypeDescs.charAt(startPos);
1✔
273

274
            switch (c) {
1✔
275
                case 'T':
276
                    endPos = parameterTypeDescs.indexOf(';', startPos);
1✔
277
                    currentPos = endPos;
1✔
278
                    break;
1✔
279
                case 'L':
280
                    endPos = advanceToEndOfTypeDesc();
1✔
281
                    break;
1✔
282
                case '[': {
283
                    char elemTypeStart = firstCharacterOfArrayElementType();
1✔
284
                    if (elemTypeStart == 'T') {
1✔
285
                        endPos = parameterTypeDescs.indexOf(';', startPos);
1✔
286
                        currentPos = endPos;
1✔
287
                    } else if (elemTypeStart == 'L') {
1✔
288
                        endPos = advanceToEndOfTypeDesc();
1✔
289
                    } else {
290
                        endPos = currentPos + 1;
1✔
291
                    }
292
                    break;
1✔
293
                }
294
                default:
295
                    endPos = currentPos + 1;
1✔
296
                    break;
297
            }
298

299
            currentPos++;
1✔
300
            String parameter = parameterTypeDescs.substring(startPos, endPos);
1✔
301
            parameters.add(parameter);
1✔
302
        }
1✔
303

304
        private int advanceToEndOfTypeDesc() {
305
            char c = '\0';
1✔
306

307
            do {
308
                currentPos++;
1✔
309
                if (currentPos == lengthOfParameterTypeDescs) {
1!
310
                    break;
×
311
                }
312
                c = parameterTypeDescs.charAt(currentPos);
1✔
313
            } while (c != ';' && c != '<');
1✔
314

315
            int endPos = currentPos;
1✔
316

317
            if (c == '<') {
1✔
318
                advancePastTypeArguments();
1✔
319
                currentPos++;
1✔
320
            }
321

322
            return endPos;
1✔
323
        }
324

325
        private char firstCharacterOfArrayElementType() {
326
            char c;
327

328
            do {
329
                currentPos++;
1✔
330
                c = parameterTypeDescs.charAt(currentPos);
1✔
331
            } while (c == '[');
1✔
332

333
            return c;
1✔
334
        }
335

336
        private void advancePastTypeArguments() {
337
            int angleBracketDepth = 1;
1✔
338

339
            do {
340
                currentPos++;
1✔
341
                char c = parameterTypeDescs.charAt(currentPos);
1✔
342
                if (c == '>') {
1✔
343
                    angleBracketDepth--;
1✔
344
                } else if (c == '<') {
1✔
345
                    angleBracketDepth++;
1✔
346
                }
347
            } while (angleBracketDepth > 0);
1✔
348
        }
1✔
349

350
        public boolean satisfiesGenericSignature(@NonNull String otherSignature) {
351
            GenericSignature other = new GenericSignature(otherSignature);
1✔
352
            return areMatchingSignatures(other);
1✔
353
        }
354

355
        private boolean areMatchingSignatures(@NonNull GenericSignature other) {
356
            int n = parameters.size();
1✔
357

358
            if (n != other.parameters.size()) {
1✔
359
                return false;
1✔
360
            }
361

362
            for (int i = 0; i < n; i++) {
1✔
363
                String p1 = other.parameters.get(i);
1✔
364
                String p2 = parameters.get(i);
1✔
365

366
                if (!areParametersOfSameType(p1, p2)) {
1✔
367
                    return false;
1✔
368
                }
369
            }
370

371
            return true;
1✔
372
        }
373

374
        @SuppressWarnings("MethodWithMultipleLoops")
375
        private boolean areParametersOfSameType(@NonNull String param1, @NonNull String param2) {
376
            if (param1.equals(param2)) {
1✔
377
                return true;
1✔
378
            }
379

380
            int i = -1;
1✔
381
            char c;
382
            do {
383
                i++;
1✔
384
                c = param1.charAt(i);
1✔
385
            } while (c == '[');
1✔
386
            if (c != 'T') {
1✔
387
                return false;
1✔
388
            }
389

390
            String typeVarName1 = param1.substring(i);
1✔
391
            String typeVarName2 = param2.substring(i);
1✔
392
            String typeArg1 = null;
1✔
393

394
            for (Entry<String, String> typeParamAndArgName : typeParametersToTypeArgumentNames.entrySet()) {
1!
395
                String typeMappingKey = typeParamAndArgName.getKey();
1✔
396
                String typeVarName = typeMappingKey.substring(typeMappingKey.indexOf(':') + 1);
1✔
397

398
                if (typeVarName.equals(typeVarName1)) {
1✔
399
                    typeArg1 = typeParamAndArgName.getValue();
1✔
400
                    break;
1✔
401
                }
402
            }
1✔
403

404
            return typeVarName2.equals(typeArg1);
1✔
405
        }
406

407
        public boolean satisfiesSignature(@NonNull String otherSignature) {
408
            GenericSignature other = new GenericSignature(otherSignature);
1✔
409
            return other.areMatchingSignatures(this);
1✔
410
        }
411
    }
412

413
    @NonNull
414
    public GenericSignature parseSignature(@NonNull String genericSignature) {
415
        return new GenericSignature(genericSignature);
1✔
416
    }
417

418
    @NonNull
419
    public String resolveSignature(@NonNull String ownerTypeDesc, @NonNull String genericSignature) {
420
        addTypeArgumentsIfAvailable(ownerTypeDesc, genericSignature);
1✔
421

422
        int p = genericSignature.lastIndexOf(')') + 1;
1✔
423
        int q = genericSignature.length();
1✔
424
        String returnType = genericSignature.substring(p, q);
1✔
425
        String resolvedReturnType = replaceTypeParametersWithActualTypes(ownerTypeDesc, returnType);
1✔
426

427
        StringBuilder finalSignature = new StringBuilder(genericSignature);
1✔
428
        finalSignature.replace(p, q, resolvedReturnType);
1✔
429
        return finalSignature.toString();
1✔
430
    }
431

432
    private void addTypeArgumentsIfAvailable(@NonNull String ownerTypeDesc, @NonNull String signature) {
433
        int firstParen = signature.indexOf('(');
1✔
434
        if (firstParen == 0) {
1✔
435
            return;
1✔
436
        }
437

438
        int p = 1;
1✔
439
        boolean lastMappingFound = false;
1✔
440

441
        while (!lastMappingFound) {
1✔
442
            int q = signature.indexOf(':', p);
1✔
443
            String typeVar = signature.substring(p, q);
1✔
444

445
            q++;
1✔
446

447
            if (signature.charAt(q) == ':') {
1✔
448
                q++; // an unbounded type argument uses ":" as separator, while a bounded one uses "::"
1✔
449
            }
450

451
            int r = signature.indexOf(':', q);
1✔
452

453
            if (r < 0) {
1✔
454
                r = firstParen - 2;
1✔
455
                lastMappingFound = true;
1✔
456
            } else {
457
                r = signature.lastIndexOf(';', r);
1✔
458
                p = r + 1;
1✔
459
            }
460

461
            String typeArg = signature.substring(q, r);
1✔
462
            addTypeMapping(ownerTypeDesc, typeVar, typeArg);
1✔
463
        }
1✔
464
    }
1✔
465

466
    @NonNull
467
    private String replaceTypeParametersWithActualTypes(@NonNull String ownerTypeDesc, @NonNull String typeDesc) {
468
        if (typeDesc.charAt(0) == 'T' && !typeParametersToTypeArgumentNames.isEmpty()) {
1✔
469
            return replaceTypeParameters(ownerTypeDesc, typeDesc);
1✔
470
        }
471

472
        int p = typeDesc.indexOf('<');
1✔
473

474
        if (p < 0) {
1✔
475
            return typeDesc;
1✔
476
        }
477

478
        String resolvedTypeDesc = typeDesc;
1✔
479

480
        for (Entry<String, String> paramAndArg : typeParametersToTypeArgumentNames.entrySet()) {
1✔
481
            String typeMappingKey = paramAndArg.getKey();
1✔
482
            String typeParam = typeMappingKey.substring(typeMappingKey.indexOf(':') + 1) + ';';
1✔
483
            String typeArg = paramAndArg.getValue() + ';';
1✔
484
            resolvedTypeDesc = resolvedTypeDesc.replace(typeParam, typeArg);
1✔
485
        }
1✔
486

487
        return resolvedTypeDesc;
1✔
488
    }
489

490
    @NonNull
491
    private String replaceTypeParameters(@NonNull String ownerTypeDesc, @NonNull String typeDesc) {
492
        String typeParameter = typeDesc.substring(0, typeDesc.length() - 1);
1✔
493

494
        while (true) {
495
            @Nullable
496
            String typeArg = typeParametersToTypeArgumentNames.get(ownerTypeDesc + ':' + typeParameter);
1✔
497

498
            if (typeArg == null) {
1✔
499
                return typeDesc;
1✔
500
            }
501

502
            if (typeArg.charAt(0) != 'T') {
1✔
503
                return typeArg + ';';
1✔
504
            }
505

506
            typeParameter = typeArg;
1✔
507
        }
1✔
508
    }
509

510
    @NonNull
511
    public Type resolveTypeVariable(@NonNull TypeVariable<?> typeVariable) {
512
        String typeVarKey = getTypeVariableKey(typeVariable);
1✔
513
        @Nullable
514
        Type typeArgument = typeParametersToTypeArguments.get(typeVarKey);
1✔
515

516
        if (typeArgument == null) {
1✔
517
            typeArgument = typeVariable.getBounds()[0];
1✔
518
        }
519

520
        if (typeArgument instanceof TypeVariable<?>) {
1✔
521
            typeArgument = resolveTypeVariable((TypeVariable<?>) typeArgument);
1✔
522
        }
523

524
        return typeArgument;
1✔
525
    }
526

527
    @NonNull
528
    private static String getTypeVariableKey(@NonNull TypeVariable<?> typeVariable) {
529
        String ownerClassDesc = getOwnerClassDesc(typeVariable);
1✔
530
        return ownerClassDesc + ':' + typeVariable.getName();
1✔
531
    }
532

533
    @NonNull
534
    private static String getOwnerClassDesc(@NonNull TypeVariable<?> typeVariable) {
535
        GenericDeclaration owner = typeVariable.getGenericDeclaration();
1✔
536
        Class<?> ownerClass = owner instanceof Member ? ((Member) owner).getDeclaringClass() : (Class<?>) owner;
1✔
537
        return getOwnerClassDesc(ownerClass);
1✔
538
    }
539

540
    public boolean areMatchingTypes(@NonNull Type declarationType, @NonNull Type realizationType) {
541
        if (declarationType.equals(realizationType)) {
1✔
542
            return true;
1✔
543
        }
544

545
        if (declarationType instanceof Class<?>) {
1✔
546
            if (realizationType instanceof Class<?>) {
1✔
547
                return ((Class<?>) declarationType).isAssignableFrom((Class<?>) realizationType);
1✔
548
            }
549
        } else if (declarationType instanceof TypeVariable<?>) {
1✔
550
            if (realizationType instanceof TypeVariable<?>) {
1✔
551
                return false;
1✔
552
            }
553

554
            // noinspection RedundantIfStatement
555
            if (areMatchingTypes((TypeVariable<?>) declarationType, realizationType)) {
1✔
556
                return true;
1✔
557
            }
558
        } else if (declarationType instanceof ParameterizedType) {
1✔
559
            ParameterizedType parameterizedDeclarationType = (ParameterizedType) declarationType;
1✔
560
            ParameterizedType parameterizedRealizationType = getParameterizedType(realizationType);
1✔
561

562
            if (parameterizedRealizationType != null) {
1✔
563
                return areMatchingTypes(parameterizedDeclarationType, parameterizedRealizationType);
1✔
564
            }
565
        }
566

567
        return false;
1✔
568
    }
569

570
    @Nullable
571
    private static ParameterizedType getParameterizedType(@NonNull Type realizationType) {
572
        if (realizationType instanceof ParameterizedType) {
1✔
573
            return (ParameterizedType) realizationType;
1✔
574
        }
575

576
        if (realizationType instanceof Class<?>) {
1✔
577
            return findRealizationSupertype((Class<?>) realizationType);
1✔
578
        }
579

580
        return null;
1✔
581
    }
582

583
    @Nullable
584
    private static ParameterizedType findRealizationSupertype(@NonNull Class<?> realizationType) {
585
        Type realizationSuperclass = realizationType.getGenericSuperclass();
1✔
586
        ParameterizedType parameterizedRealizationType = null;
1✔
587

588
        if (realizationSuperclass instanceof ParameterizedType) {
1✔
589
            parameterizedRealizationType = (ParameterizedType) realizationSuperclass;
1✔
590
        } else {
591
            for (Type realizationSupertype : realizationType.getGenericInterfaces()) {
1✔
592
                if (realizationSupertype instanceof ParameterizedType) {
1✔
593
                    parameterizedRealizationType = (ParameterizedType) realizationSupertype;
1✔
594
                    break;
1✔
595
                }
596
            }
597
        }
598

599
        return parameterizedRealizationType;
1✔
600
    }
601

602
    private boolean areMatchingTypes(@NonNull TypeVariable<?> declarationType, @NonNull Type realizationType) {
603
        String typeVarKey = getTypeVariableKey(declarationType);
1✔
604
        @Nullable
605
        Type resolvedType = typeParametersToTypeArguments.get(typeVarKey);
1✔
606

607
        return resolvedType != null && (resolvedType.equals(realizationType)
1!
608
                || typeSatisfiesResolvedTypeVariable(resolvedType, realizationType));
1✔
609
    }
610

611
    private boolean areMatchingTypes(@NonNull ParameterizedType declarationType,
612
            @NonNull ParameterizedType realizationType) {
613
        return declarationType.getRawType().equals(realizationType.getRawType())
1✔
614
                && haveMatchingActualTypeArguments(declarationType, realizationType);
1✔
615
    }
616

617
    private boolean haveMatchingActualTypeArguments(@NonNull ParameterizedType declarationType,
618
            @NonNull ParameterizedType realizationType) {
619
        Type[] declaredTypeArguments = declarationType.getActualTypeArguments();
1✔
620
        Type[] concreteTypeArguments = realizationType.getActualTypeArguments();
1✔
621

622
        for (int i = 0, n = declaredTypeArguments.length; i < n; i++) {
1✔
623
            Type declaredTypeArg = declaredTypeArguments[i];
1✔
624
            Type concreteTypeArg = concreteTypeArguments[i];
1✔
625

626
            if (declaredTypeArg instanceof TypeVariable<?>) {
1✔
627
                if (areMatchingTypeArguments((TypeVariable<?>) declaredTypeArg, concreteTypeArg)) {
1✔
628
                    continue;
1✔
629
                }
630
            } else if (areMatchingTypes(declaredTypeArg, concreteTypeArg)) {
1✔
631
                continue;
1✔
632
            }
633

634
            return false;
1✔
635
        }
636

637
        return true;
1✔
638
    }
639

640
    @SuppressWarnings("RedundantIfStatement")
641
    private boolean areMatchingTypeArguments(@NonNull TypeVariable<?> declaredType, @NonNull Type concreteType) {
642
        String typeVarKey = getTypeVariableKey(declaredType);
1✔
643
        @Nullable
644
        Type resolvedType = typeParametersToTypeArguments.get(typeVarKey);
1✔
645

646
        if (resolvedType != null) {
1✔
647
            if (resolvedType.equals(concreteType) || concreteType instanceof Class<?>
1✔
648
                    && typeSatisfiesResolvedTypeVariable(resolvedType, (Class<?>) concreteType)) {
1✔
649
                return true;
1✔
650
            }
651

652
            if (concreteType instanceof WildcardType
1✔
653
                    && typeSatisfiesUpperBounds(resolvedType, ((WildcardType) concreteType).getUpperBounds())) {
1✔
654
                return true;
1✔
655
            }
656
        } else if (typeSatisfiesUpperBounds(concreteType, declaredType.getBounds())) {
1!
657
            return true;
1✔
658
        }
659

660
        return false;
1✔
661
    }
662

663
    private boolean typeSatisfiesResolvedTypeVariable(@NonNull Type resolvedType, @NonNull Type realizationType) {
664
        Class<?> realizationClass = getClassType(realizationType);
1✔
665
        return typeSatisfiesResolvedTypeVariable(resolvedType, realizationClass);
1✔
666
    }
667

668
    private boolean typeSatisfiesResolvedTypeVariable(@NonNull Type resolvedType, @NonNull Class<?> realizationType) {
669
        Class<?> resolvedClass = getClassType(resolvedType);
1✔
670
        return resolvedClass.isAssignableFrom(realizationType);
1✔
671
    }
672

673
    private boolean typeSatisfiesUpperBounds(@NonNull Type type, @NonNull Type[] upperBounds) {
674
        Class<?> classType = getClassType(type);
1✔
675

676
        for (Type upperBound : upperBounds) {
1✔
677
            Class<?> classBound = getClassType(upperBound);
1✔
678

679
            if (!classBound.isAssignableFrom(classType)) {
1✔
680
                return false;
1✔
681
            }
682
        }
683

684
        return true;
1✔
685
    }
686
}
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