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

raphw / byte-buddy / #801

27 Oct 2025 09:37AM UTC coverage: 84.715% (-0.4%) from 85.118%
#801

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

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

90.46
/byte-buddy-dep/src/main/java/net/bytebuddy/dynamic/scaffold/MethodGraph.java
1
/*
2
 * Copyright 2014 - Present Rafael Winterhalter
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.bytebuddy.dynamic.scaffold;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
20
import net.bytebuddy.description.method.MethodDescription;
21
import net.bytebuddy.description.method.MethodList;
22
import net.bytebuddy.description.modifier.Visibility;
23
import net.bytebuddy.description.type.TypeDefinition;
24
import net.bytebuddy.description.type.TypeDescription;
25
import net.bytebuddy.matcher.ElementMatcher;
26
import net.bytebuddy.matcher.FilterableList;
27
import net.bytebuddy.utility.nullability.MaybeNull;
28
import org.objectweb.asm.Opcodes;
29

30
import java.util.ArrayList;
31
import java.util.Arrays;
32
import java.util.Collections;
33
import java.util.HashMap;
34
import java.util.HashSet;
35
import java.util.Iterator;
36
import java.util.LinkedHashMap;
37
import java.util.LinkedHashSet;
38
import java.util.List;
39
import java.util.Map;
40
import java.util.Set;
41

42
import static net.bytebuddy.matcher.ElementMatchers.any;
43
import static net.bytebuddy.matcher.ElementMatchers.isBridge;
44
import static net.bytebuddy.matcher.ElementMatchers.isVirtual;
45
import static net.bytebuddy.matcher.ElementMatchers.isVisibleTo;
46
import static net.bytebuddy.matcher.ElementMatchers.not;
47

48
/**
49
 * A method graph represents a view on a set of methods as they are seen from a given type. Any method is represented as a node that represents
50
 * a method, its bridge methods, its resolution state and information on if it was made visible by a visibility bridge.
51
 */
52
public interface MethodGraph {
53

54
    /**
55
     * Locates a node in this graph which represents the provided method token.
56
     *
57
     * @param token A method token that represents the method to be located.
58
     * @return The node representing the given token.
59
     */
60
    Node locate(MethodDescription.SignatureToken token);
61

62
    /**
63
     * Lists all nodes of this method graph.
64
     *
65
     * @return A list of all nodes of this method graph.
66
     */
67
    NodeList listNodes();
68

69
    /**
70
     * A canonical implementation of an empty method graph.
71
     */
72
    enum Empty implements MethodGraph.Linked, MethodGraph.Compiler {
1✔
73

74
        /**
75
         * The singleton instance.
76
         */
77
        INSTANCE;
1✔
78

79
        /**
80
         * {@inheritDoc}
81
         */
82
        public Node locate(MethodDescription.SignatureToken token) {
83
            return Node.Unresolved.INSTANCE;
1✔
84
        }
85

86
        /**
87
         * {@inheritDoc}
88
         */
89
        public NodeList listNodes() {
90
            return new NodeList(Collections.<Node>emptyList());
1✔
91
        }
92

93
        /**
94
         * {@inheritDoc}
95
         */
96
        public MethodGraph getSuperClassGraph() {
97
            return this;
1✔
98
        }
99

100
        /**
101
         * {@inheritDoc}
102
         */
103
        public MethodGraph getInterfaceGraph(TypeDescription typeDescription) {
104
            return this;
1✔
105
        }
106

107
        /**
108
         * {@inheritDoc}
109
         */
110
        public Linked compile(TypeDefinition typeDefinition) {
111
            return this;
1✔
112
        }
113

114
        /**
115
         * {@inheritDoc}
116
         */
117
        @Deprecated
118
        public Linked compile(TypeDescription typeDescription) {
119
            return this;
×
120
        }
121

122
        /**
123
         * {@inheritDoc}
124
         */
125
        public Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
126
            return this;
×
127
        }
128

129
        /**
130
         * {@inheritDoc}
131
         */
132
        @Deprecated
133
        public Linked compile(TypeDescription typeDefinition, TypeDescription viewPoint) {
134
            return this;
×
135
        }
136
    }
137

138
    /**
139
     * A linked method graph represents a view that additionally exposes information of a given type's super type view and a
140
     * view on this graph's directly implemented interfaces.
141
     */
142
    interface Linked extends MethodGraph {
143

144
        /**
145
         * Returns a graph representing the view on this represented type's super type.
146
         *
147
         * @return A graph representing the view on this represented type's super type.
148
         */
149
        MethodGraph getSuperClassGraph();
150

151
        /**
152
         * Returns a graph representing the view on this represented type's directly implemented interface type.
153
         *
154
         * @param typeDescription The interface type for which a view is to be returned.
155
         * @return A graph representing the view on this represented type's directly implemented interface type.
156
         */
157
        MethodGraph getInterfaceGraph(TypeDescription typeDescription);
158

159
        /**
160
         * A simple implementation of a linked method graph that exposes views by delegation to given method graphs.
161
         */
162
        @HashCodeAndEqualsPlugin.Enhance
163
        class Delegation implements Linked {
164

165
            /**
166
             * The represented type's method graph.
167
             */
168
            private final MethodGraph methodGraph;
169

170
            /**
171
             * The super class's method graph.
172
             */
173
            private final MethodGraph superClassGraph;
174

175
            /**
176
             * A mapping of method graphs of the represented type's directly implemented interfaces to their graph representatives.
177
             */
178
            private final Map<TypeDescription, MethodGraph> interfaceGraphs;
179

180
            /**
181
             * Creates a new delegation method graph.
182
             *
183
             * @param methodGraph     The represented type's method graph.
184
             * @param superClassGraph The super class's method graph.
185
             * @param interfaceGraphs A mapping of method graphs of the represented type's directly implemented interfaces to their graph representatives.
186
             */
187
            public Delegation(MethodGraph methodGraph, MethodGraph superClassGraph, Map<TypeDescription, MethodGraph> interfaceGraphs) {
1✔
188
                this.methodGraph = methodGraph;
1✔
189
                this.superClassGraph = superClassGraph;
1✔
190
                this.interfaceGraphs = interfaceGraphs;
1✔
191
            }
1✔
192

193
            /**
194
             * {@inheritDoc}
195
             */
196
            public MethodGraph getSuperClassGraph() {
197
                return superClassGraph;
1✔
198
            }
199

200
            /**
201
             * {@inheritDoc}
202
             */
203
            public MethodGraph getInterfaceGraph(TypeDescription typeDescription) {
204
                MethodGraph interfaceGraph = interfaceGraphs.get(typeDescription);
1✔
205
                return interfaceGraph == null
1✔
206
                        ? Empty.INSTANCE
207
                        : interfaceGraph;
208
            }
209

210
            /**
211
             * {@inheritDoc}
212
             */
213
            public Node locate(MethodDescription.SignatureToken token) {
214
                return methodGraph.locate(token);
1✔
215
            }
216

217
            /**
218
             * {@inheritDoc}
219
             */
220
            public NodeList listNodes() {
221
                return methodGraph.listNodes();
1✔
222
            }
223
        }
224
    }
225

226
    /**
227
     * Represents a node within a method graph.
228
     */
229
    interface Node {
230

231
        /**
232
         * Returns the sort of this node.
233
         *
234
         * @return The sort of this node.
235
         */
236
        Sort getSort();
237

238
        /**
239
         * Returns the method that is represented by this node.
240
         *
241
         * @return The method that is represented by this node.
242
         */
243
        MethodDescription getRepresentative();
244

245
        /**
246
         * Returns a set of type tokens that this method represents. This set contains the actual method's type including the
247
         * types of all bridge methods.
248
         *
249
         * @return A set of type tokens that this method represents.
250
         */
251
        Set<MethodDescription.TypeToken> getMethodTypes();
252

253
        /**
254
         * Returns the minimal method visibility of all methods that are represented by this node.
255
         *
256
         * @return The minimal method visibility of all methods that are represented by this node.
257
         */
258
        Visibility getVisibility();
259

260
        /**
261
         * Represents a {@link net.bytebuddy.dynamic.scaffold.MethodGraph.Node}'s state.
262
         */
263
        enum Sort {
1✔
264

265
            /**
266
             * Represents a resolved node that was made visible by a visibility bridge.
267
             */
268
            VISIBLE(true, true, true),
1✔
269

270
            /**
271
             * Represents a resolved node that was not made visible by a visibility bridge.
272
             */
273
            RESOLVED(true, true, false),
1✔
274

275
            /**
276
             * Represents an ambiguous node, i.e. a node that might refer to several methods.
277
             */
278
            AMBIGUOUS(true, false, false),
1✔
279

280
            /**
281
             * Represents an unresolved node.
282
             */
283
            UNRESOLVED(false, false, false);
1✔
284

285
            /**
286
             * {@code true} if this sort represents a resolved node.
287
             */
288
            private final boolean resolved;
289

290
            /**
291
             * {@code true} if this sort represents a non-ambiguous node.
292
             */
293
            private final boolean unique;
294

295
            /**
296
             * {@code true} if this sort represents a node that was made by a visibility bridge.
297
             */
298
            private final boolean madeVisible;
299

300
            /**
301
             * Creates a new sort.
302
             *
303
             * @param resolved    {@code true} if this sort represents a resolved node.
304
             * @param unique      {@code true} if this sort represents a non-ambiguous node.
305
             * @param madeVisible {@code true} if this sort represents a node that was made by a visibility bridge.
306
             */
307
            Sort(boolean resolved, boolean unique, boolean madeVisible) {
1✔
308
                this.resolved = resolved;
1✔
309
                this.unique = unique;
1✔
310
                this.madeVisible = madeVisible;
1✔
311
            }
1✔
312

313
            /**
314
             * Verifies if this sort represents a resolved node.
315
             *
316
             * @return {@code true} if this sort represents a resolved node.
317
             */
318
            public boolean isResolved() {
319
                return resolved;
1✔
320
            }
321

322
            /**
323
             * Verifies if this sort represents a non-ambiguous node.
324
             *
325
             * @return {@code true} if this sort represents a non-ambiguous node.
326
             */
327
            public boolean isUnique() {
328
                return unique;
1✔
329
            }
330

331
            /**
332
             * Verifies if this sort represents a node that was made visible by a visibility bridge.
333
             *
334
             * @return {@code true} if this sort represents a node that was made visible by a visibility bridge.
335
             */
336
            public boolean isMadeVisible() {
337
                return madeVisible;
1✔
338
            }
339
        }
340

341
        /**
342
         * A canonical implementation of an unresolved node.
343
         */
344
        enum Unresolved implements Node {
1✔
345

346
            /**
347
             * The singleton instance.
348
             */
349
            INSTANCE;
1✔
350

351
            /**
352
             * {@inheritDoc}
353
             */
354
            public Sort getSort() {
355
                return Sort.UNRESOLVED;
1✔
356
            }
357

358
            /**
359
             * {@inheritDoc}
360
             */
361
            public MethodDescription getRepresentative() {
362
                throw new IllegalStateException("Cannot resolve the method of an illegal node");
1✔
363
            }
364

365
            /**
366
             * {@inheritDoc}
367
             */
368
            public Set<MethodDescription.TypeToken> getMethodTypes() {
369
                throw new IllegalStateException("Cannot resolve bridge method of an illegal node");
1✔
370
            }
371

372
            /**
373
             * {@inheritDoc}
374
             */
375
            public Visibility getVisibility() {
376
                throw new IllegalStateException("Cannot resolve visibility of an illegal node");
×
377
            }
378
        }
379

380
        /**
381
         * A simple implementation of a resolved node of a method without bridges.
382
         */
383
        @HashCodeAndEqualsPlugin.Enhance
384
        class Simple implements Node {
385

386
            /**
387
             * The represented method.
388
             */
389
            private final MethodDescription methodDescription;
390

391
            /**
392
             * Creates a simple node.
393
             *
394
             * @param methodDescription The represented method.
395
             */
396
            public Simple(MethodDescription methodDescription) {
1✔
397
                this.methodDescription = methodDescription;
1✔
398
            }
1✔
399

400
            /**
401
             * {@inheritDoc}
402
             */
403
            public Sort getSort() {
404
                return Sort.RESOLVED;
1✔
405
            }
406

407
            /**
408
             * {@inheritDoc}
409
             */
410
            public MethodDescription getRepresentative() {
411
                return methodDescription;
1✔
412
            }
413

414
            /**
415
             * {@inheritDoc}
416
             */
417
            public Set<MethodDescription.TypeToken> getMethodTypes() {
418
                return Collections.emptySet();
1✔
419
            }
420

421
            /**
422
             * {@inheritDoc}
423
             */
424
            public Visibility getVisibility() {
425
                return methodDescription.getVisibility();
1✔
426
            }
427
        }
428
    }
429

430
    /**
431
     * A compiler to produce a {@link MethodGraph} from a given type.
432
     */
433
    @SuppressFBWarnings(value = "IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION", justification = "Safe initialization is implied.")
434
    interface Compiler {
435

436
        /**
437
         * The default compiler for compiling Java methods.
438
         */
439
        Compiler DEFAULT = MethodGraph.Compiler.Default.forJavaHierarchy();
1✔
440

441
        /**
442
         * Compiles the given type into a method graph considering the type to be the viewpoint.
443
         *
444
         * @param typeDefinition The type to be compiled.
445
         * @return A linked method graph representing the given type.
446
         */
447
        MethodGraph.Linked compile(TypeDefinition typeDefinition);
448

449
        /**
450
         * Compiles the given type into a method graph considering the type to be the viewpoint.
451
         *
452
         * @param typeDescription The type to be compiled.
453
         * @return A linked method graph representing the given type.
454
         * @deprecated Use {@link MethodGraph.Compiler#compile(TypeDefinition)}.
455
         */
456
        @Deprecated
457
        MethodGraph.Linked compile(TypeDescription typeDescription);
458

459
        /**
460
         * Compiles the given type into a method graph.
461
         *
462
         * @param typeDefinition The type to be compiled.
463
         * @param viewPoint      The view point that determines the method's visibility.
464
         * @return A linked method graph representing the given type.
465
         */
466
        MethodGraph.Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint);
467

468
        /**
469
         * Compiles the given type into a method graph.
470
         *
471
         * @param typeDefinition The type to be compiled.
472
         * @param viewPoint      The view point that determines the method's visibility.
473
         * @return A linked method graph representing the given type.
474
         * @deprecated Use {@link MethodGraph.Compiler#compile(TypeDefinition, TypeDescription)}.
475
         */
476
        @Deprecated
477
        MethodGraph.Linked compile(TypeDescription typeDefinition, TypeDescription viewPoint);
478

479
        /**
480
         * A flat compiler that simply returns the methods that are declared by the instrumented type.
481
         */
482
        enum ForDeclaredMethods implements Compiler {
1✔
483

484
            /**
485
             * The singleton instance.
486
             */
487
            INSTANCE;
1✔
488

489
            /**
490
             * {@inheritDoc}
491
             */
492
            public Linked compile(TypeDefinition typeDefinition) {
493
                return compile(typeDefinition, typeDefinition.asErasure());
1✔
494
            }
495

496
            /**
497
             * {@inheritDoc}
498
             */
499
            @Deprecated
500
            public Linked compile(TypeDescription typeDescription) {
501
                return compile((TypeDefinition) typeDescription, typeDescription);
×
502
            }
503

504
            /**
505
             * {@inheritDoc}
506
             */
507
            public Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
508
                LinkedHashMap<MethodDescription.SignatureToken, Node> nodes = new LinkedHashMap<MethodDescription.SignatureToken, Node>();
1✔
509
                for (MethodDescription methodDescription : typeDefinition.getDeclaredMethods().filter(isVirtual().and(not(isBridge())).and(isVisibleTo(viewPoint)))) {
1✔
510
                    nodes.put(methodDescription.asSignatureToken(), new Node.Simple(methodDescription));
1✔
511
                }
1✔
512
                return new Linked.Delegation(new MethodGraph.Simple(nodes), Empty.INSTANCE, Collections.<TypeDescription, MethodGraph>emptyMap());
1✔
513
            }
514

515
            /**
516
             * {@inheritDoc}
517
             */
518
            @Deprecated
519
            public Linked compile(TypeDescription typeDefinition, TypeDescription viewPoint) {
520
                return compile((TypeDefinition) typeDefinition, viewPoint);
×
521
            }
522
        }
523

524
        /**
525
         * An abstract base implementation of a method graph compiler.
526
         */
527
        abstract class AbstractBase implements Compiler {
1✔
528

529
            /**
530
             * {@inheritDoc}
531
             */
532
            public Linked compile(TypeDefinition typeDefinition) {
533
                return compile(typeDefinition, typeDefinition.asErasure());
1✔
534
            }
535

536
            /**
537
             * {@inheritDoc}
538
             */
539
            @Deprecated
540
            public Linked compile(TypeDescription typeDescription) {
541
                return compile((TypeDefinition) typeDescription, typeDescription);
×
542
            }
543

544
            /**
545
             * {@inheritDoc}
546
             */
547
            @Deprecated
548
            public Linked compile(TypeDescription typeDefinition, TypeDescription viewPoint) {
549
                return compile((TypeDefinition) typeDefinition, viewPoint);
×
550
            }
551
        }
552

553
        /**
554
         * A default implementation of a method graph.
555
         *
556
         * @param <T> The type of the harmonizer token to be used for linking methods of different types.
557
         */
558
        @HashCodeAndEqualsPlugin.Enhance
559
        class Default<T> extends AbstractBase {
560

561
            /**
562
             * The harmonizer to be used.
563
             */
564
            private final Harmonizer<T> harmonizer;
565

566
            /**
567
             * The merger to be used.
568
             */
569
            private final Merger merger;
570

571
            /**
572
             * A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
573
             */
574
            private final TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor;
575

576
            /**
577
             * A matcher to filter methods from the graph.
578
             */
579
            private final ElementMatcher<? super MethodDescription> matcher;
580

581
            /**
582
             * Creates a new default method graph compiler.
583
             *
584
             * @param harmonizer The harmonizer to be used.
585
             * @param merger     The merger to be used.
586
             * @param visitor    A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
587
             */
588
            protected Default(Harmonizer<T> harmonizer, Merger merger, TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
589
                this(harmonizer, merger, visitor, any());
1✔
590
            }
1✔
591

592
            /**
593
             * Creates a new default method graph compiler.
594
             *
595
             * @param harmonizer The harmonizer to be used.
596
             * @param merger     The merger to be used.
597
             * @param visitor    A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
598
             * @param matcher    A matcher to filter methods from the graph.
599
             */
600
            public Default(Harmonizer<T> harmonizer, Merger merger, TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor, ElementMatcher<? super MethodDescription> matcher) {
1✔
601
                this.harmonizer = harmonizer;
1✔
602
                this.merger = merger;
1✔
603
                this.visitor = visitor;
1✔
604
                this.matcher = matcher;
1✔
605
            }
1✔
606

607
            /**
608
             * Creates a default compiler using the given harmonizer and merger. All raw types are reified before analyzing their properties.
609
             *
610
             * @param harmonizer The harmonizer to be used for creating tokens that uniquely identify a method hierarchy.
611
             * @param merger     The merger to be used for identifying a method to represent an ambiguous method resolution.
612
             * @param <S>        The type of the harmonizer token.
613
             * @return A default compiler for the given harmonizer and merger.
614
             */
615
            public static <S> Compiler of(Harmonizer<S> harmonizer, Merger merger) {
616
                return new Default<S>(harmonizer, merger, TypeDescription.Generic.Visitor.Reifying.INITIATING);
1✔
617
            }
618

619
            /**
620
             * Creates a default compiler using the given harmonizer and merger. All raw types are reified before analyzing their properties.
621
             *
622
             * @param harmonizer The harmonizer to be used for creating tokens that uniquely identify a method hierarchy.
623
             * @param merger     The merger to be used for identifying a method to represent an ambiguous method resolution.
624
             * @param matcher    A matcher to filter methods from the graph.
625
             * @param <S>        The type of the harmonizer token.
626
             * @return A default compiler for the given harmonizer and merger.
627
             */
628
            public static <S> Compiler of(Harmonizer<S> harmonizer, Merger merger, ElementMatcher<? super MethodDescription> matcher) {
629
                return new Default<S>(harmonizer, merger, TypeDescription.Generic.Visitor.Reifying.INITIATING, matcher);
×
630
            }
631

632
            /**
633
             * Creates a default compiler using the given harmonizer and merger.
634
             *
635
             * @param harmonizer The harmonizer to be used for creating tokens that uniquely identify a method hierarchy.
636
             * @param merger     The merger to be used for identifying a method to represent an ambiguous method resolution.
637
             * @param visitor    A visitor to apply to all type descriptions before analyzing their methods or resolving super types.
638
             * @param <S>        The type of the harmonizer token.
639
             * @return A default compiler for the given harmonizer and merger.
640
             */
641
            public static <S> Compiler of(Harmonizer<S> harmonizer, Merger merger, TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
642
                return new Default<S>(harmonizer, merger, visitor);
×
643
            }
644

645
            /**
646
             * <p>
647
             * Creates a default compiler for a method hierarchy following the rules of the Java programming language. According
648
             * to these rules, two methods of the same name are only different if their parameter types represent different raw
649
             * types. The return type is not considered as a part of the signature.
650
             * </p>
651
             * <p>
652
             * Ambiguous methods are merged by considering the method that was discovered first.
653
             * </p>
654
             *
655
             * @return A compiler for resolving a method hierarchy following the rules of the Java programming language.
656
             */
657
            public static Compiler forJavaHierarchy() {
658
                return of(Harmonizer.ForJavaMethod.INSTANCE, Merger.Directional.LEFT);
1✔
659
            }
660

661
            /**
662
             * <p>
663
             * Creates a default compiler for a method hierarchy following the rules of the Java virtual machine. According
664
             * to these rules, two methods of the same name are different if their parameter types and return types represent
665
             * different type erasures.
666
             * </p>
667
             * <p>
668
             * Ambiguous methods are merged by considering the method that was discovered first.
669
             * </p>
670
             *
671
             * @return A compiler for resolving a method hierarchy following the rules of the Java programming language.
672
             */
673
            public static Compiler forJVMHierarchy() {
674
                return of(Harmonizer.ForJVMMethod.INSTANCE, Merger.Directional.LEFT);
1✔
675
            }
676

677
            /**
678
             * {@inheritDoc}
679
             */
680
            public MethodGraph.Linked compile(TypeDefinition typeDefinition, TypeDescription viewPoint) {
681
                Map<TypeDefinition, Key.Store<T>> snapshots = new HashMap<TypeDefinition, Key.Store<T>>();
1✔
682
                Key.Store<?> rootStore = doAnalyze(typeDefinition, snapshots, isVirtual().and(isVisibleTo(viewPoint)).and(matcher));
1✔
683
                TypeDescription.Generic superClass = typeDefinition.getSuperClass();
1✔
684
                List<TypeDescription.Generic> interfaceTypes = typeDefinition.getInterfaces();
1✔
685
                Map<TypeDescription, MethodGraph> interfaceGraphs = new HashMap<TypeDescription, MethodGraph>();
1✔
686
                for (TypeDescription.Generic interfaceType : interfaceTypes) {
1✔
687
                    Key.Store<T> store = snapshots.get(interfaceType);
1✔
688
                    if (store == null) {
1✔
689
                        throw new IllegalStateException("Failed to resolve interface type " + interfaceType + " from " + snapshots.keySet());
×
690
                    }
691
                    interfaceGraphs.put(interfaceType.asErasure(), store.asGraph(merger));
1✔
692
                }
1✔
693
                Key.Store<T> store;
694
                if (superClass == null) {
1✔
695
                    store = null;
1✔
696
                } else {
697
                    store = snapshots.get(superClass);
1✔
698
                    if (store == null) {
1✔
699
                        throw new IllegalStateException("Failed to resolve super class " + superClass + " from " + snapshots.keySet());
×
700
                    }
701
                }
702
                return new Linked.Delegation(rootStore.asGraph(merger),
1✔
703
                        store == null
704
                                ? Empty.INSTANCE
705
                                : store.asGraph(merger),
1✔
706
                        interfaceGraphs);
707
            }
708

709
            /**
710
             * Analyzes the given type description without checking if the end of the type hierarchy was reached.
711
             *
712
             * @param typeDefinition   The type to analyze.
713
             * @param key              The type in its original form before applying the visitor.
714
             * @param snapshots        A map containing snapshots of key stores for previously analyzed types.
715
             * @param relevanceMatcher A matcher for filtering methods that should be included in the graph.
716
             * @return A key store describing the provided type.
717
             */
718
            protected Key.Store<T> analyze(TypeDefinition typeDefinition,
719
                                           TypeDefinition key,
720
                                           Map<TypeDefinition, Key.Store<T>> snapshots,
721
                                           ElementMatcher<? super MethodDescription> relevanceMatcher) {
722
                Key.Store<T> store = snapshots.get(key);
1✔
723
                if (store == null) {
1✔
724
                    store = doAnalyze(typeDefinition, snapshots, relevanceMatcher);
1✔
725
                    snapshots.put(key, store);
1✔
726
                }
727
                return store;
1✔
728
            }
729

730
            /**
731
             * Analyzes the given type description.
732
             *
733
             * @param typeDescription  The type to analyze.
734
             * @param snapshots        A map containing snapshots of key stores for previously analyzed types.
735
             * @param relevanceMatcher A matcher for filtering methods that should be included in the graph.
736
             * @return A key store describing the provided type.
737
             */
738
            protected Key.Store<T> analyzeNullable(@MaybeNull TypeDescription.Generic typeDescription,
739
                                                   Map<TypeDefinition, Key.Store<T>> snapshots,
740
                                                   ElementMatcher<? super MethodDescription> relevanceMatcher) {
741
                return typeDescription == null
1✔
742
                        ? new Key.Store<T>()
743
                        : analyze(typeDescription.accept(visitor), typeDescription, snapshots, relevanceMatcher);
1✔
744
            }
745

746
            /**
747
             * Analyzes the given type description without checking if it is already presented in the key store.
748
             *
749
             * @param typeDefinition   The type to analyze.
750
             * @param snapshots        A map containing snapshots of key stores for previously analyzed types.
751
             * @param relevanceMatcher A matcher for filtering methods that should be included in the graph.
752
             * @return A key store describing the provided type.
753
             */
754
            protected Key.Store<T> doAnalyze(TypeDefinition typeDefinition,
755
                                             Map<TypeDefinition, Key.Store<T>> snapshots,
756
                                             ElementMatcher<? super MethodDescription> relevanceMatcher) {
757
                Key.Store<T> store = analyzeNullable(typeDefinition.getSuperClass(), snapshots, relevanceMatcher);
1✔
758
                Key.Store<T> interfaceStore = new Key.Store<T>();
1✔
759
                for (TypeDescription.Generic interfaceType : typeDefinition.getInterfaces()) {
1✔
760
                    interfaceStore = interfaceStore.combineWith(analyze(interfaceType.accept(visitor), interfaceType, snapshots, relevanceMatcher));
1✔
761
                }
1✔
762
                return store.inject(interfaceStore).registerTopLevel(typeDefinition.getDeclaredMethods().filter(relevanceMatcher), harmonizer);
1✔
763
            }
764

765
            /**
766
             * A harmonizer is responsible for creating a token that identifies a method's relevant attributes for considering
767
             * two methods of being equal or not.
768
             *
769
             * @param <S> The type of the token that is created by the implementing harmonizer.
770
             */
771
            public interface Harmonizer<S> {
772

773
                /**
774
                 * Harmonizes the given type token.
775
                 *
776
                 * @param typeToken The type token to harmonize.
777
                 * @return A token representing the given type token.
778
                 */
779
                S harmonize(MethodDescription.TypeToken typeToken);
780

781
                /**
782
                 * A harmonizer for the Java programming language that identifies a method by its parameter types only.
783
                 */
784
                enum ForJavaMethod implements Harmonizer<ForJavaMethod.Token> {
1✔
785

786
                    /**
787
                     * The singleton instance.
788
                     */
789
                    INSTANCE;
1✔
790

791
                    /**
792
                     * {@inheritDoc}
793
                     */
794
                    public Token harmonize(MethodDescription.TypeToken typeToken) {
795
                        return new Token(typeToken);
1✔
796
                    }
797

798
                    /**
799
                     * A token that identifies a Java method's type by its parameter types only.
800
                     */
801
                    protected static class Token {
802

803
                        /**
804
                         * The represented type token.
805
                         */
806
                        private final MethodDescription.TypeToken typeToken;
807

808
                        /**
809
                         * The hash code of this token which is precomputed for to improve performance.
810
                         */
811
                        private final int hashCode;
812

813
                        /**
814
                         * Creates a new type token for a Java method.
815
                         *
816
                         * @param typeToken The represented type token.
817
                         */
818
                        protected Token(MethodDescription.TypeToken typeToken) {
1✔
819
                            this.typeToken = typeToken;
1✔
820
                            hashCode = typeToken.getParameterTypes().hashCode();
1✔
821
                        }
1✔
822

823
                        @Override
824
                        public int hashCode() {
825
                            return hashCode;
1✔
826
                        }
827

828
                        @Override
829
                        public boolean equals(@MaybeNull Object other) {
830
                            return this == other || other instanceof Token && typeToken.getParameterTypes().equals(((Token) other).typeToken.getParameterTypes());
1✔
831
                        }
832

833
                        @Override
834
                        public String toString() {
835
                            return typeToken.getParameterTypes().toString();
×
836
                        }
837
                    }
838
                }
839

840
                /**
841
                 * A harmonizer for the Java virtual machine's method dispatching rules that identifies a method by its parameter types and return type.
842
                 */
843
                enum ForJVMMethod implements Harmonizer<ForJVMMethod.Token> {
1✔
844

845
                    /**
846
                     * The singleton instance.
847
                     */
848
                    INSTANCE;
1✔
849

850
                    /**
851
                     * {@inheritDoc}
852
                     */
853
                    public Token harmonize(MethodDescription.TypeToken typeToken) {
854
                        return new Token(typeToken);
1✔
855
                    }
856

857
                    /**
858
                     * A token that identifies a Java method's type by its parameter types and return type.
859
                     */
860
                    protected static class Token {
861

862
                        /**
863
                         * The represented type token.
864
                         */
865
                        private final MethodDescription.TypeToken typeToken;
866

867
                        /**
868
                         * The hash code of this token which is precomputed for to improve performance.
869
                         */
870
                        private final int hashCode;
871

872
                        /**
873
                         * Creates a new type token for a JVM method.
874
                         *
875
                         * @param typeToken The represented type token.
876
                         */
877
                        public Token(MethodDescription.TypeToken typeToken) {
1✔
878
                            this.typeToken = typeToken;
1✔
879
                            hashCode = typeToken.getReturnType().hashCode() + 31 * typeToken.getParameterTypes().hashCode();
1✔
880
                        }
1✔
881

882
                        @Override
883
                        public int hashCode() {
884
                            return hashCode;
1✔
885
                        }
886

887
                        @Override
888
                        public boolean equals(@MaybeNull Object other) {
889
                            if (this == other) {
1✔
890
                                return true;
×
891
                            } else if (!(other instanceof Token)) {
1✔
892
                                return false;
×
893
                            }
894
                            Token token = (Token) other;
1✔
895
                            return typeToken.getReturnType().equals(token.typeToken.getReturnType())
1✔
896
                                    && typeToken.getParameterTypes().equals(token.typeToken.getParameterTypes());
1✔
897
                        }
898

899
                        @Override
900
                        public String toString() {
901
                            return typeToken.toString();
×
902
                        }
903
                    }
904
                }
905
            }
906

907
            /**
908
             * Implementations are responsible for identifying a representative method for a {@link net.bytebuddy.dynamic.scaffold.MethodGraph.Node}
909
             * between several ambiguously resolved methods.
910
             */
911
            public interface Merger {
912

913
                /**
914
                 * Merges two ambiguously resolved methods to yield a single representative.
915
                 *
916
                 * @param left  The left method description, i.e. the method that was discovered first or was previously merged.
917
                 * @param right The right method description, i.e. the method that was discovered last.
918
                 * @return A method description compatible to both method's types that is used as a representative.
919
                 */
920
                MethodDescription merge(MethodDescription left, MethodDescription right);
921

922
                /**
923
                 * A directional merger that always returns either the left or right method description.
924
                 */
925
                enum Directional implements Merger {
1✔
926

927
                    /**
928
                     * A merger that always returns the left method, i.e. the method that was discovered first or was previously merged.
929
                     */
930
                    LEFT(true),
1✔
931

932
                    /**
933
                     * A merger that always returns the right method, i.e. the method that was discovered last.
934
                     */
935
                    RIGHT(false);
1✔
936

937
                    /**
938
                     * {@code true} if the left method should be returned when merging methods.
939
                     */
940
                    private final boolean left;
941

942
                    /**
943
                     * Creates a directional merger.
944
                     *
945
                     * @param left {@code true} if the left method should be returned when merging methods.
946
                     */
947
                    Directional(boolean left) {
1✔
948
                        this.left = left;
1✔
949
                    }
1✔
950

951
                    /**
952
                     * {@inheritDoc}
953
                     */
954
                    public MethodDescription merge(MethodDescription left, MethodDescription right) {
955
                        return this.left
1✔
956
                                ? left
957
                                : right;
958
                    }
959
                }
960
            }
961

962
            /**
963
             * A key represents a collection of methods within a method graph to later yield a node representing a collection of methods,
964
             * i.e. a method representative including information on the required method bridges.
965
             *
966
             * @param <S> The type of the token used for deciding on method equality.
967
             */
968
            protected abstract static class Key<S> {
969

970
                /**
971
                 * The internal name of the method this key identifies.
972
                 */
973
                protected final String internalName;
974

975
                /**
976
                 * The number of method parameters of the method this key identifies.
977
                 */
978
                protected final int parameterCount;
979

980
                /**
981
                 * Creates a new key.
982
                 *
983
                 * @param internalName   The internal name of the method this key identifies.
984
                 * @param parameterCount The number of method parameters of the method this key identifies.
985
                 */
986
                protected Key(String internalName, int parameterCount) {
1✔
987
                    this.internalName = internalName;
1✔
988
                    this.parameterCount = parameterCount;
1✔
989
                }
1✔
990

991
                /**
992
                 * Returns a set of all identifiers of this key.
993
                 *
994
                 * @return A set of all identifiers of this key.
995
                 */
996
                protected abstract Set<S> getIdentifiers();
997

998
                @Override
999
                public int hashCode() {
1000
                    return internalName.hashCode() + 31 * parameterCount;
1✔
1001
                }
1002

1003
                @Override
1004
                public boolean equals(@MaybeNull Object other) {
1005
                    if (this == other) {
1✔
1006
                        return true;
×
1007
                    } else if (!(other instanceof Key)) {
1✔
1008
                        return false;
×
1009
                    }
1010
                    Key<?> key = (Key<?>) other;
1✔
1011
                    return internalName.equals(key.internalName)
1✔
1012
                            && parameterCount == key.parameterCount
1013
                            && !Collections.disjoint(getIdentifiers(), key.getIdentifiers());
1✔
1014
                }
1015

1016
                /**
1017
                 * A harmonized key represents a key where equality is decided based on tokens that are returned by a
1018
                 * {@link net.bytebuddy.dynamic.scaffold.MethodGraph.Compiler.Default.Harmonizer}.
1019
                 *
1020
                 * @param <V> The type of the tokens yielded by a harmonizer.
1021
                 */
1022
                protected static class Harmonized<V> extends Key<V> {
1023

1024
                    /**
1025
                     * A mapping of identifiers to the type tokens they represent.
1026
                     */
1027
                    private final Map<V, Set<MethodDescription.TypeToken>> identifiers;
1028

1029
                    /**
1030
                     * Creates a new harmonized key.
1031
                     *
1032
                     * @param internalName   The internal name of the method this key identifies.
1033
                     * @param parameterCount The number of method parameters of the method this key identifies.
1034
                     * @param identifiers    A mapping of identifiers to the type tokens they represent.
1035
                     */
1036
                    protected Harmonized(String internalName, int parameterCount, Map<V, Set<MethodDescription.TypeToken>> identifiers) {
1037
                        super(internalName, parameterCount);
1✔
1038
                        this.identifiers = identifiers;
1✔
1039
                    }
1✔
1040

1041
                    /**
1042
                     * Creates a new harmonized key for the given method description.
1043
                     *
1044
                     * @param methodDescription The method description to represent as a harmonized key.
1045
                     * @param harmonizer        The harmonizer to use.
1046
                     * @param <Q>               The type of the token yielded by a harmonizer.
1047
                     * @return A harmonized key representing the provided method.
1048
                     */
1049
                    protected static <Q> Harmonized<Q> of(MethodDescription methodDescription, Harmonizer<Q> harmonizer) {
1050
                        MethodDescription.TypeToken typeToken = methodDescription.asTypeToken();
1✔
1051
                        return new Harmonized<Q>(methodDescription.getInternalName(),
1✔
1052
                                methodDescription.getParameters().size(),
1✔
1053
                                Collections.singletonMap(harmonizer.harmonize(typeToken), Collections.<MethodDescription.TypeToken>emptySet()));
1✔
1054
                    }
1055

1056
                    /**
1057
                     * Creates a detached version of this key.
1058
                     *
1059
                     * @param typeToken The type token of the representative method.
1060
                     * @return The detached version of this key.
1061
                     */
1062
                    protected Detached detach(MethodDescription.TypeToken typeToken) {
1063
                        Set<MethodDescription.TypeToken> identifiers = new HashSet<MethodDescription.TypeToken>();
1✔
1064
                        for (Set<MethodDescription.TypeToken> typeTokens : this.identifiers.values()) {
1✔
1065
                            identifiers.addAll(typeTokens);
1✔
1066
                        }
1✔
1067
                        identifiers.add(typeToken);
1✔
1068
                        return new Detached(internalName, parameterCount, identifiers);
1✔
1069
                    }
1070

1071
                    /**
1072
                     * Combines this key with the given key.
1073
                     *
1074
                     * @param key The key to be merged with this key.
1075
                     * @return A harmonized key representing the merger of this key and the given key.
1076
                     */
1077
                    protected Harmonized<V> combineWith(Harmonized<V> key) {
1078
                        Map<V, Set<MethodDescription.TypeToken>> identifiers = new HashMap<V, Set<MethodDescription.TypeToken>>(this.identifiers);
1✔
1079
                        for (Map.Entry<V, Set<MethodDescription.TypeToken>> entry : key.identifiers.entrySet()) {
1✔
1080
                            Set<MethodDescription.TypeToken> typeTokens = identifiers.get(entry.getKey());
1✔
1081
                            if (typeTokens == null) {
1✔
1082
                                identifiers.put(entry.getKey(), entry.getValue());
×
1083
                            } else {
1084
                                typeTokens = new HashSet<MethodDescription.TypeToken>(typeTokens);
1✔
1085
                                typeTokens.addAll(entry.getValue());
1✔
1086
                                identifiers.put(entry.getKey(), typeTokens);
1✔
1087
                            }
1088
                        }
1✔
1089
                        return new Harmonized<V>(internalName, parameterCount, identifiers);
1✔
1090
                    }
1091

1092
                    /**
1093
                     * Extends this key by the given method description.
1094
                     *
1095
                     * @param methodDescription The method to extend this key with.
1096
                     * @param harmonizer        The harmonizer to use for determining method equality.
1097
                     * @return The harmonized key representing the extension of this key with the provided method.
1098
                     */
1099
                    protected Harmonized<V> extend(MethodDescription.InDefinedShape methodDescription, Harmonizer<V> harmonizer) {
1100
                        Map<V, Set<MethodDescription.TypeToken>> identifiers = new HashMap<V, Set<MethodDescription.TypeToken>>(this.identifiers);
1✔
1101
                        MethodDescription.TypeToken typeToken = methodDescription.asTypeToken();
1✔
1102
                        V identifier = harmonizer.harmonize(typeToken);
1✔
1103
                        Set<MethodDescription.TypeToken> typeTokens = identifiers.get(identifier);
1✔
1104
                        if (typeTokens == null) {
1✔
1105
                            identifiers.put(identifier, Collections.singleton(typeToken));
1✔
1106
                        } else {
1107
                            typeTokens = new HashSet<MethodDescription.TypeToken>(typeTokens);
1✔
1108
                            typeTokens.add(typeToken);
1✔
1109
                            identifiers.put(identifier, typeTokens);
1✔
1110
                        }
1111
                        return new Harmonized<V>(internalName, parameterCount, identifiers);
1✔
1112
                    }
1113

1114
                    @Override
1115
                    protected Set<V> getIdentifiers() {
1116
                        return identifiers.keySet();
1✔
1117
                    }
1118
                }
1119

1120
                /**
1121
                 * A detached version of a key that identifies methods by their JVM signature, i.e. parameter types and return type.
1122
                 */
1123
                protected static class Detached extends Key<MethodDescription.TypeToken> {
1124

1125
                    /**
1126
                     * The type tokens represented by this key.
1127
                     */
1128
                    private final Set<MethodDescription.TypeToken> identifiers;
1129

1130
                    /**
1131
                     * Creates a new detached key.
1132
                     *
1133
                     * @param internalName   The internal name of the method this key identifies.
1134
                     * @param parameterCount The number of method parameters of the method this key identifies.
1135
                     * @param identifiers    The type tokens represented by this key.
1136
                     */
1137
                    protected Detached(String internalName, int parameterCount, Set<MethodDescription.TypeToken> identifiers) {
1138
                        super(internalName, parameterCount);
1✔
1139
                        this.identifiers = identifiers;
1✔
1140
                    }
1✔
1141

1142
                    /**
1143
                     * Creates a new detached key of the given method token.
1144
                     *
1145
                     * @param token The method token to represent as a key.
1146
                     * @return A detached key representing the given method token..
1147
                     */
1148
                    protected static Detached of(MethodDescription.SignatureToken token) {
1149
                        return new Detached(token.getName(), token.getParameterTypes().size(), Collections.singleton(token.asTypeToken()));
1✔
1150
                    }
1151

1152
                    @Override
1153
                    protected Set<MethodDescription.TypeToken> getIdentifiers() {
1154
                        return identifiers;
1✔
1155
                    }
1156
                }
1157

1158
                /**
1159
                 * A store for collected methods that are identified by keys.
1160
                 *
1161
                 * @param <V> The type of the token used for deciding on method equality.
1162
                 */
1163
                @HashCodeAndEqualsPlugin.Enhance
1164
                protected static class Store<V> {
1165

1166
                    /**
1167
                     * A mapping of harmonized keys to their represented entry.
1168
                     */
1169
                    private final LinkedHashMap<Harmonized<V>, Entry<V>> entries;
1170

1171
                    /**
1172
                     * Creates an empty store.
1173
                     */
1174
                    protected Store() {
1175
                        this(new LinkedHashMap<Harmonized<V>, Entry<V>>());
1✔
1176
                    }
1✔
1177

1178
                    /**
1179
                     * Creates a new store representing the given entries.
1180
                     *
1181
                     * @param entries A mapping of harmonized keys to their represented entry.
1182
                     */
1183
                    private Store(LinkedHashMap<Harmonized<V>, Entry<V>> entries) {
1✔
1184
                        this.entries = entries;
1✔
1185
                    }
1✔
1186

1187
                    /**
1188
                     * Combines the two given stores.
1189
                     *
1190
                     * @param left  The left store to be combined.
1191
                     * @param right The right store to be combined.
1192
                     * @param <W>   The type of the harmonized key of both stores.
1193
                     * @return An entry representing the combination of both stores.
1194
                     */
1195
                    private static <W> Entry<W> combine(Entry<W> left, Entry<W> right) {
1196
                        Set<MethodDescription> leftMethods = left.getCandidates(), rightMethods = right.getCandidates();
1✔
1197
                        LinkedHashSet<MethodDescription> combined = new LinkedHashSet<MethodDescription>();
1✔
1198
                        combined.addAll(leftMethods);
1✔
1199
                        combined.addAll(rightMethods);
1✔
1200
                        for (MethodDescription leftMethod : leftMethods) {
1✔
1201
                            TypeDescription leftType = leftMethod.getDeclaringType().asErasure();
1✔
1202
                            for (MethodDescription rightMethod : rightMethods) {
1✔
1203
                                TypeDescription rightType = rightMethod.getDeclaringType().asErasure();
1✔
1204
                                if (leftType.equals(rightType)) {
1✔
1205
                                    break;
1✔
1206
                                } else if (leftType.isAssignableTo(rightType)) {
1✔
1207
                                    combined.remove(rightMethod);
1✔
1208
                                    break;
1✔
1209
                                } else if (leftType.isAssignableFrom(rightType)) {
1✔
1210
                                    combined.remove(leftMethod);
1✔
1211
                                    break;
1✔
1212
                                }
1213
                            }
1✔
1214
                        }
1✔
1215
                        Key.Harmonized<W> key = left.getKey().combineWith(right.getKey());
1✔
1216
                        Visibility visibility = left.getVisibility().expandTo(right.getVisibility());
1✔
1217
                        return combined.size() == 1
1✔
1218
                                ? new Entry.Resolved<W>(key, combined.iterator().next(), visibility, Entry.Resolved.NOT_MADE_VISIBLE)
1✔
1219
                                : new Entry.Ambiguous<W>(key, combined, visibility);
1220
                    }
1221

1222
                    /**
1223
                     * Registers a new top level method within this store.
1224
                     *
1225
                     * @param methodDescriptions The methods to register.
1226
                     * @param harmonizer         The harmonizer to use for determining method equality.
1227
                     * @return A store with the given method registered as a top-level method.
1228
                     */
1229
                    protected Store<V> registerTopLevel(List<? extends MethodDescription> methodDescriptions, Harmonizer<V> harmonizer) {
1230
                        if (methodDescriptions.isEmpty()) {
1✔
1231
                            return this;
1✔
1232
                        }
1233
                        LinkedHashMap<Harmonized<V>, Entry<V>> entries = new LinkedHashMap<Harmonized<V>, Entry<V>>(this.entries);
1✔
1234
                        for (MethodDescription methodDescription : methodDescriptions) {
1✔
1235
                            Harmonized<V> key = Harmonized.of(methodDescription, harmonizer);
1✔
1236
                            Entry<V> currentEntry = entries.remove(key), extendedEntry = (currentEntry == null
1✔
1237
                                    ? new Entry.Initial<V>(key)
1238
                                    : currentEntry).extendBy(methodDescription, harmonizer);
1✔
1239
                            entries.put(extendedEntry.getKey(), extendedEntry);
1✔
1240
                        }
1✔
1241
                        return new Store<V>(entries);
1✔
1242
                    }
1243

1244
                    /**
1245
                     * Combines this store with the given store.
1246
                     *
1247
                     * @param store The store to combine with this store.
1248
                     * @return A store representing a combination of this store and the given store.
1249
                     */
1250
                    protected Store<V> combineWith(Store<V> store) {
1251
                        if (entries.isEmpty()) {
1✔
1252
                            return store;
1✔
1253
                        } else if (store.entries.isEmpty()) {
1✔
1254
                            return this;
1✔
1255
                        }
1256
                        LinkedHashMap<Harmonized<V>, Entry<V>> entries = new LinkedHashMap<Harmonized<V>, Entry<V>>(this.entries);
1✔
1257
                        for (Entry<V> entry : store.entries.values()) {
1✔
1258
                            Entry<V> previousEntry = entries.remove(entry.getKey()), injectedEntry = previousEntry == null
1✔
1259
                                    ? entry
1260
                                    : combine(previousEntry, entry);
1✔
1261
                            entries.put(injectedEntry.getKey(), injectedEntry);
1✔
1262
                        }
1✔
1263
                        return new Store<V>(entries);
1✔
1264
                    }
1265

1266
                    /**
1267
                     * Injects the given store into this store.
1268
                     *
1269
                     * @param store The key store to inject into this store.
1270
                     * @return A store that represents this store with the given store injected.
1271
                     */
1272
                    protected Store<V> inject(Store<V> store) {
1273
                        if (entries.isEmpty()) {
1✔
1274
                            return store;
1✔
1275
                        } else if (store.entries.isEmpty()) {
1✔
1276
                            return this;
1✔
1277
                        }
1278
                        LinkedHashMap<Harmonized<V>, Entry<V>> entries = new LinkedHashMap<Harmonized<V>, Entry<V>>(this.entries);
1✔
1279
                        for (Entry<V> entry : store.entries.values()) {
1✔
1280
                            Entry<V> previous = entries.remove(entry.getKey()), injectedEntry = previous == null
1✔
1281
                                    ? entry
1282
                                    : previous.inject(entry);
1✔
1283
                            entries.put(injectedEntry.getKey(), injectedEntry);
1✔
1284
                        }
1✔
1285
                        return new Store<V>(entries);
1✔
1286
                    }
1287

1288
                    /**
1289
                     * Transforms this store into a method graph by applying the given merger.
1290
                     *
1291
                     * @param merger The merger to apply for resolving the representative for ambiguous resolutions.
1292
                     * @return The method graph that represents this key store.
1293
                     */
1294
                    protected MethodGraph asGraph(Merger merger) {
1295
                        LinkedHashMap<Key<MethodDescription.TypeToken>, Node> entries = new LinkedHashMap<Key<MethodDescription.TypeToken>, Node>();
1✔
1296
                        for (Entry<V> entry : this.entries.values()) {
1✔
1297
                            Node node = entry.asNode(merger);
1✔
1298
                            entries.put(entry.getKey().detach(node.getRepresentative().asTypeToken()), node);
1✔
1299
                        }
1✔
1300
                        return new Graph(entries);
1✔
1301
                    }
1302

1303
                    /**
1304
                     * An entry of a key store.
1305
                     *
1306
                     * @param <W> The type of the harmonized token used for determining method equality.
1307
                     */
1308
                    protected interface Entry<W> {
1309

1310
                        /**
1311
                         * Returns the harmonized key of this entry.
1312
                         *
1313
                         * @return The harmonized key of this entry.
1314
                         */
1315
                        Harmonized<W> getKey();
1316

1317
                        /**
1318
                         * Returns all candidate methods represented by this entry.
1319
                         *
1320
                         * @return All candidate methods represented by this entry.
1321
                         */
1322
                        Set<MethodDescription> getCandidates();
1323

1324
                        /**
1325
                         * Returns the minimal visibility of this entry.
1326
                         *
1327
                         * @return The minimal visibility of this entry.
1328
                         */
1329
                        Visibility getVisibility();
1330

1331
                        /**
1332
                         * Extends this entry by the given method.
1333
                         *
1334
                         * @param methodDescription The method description to extend this entry with.
1335
                         * @param harmonizer        The harmonizer to use for determining method equality.
1336
                         * @return This key extended by the given method.
1337
                         */
1338
                        Entry<W> extendBy(MethodDescription methodDescription, Harmonizer<W> harmonizer);
1339

1340
                        /**
1341
                         * Injects the given key into this entry.
1342
                         *
1343
                         * @param entry The entry to be combined.
1344
                         * @return This entry extended with the given key.
1345
                         */
1346
                        Entry<W> inject(Entry<W> entry);
1347

1348
                        /**
1349
                         * Transforms this entry into a node.
1350
                         *
1351
                         * @param merger The merger to use for determining the representative method of an ambiguous node.
1352
                         * @return The resolved node.
1353
                         */
1354
                        Node asNode(Merger merger);
1355

1356
                        /**
1357
                         * An entry in its initial state before registering any method as a representative.
1358
                         *
1359
                         * @param <U> The type of the harmonized key to determine method equality.
1360
                         */
1361
                        class Initial<U> implements Entry<U> {
1362

1363
                            /**
1364
                             * The harmonized key this entry represents.
1365
                             */
1366
                            private final Harmonized<U> key;
1367

1368
                            /**
1369
                             * Creates a new initial key.
1370
                             *
1371
                             * @param key The harmonized key this entry represents.
1372
                             */
1373
                            protected Initial(Harmonized<U> key) {
1✔
1374
                                this.key = key;
1✔
1375
                            }
1✔
1376

1377
                            /**
1378
                             * {@inheritDoc}
1379
                             */
1380
                            public Harmonized<U> getKey() {
1381
                                throw new IllegalStateException("Cannot extract key from initial entry:" + this);
1✔
1382
                            }
1383

1384
                            /**
1385
                             * {@inheritDoc}
1386
                             */
1387
                            public Set<MethodDescription> getCandidates() {
1388
                                throw new IllegalStateException("Cannot extract method from initial entry:" + this);
×
1389
                            }
1390

1391
                            /**
1392
                             * {@inheritDoc}
1393
                             */
1394
                            public Visibility getVisibility() {
1395
                                throw new IllegalStateException("Cannot extract visibility from initial entry:" + this);
×
1396
                            }
1397

1398
                            /**
1399
                             * {@inheritDoc}
1400
                             */
1401
                            public Entry<U> extendBy(MethodDescription methodDescription, Harmonizer<U> harmonizer) {
1402
                                return new Resolved<U>(key.extend(methodDescription.asDefined(), harmonizer),
1✔
1403
                                        methodDescription,
1404
                                        methodDescription.getVisibility(),
1✔
1405
                                        Resolved.NOT_MADE_VISIBLE);
1406
                            }
1407

1408
                            /**
1409
                             * {@inheritDoc}
1410
                             */
1411
                            public Entry<U> inject(Entry<U> entry) {
1412
                                throw new IllegalStateException("Cannot inject into initial entry without a registered method: " + this);
1✔
1413
                            }
1414

1415
                            /**
1416
                             * {@inheritDoc}
1417
                             */
1418
                            public Node asNode(Merger merger) {
1419
                                throw new IllegalStateException("Cannot transform initial entry without a registered method: " + this);
1✔
1420
                            }
1421

1422
                            @Override
1423
                            public int hashCode() {
1424
                                return key.hashCode();
1✔
1425
                            }
1426

1427
                            @Override
1428
                            public boolean equals(@MaybeNull Object other) {
1429
                                if (this == other) {
×
1430
                                    return true;
×
1431
                                } else if (other == null || getClass() != other.getClass()) {
×
1432
                                    return false;
×
1433
                                }
1434
                                Initial<?> initial = (Initial<?>) other;
×
1435
                                return key.equals(initial.key);
×
1436
                            }
1437
                        }
1438

1439
                        /**
1440
                         * An entry representing a non-ambiguous node resolution.
1441
                         *
1442
                         * @param <U> The type of the harmonized key to determine method equality.
1443
                         */
1444
                        @HashCodeAndEqualsPlugin.Enhance
1445
                        class Resolved<U> implements Entry<U> {
1446

1447
                            /**
1448
                             * Indicates that a type's methods are already globally visible, meaning that a bridge method is not added
1449
                             * with the intend of creating a visibility bridge.
1450
                             */
1451
                            private static final int MADE_VISIBLE = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED;
1452

1453
                            /**
1454
                             * Indicates that the entry was not made visible.
1455
                             */
1456
                            private static final boolean NOT_MADE_VISIBLE = false;
1457

1458
                            /**
1459
                             * The harmonized key this entry represents.
1460
                             */
1461
                            private final Harmonized<U> key;
1462

1463
                            /**
1464
                             * The non-ambiguous, representative method of this entry.
1465
                             */
1466
                            private final MethodDescription methodDescription;
1467

1468
                            /**
1469
                             * The minimal required visibility for this method.
1470
                             */
1471
                            private final Visibility visibility;
1472

1473
                            /**
1474
                             * {@code true} if this entry's representative was made visible by a visibility bridge.
1475
                             */
1476
                            private final boolean madeVisible;
1477

1478
                            /**
1479
                             * Creates a new resolved entry that is not made visible.
1480
                             *
1481
                             * @param key               The harmonized key this entry represents.
1482
                             * @param methodDescription The non-ambiguous, representative method of this entry.
1483
                             * @param visibility        The minimal required visibility for this method.
1484
                             */
1485
                            protected Resolved(Harmonized<U> key, MethodDescription methodDescription, Visibility visibility) {
1486
                                this(key, methodDescription, visibility, NOT_MADE_VISIBLE);
1✔
1487
                            }
1✔
1488

1489
                            /**
1490
                             * Creates a new resolved entry.
1491
                             *
1492
                             * @param key               The harmonized key this entry represents.
1493
                             * @param methodDescription The non-ambiguous, representative method of this entry.
1494
                             * @param visibility        The minimal required visibility for this method.
1495
                             * @param madeVisible       {@code true} if this entry's representative was made visible by a visibility bridge.
1496
                             */
1497
                            protected Resolved(Harmonized<U> key, MethodDescription methodDescription, Visibility visibility, boolean madeVisible) {
1✔
1498
                                this.key = key;
1✔
1499
                                this.methodDescription = methodDescription;
1✔
1500
                                this.visibility = visibility;
1✔
1501
                                this.madeVisible = madeVisible;
1✔
1502
                            }
1✔
1503

1504
                            /**
1505
                             * Creates an entry for an override where a method overrides another method within a super class.
1506
                             *
1507
                             * @param key        The merged key for both methods.
1508
                             * @param override   The method declared by the extending type, potentially a bridge method.
1509
                             * @param original   The method that is overridden by the extending type.
1510
                             * @param visibility The minimal required visibility for this entry.
1511
                             * @param <V>        The type of the harmonized key to determine method equality.
1512
                             * @return An entry representing the merger of both methods.
1513
                             */
1514
                            private static <V> Entry<V> of(Harmonized<V> key, MethodDescription override, MethodDescription original, Visibility visibility) {
1515
                                visibility = visibility.expandTo(original.getVisibility()).expandTo(override.getVisibility());
1✔
1516
                                return override.isBridge()
1✔
1517
                                        ? new Resolved<V>(key, original, visibility, (original.getDeclaringType().getModifiers() & MADE_VISIBLE) == 0)
1✔
1518
                                        : new Resolved<V>(key, override, visibility, NOT_MADE_VISIBLE);
1519
                            }
1520

1521
                            /**
1522
                             * {@inheritDoc}
1523
                             */
1524
                            public Harmonized<U> getKey() {
1525
                                return key;
1✔
1526
                            }
1527

1528
                            /**
1529
                             * {@inheritDoc}
1530
                             */
1531
                            public Set<MethodDescription> getCandidates() {
1532
                                return Collections.singleton(methodDescription);
1✔
1533
                            }
1534

1535
                            /**
1536
                             * {@inheritDoc}
1537
                             */
1538
                            public Visibility getVisibility() {
1539
                                return visibility;
1✔
1540
                            }
1541

1542
                            /**
1543
                             * {@inheritDoc}
1544
                             */
1545
                            public Entry<U> extendBy(MethodDescription methodDescription, Harmonizer<U> harmonizer) {
1546
                                Harmonized<U> key = this.key.extend(methodDescription.asDefined(), harmonizer);
1✔
1547
                                Visibility visibility = this.visibility.expandTo(methodDescription.getVisibility());
1✔
1548
                                return methodDescription.getDeclaringType().equals(this.methodDescription.getDeclaringType())
1✔
1549
                                        ? Ambiguous.of(key, methodDescription, this.methodDescription, visibility)
1✔
1550
                                        : Resolved.of(key, methodDescription, this.methodDescription, visibility);
1✔
1551
                            }
1552

1553
                            /**
1554
                             * {@inheritDoc}
1555
                             */
1556
                            public Entry<U> inject(Entry<U> entry) {
1557
                                if (methodDescription.getDeclaringType().isInterface()) {
1✔
1558
                                    LinkedHashSet<MethodDescription> candidates = new LinkedHashSet<MethodDescription>();
1✔
1559
                                    candidates.add(methodDescription);
1✔
1560
                                    TypeDescription target = methodDescription.getDeclaringType().asErasure();
1✔
1561
                                    for (MethodDescription methodDescription : entry.getCandidates()) {
1✔
1562
                                        if (methodDescription.getDeclaringType().asErasure().isAssignableTo(target)) {
1✔
1563
                                            candidates.remove(this.methodDescription);
1✔
1564
                                            candidates.add(methodDescription);
1✔
1565
                                        } else if (!methodDescription.getDeclaringType().asErasure().isAssignableFrom(target)) {
1✔
1566
                                            candidates.add(methodDescription);
1✔
1567
                                        }
1568
                                    }
1✔
1569
                                    return candidates.size() == 1
1✔
1570
                                            ? new Resolved<U>(key.combineWith(entry.getKey()), candidates.iterator().next(), visibility.expandTo(entry.getVisibility()), madeVisible)
1✔
1571
                                            : new Ambiguous<U>(key.combineWith(entry.getKey()), candidates, visibility.expandTo(entry.getVisibility()));
1✔
1572
                                } else {
1573
                                    return new Resolved<U>(key.combineWith(entry.getKey()), methodDescription, visibility.expandTo(entry.getVisibility()), madeVisible);
1✔
1574
                                }
1575
                            }
1576

1577
                            /**
1578
                             * {@inheritDoc}
1579
                             */
1580
                            public MethodGraph.Node asNode(Merger merger) {
1581
                                return new Node(key.detach(methodDescription.asTypeToken()), methodDescription, visibility, madeVisible);
1✔
1582
                            }
1583

1584
                            /**
1585
                             * A node implementation representing a non-ambiguous method.
1586
                             */
1587
                            @HashCodeAndEqualsPlugin.Enhance
1588
                            protected static class Node implements MethodGraph.Node {
1589

1590
                                /**
1591
                                 * The detached key representing this node.
1592
                                 */
1593
                                private final Detached key;
1594

1595
                                /**
1596
                                 * The representative method of this node.
1597
                                 */
1598
                                private final MethodDescription methodDescription;
1599

1600
                                /**
1601
                                 * The node's minimal visibility.
1602
                                 */
1603
                                private final Visibility visibility;
1604

1605
                                /**
1606
                                 * {@code true} if the represented method was made explicitly visible by a visibility bridge.
1607
                                 */
1608
                                private final boolean visible;
1609

1610
                                /**
1611
                                 * Creates a new node.
1612
                                 *
1613
                                 * @param key               The detached key representing this node.
1614
                                 * @param methodDescription The representative method of this node.
1615
                                 * @param visibility        The node's minimal visibility.
1616
                                 * @param visible           {@code true} if the represented method was made explicitly visible by a visibility bridge.
1617
                                 */
1618
                                protected Node(Detached key, MethodDescription methodDescription, Visibility visibility, boolean visible) {
1✔
1619
                                    this.key = key;
1✔
1620
                                    this.methodDescription = methodDescription;
1✔
1621
                                    this.visibility = visibility;
1✔
1622
                                    this.visible = visible;
1✔
1623
                                }
1✔
1624

1625
                                /**
1626
                                 * {@inheritDoc}
1627
                                 */
1628
                                public Sort getSort() {
1629
                                    return visible
1✔
1630
                                            ? Sort.VISIBLE
1631
                                            : Sort.RESOLVED;
1632
                                }
1633

1634
                                /**
1635
                                 * {@inheritDoc}
1636
                                 */
1637
                                public MethodDescription getRepresentative() {
1638
                                    return methodDescription;
1✔
1639
                                }
1640

1641
                                /**
1642
                                 * {@inheritDoc}
1643
                                 */
1644
                                public Set<MethodDescription.TypeToken> getMethodTypes() {
1645
                                    return key.getIdentifiers();
1✔
1646
                                }
1647

1648
                                /**
1649
                                 * {@inheritDoc}
1650
                                 */
1651
                                public Visibility getVisibility() {
1652
                                    return visibility;
1✔
1653
                                }
1654
                            }
1655
                        }
1656

1657
                        /**
1658
                         * An entry representing an ambiguous node resolution.
1659
                         *
1660
                         * @param <U> The type of the harmonized key to determine method equality.
1661
                         */
1662
                        @HashCodeAndEqualsPlugin.Enhance
1663
                        class Ambiguous<U> implements Entry<U> {
1664

1665
                            /**
1666
                             * The harmonized key this entry represents.
1667
                             */
1668
                            private final Harmonized<U> key;
1669

1670
                            /**
1671
                             * A set of ambiguous methods that this entry represents.
1672
                             */
1673
                            private final LinkedHashSet<MethodDescription> methodDescriptions;
1674

1675
                            /**
1676
                             * The minimal required visibility for this method.
1677
                             */
1678
                            private final Visibility visibility;
1679

1680
                            /**
1681
                             * Creates a new ambiguous entry.
1682
                             *
1683
                             * @param key                The harmonized key this entry represents.
1684
                             * @param methodDescriptions A set of ambiguous methods that this entry represents.
1685
                             * @param visibility         The minimal required visibility for this method.
1686
                             */
1687
                            protected Ambiguous(Harmonized<U> key, LinkedHashSet<MethodDescription> methodDescriptions, Visibility visibility) {
1✔
1688
                                this.key = key;
1✔
1689
                                this.methodDescriptions = methodDescriptions;
1✔
1690
                                this.visibility = visibility;
1✔
1691
                            }
1✔
1692

1693
                            /**
1694
                             * Creates a new ambiguous entry if both provided entries are not considered to be a bridge of one another.
1695
                             *
1696
                             * @param key        The key of the entry to be created.
1697
                             * @param left       The left method to be considered.
1698
                             * @param right      The right method to be considered.
1699
                             * @param visibility The entry's minimal visibility.
1700
                             * @param <Q>        The type of the token of the harmonized key to determine method equality.
1701
                             * @return The entry representing both methods.
1702
                             */
1703
                            protected static <Q> Entry<Q> of(Harmonized<Q> key, MethodDescription left, MethodDescription right, Visibility visibility) {
1704
                                visibility = visibility.expandTo(left.getVisibility()).expandTo(right.getVisibility());
1✔
1705
                                return left.isBridge() ^ right.isBridge()
1✔
1706
                                        ? new Resolved<Q>(key, left.isBridge() ? right : left, visibility, Resolved.NOT_MADE_VISIBLE)
1✔
1707
                                        : new Ambiguous<Q>(key, new LinkedHashSet<MethodDescription>(Arrays.asList(left, right)), visibility);
1✔
1708
                            }
1709

1710
                            /**
1711
                             * {@inheritDoc}
1712
                             */
1713
                            public Harmonized<U> getKey() {
1714
                                return key;
1✔
1715
                            }
1716

1717
                            /**
1718
                             * {@inheritDoc}
1719
                             */
1720
                            public Set<MethodDescription> getCandidates() {
1721
                                return methodDescriptions;
1✔
1722
                            }
1723

1724
                            /**
1725
                             * {@inheritDoc}
1726
                             */
1727
                            public Visibility getVisibility() {
1728
                                return visibility;
1✔
1729
                            }
1730

1731
                            /**
1732
                             * {@inheritDoc}
1733
                             */
1734
                            public Entry<U> extendBy(MethodDescription methodDescription, Harmonizer<U> harmonizer) {
1735
                                Harmonized<U> key = this.key.extend(methodDescription.asDefined(), harmonizer);
1✔
1736
                                LinkedHashSet<MethodDescription> methodDescriptions = new LinkedHashSet<MethodDescription>();
1✔
1737
                                TypeDescription declaringType = methodDescription.getDeclaringType().asErasure();
1✔
1738
                                boolean bridge = methodDescription.isBridge();
1✔
1739
                                Visibility visibility = this.visibility;
1✔
1740
                                for (MethodDescription extendedMethod : this.methodDescriptions) {
1✔
1741
                                    if (extendedMethod.getDeclaringType().asErasure().equals(declaringType)) {
1✔
1742
                                        if (extendedMethod.isBridge() ^ bridge) {
×
1743
                                            methodDescriptions.add(bridge ? extendedMethod : methodDescription);
×
1744
                                        } else {
1745
                                            methodDescriptions.add(methodDescription);
×
1746
                                            methodDescriptions.add(extendedMethod);
×
1747
                                        }
1748
                                    }
1749
                                    visibility = visibility.expandTo(extendedMethod.getVisibility());
1✔
1750
                                }
1✔
1751
                                if (methodDescriptions.isEmpty()) {
1✔
1752
                                    return new Resolved<U>(key, methodDescription, visibility, bridge);
1✔
1753
                                } else if (methodDescriptions.size() == 1) {
×
1754
                                    return new Resolved<U>(key, methodDescriptions.iterator().next(), visibility, Resolved.NOT_MADE_VISIBLE);
×
1755
                                } else {
1756
                                    return new Ambiguous<U>(key, methodDescriptions, visibility);
×
1757
                                }
1758
                            }
1759

1760
                            /**
1761
                             * {@inheritDoc}
1762
                             */
1763
                            public Entry<U> inject(Entry<U> entry) {
1764
                                LinkedHashSet<MethodDescription> methodDescriptions = new LinkedHashSet<MethodDescription>();
1✔
1765
                                outer:
1766
                                for (MethodDescription methodDescription : this.methodDescriptions) {
1✔
1767
                                    TypeDescription target = methodDescription.getDeclaringType().asErasure();
1✔
1768
                                    for (MethodDescription candidate : entry.getCandidates()) {
1✔
1769
                                        TypeDescription typeDescription = candidate.getDeclaringType().asErasure();
1✔
1770
                                        if (!typeDescription.equals(target) && typeDescription.isAssignableTo(target)) {
1✔
1771
                                            continue outer;
1✔
1772
                                        }
1773
                                    }
×
1774
                                    methodDescriptions.add(methodDescription);
×
1775
                                }
×
1776
                                outer:
1777
                                for (MethodDescription candidate : entry.getCandidates()) {
1✔
1778
                                    TypeDescription target = candidate.getDeclaringType().asErasure();
1✔
1779
                                    for (MethodDescription methodDescription : this.methodDescriptions) {
1✔
1780
                                        if (methodDescription.getDeclaringType().asErasure().isAssignableTo(target)) {
1✔
1781
                                            continue outer;
×
1782
                                        }
1783
                                    }
1✔
1784
                                    methodDescriptions.add(candidate);
1✔
1785
                                }
1✔
1786
                                return methodDescriptions.size() == 1
1✔
1787
                                        ? new Resolved<U>(key.combineWith(entry.getKey()), methodDescriptions.iterator().next(), visibility.expandTo(entry.getVisibility()))
1✔
1788
                                        : new Ambiguous<U>(key.combineWith(entry.getKey()), methodDescriptions, visibility.expandTo(entry.getVisibility()));
×
1789
                            }
1790

1791
                            /**
1792
                             * {@inheritDoc}
1793
                             */
1794
                            public MethodGraph.Node asNode(Merger merger) {
1795
                                Iterator<MethodDescription> iterator = methodDescriptions.iterator();
1✔
1796
                                MethodDescription methodDescription = iterator.next();
1✔
1797
                                while (iterator.hasNext()) {
1✔
1798
                                    methodDescription = merger.merge(methodDescription, iterator.next());
1✔
1799
                                }
1800
                                return new Node(key.detach(methodDescription.asTypeToken()), methodDescription, visibility);
1✔
1801
                            }
1802

1803
                            /**
1804
                             * A node implementation representing an ambiguous method resolution.
1805
                             */
1806
                            @HashCodeAndEqualsPlugin.Enhance
1807
                            protected static class Node implements MethodGraph.Node {
1808

1809
                                /**
1810
                                 * The detached key representing this node.
1811
                                 */
1812
                                private final Detached key;
1813

1814
                                /**
1815
                                 * The representative method of this node.
1816
                                 */
1817
                                private final MethodDescription methodDescription;
1818

1819
                                /**
1820
                                 * The node's minimal visibility.
1821
                                 */
1822
                                private final Visibility visibility;
1823

1824
                                /**
1825
                                 * @param key               The detached key representing this node.
1826
                                 * @param methodDescription The representative method of this node.
1827
                                 * @param visibility        The node's minimal visibility.
1828
                                 */
1829
                                protected Node(Detached key, MethodDescription methodDescription, Visibility visibility) {
1✔
1830
                                    this.key = key;
1✔
1831
                                    this.methodDescription = methodDescription;
1✔
1832
                                    this.visibility = visibility;
1✔
1833
                                }
1✔
1834

1835
                                /**
1836
                                 * {@inheritDoc}
1837
                                 */
1838
                                public Sort getSort() {
1839
                                    return Sort.AMBIGUOUS;
1✔
1840
                                }
1841

1842
                                /**
1843
                                 * {@inheritDoc}
1844
                                 */
1845
                                public MethodDescription getRepresentative() {
1846
                                    return methodDescription;
1✔
1847
                                }
1848

1849
                                /**
1850
                                 * {@inheritDoc}
1851
                                 */
1852
                                public Set<MethodDescription.TypeToken> getMethodTypes() {
1853
                                    return key.getIdentifiers();
1✔
1854
                                }
1855

1856
                                /**
1857
                                 * {@inheritDoc}
1858
                                 */
1859
                                public Visibility getVisibility() {
1860
                                    return visibility;
1✔
1861
                                }
1862
                            }
1863
                        }
1864
                    }
1865

1866
                    /**
1867
                     * A graph implementation based on a key store.
1868
                     */
1869
                    @HashCodeAndEqualsPlugin.Enhance
1870
                    protected static class Graph implements MethodGraph {
1871

1872
                        /**
1873
                         * A mapping of a node's type tokens to the represented node.
1874
                         */
1875
                        private final LinkedHashMap<Key<MethodDescription.TypeToken>, Node> entries;
1876

1877
                        /**
1878
                         * Creates a new graph.
1879
                         *
1880
                         * @param entries A mapping of a node's type tokens to the represented node.
1881
                         */
1882
                        protected Graph(LinkedHashMap<Key<MethodDescription.TypeToken>, Node> entries) {
1✔
1883
                            this.entries = entries;
1✔
1884
                        }
1✔
1885

1886
                        /**
1887
                         * {@inheritDoc}
1888
                         */
1889
                        public Node locate(MethodDescription.SignatureToken token) {
1890
                            Node node = entries.get(Detached.of(token));
1✔
1891
                            return node == null
1✔
1892
                                    ? Node.Unresolved.INSTANCE
1893
                                    : node;
1894
                        }
1895

1896
                        /**
1897
                         * {@inheritDoc}
1898
                         */
1899
                        public NodeList listNodes() {
1900
                            return new NodeList(new ArrayList<Node>(entries.values()));
1✔
1901
                        }
1902
                    }
1903
                }
1904
            }
1905
        }
1906
    }
1907

1908
    /**
1909
     * A list of nodes.
1910
     */
1911
    class NodeList extends FilterableList.AbstractBase<Node, NodeList> {
1912

1913
        /**
1914
         * The represented nodes.
1915
         */
1916
        private final List<? extends Node> nodes;
1917

1918
        /**
1919
         * Creates a list of nodes.
1920
         *
1921
         * @param nodes The represented nodes.
1922
         */
1923
        public NodeList(List<? extends Node> nodes) {
1✔
1924
            this.nodes = nodes;
1✔
1925
        }
1✔
1926

1927
        /**
1928
         * {@inheritDoc}
1929
         */
1930
        public Node get(int index) {
1931
            return nodes.get(index);
1✔
1932
        }
1933

1934
        /**
1935
         * {@inheritDoc}
1936
         */
1937
        public int size() {
1938
            return nodes.size();
1✔
1939
        }
1940

1941
        @Override
1942
        protected NodeList wrap(List<Node> values) {
1943
            return new NodeList(values);
1✔
1944
        }
1945

1946
        /**
1947
         * Transforms this list of nodes into a list of the node's representatives.
1948
         *
1949
         * @return A list of these node's representatives.
1950
         */
1951
        public MethodList<?> asMethodList() {
1952
            List<MethodDescription> methodDescriptions = new ArrayList<MethodDescription>(size());
1✔
1953
            for (Node node : nodes) {
1✔
1954
                methodDescriptions.add(node.getRepresentative());
1✔
1955
            }
1✔
1956
            return new MethodList.Explicit<MethodDescription>(methodDescriptions);
1✔
1957
        }
1958
    }
1959

1960
    /**
1961
     * A simple implementation of a method graph.
1962
     */
1963
    @HashCodeAndEqualsPlugin.Enhance
1964
    class Simple implements MethodGraph {
1965

1966
        /**
1967
         * The nodes represented by this method graph.
1968
         */
1969
        private final LinkedHashMap<MethodDescription.SignatureToken, Node> nodes;
1970

1971
        /**
1972
         * Creates a new simple method graph.
1973
         *
1974
         * @param nodes The nodes represented by this method graph.
1975
         */
1976
        public Simple(LinkedHashMap<MethodDescription.SignatureToken, Node> nodes) {
1✔
1977
            this.nodes = nodes;
1✔
1978
        }
1✔
1979

1980
        /**
1981
         * Returns a method graph that contains all of the provided methods as simple nodes.
1982
         *
1983
         * @param methodDescriptions A list of method descriptions to be represented as simple nodes.
1984
         * @return A method graph that represents all of the provided methods as simple nodes.
1985
         */
1986
        public static MethodGraph of(List<? extends MethodDescription> methodDescriptions) {
1987
            LinkedHashMap<MethodDescription.SignatureToken, Node> nodes = new LinkedHashMap<MethodDescription.SignatureToken, Node>();
1✔
1988
            for (MethodDescription methodDescription : methodDescriptions) {
1✔
1989
                nodes.put(methodDescription.asSignatureToken(), new Node.Simple(methodDescription));
1✔
1990
            }
1✔
1991
            return new Simple(nodes);
1✔
1992
        }
1993

1994
        /**
1995
         * {@inheritDoc}
1996
         */
1997
        public Node locate(MethodDescription.SignatureToken token) {
1998
            Node node = nodes.get(token);
1✔
1999
            return node == null
1✔
2000
                    ? Node.Unresolved.INSTANCE
2001
                    : node;
2002
        }
2003

2004
        /**
2005
         * {@inheritDoc}
2006
         */
2007
        public NodeList listNodes() {
2008
            return new NodeList(new ArrayList<Node>(nodes.values()));
1✔
2009
        }
2010
    }
2011
}
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