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

raphw / byte-buddy / #811

03 Nov 2025 10:41AM UTC coverage: 83.677% (-0.3%) from 84.023%
#811

push

raphw
Fix null handling.

0 of 1 new or added line in 1 file covered. (0.0%)

791 existing lines in 12 files now uncovered.

29814 of 35630 relevant lines covered (83.68%)

0.84 hits per line

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

90.18
/byte-buddy-dep/src/main/java/net/bytebuddy/build/Plugin.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.build;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.ByteBuddy;
20
import net.bytebuddy.ClassFileVersion;
21
import net.bytebuddy.description.type.TypeDescription;
22
import net.bytebuddy.dynamic.ClassFileLocator;
23
import net.bytebuddy.dynamic.DynamicType;
24
import net.bytebuddy.dynamic.TypeResolutionStrategy;
25
import net.bytebuddy.dynamic.scaffold.inline.MethodNameTransformer;
26
import net.bytebuddy.implementation.LoadedTypeInitializer;
27
import net.bytebuddy.matcher.ElementMatcher;
28
import net.bytebuddy.pool.TypePool;
29
import net.bytebuddy.utility.CompoundList;
30
import net.bytebuddy.utility.FileSystem;
31
import net.bytebuddy.utility.QueueFactory;
32
import net.bytebuddy.utility.StreamDrainer;
33
import net.bytebuddy.utility.nullability.AlwaysNull;
34
import net.bytebuddy.utility.nullability.MaybeNull;
35

36
import java.io.BufferedReader;
37
import java.io.ByteArrayInputStream;
38
import java.io.ByteArrayOutputStream;
39
import java.io.Closeable;
40
import java.io.File;
41
import java.io.FileInputStream;
42
import java.io.FileOutputStream;
43
import java.io.IOException;
44
import java.io.InputStream;
45
import java.io.InputStreamReader;
46
import java.io.OutputStream;
47
import java.io.PrintStream;
48
import java.lang.annotation.Documented;
49
import java.lang.annotation.ElementType;
50
import java.lang.annotation.Retention;
51
import java.lang.annotation.RetentionPolicy;
52
import java.lang.annotation.Target;
53
import java.lang.reflect.Constructor;
54
import java.lang.reflect.InvocationTargetException;
55
import java.lang.reflect.Method;
56
import java.lang.reflect.Modifier;
57
import java.net.URL;
58
import java.util.ArrayList;
59
import java.util.Arrays;
60
import java.util.Collection;
61
import java.util.Collections;
62
import java.util.Enumeration;
63
import java.util.HashMap;
64
import java.util.HashSet;
65
import java.util.Iterator;
66
import java.util.LinkedHashMap;
67
import java.util.List;
68
import java.util.Map;
69
import java.util.NoSuchElementException;
70
import java.util.Queue;
71
import java.util.Set;
72
import java.util.concurrent.Callable;
73
import java.util.concurrent.CompletionService;
74
import java.util.concurrent.ExecutionException;
75
import java.util.concurrent.Executor;
76
import java.util.concurrent.ExecutorCompletionService;
77
import java.util.concurrent.ExecutorService;
78
import java.util.concurrent.Executors;
79
import java.util.concurrent.Future;
80
import java.util.jar.JarEntry;
81
import java.util.jar.JarFile;
82
import java.util.jar.JarOutputStream;
83
import java.util.jar.Manifest;
84

85
import static net.bytebuddy.matcher.ElementMatchers.none;
86

87
/**
88
 * <p>
89
 * A plugin that allows for the application of Byte Buddy transformations during a build process. This plugin's
90
 * transformation is applied to any type matching this plugin's type matcher. Plugin types must be public,
91
 * non-abstract and must declare a public default constructor to work.
92
 * </p>
93
 * <p>
94
 * A plugin is always used within the scope of a single plugin engine application and is disposed after closing. It might be used
95
 * concurrently and must assure its own thread-safety if run outside of a {@link Plugin.Engine} or when using a parallel
96
 * {@link Plugin.Engine.Dispatcher}.
97
 * </p>
98
 * <p>
99
 * For discoverability, plugin class names can be stored in a file named <i>META-INF/net.bytebuddy/build.plugins</i> with the fully
100
 * qualified class name of the plugin per line.
101
 * </p>
102
 */
103
public interface Plugin extends ElementMatcher<TypeDescription>, Closeable {
104

105
    /**
106
     * Applies this plugin.
107
     *
108
     * @param builder          The builder to use as a basis for the applied transformation.
109
     * @param typeDescription  The type being transformed.
110
     * @param classFileLocator A class file locator that can locate other types in the scope of the project.
111
     * @return The supplied builder with additional transformations registered.
112
     */
113
    DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassFileLocator classFileLocator);
114

115
    /**
116
     * <p>
117
     * A plugin that applies a preprocessor, i.e. causes a plugin engine's execution to defer all plugin applications until all types were discovered.
118
     * </p>
119
     * <p>
120
     * <b>Important</b>: The registration of a single plugin with preprocessor causes the deferral of all plugins' application that are registered
121
     * with a particular plugin engine. This will reduce parallel application if a corresponding {@link Engine.Dispatcher} is used and will increase
122
     * the engine application's memory consumption. Any alternative application of a plugin outside of a {@link Plugin.Engine} might not be capable
123
     * of preprocessing where the discovery callback is not invoked.
124
     * </p>
125
     */
126
    interface WithPreprocessor extends Plugin {
127

128
        /**
129
         * Invoked upon the discovery of a type that is not explicitly ignored.
130
         *
131
         * @param typeDescription  The discovered type.
132
         * @param classFileLocator A class file locator that can locate other types in the scope of the project.
133
         */
134
        void onPreprocess(TypeDescription typeDescription, ClassFileLocator classFileLocator);
135
    }
136

137
    /**
138
     * Allows for the generation of types before a plugin is applied.
139
     */
140
    interface WithInitialization extends Plugin {
141

142
        /**
143
         * Returns a mapping of classes that should be created before discovering any types.
144
         *
145
         * @param classFileLocator A class file locator that can locate other types in the scope of the project.
146
         * @return A mapping of types to their binary representation.
147
         */
148
        Map<TypeDescription, byte[]> initialize(ClassFileLocator classFileLocator);
149
    }
150

151
    /**
152
     * A factory for providing a build plugin.
153
     */
154
    interface Factory {
155

156
        /**
157
         * Returns a plugin that can be used for a transformation and which is subsequently closed.
158
         *
159
         * @return The plugin to use for type transformations.
160
         */
161
        Plugin make();
162

163
        /**
164
         * A simple factory that returns a preconstructed plugin instance.
165
         */
166
        @HashCodeAndEqualsPlugin.Enhance
167
        class Simple implements Factory {
168

169
            /**
170
             * The plugin to provide.
171
             */
172
            private final Plugin plugin;
173

174
            /**
175
             * Creates a simple plugin factory.
176
             *
177
             * @param plugin The plugin to provide.
178
             */
179
            public Simple(Plugin plugin) {
1✔
180
                this.plugin = plugin;
1✔
181
            }
1✔
182

183
            /**
184
             * {@inheritDoc}
185
             */
186
            public Plugin make() {
187
                return plugin;
1✔
188
            }
189
        }
190

191
        /**
192
         * A plugin factory that uses reflection for instantiating a plugin.
193
         */
194
        @HashCodeAndEqualsPlugin.Enhance
195
        class UsingReflection implements Factory {
196

197
            /**
198
             * The plugin type.
199
             */
200
            private final Class<? extends Plugin> type;
201

202
            /**
203
             * A list of argument providers that can be used for instantiating the plugin.
204
             */
205
            private final List<ArgumentResolver> argumentResolvers;
206

207
            /**
208
             * Creates a plugin factory that uses reflection for creating a plugin.
209
             *
210
             * @param type The plugin type.
211
             */
212
            public UsingReflection(Class<? extends Plugin> type) {
213
                this(type, Collections.<ArgumentResolver>emptyList());
1✔
214
            }
1✔
215

216
            /**
217
             * Creates a plugin factory that uses reflection for creating a plugin.
218
             *
219
             * @param type              The plugin type.
220
             * @param argumentResolvers A list of argument providers that can be used for instantiating the plugin.
221
             */
222
            protected UsingReflection(Class<? extends Plugin> type, List<ArgumentResolver> argumentResolvers) {
1✔
223
                this.type = type;
1✔
224
                this.argumentResolvers = argumentResolvers;
1✔
225
            }
1✔
226

227
            /**
228
             * Appends the supplied argument resolvers.
229
             *
230
             * @param argumentResolver A list of argument providers that can be used for instantiating the plugin.
231
             * @return A new plugin factory that uses reflection for creating a plugin that also uses the supplied argument resolvers.
232
             */
233
            public UsingReflection with(ArgumentResolver... argumentResolver) {
234
                return with(Arrays.asList(argumentResolver));
1✔
235
            }
236

237
            /**
238
             * Appends the supplied argument resolvers.
239
             *
240
             * @param argumentResolvers A list of argument providers that can be used for instantiating the plugin.
241
             * @return A new plugin factory that uses reflection for creating a plugin that also uses the supplied argument resolvers.
242
             */
243
            public UsingReflection with(List<? extends ArgumentResolver> argumentResolvers) {
244
                return new UsingReflection(type, CompoundList.of(argumentResolvers, this.argumentResolvers));
1✔
245
            }
246

247
            /**
248
             * {@inheritDoc}
249
             */
250
            @SuppressWarnings("unchecked")
251
            public Plugin make() {
252
                Instantiator instantiator = new Instantiator.Unresolved(type);
1✔
253
                candidates:
254
                for (Constructor<?> constructor : type.getConstructors()) {
1✔
255
                    if (!constructor.isSynthetic()) {
1✔
256
                        List<Object> arguments = new ArrayList<Object>(constructor.getParameterTypes().length);
1✔
257
                        int index = 0;
1✔
258
                        for (Class<?> type : constructor.getParameterTypes()) {
1✔
259
                            boolean resolved = false;
1✔
260
                            for (ArgumentResolver argumentResolver : argumentResolvers) {
1✔
261
                                ArgumentResolver.Resolution resolution = argumentResolver.resolve(index, type);
1✔
262
                                if (resolution.isResolved()) {
1✔
263
                                    arguments.add(resolution.getArgument());
1✔
264
                                    resolved = true;
1✔
265
                                    break;
1✔
266
                                }
267
                            }
1✔
268
                            if (resolved) {
1✔
269
                                index += 1;
1✔
270
                            } else {
271
                                continue candidates;
272
                            }
273
                        }
274
                        instantiator = instantiator.replaceBy(new Instantiator.Resolved((Constructor<? extends Plugin>) constructor, arguments));
1✔
275
                    }
276
                }
277
                return instantiator.instantiate();
1✔
278
            }
279

280
            /**
281
             * An instantiator is responsible for invoking a plugin constructor reflectively.
282
             */
283
            protected interface Instantiator {
284

285
                /**
286
                 * Returns either this instantiator or the supplied instantiator, depending on the instances' states.
287
                 *
288
                 * @param instantiator The alternative instantiator.
289
                 * @return The dominant instantiator.
290
                 */
291
                Instantiator replaceBy(Resolved instantiator);
292

293
                /**
294
                 * Instantiates the represented plugin.
295
                 *
296
                 * @return The instantiated plugin.
297
                 */
298
                Plugin instantiate();
299

300
                /**
301
                 * An instantiator that is not resolved for creating an instance.
302
                 */
303
                @HashCodeAndEqualsPlugin.Enhance
304
                class Unresolved implements Instantiator {
305

306
                    /**
307
                     * The type for which no constructor was yet resolved.
308
                     */
309
                    private final Class<? extends Plugin> type;
310

311
                    /**
312
                     * Creates a new unresolved constructor.
313
                     *
314
                     * @param type The type for which no constructor was yet resolved.
315
                     */
316
                    protected Unresolved(Class<? extends Plugin> type) {
1✔
317
                        this.type = type;
1✔
318
                    }
1✔
319

320
                    /**
321
                     * {@inheritDoc}
322
                     */
323
                    public Instantiator replaceBy(Resolved instantiator) {
324
                        return instantiator;
1✔
325
                    }
326

327
                    /**
328
                     * {@inheritDoc}
329
                     */
330
                    public Plugin instantiate() {
331
                        throw new IllegalStateException("No constructor resolvable for " + type);
1✔
332
                    }
333
                }
334

335
                /**
336
                 * Represents an ambiguously resolved instantiator.
337
                 */
338
                @HashCodeAndEqualsPlugin.Enhance
339
                class Ambiguous implements Instantiator {
340

341
                    /**
342
                     * The left constructor.
343
                     */
344
                    private final Constructor<?> left;
345

346
                    /**
347
                     * The right constructor.
348
                     */
349
                    private final Constructor<?> right;
350

351
                    /**
352
                     * The resolved priority.
353
                     */
354
                    private final int priority;
355

356
                    /**
357
                     * The resolved number of parameters.
358
                     */
359
                    private final int parameters;
360

361
                    /**
362
                     * Creates a new ambiguous instantiator.
363
                     *
364
                     * @param left       The left constructor.
365
                     * @param right      The right constructor.
366
                     * @param priority   The resolved priority.
367
                     * @param parameters The resolved number of parameters.
368
                     */
369
                    protected Ambiguous(Constructor<?> left, Constructor<?> right, int priority, int parameters) {
1✔
370
                        this.left = left;
1✔
371
                        this.right = right;
1✔
372
                        this.priority = priority;
1✔
373
                        this.parameters = parameters;
1✔
374
                    }
1✔
375

376
                    /**
377
                     * {@inheritDoc}
378
                     */
379
                    public Instantiator replaceBy(Resolved instantiator) {
380
                        Priority priority = instantiator.getConstructor().getAnnotation(Priority.class);
1✔
381
                        if ((priority == null ? Priority.DEFAULT : priority.value()) > this.priority) {
1✔
382
                            return instantiator;
×
383
                        } else if ((priority == null ? Priority.DEFAULT : priority.value()) < this.priority) {
1✔
384
                            return this;
×
385
                        } else if (instantiator.getConstructor().getParameterTypes().length > parameters) {
1✔
386
                            return instantiator;
×
387
                        } else {
388
                            return this;
1✔
389
                        }
390
                    }
391

392
                    /**
393
                     * {@inheritDoc}
394
                     */
395
                    public Plugin instantiate() {
396
                        throw new IllegalStateException("Ambiguous constructors " + left + " and " + right);
1✔
397
                    }
398
                }
399

400
                /**
401
                 * An instantiator that is resolved for a given constructor with arguments.
402
                 */
403
                @HashCodeAndEqualsPlugin.Enhance
404
                class Resolved implements Instantiator {
405

406
                    /**
407
                     * The represented constructor.
408
                     */
409
                    private final Constructor<? extends Plugin> constructor;
410

411
                    /**
412
                     * The constructor arguments.
413
                     */
414
                    private final List<?> arguments;
415

416
                    /**
417
                     * Creates a new resolved constructor.
418
                     *
419
                     * @param constructor The represented constructor.
420
                     * @param arguments   The constructor arguments.
421
                     */
422
                    protected Resolved(Constructor<? extends Plugin> constructor, List<?> arguments) {
1✔
423
                        this.constructor = constructor;
1✔
424
                        this.arguments = arguments;
1✔
425
                    }
1✔
426

427
                    /**
428
                     * Returns the resolved constructor.
429
                     *
430
                     * @return The resolved constructor.
431
                     */
432
                    protected Constructor<? extends Plugin> getConstructor() {
433
                        return constructor;
1✔
434
                    }
435

436
                    /**
437
                     * {@inheritDoc}
438
                     */
439
                    public Instantiator replaceBy(Resolved instantiator) {
440
                        Priority left = constructor.getAnnotation(Priority.class), right = instantiator.getConstructor().getAnnotation(Priority.class);
1✔
441
                        int leftPriority = left == null ? Priority.DEFAULT : left.value(), rightPriority = right == null ? Priority.DEFAULT : right.value();
1✔
442
                        if (leftPriority > rightPriority) {
1✔
443
                            return this;
1✔
444
                        } else if (leftPriority < rightPriority) {
1✔
445
                            return instantiator;
1✔
446
                        } else if (constructor.getParameterTypes().length > instantiator.getConstructor().getParameterTypes().length) {
1✔
447
                            return this;
1✔
448
                        } else if (constructor.getParameterTypes().length < instantiator.getConstructor().getParameterTypes().length) {
1✔
449
                            return instantiator;
1✔
450
                        } else {
451
                            return new Ambiguous(constructor, instantiator.getConstructor(), leftPriority, constructor.getParameterTypes().length);
1✔
452
                        }
453
                    }
454

455
                    /**
456
                     * {@inheritDoc}
457
                     */
458
                    public Plugin instantiate() {
459
                        try {
460
                            return constructor.newInstance(arguments.toArray(new Object[0]));
1✔
461
                        } catch (InstantiationException exception) {
×
462
                            throw new IllegalStateException("Failed to instantiate plugin via " + constructor, exception);
×
463
                        } catch (IllegalAccessException exception) {
×
464
                            throw new IllegalStateException("Failed to access " + constructor, exception);
×
465
                        } catch (InvocationTargetException exception) {
×
466
                            throw new IllegalStateException("Error during construction of" + constructor, exception.getTargetException());
×
467
                        }
468
                    }
469
                }
470
            }
471

472
            /**
473
             * Indicates that a constructor should be treated with a given priority if several constructors can be resolved.
474
             */
475
            @Documented
476
            @Target(ElementType.CONSTRUCTOR)
477
            @Retention(RetentionPolicy.RUNTIME)
478
            public @interface Priority {
479

480
                /**
481
                 * The default priority that is assumed for non-annotated constructors.
482
                 */
483
                int DEFAULT = 0;
484

485
                /**
486
                 * Indicates the priority of the annotated constructor.
487
                 *
488
                 * @return The priority of the annotated constructor.
489
                 */
490
                int value();
491
            }
492

493
            /**
494
             * Allows to resolve arguments for a {@link Plugin} constructor.
495
             */
496
            public interface ArgumentResolver {
497

498
                /**
499
                 * Attempts the resolution of an argument for a given parameter.
500
                 *
501
                 * @param index The parameter's index.
502
                 * @param type  The parameter's type.
503
                 * @return The resolution for the parameter.
504
                 */
505
                Resolution resolve(int index, Class<?> type);
506

507
                /**
508
                 * A resolution provided by an argument provider.
509
                 */
510
                interface Resolution {
511

512
                    /**
513
                     * Returns {@code true} if the represented argument is resolved successfully.
514
                     *
515
                     * @return {@code true} if the represented argument is resolved successfully.
516
                     */
517
                    boolean isResolved();
518

519
                    /**
520
                     * Returns the resolved argument if the resolution was successful.
521
                     *
522
                     * @return The resolved argument if the resolution was successful.
523
                     */
524
                    @MaybeNull
525
                    Object getArgument();
526

527
                    /**
528
                     * Represents an unresolved argument resolution.
529
                     */
530
                    enum Unresolved implements Resolution {
1✔
531

532
                        /**
533
                         * The singleton instance.
534
                         */
535
                        INSTANCE;
1✔
536

537
                        /**
538
                         * {@inheritDoc}
539
                         */
540
                        public boolean isResolved() {
541
                            return false;
1✔
542
                        }
543

544
                        /**
545
                         * {@inheritDoc}
546
                         */
547
                        public Object getArgument() {
548
                            throw new IllegalStateException("Cannot get the argument for an unresolved parameter");
1✔
549
                        }
550
                    }
551

552
                    /**
553
                     * Represents a resolved argument resolution.
554
                     */
555
                    @HashCodeAndEqualsPlugin.Enhance
556
                    class Resolved implements Resolution {
557

558
                        /**
559
                         * The resolved argument which might be {@code null}.
560
                         */
561
                        @MaybeNull
562
                        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
563
                        private final Object argument;
564

565
                        /**
566
                         * Creates a resolved argument resolution.
567
                         *
568
                         * @param argument The resolved argument which might be {@code null}.
569
                         */
570
                        public Resolved(@MaybeNull Object argument) {
1✔
571
                            this.argument = argument;
1✔
572
                        }
1✔
573

574
                        /**
575
                         * {@inheritDoc}
576
                         */
577
                        public boolean isResolved() {
578
                            return true;
1✔
579
                        }
580

581
                        /**
582
                         * {@inheritDoc}
583
                         */
584
                        @MaybeNull
585
                        public Object getArgument() {
586
                            return argument;
1✔
587
                        }
588
                    }
589
                }
590

591
                /**
592
                 * An argument resolver that never resolves an argument.
593
                 */
594
                enum NoOp implements ArgumentResolver {
×
595

596
                    /**
597
                     * The singleton instance.
598
                     */
599
                    INSTANCE;
×
600

601
                    /**
602
                     * {@inheritDoc}
603
                     */
604
                    public Resolution resolve(int index, Class<?> type) {
605
                        return Resolution.Unresolved.INSTANCE;
×
606
                    }
607
                }
608

609
                /**
610
                 * An argument resolver that resolves parameters for a given type.
611
                 *
612
                 * @param <T> The type being resolved.
613
                 */
614
                @HashCodeAndEqualsPlugin.Enhance
615
                class ForType<T> implements ArgumentResolver {
616

617
                    /**
618
                     * The type being resolved.
619
                     */
620
                    private final Class<? extends T> type;
621

622
                    /**
623
                     * The instance to resolve for the represented type.
624
                     */
625
                    private final T value;
626

627
                    /**
628
                     * Creates a new argument resolver for a given type.
629
                     *
630
                     * @param type  The type being resolved.
631
                     * @param value The instance to resolve for the represented type.
632
                     */
633
                    protected ForType(Class<? extends T> type, T value) {
1✔
634
                        this.type = type;
1✔
635
                        this.value = value;
1✔
636
                    }
1✔
637

638
                    /**
639
                     * Creates an argument resolver for a given type.
640
                     *
641
                     * @param type  The type being resolved.
642
                     * @param value The instance to resolve for the represented type.
643
                     * @param <S>   The type being resolved.
644
                     * @return An appropriate argument resolver.
645
                     */
646
                    public static <S> ArgumentResolver of(Class<? extends S> type, S value) {
647
                        return new ForType<S>(type, value);
1✔
648
                    }
649

650
                    /**
651
                     * {@inheritDoc}
652
                     */
653
                    public Resolution resolve(int index, Class<?> type) {
654
                        return type == this.type
1✔
655
                                ? new Resolution.Resolved(value)
656
                                : Resolution.Unresolved.INSTANCE;
657
                    }
658
                }
659

660
                /**
661
                 * An argument resolver that resolves an argument for a specific parameter index.
662
                 */
663
                @HashCodeAndEqualsPlugin.Enhance
664
                class ForIndex implements ArgumentResolver {
665

666
                    /**
667
                     * A mapping of primitive types to their wrapper types.
668
                     */
669
                    private static final Map<Class<?>, Class<?>> WRAPPER_TYPES;
670

671
                    /*
672
                     * Creates the primitive to wrapper type mapping.
673
                     */
674
                    static {
675
                        WRAPPER_TYPES = new HashMap<Class<?>, Class<?>>();
1✔
676
                        WRAPPER_TYPES.put(boolean.class, Boolean.class);
1✔
677
                        WRAPPER_TYPES.put(byte.class, Byte.class);
1✔
678
                        WRAPPER_TYPES.put(short.class, Short.class);
1✔
679
                        WRAPPER_TYPES.put(char.class, Character.class);
1✔
680
                        WRAPPER_TYPES.put(int.class, Integer.class);
1✔
681
                        WRAPPER_TYPES.put(long.class, Long.class);
1✔
682
                        WRAPPER_TYPES.put(float.class, Float.class);
1✔
683
                        WRAPPER_TYPES.put(double.class, Double.class);
1✔
684
                    }
1✔
685

686
                    /**
687
                     * The index of the parameter to resolve.
688
                     */
689
                    private final int index;
690

691
                    /**
692
                     * The value to resolve for the represented index.
693
                     */
694
                    @MaybeNull
695
                    @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
696
                    private final Object value;
697

698
                    /**
699
                     * Creates an argument resolver for a given index.
700
                     *
701
                     * @param index The index of the parameter to resolve.
702
                     * @param value The value to resolve for the represented index.
703
                     */
704
                    public ForIndex(int index, @MaybeNull Object value) {
1✔
705
                        this.index = index;
1✔
706
                        this.value = value;
1✔
707
                    }
1✔
708

709
                    /**
710
                     * {@inheritDoc}
711
                     */
712
                    public Resolution resolve(int index, Class<?> type) {
713
                        if (this.index != index) {
1✔
714
                            return Resolution.Unresolved.INSTANCE;
1✔
715
                        } else if (type.isPrimitive()) {
1✔
716
                            return WRAPPER_TYPES.get(type).isInstance(value)
1✔
717
                                    ? new Resolution.Resolved(value)
718
                                    : Resolution.Unresolved.INSTANCE;
719
                        } else {
720
                            return value == null || type.isInstance(value)
1✔
721
                                    ? new Resolution.Resolved(value)
722
                                    : Resolution.Unresolved.INSTANCE;
723
                        }
724
                    }
725

726
                    /**
727
                     * An argument resolver that resolves an argument for a specific parameter index by attempting a conversion via
728
                     * invoking a static {@code valueOf} method on the target type, if it exists. As an exception, the {@code char}
729
                     * and {@link Character} types are resolved if the string value represents a single character.
730
                     */
731
                    @HashCodeAndEqualsPlugin.Enhance
732
                    public static class WithDynamicType implements ArgumentResolver {
733

734
                        /**
735
                         * The index of the parameter to resolve.
736
                         */
737
                        private final int index;
738

739
                        /**
740
                         * A string representation of the supplied value.
741
                         */
742
                        @MaybeNull
743
                        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
744
                        private final String value;
745

746
                        /**
747
                         * Creates an argument resolver for a specific parameter index and attempts a dynamic resolution.
748
                         *
749
                         * @param index The index of the parameter to resolve.
750
                         * @param value A string representation of the supplied value.
751
                         */
752
                        public WithDynamicType(int index, @MaybeNull String value) {
1✔
753
                            this.index = index;
1✔
754
                            this.value = value;
1✔
755
                        }
1✔
756

757
                        /**
758
                         * {@inheritDoc}
759
                         */
760
                        public Resolution resolve(int index, Class<?> type) {
761
                            if (this.index != index) {
1✔
762
                                return Resolution.Unresolved.INSTANCE;
1✔
763
                            } else if (type == char.class || type == Character.class) {
1✔
764
                                return value != null && value.length() == 1
1✔
765
                                        ? new Resolution.Resolved(value.charAt(0))
1✔
766
                                        : Resolution.Unresolved.INSTANCE;
767
                            } else if (type == String.class) {
1✔
768
                                return new Resolution.Resolved(value);
1✔
769
                            } else if (type.isPrimitive()) {
1✔
770
                                type = WRAPPER_TYPES.get(type);
1✔
771
                            }
772
                            try {
773
                                Method valueOf = type.getMethod("valueOf", String.class);
1✔
774
                                return Modifier.isStatic(valueOf.getModifiers()) && type.isAssignableFrom(valueOf.getReturnType())
1✔
775
                                        ? new Resolution.Resolved(valueOf.invoke(null, value))
1✔
776
                                        : Resolution.Unresolved.INSTANCE;
777
                            } catch (IllegalAccessException exception) {
×
778
                                throw new IllegalStateException(exception);
×
779
                            } catch (InvocationTargetException exception) {
×
780
                                throw new IllegalStateException(exception.getTargetException());
×
781
                            } catch (NoSuchMethodException ignored) {
1✔
782
                                return Resolution.Unresolved.INSTANCE;
1✔
783
                            }
784
                        }
785
                    }
786
                }
787
            }
788
        }
789
    }
790

791
    /**
792
     * A plugin engine allows the application of one or more plugins on class files found at a {@link Source} which are
793
     * then transferred and consumed by a {@link Target}.
794
     */
795
    interface Engine {
796

797
        /**
798
         * The name of the file that contains declares Byte Buddy plugins for discovery.
799
         */
800
        String PLUGIN_FILE = "META-INF/net.bytebuddy/build.plugins";
801

802
        /**
803
         * Defines a new Byte Buddy instance for usage for type creation.
804
         *
805
         * @param byteBuddy The Byte Buddy instance to use.
806
         * @return A new plugin engine that is equal to this engine but uses the supplied Byte Buddy instance.
807
         */
808
        Engine with(ByteBuddy byteBuddy);
809

810
        /**
811
         * Defines a new type strategy which determines the transformation mode for any instrumented type.
812
         *
813
         * @param typeStrategy The type strategy to use.
814
         * @return A new plugin engine that is equal to this engine but uses the supplied type strategy.
815
         */
816
        Engine with(TypeStrategy typeStrategy);
817

818
        /**
819
         * Defines a new pool strategy that determines how types are being described.
820
         *
821
         * @param poolStrategy The pool strategy to use.
822
         * @return A new plugin engine that is equal to this engine but uses the supplied pool strategy.
823
         */
824
        Engine with(PoolStrategy poolStrategy);
825

826
        /**
827
         * Appends the supplied class file locator to be queried for class files additionally to any previously registered
828
         * class file locators.
829
         *
830
         * @param classFileLocator The class file locator to append.
831
         * @return A new plugin engine that is equal to this engine but with the supplied class file locator being appended.
832
         */
833
        Engine with(ClassFileLocator classFileLocator);
834

835
        /**
836
         * Uses the supplied {@link ClassFileVersion} as a base for resolving multi-release jars, or {@code null}
837
         * if multi-release jars should not be resolved but be treated as regular jar files. This property might
838
         * not be applied if the underlying location mechanism does not supply manual resource resolution. Note that
839
         * classes that are of newer class file versions than the specified version are not resolved and simply copied.
840
         *
841
         * @param classFileVersion The class file version to use or {@code null} if multi-release jars should be ignored.
842
         * @return A new plugin engine that is equal to this engine but with the supplied class file version being used.
843
         */
844
        Engine with(@MaybeNull ClassFileVersion classFileVersion);
845

846
        /**
847
         * Appends the supplied listener to this engine.
848
         *
849
         * @param listener The listener to append.
850
         * @return A new plugin engine that is equal to this engine but with the supplied listener being appended.
851
         */
852
        Engine with(Listener listener);
853

854
        /**
855
         * Replaces the error handlers of this plugin engine without applying any error handlers.
856
         *
857
         * @return A new plugin engine that is equal to this engine but without any error handlers being registered.
858
         */
859
        Engine withoutErrorHandlers();
860

861
        /**
862
         * Replaces the error handlers of this plugin engine with the supplied error handlers.
863
         *
864
         * @param errorHandler The error handlers to apply.
865
         * @return A new plugin engine that is equal to this engine but with only the supplied error handlers being applied.
866
         */
867
        Engine withErrorHandlers(ErrorHandler... errorHandler);
868

869
        /**
870
         * Replaces the error handlers of this plugin engine with the supplied error handlers.
871
         *
872
         * @param errorHandlers The error handlers to apply.
873
         * @return A new plugin engine that is equal to this engine but with only the supplied error handlers being applied.
874
         */
875
        Engine withErrorHandlers(List<? extends ErrorHandler> errorHandlers);
876

877
        /**
878
         * Replaces the dispatcher factory of this plugin engine with a parallel dispatcher factory that uses the given amount of threads.
879
         *
880
         * @param threads The amount of threads to use.
881
         * @return A new plugin engine that is equal to this engine but with a parallel dispatcher factory using the specified amount of threads.
882
         */
883
        Engine withParallelTransformation(int threads);
884

885
        /**
886
         * Replaces the dispatcher factory of this plugin engine with the supplied dispatcher factory.
887
         *
888
         * @param dispatcherFactory The dispatcher factory to use.
889
         * @return A new plugin engine that is equal to this engine but with the supplied dispatcher factory being used.
890
         */
891
        Engine with(Dispatcher.Factory dispatcherFactory);
892

893
        /**
894
         * Ignores all types that are matched by this matcher or any previously registered ignore matcher.
895
         *
896
         * @param matcher The ignore matcher to append.
897
         * @return A new plugin engine that is equal to this engine but which ignores any type that is matched by the supplied matcher.
898
         */
899
        Engine ignore(ElementMatcher<? super TypeDescription> matcher);
900

901
        /**
902
         * Applies this plugin engine onto a given source and target.
903
         *
904
         * @param source  The source which is treated as a folder or a jar file, if a folder does not exist.
905
         * @param target  The target which is treated as a folder or a jar file, if a folder does not exist.
906
         * @param factory A list of plugin factories to a apply.
907
         * @return A summary of the applied transformation.
908
         * @throws IOException If an I/O error occurs.
909
         */
910
        Summary apply(File source, File target, Plugin.Factory... factory) throws IOException;
911

912
        /**
913
         * Applies this plugin engine onto a given source and target.
914
         *
915
         * @param source    The source which is treated as a folder or a jar file, if a folder does not exist.
916
         * @param target    The target which is treated as a folder or a jar file, if a folder does not exist.
917
         * @param factories A list of plugin factories to a apply.
918
         * @return A summary of the applied transformation.
919
         * @throws IOException If an I/O error occurs.
920
         */
921
        Summary apply(File source, File target, List<? extends Plugin.Factory> factories) throws IOException;
922

923
        /**
924
         * Applies this plugin engine onto a given source and target.
925
         *
926
         * @param source  The source to use.
927
         * @param target  The target to use.
928
         * @param factory A list of plugin factories to a apply.
929
         * @return A summary of the applied transformation.
930
         * @throws IOException If an I/O error occurs.
931
         */
932
        Summary apply(Source source, Target target, Plugin.Factory... factory) throws IOException;
933

934
        /**
935
         * Applies this plugin engine onto a given source and target.
936
         *
937
         * @param source    The source to use.
938
         * @param target    The target to use.
939
         * @param factories A list of plugin factories to a apply.
940
         * @return A summary of the applied transformation.
941
         * @throws IOException If an I/O error occurs.
942
         */
943
        Summary apply(Source source, Target target, List<? extends Plugin.Factory> factories) throws IOException;
944

945
        /**
946
         * A type strategy determines the transformation that is applied to a type description.
947
         */
948
        interface TypeStrategy {
949

950
            /**
951
             * Creates a builder for a given type.
952
             *
953
             * @param byteBuddy        The Byte Buddy instance to use.
954
             * @param typeDescription  The type being transformed.
955
             * @param classFileLocator A class file locator for finding the type's class file.
956
             * @return A dynamic type builder for the provided type.
957
             */
958
            DynamicType.Builder<?> builder(ByteBuddy byteBuddy, TypeDescription typeDescription, ClassFileLocator classFileLocator);
959

960
            /**
961
             * Default implementations for type strategies.
962
             */
963
            enum Default implements TypeStrategy {
1✔
964

965
                /**
966
                 * A type strategy that redefines a type's methods.
967
                 */
968
                REDEFINE {
1✔
969
                    /**
970
                     * {@inheritDoc}
971
                     */
972
                    public DynamicType.Builder<?> builder(ByteBuddy byteBuddy, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
973
                        return byteBuddy.redefine(typeDescription, classFileLocator);
1✔
974
                    }
975
                },
976

977
                /**
978
                 * A type strategy that rebases a type's methods.
979
                 */
980
                REBASE {
1✔
981
                    /**
982
                     * {@inheritDoc}
983
                     */
984
                    public DynamicType.Builder<?> builder(ByteBuddy byteBuddy, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
985
                        return byteBuddy.rebase(typeDescription, classFileLocator);
1✔
986
                    }
987
                },
988

989
                /**
990
                 * A type strategy that decorates a type.
991
                 */
992
                DECORATE {
1✔
993
                    /**
994
                     * {@inheritDoc}
995
                     */
996
                    public DynamicType.Builder<?> builder(ByteBuddy byteBuddy, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
997
                        return byteBuddy.decorate(typeDescription, classFileLocator);
1✔
998
                    }
999
                }
1000
            }
1001

1002
            /**
1003
             * A type strategy that represents a given {@link EntryPoint} for a build tool.
1004
             */
1005
            @HashCodeAndEqualsPlugin.Enhance
1006
            class ForEntryPoint implements TypeStrategy {
1007

1008
                /**
1009
                 * The represented entry point.
1010
                 */
1011
                private final EntryPoint entryPoint;
1012

1013
                /**
1014
                 * A method name transformer to use for rebasements.
1015
                 */
1016
                private final MethodNameTransformer methodNameTransformer;
1017

1018
                /**
1019
                 * Creates a new type stratrgy for an entry point.
1020
                 *
1021
                 * @param entryPoint            The represented entry point.
1022
                 * @param methodNameTransformer A method name transformer to use for rebasements.
1023
                 */
1024
                public ForEntryPoint(EntryPoint entryPoint, MethodNameTransformer methodNameTransformer) {
1✔
1025
                    this.entryPoint = entryPoint;
1✔
1026
                    this.methodNameTransformer = methodNameTransformer;
1✔
1027
                }
1✔
1028

1029
                /**
1030
                 * {@inheritDoc}
1031
                 */
1032
                public DynamicType.Builder<?> builder(ByteBuddy byteBuddy, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
1033
                    return entryPoint.transform(typeDescription, byteBuddy, classFileLocator, methodNameTransformer);
1✔
1034
                }
1035
            }
1036
        }
1037

1038
        /**
1039
         * A pool strategy determines the creation of a {@link TypePool} for a plugin engine application.
1040
         */
1041
        interface PoolStrategy {
1042

1043
            /**
1044
             * Creates a type pool.
1045
             *
1046
             * @param classFileLocator The class file locator to use.
1047
             * @return An approptiate type pool.
1048
             */
1049
            TypePool typePool(ClassFileLocator classFileLocator);
1050

1051
            /**
1052
             * A default implementation of a pool strategy where type descriptions are resolved lazily.
1053
             */
1054
            enum Default implements PoolStrategy {
1✔
1055

1056
                /**
1057
                 * Enables faster class file parsing that does not process debug information of a class file.
1058
                 */
1059
                FAST(TypePool.Default.ReaderMode.FAST),
1✔
1060

1061
                /**
1062
                 * Enables extended class file parsing that extracts parameter names from debug information, if available.
1063
                 */
1064
                EXTENDED(TypePool.Default.ReaderMode.EXTENDED);
1✔
1065

1066
                /**
1067
                 * This strategy's reader mode.
1068
                 */
1069
                private final TypePool.Default.ReaderMode readerMode;
1070

1071
                /**
1072
                 * Creates a default pool strategy.
1073
                 *
1074
                 * @param readerMode This strategy's reader mode.
1075
                 */
1076
                Default(TypePool.Default.ReaderMode readerMode) {
1✔
1077
                    this.readerMode = readerMode;
1✔
1078
                }
1✔
1079

1080
                /**
1081
                 * {@inheritDoc}
1082
                 */
1083
                public TypePool typePool(ClassFileLocator classFileLocator) {
1084
                    return new TypePool.Default.WithLazyResolution(new TypePool.CacheProvider.Simple(),
1✔
1085
                            classFileLocator,
1086
                            readerMode,
1087
                            TypePool.ClassLoading.ofPlatformLoader());
1✔
1088
                }
1089
            }
1090

1091
            /**
1092
             * A pool strategy that resolves type descriptions eagerly. This can avoid additional overhead if the
1093
             * majority of types is assumed to be resolved eventually.
1094
             */
1095
            enum Eager implements PoolStrategy {
1✔
1096

1097
                /**
1098
                 * Enables faster class file parsing that does not process debug information of a class file.
1099
                 */
1100
                FAST(TypePool.Default.ReaderMode.FAST),
1✔
1101

1102
                /**
1103
                 * Enables extended class file parsing that extracts parameter names from debug information, if available.
1104
                 */
1105
                EXTENDED(TypePool.Default.ReaderMode.EXTENDED);
1✔
1106

1107
                /**
1108
                 * This strategy's reader mode.
1109
                 */
1110
                private final TypePool.Default.ReaderMode readerMode;
1111

1112
                /**
1113
                 * Creates an eager pool strategy.
1114
                 *
1115
                 * @param readerMode This strategy's reader mode.
1116
                 */
1117
                Eager(TypePool.Default.ReaderMode readerMode) {
1✔
1118
                    this.readerMode = readerMode;
1✔
1119
                }
1✔
1120

1121
                /**
1122
                 * {@inheritDoc}
1123
                 */
1124
                public TypePool typePool(ClassFileLocator classFileLocator) {
1125
                    return new TypePool.Default(new TypePool.CacheProvider.Simple(),
1✔
1126
                            classFileLocator,
1127
                            readerMode,
1128
                            TypePool.ClassLoading.ofPlatformLoader());
1✔
1129
                }
1130
            }
1131
        }
1132

1133
        /**
1134
         * An error handler that is used during a plugin engine application.
1135
         */
1136
        interface ErrorHandler {
1137

1138
            /**
1139
             * Invoked if an error occured during a plugin's application on a given type.
1140
             *
1141
             * @param typeDescription The type being matched or transformed.
1142
             * @param plugin          The plugin being applied.
1143
             * @param throwable       The throwable that caused the error.
1144
             */
1145
            void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable);
1146

1147
            /**
1148
             * Invoked after the application of all plugins was attempted if at least one error occured during handling a given type.
1149
             *
1150
             * @param typeDescription The type being transformed.
1151
             * @param throwables      The throwables that caused errors during the application.
1152
             */
1153
            void onError(TypeDescription typeDescription, List<Throwable> throwables);
1154

1155
            /**
1156
             * Invoked at the end of the build if at least one type transformation failed.
1157
             *
1158
             * @param throwables A mapping of types that failed during transformation to the errors that were caught.
1159
             */
1160
            void onError(Map<TypeDescription, List<Throwable>> throwables);
1161

1162
            /**
1163
             * Invoked at the end of the build if a plugin could not be closed.
1164
             *
1165
             * @param plugin    The plugin that could not be closed.
1166
             * @param throwable The error that was caused when the plugin was attempted to be closed.
1167
             */
1168
            void onError(Plugin plugin, Throwable throwable);
1169

1170
            /**
1171
             * Invoked if a type transformation implied a live initializer.
1172
             *
1173
             * @param typeDescription The type that was transformed.
1174
             * @param definingType    The type that implies the initializer which might be the type itself or an auxiliary type.
1175
             */
1176
            void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType);
1177

1178
            /**
1179
             * Invoked if a type could not be resolved.
1180
             *
1181
             * @param typeName The name of the unresolved type.
1182
             */
1183
            void onUnresolved(String typeName);
1184

1185
            /**
1186
             * Invoked when a manifest was found or found missing.
1187
             *
1188
             * @param manifest The located manifest or {@code null} if no manifest was found.
1189
             */
1190
            void onManifest(@MaybeNull Manifest manifest);
1191

1192
            /**
1193
             * Invoked if a resource that is not a class file is discovered.
1194
             *
1195
             * @param name The name of the discovered resource.
1196
             */
1197
            void onResource(String name);
1198

1199
            /**
1200
             * An implementation of an error handler that fails the plugin engine application.
1201
             */
1202
            enum Failing implements ErrorHandler {
1✔
1203

1204
                /**
1205
                 * An error handler that fails the build immediatly on the first error.
1206
                 */
1207
                FAIL_FAST {
1✔
1208
                    /**
1209
                     * {@inheritDoc}
1210
                     */
1211
                    public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1212
                        throw new IllegalStateException("Failed to transform " + typeDescription + " using " + plugin, throwable);
1✔
1213
                    }
1214

1215
                    /**
1216
                     * {@inheritDoc}
1217
                     */
1218
                    public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1219
                        throw new IllegalStateException("Failed to transform " + typeDescription + ": " + throwables);
1✔
1220
                    }
1221

1222
                    /**
1223
                     * {@inheritDoc}
1224
                     */
1225
                    public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1226
                        throw new IllegalStateException("Failed to transform at least one type: " + throwables);
1✔
1227
                    }
1228
                },
1229

1230
                /**
1231
                 * An error handler that fails the build after applying all plugins if at least one plugin failed.
1232
                 */
1233
                FAIL_AFTER_TYPE {
1✔
1234
                    /**
1235
                     * {@inheritDoc}
1236
                     */
1237
                    public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1238
                        /* do nothing */
1239
                    }
1✔
1240

1241
                    /**
1242
                     * {@inheritDoc}
1243
                     */
1244
                    public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1245
                        throw new IllegalStateException("Failed to transform " + typeDescription + ": " + throwables);
1✔
1246
                    }
1247

1248
                    /**
1249
                     * {@inheritDoc}
1250
                     */
1251
                    public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1252
                        throw new IllegalStateException("Failed to transform at least one type: " + throwables);
1✔
1253
                    }
1254
                },
1255

1256
                /**
1257
                 * An error handler that fails the build after transforming all types if at least one plugin failed.
1258
                 */
1259
                FAIL_LAST {
1✔
1260
                    /**
1261
                     * {@inheritDoc}
1262
                     */
1263
                    public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1264
                        /* do nothing */
1265
                    }
1✔
1266

1267
                    /**
1268
                     * {@inheritDoc}
1269
                     */
1270
                    public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1271
                        /* do nothing */
1272
                    }
1✔
1273

1274
                    /**
1275
                     * {@inheritDoc}
1276
                     */
1277
                    public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1278
                        throw new IllegalStateException("Failed to transform at least one type: " + throwables);
1✔
1279
                    }
1280
                };
1281

1282
                /**
1283
                 * {@inheritDoc}
1284
                 */
1285
                public void onError(Plugin plugin, Throwable throwable) {
1286
                    throw new IllegalStateException("Failed to close plugin " + plugin, throwable);
1✔
1287
                }
1288

1289
                /**
1290
                 * {@inheritDoc}
1291
                 */
1292
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1293
                    /* do nothing */
1294
                }
1✔
1295

1296
                /**
1297
                 * {@inheritDoc}
1298
                 */
1299
                public void onUnresolved(String typeName) {
1300
                    /* do nothing */
1301
                }
1✔
1302

1303
                /**
1304
                 * {@inheritDoc}
1305
                 */
1306
                public void onManifest(Manifest manifest) {
1307
                    /* do nothing */
1308
                }
1✔
1309

1310
                /**
1311
                 * {@inheritDoc}
1312
                 */
1313
                public void onResource(String name) {
1314
                    /* do nothing */
1315
                }
1✔
1316
            }
1317

1318
            /**
1319
             * An error handler that enforces certain properties of the transformation.
1320
             */
1321
            enum Enforcing implements ErrorHandler {
1✔
1322

1323
                /**
1324
                 * Enforces that all types could be resolved.
1325
                 */
1326
                ALL_TYPES_RESOLVED {
1✔
1327
                    @Override
1328
                    public void onUnresolved(String typeName) {
1329
                        throw new IllegalStateException("Failed to resolve type description for " + typeName);
1✔
1330
                    }
1331
                },
1332

1333
                /**
1334
                 * Enforces that no type has a live initializer.
1335
                 */
1336
                NO_LIVE_INITIALIZERS {
1✔
1337
                    @Override
1338
                    public void onLiveInitializer(TypeDescription typeDescription, TypeDescription initializedType) {
1339
                        throw new IllegalStateException("Failed to instrument " + typeDescription + " due to live initializer for " + initializedType);
1✔
1340
                    }
1341
                },
1342

1343
                /**
1344
                 * Enforces that a source only produces class files.
1345
                 */
1346
                CLASS_FILES_ONLY {
1✔
1347
                    @Override
1348
                    public void onResource(String name) {
1349
                        throw new IllegalStateException("Discovered a resource when only class files were allowed: " + name);
1✔
1350
                    }
1351
                },
1352

1353
                /**
1354
                 * Enforces that a manifest is written to a target.
1355
                 */
1356
                MANIFEST_REQUIRED {
1✔
1357
                    @Override
1358
                    public void onManifest(@MaybeNull Manifest manifest) {
1359
                        if (manifest == null) {
1✔
1360
                            throw new IllegalStateException("Required a manifest but no manifest was found");
1✔
1361
                        }
1362
                    }
1✔
1363
                };
1364

1365
                /**
1366
                 * {@inheritDoc}
1367
                 */
1368
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1369
                    /* do nothing */
1370
                }
1✔
1371

1372
                /**
1373
                 * {@inheritDoc}
1374
                 */
1375
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1376
                    /* do nothing */
1377
                }
1✔
1378

1379
                /**
1380
                 * {@inheritDoc}
1381
                 */
1382
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1383
                    /* do nothing */
1384
                }
1✔
1385

1386
                /**
1387
                 * {@inheritDoc}
1388
                 */
1389
                public void onError(Plugin plugin, Throwable throwable) {
1390
                    /* do nothing */
1391
                }
1✔
1392

1393
                /**
1394
                 * {@inheritDoc}
1395
                 */
1396
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1397
                    /* do nothing */
1398
                }
1✔
1399

1400
                /**
1401
                 * {@inheritDoc}
1402
                 */
1403
                public void onUnresolved(String typeName) {
1404
                    /* do nothing */
1405
                }
1✔
1406

1407
                /**
1408
                 * {@inheritDoc}
1409
                 */
1410
                public void onManifest(@MaybeNull Manifest manifest) {
1411
                    /* do nothing */
1412
                }
1✔
1413

1414
                /**
1415
                 * {@inheritDoc}
1416
                 */
1417
                public void onResource(String name) {
1418
                    /* do nothing */
1419
                }
1✔
1420
            }
1421

1422
            /**
1423
             * A compound error handler.
1424
             */
1425
            class Compound implements ErrorHandler {
1426

1427
                /**
1428
                 * The error handlers that are represented by this instance.
1429
                 */
1430
                private final List<ErrorHandler> errorHandlers;
1431

1432
                /**
1433
                 * Creates a new compound error handler.
1434
                 *
1435
                 * @param errorHandler The error handlers that are represented by this instance.
1436
                 */
1437
                public Compound(ErrorHandler... errorHandler) {
1438
                    this(Arrays.asList(errorHandler));
1✔
1439
                }
1✔
1440

1441
                /**
1442
                 * Creates a new compound error handler.
1443
                 *
1444
                 * @param errorHandlers The error handlers that are represented by this instance.
1445
                 */
1446
                public Compound(List<? extends ErrorHandler> errorHandlers) {
1✔
1447
                    this.errorHandlers = new ArrayList<ErrorHandler>();
1✔
1448
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1449
                        if (errorHandler instanceof Compound) {
1✔
UNCOV
1450
                            this.errorHandlers.addAll(((Compound) errorHandler).errorHandlers);
×
1451
                        } else if (!(errorHandler instanceof Listener.NoOp)) {
1✔
1452
                            this.errorHandlers.add(errorHandler);
1✔
1453
                        }
1454
                    }
1✔
1455
                }
1✔
1456

1457
                /**
1458
                 * {@inheritDoc}
1459
                 */
1460
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1461
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1462
                        errorHandler.onError(typeDescription, plugin, throwable);
1✔
1463
                    }
1✔
1464
                }
1✔
1465

1466
                /**
1467
                 * {@inheritDoc}
1468
                 */
1469
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1470
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1471
                        errorHandler.onError(typeDescription, throwables);
1✔
1472
                    }
1✔
1473
                }
1✔
1474

1475
                /**
1476
                 * {@inheritDoc}
1477
                 */
1478
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1479
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1480
                        errorHandler.onError(throwables);
1✔
1481
                    }
1✔
1482

1483
                }
1✔
1484

1485
                /**
1486
                 * {@inheritDoc}
1487
                 */
1488
                public void onError(Plugin plugin, Throwable throwable) {
1489
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1490
                        errorHandler.onError(plugin, throwable);
1✔
1491
                    }
1✔
1492
                }
1✔
1493

1494
                /**
1495
                 * {@inheritDoc}
1496
                 */
1497
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1498
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1499
                        errorHandler.onLiveInitializer(typeDescription, definingType);
1✔
1500
                    }
1✔
1501
                }
1✔
1502

1503
                /**
1504
                 * {@inheritDoc}
1505
                 */
1506
                public void onUnresolved(String typeName) {
1507
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1508
                        errorHandler.onUnresolved(typeName);
1✔
1509
                    }
1✔
1510
                }
1✔
1511

1512
                /**
1513
                 * {@inheritDoc}
1514
                 */
1515
                public void onManifest(@MaybeNull Manifest manifest) {
1516
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1517
                        errorHandler.onManifest(manifest);
1✔
1518
                    }
1✔
1519
                }
1✔
1520

1521
                /**
1522
                 * {@inheritDoc}
1523
                 */
1524
                public void onResource(String name) {
1525
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1526
                        errorHandler.onResource(name);
1✔
1527
                    }
1✔
1528
                }
1✔
1529
            }
1530
        }
1531

1532
        /**
1533
         * A listener that is invoked upon any event during a plugin engine application.
1534
         */
1535
        interface Listener extends ErrorHandler {
1536

1537
            /**
1538
             * Invoked upon discovering a type but prior to its resolution.
1539
             *
1540
             * @param typeName The name of the discovered type.
1541
             */
1542
            void onDiscovery(String typeName);
1543

1544
            /**
1545
             * Invoked after a type was transformed using a specific plugin.
1546
             *
1547
             * @param typeDescription The type being transformed.
1548
             * @param plugin          The plugin that was applied.
1549
             */
1550
            void onTransformation(TypeDescription typeDescription, Plugin plugin);
1551

1552
            /**
1553
             * Invoked after a type was transformed using at least one plugin.
1554
             *
1555
             * @param typeDescription The type being transformed.
1556
             * @param plugins         A list of plugins that were applied.
1557
             */
1558
            void onTransformation(TypeDescription typeDescription, List<Plugin> plugins);
1559

1560
            /**
1561
             * Invoked if a type description is ignored by a given plugin. This callback is not invoked,
1562
             * if the ignore type matcher excluded a type from transformation.
1563
             *
1564
             * @param typeDescription The type being transformed.
1565
             * @param plugin          The plugin that ignored the given type.
1566
             */
1567
            void onIgnored(TypeDescription typeDescription, Plugin plugin);
1568

1569
            /**
1570
             * Invoked if one or more plugins did not transform a type. This callback is also invoked if an
1571
             * ignore matcher excluded a type from transformation.
1572
             *
1573
             * @param typeDescription The type being transformed.
1574
             * @param plugins         the plugins that ignored the type.
1575
             */
1576
            void onIgnored(TypeDescription typeDescription, List<Plugin> plugins);
1577

1578
            /**
1579
             * Invoked upon completing handling a type that was either transformed or ignored.
1580
             *
1581
             * @param typeDescription The type that was transformed.
1582
             */
1583
            void onComplete(TypeDescription typeDescription);
1584

1585
            /**
1586
             * A non-operational listener.
1587
             */
1588
            enum NoOp implements Listener {
1✔
1589

1590
                /**
1591
                 * The singleton instance.
1592
                 */
1593
                INSTANCE;
1✔
1594

1595
                /**
1596
                 * {@inheritDoc}
1597
                 */
1598
                public void onDiscovery(String typeName) {
1599
                    /* do nothing */
UNCOV
1600
                }
×
1601

1602
                /**
1603
                 * {@inheritDoc}
1604
                 */
1605
                public void onTransformation(TypeDescription typeDescription, Plugin plugin) {
1606
                    /* do nothing */
1607
                }
1✔
1608

1609
                /**
1610
                 * {@inheritDoc}
1611
                 */
1612
                public void onTransformation(TypeDescription typeDescription, List<Plugin> plugins) {
1613
                    /* do nothing */
1614
                }
1✔
1615

1616
                /**
1617
                 * {@inheritDoc}
1618
                 */
1619
                public void onIgnored(TypeDescription typeDescription, Plugin plugin) {
1620
                    /* do nothing */
1621
                }
1✔
1622

1623
                /**
1624
                 * {@inheritDoc}
1625
                 */
1626
                public void onIgnored(TypeDescription typeDescription, List<Plugin> plugins) {
1627
                    /* do nothing */
1628
                }
1✔
1629

1630
                /**
1631
                 * {@inheritDoc}
1632
                 */
1633
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1634
                    /* do nothing */
1635
                }
1✔
1636

1637
                /**
1638
                 * {@inheritDoc}
1639
                 */
1640
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1641
                    /* do nothing */
1642
                }
1✔
1643

1644
                /**
1645
                 * {@inheritDoc}
1646
                 */
1647
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1648
                    /* do nothing */
1649
                }
1✔
1650

1651
                /**
1652
                 * {@inheritDoc}
1653
                 */
1654
                public void onError(Plugin plugin, Throwable throwable) {
1655
                    /* do nothing */
1656
                }
1✔
1657

1658
                /**
1659
                 * {@inheritDoc}
1660
                 */
1661
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1662
                    /* do nothing */
1663
                }
1✔
1664

1665
                /**
1666
                 * {@inheritDoc}
1667
                 */
1668
                public void onComplete(TypeDescription typeDescription) {
1669
                    /* do nothing */
1670
                }
1✔
1671

1672
                /**
1673
                 * {@inheritDoc}
1674
                 */
1675
                public void onUnresolved(String typeName) {
1676
                    /* do nothing */
1677
                }
1✔
1678

1679
                /**
1680
                 * {@inheritDoc}
1681
                 */
1682
                public void onManifest(@MaybeNull Manifest manifest) {
1683
                    /* do nothing */
1684
                }
1✔
1685

1686
                /**
1687
                 * {@inheritDoc}
1688
                 */
1689
                public void onResource(String name) {
1690
                    /* do nothing */
1691
                }
1✔
1692
            }
1693

1694
            /**
1695
             * An adapter that implements all methods non-operational.
1696
             */
1697
            abstract class Adapter implements Listener {
1✔
1698

1699
                /**
1700
                 * {@inheritDoc}
1701
                 */
1702
                public void onDiscovery(String typeName) {
1703
                    /* do nothing */
1704
                }
1✔
1705

1706
                /**
1707
                 * {@inheritDoc}
1708
                 */
1709
                public void onTransformation(TypeDescription typeDescription, Plugin plugin) {
1710
                    /* do nothing */
1711
                }
1✔
1712

1713
                /**
1714
                 * {@inheritDoc}
1715
                 */
1716
                public void onTransformation(TypeDescription typeDescription, List<Plugin> plugins) {
1717
                    /* do nothing */
1718
                }
1✔
1719

1720
                /**
1721
                 * {@inheritDoc}
1722
                 */
1723
                public void onIgnored(TypeDescription typeDescription, Plugin plugin) {
1724
                    /* do nothing */
1725
                }
1✔
1726

1727
                /**
1728
                 * {@inheritDoc}
1729
                 */
1730
                public void onIgnored(TypeDescription typeDescription, List<Plugin> plugins) {
1731
                    /* do nothing */
1732
                }
1✔
1733

1734
                /**
1735
                 * {@inheritDoc}
1736
                 */
1737
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1738
                    /* do nothing */
1739
                }
1✔
1740

1741
                /**
1742
                 * {@inheritDoc}
1743
                 */
1744
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1745
                    /* do nothing */
1746
                }
1✔
1747

1748
                /**
1749
                 * {@inheritDoc}
1750
                 */
1751
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1752
                    /* do nothing */
1753
                }
1✔
1754

1755
                /**
1756
                 * {@inheritDoc}
1757
                 */
1758
                public void onError(Plugin plugin, Throwable throwable) {
1759
                    /* do nothing */
1760
                }
1✔
1761

1762
                /**
1763
                 * {@inheritDoc}
1764
                 */
1765
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1766
                    /* do nothing */
1767
                }
1✔
1768

1769
                /**
1770
                 * {@inheritDoc}
1771
                 */
1772
                public void onComplete(TypeDescription typeDescription) {
1773
                    /* do nothing */
1774
                }
1✔
1775

1776
                /**
1777
                 * {@inheritDoc}
1778
                 */
1779
                public void onUnresolved(String typeName) {
1780
                    /* do nothing */
1781
                }
1✔
1782

1783
                /**
1784
                 * {@inheritDoc}
1785
                 */
1786
                public void onManifest(@MaybeNull Manifest manifest) {
1787
                    /* do nothing */
1788
                }
1✔
1789

1790
                /**
1791
                 * {@inheritDoc}
1792
                 */
1793
                public void onResource(String name) {
1794
                    /* do nothing */
1795
                }
1✔
1796
            }
1797

1798
            /**
1799
             * A listener that forwards significant events of a plugin engine application to a {@link PrintStream}.
1800
             */
1801
            @HashCodeAndEqualsPlugin.Enhance
1802
            class StreamWriting extends Adapter {
1803

1804
                /**
1805
                 * The prefix that is appended to all written messages.
1806
                 */
1807
                protected static final String PREFIX = "[Byte Buddy]";
1808

1809
                /**
1810
                 * The print stream to delegate to.
1811
                 */
1812
                private final PrintStream printStream;
1813

1814
                /**
1815
                 * Creates a new stream writing listener.
1816
                 *
1817
                 * @param printStream The print stream to delegate to.
1818
                 */
1819
                public StreamWriting(PrintStream printStream) {
1✔
1820
                    this.printStream = printStream;
1✔
1821
                }
1✔
1822

1823
                /**
1824
                 * Creates a stream writing listener that prints all events on {@link System#out}.
1825
                 *
1826
                 * @return A listener that writes events to the system output stream.
1827
                 */
1828
                public static StreamWriting toSystemOut() {
1829
                    return new StreamWriting(System.out);
1✔
1830
                }
1831

1832
                /**
1833
                 * Creates a stream writing listener that prints all events on {@link System#err}.
1834
                 *
1835
                 * @return A listener that writes events to the system error stream.
1836
                 */
1837
                public static StreamWriting toSystemError() {
1838
                    return new StreamWriting(System.err);
1✔
1839
                }
1840

1841
                /**
1842
                 * Returns a new listener that only prints transformation and error events.
1843
                 *
1844
                 * @return A new listener that only prints transformation and error events.
1845
                 */
1846
                public Listener withTransformationsOnly() {
1847
                    return new WithTransformationsOnly(this);
1✔
1848
                }
1849

1850
                /**
1851
                 * Returns a new listener that only prints error events.
1852
                 *
1853
                 * @return A new listener that only prints error events.
1854
                 */
1855
                public Listener withErrorsOnly() {
1856
                    return new WithErrorsOnly(this);
1✔
1857
                }
1858

1859
                /**
1860
                 * {@inheritDoc}
1861
                 */
1862
                public void onDiscovery(String typeName) {
UNCOV
1863
                    printStream.printf(PREFIX + " DISCOVERY %s", typeName);
×
UNCOV
1864
                }
×
1865

1866
                /**
1867
                 * {@inheritDoc}
1868
                 */
1869
                public void onTransformation(TypeDescription typeDescription, Plugin plugin) {
1870
                    printStream.printf(PREFIX + " TRANSFORM %s for %s", typeDescription, plugin);
1✔
1871
                }
1✔
1872

1873
                /**
1874
                 * {@inheritDoc}
1875
                 */
1876
                public void onIgnored(TypeDescription typeDescription, Plugin plugin) {
1877
                    printStream.printf(PREFIX + " IGNORE %s for %s", typeDescription, plugin);
1✔
1878
                }
1✔
1879

1880
                /**
1881
                 * {@inheritDoc}
1882
                 */
1883
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1884
                    synchronized (printStream) {
1✔
1885
                        printStream.printf(PREFIX + " ERROR %s for %s", typeDescription, plugin);
1✔
1886
                        throwable.printStackTrace(printStream);
1✔
1887
                    }
1✔
1888
                }
1✔
1889

1890
                /**
1891
                 * {@inheritDoc}
1892
                 */
1893
                public void onError(Plugin plugin, Throwable throwable) {
1894
                    synchronized (printStream) {
1✔
1895
                        printStream.printf(PREFIX + " ERROR %s", plugin);
1✔
1896
                        throwable.printStackTrace(printStream);
1✔
1897
                    }
1✔
1898
                }
1✔
1899

1900
                /**
1901
                 * {@inheritDoc}
1902
                 */
1903
                public void onUnresolved(String typeName) {
1904
                    printStream.printf(PREFIX + " UNRESOLVED %s", typeName);
1✔
1905
                }
1✔
1906

1907
                /**
1908
                 * {@inheritDoc}
1909
                 */
1910
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1911
                    printStream.printf(PREFIX + " LIVE %s on %s", typeDescription, definingType);
1✔
1912
                }
1✔
1913

1914
                /**
1915
                 * {@inheritDoc}
1916
                 */
1917
                public void onComplete(TypeDescription typeDescription) {
1918
                    printStream.printf(PREFIX + " COMPLETE %s", typeDescription);
1✔
1919
                }
1✔
1920

1921
                /**
1922
                 * {@inheritDoc}
1923
                 */
1924
                public void onManifest(@MaybeNull Manifest manifest) {
1925
                    printStream.printf(PREFIX + " MANIFEST %b", manifest != null);
1✔
1926
                }
1✔
1927

1928
                /**
1929
                 * {@inheritDoc}
1930
                 */
1931
                public void onResource(String name) {
1932
                    printStream.printf(PREFIX + " RESOURCE %s", name);
1✔
1933
                }
1✔
1934
            }
1935

1936
            /**
1937
             * A decorator for another listener to only print transformation and error events.
1938
             */
1939
            @HashCodeAndEqualsPlugin.Enhance
1940
            class WithTransformationsOnly extends Adapter {
1941

1942
                /**
1943
                 * The delegate to forward events to.
1944
                 */
1945
                private final Listener delegate;
1946

1947
                /**
1948
                 * Creates a new listener decorator that filter any event that is not related to transformation or errors.
1949
                 *
1950
                 * @param delegate The delegate to forward events to.
1951
                 */
1952
                public WithTransformationsOnly(Listener delegate) {
1✔
1953
                    this.delegate = delegate;
1✔
1954
                }
1✔
1955

1956
                @Override
1957
                public void onTransformation(TypeDescription typeDescription, Plugin plugin) {
1958
                    delegate.onTransformation(typeDescription, plugin);
1✔
1959
                }
1✔
1960

1961
                @Override
1962
                public void onTransformation(TypeDescription typeDescription, List<Plugin> plugins) {
1963
                    delegate.onTransformation(typeDescription, plugins);
1✔
1964
                }
1✔
1965

1966
                @Override
1967
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1968
                    delegate.onError(typeDescription, plugin, throwable);
1✔
1969
                }
1✔
1970

1971
                @Override
1972
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1973
                    delegate.onError(typeDescription, throwables);
1✔
1974
                }
1✔
1975

1976
                @Override
1977
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1978
                    delegate.onError(throwables);
1✔
1979
                }
1✔
1980

1981
                @Override
1982
                public void onError(Plugin plugin, Throwable throwable) {
1983
                    delegate.onError(plugin, throwable);
1✔
1984
                }
1✔
1985
            }
1986

1987
            /**
1988
             * A decorator for another listener to only print error events.
1989
             */
1990
            @HashCodeAndEqualsPlugin.Enhance
1991
            class WithErrorsOnly extends Adapter {
1992

1993
                /**
1994
                 * The delegate to forward events to.
1995
                 */
1996
                private final Listener delegate;
1997

1998
                /**
1999
                 * Creates a new listener decorator that filter any event that is not related to errors.
2000
                 *
2001
                 * @param delegate The delegate to forward events to.
2002
                 */
2003
                public WithErrorsOnly(Listener delegate) {
1✔
2004
                    this.delegate = delegate;
1✔
2005
                }
1✔
2006

2007
                @Override
2008
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
2009
                    delegate.onError(typeDescription, plugin, throwable);
1✔
2010
                }
1✔
2011

2012
                @Override
2013
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
2014
                    delegate.onError(typeDescription, throwables);
1✔
2015
                }
1✔
2016

2017
                @Override
2018
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
2019
                    delegate.onError(throwables);
1✔
2020
                }
1✔
2021

2022
                @Override
2023
                public void onError(Plugin plugin, Throwable throwable) {
2024
                    delegate.onError(plugin, throwable);
1✔
2025
                }
1✔
2026
            }
2027

2028
            /**
2029
             * A listener decorator that forwards events to an error handler if they are applicable.
2030
             */
2031
            @HashCodeAndEqualsPlugin.Enhance
2032
            class ForErrorHandler extends Adapter {
2033

2034
                /**
2035
                 * The error handler to delegate to.
2036
                 */
2037
                private final ErrorHandler errorHandler;
2038

2039
                /**
2040
                 * Creates a new listener representation for an error handler.
2041
                 *
2042
                 * @param errorHandler The error handler to delegate to.
2043
                 */
2044
                public ForErrorHandler(ErrorHandler errorHandler) {
1✔
2045
                    this.errorHandler = errorHandler;
1✔
2046
                }
1✔
2047

2048
                @Override
2049
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
2050
                    errorHandler.onError(typeDescription, plugin, throwable);
1✔
2051
                }
1✔
2052

2053
                @Override
2054
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
2055
                    errorHandler.onError(typeDescription, throwables);
1✔
2056
                }
1✔
2057

2058
                @Override
2059
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
2060
                    errorHandler.onError(throwables);
1✔
2061
                }
1✔
2062

2063
                @Override
2064
                public void onError(Plugin plugin, Throwable throwable) {
2065
                    errorHandler.onError(plugin, throwable);
1✔
2066
                }
1✔
2067

2068
                @Override
2069
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
2070
                    errorHandler.onLiveInitializer(typeDescription, definingType);
1✔
2071
                }
1✔
2072

2073
                @Override
2074
                public void onUnresolved(String typeName) {
2075
                    errorHandler.onUnresolved(typeName);
1✔
2076
                }
1✔
2077

2078
                @Override
2079
                public void onManifest(@MaybeNull Manifest manifest) {
2080
                    errorHandler.onManifest(manifest);
1✔
2081
                }
1✔
2082

2083
                @Override
2084
                public void onResource(String name) {
2085
                    errorHandler.onResource(name);
1✔
2086
                }
1✔
2087
            }
2088

2089
            /**
2090
             * A compound listener.
2091
             */
2092
            @HashCodeAndEqualsPlugin.Enhance
2093
            class Compound implements Listener {
2094

2095
                /**
2096
                 * A list of listeners that are represented by this compound instance.
2097
                 */
2098
                private final List<Listener> listeners;
2099

2100
                /**
2101
                 * Creates a new compound listener.
2102
                 *
2103
                 * @param listener A list of listeners that are represented by this compound instance.
2104
                 */
2105
                public Compound(Listener... listener) {
2106
                    this(Arrays.asList(listener));
1✔
2107
                }
1✔
2108

2109
                /**
2110
                 * Creates a new compound listener.
2111
                 *
2112
                 * @param listeners A list of listeners that are represented by this compound instance.
2113
                 */
2114
                public Compound(List<? extends Listener> listeners) {
1✔
2115
                    this.listeners = new ArrayList<Listener>();
1✔
2116
                    for (Listener listener : listeners) {
1✔
2117
                        if (listener instanceof Listener.Compound) {
1✔
2118
                            this.listeners.addAll(((Listener.Compound) listener).listeners);
1✔
2119
                        } else if (!(listener instanceof NoOp)) {
1✔
2120
                            this.listeners.add(listener);
1✔
2121
                        }
2122
                    }
1✔
2123
                }
1✔
2124

2125
                /**
2126
                 * {@inheritDoc}
2127
                 */
2128
                public void onDiscovery(String typeName) {
2129
                    for (Listener listener : listeners) {
1✔
2130
                        listener.onDiscovery(typeName);
1✔
2131
                    }
1✔
2132
                }
1✔
2133

2134
                /**
2135
                 * {@inheritDoc}
2136
                 */
2137
                public void onTransformation(TypeDescription typeDescription, Plugin plugin) {
2138
                    for (Listener listener : listeners) {
1✔
2139
                        listener.onTransformation(typeDescription, plugin);
1✔
2140
                    }
1✔
2141
                }
1✔
2142

2143
                /**
2144
                 * {@inheritDoc}
2145
                 */
2146
                public void onTransformation(TypeDescription typeDescription, List<Plugin> plugins) {
2147
                    for (Listener listener : listeners) {
1✔
2148
                        listener.onTransformation(typeDescription, plugins);
1✔
2149
                    }
1✔
2150
                }
1✔
2151

2152
                /**
2153
                 * {@inheritDoc}
2154
                 */
2155
                public void onIgnored(TypeDescription typeDescription, Plugin plugin) {
2156
                    for (Listener listener : listeners) {
1✔
2157
                        listener.onIgnored(typeDescription, plugin);
1✔
2158
                    }
1✔
2159
                }
1✔
2160

2161
                /**
2162
                 * {@inheritDoc}
2163
                 */
2164
                public void onIgnored(TypeDescription typeDescription, List<Plugin> plugins) {
2165
                    for (Listener listener : listeners) {
1✔
2166
                        listener.onIgnored(typeDescription, plugins);
1✔
2167
                    }
1✔
2168
                }
1✔
2169

2170
                /**
2171
                 * {@inheritDoc}
2172
                 */
2173
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
2174
                    for (Listener listener : listeners) {
1✔
2175
                        listener.onError(typeDescription, plugin, throwable);
1✔
2176
                    }
1✔
2177
                }
1✔
2178

2179
                /**
2180
                 * {@inheritDoc}
2181
                 */
2182
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
2183
                    for (Listener listener : listeners) {
1✔
2184
                        listener.onError(typeDescription, throwables);
1✔
2185
                    }
1✔
2186
                }
1✔
2187

2188
                /**
2189
                 * {@inheritDoc}
2190
                 */
2191
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
2192
                    for (Listener listener : listeners) {
1✔
2193
                        listener.onError(throwables);
1✔
2194
                    }
1✔
2195
                }
1✔
2196

2197
                /**
2198
                 * {@inheritDoc}
2199
                 */
2200
                public void onError(Plugin plugin, Throwable throwable) {
2201
                    for (Listener listener : listeners) {
1✔
2202
                        listener.onError(plugin, throwable);
1✔
2203
                    }
1✔
2204
                }
1✔
2205

2206
                /**
2207
                 * {@inheritDoc}
2208
                 */
2209
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
2210
                    for (Listener listener : listeners) {
1✔
2211
                        listener.onLiveInitializer(typeDescription, definingType);
1✔
2212
                    }
1✔
2213
                }
1✔
2214

2215
                /**
2216
                 * {@inheritDoc}
2217
                 */
2218
                public void onComplete(TypeDescription typeDescription) {
2219
                    for (Listener listener : listeners) {
1✔
2220
                        listener.onComplete(typeDescription);
1✔
2221
                    }
1✔
2222
                }
1✔
2223

2224
                /**
2225
                 * {@inheritDoc}
2226
                 */
2227
                public void onUnresolved(String typeName) {
2228
                    for (Listener listener : listeners) {
1✔
2229
                        listener.onUnresolved(typeName);
1✔
2230
                    }
1✔
2231
                }
1✔
2232

2233
                /**
2234
                 * {@inheritDoc}
2235
                 */
2236
                public void onManifest(@MaybeNull Manifest manifest) {
2237
                    for (Listener listener : listeners) {
1✔
2238
                        listener.onManifest(manifest);
1✔
2239
                    }
1✔
2240
                }
1✔
2241

2242
                /**
2243
                 * {@inheritDoc}
2244
                 */
2245
                public void onResource(String name) {
2246
                    for (Listener listener : listeners) {
1✔
2247
                        listener.onResource(name);
1✔
2248
                    }
1✔
2249
                }
1✔
2250
            }
2251
        }
2252

2253
        /**
2254
         * A source for a plugin engine provides binary elements to consider for transformation.
2255
         */
2256
        interface Source {
2257

2258
            /**
2259
             * Initiates reading from a source.
2260
             *
2261
             * @return The origin to read from.
2262
             * @throws IOException If an I/O error occurs.
2263
             */
2264
            Origin read() throws IOException;
2265

2266
            /**
2267
             * An origin for elements.
2268
             */
2269
            interface Origin extends Iterable<Element>, Closeable {
2270

2271
                /**
2272
                 * Indicates that no manifest exists.
2273
                 */
2274
                @AlwaysNull
2275
                Manifest NO_MANIFEST = null;
1✔
2276

2277
                /**
2278
                 * Returns the manifest file of the source location or {@code null} if no manifest exists.
2279
                 *
2280
                 * @return This source's manifest or {@code null}.
2281
                 * @throws IOException If an I/O error occurs.
2282
                 */
2283
                @MaybeNull
2284
                Manifest getManifest() throws IOException;
2285

2286
                /**
2287
                 * Creates a class file locator for the represented source. If the class file locator needs to be closed,
2288
                 * it is the responsibility of this origin to close the locator or its underlying resources.
2289
                 *
2290
                 * @param classFileVersion The class file version to consider for multi-release jars or {@code null}
2291
                 *                         if multi-release jars should not be considered.
2292
                 * @return A class file locator for locating class files of this instance.
2293
                 * @throws IOException If an I/O exception occurs.
2294
                 */
2295
                ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException;
2296

2297
                /**
2298
                 * An origin implementation for a jar file.
2299
                 */
2300
                class ForJarFile implements Origin {
2301

2302
                    /**
2303
                     * The represented file.
2304
                     */
2305
                    private final JarFile file;
2306

2307
                    /**
2308
                     * Creates a new origin for a jar file.
2309
                     *
2310
                     * @param file The represented file.
2311
                     */
2312
                    public ForJarFile(JarFile file) {
1✔
2313
                        this.file = file;
1✔
2314
                    }
1✔
2315

2316
                    /**
2317
                     * {@inheritDoc}
2318
                     */
2319
                    @MaybeNull
2320
                    public Manifest getManifest() throws IOException {
2321
                        return file.getManifest();
1✔
2322
                    }
2323

2324
                    /**
2325
                     * {@inheritDoc}
2326
                     */
2327
                    public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException {
2328
                        return classFileVersion == null
1✔
2329
                                ? new ClassFileLocator.ForJarFile(file)
UNCOV
2330
                                : ClassFileLocator.ForJarFile.of(file, classFileVersion);
×
2331
                    }
2332

2333
                    /**
2334
                     * {@inheritDoc}
2335
                     */
2336
                    public void close() throws IOException {
2337
                        file.close();
1✔
2338
                    }
1✔
2339

2340
                    /**
2341
                     * {@inheritDoc}
2342
                     */
2343
                    public Iterator<Element> iterator() {
2344
                        return new JarFileIterator(file.entries());
1✔
2345
                    }
2346

2347
                    /**
2348
                     * An iterator for jar file entries.
2349
                     */
2350
                    protected class JarFileIterator implements Iterator<Element> {
2351

2352
                        /**
2353
                         * The represented enumeration.
2354
                         */
2355
                        private final Enumeration<JarEntry> enumeration;
2356

2357
                        /**
2358
                         * Creates a new jar file iterator.
2359
                         *
2360
                         * @param enumeration The represented enumeration.
2361
                         */
2362
                        protected JarFileIterator(Enumeration<JarEntry> enumeration) {
1✔
2363
                            this.enumeration = enumeration;
1✔
2364
                        }
1✔
2365

2366
                        /**
2367
                         * {@inheritDoc}
2368
                         */
2369
                        public boolean hasNext() {
2370
                            return enumeration.hasMoreElements();
1✔
2371
                        }
2372

2373
                        /**
2374
                         * {@inheritDoc}
2375
                         */
2376
                        public Element next() {
2377
                            return new Element.ForJarEntry(file, enumeration.nextElement());
1✔
2378
                        }
2379

2380
                        /**
2381
                         * {@inheritDoc}
2382
                         */
2383
                        public void remove() {
UNCOV
2384
                            throw new UnsupportedOperationException("remove");
×
2385
                        }
2386
                    }
2387
                }
2388

2389
                /**
2390
                 * An origin that forwards all invocations to a delegate where an {@link ElementMatcher} is applied prior to iteration.
2391
                 */
2392
                @HashCodeAndEqualsPlugin.Enhance
2393
                class Filtering implements Origin {
2394

2395
                    /**
2396
                     * The origin to which invocations are delegated.
2397
                     */
2398
                    private final Origin delegate;
2399

2400
                    /**
2401
                     * The element matcher being used to filter elements.
2402
                     */
2403
                    private final ElementMatcher<Element> matcher;
2404

2405
                    /**
2406
                     * {@code true} if the manifest should be retained.
2407
                     */
2408
                    private final boolean manifest;
2409

2410
                    /**
2411
                     * Creates a new filtering origin that retains the delegated origin's manifest.
2412
                     *
2413
                     * @param delegate The origin to which invocations are delegated.
2414
                     * @param matcher  The element matcher being used to filter elements.
2415
                     */
2416
                    public Filtering(Origin delegate, ElementMatcher<Element> matcher) {
UNCOV
2417
                        this(delegate, matcher, true);
×
UNCOV
2418
                    }
×
2419

2420
                    /**
2421
                     * Creates a new filtering origin.
2422
                     *
2423
                     * @param delegate The origin to which invocations are delegated.
2424
                     * @param matcher  The element matcher being used to filter elements.
2425
                     * @param manifest {@code true} if the manifest should be retained.
2426
                     */
2427
                    public Filtering(Origin delegate, ElementMatcher<Element> matcher, boolean manifest) {
1✔
2428
                        this.delegate = delegate;
1✔
2429
                        this.matcher = matcher;
1✔
2430
                        this.manifest = manifest;
1✔
2431
                    }
1✔
2432

2433
                    /**
2434
                     * {@inheritDoc}
2435
                     */
2436
                    @MaybeNull
2437
                    public Manifest getManifest() throws IOException {
2438
                        return manifest ? delegate.getManifest() : NO_MANIFEST;
1✔
2439
                    }
2440

2441
                    /**
2442
                     * {@inheritDoc}
2443
                     */
2444
                    public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException {
2445
                        return delegate.toClassFileLocator(classFileVersion);
1✔
2446
                    }
2447

2448
                    /**
2449
                     * {@inheritDoc}
2450
                     */
2451
                    public Iterator<Element> iterator() {
2452
                        return new FilteringIterator(delegate.iterator(), matcher);
1✔
2453
                    }
2454

2455
                    /**
2456
                     * {@inheritDoc}
2457
                     */
2458
                    public void close() throws IOException {
2459
                        delegate.close();
1✔
2460
                    }
1✔
2461

2462
                    /**
2463
                     * An iterator that applies a filter to observed elements.
2464
                     */
2465
                    private static class FilteringIterator implements Iterator<Element> {
2466

2467
                        /**
2468
                         * The underlying iterator.
2469
                         */
2470
                        private final Iterator<Element> iterator;
2471

2472
                        /**
2473
                         * The element matcher being used to filter elements.
2474
                         */
2475
                        private final ElementMatcher<Element> matcher;
2476

2477
                        /**
2478
                         * The current element or {@code null} if no further elements are available.
2479
                         */
2480
                        @MaybeNull
2481
                        private Element current;
2482

2483
                        /**
2484
                         * Creates a new filtering iterator.
2485
                         *
2486
                         * @param iterator The underlying iterator.
2487
                         * @param matcher  The element matcher being used to filter elements.
2488
                         */
2489
                        private FilteringIterator(Iterator<Element> iterator, ElementMatcher<Element> matcher) {
1✔
2490
                            this.iterator = iterator;
1✔
2491
                            this.matcher = matcher;
1✔
2492
                            Element element;
2493
                            while (iterator.hasNext()) {
1✔
2494
                                element = iterator.next();
1✔
2495
                                if (matcher.matches(element)) {
1✔
2496
                                    current = element;
1✔
2497
                                    break;
1✔
2498
                                }
2499
                            }
2500
                        }
1✔
2501

2502
                        /**
2503
                         * {@inheritDoc}
2504
                         */
2505
                        public boolean hasNext() {
2506
                            return current != null;
1✔
2507
                        }
2508

2509
                        /**
2510
                         * {@inheritDoc}
2511
                         */
2512
                        public Element next() {
2513
                            if (current == null) {
1✔
UNCOV
2514
                                throw new NoSuchElementException();
×
2515
                            }
2516
                            try {
2517
                                return current;
1✔
2518
                            } finally {
2519
                                current = null;
1✔
2520
                                Element element;
2521
                                while (iterator.hasNext()) {
1✔
2522
                                    element = iterator.next();
1✔
2523
                                    if (matcher.matches(element)) {
1✔
2524
                                        current = element;
1✔
2525
                                        break;
1✔
2526
                                    }
2527
                                }
2528
                            }
2529
                        }
2530

2531
                        /**
2532
                         * {@inheritDoc}
2533
                         */
2534
                        public void remove() {
UNCOV
2535
                            iterator.remove();
×
UNCOV
2536
                        }
×
2537
                    }
2538
                }
2539
            }
2540

2541
            /**
2542
             * Represents a binary element found in a source location.
2543
             */
2544
            interface Element {
2545

2546
                /**
2547
                 * Returns the element's relative path and name. If the name ends with a {@code /}, it represents
2548
                 * a folder.
2549
                 *
2550
                 * @return The element's path and name.
2551
                 */
2552
                String getName();
2553

2554
                /**
2555
                 * Returns an input stream to read this element's binary information. Must not be invoked for
2556
                 * folders.
2557
                 *
2558
                 * @return An input stream that represents this element's binary information.
2559
                 * @throws IOException If an I/O error occurs.
2560
                 */
2561
                InputStream getInputStream() throws IOException;
2562

2563
                /**
2564
                 * Resolves this element to a more specialized form if possible. Doing so allows for performance
2565
                 * optimizations if more specialized formats are available.
2566
                 *
2567
                 * @param type The requested spezialized type.
2568
                 * @param <T>  The requested spezialized type.
2569
                 * @return The resolved element or {@code null} if a transformation is impossible.
2570
                 */
2571
                @MaybeNull
2572
                <T> T resolveAs(Class<T> type);
2573

2574
                /**
2575
                 * An element representation for a byte array.
2576
                 */
2577
                @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "The array is not modified by class contract.")
2578
                @HashCodeAndEqualsPlugin.Enhance
2579
                class ForByteArray implements Element {
2580

2581
                    /**
2582
                     * The element's name.
2583
                     */
2584
                    private final String name;
2585

2586
                    /**
2587
                     * The element's binary representation.
2588
                     */
2589
                    private final byte[] binaryRepresentation;
2590

2591
                    /**
2592
                     * Creates an element that is represented by a byte array.
2593
                     *
2594
                     * @param name                 The element's name.
2595
                     * @param binaryRepresentation The element's binary representation.
2596
                     */
2597
                    public ForByteArray(String name, byte[] binaryRepresentation) {
1✔
2598
                        this.name = name;
1✔
2599
                        this.binaryRepresentation = binaryRepresentation;
1✔
2600
                    }
1✔
2601

2602
                    /**
2603
                     * {@inheritDoc}
2604
                     */
2605
                    public String getName() {
2606
                        return name;
1✔
2607
                    }
2608

2609
                    /**
2610
                     * {@inheritDoc}
2611
                     */
2612
                    public InputStream getInputStream() {
2613
                        return new ByteArrayInputStream(binaryRepresentation);
1✔
2614
                    }
2615

2616
                    /**
2617
                     * {@inheritDoc}
2618
                     */
2619
                    @AlwaysNull
2620
                    public <T> T resolveAs(Class<T> type) {
2621
                        return null;
1✔
2622
                    }
2623
                }
2624

2625
                /**
2626
                 * An element representation for a file.
2627
                 */
2628
                @HashCodeAndEqualsPlugin.Enhance
2629
                class ForFile implements Element {
2630

2631
                    /**
2632
                     * The root folder of the represented source.
2633
                     */
2634
                    private final File root;
2635

2636
                    /**
2637
                     * The file location of the represented file that is located within the root directory.
2638
                     */
2639
                    private final File file;
2640

2641
                    /**
2642
                     * Creates an element representation for a file.
2643
                     *
2644
                     * @param root The root folder of the represented source.
2645
                     * @param file The file location of the represented file that is located within the root directory.
2646
                     */
2647
                    public ForFile(File root, File file) {
1✔
2648
                        this.root = root;
1✔
2649
                        this.file = file;
1✔
2650
                    }
1✔
2651

2652
                    /**
2653
                     * {@inheritDoc}
2654
                     */
2655
                    public String getName() {
2656
                        return root.getAbsoluteFile().toURI().relativize(file.getAbsoluteFile().toURI()).getPath();
1✔
2657
                    }
2658

2659
                    /**
2660
                     * {@inheritDoc}
2661
                     */
2662
                    public InputStream getInputStream() throws IOException {
2663
                        return new FileInputStream(file);
1✔
2664
                    }
2665

2666
                    /**
2667
                     * {@inheritDoc}
2668
                     */
2669
                    @MaybeNull
2670
                    @SuppressWarnings("unchecked")
2671
                    public <T> T resolveAs(Class<T> type) {
2672
                        return File.class.isAssignableFrom(type)
1✔
2673
                                ? (T) file
2674
                                : null;
2675
                    }
2676
                }
2677

2678
                /**
2679
                 * Represents a jar file entry as an element.
2680
                 */
2681
                @HashCodeAndEqualsPlugin.Enhance
2682
                class ForJarEntry implements Element {
2683

2684
                    /**
2685
                     * The source's underlying jar file.
2686
                     */
2687
                    private final JarFile file;
2688

2689
                    /**
2690
                     * The entry that is represented by this element.
2691
                     */
2692
                    private final JarEntry entry;
2693

2694
                    /**
2695
                     * Creates a new element representation for a jar file entry.
2696
                     *
2697
                     * @param file  The source's underlying jar file.
2698
                     * @param entry The entry that is represented by this element.
2699
                     */
2700
                    public ForJarEntry(JarFile file, JarEntry entry) {
1✔
2701
                        this.file = file;
1✔
2702
                        this.entry = entry;
1✔
2703
                    }
1✔
2704

2705
                    /**
2706
                     * {@inheritDoc}
2707
                     */
2708
                    public String getName() {
2709
                        return entry.getName();
1✔
2710
                    }
2711

2712
                    /**
2713
                     * {@inheritDoc}
2714
                     */
2715
                    public InputStream getInputStream() throws IOException {
2716
                        return file.getInputStream(entry);
1✔
2717
                    }
2718

2719
                    /**
2720
                     * {@inheritDoc}
2721
                     */
2722
                    @MaybeNull
2723
                    @SuppressWarnings("unchecked")
2724
                    public <T> T resolveAs(Class<T> type) {
2725
                        return JarEntry.class.isAssignableFrom(type)
1✔
2726
                                ? (T) entry
2727
                                : null;
2728
                    }
2729
                }
2730
            }
2731

2732
            /**
2733
             * An empty source that does not contain any elements or a manifest.
2734
             */
2735
            enum Empty implements Source, Origin {
1✔
2736

2737
                /**
2738
                 * The singleton instance.
2739
                 */
2740
                INSTANCE;
1✔
2741

2742
                /**
2743
                 * {@inheritDoc}
2744
                 */
2745
                public Origin read() {
UNCOV
2746
                    return this;
×
2747
                }
2748

2749
                /**
2750
                 * {@inheritDoc}
2751
                 */
2752
                public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) {
2753
                    return ClassFileLocator.NoOp.INSTANCE;
1✔
2754
                }
2755

2756
                /**
2757
                 * {@inheritDoc}
2758
                 */
2759
                @MaybeNull
2760
                public Manifest getManifest() {
2761
                    return NO_MANIFEST;
1✔
2762
                }
2763

2764
                /**
2765
                 * {@inheritDoc}
2766
                 */
2767
                public Iterator<Element> iterator() {
2768
                    return Collections.<Element>emptySet().iterator();
1✔
2769
                }
2770

2771
                /**
2772
                 * {@inheritDoc}
2773
                 */
2774
                public void close() {
2775
                    /* do nothing */
UNCOV
2776
                }
×
2777
            }
2778

2779
            /**
2780
             * A compound source that combines multiple sources into a single representation.
2781
             */
2782
            @HashCodeAndEqualsPlugin.Enhance
2783
            class Compound implements Source {
2784

2785
                /**
2786
                 * The represented sources.
2787
                 */
2788
                private final Collection<? extends Source> sources;
2789

2790
                /**
2791
                 * Creates a new compound source.
2792
                 *
2793
                 * @param sources The represented sources.
2794
                 */
2795
                public Compound(Collection<? extends Source> sources) {
1✔
2796
                    this.sources = sources;
1✔
2797
                }
1✔
2798

2799
                /**
2800
                 * {@inheritDoc}
2801
                 */
2802
                public Source.Origin read() throws IOException {
2803
                    if (sources.isEmpty()) {
1✔
2804
                        return Empty.INSTANCE;
1✔
2805
                    } else if (sources.size() == 1) {
1✔
UNCOV
2806
                        return sources.iterator().next().read();
×
2807
                    }
2808
                    List<Source.Origin> origins = new ArrayList<Source.Origin>(sources.size());
1✔
2809
                    try {
2810
                        for (Source source : sources) {
1✔
2811
                            origins.add(source.read());
1✔
2812
                        }
1✔
UNCOV
2813
                    } catch (IOException exception) {
×
UNCOV
2814
                        for (Source.Origin origin : origins) {
×
UNCOV
2815
                            origin.close();
×
2816
                        }
×
UNCOV
2817
                        throw exception;
×
2818
                    }
1✔
2819
                    return new Origin(origins);
1✔
2820
                }
2821

2822
                /**
2823
                 * Implements a compound {@link Source.Origin}.
2824
                 */
2825
                @HashCodeAndEqualsPlugin.Enhance
2826
                protected static class Origin implements Source.Origin {
2827

2828
                    /**
2829
                     * A list of represented origins.
2830
                     */
2831
                    private final List<Source.Origin> origins;
2832

2833
                    /**
2834
                     * Creates a new compound origin.
2835
                     *
2836
                     * @param origins A list of represented origins.
2837
                     */
2838
                    protected Origin(List<Source.Origin> origins) {
1✔
2839
                        this.origins = origins;
1✔
2840
                    }
1✔
2841

2842
                    /**
2843
                     * {@inheritDoc}
2844
                     */
2845
                    public Manifest getManifest() throws IOException {
2846
                        for (Source.Origin origin : origins) {
1✔
2847
                            Manifest manifest = origin.getManifest();
1✔
2848
                            if (manifest != null) {
1✔
UNCOV
2849
                                return manifest;
×
2850
                            }
2851
                        }
1✔
2852
                        return NO_MANIFEST;
1✔
2853
                    }
2854

2855
                    /**
2856
                     * {@inheritDoc}
2857
                     */
2858
                    public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException {
2859
                        List<ClassFileLocator> classFileLocators = new ArrayList<ClassFileLocator>(origins.size());
1✔
2860
                        for (Source.Origin origin : origins) {
1✔
2861
                            classFileLocators.add(origin.toClassFileLocator(classFileVersion));
1✔
2862
                        }
1✔
2863
                        return new ClassFileLocator.Compound(classFileLocators);
1✔
2864
                    }
2865

2866
                    /**
2867
                     * {@inheritDoc}
2868
                     */
2869
                    public Iterator<Element> iterator() {
2870
                        return new CompoundIterator(origins);
1✔
2871
                    }
2872

2873
                    /**
2874
                     * {@inheritDoc}
2875
                     */
2876
                    public void close() throws IOException {
2877
                        for (Source.Origin origin : origins) {
1✔
2878
                            origin.close();
1✔
2879
                        }
1✔
2880
                    }
1✔
2881

2882
                    /**
2883
                     * A compound iterator that combines several iterables.
2884
                     */
2885
                    protected static class CompoundIterator implements Iterator<Element> {
2886

2887
                        /**
2888
                         * The current iterator or {@code null} if no such iterator is defined.
2889
                         */
2890
                        @MaybeNull
2891
                        private Iterator<? extends Element> current;
2892

2893
                        /**
2894
                         * A backlog of iterables to still consider.
2895
                         */
2896
                        private final Queue<? extends Iterable<? extends Element>> backlog;
2897

2898
                        /**
2899
                         * Creates a compound iterator.
2900
                         *
2901
                         * @param iterables The iterables to consider.
2902
                         */
2903
                        protected CompoundIterator(List<? extends Iterable<? extends Element>> iterables) {
1✔
2904
                            backlog = QueueFactory.make(iterables);
1✔
2905
                            forward();
1✔
2906
                        }
1✔
2907

2908
                        /**
2909
                         * {@inheritDoc}
2910
                         */
2911
                        public boolean hasNext() {
2912
                            return current != null && current.hasNext();
1✔
2913
                        }
2914

2915
                        /**
2916
                         * {@inheritDoc}
2917
                         */
2918
                        public Element next() {
2919
                            try {
2920
                                if (current != null) {
1✔
2921
                                    return current.next();
1✔
2922
                                } else {
UNCOV
2923
                                    throw new NoSuchElementException();
×
2924
                                }
2925
                            } finally {
2926
                                forward();
1✔
2927
                            }
2928
                        }
2929

2930
                        /**
2931
                         * Forwards the iterator to the next relevant iterable.
2932
                         */
2933
                        private void forward() {
2934
                            while ((current == null || !current.hasNext()) && !backlog.isEmpty()) {
1✔
2935
                                current = backlog.remove().iterator();
1✔
2936
                            }
2937
                        }
1✔
2938

2939
                        /**
2940
                         * {@inheritDoc}
2941
                         */
2942
                        public void remove() {
UNCOV
2943
                            throw new UnsupportedOperationException("remove");
×
2944
                        }
2945
                    }
2946
                }
2947
            }
2948

2949
            /**
2950
             * A source that represents a collection of in-memory resources that are represented as byte arrays.
2951
             */
2952
            @HashCodeAndEqualsPlugin.Enhance
2953
            class InMemory implements Source, Origin {
2954

2955
                /**
2956
                 * A mapping of resource names to their binary representation.
2957
                 */
2958
                private final Map<String, byte[]> storage;
2959

2960
                /**
2961
                 * Creates a new in-memory source.
2962
                 *
2963
                 * @param storage A mapping of resource names to their binary representation.
2964
                 */
2965
                public InMemory(Map<String, byte[]> storage) {
1✔
2966
                    this.storage = storage;
1✔
2967
                }
1✔
2968

2969
                /**
2970
                 * Represents a collection of types as an in-memory source.
2971
                 *
2972
                 * @param type The types to represent.
2973
                 * @return A source representing the supplied types.
2974
                 */
2975
                public static Source ofTypes(Class<?>... type) {
2976
                    return ofTypes(Arrays.asList(type));
1✔
2977
                }
2978

2979
                /**
2980
                 * Represents a collection of types as an in-memory source.
2981
                 *
2982
                 * @param types The types to represent.
2983
                 * @return A source representing the supplied types.
2984
                 */
2985
                public static Source ofTypes(Collection<? extends Class<?>> types) {
2986
                    return ofTypes(types, Collections.<ClassFileVersion, Collection<? extends Class<?>>>emptyMap());
1✔
2987
                }
2988

2989
                /**
2990
                 * Represents a collection of types as an in-memory source.
2991
                 *
2992
                 * @param types          The types to represent.
2993
                 * @param versionedTypes A versioned mapping of types to represent.
2994
                 * @return A source representing the supplied types.
2995
                 */
2996
                public static Source ofTypes(Collection<? extends Class<?>> types, Map<ClassFileVersion, Collection<? extends Class<?>>> versionedTypes) {
2997
                    Map<ClassFileVersion, Map<TypeDescription, byte[]>> versionedBinaryRepresentations = new HashMap<ClassFileVersion, Map<TypeDescription, byte[]>>();
1✔
2998
                    for (Map.Entry<ClassFileVersion, Collection<? extends Class<?>>> entry : versionedTypes.entrySet()) {
1✔
2999
                        Map<TypeDescription, byte[]> binaryRepresentations = new HashMap<TypeDescription, byte[]>();
1✔
3000
                        for (Class<?> type : entry.getValue()) {
1✔
3001
                            binaryRepresentations.put(TypeDescription.ForLoadedType.of(type), ClassFileLocator.ForClassLoader.read(type));
1✔
3002
                        }
1✔
3003
                        versionedBinaryRepresentations.put(entry.getKey(), binaryRepresentations);
1✔
3004
                    }
1✔
3005
                    Map<TypeDescription, byte[]> binaryRepresentations = new HashMap<TypeDescription, byte[]>();
1✔
3006
                    for (Class<?> type : types) {
1✔
3007
                        binaryRepresentations.put(TypeDescription.ForLoadedType.of(type), ClassFileLocator.ForClassLoader.read(type));
1✔
3008
                    }
1✔
3009
                    return ofTypes(binaryRepresentations, versionedBinaryRepresentations);
1✔
3010
                }
3011

3012
                /**
3013
                 * Represents a map of type names to their binary representation as an in-memory source.
3014
                 *
3015
                 * @param binaryRepresentations A mapping of type names to their binary representation.
3016
                 * @return A source representing the supplied types.
3017
                 */
3018
                public static Source ofTypes(Map<TypeDescription, byte[]> binaryRepresentations) {
UNCOV
3019
                    return ofTypes(binaryRepresentations, Collections.<ClassFileVersion, Map<TypeDescription, byte[]>>emptyMap());
×
3020
                }
3021

3022
                /**
3023
                 * Represents a map of type names to their binary representation as an in-memory source.
3024
                 *
3025
                 * @param binaryRepresentations          A mapping of type names to their binary representation.
3026
                 * @param versionedBinaryRepresentations A versioned mapping of type names to their binary representation.
3027
                 * @return A source representing the supplied types.
3028
                 */
3029
                public static Source ofTypes(
3030
                        Map<TypeDescription, byte[]> binaryRepresentations,
3031
                        Map<ClassFileVersion, Map<TypeDescription, byte[]>> versionedBinaryRepresentations
3032
                ) {
3033
                    Map<String, byte[]> storage = new HashMap<String, byte[]>();
1✔
3034
                    for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
1✔
3035
                        storage.put(entry.getKey().getInternalName() + ClassFileLocator.CLASS_FILE_EXTENSION, entry.getValue());
1✔
3036
                    }
1✔
3037
                    for (Map.Entry<ClassFileVersion, Map<TypeDescription, byte[]>> versioned : versionedBinaryRepresentations.entrySet()) {
1✔
3038
                        for (Map.Entry<TypeDescription, byte[]> entry : versioned.getValue().entrySet()) {
1✔
3039
                            storage.put(ClassFileLocator.META_INF_VERSIONS
1✔
3040
                                    + versioned.getKey().getJavaVersion()
1✔
3041
                                    + "/"
3042
                                    + entry.getKey().getInternalName()
1✔
3043
                                    + ClassFileLocator.CLASS_FILE_EXTENSION, entry.getValue());
1✔
3044
                        }
1✔
3045
                    }
1✔
3046
                    return new InMemory(storage);
1✔
3047
                }
3048

3049
                /**
3050
                 * {@inheritDoc}
3051
                 */
3052
                public Origin read() {
3053
                    return this;
1✔
3054
                }
3055

3056
                /**
3057
                 * {@inheritDoc}
3058
                 */
3059
                public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) {
3060
                    return ClassFileLocator.Simple.ofResources(storage);
1✔
3061
                }
3062

3063
                /**
3064
                 * {@inheritDoc}
3065
                 */
3066
                @MaybeNull
3067
                public Manifest getManifest() throws IOException {
3068
                    byte[] binaryRepresentation = storage.get(JarFile.MANIFEST_NAME);
1✔
3069
                    if (binaryRepresentation == null) {
1✔
3070
                        return NO_MANIFEST;
1✔
3071
                    } else {
3072
                        return new Manifest(new ByteArrayInputStream(binaryRepresentation));
1✔
3073
                    }
3074
                }
3075

3076
                /**
3077
                 * {@inheritDoc}
3078
                 */
3079
                public Iterator<Element> iterator() {
3080
                    return new MapEntryIterator(storage.entrySet().iterator());
1✔
3081
                }
3082

3083
                /**
3084
                 * {@inheritDoc}
3085
                 */
3086
                public void close() {
3087
                    /* do nothing */
3088
                }
1✔
3089

3090
                /**
3091
                 * An iterator that represents map entries as sources.
3092
                 */
3093
                protected static class MapEntryIterator implements Iterator<Element> {
3094

3095
                    /**
3096
                     * The represented iterator.
3097
                     */
3098
                    private final Iterator<Map.Entry<String, byte[]>> iterator;
3099

3100
                    /**
3101
                     * Creates a new map entry iterator.
3102
                     *
3103
                     * @param iterator The represented iterator.
3104
                     */
3105
                    protected MapEntryIterator(Iterator<Map.Entry<String, byte[]>> iterator) {
1✔
3106
                        this.iterator = iterator;
1✔
3107
                    }
1✔
3108

3109
                    /**
3110
                     * {@inheritDoc}
3111
                     */
3112
                    public boolean hasNext() {
3113
                        return iterator.hasNext();
1✔
3114
                    }
3115

3116
                    /**
3117
                     * {@inheritDoc}
3118
                     */
3119
                    public Element next() {
3120
                        Map.Entry<String, byte[]> entry = iterator.next();
1✔
3121
                        return new Element.ForByteArray(entry.getKey(), entry.getValue());
1✔
3122
                    }
3123

3124
                    /**
3125
                     * {@inheritDoc}
3126
                     */
3127
                    public void remove() {
3128
                        throw new UnsupportedOperationException("remove");
1✔
3129
                    }
3130
                }
3131
            }
3132

3133
            /**
3134
             * Represents the contents of a folder as class files.
3135
             */
3136
            @HashCodeAndEqualsPlugin.Enhance
3137
            class ForFolder implements Source, Origin {
3138

3139
                /**
3140
                 * The folder to represent.
3141
                 */
3142
                private final File folder;
3143

3144
                /**
3145
                 * Creates a new source representation for a given folder.
3146
                 *
3147
                 * @param folder The folder to represent.
3148
                 */
3149
                public ForFolder(File folder) {
1✔
3150
                    this.folder = folder;
1✔
3151
                }
1✔
3152

3153
                /**
3154
                 * Initializes a reading from this source.
3155
                 *
3156
                 * @return A source that represents the resource of this origin.
3157
                 */
3158
                public Origin read() {
3159
                    return this;
1✔
3160
                }
3161

3162
                /**
3163
                 * {@inheritDoc}
3164
                 */
3165
                public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException {
3166
                    return classFileVersion == null
1✔
3167
                            ? new ClassFileLocator.ForFolder(folder)
UNCOV
3168
                            : ClassFileLocator.ForFolder.of(folder, classFileVersion);
×
3169
                }
3170

3171
                /**
3172
                 * {@inheritDoc}
3173
                 */
3174
                @MaybeNull
3175
                public Manifest getManifest() throws IOException {
3176
                    File file = new File(folder, JarFile.MANIFEST_NAME);
1✔
3177
                    if (file.exists()) {
1✔
3178
                        InputStream inputStream = new FileInputStream(file);
1✔
3179
                        try {
3180
                            return new Manifest(inputStream);
1✔
3181
                        } finally {
3182
                            inputStream.close();
1✔
3183
                        }
3184
                    } else {
3185
                        return NO_MANIFEST;
1✔
3186
                    }
3187
                }
3188

3189
                /**
3190
                 * {@inheritDoc}
3191
                 */
3192
                public Iterator<Element> iterator() {
3193
                    return new FolderIterator(folder);
1✔
3194
                }
3195

3196
                /**
3197
                 * {@inheritDoc}
3198
                 */
3199
                public void close() {
3200
                    /* do nothing */
3201
                }
1✔
3202

3203
                /**
3204
                 * An iterator that exposes all files within a folder structure as elements.
3205
                 */
3206
                protected class FolderIterator implements Iterator<Element> {
3207

3208
                    /**
3209
                     * A list of files and folders to process with the next processed file at the end of the list.
3210
                     */
3211
                    private final Queue<File> files;
3212

3213
                    /**
3214
                     * Creates a new iterator representation for all files within a folder.
3215
                     *
3216
                     * @param folder The root folder.
3217
                     */
3218
                    protected FolderIterator(File folder) {
1✔
3219
                        files = QueueFactory.make();
1✔
3220
                        File[] file = folder.listFiles();
1✔
3221
                        if (file != null) {
1✔
3222
                            for (File candidate : file) {
1✔
3223
                                if (!candidate.equals(new File(folder, JarFile.MANIFEST_NAME))) {
1✔
3224
                                    files.add(candidate);
1✔
3225
                                }
3226
                            }
3227
                        }
3228
                    }
1✔
3229

3230
                    /**
3231
                     * {@inheritDoc}
3232
                     */
3233
                    public boolean hasNext() {
3234
                        return !files.isEmpty();
1✔
3235
                    }
3236

3237
                    /**
3238
                     * {@inheritDoc}
3239
                     */
3240
                    @SuppressFBWarnings(value = "IT_NO_SUCH_ELEMENT", justification = "Exception is thrown by invoking removeFirst on an empty list.")
3241
                    public Element next() {
3242
                        File next = files.remove();
1✔
3243
                        if (next.isDirectory()) {
1✔
3244
                            File[] file = next.listFiles();
1✔
3245
                            if (file != null) {
1✔
3246
                                for (File candidate : file) {
1✔
3247
                                    if (!candidate.equals(new File(folder, JarFile.MANIFEST_NAME))) {
1✔
3248
                                        files.add(candidate);
1✔
3249
                                    }
3250
                                }
3251
                            }
3252
                        }
3253
                        return new Element.ForFile(folder, next);
1✔
3254
                    }
3255

3256
                    /**
3257
                     * {@inheritDoc}
3258
                     */
3259
                    public void remove() {
UNCOV
3260
                        throw new UnsupportedOperationException("remove");
×
3261
                    }
3262
                }
3263
            }
3264

3265
            /**
3266
             * Represents a jar file as a source.
3267
             */
3268
            @HashCodeAndEqualsPlugin.Enhance
3269
            class ForJarFile implements Source {
3270

3271
                /**
3272
                 * The jar file being represented by this source.
3273
                 */
3274
                private final File file;
3275

3276
                /**
3277
                 * Creates a new source for a jar file.
3278
                 *
3279
                 * @param file The jar file being represented by this source.
3280
                 */
3281
                public ForJarFile(File file) {
1✔
3282
                    this.file = file;
1✔
3283
                }
1✔
3284

3285
                /**
3286
                 * {@inheritDoc}
3287
                 */
3288
                public Origin read() throws IOException {
3289
                    return new Origin.ForJarFile(new JarFile(file, false));
1✔
3290
                }
3291
            }
3292

3293
            /**
3294
             * A source that applies a filter upon iterating elements.
3295
             */
3296
            @HashCodeAndEqualsPlugin.Enhance
3297
            class Filtering implements Source {
3298

3299
                /**
3300
                 * The source to which invocations are delegated.
3301
                 */
3302
                private final Source delegate;
3303

3304
                /**
3305
                 * The element matcher being used to filter elements.
3306
                 */
3307
                private final ElementMatcher<Element> matcher;
3308

3309
                /**
3310
                 * {@code true} if the manifest should be retained.
3311
                 */
3312
                private final boolean manifest;
3313

3314
                /**
3315
                 * Creates a new filtering source that retains the manifest of the delegated source.
3316
                 *
3317
                 * @param delegate The source to which invocations are delegated.
3318
                 * @param matcher  The element matcher being used to filter elements.
3319
                 */
3320
                public Filtering(Source delegate, ElementMatcher<Element> matcher) {
3321
                    this(delegate, matcher, true);
1✔
3322
                }
1✔
3323

3324
                /**
3325
                 * Creates a new filtering source.
3326
                 *
3327
                 * @param delegate The source to which invocations are delegated.
3328
                 * @param matcher  The element matcher being used to filter elements.
3329
                 * @param manifest {@code true} if the manifest should be retained.
3330
                 */
3331
                public Filtering(Source delegate, ElementMatcher<Element> matcher, boolean manifest) {
1✔
3332
                    this.delegate = delegate;
1✔
3333
                    this.matcher = matcher;
1✔
3334
                    this.manifest = manifest;
1✔
3335
                }
1✔
3336

3337
                /**
3338
                 * Wraps a source to exclude elements that are above the specified Java version.
3339
                 *
3340
                 * @param delegate         The delegate source.
3341
                 * @param classFileVersion The latest multi-release Java version to retain from the source.
3342
                 * @return A source that applies an appropriate filter.
3343
                 */
3344
                public static Source dropMultiReleaseClassFilesAbove(Source delegate, ClassFileVersion classFileVersion) {
3345
                    return new Filtering(delegate, new MultiReleaseVersionMatcher(classFileVersion));
1✔
3346
                }
3347

3348
                /**
3349
                 * Wraps a source to exclude elements that represent folders.
3350
                 *
3351
                 * @param delegate The delegate source.
3352
                 * @return A source that drops folders and delegates to the original source.
3353
                 */
3354
                public static Source dropFolders(Source delegate) {
3355
                    return new Filtering(delegate, NoFolderMatcher.INSTANCE);
1✔
3356
                }
3357

3358
                /**
3359
                 * {@inheritDoc}
3360
                 */
3361
                public Origin read() throws IOException {
3362
                    return new Origin.Filtering(delegate.read(), matcher, manifest);
1✔
3363
                }
3364

3365
                /**
3366
                 * An element matcher that filters multi-release files above a given version.
3367
                 */
3368
                @HashCodeAndEqualsPlugin.Enhance
3369
                protected static class MultiReleaseVersionMatcher implements ElementMatcher<Element> {
3370

3371
                    /**
3372
                     * The latest version to consider.
3373
                     */
3374
                    private final ClassFileVersion classFileVersion;
3375

3376
                    /**
3377
                     * Creates a multi-release version matcher.
3378
                     *
3379
                     * @param classFileVersion The latest class file version to consider.
3380
                     */
3381
                    protected MultiReleaseVersionMatcher(ClassFileVersion classFileVersion) {
1✔
3382
                        this.classFileVersion = classFileVersion;
1✔
3383
                    }
1✔
3384

3385
                    /**
3386
                     * {@inheritDoc}
3387
                     */
3388
                    public boolean matches(@MaybeNull Element target) {
3389
                        if (target == null) {
1✔
UNCOV
3390
                            return true;
×
3391
                        }
3392
                        String name = target.getName();
1✔
3393
                        if (name.startsWith("/")) {
1✔
UNCOV
3394
                            name = name.substring(1);
×
3395
                        }
3396
                        if (name.startsWith(ClassFileLocator.META_INF_VERSIONS)) {
1✔
3397
                            int version;
3398
                            try {
3399
                                version = Integer.parseInt(name.substring(
1✔
3400
                                        ClassFileLocator.META_INF_VERSIONS.length(),
1✔
3401
                                        name.indexOf('/', ClassFileLocator.META_INF_VERSIONS.length())));
1✔
UNCOV
3402
                            } catch (NumberFormatException ignored) {
×
UNCOV
3403
                                return true;
×
3404
                            }
1✔
3405
                            return version <= classFileVersion.getJavaVersion();
1✔
3406
                        }
3407
                        return true;
1✔
3408
                    }
3409
                }
3410

3411
                /**
3412
                 * A matcher that removes folders from the iteration.
3413
                 */
3414
                @HashCodeAndEqualsPlugin.Enhance
1✔
3415
                protected enum NoFolderMatcher implements ElementMatcher<Element> {
3416

3417
                    /**
3418
                     * The singleton instance.
3419
                     */
3420
                    INSTANCE;
1✔
3421

3422
                    /**
3423
                     * {@inheritDoc}
3424
                     */
3425
                    public boolean matches(@MaybeNull Element target) {
3426
                        return target == null || !target.getName().endsWith("/");
1✔
3427
                    }
3428
                }
3429
            }
3430
        }
3431

3432
        /**
3433
         * A target for a plugin engine represents a sink container for all elements that are supplied by a {@link Source}.
3434
         */
3435
        interface Target {
3436

3437
            /**
3438
             * Initializes this target prior to writing.
3439
             *
3440
             * @param manifest The manifest for the target or {@code null} if no manifest was found.
3441
             * @return The sink to write to.
3442
             * @throws IOException If an I/O error occurs.
3443
             */
3444
            Sink write(@MaybeNull Manifest manifest) throws IOException;
3445

3446
            /**
3447
             * A sink represents an active writing process.
3448
             */
3449
            interface Sink extends Closeable {
3450

3451
                /**
3452
                 * Stores the supplied binary representation of types in this sink.
3453
                 *
3454
                 * @param binaryRepresentations The binary representations to store.
3455
                 * @throws IOException If an I/O error occurs.
3456
                 */
3457
                void store(Map<TypeDescription, byte[]> binaryRepresentations) throws IOException;
3458

3459
                /**
3460
                 * Stores the supplied binary representation of types in this sink.
3461
                 *
3462
                 * @param classFileVersion      The version of the multi-release jar file, which should at least be {@code 8} as previous
3463
                 *                              versions are not recognized by regular class loaders.
3464
                 * @param binaryRepresentations The binary representations to store.
3465
                 * @throws IOException If an I/O error occurs.
3466
                 */
3467
                void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException;
3468

3469
                /**
3470
                 * Retains the supplied element in its original form.
3471
                 *
3472
                 * @param element The element to retain.
3473
                 * @throws IOException If an I/O error occurs.
3474
                 */
3475
                void retain(Source.Element element) throws IOException;
3476

3477
                /**
3478
                 * Implements a sink for a jar file.
3479
                 */
3480
                class ForJarOutputStream implements Sink {
3481

3482
                    /**
3483
                     * The output stream to write to.
3484
                     */
3485
                    private final JarOutputStream outputStream;
3486

3487
                    /**
3488
                     * Creates a new sink for a jar file.
3489
                     *
3490
                     * @param outputStream The output stream to write to.
3491
                     */
3492
                    public ForJarOutputStream(JarOutputStream outputStream) {
1✔
3493
                        this.outputStream = outputStream;
1✔
3494
                    }
1✔
3495

3496
                    /**
3497
                     * {@inheritDoc}
3498
                     */
3499
                    public void store(Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3500
                        for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
1✔
3501
                            outputStream.putNextEntry(new JarEntry(entry.getKey().getInternalName() + ClassFileLocator.CLASS_FILE_EXTENSION));
1✔
3502
                            outputStream.write(entry.getValue());
1✔
3503
                            outputStream.closeEntry();
1✔
3504
                        }
1✔
3505
                    }
1✔
3506

3507
                    /**
3508
                     * {@inheritDoc}
3509
                     */
3510
                    public void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
UNCOV
3511
                        for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
×
UNCOV
3512
                            outputStream.putNextEntry(new JarEntry(ClassFileLocator.META_INF_VERSIONS
×
UNCOV
3513
                                    + classFileVersion.getJavaVersion()
×
3514
                                    + "/"
UNCOV
3515
                                    + entry.getKey().getInternalName()
×
3516
                                    + ClassFileLocator.CLASS_FILE_EXTENSION));
UNCOV
3517
                            outputStream.write(entry.getValue());
×
UNCOV
3518
                            outputStream.closeEntry();
×
UNCOV
3519
                        }
×
UNCOV
3520
                    }
×
3521

3522
                    /**
3523
                     * {@inheritDoc}
3524
                     */
3525
                    public void retain(Source.Element element) throws IOException {
3526
                        JarEntry entry = element.resolveAs(JarEntry.class);
1✔
3527
                        String name = element.getName();
1✔
3528
                        outputStream.putNextEntry(entry == null
1✔
3529
                                ? new JarEntry(name)
3530
                                : entry);
3531
                        if (entry != null || !name.endsWith("/")) {
1✔
3532
                            InputStream inputStream = element.getInputStream();
1✔
3533
                            try {
3534
                                byte[] buffer = new byte[1024];
1✔
3535
                                int length;
3536
                                while ((length = inputStream.read(buffer)) != -1) {
1✔
3537
                                    outputStream.write(buffer, 0, length);
1✔
3538
                                }
3539
                            } finally {
3540
                                inputStream.close();
1✔
3541
                            }
3542
                        }
3543
                        outputStream.closeEntry();
1✔
3544
                    }
1✔
3545

3546
                    /**
3547
                     * {@inheritDoc}
3548
                     */
3549
                    public void close() throws IOException {
3550
                        outputStream.close();
1✔
3551
                    }
1✔
3552
                }
3553
            }
3554

3555
            /**
3556
             * A sink that discards any entry.
3557
             */
3558
            enum Discarding implements Target, Sink {
1✔
3559

3560
                /**
3561
                 * The singleton instance.
3562
                 */
3563
                INSTANCE;
1✔
3564

3565
                /**
3566
                 * {@inheritDoc}
3567
                 */
3568
                public Sink write(@MaybeNull Manifest manifest) {
3569
                    return this;
1✔
3570
                }
3571

3572
                /**
3573
                 * {@inheritDoc}
3574
                 */
3575
                public void store(Map<TypeDescription, byte[]> binaryRepresentations) {
3576
                    /* do nothing */
3577
                }
1✔
3578

3579
                /**
3580
                 * {@inheritDoc}
3581
                 */
3582
                public void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3583
                    /* do nothing */
UNCOV
3584
                }
×
3585

3586
                /**
3587
                 * {@inheritDoc}
3588
                 */
3589
                public void retain(Source.Element element) {
3590
                    /* do nothing */
3591
                }
1✔
3592

3593
                /**
3594
                 * {@inheritDoc}
3595
                 */
3596
                public void close() {
3597
                    /* do nothing */
UNCOV
3598
                }
×
3599
            }
3600

3601
            /**
3602
             * A sink that stores all elements in a memory map.
3603
             */
3604
            @HashCodeAndEqualsPlugin.Enhance
3605
            class InMemory implements Target, Sink {
3606

3607
                /**
3608
                 * The map for storing all elements being received.
3609
                 */
3610
                @HashCodeAndEqualsPlugin.Identity
3611
                private final Map<String, byte[]> storage;
3612

3613
                /**
3614
                 * Creates a new in-memory storage.
3615
                 */
3616
                public InMemory() {
3617
                    this(new HashMap<String, byte[]>());
1✔
3618
                }
1✔
3619

3620
                /**
3621
                 * Creates a new in-memory storage.
3622
                 *
3623
                 * @param storage The map for storing all elements being received.
3624
                 */
3625
                public InMemory(Map<String, byte[]> storage) {
1✔
3626
                    this.storage = storage;
1✔
3627
                }
1✔
3628

3629
                /**
3630
                 * {@inheritDoc}
3631
                 */
3632
                public Sink write(@MaybeNull Manifest manifest) throws IOException {
3633
                    if (manifest != null) {
1✔
3634
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1✔
3635
                        try {
3636
                            manifest.write(outputStream);
1✔
3637
                        } finally {
3638
                            outputStream.close();
1✔
3639
                        }
3640
                        storage.put(JarFile.MANIFEST_NAME, outputStream.toByteArray());
1✔
3641
                    }
3642
                    return this;
1✔
3643
                }
3644

3645
                /**
3646
                 * {@inheritDoc}
3647
                 */
3648
                public void store(Map<TypeDescription, byte[]> binaryRepresentations) {
3649
                    for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
1✔
3650
                        storage.put(entry.getKey().getInternalName() + ClassFileLocator.CLASS_FILE_EXTENSION, entry.getValue());
1✔
3651
                    }
1✔
3652
                }
1✔
3653

3654
                /**
3655
                 * {@inheritDoc}
3656
                 */
3657
                public void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3658
                    for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
1✔
3659
                        storage.put(ClassFileLocator.META_INF_VERSIONS
1✔
3660
                                + classFileVersion.getJavaVersion()
1✔
3661
                                + "/"
3662
                                + entry.getKey().getInternalName()
1✔
3663
                                + ClassFileLocator.CLASS_FILE_EXTENSION, entry.getValue());
1✔
3664
                    }
1✔
3665
                }
1✔
3666

3667
                /**
3668
                 * {@inheritDoc}
3669
                 */
3670
                public void retain(Source.Element element) throws IOException {
3671
                    String name = element.getName();
1✔
3672
                    if (!name.endsWith("/")) {
1✔
3673
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1✔
3674
                        try {
3675
                            InputStream inputStream = element.getInputStream();
1✔
3676
                            try {
3677
                                byte[] buffer = new byte[1024];
1✔
3678
                                int length;
3679
                                while ((length = inputStream.read(buffer)) != -1) {
1✔
3680
                                    outputStream.write(buffer, 0, length);
1✔
3681
                                }
3682
                            } finally {
3683
                                inputStream.close();
1✔
3684
                            }
3685
                        } finally {
3686
                            outputStream.close();
1✔
3687
                        }
3688
                        storage.put(element.getName(), outputStream.toByteArray());
1✔
3689
                    }
3690
                }
1✔
3691

3692
                /**
3693
                 * {@inheritDoc}
3694
                 */
3695
                public void close() {
3696
                    /* do nothing */
3697
                }
1✔
3698

3699
                /**
3700
                 * Returns the in-memory storage.
3701
                 *
3702
                 * @return The in-memory storage.
3703
                 */
3704
                public Map<String, byte[]> getStorage() {
3705
                    return storage;
1✔
3706
                }
3707

3708
                /**
3709
                 * Returns the in-memory storage as a type-map where all non-class files are discarded.
3710
                 *
3711
                 * @return The in-memory storage as a type map.
3712
                 */
3713
                public Map<String, byte[]> toTypeMap() {
3714
                    Map<String, byte[]> binaryRepresentations = new HashMap<String, byte[]>();
1✔
3715
                    for (Map.Entry<String, byte[]> entry : storage.entrySet()) {
1✔
3716
                        if (entry.getKey().endsWith(ClassFileLocator.CLASS_FILE_EXTENSION) && !entry.getKey().startsWith(ClassFileLocator.META_INF_VERSIONS)) {
1✔
3717
                            binaryRepresentations.put(entry.getKey()
1✔
3718
                                    .substring(0, entry.getKey().length() - ClassFileLocator.CLASS_FILE_EXTENSION.length())
1✔
3719
                                    .replace('/', '.'), entry.getValue());
1✔
3720
                        }
3721
                    }
1✔
3722
                    return binaryRepresentations;
1✔
3723
                }
3724

3725
                /**
3726
                 * Returns the in-memory storage as a type-map where all non-class files are discarded.
3727
                 *
3728
                 * @param classFileVersion The class file version to consider when encountering multi-release class files.
3729
                 * @return The in-memory storage as a type map.
3730
                 */
3731
                public Map<String, byte[]> toTypeMap(ClassFileVersion classFileVersion) {
3732
                    Map<String, byte[]> binaryRepresentations = new HashMap<String, byte[]>();
1✔
3733
                    Map<String, Integer> versions = new HashMap<String, Integer>();
1✔
3734
                    for (Map.Entry<String, byte[]> entry : storage.entrySet()) {
1✔
3735
                        if (entry.getKey().endsWith(ClassFileLocator.CLASS_FILE_EXTENSION)) {
1✔
3736
                            String suffix;
3737
                            int version;
3738
                            if (entry.getKey().startsWith(ClassFileLocator.META_INF_VERSIONS)) {
1✔
3739
                                suffix = entry.getKey().substring(entry.getKey().indexOf('/', ClassFileLocator.META_INF_VERSIONS.length()) + 1);
1✔
3740
                                try {
3741
                                    int candidate = Integer.parseInt(entry.getKey().substring(ClassFileLocator.META_INF_VERSIONS.length(), entry.getKey().indexOf('/', ClassFileLocator.META_INF_VERSIONS.length())));
1✔
3742
                                    if (candidate < 7 || candidate > classFileVersion.getJavaVersion()) {
1✔
3743
                                        continue;
1✔
3744
                                    }
3745
                                    version = candidate;
1✔
UNCOV
3746
                                } catch (NumberFormatException ignored) {
×
UNCOV
3747
                                    continue;
×
3748
                                }
1✔
3749
                            } else {
3750
                                suffix = entry.getKey();
1✔
3751
                                version = 0;
1✔
3752
                            }
3753
                            Integer current = versions.get(suffix);
1✔
3754
                            if (current == null || current < version) {
1✔
3755
                                versions.put(suffix, version);
1✔
3756
                                binaryRepresentations.put(suffix
1✔
3757
                                        .substring(0, suffix.length() - ClassFileLocator.CLASS_FILE_EXTENSION.length())
1✔
3758
                                        .replace('/', '.'), entry.getValue());
1✔
3759
                            }
3760
                        }
3761
                    }
1✔
3762
                    return binaryRepresentations;
1✔
3763
                }
3764
            }
3765

3766
            /**
3767
             * Represents a folder as the target for a plugin engine's application.
3768
             */
3769
            @HashCodeAndEqualsPlugin.Enhance
3770
            class ForFolder implements Target, Sink {
3771

3772
                /**
3773
                 * The folder that is represented by this instance.
3774
                 */
3775
                private final File folder;
3776

3777
                /**
3778
                 * {@code true} if retained files should be linked and not copied.
3779
                 */
3780
                private final boolean link;
3781

3782
                /**
3783
                 * Creates a new target for a folder.
3784
                 *
3785
                 * @param folder The folder that is represented by this instance.
3786
                 */
3787
                public ForFolder(File folder) {
3788
                    this(folder, false);
1✔
3789
                }
1✔
3790

3791
                /**
3792
                 * Creates a new target for a folder.
3793
                 *
3794
                 * @param folder The folder that is represented by this instance.
3795
                 * @param link   {@code true} if retained files should be linked and not copied.
3796
                 */
3797
                public ForFolder(File folder, boolean link) {
1✔
3798
                    this.folder = folder;
1✔
3799
                    this.link = link;
1✔
3800
                }
1✔
3801

3802
                /**
3803
                 * Stores binary representations to a folder.
3804
                 *
3805
                 * @param folder                The base folder.
3806
                 * @param binaryRepresentations The binary representations to store.
3807
                 * @throws IOException If an I/O exception occurs.
3808
                 */
3809
                private static void doStore(File folder, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3810
                    for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
1✔
3811
                        File target = new File(folder, entry.getKey().getInternalName() + ClassFileLocator.CLASS_FILE_EXTENSION);
1✔
3812
                        if (!target.getParentFile().isDirectory() && !target.getParentFile().mkdirs()) {
1✔
UNCOV
3813
                            throw new IOException("Could not create directory: " + target.getParent());
×
3814
                        }
3815
                        OutputStream outputStream = new FileOutputStream(target);
1✔
3816
                        try {
3817
                            outputStream.write(entry.getValue());
1✔
3818
                        } finally {
3819
                            outputStream.close();
1✔
3820
                        }
3821
                    }
1✔
3822
                }
1✔
3823

3824
                /**
3825
                 * {@inheritDoc}
3826
                 */
3827
                public Sink write(@MaybeNull Manifest manifest) throws IOException {
3828
                    if (manifest != null) {
1✔
3829
                        File target = new File(folder, JarFile.MANIFEST_NAME);
1✔
3830
                        if (!target.getParentFile().isDirectory() && !target.getParentFile().mkdirs()) {
1✔
UNCOV
3831
                            throw new IOException("Could not create directory: " + target.getParent());
×
3832
                        }
3833
                        OutputStream outputStream = new FileOutputStream(target);
1✔
3834
                        try {
3835
                            manifest.write(outputStream);
1✔
3836
                        } finally {
3837
                            outputStream.close();
1✔
3838
                        }
3839
                    }
3840
                    return this;
1✔
3841
                }
3842

3843
                /**
3844
                 * {@inheritDoc}
3845
                 */
3846
                public void store(Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3847
                    doStore(folder, binaryRepresentations);
1✔
3848
                }
1✔
3849

3850
                /**
3851
                 * {@inheritDoc}
3852
                 */
3853
                public void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
UNCOV
3854
                    doStore(new File(folder, ClassFileLocator.META_INF_VERSIONS + classFileVersion.getJavaVersion()), binaryRepresentations);
×
UNCOV
3855
                }
×
3856

3857
                /**
3858
                 * {@inheritDoc}
3859
                 */
3860
                public void retain(Source.Element element) throws IOException {
3861
                    String name = element.getName();
1✔
3862
                    File target = new File(folder, name);
1✔
3863
                    if (!name.endsWith("/")) {
1✔
3864
                        File resolved = element.resolveAs(File.class);
1✔
3865
                        if (!target.getCanonicalPath().startsWith(folder.getCanonicalPath() + File.separatorChar)) {
1✔
3866
                            throw new IllegalArgumentException(target + " is not a subdirectory of " + folder);
1✔
3867
                        } else if (!target.getParentFile().isDirectory() && !target.getParentFile().mkdirs()) {
1✔
UNCOV
3868
                            throw new IOException("Could not create directory: " + target.getParent());
×
3869
                        } else if (resolved != null && !resolved.equals(target)) {
1✔
3870
                            if (link) {
1✔
UNCOV
3871
                                FileSystem.getInstance().link(resolved, target);
×
3872
                            } else {
3873
                                FileSystem.getInstance().copy(resolved, target);
1✔
3874
                            }
3875
                        } else if (!target.equals(resolved)) {
1✔
3876
                            InputStream inputStream = element.getInputStream();
1✔
3877
                            try {
3878
                                OutputStream outputStream = new FileOutputStream(target);
1✔
3879
                                try {
3880
                                    byte[] buffer = new byte[1024];
1✔
3881
                                    int length;
3882
                                    while ((length = inputStream.read(buffer)) != -1) {
1✔
3883
                                        outputStream.write(buffer, 0, length);
1✔
3884
                                    }
3885
                                } finally {
3886
                                    outputStream.close();
1✔
3887
                                }
3888
                            } finally {
3889
                                inputStream.close();
1✔
3890
                            }
3891
                        }
3892
                    } else if (!target.isDirectory() && !target.mkdirs()) {
1✔
3893
                        throw new IllegalStateException("Cannot create requested directory: " + target);
1✔
3894
                    }
3895
                }
1✔
3896

3897
                /**
3898
                 * {@inheritDoc}
3899
                 */
3900
                public void close() {
3901
                    /* do nothing */
3902
                }
1✔
3903
            }
3904

3905
            /**
3906
             * Represents a jar file as a target.
3907
             */
3908
            @HashCodeAndEqualsPlugin.Enhance
3909
            class ForJarFile implements Target {
3910

3911
                /**
3912
                 * The jar file that is represented by this target.
3913
                 */
3914
                private final File file;
3915

3916
                /**
3917
                 * Creates a new target for a jar file.
3918
                 *
3919
                 * @param file The jar file that is represented by this target.
3920
                 */
3921
                public ForJarFile(File file) {
1✔
3922
                    this.file = file;
1✔
3923
                }
1✔
3924

3925
                /**
3926
                 * {@inheritDoc}
3927
                 */
3928
                public Sink write(@MaybeNull Manifest manifest) throws IOException {
3929
                    OutputStream outputStream = new FileOutputStream(file);
1✔
3930
                    try {
3931
                        return manifest == null
1✔
3932
                                ? new Sink.ForJarOutputStream(new JarOutputStream(outputStream))
3933
                                : new Sink.ForJarOutputStream(new JarOutputStream(outputStream, manifest));
UNCOV
3934
                    } catch (RuntimeException exception) {
×
UNCOV
3935
                        outputStream.close();
×
UNCOV
3936
                        throw exception;
×
UNCOV
3937
                    } catch (IOException exception) {
×
UNCOV
3938
                        outputStream.close();
×
UNCOV
3939
                        throw exception;
×
UNCOV
3940
                    } catch (Error error) {
×
UNCOV
3941
                        outputStream.close();
×
UNCOV
3942
                        throw error;
×
3943
                    }
3944
                }
3945
            }
3946
        }
3947

3948
        /**
3949
         * A dispatcher to execute a plugin engine transformation. A dispatcher will receive all work assignments prior to the invocation
3950
         * of complete. After registering and eventually completing the supplied work, the close method will always be called. Any dispatcher
3951
         * will only be used once and from a single thread.
3952
         */
3953
        interface Dispatcher extends Closeable {
3954

3955
            /**
3956
             * Accepts a new work assignment.
3957
             *
3958
             * @param work  The work to handle prefixed by a preprocessing step.
3959
             * @param eager {@code true} if the processing does not need to be deferred until all preprocessing is complete.
3960
             * @throws IOException If an I/O exception occurs.
3961
             */
3962
            void accept(Callable<? extends Callable<? extends Materializable>> work, boolean eager) throws IOException;
3963

3964
            /**
3965
             * Completes the work being handled.
3966
             *
3967
             * @throws IOException If an I/O exception occurs.
3968
             */
3969
            void complete() throws IOException;
3970

3971
            /**
3972
             * The result of a work assignment that needs to be invoked from the main thread that triggers a dispatchers life-cycle methods.
3973
             */
3974
            interface Materializable {
3975

3976
                /**
3977
                 * Materializes this work result and adds any results to the corresponding collection.
3978
                 *
3979
                 * @param sink        The sink to write any work to.
3980
                 * @param transformed A list of all types that are transformed.
3981
                 * @param failed      A mapping of all types that failed during transformation to the exceptions that explain the failure.
3982
                 * @param unresolved  A list of type names that could not be resolved.
3983
                 * @throws IOException If an I/O exception occurs.
3984
                 */
3985
                void materialize(Target.Sink sink,
3986
                                 List<TypeDescription> transformed,
3987
                                 Map<TypeDescription,
3988
                                         List<Throwable>> failed,
3989
                                 List<String> unresolved) throws IOException;
3990

3991
                /**
3992
                 * A materializable for a successfully transformed type.
3993
                 */
3994
                class ForTransformedElement implements Materializable {
3995

3996
                    /**
3997
                     * The multi-release class file version number or {@code null} if a regular class.
3998
                     */
3999
                    @MaybeNull
4000
                    private final ClassFileVersion classFileVersion;
4001

4002
                    /**
4003
                     * The type that has been transformed.
4004
                     */
4005
                    private final DynamicType dynamicType;
4006

4007
                    /**
4008
                     * Creates a new materializable for a successfully transformed type.
4009
                     *
4010
                     * @param classFileVersion The multi-release class file version number or {@code null} if a regular class.
4011
                     * @param dynamicType      The type that has been transformed.
4012
                     */
4013
                    protected ForTransformedElement(@MaybeNull ClassFileVersion classFileVersion, DynamicType dynamicType) {
1✔
4014
                        this.classFileVersion = classFileVersion;
1✔
4015
                        this.dynamicType = dynamicType;
1✔
4016
                    }
1✔
4017

4018
                    /**
4019
                     * {@inheritDoc}
4020
                     */
4021
                    public void materialize(Target.Sink sink,
4022
                                            List<TypeDescription> transformed,
4023
                                            Map<TypeDescription,
4024
                                                    List<Throwable>> failed,
4025
                                            List<String> unresolved) throws IOException {
4026
                        if (classFileVersion == null) {
1✔
4027
                            sink.store(dynamicType.getAllTypes());
1✔
4028
                        } else {
4029
                            sink.store(classFileVersion, dynamicType.getAllTypes());
1✔
4030
                        }
4031
                        transformed.add(dynamicType.getTypeDescription());
1✔
4032
                    }
1✔
4033
                }
4034

4035
                /**
4036
                 * A materializable for an element that is retained in its original state.
4037
                 */
4038
                class ForRetainedElement implements Materializable {
4039

4040
                    /**
4041
                     * The retained element.
4042
                     */
4043
                    private final Source.Element element;
4044

4045
                    /**
4046
                     * Creates a new materializable for a retained element.
4047
                     *
4048
                     * @param element The retained element.
4049
                     */
4050
                    protected ForRetainedElement(Source.Element element) {
1✔
4051
                        this.element = element;
1✔
4052
                    }
1✔
4053

4054
                    /**
4055
                     * {@inheritDoc}
4056
                     */
4057
                    public void materialize(Target.Sink sink,
4058
                                            List<TypeDescription> transformed,
4059
                                            Map<TypeDescription,
4060
                                                    List<Throwable>> failed,
4061
                                            List<String> unresolved) throws IOException {
4062
                        sink.retain(element);
1✔
4063
                    }
1✔
4064
                }
4065

4066
                /**
4067
                 * A materializable for an element that failed to be transformed.
4068
                 */
4069
                class ForFailedElement implements Materializable {
4070

4071
                    /**
4072
                     * The element for which the transformation failed.
4073
                     */
4074
                    private final Source.Element element;
4075

4076
                    /**
4077
                     * The type description for the represented type.
4078
                     */
4079
                    private final TypeDescription typeDescription;
4080

4081
                    /**
4082
                     * A non-empty list of errors that occurred when attempting the transformation.
4083
                     */
4084
                    private final List<Throwable> errored;
4085

4086
                    /**
4087
                     * Creates a new materializable for an element that failed to be transformed.
4088
                     *
4089
                     * @param element         The element for which the transformation failed.
4090
                     * @param typeDescription The type description for the represented type.
4091
                     * @param errored         A non-empty list of errors that occurred when attempting the transformation.
4092
                     */
4093
                    protected ForFailedElement(Source.Element element, TypeDescription typeDescription, List<Throwable> errored) {
1✔
4094
                        this.element = element;
1✔
4095
                        this.typeDescription = typeDescription;
1✔
4096
                        this.errored = errored;
1✔
4097
                    }
1✔
4098

4099
                    /**
4100
                     * {@inheritDoc}
4101
                     */
4102
                    public void materialize(Target.Sink sink,
4103
                                            List<TypeDescription> transformed,
4104
                                            Map<TypeDescription,
4105
                                                    List<Throwable>> failed,
4106
                                            List<String> unresolved) throws IOException {
4107
                        sink.retain(element);
1✔
4108
                        failed.put(typeDescription, errored);
1✔
4109
                    }
1✔
4110
                }
4111

4112
                /**
4113
                 * A materializable for an element that could not be resolved.
4114
                 */
4115
                class ForUnresolvedElement implements Materializable {
4116

4117
                    /**
4118
                     * The element that could not be resolved.
4119
                     */
4120
                    private final Source.Element element;
4121

4122
                    /**
4123
                     * The name of the type that was deducted for this element.
4124
                     */
4125
                    private final String typeName;
4126

4127
                    /**
4128
                     * Creates a new materializable for an element that could not be resolved.
4129
                     *
4130
                     * @param element  The element that could not be resolved.
4131
                     * @param typeName The name of the type that was deducted for this element.
4132
                     */
4133
                    protected ForUnresolvedElement(Source.Element element, String typeName) {
1✔
4134
                        this.element = element;
1✔
4135
                        this.typeName = typeName;
1✔
4136
                    }
1✔
4137

4138
                    /**
4139
                     * {@inheritDoc}
4140
                     */
4141
                    public void materialize(Target.Sink sink,
4142
                                            List<TypeDescription> transformed,
4143
                                            Map<TypeDescription,
4144
                                                    List<Throwable>> failed,
4145
                                            List<String> unresolved) throws IOException {
4146
                        sink.retain(element);
1✔
4147
                        unresolved.add(typeName);
1✔
4148
                    }
1✔
4149
                }
4150
            }
4151

4152
            /**
4153
             * A factory that is used for creating a dispatcher that is used for a specific plugin engine application.
4154
             */
4155
            interface Factory {
4156

4157
                /**
4158
                 * Creates a new dispatcher.
4159
                 *
4160
                 * @param sink        The sink to write any work to.
4161
                 * @param transformed A list of all types that are transformed.
4162
                 * @param failed      A mapping of all types that failed during transformation to the exceptions that explain the failure.
4163
                 * @param unresolved  A list of type names that could not be resolved.
4164
                 * @return The dispatcher to use.
4165
                 */
4166
                Dispatcher make(Target.Sink sink,
4167
                                List<TypeDescription> transformed,
4168
                                Map<TypeDescription,
4169
                                        List<Throwable>> failed,
4170
                                List<String> unresolved);
4171
            }
4172

4173
            /**
4174
             * A dispatcher that applies transformation upon discovery.
4175
             */
4176
            class ForSerialTransformation implements Dispatcher {
4177

4178
                /**
4179
                 * The sink to write any work to.
4180
                 */
4181
                private final Target.Sink sink;
4182

4183
                /**
4184
                 * A list of all types that are transformed.
4185
                 */
4186
                private final List<TypeDescription> transformed;
4187

4188
                /**
4189
                 * A mapping of all types that failed during transformation to the exceptions that explain the failure.
4190
                 */
4191
                private final Map<TypeDescription, List<Throwable>> failed;
4192

4193
                /**
4194
                 * A list of type names that could not be resolved.
4195
                 */
4196
                private final List<String> unresolved;
4197

4198
                /**
4199
                 * A list of deferred processings.
4200
                 */
4201
                private final List<Callable<? extends Materializable>> preprocessings;
4202

4203
                /**
4204
                 * Creates a dispatcher for a serial transformation.
4205
                 *
4206
                 * @param sink        The sink to write any work to.
4207
                 * @param transformed A list of all types that are transformed.
4208
                 * @param failed      A mapping of all types that failed during transformation to the exceptions that explain the failure.
4209
                 * @param unresolved  A list of type names that could not be resolved.
4210
                 */
4211
                protected ForSerialTransformation(Target.Sink sink,
4212
                                                  List<TypeDescription> transformed,
4213
                                                  Map<TypeDescription, List<Throwable>> failed,
4214
                                                  List<String> unresolved) {
1✔
4215
                    this.sink = sink;
1✔
4216
                    this.transformed = transformed;
1✔
4217
                    this.failed = failed;
1✔
4218
                    this.unresolved = unresolved;
1✔
4219
                    preprocessings = new ArrayList<Callable<? extends Materializable>>();
1✔
4220
                }
1✔
4221

4222
                /**
4223
                 * {@inheritDoc}
4224
                 */
4225
                public void accept(Callable<? extends Callable<? extends Materializable>> work, boolean eager) throws IOException {
4226
                    try {
4227
                        Callable<? extends Materializable> preprocessed = work.call();
1✔
4228
                        if (eager) {
1✔
4229
                            preprocessed.call().materialize(sink, transformed, failed, unresolved);
1✔
4230
                        } else {
4231
                            preprocessings.add(preprocessed);
1✔
4232
                        }
4233
                    } catch (Exception exception) {
1✔
4234
                        if (exception instanceof IOException) {
1✔
UNCOV
4235
                            throw (IOException) exception;
×
4236
                        } else if (exception instanceof RuntimeException) {
1✔
4237
                            throw (RuntimeException) exception;
1✔
4238
                        } else {
UNCOV
4239
                            throw new IllegalStateException(exception);
×
4240
                        }
4241
                    }
1✔
4242
                }
1✔
4243

4244
                /**
4245
                 * {@inheritDoc}
4246
                 */
4247
                public void complete() throws IOException {
4248
                    for (Callable<? extends Materializable> preprocessing : preprocessings) {
1✔
4249
                        if (Thread.interrupted()) {
1✔
UNCOV
4250
                            Thread.currentThread().interrupt();
×
UNCOV
4251
                            throw new IllegalStateException("Interrupted during plugin engine completion");
×
4252
                        }
4253
                        try {
4254
                            preprocessing.call().materialize(sink, transformed, failed, unresolved);
1✔
4255
                        } catch (Exception exception) {
1✔
4256
                            if (exception instanceof IOException) {
1✔
UNCOV
4257
                                throw (IOException) exception;
×
4258
                            } else if (exception instanceof RuntimeException) {
1✔
4259
                                throw (RuntimeException) exception;
1✔
4260
                            } else {
4261
                                throw new IllegalStateException(exception);
×
4262
                            }
4263
                        }
1✔
4264
                    }
1✔
4265
                }
1✔
4266

4267
                /**
4268
                 * {@inheritDoc}
4269
                 */
4270
                public void close() {
4271
                    /* do nothing */
4272
                }
1✔
4273

4274
                /**
4275
                 * A factory for creating a serial dispatcher.
4276
                 */
4277
                public enum Factory implements Dispatcher.Factory {
1✔
4278

4279
                    /**
4280
                     * The singleton instance.
4281
                     */
4282
                    INSTANCE;
1✔
4283

4284
                    /**
4285
                     * {@inheritDoc}
4286
                     */
4287
                    public Dispatcher make(Target.Sink sink,
4288
                                           List<TypeDescription> transformed,
4289
                                           Map<TypeDescription,
4290
                                                   List<Throwable>> failed,
4291
                                           List<String> unresolved) {
4292
                        return new ForSerialTransformation(sink, transformed, failed, unresolved);
1✔
4293
                    }
4294
                }
4295
            }
4296

4297
            /**
4298
             * A dispatcher that applies transformations within one or more threads in parallel to the default transformer.
4299
             */
4300
            class ForParallelTransformation implements Dispatcher {
4301

4302
                /**
4303
                 * The target sink.
4304
                 */
4305
                private final Target.Sink sink;
4306

4307
                /**
4308
                 * A list of all types that are transformed.
4309
                 */
4310
                private final List<TypeDescription> transformed;
4311

4312
                /**
4313
                 * A mapping of all types that failed during transformation to the exceptions that explain the failure.
4314
                 */
4315
                private final Map<TypeDescription, List<Throwable>> failed;
4316

4317
                /**
4318
                 * A list of type names that could not be resolved.
4319
                 */
4320
                private final List<String> unresolved;
4321

4322
                /**
4323
                 * A completion service for all preprocessings.
4324
                 */
4325
                private final CompletionService<Callable<Materializable>> preprocessings;
4326

4327
                /**
4328
                 * A completion service for all materializers.
4329
                 */
4330
                private final CompletionService<Materializable> materializers;
4331

4332
                /**
4333
                 * A count of deferred processings.
4334
                 */
4335
                private int deferred;
4336

4337
                /**
4338
                 * A collection of futures that are currently scheduled.
4339
                 */
4340
                private final Set<Future<?>> futures;
4341

4342
                /**
4343
                 * Creates a new dispatcher that applies transformations in parallel.
4344
                 *
4345
                 * @param executor    The executor to delegate any work to.
4346
                 * @param sink        The target sink.
4347
                 * @param transformed A list of all types that are transformed.
4348
                 * @param failed      A mapping of all types that failed during transformation to the exceptions that explain the failure.
4349
                 * @param unresolved  A list of type names that could not be resolved.
4350
                 */
4351
                protected ForParallelTransformation(Executor executor,
4352
                                                    Target.Sink sink,
4353
                                                    List<TypeDescription> transformed,
4354
                                                    Map<TypeDescription,
4355
                                                            List<Throwable>> failed,
4356
                                                    List<String> unresolved) {
1✔
4357
                    this.sink = sink;
1✔
4358
                    this.transformed = transformed;
1✔
4359
                    this.failed = failed;
1✔
4360
                    this.unresolved = unresolved;
1✔
4361
                    preprocessings = new ExecutorCompletionService<Callable<Materializable>>(executor);
1✔
4362
                    materializers = new ExecutorCompletionService<Materializable>(executor);
1✔
4363
                    futures = new HashSet<Future<?>>();
1✔
4364
                }
1✔
4365

4366
                /**
4367
                 * {@inheritDoc}
4368
                 */
4369
                @SuppressWarnings("unchecked")
4370
                public void accept(Callable<? extends Callable<? extends Materializable>> work, boolean eager) {
4371
                    if (eager) {
1✔
4372
                        futures.add(materializers.submit(new EagerWork(work)));
1✔
4373
                    } else {
4374
                        deferred += 1;
1✔
4375
                        futures.add(preprocessings.submit((Callable<Callable<Materializable>>) work));
1✔
4376
                    }
4377
                }
1✔
4378

4379
                /**
4380
                 * {@inheritDoc}
4381
                 */
4382
                public void complete() throws IOException {
4383
                    try {
4384
                        List<Callable<Materializable>> preprocessings = new ArrayList<Callable<Materializable>>(deferred);
1✔
4385
                        while (deferred-- > 0) {
1✔
4386
                            Future<Callable<Materializable>> future = this.preprocessings.take();
1✔
4387
                            futures.remove(future);
1✔
4388
                            preprocessings.add(future.get());
1✔
4389
                        }
1✔
4390
                        for (Callable<Materializable> preprocessing : preprocessings) {
1✔
4391
                            futures.add(materializers.submit(preprocessing));
1✔
4392
                        }
1✔
4393
                        while (!futures.isEmpty()) {
1✔
4394
                            Future<Materializable> future = materializers.take();
1✔
4395
                            futures.remove(future);
1✔
4396
                            future.get().materialize(sink, transformed, failed, unresolved);
1✔
4397
                        }
1✔
UNCOV
4398
                    } catch (InterruptedException exception) {
×
UNCOV
4399
                        Thread.currentThread().interrupt();
×
UNCOV
4400
                        throw new IllegalStateException(exception);
×
4401
                    } catch (ExecutionException exception) {
1✔
4402
                        Throwable cause = exception.getCause();
1✔
4403
                        if (cause instanceof IOException) {
1✔
UNCOV
4404
                            throw (IOException) cause;
×
4405
                        } else if (cause instanceof RuntimeException) {
1✔
4406
                            throw (RuntimeException) cause;
1✔
UNCOV
4407
                        } else if (cause instanceof Error) {
×
4408
                            throw (Error) cause;
×
4409
                        } else {
4410
                            throw new IllegalStateException(cause);
×
4411
                        }
4412
                    }
1✔
4413
                }
1✔
4414

4415
                /**
4416
                 * {@inheritDoc}
4417
                 */
4418
                public void close() {
4419
                    for (Future<?> future : futures) {
1✔
4420
                        future.cancel(true);
×
UNCOV
4421
                    }
×
4422
                }
1✔
4423

4424
                /**
4425
                 * A parallel dispatcher that shuts down its executor service upon completion of a plugin engine's application.
4426
                 */
4427
                @HashCodeAndEqualsPlugin.Enhance
4428
                public static class WithThrowawayExecutorService extends ForParallelTransformation {
4429

4430
                    /**
4431
                     * The executor service to delegate any work to.
4432
                     */
4433
                    private final ExecutorService executorService;
4434

4435
                    /**
4436
                     * Creates a new dispatcher that applies transformations in parallel and that closes the supplies executor service.
4437
                     *
4438
                     * @param executorService The executor service to delegate any work to.
4439
                     * @param sink            The target sink.
4440
                     * @param transformed     A list of all types that are transformed.
4441
                     * @param failed          A mapping of all types that failed during transformation to the exceptions that explain the failure.
4442
                     * @param unresolved      A list of type names that could not be resolved.
4443
                     */
4444
                    protected WithThrowawayExecutorService(ExecutorService executorService,
4445
                                                           Target.Sink sink,
4446
                                                           List<TypeDescription> transformed,
4447
                                                           Map<TypeDescription, List<Throwable>> failed,
4448
                                                           List<String> unresolved) {
4449
                        super(executorService, sink, transformed, failed, unresolved);
1✔
4450
                        this.executorService = executorService;
1✔
4451
                    }
1✔
4452

4453
                    @Override
4454
                    public void close() {
4455
                        try {
4456
                            super.close();
1✔
4457
                        } finally {
4458
                            executorService.shutdown();
1✔
4459
                        }
4460
                    }
1✔
4461

4462
                    /**
4463
                     * A factory for a parallel executor service that creates a new executor service on each plugin engine application.
4464
                     */
4465
                    @HashCodeAndEqualsPlugin.Enhance
4466
                    public static class Factory implements Dispatcher.Factory {
4467

4468
                        /**
4469
                         * The amount of threads to create in the throw-away executor service.
4470
                         */
4471
                        private final int threads;
4472

4473
                        /**
4474
                         * Creates a new factory.
4475
                         *
4476
                         * @param threads The amount of threads to create in the throw-away executor service.
4477
                         */
4478
                        public Factory(int threads) {
1✔
4479
                            this.threads = threads;
1✔
4480
                        }
1✔
4481

4482
                        /**
4483
                         * {@inheritDoc}
4484
                         */
4485
                        public Dispatcher make(Target.Sink sink,
4486
                                               List<TypeDescription> transformed,
4487
                                               Map<TypeDescription, List<Throwable>> failed,
4488
                                               List<String> unresolved) {
4489
                            return new WithThrowawayExecutorService(Executors.newFixedThreadPool(threads), sink, transformed, failed, unresolved);
1✔
4490
                        }
4491
                    }
4492
                }
4493

4494
                /**
4495
                 * A factory for a dispatcher that uses a given executor service for parallel dispatching.
4496
                 */
4497
                @HashCodeAndEqualsPlugin.Enhance
4498
                public static class Factory implements Dispatcher.Factory {
4499

4500
                    /**
4501
                     * The executor to use.
4502
                     */
4503
                    private final Executor executor;
4504

4505
                    /**
4506
                     * Creates a new dispatcher factory for parallel dispatching using the supplied executor.
4507
                     *
4508
                     * @param executor The executor to use.
4509
                     */
UNCOV
4510
                    public Factory(Executor executor) {
×
UNCOV
4511
                        this.executor = executor;
×
UNCOV
4512
                    }
×
4513

4514
                    /**
4515
                     * {@inheritDoc}
4516
                     */
4517
                    public Dispatcher make(Target.Sink sink,
4518
                                           List<TypeDescription> transformed,
4519
                                           Map<TypeDescription, List<Throwable>> failed,
4520
                                           List<String> unresolved) {
4521
                        return new ForParallelTransformation(executor, sink, transformed, failed, unresolved);
×
4522
                    }
4523
                }
4524

4525
                /**
4526
                 * An eager materialization that does not defer processing after preprocessing.
4527
                 */
4528
                @HashCodeAndEqualsPlugin.Enhance
4529
                protected static class EagerWork implements Callable<Materializable> {
4530

4531
                    /**
4532
                     * The work to apply.
4533
                     */
4534
                    private final Callable<? extends Callable<? extends Materializable>> work;
4535

4536
                    /**
4537
                     * Creates an eager work resolution.
4538
                     *
4539
                     * @param work The work to apply.
4540
                     */
4541
                    protected EagerWork(Callable<? extends Callable<? extends Materializable>> work) {
1✔
4542
                        this.work = work;
1✔
4543
                    }
1✔
4544

4545
                    /**
4546
                     * {@inheritDoc}
4547
                     */
4548
                    public Materializable call() throws Exception {
4549
                        return work.call().call();
1✔
4550
                    }
4551
                }
4552
            }
4553
        }
4554

4555
        /**
4556
         * A summary of the application of a {@link Engine} to a source and target.
4557
         */
4558
        class Summary {
4559

4560
            /**
4561
             * A list of all types that were transformed.
4562
             */
4563
            private final List<TypeDescription> transformed;
4564

4565
            /**
4566
             * A mapping of all types that failed during transformation to the exceptions that explain the failure.
4567
             */
4568
            private final Map<TypeDescription, List<Throwable>> failed;
4569

4570
            /**
4571
             * A list of type names that could not be resolved.
4572
             */
4573
            private final List<String> unresolved;
4574

4575
            /**
4576
             * Creates a new summary.
4577
             *
4578
             * @param transformed A list of all types that were transformed.
4579
             * @param failed      A mapping of all types that failed during transformation to the exceptions that explain the failure.
4580
             * @param unresolved  A list of type names that could not be resolved.
4581
             */
4582
            public Summary(List<TypeDescription> transformed, Map<TypeDescription, List<Throwable>> failed, List<String> unresolved) {
1✔
4583
                this.transformed = transformed;
1✔
4584
                this.failed = failed;
1✔
4585
                this.unresolved = unresolved;
1✔
4586
            }
1✔
4587

4588
            /**
4589
             * Returns a list of all types that were transformed.
4590
             *
4591
             * @return A list of all types that were transformed.
4592
             */
4593
            public List<TypeDescription> getTransformed() {
4594
                return transformed;
1✔
4595
            }
4596

4597
            /**
4598
             * Returns a mapping of all types that failed during transformation to the exceptions that explain the failure.
4599
             *
4600
             * @return A mapping of all types that failed during transformation to the exceptions that explain the failure.
4601
             */
4602
            public Map<TypeDescription, List<Throwable>> getFailed() {
4603
                return failed;
1✔
4604
            }
4605

4606
            /**
4607
             * Returns a list of type names that could not be resolved.
4608
             *
4609
             * @return A list of type names that could not be resolved.
4610
             */
4611
            public List<String> getUnresolved() {
4612
                return unresolved;
1✔
4613
            }
4614

4615
            @Override
4616
            public int hashCode() {
4617
                int result = transformed.hashCode();
1✔
4618
                result = 31 * result + failed.hashCode();
1✔
4619
                result = 31 * result + unresolved.hashCode();
1✔
4620
                return result;
1✔
4621
            }
4622

4623
            @Override
4624
            public boolean equals(@MaybeNull Object other) {
4625
                if (this == other) {
1✔
4626
                    return true;
1✔
4627
                } else if (other == null || getClass() != other.getClass()) {
1✔
4628
                    return false;
1✔
4629
                }
4630
                Summary summary = (Summary) other;
1✔
4631
                return transformed.equals(summary.transformed)
1✔
4632
                        && failed.equals(summary.failed)
1✔
4633
                        && unresolved.equals(summary.unresolved);
1✔
4634
            }
4635
        }
4636

4637
        /**
4638
         * An abstract base implementation of a plugin engine.
4639
         */
4640
        abstract class AbstractBase implements Engine {
1✔
4641

4642
            /**
4643
             * {@inheritDoc}
4644
             */
4645
            public Engine withErrorHandlers(ErrorHandler... errorHandler) {
4646
                return withErrorHandlers(Arrays.asList(errorHandler));
1✔
4647
            }
4648

4649
            /**
4650
             * {@inheritDoc}
4651
             */
4652
            public Engine withParallelTransformation(int threads) {
UNCOV
4653
                if (threads < 1) {
×
UNCOV
4654
                    throw new IllegalArgumentException("Number of threads must be positive: " + threads);
×
4655
                }
UNCOV
4656
                return with(new Dispatcher.ForParallelTransformation.WithThrowawayExecutorService.Factory(threads));
×
4657
            }
4658

4659
            /**
4660
             * {@inheritDoc}
4661
             */
4662
            public Summary apply(File source, File target, Factory... factory) throws IOException {
4663
                return apply(source, target, Arrays.asList(factory));
1✔
4664
            }
4665

4666
            /**
4667
             * {@inheritDoc}
4668
             */
4669
            public Summary apply(File source, File target, List<? extends Factory> factories) throws IOException {
4670
                return apply(source.isDirectory()
1✔
4671
                        ? new Source.ForFolder(source)
4672
                        : new Source.ForJarFile(source), target.isDirectory()
1✔
4673
                        ? new Target.ForFolder(target)
4674
                        : new Target.ForJarFile(target), factories);
4675
            }
4676

4677
            /**
4678
             * {@inheritDoc}
4679
             */
4680
            public Summary apply(Source source, Target target, Factory... factory) throws IOException {
4681
                return apply(source, target, Arrays.asList(factory));
1✔
4682
            }
4683
        }
4684

4685
        /**
4686
         * A default implementation of a plugin engine.
4687
         */
4688
        @HashCodeAndEqualsPlugin.Enhance
4689
        class Default extends AbstractBase {
4690

4691
            /**
4692
             * The Byte Buddy instance to use.
4693
             */
4694
            private final ByteBuddy byteBuddy;
4695

4696
            /**
4697
             * The type strategy to use.
4698
             */
4699
            private final TypeStrategy typeStrategy;
4700

4701
            /**
4702
             * The pool strategy to use.
4703
             */
4704
            private final PoolStrategy poolStrategy;
4705

4706
            /**
4707
             * The class file locator to use.
4708
             */
4709
            private final ClassFileLocator classFileLocator;
4710

4711
            /**
4712
             * The class file version to use for multi-release jars, or {@code null}.
4713
             */
4714
            @MaybeNull
4715
            @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
4716
            private final ClassFileVersion classFileVersion;
4717

4718
            /**
4719
             * The listener to use.
4720
             */
4721
            private final Listener listener;
4722

4723
            /**
4724
             * The error handler to use.
4725
             */
4726
            private final ErrorHandler errorHandler;
4727

4728
            /**
4729
             * The dispatcher factory to use.
4730
             */
4731
            private final Dispatcher.Factory dispatcherFactory;
4732

4733
            /**
4734
             * A matcher for types to exclude from transformation.
4735
             */
4736
            private final ElementMatcher.Junction<? super TypeDescription> ignoredTypeMatcher;
4737

4738
            /**
4739
             * Creates a new default plugin engine that rebases types and fails fast and on unresolved types and on live initializers.
4740
             */
4741
            public Default() {
4742
                this(new ByteBuddy());
1✔
4743
            }
1✔
4744

4745
            /**
4746
             * Creates a new default plugin engine that rebases types and fails fast and on unresolved types and on live initializers.
4747
             *
4748
             * @param byteBuddy The Byte Buddy instance to use.
4749
             */
4750
            public Default(ByteBuddy byteBuddy) {
4751
                this(byteBuddy, TypeStrategy.Default.REBASE);
1✔
4752
            }
1✔
4753

4754
            /**
4755
             * Creates a new default plugin engine.
4756
             *
4757
             * @param byteBuddy    The Byte Buddy instance to use.
4758
             * @param typeStrategy The type strategy to use.
4759
             */
4760
            protected Default(ByteBuddy byteBuddy, TypeStrategy typeStrategy) {
4761
                this(byteBuddy,
1✔
4762
                        typeStrategy,
4763
                        PoolStrategy.Default.FAST,
4764
                        ClassFileLocator.NoOp.INSTANCE,
4765
                        null,
4766
                        Listener.NoOp.INSTANCE,
4767
                        new ErrorHandler.Compound(ErrorHandler.Failing.FAIL_FAST,
4768
                                ErrorHandler.Enforcing.ALL_TYPES_RESOLVED,
4769
                                ErrorHandler.Enforcing.NO_LIVE_INITIALIZERS),
4770
                        Dispatcher.ForSerialTransformation.Factory.INSTANCE,
4771
                        none());
1✔
4772
            }
1✔
4773

4774
            /**
4775
             * Creates a new default plugin engine.
4776
             *
4777
             * @param byteBuddy          The Byte Buddy instance to use.
4778
             * @param typeStrategy       The type strategy to use.
4779
             * @param poolStrategy       The pool strategy to use.
4780
             * @param classFileLocator   The class file locator to use.
4781
             * @param classFileVersion   The class file version to use for multi-release jars, or {@code null}.
4782
             * @param listener           The listener to use.
4783
             * @param errorHandler       The error handler to use.
4784
             * @param dispatcherFactory  The dispatcher factory to use.
4785
             * @param ignoredTypeMatcher A matcher for types to exclude from transformation.
4786
             */
4787
            protected Default(ByteBuddy byteBuddy,
4788
                              TypeStrategy typeStrategy,
4789
                              PoolStrategy poolStrategy,
4790
                              ClassFileLocator classFileLocator,
4791
                              @MaybeNull ClassFileVersion classFileVersion,
4792
                              Listener listener,
4793
                              ErrorHandler errorHandler,
4794
                              Dispatcher.Factory dispatcherFactory,
4795
                              ElementMatcher.Junction<? super TypeDescription> ignoredTypeMatcher) {
1✔
4796
                this.byteBuddy = byteBuddy;
1✔
4797
                this.typeStrategy = typeStrategy;
1✔
4798
                this.poolStrategy = poolStrategy;
1✔
4799
                this.classFileLocator = classFileLocator;
1✔
4800
                this.classFileVersion = classFileVersion;
1✔
4801
                this.listener = listener;
1✔
4802
                this.errorHandler = errorHandler;
1✔
4803
                this.dispatcherFactory = dispatcherFactory;
1✔
4804
                this.ignoredTypeMatcher = ignoredTypeMatcher;
1✔
4805
            }
1✔
4806

4807
            /**
4808
             * Creates a plugin engine from an {@link EntryPoint}.
4809
             *
4810
             * @param entryPoint            The entry point to resolve into a plugin engine.
4811
             * @param classFileVersion      The class file version to assume.
4812
             * @param methodNameTransformer The method name transformer to use.
4813
             * @return An appropriate plugin engine.
4814
             */
4815
            public static Engine of(EntryPoint entryPoint, ClassFileVersion classFileVersion, MethodNameTransformer methodNameTransformer) {
4816
                return new Default(entryPoint.byteBuddy(classFileVersion), new TypeStrategy.ForEntryPoint(entryPoint, methodNameTransformer));
1✔
4817
            }
4818

4819
            /**
4820
             * Scans a class loader for plugins declared in a <i>META-INF/net.bytebuddy/build.plugins</i> file.
4821
             *
4822
             * @param classLoader The class loader to scan.
4823
             * @return A set containing all plugins that were found.
4824
             * @throws IOException If an I/O exception occurred.
4825
             */
4826
            public static Set<String> scan(ClassLoader classLoader) throws IOException {
4827
                Set<String> plugins = new HashSet<String>();
1✔
4828
                Enumeration<URL> enumeration = classLoader.getResources(PLUGIN_FILE);
1✔
4829
                while (enumeration.hasMoreElements()) {
1✔
4830
                    BufferedReader reader = new BufferedReader(new InputStreamReader(enumeration.nextElement().openStream(), "UTF-8"));
1✔
4831
                    try {
4832
                        String line;
4833
                        while ((line = reader.readLine()) != null) {
1✔
4834
                            plugins.add(line);
1✔
4835
                        }
4836
                    } finally {
4837
                        reader.close();
1✔
4838
                    }
4839
                }
1✔
4840
                return plugins;
1✔
4841
            }
4842

4843
            /**
4844
             * Runs a plugin engine using the first and second argument as source and target file location and any additional argument as
4845
             * the fully qualified name of any plugin to apply.
4846
             *
4847
             * @param argument The arguments for running the plugin engine.
4848
             * @throws ClassNotFoundException If a plugin class cannot be found on the system class loader.
4849
             * @throws IOException            If an I/O exception occurs.
4850
             */
4851
            @SuppressWarnings("unchecked")
4852
            public static void main(String... argument) throws ClassNotFoundException, IOException {
4853
                if (argument.length < 2) {
1✔
UNCOV
4854
                    throw new IllegalArgumentException("Expected arguments: <source> <target> [<plugin>, ...]");
×
4855
                }
4856
                List<Plugin.Factory> factories = new ArrayList<Factory>(argument.length - 2);
1✔
4857
                for (String plugin : Arrays.asList(argument).subList(2, argument.length)) {
1✔
4858
                    factories.add(new Factory.UsingReflection((Class<? extends Plugin>) Class.forName(plugin)));
1✔
4859
                }
1✔
4860
                new Default().apply(new File(argument[0]), new File(argument[1]), factories);
1✔
4861
            }
1✔
4862

4863
            /**
4864
             * {@inheritDoc}
4865
             */
4866
            public Engine with(ByteBuddy byteBuddy) {
4867
                return new Default(byteBuddy,
1✔
4868
                        typeStrategy,
4869
                        poolStrategy,
4870
                        classFileLocator,
4871
                        classFileVersion,
4872
                        listener,
4873
                        errorHandler,
4874
                        dispatcherFactory,
4875
                        ignoredTypeMatcher);
4876
            }
4877

4878
            /**
4879
             * {@inheritDoc}
4880
             */
4881
            public Engine with(TypeStrategy typeStrategy) {
4882
                return new Default(byteBuddy,
1✔
4883
                        typeStrategy,
4884
                        poolStrategy,
4885
                        classFileLocator,
4886
                        classFileVersion,
4887
                        listener,
4888
                        errorHandler,
4889
                        dispatcherFactory,
4890
                        ignoredTypeMatcher);
4891
            }
4892

4893
            /**
4894
             * {@inheritDoc}
4895
             */
4896
            public Engine with(PoolStrategy poolStrategy) {
4897
                return new Default(byteBuddy,
1✔
4898
                        typeStrategy,
4899
                        poolStrategy,
4900
                        classFileLocator,
4901
                        classFileVersion,
4902
                        listener,
4903
                        errorHandler,
4904
                        dispatcherFactory,
4905
                        ignoredTypeMatcher);
4906
            }
4907

4908
            /**
4909
             * {@inheritDoc}
4910
             */
4911
            public Engine with(ClassFileLocator classFileLocator) {
4912
                return new Default(byteBuddy,
1✔
4913
                        typeStrategy,
4914
                        poolStrategy,
4915
                        new ClassFileLocator.Compound(this.classFileLocator, classFileLocator),
4916
                        classFileVersion,
4917
                        listener,
4918
                        errorHandler,
4919
                        dispatcherFactory,
4920
                        ignoredTypeMatcher);
4921
            }
4922

4923
            /**
4924
             * {@inheritDoc}
4925
             */
4926
            public Engine with(@MaybeNull ClassFileVersion classFileVersion) {
4927
                return new Default(byteBuddy,
1✔
4928
                        typeStrategy,
4929
                        poolStrategy,
4930
                        classFileLocator,
4931
                        classFileVersion,
4932
                        listener,
4933
                        errorHandler,
4934
                        dispatcherFactory,
4935
                        ignoredTypeMatcher);
4936
            }
4937

4938
            /**
4939
             * {@inheritDoc}
4940
             */
4941
            public Engine with(Listener listener) {
4942
                return new Default(byteBuddy,
1✔
4943
                        typeStrategy,
4944
                        poolStrategy,
4945
                        classFileLocator,
4946
                        classFileVersion,
4947
                        new Listener.Compound(this.listener, listener),
4948
                        errorHandler,
4949
                        dispatcherFactory,
4950
                        ignoredTypeMatcher);
4951
            }
4952

4953
            /**
4954
             * {@inheritDoc}
4955
             */
4956
            public Engine withoutErrorHandlers() {
4957
                return new Default(byteBuddy,
1✔
4958
                        typeStrategy,
4959
                        poolStrategy,
4960
                        classFileLocator,
4961
                        classFileVersion,
4962
                        listener,
4963
                        Listener.NoOp.INSTANCE,
4964
                        dispatcherFactory,
4965
                        ignoredTypeMatcher);
4966
            }
4967

4968
            /**
4969
             * {@inheritDoc}
4970
             */
4971
            public Engine withErrorHandlers(List<? extends ErrorHandler> errorHandlers) {
4972
                return new Default(byteBuddy,
1✔
4973
                        typeStrategy,
4974
                        poolStrategy,
4975
                        classFileLocator,
4976
                        classFileVersion,
4977
                        listener,
4978
                        new ErrorHandler.Compound(errorHandlers),
4979
                        dispatcherFactory,
4980
                        ignoredTypeMatcher);
4981
            }
4982

4983
            /**
4984
             * {@inheritDoc}
4985
             */
4986
            public Engine with(Dispatcher.Factory dispatcherFactory) {
4987
                return new Default(byteBuddy,
1✔
4988
                        typeStrategy,
4989
                        poolStrategy,
4990
                        classFileLocator,
4991
                        classFileVersion,
4992
                        listener,
4993
                        errorHandler,
4994
                        dispatcherFactory,
4995
                        ignoredTypeMatcher);
4996
            }
4997

4998
            /**
4999
             * {@inheritDoc}
5000
             */
5001
            public Engine ignore(ElementMatcher<? super TypeDescription> matcher) {
5002
                return new Default(byteBuddy,
1✔
5003
                        typeStrategy,
5004
                        poolStrategy,
5005
                        classFileLocator,
5006
                        classFileVersion,
5007
                        listener,
5008
                        errorHandler,
5009
                        dispatcherFactory,
5010
                        ignoredTypeMatcher.<TypeDescription>or(matcher));
1✔
5011
            }
5012

5013
            /**
5014
             * {@inheritDoc}
5015
             */
5016
            public Summary apply(Source source, Target target, List<? extends Plugin.Factory> factories) throws IOException {
5017
                Listener listener = new Listener.Compound(this.listener, new Listener.ForErrorHandler(errorHandler));
1✔
5018
                List<TypeDescription> transformed = new ArrayList<TypeDescription>();
1✔
5019
                Map<TypeDescription, List<Throwable>> failed = new LinkedHashMap<TypeDescription, List<Throwable>>();
1✔
5020
                List<String> unresolved = new ArrayList<String>();
1✔
5021
                Throwable rethrown = null;
1✔
5022
                List<Plugin> plugins = new ArrayList<Plugin>(factories.size());
1✔
5023
                List<WithInitialization> initializers = new ArrayList<WithInitialization>();
1✔
5024
                List<WithPreprocessor> preprocessors = new ArrayList<WithPreprocessor>();
1✔
5025
                try {
5026
                    for (Plugin.Factory factory : factories) {
1✔
5027
                        Plugin plugin = factory.make();
1✔
5028
                        plugins.add(plugin);
1✔
5029
                        if (plugin instanceof WithPreprocessor) {
1✔
5030
                            preprocessors.add((WithPreprocessor) plugin);
1✔
5031
                        }
5032
                        if (plugin instanceof WithInitialization) {
1✔
5033
                            initializers.add((WithInitialization) plugin);
1✔
5034
                        }
5035
                    }
1✔
5036
                    Source.Origin origin = source.read();
1✔
5037
                    try {
5038
                        ClassFileLocator classFileLocator = new ClassFileLocator.Compound(origin.toClassFileLocator(classFileVersion), this.classFileLocator);
1✔
5039
                        TypePool typePool = poolStrategy.typePool(classFileLocator);
1✔
5040
                        Manifest manifest = origin.getManifest();
1✔
5041
                        listener.onManifest(manifest);
1✔
5042
                        Target.Sink sink = target.write(manifest);
1✔
5043
                        try {
5044
                            for (WithInitialization initializer : initializers) {
1✔
5045
                                sink.store(initializer.initialize(classFileLocator));
1✔
5046
                            }
1✔
5047
                            Dispatcher dispatcher = dispatcherFactory.make(sink, transformed, failed, unresolved);
1✔
5048
                            try {
5049
                                for (Source.Element element : origin) {
1✔
5050
                                    if (Thread.interrupted()) {
1✔
UNCOV
5051
                                        Thread.currentThread().interrupt();
×
UNCOV
5052
                                        throw new IllegalStateException("Thread interrupted during plugin engine application");
×
5053
                                    }
5054
                                    String name = element.getName();
1✔
5055
                                    while (name.startsWith("/")) {
1✔
UNCOV
5056
                                        name = name.substring(1);
×
5057
                                    }
5058
                                    if (name.endsWith(ClassFileLocator.CLASS_FILE_EXTENSION) && (!name.startsWith("META-INF") || name.startsWith(ClassFileLocator.META_INF_VERSIONS))) {
1✔
5059
                                        try {
5060
                                            ClassFileVersion classFileVersion = name.startsWith(ClassFileLocator.META_INF_VERSIONS)
1✔
5061
                                                    ? ClassFileVersion.ofJavaVersion(Integer.parseInt(name.substring(ClassFileLocator.META_INF_VERSIONS.length(), name.indexOf('/', ClassFileLocator.META_INF_VERSIONS.length()))))
1✔
5062
                                                    : null;
5063
                                            if (classFileVersion == null || classFileVersion.isAtLeast(ClassFileVersion.JAVA_V8)
1✔
5064
                                                    && this.classFileVersion != null
5065
                                                    && this.classFileVersion.isAtLeast(ClassFileVersion.JAVA_V9)
1✔
5066
                                                    && classFileVersion.isAtMost(this.classFileVersion)) {
1✔
5067
                                                String typeName = name.substring(name.startsWith(ClassFileLocator.META_INF_VERSIONS)
1✔
5068
                                                        ? name.indexOf('/', ClassFileLocator.META_INF_VERSIONS.length()) + 1
1✔
5069
                                                        : 0, name.length() - ClassFileLocator.CLASS_FILE_EXTENSION.length()).replace('/', '.');
1✔
5070
                                                dispatcher.accept(new Preprocessor(element,
1✔
5071
                                                        typeName,
5072
                                                        new SourceEntryPrependingClassFileLocator(typeName, element, classFileLocator),
5073
                                                        classFileVersion,
5074
                                                        typePool,
5075
                                                        listener,
5076
                                                        plugins,
5077
                                                        preprocessors), preprocessors.isEmpty());
1✔
5078
                                            } else {
1✔
UNCOV
5079
                                                listener.onResource(name);
×
UNCOV
5080
                                                sink.retain(element);
×
5081
                                            }
UNCOV
5082
                                        } catch (NumberFormatException ignored) {
×
UNCOV
5083
                                            listener.onResource(name);
×
UNCOV
5084
                                            sink.retain(element);
×
5085
                                        }
1✔
5086
                                    } else if (!name.equals(JarFile.MANIFEST_NAME)) {
1✔
5087
                                        listener.onResource(name);
1✔
5088
                                        sink.retain(element);
1✔
5089
                                    }
5090
                                }
1✔
5091
                                dispatcher.complete();
1✔
5092
                            } finally {
5093
                                dispatcher.close();
1✔
5094
                            }
5095
                            if (!failed.isEmpty()) {
1✔
5096
                                listener.onError(failed);
1✔
5097
                            }
5098
                        } finally {
5099
                            sink.close();
1✔
5100
                        }
5101
                    } finally {
5102
                        origin.close();
1✔
5103
                    }
5104
                } finally {
5105
                    for (Plugin plugin : plugins) {
1✔
5106
                        try {
5107
                            plugin.close();
1✔
5108
                        } catch (Throwable throwable) {
1✔
5109
                            try {
5110
                                listener.onError(plugin, throwable);
1✔
5111
                            } catch (Throwable chained) {
1✔
5112
                                rethrown = rethrown == null
1✔
5113
                                        ? chained
5114
                                        : rethrown;
5115
                            }
1✔
5116
                        }
1✔
5117
                    }
1✔
5118
                }
5119
                if (rethrown == null) {
1✔
5120
                    return new Summary(transformed, failed, unresolved);
1✔
UNCOV
5121
                } else if (rethrown instanceof IOException) {
×
UNCOV
5122
                    throw (IOException) rethrown;
×
UNCOV
5123
                } else if (rethrown instanceof RuntimeException) {
×
UNCOV
5124
                    throw (RuntimeException) rethrown;
×
5125
                } else {
UNCOV
5126
                    throw new IllegalStateException(rethrown);
×
5127
                }
5128
            }
5129

5130
            /**
5131
             * A class file locator that shadows a given {@link Source.Element}'s type with the explicit element.
5132
             * This avoids that caching yields the wrong class file in case of multi-release jars.
5133
             */
5134
            @HashCodeAndEqualsPlugin.Enhance
5135
            protected static class SourceEntryPrependingClassFileLocator implements ClassFileLocator {
5136

5137
                /**
5138
                 * The name of the represented type.
5139
                 */
5140
                private final String name;
5141

5142
                /**
5143
                 * The corresponding source element.
5144
                 */
5145
                private final Source.Element element;
5146

5147
                /**
5148
                 * The actual class file locator to query for all other types.
5149
                 */
5150
                private final ClassFileLocator delegate;
5151

5152
                /**
5153
                 * Creates a class file locator that prepends a {@link Source.Element}.
5154
                 *
5155
                 * @param name     The name of the represented type.
5156
                 * @param element  The corresponding source element.
5157
                 * @param delegate The actual class file locator to query for all other types.
5158
                 */
5159
                protected SourceEntryPrependingClassFileLocator(String name, Source.Element element, ClassFileLocator delegate) {
1✔
5160
                    this.name = name;
1✔
5161
                    this.element = element;
1✔
5162
                    this.delegate = delegate;
1✔
5163
                }
1✔
5164

5165
                /**
5166
                 * {@inheritDoc}
5167
                 */
5168
                public Resolution locate(String name) throws IOException {
5169
                    if (name.endsWith(this.name)) {
1✔
5170
                        InputStream inputStream = element.getInputStream();
1✔
5171
                        try {
5172
                            return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
1✔
5173
                        } finally {
5174
                            inputStream.close();
1✔
5175
                        }
5176
                    } else {
UNCOV
5177
                        return delegate.locate(name);
×
5178
                    }
5179
                }
5180

5181
                /**
5182
                 * {@inheritDoc}
5183
                 */
5184
                public void close() throws IOException {
UNCOV
5185
                    delegate.close();
×
UNCOV
5186
                }
×
5187
            }
5188

5189
            /**
5190
             * A preprocessor for a parallel plugin engine.
5191
             */
5192
            private class Preprocessor implements Callable<Callable<? extends Dispatcher.Materializable>> {
5193

5194
                /**
5195
                 * The processed element.
5196
                 */
5197
                private final Source.Element element;
5198

5199
                /**
5200
                 * The name of the processed type.
5201
                 */
5202
                private final String typeName;
5203

5204
                /**
5205
                 * The class file locator to use.
5206
                 */
5207
                private final ClassFileLocator classFileLocator;
5208

5209
                /**
5210
                 * The multi-release class file version or {@code null} for a regular class.
5211
                 */
5212
                @MaybeNull
5213
                @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
5214
                private final ClassFileVersion classFileVersion;
5215

5216
                /**
5217
                 * The type pool to use.
5218
                 */
5219
                private final TypePool typePool;
5220

5221
                /**
5222
                 * The listener to notify.
5223
                 */
5224
                private final Listener listener;
5225

5226
                /**
5227
                 * The plugins to apply.
5228
                 */
5229
                private final List<Plugin> plugins;
5230

5231
                /**
5232
                 * The plugins with preprocessors to preprocess.
5233
                 */
5234
                private final List<WithPreprocessor> preprocessors;
5235

5236
                /**
5237
                 * Creates a new preprocessor.
5238
                 *
5239
                 * @param element          The processed element.
5240
                 * @param typeName         The name of the processed type.
5241
                 * @param classFileLocator The class file locator to use.
5242
                 * @param classFileVersion The multi-release class file version or {@code null} for a regular class.
5243
                 * @param typePool         The type pool to use.
5244
                 * @param listener         The listener to notify.
5245
                 * @param plugins          The plugins to apply.
5246
                 * @param preprocessors    The plugins with preprocessors to preprocess.
5247
                 */
5248
                private Preprocessor(Source.Element element,
5249
                                     String typeName,
5250
                                     ClassFileLocator classFileLocator,
5251
                                     @MaybeNull ClassFileVersion classFileVersion,
5252
                                     TypePool typePool,
5253
                                     Listener listener,
5254
                                     List<Plugin> plugins,
5255
                                     List<WithPreprocessor> preprocessors) {
1✔
5256
                    this.element = element;
1✔
5257
                    this.typeName = typeName;
1✔
5258
                    this.classFileLocator = classFileLocator;
1✔
5259
                    this.classFileVersion = classFileVersion;
1✔
5260
                    this.typePool = typePool;
1✔
5261
                    this.listener = listener;
1✔
5262
                    this.plugins = plugins;
1✔
5263
                    this.preprocessors = preprocessors;
1✔
5264
                }
1✔
5265

5266
                /**
5267
                 * {@inheritDoc}
5268
                 */
5269
                public Callable<Dispatcher.Materializable> call() throws Exception {
5270
                    listener.onDiscovery(typeName);
1✔
5271
                    TypePool.Resolution resolution = typePool.describe(typeName);
1✔
5272
                    if (resolution.isResolved()) {
1✔
5273
                        TypeDescription typeDescription = resolution.resolve();
1✔
5274
                        try {
5275
                            if (!ignoredTypeMatcher.matches(typeDescription)) {
1✔
5276
                                for (WithPreprocessor preprocessor : preprocessors) {
1✔
5277
                                    preprocessor.onPreprocess(typeDescription, classFileLocator);
1✔
5278
                                }
1✔
5279
                                return new Resolved(classFileVersion, typeDescription);
1✔
5280
                            } else {
5281
                                return new Ignored(typeDescription);
1✔
5282
                            }
UNCOV
5283
                        } catch (Throwable throwable) {
×
UNCOV
5284
                            listener.onComplete(typeDescription);
×
UNCOV
5285
                            if (throwable instanceof Exception) {
×
UNCOV
5286
                                throw (Exception) throwable;
×
UNCOV
5287
                            } else if (throwable instanceof Error) {
×
UNCOV
5288
                                throw (Error) throwable;
×
5289
                            } else {
UNCOV
5290
                                throw new IllegalStateException(throwable);
×
5291
                            }
5292
                        }
5293
                    } else {
5294
                        return new Unresolved();
1✔
5295
                    }
5296
                }
5297

5298
                /**
5299
                 * A resolved materializable.
5300
                 */
5301
                private class Resolved implements Callable<Dispatcher.Materializable> {
5302

5303
                    /**
5304
                     * The multi-release Java version number or {@code null} if a regular class.
5305
                     */
5306
                    @MaybeNull
5307
                    private final ClassFileVersion classFileVersion;
5308

5309
                    /**
5310
                     * A description of the resolved type.
5311
                     */
5312
                    private final TypeDescription typeDescription;
5313

5314
                    /**
5315
                     * Creates a new resolved materializable.
5316
                     *
5317
                     * @param classFileVersion The multi-release Java version number or {@code null} if a regular class.
5318
                     * @param typeDescription  A description of the resolved type.
5319
                     */
5320
                    private Resolved(@MaybeNull ClassFileVersion classFileVersion, TypeDescription typeDescription) {
1✔
5321
                        this.classFileVersion = classFileVersion;
1✔
5322
                        this.typeDescription = typeDescription;
1✔
5323
                    }
1✔
5324

5325
                    /**
5326
                     * {@inheritDoc}
5327
                     */
5328
                    public Dispatcher.Materializable call() {
5329
                        List<Plugin> applied = new ArrayList<Plugin>(), ignored = new ArrayList<Plugin>();
1✔
5330
                        List<Throwable> errored = new ArrayList<Throwable>();
1✔
5331
                        try {
5332
                            DynamicType.Builder<?> builder = typeStrategy.builder(byteBuddy, typeDescription, classFileLocator);
1✔
5333
                            for (Plugin plugin : plugins) {
1✔
5334
                                try {
5335
                                    if (plugin.matches(typeDescription)) {
1✔
5336
                                        builder = plugin.apply(builder, typeDescription, classFileLocator);
1✔
5337
                                        listener.onTransformation(typeDescription, plugin);
1✔
5338
                                        applied.add(plugin);
1✔
5339
                                    } else {
5340
                                        listener.onIgnored(typeDescription, plugin);
1✔
5341
                                        ignored.add(plugin);
1✔
5342
                                    }
5343
                                } catch (Throwable throwable) {
1✔
5344
                                    listener.onError(typeDescription, plugin, throwable);
1✔
5345
                                    errored.add(throwable);
1✔
5346
                                }
1✔
5347
                            }
1✔
5348
                            if (!errored.isEmpty()) {
1✔
5349
                                listener.onError(typeDescription, errored);
1✔
5350
                                return new Dispatcher.Materializable.ForFailedElement(element, typeDescription, errored);
1✔
5351
                            } else if (!applied.isEmpty()) {
1✔
5352
                                try {
5353
                                    DynamicType dynamicType = builder.make(TypeResolutionStrategy.Disabled.INSTANCE, typePool);
1✔
5354
                                    listener.onTransformation(typeDescription, applied);
1✔
5355
                                    for (Map.Entry<TypeDescription, LoadedTypeInitializer> entry : dynamicType.getLoadedTypeInitializers().entrySet()) {
1✔
5356
                                        if (entry.getValue().isAlive()) {
1✔
5357
                                            listener.onLiveInitializer(typeDescription, entry.getKey());
1✔
5358
                                        }
5359
                                    }
1✔
5360
                                    return new Dispatcher.Materializable.ForTransformedElement(classFileVersion, dynamicType);
1✔
5361
                                } catch (Throwable throwable) {
1✔
5362
                                    errored.add(throwable);
1✔
5363
                                    listener.onError(typeDescription, errored);
1✔
5364
                                    return new Dispatcher.Materializable.ForFailedElement(element, typeDescription, errored);
1✔
5365
                                }
5366
                            } else {
5367
                                listener.onIgnored(typeDescription, ignored);
1✔
5368
                                return new Dispatcher.Materializable.ForRetainedElement(element);
1✔
5369
                            }
5370
                        } finally {
5371
                            listener.onComplete(typeDescription);
1✔
5372
                        }
5373
                    }
5374
                }
5375

5376
                /**
5377
                 * A materializable for an ignored element.
5378
                 */
5379
                private class Ignored implements Callable<Dispatcher.Materializable> {
5380

5381
                    /**
5382
                     * A description of the ignored type.
5383
                     */
5384
                    private final TypeDescription typeDescription;
5385

5386
                    /**
5387
                     * A materializable for an ignored element.
5388
                     *
5389
                     * @param typeDescription A description of the ignored type.
5390
                     */
5391
                    private Ignored(TypeDescription typeDescription) {
1✔
5392
                        this.typeDescription = typeDescription;
1✔
5393
                    }
1✔
5394

5395
                    /**
5396
                     * {@inheritDoc}
5397
                     */
5398
                    public Dispatcher.Materializable call() {
5399
                        try {
5400
                            listener.onIgnored(typeDescription, plugins);
1✔
5401
                        } finally {
5402
                            listener.onComplete(typeDescription);
1✔
5403
                        }
5404
                        return new Dispatcher.Materializable.ForRetainedElement(element);
1✔
5405
                    }
5406
                }
5407

5408
                /**
5409
                 * A materializable that represents an unresolved type.
5410
                 */
5411
                private class Unresolved implements Callable<Dispatcher.Materializable> {
1✔
5412

5413
                    /**
5414
                     * {@inheritDoc}
5415
                     */
5416
                    public Dispatcher.Materializable call() {
5417
                        listener.onUnresolved(typeName);
1✔
5418
                        return new Dispatcher.Materializable.ForUnresolvedElement(element, typeName);
1✔
5419
                    }
5420
                }
5421
            }
5422
        }
5423
    }
5424

5425
    /**
5426
     * A non-operational plugin that does not instrument any type. This plugin does not need to be closed.
5427
     */
5428
    @HashCodeAndEqualsPlugin.Enhance
5429
    class NoOp implements Plugin, Plugin.Factory {
1✔
5430

5431
        /**
5432
         * {@inheritDoc}
5433
         */
5434
        public Plugin make() {
5435
            return this;
1✔
5436
        }
5437

5438
        /**
5439
         * {@inheritDoc}
5440
         */
5441
        public boolean matches(@MaybeNull TypeDescription target) {
5442
            return false;
1✔
5443
        }
5444

5445
        /**
5446
         * {@inheritDoc}
5447
         */
5448
        public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
5449
            throw new IllegalStateException("Cannot apply non-operational plugin");
1✔
5450
        }
5451

5452
        /**
5453
         * {@inheritDoc}
5454
         */
5455
        public void close() {
5456
            /* do nothing */
5457
        }
1✔
5458
    }
5459

5460
    /**
5461
     * An abstract base for a {@link Plugin} that matches types by a given {@link ElementMatcher}.
5462
     */
5463
    @HashCodeAndEqualsPlugin.Enhance
5464
    abstract class ForElementMatcher implements Plugin {
5465

5466
        /**
5467
         * The element matcher to apply.
5468
         */
5469
        private final ElementMatcher<? super TypeDescription> matcher;
5470

5471
        /**
5472
         * Creates a new plugin that matches types using an element matcher.
5473
         *
5474
         * @param matcher The element matcher to apply.
5475
         */
5476
        protected ForElementMatcher(ElementMatcher<? super TypeDescription> matcher) {
1✔
5477
            this.matcher = matcher;
1✔
5478
        }
1✔
5479

5480
        /**
5481
         * {@inheritDoc}
5482
         */
5483
        public boolean matches(@MaybeNull TypeDescription target) {
5484
            return matcher.matches(target);
1✔
5485
        }
5486
    }
5487
}
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