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

raphw / byte-buddy / #801

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

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

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

90.21
/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 module info class file.
799
         */
800
        String MODULE_INFO = "module-info" + ClassFileLocator.CLASS_FILE_EXTENSION;
801

802
        /**
803
         * The package info class file.
804
         */
805
        String PACKAGE_INFO = "package-info" + ClassFileLocator.CLASS_FILE_EXTENSION;
806

807
        /**
808
         * The name of the file that contains declares Byte Buddy plugins for discovery.
809
         */
810
        String PLUGIN_FILE = "META-INF/net.bytebuddy/build.plugins";
811

812
        /**
813
         * Defines a new Byte Buddy instance for usage for type creation.
814
         *
815
         * @param byteBuddy The Byte Buddy instance to use.
816
         * @return A new plugin engine that is equal to this engine but uses the supplied Byte Buddy instance.
817
         */
818
        Engine with(ByteBuddy byteBuddy);
819

820
        /**
821
         * Defines a new type strategy which determines the transformation mode for any instrumented type.
822
         *
823
         * @param typeStrategy The type stategy to use.
824
         * @return A new plugin engine that is equal to this engine but uses the supplied type strategy.
825
         */
826
        Engine with(TypeStrategy typeStrategy);
827

828
        /**
829
         * Defines a new pool strategy that determines how types are being described.
830
         *
831
         * @param poolStrategy The pool strategy to use.
832
         * @return A new plugin engine that is equal to this engine but uses the supplied pool strategy.
833
         */
834
        Engine with(PoolStrategy poolStrategy);
835

836
        /**
837
         * Appends the supplied class file locator to be queried for class files additionally to any previously registered
838
         * class file locators.
839
         *
840
         * @param classFileLocator The class file locator to append.
841
         * @return A new plugin engine that is equal to this engine but with the supplied class file locator being appended.
842
         */
843
        Engine with(ClassFileLocator classFileLocator);
844

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

856
        /**
857
         * Appends the supplied listener to this engine.
858
         *
859
         * @param listener The listener to append.
860
         * @return A new plugin engine that is equal to this engine but with the supplied listener being appended.
861
         */
862
        Engine with(Listener listener);
863

864
        /**
865
         * Replaces the error handlers of this plugin engine without applying any error handlers.
866
         *
867
         * @return A new plugin engine that is equal to this engine but without any error handlers being registered.
868
         */
869
        Engine withoutErrorHandlers();
870

871
        /**
872
         * Replaces the error handlers of this plugin engine with the supplied error handlers.
873
         *
874
         * @param errorHandler The error handlers to apply.
875
         * @return A new plugin engine that is equal to this engine but with only the supplied error handlers being applied.
876
         */
877
        Engine withErrorHandlers(ErrorHandler... errorHandler);
878

879
        /**
880
         * Replaces the error handlers of this plugin engine with the supplied error handlers.
881
         *
882
         * @param errorHandlers The error handlers to apply.
883
         * @return A new plugin engine that is equal to this engine but with only the supplied error handlers being applied.
884
         */
885
        Engine withErrorHandlers(List<? extends ErrorHandler> errorHandlers);
886

887
        /**
888
         * Replaces the dispatcher factory of this plugin engine with a parallel dispatcher factory that uses the given amount of threads.
889
         *
890
         * @param threads The amount of threads to use.
891
         * @return A new plugin engine that is equal to this engine but with a parallel dispatcher factory using the specified amount of threads.
892
         */
893
        Engine withParallelTransformation(int threads);
894

895
        /**
896
         * Replaces the dispatcher factory of this plugin engine with the supplied dispatcher factory.
897
         *
898
         * @param dispatcherFactory The dispatcher factory to use.
899
         * @return A new plugin engine that is equal to this engine but with the supplied dispatcher factory being used.
900
         */
901
        Engine with(Dispatcher.Factory dispatcherFactory);
902

903
        /**
904
         * Ignores all types that are matched by this matcher or any previously registered ignore matcher.
905
         *
906
         * @param matcher The ignore matcher to append.
907
         * @return A new plugin engine that is equal to this engine but which ignores any type that is matched by the supplied matcher.
908
         */
909
        Engine ignore(ElementMatcher<? super TypeDescription> matcher);
910

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

922
        /**
923
         * Applies this plugin engine onto a given source and target.
924
         *
925
         * @param source    The source which is treated as a folder or a jar file, if a folder does not exist.
926
         * @param target    The target which is treated as a folder or a jar file, if a folder does not exist.
927
         * @param factories A list of plugin factories to a apply.
928
         * @return A summary of the applied transformation.
929
         * @throws IOException If an I/O error occurs.
930
         */
931
        Summary apply(File source, File target, List<? extends Plugin.Factory> factories) throws IOException;
932

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

944
        /**
945
         * Applies this plugin engine onto a given source and target.
946
         *
947
         * @param source    The source to use.
948
         * @param target    The target to use.
949
         * @param factories A list of plugin factories to a apply.
950
         * @return A summary of the applied transformation.
951
         * @throws IOException If an I/O error occurs.
952
         */
953
        Summary apply(Source source, Target target, List<? extends Plugin.Factory> factories) throws IOException;
954

955
        /**
956
         * A type strategy determines the transformation that is applied to a type description.
957
         */
958
        interface TypeStrategy {
959

960
            /**
961
             * Creates a builder for a given type.
962
             *
963
             * @param byteBuddy        The Byte Buddy instance to use.
964
             * @param typeDescription  The type being transformed.
965
             * @param classFileLocator A class file locator for finding the type's class file.
966
             * @return A dynamic type builder for the provided type.
967
             */
968
            DynamicType.Builder<?> builder(ByteBuddy byteBuddy, TypeDescription typeDescription, ClassFileLocator classFileLocator);
969

970
            /**
971
             * Default implementations for type strategies.
972
             */
973
            enum Default implements TypeStrategy {
1✔
974

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

987
                /**
988
                 * A type strategy that rebases a type's methods.
989
                 */
990
                REBASE {
1✔
991
                    /**
992
                     * {@inheritDoc}
993
                     */
994
                    public DynamicType.Builder<?> builder(ByteBuddy byteBuddy, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
995
                        return byteBuddy.rebase(typeDescription, classFileLocator);
1✔
996
                    }
997
                },
998

999
                /**
1000
                 * A type strategy that decorates a type.
1001
                 */
1002
                DECORATE {
1✔
1003
                    /**
1004
                     * {@inheritDoc}
1005
                     */
1006
                    public DynamicType.Builder<?> builder(ByteBuddy byteBuddy, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
1007
                        return byteBuddy.decorate(typeDescription, classFileLocator);
1✔
1008
                    }
1009
                }
1010
            }
1011

1012
            /**
1013
             * A type strategy that represents a given {@link EntryPoint} for a build tool.
1014
             */
1015
            @HashCodeAndEqualsPlugin.Enhance
1016
            class ForEntryPoint implements TypeStrategy {
1017

1018
                /**
1019
                 * The represented entry point.
1020
                 */
1021
                private final EntryPoint entryPoint;
1022

1023
                /**
1024
                 * A method name transformer to use for rebasements.
1025
                 */
1026
                private final MethodNameTransformer methodNameTransformer;
1027

1028
                /**
1029
                 * Creates a new type stratrgy for an entry point.
1030
                 *
1031
                 * @param entryPoint            The represented entry point.
1032
                 * @param methodNameTransformer A method name transformer to use for rebasements.
1033
                 */
1034
                public ForEntryPoint(EntryPoint entryPoint, MethodNameTransformer methodNameTransformer) {
1✔
1035
                    this.entryPoint = entryPoint;
1✔
1036
                    this.methodNameTransformer = methodNameTransformer;
1✔
1037
                }
1✔
1038

1039
                /**
1040
                 * {@inheritDoc}
1041
                 */
1042
                public DynamicType.Builder<?> builder(ByteBuddy byteBuddy, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
1043
                    return entryPoint.transform(typeDescription, byteBuddy, classFileLocator, methodNameTransformer);
1✔
1044
                }
1045
            }
1046
        }
1047

1048
        /**
1049
         * A pool strategy determines the creation of a {@link TypePool} for a plugin engine application.
1050
         */
1051
        interface PoolStrategy {
1052

1053
            /**
1054
             * Creates a type pool.
1055
             *
1056
             * @param classFileLocator The class file locator to use.
1057
             * @return An approptiate type pool.
1058
             */
1059
            TypePool typePool(ClassFileLocator classFileLocator);
1060

1061
            /**
1062
             * A default implementation of a pool strategy where type descriptions are resolved lazily.
1063
             */
1064
            enum Default implements PoolStrategy {
1✔
1065

1066
                /**
1067
                 * Enables faster class file parsing that does not process debug information of a class file.
1068
                 */
1069
                FAST(TypePool.Default.ReaderMode.FAST),
1✔
1070

1071
                /**
1072
                 * Enables extended class file parsing that extracts parameter names from debug information, if available.
1073
                 */
1074
                EXTENDED(TypePool.Default.ReaderMode.EXTENDED);
1✔
1075

1076
                /**
1077
                 * This strategy's reader mode.
1078
                 */
1079
                private final TypePool.Default.ReaderMode readerMode;
1080

1081
                /**
1082
                 * Creates a default pool strategy.
1083
                 *
1084
                 * @param readerMode This strategy's reader mode.
1085
                 */
1086
                Default(TypePool.Default.ReaderMode readerMode) {
1✔
1087
                    this.readerMode = readerMode;
1✔
1088
                }
1✔
1089

1090
                /**
1091
                 * {@inheritDoc}
1092
                 */
1093
                public TypePool typePool(ClassFileLocator classFileLocator) {
1094
                    return new TypePool.Default.WithLazyResolution(new TypePool.CacheProvider.Simple(),
1✔
1095
                            classFileLocator,
1096
                            readerMode,
1097
                            TypePool.ClassLoading.ofPlatformLoader());
1✔
1098
                }
1099
            }
1100

1101
            /**
1102
             * A pool strategy that resolves type descriptions eagerly. This can avoid additional overhead if the
1103
             * majority of types is assumed to be resolved eventually.
1104
             */
1105
            enum Eager implements PoolStrategy {
1✔
1106

1107
                /**
1108
                 * Enables faster class file parsing that does not process debug information of a class file.
1109
                 */
1110
                FAST(TypePool.Default.ReaderMode.FAST),
1✔
1111

1112
                /**
1113
                 * Enables extended class file parsing that extracts parameter names from debug information, if available.
1114
                 */
1115
                EXTENDED(TypePool.Default.ReaderMode.EXTENDED);
1✔
1116

1117
                /**
1118
                 * This strategy's reader mode.
1119
                 */
1120
                private final TypePool.Default.ReaderMode readerMode;
1121

1122
                /**
1123
                 * Creates an eager pool strategy.
1124
                 *
1125
                 * @param readerMode This strategy's reader mode.
1126
                 */
1127
                Eager(TypePool.Default.ReaderMode readerMode) {
1✔
1128
                    this.readerMode = readerMode;
1✔
1129
                }
1✔
1130

1131
                /**
1132
                 * {@inheritDoc}
1133
                 */
1134
                public TypePool typePool(ClassFileLocator classFileLocator) {
1135
                    return new TypePool.Default(new TypePool.CacheProvider.Simple(),
1✔
1136
                            classFileLocator,
1137
                            readerMode,
1138
                            TypePool.ClassLoading.ofPlatformLoader());
1✔
1139
                }
1140
            }
1141
        }
1142

1143
        /**
1144
         * An error handler that is used during a plugin engine application.
1145
         */
1146
        interface ErrorHandler {
1147

1148
            /**
1149
             * Invoked if an error occured during a plugin's application on a given type.
1150
             *
1151
             * @param typeDescription The type being matched or transformed.
1152
             * @param plugin          The plugin being applied.
1153
             * @param throwable       The throwable that caused the error.
1154
             */
1155
            void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable);
1156

1157
            /**
1158
             * Invoked after the application of all plugins was attempted if at least one error occured during handling a given type.
1159
             *
1160
             * @param typeDescription The type being transformed.
1161
             * @param throwables      The throwables that caused errors during the application.
1162
             */
1163
            void onError(TypeDescription typeDescription, List<Throwable> throwables);
1164

1165
            /**
1166
             * Invoked at the end of the build if at least one type transformation failed.
1167
             *
1168
             * @param throwables A mapping of types that failed during transformation to the errors that were caught.
1169
             */
1170
            void onError(Map<TypeDescription, List<Throwable>> throwables);
1171

1172
            /**
1173
             * Invoked at the end of the build if a plugin could not be closed.
1174
             *
1175
             * @param plugin    The plugin that could not be closed.
1176
             * @param throwable The error that was caused when the plugin was attempted to be closed.
1177
             */
1178
            void onError(Plugin plugin, Throwable throwable);
1179

1180
            /**
1181
             * Invoked if a type transformation implied a live initializer.
1182
             *
1183
             * @param typeDescription The type that was transformed.
1184
             * @param definingType    The type that implies the initializer which might be the type itself or an auxiliary type.
1185
             */
1186
            void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType);
1187

1188
            /**
1189
             * Invoked if a type could not be resolved.
1190
             *
1191
             * @param typeName The name of the unresolved type.
1192
             */
1193
            void onUnresolved(String typeName);
1194

1195
            /**
1196
             * Invoked when a manifest was found or found missing.
1197
             *
1198
             * @param manifest The located manifest or {@code null} if no manifest was found.
1199
             */
1200
            void onManifest(@MaybeNull Manifest manifest);
1201

1202
            /**
1203
             * Invoked if a resource that is not a class file is discovered.
1204
             *
1205
             * @param name The name of the discovered resource.
1206
             */
1207
            void onResource(String name);
1208

1209
            /**
1210
             * An implementation of an error handler that fails the plugin engine application.
1211
             */
1212
            enum Failing implements ErrorHandler {
1✔
1213

1214
                /**
1215
                 * An error handler that fails the build immediatly on the first error.
1216
                 */
1217
                FAIL_FAST {
1✔
1218
                    /**
1219
                     * {@inheritDoc}
1220
                     */
1221
                    public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1222
                        throw new IllegalStateException("Failed to transform " + typeDescription + " using " + plugin, throwable);
1✔
1223
                    }
1224

1225
                    /**
1226
                     * {@inheritDoc}
1227
                     */
1228
                    public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1229
                        throw new IllegalStateException("Failed to transform " + typeDescription + ": " + throwables);
1✔
1230
                    }
1231

1232
                    /**
1233
                     * {@inheritDoc}
1234
                     */
1235
                    public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1236
                        throw new IllegalStateException("Failed to transform at least one type: " + throwables);
1✔
1237
                    }
1238
                },
1239

1240
                /**
1241
                 * An error handler that fails the build after applying all plugins if at least one plugin failed.
1242
                 */
1243
                FAIL_AFTER_TYPE {
1✔
1244
                    /**
1245
                     * {@inheritDoc}
1246
                     */
1247
                    public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1248
                        /* do nothing */
1249
                    }
1✔
1250

1251
                    /**
1252
                     * {@inheritDoc}
1253
                     */
1254
                    public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1255
                        throw new IllegalStateException("Failed to transform " + typeDescription + ": " + throwables);
1✔
1256
                    }
1257

1258
                    /**
1259
                     * {@inheritDoc}
1260
                     */
1261
                    public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1262
                        throw new IllegalStateException("Failed to transform at least one type: " + throwables);
1✔
1263
                    }
1264
                },
1265

1266
                /**
1267
                 * An error handler that fails the build after transforming all types if at least one plugin failed.
1268
                 */
1269
                FAIL_LAST {
1✔
1270
                    /**
1271
                     * {@inheritDoc}
1272
                     */
1273
                    public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1274
                        /* do nothing */
1275
                    }
1✔
1276

1277
                    /**
1278
                     * {@inheritDoc}
1279
                     */
1280
                    public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1281
                        /* do nothing */
1282
                    }
1✔
1283

1284
                    /**
1285
                     * {@inheritDoc}
1286
                     */
1287
                    public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1288
                        throw new IllegalStateException("Failed to transform at least one type: " + throwables);
1✔
1289
                    }
1290
                };
1291

1292
                /**
1293
                 * {@inheritDoc}
1294
                 */
1295
                public void onError(Plugin plugin, Throwable throwable) {
1296
                    throw new IllegalStateException("Failed to close plugin " + plugin, throwable);
1✔
1297
                }
1298

1299
                /**
1300
                 * {@inheritDoc}
1301
                 */
1302
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1303
                    /* do nothing */
1304
                }
1✔
1305

1306
                /**
1307
                 * {@inheritDoc}
1308
                 */
1309
                public void onUnresolved(String typeName) {
1310
                    /* do nothing */
1311
                }
1✔
1312

1313
                /**
1314
                 * {@inheritDoc}
1315
                 */
1316
                public void onManifest(Manifest manifest) {
1317
                    /* do nothing */
1318
                }
1✔
1319

1320
                /**
1321
                 * {@inheritDoc}
1322
                 */
1323
                public void onResource(String name) {
1324
                    /* do nothing */
1325
                }
1✔
1326
            }
1327

1328
            /**
1329
             * An error handler that enforces certain properties of the transformation.
1330
             */
1331
            enum Enforcing implements ErrorHandler {
1✔
1332

1333
                /**
1334
                 * Enforces that all types could be resolved.
1335
                 */
1336
                ALL_TYPES_RESOLVED {
1✔
1337
                    @Override
1338
                    public void onUnresolved(String typeName) {
1339
                        throw new IllegalStateException("Failed to resolve type description for " + typeName);
1✔
1340
                    }
1341
                },
1342

1343
                /**
1344
                 * Enforces that no type has a live initializer.
1345
                 */
1346
                NO_LIVE_INITIALIZERS {
1✔
1347
                    @Override
1348
                    public void onLiveInitializer(TypeDescription typeDescription, TypeDescription initializedType) {
1349
                        throw new IllegalStateException("Failed to instrument " + typeDescription + " due to live initializer for " + initializedType);
1✔
1350
                    }
1351
                },
1352

1353
                /**
1354
                 * Enforces that a source only produces class files.
1355
                 */
1356
                CLASS_FILES_ONLY {
1✔
1357
                    @Override
1358
                    public void onResource(String name) {
1359
                        throw new IllegalStateException("Discovered a resource when only class files were allowed: " + name);
1✔
1360
                    }
1361
                },
1362

1363
                /**
1364
                 * Enforces that a manifest is written to a target.
1365
                 */
1366
                MANIFEST_REQUIRED {
1✔
1367
                    @Override
1368
                    public void onManifest(@MaybeNull Manifest manifest) {
1369
                        if (manifest == null) {
1✔
1370
                            throw new IllegalStateException("Required a manifest but no manifest was found");
1✔
1371
                        }
1372
                    }
1✔
1373
                };
1374

1375
                /**
1376
                 * {@inheritDoc}
1377
                 */
1378
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1379
                    /* do nothing */
1380
                }
1✔
1381

1382
                /**
1383
                 * {@inheritDoc}
1384
                 */
1385
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1386
                    /* do nothing */
1387
                }
1✔
1388

1389
                /**
1390
                 * {@inheritDoc}
1391
                 */
1392
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1393
                    /* do nothing */
1394
                }
1✔
1395

1396
                /**
1397
                 * {@inheritDoc}
1398
                 */
1399
                public void onError(Plugin plugin, Throwable throwable) {
1400
                    /* do nothing */
1401
                }
1✔
1402

1403
                /**
1404
                 * {@inheritDoc}
1405
                 */
1406
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1407
                    /* do nothing */
1408
                }
1✔
1409

1410
                /**
1411
                 * {@inheritDoc}
1412
                 */
1413
                public void onUnresolved(String typeName) {
1414
                    /* do nothing */
1415
                }
1✔
1416

1417
                /**
1418
                 * {@inheritDoc}
1419
                 */
1420
                public void onManifest(@MaybeNull Manifest manifest) {
1421
                    /* do nothing */
1422
                }
1✔
1423

1424
                /**
1425
                 * {@inheritDoc}
1426
                 */
1427
                public void onResource(String name) {
1428
                    /* do nothing */
1429
                }
1✔
1430
            }
1431

1432
            /**
1433
             * A compound error handler.
1434
             */
1435
            class Compound implements ErrorHandler {
1436

1437
                /**
1438
                 * The error handlers that are represented by this instance.
1439
                 */
1440
                private final List<ErrorHandler> errorHandlers;
1441

1442
                /**
1443
                 * Creates a new compound error handler.
1444
                 *
1445
                 * @param errorHandler The error handlers that are represented by this instance.
1446
                 */
1447
                public Compound(ErrorHandler... errorHandler) {
1448
                    this(Arrays.asList(errorHandler));
1✔
1449
                }
1✔
1450

1451
                /**
1452
                 * Creates a new compound error handler.
1453
                 *
1454
                 * @param errorHandlers The error handlers that are represented by this instance.
1455
                 */
1456
                public Compound(List<? extends ErrorHandler> errorHandlers) {
1✔
1457
                    this.errorHandlers = new ArrayList<ErrorHandler>();
1✔
1458
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1459
                        if (errorHandler instanceof Compound) {
1✔
1460
                            this.errorHandlers.addAll(((Compound) errorHandler).errorHandlers);
×
1461
                        } else if (!(errorHandler instanceof Listener.NoOp)) {
1✔
1462
                            this.errorHandlers.add(errorHandler);
1✔
1463
                        }
1464
                    }
1✔
1465
                }
1✔
1466

1467
                /**
1468
                 * {@inheritDoc}
1469
                 */
1470
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1471
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1472
                        errorHandler.onError(typeDescription, plugin, throwable);
1✔
1473
                    }
1✔
1474
                }
1✔
1475

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

1485
                /**
1486
                 * {@inheritDoc}
1487
                 */
1488
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1489
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1490
                        errorHandler.onError(throwables);
1✔
1491
                    }
1✔
1492

1493
                }
1✔
1494

1495
                /**
1496
                 * {@inheritDoc}
1497
                 */
1498
                public void onError(Plugin plugin, Throwable throwable) {
1499
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1500
                        errorHandler.onError(plugin, throwable);
1✔
1501
                    }
1✔
1502
                }
1✔
1503

1504
                /**
1505
                 * {@inheritDoc}
1506
                 */
1507
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1508
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1509
                        errorHandler.onLiveInitializer(typeDescription, definingType);
1✔
1510
                    }
1✔
1511
                }
1✔
1512

1513
                /**
1514
                 * {@inheritDoc}
1515
                 */
1516
                public void onUnresolved(String typeName) {
1517
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1518
                        errorHandler.onUnresolved(typeName);
1✔
1519
                    }
1✔
1520
                }
1✔
1521

1522
                /**
1523
                 * {@inheritDoc}
1524
                 */
1525
                public void onManifest(@MaybeNull Manifest manifest) {
1526
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1527
                        errorHandler.onManifest(manifest);
1✔
1528
                    }
1✔
1529
                }
1✔
1530

1531
                /**
1532
                 * {@inheritDoc}
1533
                 */
1534
                public void onResource(String name) {
1535
                    for (ErrorHandler errorHandler : errorHandlers) {
1✔
1536
                        errorHandler.onResource(name);
1✔
1537
                    }
1✔
1538
                }
1✔
1539
            }
1540
        }
1541

1542
        /**
1543
         * A listener that is invoked upon any event during a plugin engine application.
1544
         */
1545
        interface Listener extends ErrorHandler {
1546

1547
            /**
1548
             * Invoked upon discovering a type but prior to its resolution.
1549
             *
1550
             * @param typeName The name of the discovered type.
1551
             */
1552
            void onDiscovery(String typeName);
1553

1554
            /**
1555
             * Invoked after a type was transformed using a specific plugin.
1556
             *
1557
             * @param typeDescription The type being transformed.
1558
             * @param plugin          The plugin that was applied.
1559
             */
1560
            void onTransformation(TypeDescription typeDescription, Plugin plugin);
1561

1562
            /**
1563
             * Invoked after a type was transformed using at least one plugin.
1564
             *
1565
             * @param typeDescription The type being transformed.
1566
             * @param plugins         A list of plugins that were applied.
1567
             */
1568
            void onTransformation(TypeDescription typeDescription, List<Plugin> plugins);
1569

1570
            /**
1571
             * Invoked if a type description is ignored by a given plugin. This callback is not invoked,
1572
             * if the ignore type matcher excluded a type from transformation.
1573
             *
1574
             * @param typeDescription The type being transformed.
1575
             * @param plugin          The plugin that ignored the given type.
1576
             */
1577
            void onIgnored(TypeDescription typeDescription, Plugin plugin);
1578

1579
            /**
1580
             * Invoked if one or more plugins did not transform a type. This callback is also invoked if an
1581
             * ignore matcher excluded a type from transformation.
1582
             *
1583
             * @param typeDescription The type being transformed.
1584
             * @param plugins         the plugins that ignored the type.
1585
             */
1586
            void onIgnored(TypeDescription typeDescription, List<Plugin> plugins);
1587

1588
            /**
1589
             * Invoked upon completing handling a type that was either transformed or ignored.
1590
             *
1591
             * @param typeDescription The type that was transformed.
1592
             */
1593
            void onComplete(TypeDescription typeDescription);
1594

1595
            /**
1596
             * A non-operational listener.
1597
             */
1598
            enum NoOp implements Listener {
1✔
1599

1600
                /**
1601
                 * The singleton instance.
1602
                 */
1603
                INSTANCE;
1✔
1604

1605
                /**
1606
                 * {@inheritDoc}
1607
                 */
1608
                public void onDiscovery(String typeName) {
1609
                    /* do nothing */
1610
                }
×
1611

1612
                /**
1613
                 * {@inheritDoc}
1614
                 */
1615
                public void onTransformation(TypeDescription typeDescription, Plugin plugin) {
1616
                    /* do nothing */
1617
                }
1✔
1618

1619
                /**
1620
                 * {@inheritDoc}
1621
                 */
1622
                public void onTransformation(TypeDescription typeDescription, List<Plugin> plugins) {
1623
                    /* do nothing */
1624
                }
1✔
1625

1626
                /**
1627
                 * {@inheritDoc}
1628
                 */
1629
                public void onIgnored(TypeDescription typeDescription, Plugin plugin) {
1630
                    /* do nothing */
1631
                }
1✔
1632

1633
                /**
1634
                 * {@inheritDoc}
1635
                 */
1636
                public void onIgnored(TypeDescription typeDescription, List<Plugin> plugins) {
1637
                    /* do nothing */
1638
                }
1✔
1639

1640
                /**
1641
                 * {@inheritDoc}
1642
                 */
1643
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1644
                    /* do nothing */
1645
                }
1✔
1646

1647
                /**
1648
                 * {@inheritDoc}
1649
                 */
1650
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1651
                    /* do nothing */
1652
                }
1✔
1653

1654
                /**
1655
                 * {@inheritDoc}
1656
                 */
1657
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1658
                    /* do nothing */
1659
                }
1✔
1660

1661
                /**
1662
                 * {@inheritDoc}
1663
                 */
1664
                public void onError(Plugin plugin, Throwable throwable) {
1665
                    /* do nothing */
1666
                }
1✔
1667

1668
                /**
1669
                 * {@inheritDoc}
1670
                 */
1671
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1672
                    /* do nothing */
1673
                }
1✔
1674

1675
                /**
1676
                 * {@inheritDoc}
1677
                 */
1678
                public void onComplete(TypeDescription typeDescription) {
1679
                    /* do nothing */
1680
                }
1✔
1681

1682
                /**
1683
                 * {@inheritDoc}
1684
                 */
1685
                public void onUnresolved(String typeName) {
1686
                    /* do nothing */
1687
                }
1✔
1688

1689
                /**
1690
                 * {@inheritDoc}
1691
                 */
1692
                public void onManifest(@MaybeNull Manifest manifest) {
1693
                    /* do nothing */
1694
                }
1✔
1695

1696
                /**
1697
                 * {@inheritDoc}
1698
                 */
1699
                public void onResource(String name) {
1700
                    /* do nothing */
1701
                }
1✔
1702
            }
1703

1704
            /**
1705
             * An adapter that implements all methods non-operational.
1706
             */
1707
            abstract class Adapter implements Listener {
1✔
1708

1709
                /**
1710
                 * {@inheritDoc}
1711
                 */
1712
                public void onDiscovery(String typeName) {
1713
                    /* do nothing */
1714
                }
1✔
1715

1716
                /**
1717
                 * {@inheritDoc}
1718
                 */
1719
                public void onTransformation(TypeDescription typeDescription, Plugin plugin) {
1720
                    /* do nothing */
1721
                }
1✔
1722

1723
                /**
1724
                 * {@inheritDoc}
1725
                 */
1726
                public void onTransformation(TypeDescription typeDescription, List<Plugin> plugins) {
1727
                    /* do nothing */
1728
                }
1✔
1729

1730
                /**
1731
                 * {@inheritDoc}
1732
                 */
1733
                public void onIgnored(TypeDescription typeDescription, Plugin plugin) {
1734
                    /* do nothing */
1735
                }
1✔
1736

1737
                /**
1738
                 * {@inheritDoc}
1739
                 */
1740
                public void onIgnored(TypeDescription typeDescription, List<Plugin> plugins) {
1741
                    /* do nothing */
1742
                }
1✔
1743

1744
                /**
1745
                 * {@inheritDoc}
1746
                 */
1747
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1748
                    /* do nothing */
1749
                }
1✔
1750

1751
                /**
1752
                 * {@inheritDoc}
1753
                 */
1754
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1755
                    /* do nothing */
1756
                }
1✔
1757

1758
                /**
1759
                 * {@inheritDoc}
1760
                 */
1761
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1762
                    /* do nothing */
1763
                }
1✔
1764

1765
                /**
1766
                 * {@inheritDoc}
1767
                 */
1768
                public void onError(Plugin plugin, Throwable throwable) {
1769
                    /* do nothing */
1770
                }
1✔
1771

1772
                /**
1773
                 * {@inheritDoc}
1774
                 */
1775
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1776
                    /* do nothing */
1777
                }
1✔
1778

1779
                /**
1780
                 * {@inheritDoc}
1781
                 */
1782
                public void onComplete(TypeDescription typeDescription) {
1783
                    /* do nothing */
1784
                }
1✔
1785

1786
                /**
1787
                 * {@inheritDoc}
1788
                 */
1789
                public void onUnresolved(String typeName) {
1790
                    /* do nothing */
1791
                }
1✔
1792

1793
                /**
1794
                 * {@inheritDoc}
1795
                 */
1796
                public void onManifest(@MaybeNull Manifest manifest) {
1797
                    /* do nothing */
1798
                }
1✔
1799

1800
                /**
1801
                 * {@inheritDoc}
1802
                 */
1803
                public void onResource(String name) {
1804
                    /* do nothing */
1805
                }
1✔
1806
            }
1807

1808
            /**
1809
             * A listener that forwards significant events of a plugin engine application to a {@link PrintStream}.
1810
             */
1811
            @HashCodeAndEqualsPlugin.Enhance
1812
            class StreamWriting extends Adapter {
1813

1814
                /**
1815
                 * The prefix that is appended to all written messages.
1816
                 */
1817
                protected static final String PREFIX = "[Byte Buddy]";
1818

1819
                /**
1820
                 * The print stream to delegate to.
1821
                 */
1822
                private final PrintStream printStream;
1823

1824
                /**
1825
                 * Creates a new stream writing listener.
1826
                 *
1827
                 * @param printStream The print stream to delegate to.
1828
                 */
1829
                public StreamWriting(PrintStream printStream) {
1✔
1830
                    this.printStream = printStream;
1✔
1831
                }
1✔
1832

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

1842
                /**
1843
                 * Creates a stream writing listener that prints all events on {@link System#err}.
1844
                 *
1845
                 * @return A listener that writes events to the system error stream.
1846
                 */
1847
                public static StreamWriting toSystemError() {
1848
                    return new StreamWriting(System.err);
1✔
1849
                }
1850

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

1860
                /**
1861
                 * Returns a new listener that only prints error events.
1862
                 *
1863
                 * @return A new listener that only prints error events.
1864
                 */
1865
                public Listener withErrorsOnly() {
1866
                    return new WithErrorsOnly(this);
1✔
1867
                }
1868

1869
                /**
1870
                 * {@inheritDoc}
1871
                 */
1872
                public void onDiscovery(String typeName) {
1873
                    printStream.printf(PREFIX + " DISCOVERY %s", typeName);
×
1874
                }
×
1875

1876
                /**
1877
                 * {@inheritDoc}
1878
                 */
1879
                public void onTransformation(TypeDescription typeDescription, Plugin plugin) {
1880
                    printStream.printf(PREFIX + " TRANSFORM %s for %s", typeDescription, plugin);
1✔
1881
                }
1✔
1882

1883
                /**
1884
                 * {@inheritDoc}
1885
                 */
1886
                public void onIgnored(TypeDescription typeDescription, Plugin plugin) {
1887
                    printStream.printf(PREFIX + " IGNORE %s for %s", typeDescription, plugin);
1✔
1888
                }
1✔
1889

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

1900
                /**
1901
                 * {@inheritDoc}
1902
                 */
1903
                public void onError(Plugin plugin, Throwable throwable) {
1904
                    synchronized (printStream) {
1✔
1905
                        printStream.printf(PREFIX + " ERROR %s", plugin);
1✔
1906
                        throwable.printStackTrace(printStream);
1✔
1907
                    }
1✔
1908
                }
1✔
1909

1910
                /**
1911
                 * {@inheritDoc}
1912
                 */
1913
                public void onUnresolved(String typeName) {
1914
                    printStream.printf(PREFIX + " UNRESOLVED %s", typeName);
1✔
1915
                }
1✔
1916

1917
                /**
1918
                 * {@inheritDoc}
1919
                 */
1920
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
1921
                    printStream.printf(PREFIX + " LIVE %s on %s", typeDescription, definingType);
1✔
1922
                }
1✔
1923

1924
                /**
1925
                 * {@inheritDoc}
1926
                 */
1927
                public void onComplete(TypeDescription typeDescription) {
1928
                    printStream.printf(PREFIX + " COMPLETE %s", typeDescription);
1✔
1929
                }
1✔
1930

1931
                /**
1932
                 * {@inheritDoc}
1933
                 */
1934
                public void onManifest(@MaybeNull Manifest manifest) {
1935
                    printStream.printf(PREFIX + " MANIFEST %b", manifest != null);
1✔
1936
                }
1✔
1937

1938
                /**
1939
                 * {@inheritDoc}
1940
                 */
1941
                public void onResource(String name) {
1942
                    printStream.printf(PREFIX + " RESOURCE %s", name);
1✔
1943
                }
1✔
1944
            }
1945

1946
            /**
1947
             * A decorator for another listener to only print transformation and error events.
1948
             */
1949
            @HashCodeAndEqualsPlugin.Enhance
1950
            class WithTransformationsOnly extends Adapter {
1951

1952
                /**
1953
                 * The delegate to forward events to.
1954
                 */
1955
                private final Listener delegate;
1956

1957
                /**
1958
                 * Creates a new listener decorator that filter any event that is not related to transformation or errors.
1959
                 *
1960
                 * @param delegate The delegate to forward events to.
1961
                 */
1962
                public WithTransformationsOnly(Listener delegate) {
1✔
1963
                    this.delegate = delegate;
1✔
1964
                }
1✔
1965

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

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

1976
                @Override
1977
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
1978
                    delegate.onError(typeDescription, plugin, throwable);
1✔
1979
                }
1✔
1980

1981
                @Override
1982
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
1983
                    delegate.onError(typeDescription, throwables);
1✔
1984
                }
1✔
1985

1986
                @Override
1987
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
1988
                    delegate.onError(throwables);
1✔
1989
                }
1✔
1990

1991
                @Override
1992
                public void onError(Plugin plugin, Throwable throwable) {
1993
                    delegate.onError(plugin, throwable);
1✔
1994
                }
1✔
1995
            }
1996

1997
            /**
1998
             * A decorator for another listener to only print error events.
1999
             */
2000
            @HashCodeAndEqualsPlugin.Enhance
2001
            class WithErrorsOnly extends Adapter {
2002

2003
                /**
2004
                 * The delegate to forward events to.
2005
                 */
2006
                private final Listener delegate;
2007

2008
                /**
2009
                 * Creates a new listener decorator that filter any event that is not related to errors.
2010
                 *
2011
                 * @param delegate The delegate to forward events to.
2012
                 */
2013
                public WithErrorsOnly(Listener delegate) {
1✔
2014
                    this.delegate = delegate;
1✔
2015
                }
1✔
2016

2017
                @Override
2018
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
2019
                    delegate.onError(typeDescription, plugin, throwable);
1✔
2020
                }
1✔
2021

2022
                @Override
2023
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
2024
                    delegate.onError(typeDescription, throwables);
1✔
2025
                }
1✔
2026

2027
                @Override
2028
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
2029
                    delegate.onError(throwables);
1✔
2030
                }
1✔
2031

2032
                @Override
2033
                public void onError(Plugin plugin, Throwable throwable) {
2034
                    delegate.onError(plugin, throwable);
1✔
2035
                }
1✔
2036
            }
2037

2038
            /**
2039
             * A listener decorator that forwards events to an error handler if they are applicable.
2040
             */
2041
            @HashCodeAndEqualsPlugin.Enhance
2042
            class ForErrorHandler extends Adapter {
2043

2044
                /**
2045
                 * The error handler to delegate to.
2046
                 */
2047
                private final ErrorHandler errorHandler;
2048

2049
                /**
2050
                 * Creates a new listener representation for an error handler.
2051
                 *
2052
                 * @param errorHandler The error handler to delegate to.
2053
                 */
2054
                public ForErrorHandler(ErrorHandler errorHandler) {
1✔
2055
                    this.errorHandler = errorHandler;
1✔
2056
                }
1✔
2057

2058
                @Override
2059
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
2060
                    errorHandler.onError(typeDescription, plugin, throwable);
1✔
2061
                }
1✔
2062

2063
                @Override
2064
                public void onError(TypeDescription typeDescription, List<Throwable> throwables) {
2065
                    errorHandler.onError(typeDescription, throwables);
1✔
2066
                }
1✔
2067

2068
                @Override
2069
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
2070
                    errorHandler.onError(throwables);
1✔
2071
                }
1✔
2072

2073
                @Override
2074
                public void onError(Plugin plugin, Throwable throwable) {
2075
                    errorHandler.onError(plugin, throwable);
1✔
2076
                }
1✔
2077

2078
                @Override
2079
                public void onLiveInitializer(TypeDescription typeDescription, TypeDescription definingType) {
2080
                    errorHandler.onLiveInitializer(typeDescription, definingType);
1✔
2081
                }
1✔
2082

2083
                @Override
2084
                public void onUnresolved(String typeName) {
2085
                    errorHandler.onUnresolved(typeName);
1✔
2086
                }
1✔
2087

2088
                @Override
2089
                public void onManifest(@MaybeNull Manifest manifest) {
2090
                    errorHandler.onManifest(manifest);
1✔
2091
                }
1✔
2092

2093
                @Override
2094
                public void onResource(String name) {
2095
                    errorHandler.onResource(name);
1✔
2096
                }
1✔
2097
            }
2098

2099
            /**
2100
             * A compound listener.
2101
             */
2102
            @HashCodeAndEqualsPlugin.Enhance
2103
            class Compound implements Listener {
2104

2105
                /**
2106
                 * A list of listeners that are represented by this compound instance.
2107
                 */
2108
                private final List<Listener> listeners;
2109

2110
                /**
2111
                 * Creates a new compound listener.
2112
                 *
2113
                 * @param listener A list of listeners that are represented by this compound instance.
2114
                 */
2115
                public Compound(Listener... listener) {
2116
                    this(Arrays.asList(listener));
1✔
2117
                }
1✔
2118

2119
                /**
2120
                 * Creates a new compound listener.
2121
                 *
2122
                 * @param listeners A list of listeners that are represented by this compound instance.
2123
                 */
2124
                public Compound(List<? extends Listener> listeners) {
1✔
2125
                    this.listeners = new ArrayList<Listener>();
1✔
2126
                    for (Listener listener : listeners) {
1✔
2127
                        if (listener instanceof Listener.Compound) {
1✔
2128
                            this.listeners.addAll(((Listener.Compound) listener).listeners);
1✔
2129
                        } else if (!(listener instanceof NoOp)) {
1✔
2130
                            this.listeners.add(listener);
1✔
2131
                        }
2132
                    }
1✔
2133
                }
1✔
2134

2135
                /**
2136
                 * {@inheritDoc}
2137
                 */
2138
                public void onDiscovery(String typeName) {
2139
                    for (Listener listener : listeners) {
1✔
2140
                        listener.onDiscovery(typeName);
1✔
2141
                    }
1✔
2142
                }
1✔
2143

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

2153
                /**
2154
                 * {@inheritDoc}
2155
                 */
2156
                public void onTransformation(TypeDescription typeDescription, List<Plugin> plugins) {
2157
                    for (Listener listener : listeners) {
1✔
2158
                        listener.onTransformation(typeDescription, plugins);
1✔
2159
                    }
1✔
2160
                }
1✔
2161

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

2171
                /**
2172
                 * {@inheritDoc}
2173
                 */
2174
                public void onIgnored(TypeDescription typeDescription, List<Plugin> plugins) {
2175
                    for (Listener listener : listeners) {
1✔
2176
                        listener.onIgnored(typeDescription, plugins);
1✔
2177
                    }
1✔
2178
                }
1✔
2179

2180
                /**
2181
                 * {@inheritDoc}
2182
                 */
2183
                public void onError(TypeDescription typeDescription, Plugin plugin, Throwable throwable) {
2184
                    for (Listener listener : listeners) {
1✔
2185
                        listener.onError(typeDescription, plugin, throwable);
1✔
2186
                    }
1✔
2187
                }
1✔
2188

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

2198
                /**
2199
                 * {@inheritDoc}
2200
                 */
2201
                public void onError(Map<TypeDescription, List<Throwable>> throwables) {
2202
                    for (Listener listener : listeners) {
1✔
2203
                        listener.onError(throwables);
1✔
2204
                    }
1✔
2205
                }
1✔
2206

2207
                /**
2208
                 * {@inheritDoc}
2209
                 */
2210
                public void onError(Plugin plugin, Throwable throwable) {
2211
                    for (Listener listener : listeners) {
1✔
2212
                        listener.onError(plugin, throwable);
1✔
2213
                    }
1✔
2214
                }
1✔
2215

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

2225
                /**
2226
                 * {@inheritDoc}
2227
                 */
2228
                public void onComplete(TypeDescription typeDescription) {
2229
                    for (Listener listener : listeners) {
1✔
2230
                        listener.onComplete(typeDescription);
1✔
2231
                    }
1✔
2232
                }
1✔
2233

2234
                /**
2235
                 * {@inheritDoc}
2236
                 */
2237
                public void onUnresolved(String typeName) {
2238
                    for (Listener listener : listeners) {
1✔
2239
                        listener.onUnresolved(typeName);
1✔
2240
                    }
1✔
2241
                }
1✔
2242

2243
                /**
2244
                 * {@inheritDoc}
2245
                 */
2246
                public void onManifest(@MaybeNull Manifest manifest) {
2247
                    for (Listener listener : listeners) {
1✔
2248
                        listener.onManifest(manifest);
1✔
2249
                    }
1✔
2250
                }
1✔
2251

2252
                /**
2253
                 * {@inheritDoc}
2254
                 */
2255
                public void onResource(String name) {
2256
                    for (Listener listener : listeners) {
1✔
2257
                        listener.onResource(name);
1✔
2258
                    }
1✔
2259
                }
1✔
2260
            }
2261
        }
2262

2263
        /**
2264
         * A source for a plugin engine provides binary elements to consider for transformation.
2265
         */
2266
        interface Source {
2267

2268
            /**
2269
             * Initiates reading from a source.
2270
             *
2271
             * @return The origin to read from.
2272
             * @throws IOException If an I/O error occurs.
2273
             */
2274
            Origin read() throws IOException;
2275

2276
            /**
2277
             * An origin for elements.
2278
             */
2279
            interface Origin extends Iterable<Element>, Closeable {
2280

2281
                /**
2282
                 * Indicates that no manifest exists.
2283
                 */
2284
                @AlwaysNull
2285
                Manifest NO_MANIFEST = null;
1✔
2286

2287
                /**
2288
                 * Returns the manifest file of the source location or {@code null} if no manifest exists.
2289
                 *
2290
                 * @return This source's manifest or {@code null}.
2291
                 * @throws IOException If an I/O error occurs.
2292
                 */
2293
                @MaybeNull
2294
                Manifest getManifest() throws IOException;
2295

2296
                /**
2297
                 * Creates a class file locator for the represented source. If the class file locator needs to be closed,
2298
                 * it is the responsibility of this origin to close the locator or its underlying resources.
2299
                 *
2300
                 * @param classFileVersion The class file version to consider for multi-release jars or {@code null}
2301
                 *                         if multi-release jars should not be considered.
2302
                 * @return A class file locator for locating class files of this instance.
2303
                 * @throws IOException If an I/O exception occurs.
2304
                 */
2305
                ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException;
2306

2307
                /**
2308
                 * An origin implementation for a jar file.
2309
                 */
2310
                class ForJarFile implements Origin {
2311

2312
                    /**
2313
                     * The represented file.
2314
                     */
2315
                    private final JarFile file;
2316

2317
                    /**
2318
                     * Creates a new origin for a jar file.
2319
                     *
2320
                     * @param file The represented file.
2321
                     */
2322
                    public ForJarFile(JarFile file) {
1✔
2323
                        this.file = file;
1✔
2324
                    }
1✔
2325

2326
                    /**
2327
                     * {@inheritDoc}
2328
                     */
2329
                    @MaybeNull
2330
                    public Manifest getManifest() throws IOException {
2331
                        return file.getManifest();
1✔
2332
                    }
2333

2334
                    /**
2335
                     * {@inheritDoc}
2336
                     */
2337
                    public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException {
2338
                        return classFileVersion == null
1✔
2339
                                ? new ClassFileLocator.ForJarFile(file)
2340
                                : ClassFileLocator.ForJarFile.of(file, classFileVersion);
×
2341
                    }
2342

2343
                    /**
2344
                     * {@inheritDoc}
2345
                     */
2346
                    public void close() throws IOException {
2347
                        file.close();
1✔
2348
                    }
1✔
2349

2350
                    /**
2351
                     * {@inheritDoc}
2352
                     */
2353
                    public Iterator<Element> iterator() {
2354
                        return new JarFileIterator(file.entries());
1✔
2355
                    }
2356

2357
                    /**
2358
                     * An iterator for jar file entries.
2359
                     */
2360
                    protected class JarFileIterator implements Iterator<Element> {
2361

2362
                        /**
2363
                         * The represented enumeration.
2364
                         */
2365
                        private final Enumeration<JarEntry> enumeration;
2366

2367
                        /**
2368
                         * Creates a new jar file iterator.
2369
                         *
2370
                         * @param enumeration The represented enumeration.
2371
                         */
2372
                        protected JarFileIterator(Enumeration<JarEntry> enumeration) {
1✔
2373
                            this.enumeration = enumeration;
1✔
2374
                        }
1✔
2375

2376
                        /**
2377
                         * {@inheritDoc}
2378
                         */
2379
                        public boolean hasNext() {
2380
                            return enumeration.hasMoreElements();
1✔
2381
                        }
2382

2383
                        /**
2384
                         * {@inheritDoc}
2385
                         */
2386
                        public Element next() {
2387
                            return new Element.ForJarEntry(file, enumeration.nextElement());
1✔
2388
                        }
2389

2390
                        /**
2391
                         * {@inheritDoc}
2392
                         */
2393
                        public void remove() {
2394
                            throw new UnsupportedOperationException("remove");
×
2395
                        }
2396
                    }
2397
                }
2398

2399
                /**
2400
                 * An origin that forwards all invocations to a delegate where an {@link ElementMatcher} is applied prior to iteration.
2401
                 */
2402
                @HashCodeAndEqualsPlugin.Enhance
2403
                class Filtering implements Origin {
2404

2405
                    /**
2406
                     * The origin to which invocations are delegated.
2407
                     */
2408
                    private final Origin delegate;
2409

2410
                    /**
2411
                     * The element matcher being used to filter elements.
2412
                     */
2413
                    private final ElementMatcher<Element> matcher;
2414

2415
                    /**
2416
                     * {@code true} if the manifest should be retained.
2417
                     */
2418
                    private final boolean manifest;
2419

2420
                    /**
2421
                     * Creates a new filtering origin that retains the delegated origin's manifest.
2422
                     *
2423
                     * @param delegate The origin to which invocations are delegated.
2424
                     * @param matcher  The element matcher being used to filter elements.
2425
                     */
2426
                    public Filtering(Origin delegate, ElementMatcher<Element> matcher) {
2427
                        this(delegate, matcher, true);
×
2428
                    }
×
2429

2430
                    /**
2431
                     * Creates a new filtering origin.
2432
                     *
2433
                     * @param delegate The origin to which invocations are delegated.
2434
                     * @param matcher  The element matcher being used to filter elements.
2435
                     * @param manifest {@code true} if the manifest should be retained.
2436
                     */
2437
                    public Filtering(Origin delegate, ElementMatcher<Element> matcher, boolean manifest) {
1✔
2438
                        this.delegate = delegate;
1✔
2439
                        this.matcher = matcher;
1✔
2440
                        this.manifest = manifest;
1✔
2441
                    }
1✔
2442

2443
                    /**
2444
                     * {@inheritDoc}
2445
                     */
2446
                    @MaybeNull
2447
                    public Manifest getManifest() throws IOException {
2448
                        return manifest ? delegate.getManifest() : NO_MANIFEST;
1✔
2449
                    }
2450

2451
                    /**
2452
                     * {@inheritDoc}
2453
                     */
2454
                    public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException {
2455
                        return delegate.toClassFileLocator(classFileVersion);
1✔
2456
                    }
2457

2458
                    /**
2459
                     * {@inheritDoc}
2460
                     */
2461
                    public Iterator<Element> iterator() {
2462
                        return new FilteringIterator(delegate.iterator(), matcher);
1✔
2463
                    }
2464

2465
                    /**
2466
                     * {@inheritDoc}
2467
                     */
2468
                    public void close() throws IOException {
2469
                        delegate.close();
1✔
2470
                    }
1✔
2471

2472
                    /**
2473
                     * An iterator that applies a filter to observed elements.
2474
                     */
2475
                    private static class FilteringIterator implements Iterator<Element> {
2476

2477
                        /**
2478
                         * The underlying iterator.
2479
                         */
2480
                        private final Iterator<Element> iterator;
2481

2482
                        /**
2483
                         * The element matcher being used to filter elements.
2484
                         */
2485
                        private final ElementMatcher<Element> matcher;
2486

2487
                        /**
2488
                         * The current element or {@code null} if no further elements are available.
2489
                         */
2490
                        @MaybeNull
2491
                        private Element current;
2492

2493
                        /**
2494
                         * Creates a new filtering iterator.
2495
                         *
2496
                         * @param iterator The underlying iterator.
2497
                         * @param matcher  The element matcher being used to filter elements.
2498
                         */
2499
                        private FilteringIterator(Iterator<Element> iterator, ElementMatcher<Element> matcher) {
1✔
2500
                            this.iterator = iterator;
1✔
2501
                            this.matcher = matcher;
1✔
2502
                            Element element;
2503
                            while (iterator.hasNext()) {
1✔
2504
                                element = iterator.next();
1✔
2505
                                if (matcher.matches(element)) {
1✔
2506
                                    current = element;
1✔
2507
                                    break;
1✔
2508
                                }
2509
                            }
2510
                        }
1✔
2511

2512
                        /**
2513
                         * {@inheritDoc}
2514
                         */
2515
                        public boolean hasNext() {
2516
                            return current != null;
1✔
2517
                        }
2518

2519
                        /**
2520
                         * {@inheritDoc}
2521
                         */
2522
                        public Element next() {
2523
                            if (current == null) {
1✔
2524
                                throw new NoSuchElementException();
×
2525
                            }
2526
                            try {
2527
                                return current;
1✔
2528
                            } finally {
2529
                                current = null;
1✔
2530
                                Element element;
2531
                                while (iterator.hasNext()) {
1✔
2532
                                    element = iterator.next();
1✔
2533
                                    if (matcher.matches(element)) {
1✔
2534
                                        current = element;
1✔
2535
                                        break;
1✔
2536
                                    }
2537
                                }
2538
                            }
2539
                        }
2540

2541
                        /**
2542
                         * {@inheritDoc}
2543
                         */
2544
                        public void remove() {
2545
                            iterator.remove();
×
2546
                        }
×
2547
                    }
2548
                }
2549
            }
2550

2551
            /**
2552
             * Represents a binary element found in a source location.
2553
             */
2554
            interface Element {
2555

2556
                /**
2557
                 * Returns the element's relative path and name. If the name ends with a {@code /}, it represents
2558
                 * a folder.
2559
                 *
2560
                 * @return The element's path and name.
2561
                 */
2562
                String getName();
2563

2564
                /**
2565
                 * Returns an input stream to read this element's binary information. Must not be invoked for
2566
                 * folders.
2567
                 *
2568
                 * @return An input stream that represents this element's binary information.
2569
                 * @throws IOException If an I/O error occurs.
2570
                 */
2571
                InputStream getInputStream() throws IOException;
2572

2573
                /**
2574
                 * Resolves this element to a more specialized form if possible. Doing so allows for performance
2575
                 * optimizations if more specialized formats are available.
2576
                 *
2577
                 * @param type The requested spezialized type.
2578
                 * @param <T>  The requested spezialized type.
2579
                 * @return The resolved element or {@code null} if a transformation is impossible.
2580
                 */
2581
                @MaybeNull
2582
                <T> T resolveAs(Class<T> type);
2583

2584
                /**
2585
                 * An element representation for a byte array.
2586
                 */
2587
                @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "The array is not modified by class contract.")
2588
                @HashCodeAndEqualsPlugin.Enhance
2589
                class ForByteArray implements Element {
2590

2591
                    /**
2592
                     * The element's name.
2593
                     */
2594
                    private final String name;
2595

2596
                    /**
2597
                     * The element's binary representation.
2598
                     */
2599
                    private final byte[] binaryRepresentation;
2600

2601
                    /**
2602
                     * Creates an element that is represented by a byte array.
2603
                     *
2604
                     * @param name                 The element's name.
2605
                     * @param binaryRepresentation The element's binary representation.
2606
                     */
2607
                    public ForByteArray(String name, byte[] binaryRepresentation) {
1✔
2608
                        this.name = name;
1✔
2609
                        this.binaryRepresentation = binaryRepresentation;
1✔
2610
                    }
1✔
2611

2612
                    /**
2613
                     * {@inheritDoc}
2614
                     */
2615
                    public String getName() {
2616
                        return name;
1✔
2617
                    }
2618

2619
                    /**
2620
                     * {@inheritDoc}
2621
                     */
2622
                    public InputStream getInputStream() {
2623
                        return new ByteArrayInputStream(binaryRepresentation);
1✔
2624
                    }
2625

2626
                    /**
2627
                     * {@inheritDoc}
2628
                     */
2629
                    @AlwaysNull
2630
                    public <T> T resolveAs(Class<T> type) {
2631
                        return null;
1✔
2632
                    }
2633
                }
2634

2635
                /**
2636
                 * An element representation for a file.
2637
                 */
2638
                @HashCodeAndEqualsPlugin.Enhance
2639
                class ForFile implements Element {
2640

2641
                    /**
2642
                     * The root folder of the represented source.
2643
                     */
2644
                    private final File root;
2645

2646
                    /**
2647
                     * The file location of the represented file that is located within the root directory.
2648
                     */
2649
                    private final File file;
2650

2651
                    /**
2652
                     * Creates an element representation for a file.
2653
                     *
2654
                     * @param root The root folder of the represented source.
2655
                     * @param file The file location of the represented file that is located within the root directory.
2656
                     */
2657
                    public ForFile(File root, File file) {
1✔
2658
                        this.root = root;
1✔
2659
                        this.file = file;
1✔
2660
                    }
1✔
2661

2662
                    /**
2663
                     * {@inheritDoc}
2664
                     */
2665
                    public String getName() {
2666
                        return root.getAbsoluteFile().toURI().relativize(file.getAbsoluteFile().toURI()).getPath();
1✔
2667
                    }
2668

2669
                    /**
2670
                     * {@inheritDoc}
2671
                     */
2672
                    public InputStream getInputStream() throws IOException {
2673
                        return new FileInputStream(file);
1✔
2674
                    }
2675

2676
                    /**
2677
                     * {@inheritDoc}
2678
                     */
2679
                    @MaybeNull
2680
                    @SuppressWarnings("unchecked")
2681
                    public <T> T resolveAs(Class<T> type) {
2682
                        return File.class.isAssignableFrom(type)
1✔
2683
                                ? (T) file
2684
                                : null;
2685
                    }
2686
                }
2687

2688
                /**
2689
                 * Represents a jar file entry as an element.
2690
                 */
2691
                @HashCodeAndEqualsPlugin.Enhance
2692
                class ForJarEntry implements Element {
2693

2694
                    /**
2695
                     * The source's underlying jar file.
2696
                     */
2697
                    private final JarFile file;
2698

2699
                    /**
2700
                     * The entry that is represented by this element.
2701
                     */
2702
                    private final JarEntry entry;
2703

2704
                    /**
2705
                     * Creates a new element representation for a jar file entry.
2706
                     *
2707
                     * @param file  The source's underlying jar file.
2708
                     * @param entry The entry that is represented by this element.
2709
                     */
2710
                    public ForJarEntry(JarFile file, JarEntry entry) {
1✔
2711
                        this.file = file;
1✔
2712
                        this.entry = entry;
1✔
2713
                    }
1✔
2714

2715
                    /**
2716
                     * {@inheritDoc}
2717
                     */
2718
                    public String getName() {
2719
                        return entry.getName();
1✔
2720
                    }
2721

2722
                    /**
2723
                     * {@inheritDoc}
2724
                     */
2725
                    public InputStream getInputStream() throws IOException {
2726
                        return file.getInputStream(entry);
1✔
2727
                    }
2728

2729
                    /**
2730
                     * {@inheritDoc}
2731
                     */
2732
                    @MaybeNull
2733
                    @SuppressWarnings("unchecked")
2734
                    public <T> T resolveAs(Class<T> type) {
2735
                        return JarEntry.class.isAssignableFrom(type)
1✔
2736
                                ? (T) entry
2737
                                : null;
2738
                    }
2739
                }
2740
            }
2741

2742
            /**
2743
             * An empty source that does not contain any elements or a manifest.
2744
             */
2745
            enum Empty implements Source, Origin {
1✔
2746

2747
                /**
2748
                 * The singleton instance.
2749
                 */
2750
                INSTANCE;
1✔
2751

2752
                /**
2753
                 * {@inheritDoc}
2754
                 */
2755
                public Origin read() {
2756
                    return this;
×
2757
                }
2758

2759
                /**
2760
                 * {@inheritDoc}
2761
                 */
2762
                public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) {
2763
                    return ClassFileLocator.NoOp.INSTANCE;
1✔
2764
                }
2765

2766
                /**
2767
                 * {@inheritDoc}
2768
                 */
2769
                @MaybeNull
2770
                public Manifest getManifest() {
2771
                    return NO_MANIFEST;
1✔
2772
                }
2773

2774
                /**
2775
                 * {@inheritDoc}
2776
                 */
2777
                public Iterator<Element> iterator() {
2778
                    return Collections.<Element>emptySet().iterator();
1✔
2779
                }
2780

2781
                /**
2782
                 * {@inheritDoc}
2783
                 */
2784
                public void close() {
2785
                    /* do nothing */
2786
                }
×
2787
            }
2788

2789
            /**
2790
             * A compound source that combines multiple sources into a single representation.
2791
             */
2792
            @HashCodeAndEqualsPlugin.Enhance
2793
            class Compound implements Source {
2794

2795
                /**
2796
                 * The represented sources.
2797
                 */
2798
                private final Collection<? extends Source> sources;
2799

2800
                /**
2801
                 * Creates a new compound source.
2802
                 *
2803
                 * @param sources The represented sources.
2804
                 */
2805
                public Compound(Collection<? extends Source> sources) {
1✔
2806
                    this.sources = sources;
1✔
2807
                }
1✔
2808

2809
                /**
2810
                 * {@inheritDoc}
2811
                 */
2812
                public Source.Origin read() throws IOException {
2813
                    if (sources.isEmpty()) {
1✔
2814
                        return Empty.INSTANCE;
1✔
2815
                    } else if (sources.size() == 1) {
1✔
2816
                        return sources.iterator().next().read();
×
2817
                    }
2818
                    List<Source.Origin> origins = new ArrayList<Source.Origin>(sources.size());
1✔
2819
                    try {
2820
                        for (Source source : sources) {
1✔
2821
                            origins.add(source.read());
1✔
2822
                        }
1✔
2823
                    } catch (IOException exception) {
×
2824
                        for (Source.Origin origin : origins) {
×
2825
                            origin.close();
×
2826
                        }
×
2827
                        throw exception;
×
2828
                    }
1✔
2829
                    return new Origin(origins);
1✔
2830
                }
2831

2832
                /**
2833
                 * Implements a compound {@link Source.Origin}.
2834
                 */
2835
                @HashCodeAndEqualsPlugin.Enhance
2836
                protected static class Origin implements Source.Origin {
2837

2838
                    /**
2839
                     * A list of represented origins.
2840
                     */
2841
                    private final List<Source.Origin> origins;
2842

2843
                    /**
2844
                     * Creates a new compound origin.
2845
                     *
2846
                     * @param origins A list of represented origins.
2847
                     */
2848
                    protected Origin(List<Source.Origin> origins) {
1✔
2849
                        this.origins = origins;
1✔
2850
                    }
1✔
2851

2852
                    /**
2853
                     * {@inheritDoc}
2854
                     */
2855
                    public Manifest getManifest() throws IOException {
2856
                        for (Source.Origin origin : origins) {
1✔
2857
                            Manifest manifest = origin.getManifest();
1✔
2858
                            if (manifest != null) {
1✔
2859
                                return manifest;
×
2860
                            }
2861
                        }
1✔
2862
                        return NO_MANIFEST;
1✔
2863
                    }
2864

2865
                    /**
2866
                     * {@inheritDoc}
2867
                     */
2868
                    public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException {
2869
                        List<ClassFileLocator> classFileLocators = new ArrayList<ClassFileLocator>(origins.size());
1✔
2870
                        for (Source.Origin origin : origins) {
1✔
2871
                            classFileLocators.add(origin.toClassFileLocator(classFileVersion));
1✔
2872
                        }
1✔
2873
                        return new ClassFileLocator.Compound(classFileLocators);
1✔
2874
                    }
2875

2876
                    /**
2877
                     * {@inheritDoc}
2878
                     */
2879
                    public Iterator<Element> iterator() {
2880
                        return new CompoundIterator(origins);
1✔
2881
                    }
2882

2883
                    /**
2884
                     * {@inheritDoc}
2885
                     */
2886
                    public void close() throws IOException {
2887
                        for (Source.Origin origin : origins) {
1✔
2888
                            origin.close();
1✔
2889
                        }
1✔
2890
                    }
1✔
2891

2892
                    /**
2893
                     * A compound iterator that combines several iterables.
2894
                     */
2895
                    protected static class CompoundIterator implements Iterator<Element> {
2896

2897
                        /**
2898
                         * The current iterator or {@code null} if no such iterator is defined.
2899
                         */
2900
                        @MaybeNull
2901
                        private Iterator<? extends Element> current;
2902

2903
                        /**
2904
                         * A backlog of iterables to still consider.
2905
                         */
2906
                        private final Queue<? extends Iterable<? extends Element>> backlog;
2907

2908
                        /**
2909
                         * Creates a compound iterator.
2910
                         *
2911
                         * @param iterables The iterables to consider.
2912
                         */
2913
                        protected CompoundIterator(List<? extends Iterable<? extends Element>> iterables) {
1✔
2914
                            backlog = QueueFactory.make(iterables);
1✔
2915
                            forward();
1✔
2916
                        }
1✔
2917

2918
                        /**
2919
                         * {@inheritDoc}
2920
                         */
2921
                        public boolean hasNext() {
2922
                            return current != null && current.hasNext();
1✔
2923
                        }
2924

2925
                        /**
2926
                         * {@inheritDoc}
2927
                         */
2928
                        public Element next() {
2929
                            try {
2930
                                if (current != null) {
1✔
2931
                                    return current.next();
1✔
2932
                                } else {
2933
                                    throw new NoSuchElementException();
×
2934
                                }
2935
                            } finally {
2936
                                forward();
1✔
2937
                            }
2938
                        }
2939

2940
                        /**
2941
                         * Forwards the iterator to the next relevant iterable.
2942
                         */
2943
                        private void forward() {
2944
                            while ((current == null || !current.hasNext()) && !backlog.isEmpty()) {
1✔
2945
                                current = backlog.remove().iterator();
1✔
2946
                            }
2947
                        }
1✔
2948

2949
                        /**
2950
                         * {@inheritDoc}
2951
                         */
2952
                        public void remove() {
2953
                            throw new UnsupportedOperationException("remove");
×
2954
                        }
2955
                    }
2956
                }
2957
            }
2958

2959
            /**
2960
             * A source that represents a collection of in-memory resources that are represented as byte arrays.
2961
             */
2962
            @HashCodeAndEqualsPlugin.Enhance
2963
            class InMemory implements Source, Origin {
2964

2965
                /**
2966
                 * A mapping of resource names to their binary representation.
2967
                 */
2968
                private final Map<String, byte[]> storage;
2969

2970
                /**
2971
                 * Creates a new in-memory source.
2972
                 *
2973
                 * @param storage A mapping of resource names to their binary representation.
2974
                 */
2975
                public InMemory(Map<String, byte[]> storage) {
1✔
2976
                    this.storage = storage;
1✔
2977
                }
1✔
2978

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

2989
                /**
2990
                 * Represents a collection of types as an in-memory source.
2991
                 *
2992
                 * @param types The types to represent.
2993
                 * @return A source representing the supplied types.
2994
                 */
2995
                public static Source ofTypes(Collection<? extends Class<?>> types) {
2996
                    return ofTypes(types, Collections.<ClassFileVersion, Collection<? extends Class<?>>>emptyMap());
1✔
2997
                }
2998

2999
                /**
3000
                 * Represents a collection of types as an in-memory source.
3001
                 *
3002
                 * @param types          The types to represent.
3003
                 * @param versionedTypes A versioned mapping of types to represent.
3004
                 * @return A source representing the supplied types.
3005
                 */
3006
                public static Source ofTypes(Collection<? extends Class<?>> types, Map<ClassFileVersion, Collection<? extends Class<?>>> versionedTypes) {
3007
                    Map<ClassFileVersion, Map<TypeDescription, byte[]>> versionedBinaryRepresentations = new HashMap<ClassFileVersion, Map<TypeDescription, byte[]>>();
1✔
3008
                    for (Map.Entry<ClassFileVersion, Collection<? extends Class<?>>> entry : versionedTypes.entrySet()) {
1✔
3009
                        Map<TypeDescription, byte[]> binaryRepresentations = new HashMap<TypeDescription, byte[]>();
1✔
3010
                        for (Class<?> type : entry.getValue()) {
1✔
3011
                            binaryRepresentations.put(TypeDescription.ForLoadedType.of(type), ClassFileLocator.ForClassLoader.read(type));
1✔
3012
                        }
1✔
3013
                        versionedBinaryRepresentations.put(entry.getKey(), binaryRepresentations);
1✔
3014
                    }
1✔
3015
                    Map<TypeDescription, byte[]> binaryRepresentations = new HashMap<TypeDescription, byte[]>();
1✔
3016
                    for (Class<?> type : types) {
1✔
3017
                        binaryRepresentations.put(TypeDescription.ForLoadedType.of(type), ClassFileLocator.ForClassLoader.read(type));
1✔
3018
                    }
1✔
3019
                    return ofTypes(binaryRepresentations, versionedBinaryRepresentations);
1✔
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
                 * @return A source representing the supplied types.
3027
                 */
3028
                public static Source ofTypes(Map<TypeDescription, byte[]> binaryRepresentations) {
3029
                    return ofTypes(binaryRepresentations, Collections.<ClassFileVersion, Map<TypeDescription, byte[]>>emptyMap());
×
3030
                }
3031

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

3059
                /**
3060
                 * {@inheritDoc}
3061
                 */
3062
                public Origin read() {
3063
                    return this;
1✔
3064
                }
3065

3066
                /**
3067
                 * {@inheritDoc}
3068
                 */
3069
                public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) {
3070
                    return ClassFileLocator.Simple.ofResources(storage);
1✔
3071
                }
3072

3073
                /**
3074
                 * {@inheritDoc}
3075
                 */
3076
                @MaybeNull
3077
                public Manifest getManifest() throws IOException {
3078
                    byte[] binaryRepresentation = storage.get(JarFile.MANIFEST_NAME);
1✔
3079
                    if (binaryRepresentation == null) {
1✔
3080
                        return NO_MANIFEST;
1✔
3081
                    } else {
3082
                        return new Manifest(new ByteArrayInputStream(binaryRepresentation));
1✔
3083
                    }
3084
                }
3085

3086
                /**
3087
                 * {@inheritDoc}
3088
                 */
3089
                public Iterator<Element> iterator() {
3090
                    return new MapEntryIterator(storage.entrySet().iterator());
1✔
3091
                }
3092

3093
                /**
3094
                 * {@inheritDoc}
3095
                 */
3096
                public void close() {
3097
                    /* do nothing */
3098
                }
1✔
3099

3100
                /**
3101
                 * An iterator that represents map entries as sources.
3102
                 */
3103
                protected static class MapEntryIterator implements Iterator<Element> {
3104

3105
                    /**
3106
                     * The represented iterator.
3107
                     */
3108
                    private final Iterator<Map.Entry<String, byte[]>> iterator;
3109

3110
                    /**
3111
                     * Creates a new map entry iterator.
3112
                     *
3113
                     * @param iterator The represented iterator.
3114
                     */
3115
                    protected MapEntryIterator(Iterator<Map.Entry<String, byte[]>> iterator) {
1✔
3116
                        this.iterator = iterator;
1✔
3117
                    }
1✔
3118

3119
                    /**
3120
                     * {@inheritDoc}
3121
                     */
3122
                    public boolean hasNext() {
3123
                        return iterator.hasNext();
1✔
3124
                    }
3125

3126
                    /**
3127
                     * {@inheritDoc}
3128
                     */
3129
                    public Element next() {
3130
                        Map.Entry<String, byte[]> entry = iterator.next();
1✔
3131
                        return new Element.ForByteArray(entry.getKey(), entry.getValue());
1✔
3132
                    }
3133

3134
                    /**
3135
                     * {@inheritDoc}
3136
                     */
3137
                    public void remove() {
3138
                        throw new UnsupportedOperationException("remove");
1✔
3139
                    }
3140
                }
3141
            }
3142

3143
            /**
3144
             * Represents the contents of a folder as class files.
3145
             */
3146
            @HashCodeAndEqualsPlugin.Enhance
3147
            class ForFolder implements Source, Origin {
3148

3149
                /**
3150
                 * The folder to represent.
3151
                 */
3152
                private final File folder;
3153

3154
                /**
3155
                 * Creates a new source representation for a given folder.
3156
                 *
3157
                 * @param folder The folder to represent.
3158
                 */
3159
                public ForFolder(File folder) {
1✔
3160
                    this.folder = folder;
1✔
3161
                }
1✔
3162

3163
                /**
3164
                 * Initializes a reading from this source.
3165
                 *
3166
                 * @return A source that represents the resource of this origin.
3167
                 */
3168
                public Origin read() {
3169
                    return this;
1✔
3170
                }
3171

3172
                /**
3173
                 * {@inheritDoc}
3174
                 */
3175
                public ClassFileLocator toClassFileLocator(@MaybeNull ClassFileVersion classFileVersion) throws IOException {
3176
                    return classFileVersion == null
1✔
3177
                            ? new ClassFileLocator.ForFolder(folder)
3178
                            : ClassFileLocator.ForFolder.of(folder, classFileVersion);
×
3179
                }
3180

3181
                /**
3182
                 * {@inheritDoc}
3183
                 */
3184
                @MaybeNull
3185
                public Manifest getManifest() throws IOException {
3186
                    File file = new File(folder, JarFile.MANIFEST_NAME);
1✔
3187
                    if (file.exists()) {
1✔
3188
                        InputStream inputStream = new FileInputStream(file);
1✔
3189
                        try {
3190
                            return new Manifest(inputStream);
1✔
3191
                        } finally {
3192
                            inputStream.close();
1✔
3193
                        }
3194
                    } else {
3195
                        return NO_MANIFEST;
1✔
3196
                    }
3197
                }
3198

3199
                /**
3200
                 * {@inheritDoc}
3201
                 */
3202
                public Iterator<Element> iterator() {
3203
                    return new FolderIterator(folder);
1✔
3204
                }
3205

3206
                /**
3207
                 * {@inheritDoc}
3208
                 */
3209
                public void close() {
3210
                    /* do nothing */
3211
                }
1✔
3212

3213
                /**
3214
                 * An iterator that exposes all files within a folder structure as elements.
3215
                 */
3216
                protected class FolderIterator implements Iterator<Element> {
3217

3218
                    /**
3219
                     * A list of files and folders to process with the next processed file at the end of the list.
3220
                     */
3221
                    private final Queue<File> files;
3222

3223
                    /**
3224
                     * Creates a new iterator representation for all files within a folder.
3225
                     *
3226
                     * @param folder The root folder.
3227
                     */
3228
                    protected FolderIterator(File folder) {
1✔
3229
                        files = QueueFactory.make();
1✔
3230
                        File[] file = folder.listFiles();
1✔
3231
                        if (file != null) {
1✔
3232
                            for (File candidate : file) {
1✔
3233
                                if (!candidate.equals(new File(folder, JarFile.MANIFEST_NAME))) {
1✔
3234
                                    files.add(candidate);
1✔
3235
                                }
3236
                            }
3237
                        }
3238
                    }
1✔
3239

3240
                    /**
3241
                     * {@inheritDoc}
3242
                     */
3243
                    public boolean hasNext() {
3244
                        return !files.isEmpty();
1✔
3245
                    }
3246

3247
                    /**
3248
                     * {@inheritDoc}
3249
                     */
3250
                    @SuppressFBWarnings(value = "IT_NO_SUCH_ELEMENT", justification = "Exception is thrown by invoking removeFirst on an empty list.")
3251
                    public Element next() {
3252
                        File next = files.remove();
1✔
3253
                        if (next.isDirectory()) {
1✔
3254
                            File[] file = next.listFiles();
1✔
3255
                            if (file != null) {
1✔
3256
                                for (File candidate : file) {
1✔
3257
                                    if (!candidate.equals(new File(folder, JarFile.MANIFEST_NAME))) {
1✔
3258
                                        files.add(candidate);
1✔
3259
                                    }
3260
                                }
3261
                            }
3262
                        }
3263
                        return new Element.ForFile(folder, next);
1✔
3264
                    }
3265

3266
                    /**
3267
                     * {@inheritDoc}
3268
                     */
3269
                    public void remove() {
3270
                        throw new UnsupportedOperationException("remove");
×
3271
                    }
3272
                }
3273
            }
3274

3275
            /**
3276
             * Represents a jar file as a source.
3277
             */
3278
            @HashCodeAndEqualsPlugin.Enhance
3279
            class ForJarFile implements Source {
3280

3281
                /**
3282
                 * The jar file being represented by this source.
3283
                 */
3284
                private final File file;
3285

3286
                /**
3287
                 * Creates a new source for a jar file.
3288
                 *
3289
                 * @param file The jar file being represented by this source.
3290
                 */
3291
                public ForJarFile(File file) {
1✔
3292
                    this.file = file;
1✔
3293
                }
1✔
3294

3295
                /**
3296
                 * {@inheritDoc}
3297
                 */
3298
                public Origin read() throws IOException {
3299
                    return new Origin.ForJarFile(new JarFile(file, false));
1✔
3300
                }
3301
            }
3302

3303
            /**
3304
             * A source that applies a filter upon iterating elements.
3305
             */
3306
            @HashCodeAndEqualsPlugin.Enhance
3307
            class Filtering implements Source {
3308

3309
                /**
3310
                 * The source to which invocations are delegated.
3311
                 */
3312
                private final Source delegate;
3313

3314
                /**
3315
                 * The element matcher being used to filter elements.
3316
                 */
3317
                private final ElementMatcher<Element> matcher;
3318

3319
                /**
3320
                 * {@code true} if the manifest should be retained.
3321
                 */
3322
                private final boolean manifest;
3323

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

3334
                /**
3335
                 * Creates a new filtering source.
3336
                 *
3337
                 * @param delegate The source to which invocations are delegated.
3338
                 * @param matcher  The element matcher being used to filter elements.
3339
                 * @param manifest {@code true} if the manifest should be retained.
3340
                 */
3341
                public Filtering(Source delegate, ElementMatcher<Element> matcher, boolean manifest) {
1✔
3342
                    this.delegate = delegate;
1✔
3343
                    this.matcher = matcher;
1✔
3344
                    this.manifest = manifest;
1✔
3345
                }
1✔
3346

3347
                /**
3348
                 * Wraps a source to exclude elements that are above the specified Java version.
3349
                 *
3350
                 * @param delegate         The delegate source.
3351
                 * @param classFileVersion The latest multi-release Java version to retain from the source.
3352
                 * @return A source that applies an appropriate filter.
3353
                 */
3354
                public static Source dropMultiReleaseClassFilesAbove(Source delegate, ClassFileVersion classFileVersion) {
3355
                    return new Filtering(delegate, new MultiReleaseVersionMatcher(classFileVersion));
1✔
3356
                }
3357

3358
                /**
3359
                 * Wraps a source to exclude elements that represent folders.
3360
                 *
3361
                 * @param delegate The delegate source.
3362
                 * @return A source that drops folders and delegates to the original source.
3363
                 */
3364
                public static Source dropFolders(Source delegate) {
3365
                    return new Filtering(delegate, NoFolderMatcher.INSTANCE);
1✔
3366
                }
3367

3368
                /**
3369
                 * {@inheritDoc}
3370
                 */
3371
                public Origin read() throws IOException {
3372
                    return new Origin.Filtering(delegate.read(), matcher, manifest);
1✔
3373
                }
3374

3375
                /**
3376
                 * An element matcher that filters multi-release files above a given version.
3377
                 */
3378
                @HashCodeAndEqualsPlugin.Enhance
3379
                protected static class MultiReleaseVersionMatcher implements ElementMatcher<Element> {
3380

3381
                    /**
3382
                     * The latest version to consider.
3383
                     */
3384
                    private final ClassFileVersion classFileVersion;
3385

3386
                    /**
3387
                     * Creates a multi-release version matcher.
3388
                     *
3389
                     * @param classFileVersion The latest class file version to consider.
3390
                     */
3391
                    protected MultiReleaseVersionMatcher(ClassFileVersion classFileVersion) {
1✔
3392
                        this.classFileVersion = classFileVersion;
1✔
3393
                    }
1✔
3394

3395
                    /**
3396
                     * {@inheritDoc}
3397
                     */
3398
                    public boolean matches(@MaybeNull Element target) {
3399
                        if (target == null) {
1✔
3400
                            return true;
×
3401
                        }
3402
                        String name = target.getName();
1✔
3403
                        if (name.startsWith("/")) {
1✔
3404
                            name = name.substring(1);
×
3405
                        }
3406
                        if (name.startsWith(ClassFileLocator.META_INF_VERSIONS)) {
1✔
3407
                            int version;
3408
                            try {
3409
                                version = Integer.parseInt(name.substring(
1✔
3410
                                        ClassFileLocator.META_INF_VERSIONS.length(),
1✔
3411
                                        name.indexOf('/', ClassFileLocator.META_INF_VERSIONS.length())));
1✔
3412
                            } catch (NumberFormatException ignored) {
×
3413
                                return true;
×
3414
                            }
1✔
3415
                            return version <= classFileVersion.getJavaVersion();
1✔
3416
                        }
3417
                        return true;
1✔
3418
                    }
3419
                }
3420

3421
                /**
3422
                 * A matcher that removes folders from the iteration.
3423
                 */
3424
                @HashCodeAndEqualsPlugin.Enhance
1✔
3425
                protected enum NoFolderMatcher implements ElementMatcher<Element> {
3426

3427
                    /**
3428
                     * The singleton instance.
3429
                     */
3430
                    INSTANCE;
1✔
3431

3432
                    /**
3433
                     * {@inheritDoc}
3434
                     */
3435
                    public boolean matches(@MaybeNull Element target) {
3436
                        return target == null || !target.getName().endsWith("/");
1✔
3437
                    }
3438
                }
3439
            }
3440
        }
3441

3442
        /**
3443
         * A target for a plugin engine represents a sink container for all elements that are supplied by a {@link Source}.
3444
         */
3445
        interface Target {
3446

3447
            /**
3448
             * Initializes this target prior to writing.
3449
             *
3450
             * @param manifest The manifest for the target or {@code null} if no manifest was found.
3451
             * @return The sink to write to.
3452
             * @throws IOException If an I/O error occurs.
3453
             */
3454
            Sink write(@MaybeNull Manifest manifest) throws IOException;
3455

3456
            /**
3457
             * A sink represents an active writing process.
3458
             */
3459
            interface Sink extends Closeable {
3460

3461
                /**
3462
                 * Stores the supplied binary representation of types in this sink.
3463
                 *
3464
                 * @param binaryRepresentations The binary representations to store.
3465
                 * @throws IOException If an I/O error occurs.
3466
                 */
3467
                void store(Map<TypeDescription, byte[]> binaryRepresentations) throws IOException;
3468

3469
                /**
3470
                 * Stores the supplied binary representation of types in this sink.
3471
                 *
3472
                 * @param classFileVersion      The version of the multi-release jar file, which should at least be {@code 8} as previous
3473
                 *                              versions are not recognized by regular class loaders.
3474
                 * @param binaryRepresentations The binary representations to store.
3475
                 * @throws IOException If an I/O error occurs.
3476
                 */
3477
                void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException;
3478

3479
                /**
3480
                 * Retains the supplied element in its original form.
3481
                 *
3482
                 * @param element The element to retain.
3483
                 * @throws IOException If an I/O error occurs.
3484
                 */
3485
                void retain(Source.Element element) throws IOException;
3486

3487
                /**
3488
                 * Implements a sink for a jar file.
3489
                 */
3490
                class ForJarOutputStream implements Sink {
3491

3492
                    /**
3493
                     * The output stream to write to.
3494
                     */
3495
                    private final JarOutputStream outputStream;
3496

3497
                    /**
3498
                     * Creates a new sink for a jar file.
3499
                     *
3500
                     * @param outputStream The output stream to write to.
3501
                     */
3502
                    public ForJarOutputStream(JarOutputStream outputStream) {
1✔
3503
                        this.outputStream = outputStream;
1✔
3504
                    }
1✔
3505

3506
                    /**
3507
                     * {@inheritDoc}
3508
                     */
3509
                    public void store(Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3510
                        for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
1✔
3511
                            outputStream.putNextEntry(new JarEntry(entry.getKey().getInternalName() + ClassFileLocator.CLASS_FILE_EXTENSION));
1✔
3512
                            outputStream.write(entry.getValue());
1✔
3513
                            outputStream.closeEntry();
1✔
3514
                        }
1✔
3515
                    }
1✔
3516

3517
                    /**
3518
                     * {@inheritDoc}
3519
                     */
3520
                    public void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3521
                        for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
×
3522
                            outputStream.putNextEntry(new JarEntry(ClassFileLocator.META_INF_VERSIONS
×
3523
                                    + classFileVersion.getJavaVersion()
×
3524
                                    + "/"
3525
                                    + entry.getKey().getInternalName()
×
3526
                                    + ClassFileLocator.CLASS_FILE_EXTENSION));
3527
                            outputStream.write(entry.getValue());
×
3528
                            outputStream.closeEntry();
×
3529
                        }
×
3530
                    }
×
3531

3532
                    /**
3533
                     * {@inheritDoc}
3534
                     */
3535
                    public void retain(Source.Element element) throws IOException {
3536
                        JarEntry entry = element.resolveAs(JarEntry.class);
1✔
3537
                        String name = element.getName();
1✔
3538
                        outputStream.putNextEntry(entry == null
1✔
3539
                                ? new JarEntry(name)
3540
                                : entry);
3541
                        if (entry != null || !name.endsWith("/")) {
1✔
3542
                            InputStream inputStream = element.getInputStream();
1✔
3543
                            try {
3544
                                byte[] buffer = new byte[1024];
1✔
3545
                                int length;
3546
                                while ((length = inputStream.read(buffer)) != -1) {
1✔
3547
                                    outputStream.write(buffer, 0, length);
1✔
3548
                                }
3549
                            } finally {
3550
                                inputStream.close();
1✔
3551
                            }
3552
                        }
3553
                        outputStream.closeEntry();
1✔
3554
                    }
1✔
3555

3556
                    /**
3557
                     * {@inheritDoc}
3558
                     */
3559
                    public void close() throws IOException {
3560
                        outputStream.close();
1✔
3561
                    }
1✔
3562
                }
3563
            }
3564

3565
            /**
3566
             * A sink that discards any entry.
3567
             */
3568
            enum Discarding implements Target, Sink {
1✔
3569

3570
                /**
3571
                 * The singleton instance.
3572
                 */
3573
                INSTANCE;
1✔
3574

3575
                /**
3576
                 * {@inheritDoc}
3577
                 */
3578
                public Sink write(@MaybeNull Manifest manifest) {
3579
                    return this;
1✔
3580
                }
3581

3582
                /**
3583
                 * {@inheritDoc}
3584
                 */
3585
                public void store(Map<TypeDescription, byte[]> binaryRepresentations) {
3586
                    /* do nothing */
3587
                }
1✔
3588

3589
                /**
3590
                 * {@inheritDoc}
3591
                 */
3592
                public void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3593
                    /* do nothing */
3594
                }
×
3595

3596
                /**
3597
                 * {@inheritDoc}
3598
                 */
3599
                public void retain(Source.Element element) {
3600
                    /* do nothing */
3601
                }
1✔
3602

3603
                /**
3604
                 * {@inheritDoc}
3605
                 */
3606
                public void close() {
3607
                    /* do nothing */
3608
                }
×
3609
            }
3610

3611
            /**
3612
             * A sink that stores all elements in a memory map.
3613
             */
3614
            @HashCodeAndEqualsPlugin.Enhance
3615
            class InMemory implements Target, Sink {
3616

3617
                /**
3618
                 * The map for storing all elements being received.
3619
                 */
3620
                @HashCodeAndEqualsPlugin.Identity
3621
                private final Map<String, byte[]> storage;
3622

3623
                /**
3624
                 * Creates a new in-memory storage.
3625
                 */
3626
                public InMemory() {
3627
                    this(new HashMap<String, byte[]>());
1✔
3628
                }
1✔
3629

3630
                /**
3631
                 * Creates a new in-memory storage.
3632
                 *
3633
                 * @param storage The map for storing all elements being received.
3634
                 */
3635
                public InMemory(Map<String, byte[]> storage) {
1✔
3636
                    this.storage = storage;
1✔
3637
                }
1✔
3638

3639
                /**
3640
                 * {@inheritDoc}
3641
                 */
3642
                public Sink write(@MaybeNull Manifest manifest) throws IOException {
3643
                    if (manifest != null) {
1✔
3644
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1✔
3645
                        try {
3646
                            manifest.write(outputStream);
1✔
3647
                        } finally {
3648
                            outputStream.close();
1✔
3649
                        }
3650
                        storage.put(JarFile.MANIFEST_NAME, outputStream.toByteArray());
1✔
3651
                    }
3652
                    return this;
1✔
3653
                }
3654

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

3664
                /**
3665
                 * {@inheritDoc}
3666
                 */
3667
                public void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3668
                    for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
1✔
3669
                        storage.put(ClassFileLocator.META_INF_VERSIONS
1✔
3670
                                + classFileVersion.getJavaVersion()
1✔
3671
                                + "/"
3672
                                + entry.getKey().getInternalName()
1✔
3673
                                + ClassFileLocator.CLASS_FILE_EXTENSION, entry.getValue());
1✔
3674
                    }
1✔
3675
                }
1✔
3676

3677
                /**
3678
                 * {@inheritDoc}
3679
                 */
3680
                public void retain(Source.Element element) throws IOException {
3681
                    String name = element.getName();
1✔
3682
                    if (!name.endsWith("/")) {
1✔
3683
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1✔
3684
                        try {
3685
                            InputStream inputStream = element.getInputStream();
1✔
3686
                            try {
3687
                                byte[] buffer = new byte[1024];
1✔
3688
                                int length;
3689
                                while ((length = inputStream.read(buffer)) != -1) {
1✔
3690
                                    outputStream.write(buffer, 0, length);
1✔
3691
                                }
3692
                            } finally {
3693
                                inputStream.close();
1✔
3694
                            }
3695
                        } finally {
3696
                            outputStream.close();
1✔
3697
                        }
3698
                        storage.put(element.getName(), outputStream.toByteArray());
1✔
3699
                    }
3700
                }
1✔
3701

3702
                /**
3703
                 * {@inheritDoc}
3704
                 */
3705
                public void close() {
3706
                    /* do nothing */
3707
                }
1✔
3708

3709
                /**
3710
                 * Returns the in-memory storage.
3711
                 *
3712
                 * @return The in-memory storage.
3713
                 */
3714
                public Map<String, byte[]> getStorage() {
3715
                    return storage;
1✔
3716
                }
3717

3718
                /**
3719
                 * Returns the in-memory storage as a type-map where all non-class files are discarded.
3720
                 *
3721
                 * @return The in-memory storage as a type map.
3722
                 */
3723
                public Map<String, byte[]> toTypeMap() {
3724
                    Map<String, byte[]> binaryRepresentations = new HashMap<String, byte[]>();
1✔
3725
                    for (Map.Entry<String, byte[]> entry : storage.entrySet()) {
1✔
3726
                        if (entry.getKey().endsWith(ClassFileLocator.CLASS_FILE_EXTENSION) && !entry.getKey().startsWith(ClassFileLocator.META_INF_VERSIONS)) {
1✔
3727
                            binaryRepresentations.put(entry.getKey()
1✔
3728
                                    .substring(0, entry.getKey().length() - ClassFileLocator.CLASS_FILE_EXTENSION.length())
1✔
3729
                                    .replace('/', '.'), entry.getValue());
1✔
3730
                        }
3731
                    }
1✔
3732
                    return binaryRepresentations;
1✔
3733
                }
3734

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

3776
            /**
3777
             * Represents a folder as the target for a plugin engine's application.
3778
             */
3779
            @HashCodeAndEqualsPlugin.Enhance
3780
            class ForFolder implements Target, Sink {
3781

3782
                /**
3783
                 * The folder that is represented by this instance.
3784
                 */
3785
                private final File folder;
3786

3787
                /**
3788
                 * {@code true} if retained files should be linked and not copied.
3789
                 */
3790
                private final boolean link;
3791

3792
                /**
3793
                 * Creates a new target for a folder.
3794
                 *
3795
                 * @param folder The folder that is represented by this instance.
3796
                 */
3797
                public ForFolder(File folder) {
3798
                    this(folder, false);
1✔
3799
                }
1✔
3800

3801
                /**
3802
                 * Creates a new target for a folder.
3803
                 *
3804
                 * @param folder The folder that is represented by this instance.
3805
                 * @param link   {@code true} if retained files should be linked and not copied.
3806
                 */
3807
                public ForFolder(File folder, boolean link) {
1✔
3808
                    this.folder = folder;
1✔
3809
                    this.link = link;
1✔
3810
                }
1✔
3811

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

3834
                /**
3835
                 * {@inheritDoc}
3836
                 */
3837
                public Sink write(@MaybeNull Manifest manifest) throws IOException {
3838
                    if (manifest != null) {
1✔
3839
                        File target = new File(folder, JarFile.MANIFEST_NAME);
1✔
3840
                        if (!target.getParentFile().isDirectory() && !target.getParentFile().mkdirs()) {
1✔
3841
                            throw new IOException("Could not create directory: " + target.getParent());
×
3842
                        }
3843
                        OutputStream outputStream = new FileOutputStream(target);
1✔
3844
                        try {
3845
                            manifest.write(outputStream);
1✔
3846
                        } finally {
3847
                            outputStream.close();
1✔
3848
                        }
3849
                    }
3850
                    return this;
1✔
3851
                }
3852

3853
                /**
3854
                 * {@inheritDoc}
3855
                 */
3856
                public void store(Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3857
                    doStore(folder, binaryRepresentations);
1✔
3858
                }
1✔
3859

3860
                /**
3861
                 * {@inheritDoc}
3862
                 */
3863
                public void store(ClassFileVersion classFileVersion, Map<TypeDescription, byte[]> binaryRepresentations) throws IOException {
3864
                    doStore(new File(folder, ClassFileLocator.META_INF_VERSIONS + classFileVersion.getJavaVersion()), binaryRepresentations);
×
3865
                }
×
3866

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

3907
                /**
3908
                 * {@inheritDoc}
3909
                 */
3910
                public void close() {
3911
                    /* do nothing */
3912
                }
1✔
3913
            }
3914

3915
            /**
3916
             * Represents a jar file as a target.
3917
             */
3918
            @HashCodeAndEqualsPlugin.Enhance
3919
            class ForJarFile implements Target {
3920

3921
                /**
3922
                 * The jar file that is represented by this target.
3923
                 */
3924
                private final File file;
3925

3926
                /**
3927
                 * Creates a new target for a jar file.
3928
                 *
3929
                 * @param file The jar file that is represented by this target.
3930
                 */
3931
                public ForJarFile(File file) {
1✔
3932
                    this.file = file;
1✔
3933
                }
1✔
3934

3935
                /**
3936
                 * {@inheritDoc}
3937
                 */
3938
                public Sink write(@MaybeNull Manifest manifest) throws IOException {
3939
                    OutputStream outputStream = new FileOutputStream(file);
1✔
3940
                    try {
3941
                        return manifest == null
1✔
3942
                                ? new Sink.ForJarOutputStream(new JarOutputStream(outputStream))
3943
                                : new Sink.ForJarOutputStream(new JarOutputStream(outputStream, manifest));
3944
                    } catch (RuntimeException exception) {
×
3945
                        outputStream.close();
×
3946
                        throw exception;
×
3947
                    } catch (IOException exception) {
×
3948
                        outputStream.close();
×
3949
                        throw exception;
×
3950
                    } catch (Error error) {
×
3951
                        outputStream.close();
×
3952
                        throw error;
×
3953
                    }
3954
                }
3955
            }
3956
        }
3957

3958
        /**
3959
         * A dispatcher to execute a plugin engine transformation. A dispatcher will receive all work assignments prior to the invocation
3960
         * of complete. After registering and eventually completing the supplied work, the close method will always be called. Any dispatcher
3961
         * will only be used once and from a single thread.
3962
         */
3963
        interface Dispatcher extends Closeable {
3964

3965
            /**
3966
             * Accepts a new work assignment.
3967
             *
3968
             * @param work  The work to handle prefixed by a preprocessing step.
3969
             * @param eager {@code true} if the processing does not need to be deferred until all preprocessing is complete.
3970
             * @throws IOException If an I/O exception occurs.
3971
             */
3972
            void accept(Callable<? extends Callable<? extends Materializable>> work, boolean eager) throws IOException;
3973

3974
            /**
3975
             * Completes the work being handled.
3976
             *
3977
             * @throws IOException If an I/O exception occurs.
3978
             */
3979
            void complete() throws IOException;
3980

3981
            /**
3982
             * The result of a work assignment that needs to be invoked from the main thread that triggers a dispatchers life-cycle methods.
3983
             */
3984
            interface Materializable {
3985

3986
                /**
3987
                 * Materializes this work result and adds any results to the corresponding collection.
3988
                 *
3989
                 * @param sink        The sink to write any work to.
3990
                 * @param transformed A list of all types that are transformed.
3991
                 * @param failed      A mapping of all types that failed during transformation to the exceptions that explain the failure.
3992
                 * @param unresolved  A list of type names that could not be resolved.
3993
                 * @throws IOException If an I/O exception occurs.
3994
                 */
3995
                void materialize(Target.Sink sink,
3996
                                 List<TypeDescription> transformed,
3997
                                 Map<TypeDescription,
3998
                                         List<Throwable>> failed,
3999
                                 List<String> unresolved) throws IOException;
4000

4001
                /**
4002
                 * A materializable for a successfully transformed type.
4003
                 */
4004
                class ForTransformedElement implements Materializable {
4005

4006
                    /**
4007
                     * The multi-release class file version number or {@code null} if a regular class.
4008
                     */
4009
                    @MaybeNull
4010
                    private final ClassFileVersion classFileVersion;
4011

4012
                    /**
4013
                     * The type that has been transformed.
4014
                     */
4015
                    private final DynamicType dynamicType;
4016

4017
                    /**
4018
                     * Creates a new materializable for a successfully transformed type.
4019
                     *
4020
                     * @param classFileVersion The multi-release class file version number or {@code null} if a regular class.
4021
                     * @param dynamicType      The type that has been transformed.
4022
                     */
4023
                    protected ForTransformedElement(@MaybeNull ClassFileVersion classFileVersion, DynamicType dynamicType) {
1✔
4024
                        this.classFileVersion = classFileVersion;
1✔
4025
                        this.dynamicType = dynamicType;
1✔
4026
                    }
1✔
4027

4028
                    /**
4029
                     * {@inheritDoc}
4030
                     */
4031
                    public void materialize(Target.Sink sink,
4032
                                            List<TypeDescription> transformed,
4033
                                            Map<TypeDescription,
4034
                                                    List<Throwable>> failed,
4035
                                            List<String> unresolved) throws IOException {
4036
                        if (classFileVersion == null) {
1✔
4037
                            sink.store(dynamicType.getAllTypes());
1✔
4038
                        } else {
4039
                            sink.store(classFileVersion, dynamicType.getAllTypes());
1✔
4040
                        }
4041
                        transformed.add(dynamicType.getTypeDescription());
1✔
4042
                    }
1✔
4043
                }
4044

4045
                /**
4046
                 * A materializable for an element that is retained in its original state.
4047
                 */
4048
                class ForRetainedElement implements Materializable {
4049

4050
                    /**
4051
                     * The retained element.
4052
                     */
4053
                    private final Source.Element element;
4054

4055
                    /**
4056
                     * Creates a new materializable for a retained element.
4057
                     *
4058
                     * @param element The retained element.
4059
                     */
4060
                    protected ForRetainedElement(Source.Element element) {
1✔
4061
                        this.element = element;
1✔
4062
                    }
1✔
4063

4064
                    /**
4065
                     * {@inheritDoc}
4066
                     */
4067
                    public void materialize(Target.Sink sink,
4068
                                            List<TypeDescription> transformed,
4069
                                            Map<TypeDescription,
4070
                                                    List<Throwable>> failed,
4071
                                            List<String> unresolved) throws IOException {
4072
                        sink.retain(element);
1✔
4073
                    }
1✔
4074
                }
4075

4076
                /**
4077
                 * A materializable for an element that failed to be transformed.
4078
                 */
4079
                class ForFailedElement implements Materializable {
4080

4081
                    /**
4082
                     * The element for which the transformation failed.
4083
                     */
4084
                    private final Source.Element element;
4085

4086
                    /**
4087
                     * The type description for the represented type.
4088
                     */
4089
                    private final TypeDescription typeDescription;
4090

4091
                    /**
4092
                     * A non-empty list of errors that occurred when attempting the transformation.
4093
                     */
4094
                    private final List<Throwable> errored;
4095

4096
                    /**
4097
                     * Creates a new materializable for an element that failed to be transformed.
4098
                     *
4099
                     * @param element         The element for which the transformation failed.
4100
                     * @param typeDescription The type description for the represented type.
4101
                     * @param errored         A non-empty list of errors that occurred when attempting the transformation.
4102
                     */
4103
                    protected ForFailedElement(Source.Element element, TypeDescription typeDescription, List<Throwable> errored) {
1✔
4104
                        this.element = element;
1✔
4105
                        this.typeDescription = typeDescription;
1✔
4106
                        this.errored = errored;
1✔
4107
                    }
1✔
4108

4109
                    /**
4110
                     * {@inheritDoc}
4111
                     */
4112
                    public void materialize(Target.Sink sink,
4113
                                            List<TypeDescription> transformed,
4114
                                            Map<TypeDescription,
4115
                                                    List<Throwable>> failed,
4116
                                            List<String> unresolved) throws IOException {
4117
                        sink.retain(element);
1✔
4118
                        failed.put(typeDescription, errored);
1✔
4119
                    }
1✔
4120
                }
4121

4122
                /**
4123
                 * A materializable for an element that could not be resolved.
4124
                 */
4125
                class ForUnresolvedElement implements Materializable {
4126

4127
                    /**
4128
                     * The element that could not be resolved.
4129
                     */
4130
                    private final Source.Element element;
4131

4132
                    /**
4133
                     * The name of the type that was deducted for this element.
4134
                     */
4135
                    private final String typeName;
4136

4137
                    /**
4138
                     * Creates a new materializable for an element that could not be resolved.
4139
                     *
4140
                     * @param element  The element that could not be resolved.
4141
                     * @param typeName The name of the type that was deducted for this element.
4142
                     */
4143
                    protected ForUnresolvedElement(Source.Element element, String typeName) {
1✔
4144
                        this.element = element;
1✔
4145
                        this.typeName = typeName;
1✔
4146
                    }
1✔
4147

4148
                    /**
4149
                     * {@inheritDoc}
4150
                     */
4151
                    public void materialize(Target.Sink sink,
4152
                                            List<TypeDescription> transformed,
4153
                                            Map<TypeDescription,
4154
                                                    List<Throwable>> failed,
4155
                                            List<String> unresolved) throws IOException {
4156
                        sink.retain(element);
1✔
4157
                        unresolved.add(typeName);
1✔
4158
                    }
1✔
4159
                }
4160
            }
4161

4162
            /**
4163
             * A factory that is used for creating a dispatcher that is used for a specific plugin engine application.
4164
             */
4165
            interface Factory {
4166

4167
                /**
4168
                 * Creates a new dispatcher.
4169
                 *
4170
                 * @param sink        The sink to write any work to.
4171
                 * @param transformed A list of all types that are transformed.
4172
                 * @param failed      A mapping of all types that failed during transformation to the exceptions that explain the failure.
4173
                 * @param unresolved  A list of type names that could not be resolved.
4174
                 * @return The dispatcher to use.
4175
                 */
4176
                Dispatcher make(Target.Sink sink,
4177
                                List<TypeDescription> transformed,
4178
                                Map<TypeDescription,
4179
                                        List<Throwable>> failed,
4180
                                List<String> unresolved);
4181
            }
4182

4183
            /**
4184
             * A dispatcher that applies transformation upon discovery.
4185
             */
4186
            class ForSerialTransformation implements Dispatcher {
4187

4188
                /**
4189
                 * The sink to write any work to.
4190
                 */
4191
                private final Target.Sink sink;
4192

4193
                /**
4194
                 * A list of all types that are transformed.
4195
                 */
4196
                private final List<TypeDescription> transformed;
4197

4198
                /**
4199
                 * A mapping of all types that failed during transformation to the exceptions that explain the failure.
4200
                 */
4201
                private final Map<TypeDescription, List<Throwable>> failed;
4202

4203
                /**
4204
                 * A list of type names that could not be resolved.
4205
                 */
4206
                private final List<String> unresolved;
4207

4208
                /**
4209
                 * A list of deferred processings.
4210
                 */
4211
                private final List<Callable<? extends Materializable>> preprocessings;
4212

4213
                /**
4214
                 * Creates a dispatcher for a serial transformation.
4215
                 *
4216
                 * @param sink        The sink to write any work to.
4217
                 * @param transformed A list of all types that are transformed.
4218
                 * @param failed      A mapping of all types that failed during transformation to the exceptions that explain the failure.
4219
                 * @param unresolved  A list of type names that could not be resolved.
4220
                 */
4221
                protected ForSerialTransformation(Target.Sink sink,
4222
                                                  List<TypeDescription> transformed,
4223
                                                  Map<TypeDescription, List<Throwable>> failed,
4224
                                                  List<String> unresolved) {
1✔
4225
                    this.sink = sink;
1✔
4226
                    this.transformed = transformed;
1✔
4227
                    this.failed = failed;
1✔
4228
                    this.unresolved = unresolved;
1✔
4229
                    preprocessings = new ArrayList<Callable<? extends Materializable>>();
1✔
4230
                }
1✔
4231

4232
                /**
4233
                 * {@inheritDoc}
4234
                 */
4235
                public void accept(Callable<? extends Callable<? extends Materializable>> work, boolean eager) throws IOException {
4236
                    try {
4237
                        Callable<? extends Materializable> preprocessed = work.call();
1✔
4238
                        if (eager) {
1✔
4239
                            preprocessed.call().materialize(sink, transformed, failed, unresolved);
1✔
4240
                        } else {
4241
                            preprocessings.add(preprocessed);
1✔
4242
                        }
4243
                    } catch (Exception exception) {
1✔
4244
                        if (exception instanceof IOException) {
1✔
4245
                            throw (IOException) exception;
×
4246
                        } else if (exception instanceof RuntimeException) {
1✔
4247
                            throw (RuntimeException) exception;
1✔
4248
                        } else {
4249
                            throw new IllegalStateException(exception);
×
4250
                        }
4251
                    }
1✔
4252
                }
1✔
4253

4254
                /**
4255
                 * {@inheritDoc}
4256
                 */
4257
                public void complete() throws IOException {
4258
                    for (Callable<? extends Materializable> preprocessing : preprocessings) {
1✔
4259
                        if (Thread.interrupted()) {
1✔
4260
                            Thread.currentThread().interrupt();
×
4261
                            throw new IllegalStateException("Interrupted during plugin engine completion");
×
4262
                        }
4263
                        try {
4264
                            preprocessing.call().materialize(sink, transformed, failed, unresolved);
1✔
4265
                        } catch (Exception exception) {
1✔
4266
                            if (exception instanceof IOException) {
1✔
4267
                                throw (IOException) exception;
×
4268
                            } else if (exception instanceof RuntimeException) {
1✔
4269
                                throw (RuntimeException) exception;
1✔
4270
                            } else {
4271
                                throw new IllegalStateException(exception);
×
4272
                            }
4273
                        }
1✔
4274
                    }
1✔
4275
                }
1✔
4276

4277
                /**
4278
                 * {@inheritDoc}
4279
                 */
4280
                public void close() {
4281
                    /* do nothing */
4282
                }
1✔
4283

4284
                /**
4285
                 * A factory for creating a serial dispatcher.
4286
                 */
4287
                public enum Factory implements Dispatcher.Factory {
1✔
4288

4289
                    /**
4290
                     * The singleton instance.
4291
                     */
4292
                    INSTANCE;
1✔
4293

4294
                    /**
4295
                     * {@inheritDoc}
4296
                     */
4297
                    public Dispatcher make(Target.Sink sink,
4298
                                           List<TypeDescription> transformed,
4299
                                           Map<TypeDescription,
4300
                                                   List<Throwable>> failed,
4301
                                           List<String> unresolved) {
4302
                        return new ForSerialTransformation(sink, transformed, failed, unresolved);
1✔
4303
                    }
4304
                }
4305
            }
4306

4307
            /**
4308
             * A dispatcher that applies transformations within one or more threads in parallel to the default transformer.
4309
             */
4310
            class ForParallelTransformation implements Dispatcher {
4311

4312
                /**
4313
                 * The target sink.
4314
                 */
4315
                private final Target.Sink sink;
4316

4317
                /**
4318
                 * A list of all types that are transformed.
4319
                 */
4320
                private final List<TypeDescription> transformed;
4321

4322
                /**
4323
                 * A mapping of all types that failed during transformation to the exceptions that explain the failure.
4324
                 */
4325
                private final Map<TypeDescription, List<Throwable>> failed;
4326

4327
                /**
4328
                 * A list of type names that could not be resolved.
4329
                 */
4330
                private final List<String> unresolved;
4331

4332
                /**
4333
                 * A completion service for all preprocessings.
4334
                 */
4335
                private final CompletionService<Callable<Materializable>> preprocessings;
4336

4337
                /**
4338
                 * A completion service for all materializers.
4339
                 */
4340
                private final CompletionService<Materializable> materializers;
4341

4342
                /**
4343
                 * A count of deferred processings.
4344
                 */
4345
                private int deferred;
4346

4347
                /**
4348
                 * A collection of futures that are currently scheduled.
4349
                 */
4350
                private final Set<Future<?>> futures;
4351

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

4376
                /**
4377
                 * {@inheritDoc}
4378
                 */
4379
                @SuppressWarnings("unchecked")
4380
                public void accept(Callable<? extends Callable<? extends Materializable>> work, boolean eager) {
4381
                    if (eager) {
1✔
4382
                        futures.add(materializers.submit(new EagerWork(work)));
1✔
4383
                    } else {
4384
                        deferred += 1;
1✔
4385
                        futures.add(preprocessings.submit((Callable<Callable<Materializable>>) work));
1✔
4386
                    }
4387
                }
1✔
4388

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

4425
                /**
4426
                 * {@inheritDoc}
4427
                 */
4428
                public void close() {
4429
                    for (Future<?> future : futures) {
1✔
4430
                        future.cancel(true);
×
4431
                    }
×
4432
                }
1✔
4433

4434
                /**
4435
                 * A parallel dispatcher that shuts down its executor service upon completion of a plugin engine's application.
4436
                 */
4437
                @HashCodeAndEqualsPlugin.Enhance
4438
                public static class WithThrowawayExecutorService extends ForParallelTransformation {
4439

4440
                    /**
4441
                     * The executor service to delegate any work to.
4442
                     */
4443
                    private final ExecutorService executorService;
4444

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

4463
                    @Override
4464
                    public void close() {
4465
                        try {
4466
                            super.close();
1✔
4467
                        } finally {
4468
                            executorService.shutdown();
1✔
4469
                        }
4470
                    }
1✔
4471

4472
                    /**
4473
                     * A factory for a parallel executor service that creates a new executor service on each plugin engine application.
4474
                     */
4475
                    @HashCodeAndEqualsPlugin.Enhance
4476
                    public static class Factory implements Dispatcher.Factory {
4477

4478
                        /**
4479
                         * The amount of threads to create in the throw-away executor service.
4480
                         */
4481
                        private final int threads;
4482

4483
                        /**
4484
                         * Creates a new factory.
4485
                         *
4486
                         * @param threads The amount of threads to create in the throw-away executor service.
4487
                         */
4488
                        public Factory(int threads) {
1✔
4489
                            this.threads = threads;
1✔
4490
                        }
1✔
4491

4492
                        /**
4493
                         * {@inheritDoc}
4494
                         */
4495
                        public Dispatcher make(Target.Sink sink,
4496
                                               List<TypeDescription> transformed,
4497
                                               Map<TypeDescription, List<Throwable>> failed,
4498
                                               List<String> unresolved) {
4499
                            return new WithThrowawayExecutorService(Executors.newFixedThreadPool(threads), sink, transformed, failed, unresolved);
1✔
4500
                        }
4501
                    }
4502
                }
4503

4504
                /**
4505
                 * A factory for a dispatcher that uses a given executor service for parallel dispatching.
4506
                 */
4507
                @HashCodeAndEqualsPlugin.Enhance
4508
                public static class Factory implements Dispatcher.Factory {
4509

4510
                    /**
4511
                     * The executor to use.
4512
                     */
4513
                    private final Executor executor;
4514

4515
                    /**
4516
                     * Creates a new dispatcher factory for parallel dispatching using the supplied executor.
4517
                     *
4518
                     * @param executor The executor to use.
4519
                     */
4520
                    public Factory(Executor executor) {
×
4521
                        this.executor = executor;
×
4522
                    }
×
4523

4524
                    /**
4525
                     * {@inheritDoc}
4526
                     */
4527
                    public Dispatcher make(Target.Sink sink,
4528
                                           List<TypeDescription> transformed,
4529
                                           Map<TypeDescription, List<Throwable>> failed,
4530
                                           List<String> unresolved) {
4531
                        return new ForParallelTransformation(executor, sink, transformed, failed, unresolved);
×
4532
                    }
4533
                }
4534

4535
                /**
4536
                 * An eager materialization that does not defer processing after preprocessing.
4537
                 */
4538
                @HashCodeAndEqualsPlugin.Enhance
4539
                protected static class EagerWork implements Callable<Materializable> {
4540

4541
                    /**
4542
                     * The work to apply.
4543
                     */
4544
                    private final Callable<? extends Callable<? extends Materializable>> work;
4545

4546
                    /**
4547
                     * Creates an eager work resolution.
4548
                     *
4549
                     * @param work The work to apply.
4550
                     */
4551
                    protected EagerWork(Callable<? extends Callable<? extends Materializable>> work) {
1✔
4552
                        this.work = work;
1✔
4553
                    }
1✔
4554

4555
                    /**
4556
                     * {@inheritDoc}
4557
                     */
4558
                    public Materializable call() throws Exception {
4559
                        return work.call().call();
1✔
4560
                    }
4561
                }
4562
            }
4563
        }
4564

4565
        /**
4566
         * A summary of the application of a {@link Engine} to a source and target.
4567
         */
4568
        class Summary {
4569

4570
            /**
4571
             * A list of all types that were transformed.
4572
             */
4573
            private final List<TypeDescription> transformed;
4574

4575
            /**
4576
             * A mapping of all types that failed during transformation to the exceptions that explain the failure.
4577
             */
4578
            private final Map<TypeDescription, List<Throwable>> failed;
4579

4580
            /**
4581
             * A list of type names that could not be resolved.
4582
             */
4583
            private final List<String> unresolved;
4584

4585
            /**
4586
             * Creates a new summary.
4587
             *
4588
             * @param transformed A list of all types that were transformed.
4589
             * @param failed      A mapping of all types that failed during transformation to the exceptions that explain the failure.
4590
             * @param unresolved  A list of type names that could not be resolved.
4591
             */
4592
            public Summary(List<TypeDescription> transformed, Map<TypeDescription, List<Throwable>> failed, List<String> unresolved) {
1✔
4593
                this.transformed = transformed;
1✔
4594
                this.failed = failed;
1✔
4595
                this.unresolved = unresolved;
1✔
4596
            }
1✔
4597

4598
            /**
4599
             * Returns a list of all types that were transformed.
4600
             *
4601
             * @return A list of all types that were transformed.
4602
             */
4603
            public List<TypeDescription> getTransformed() {
4604
                return transformed;
1✔
4605
            }
4606

4607
            /**
4608
             * Returns a mapping of all types that failed during transformation to the exceptions that explain the failure.
4609
             *
4610
             * @return A mapping of all types that failed during transformation to the exceptions that explain the failure.
4611
             */
4612
            public Map<TypeDescription, List<Throwable>> getFailed() {
4613
                return failed;
1✔
4614
            }
4615

4616
            /**
4617
             * Returns a list of type names that could not be resolved.
4618
             *
4619
             * @return A list of type names that could not be resolved.
4620
             */
4621
            public List<String> getUnresolved() {
4622
                return unresolved;
1✔
4623
            }
4624

4625
            @Override
4626
            public int hashCode() {
4627
                int result = transformed.hashCode();
1✔
4628
                result = 31 * result + failed.hashCode();
1✔
4629
                result = 31 * result + unresolved.hashCode();
1✔
4630
                return result;
1✔
4631
            }
4632

4633
            @Override
4634
            public boolean equals(@MaybeNull Object other) {
4635
                if (this == other) {
1✔
4636
                    return true;
1✔
4637
                } else if (other == null || getClass() != other.getClass()) {
1✔
4638
                    return false;
1✔
4639
                }
4640
                Summary summary = (Summary) other;
1✔
4641
                return transformed.equals(summary.transformed)
1✔
4642
                        && failed.equals(summary.failed)
1✔
4643
                        && unresolved.equals(summary.unresolved);
1✔
4644
            }
4645
        }
4646

4647
        /**
4648
         * An abstract base implementation of a plugin engine.
4649
         */
4650
        abstract class AbstractBase implements Engine {
1✔
4651

4652
            /**
4653
             * {@inheritDoc}
4654
             */
4655
            public Engine withErrorHandlers(ErrorHandler... errorHandler) {
4656
                return withErrorHandlers(Arrays.asList(errorHandler));
1✔
4657
            }
4658

4659
            /**
4660
             * {@inheritDoc}
4661
             */
4662
            public Engine withParallelTransformation(int threads) {
4663
                if (threads < 1) {
×
4664
                    throw new IllegalArgumentException("Number of threads must be positive: " + threads);
×
4665
                }
4666
                return with(new Dispatcher.ForParallelTransformation.WithThrowawayExecutorService.Factory(threads));
×
4667
            }
4668

4669
            /**
4670
             * {@inheritDoc}
4671
             */
4672
            public Summary apply(File source, File target, Factory... factory) throws IOException {
4673
                return apply(source, target, Arrays.asList(factory));
1✔
4674
            }
4675

4676
            /**
4677
             * {@inheritDoc}
4678
             */
4679
            public Summary apply(File source, File target, List<? extends Factory> factories) throws IOException {
4680
                return apply(source.isDirectory()
1✔
4681
                        ? new Source.ForFolder(source)
4682
                        : new Source.ForJarFile(source), target.isDirectory()
1✔
4683
                        ? new Target.ForFolder(target)
4684
                        : new Target.ForJarFile(target), factories);
4685
            }
4686

4687
            /**
4688
             * {@inheritDoc}
4689
             */
4690
            public Summary apply(Source source, Target target, Factory... factory) throws IOException {
4691
                return apply(source, target, Arrays.asList(factory));
1✔
4692
            }
4693
        }
4694

4695
        /**
4696
         * A default implementation of a plugin engine.
4697
         */
4698
        @HashCodeAndEqualsPlugin.Enhance
4699
        class Default extends AbstractBase {
4700

4701
            /**
4702
             * The Byte Buddy instance to use.
4703
             */
4704
            private final ByteBuddy byteBuddy;
4705

4706
            /**
4707
             * The type strategy to use.
4708
             */
4709
            private final TypeStrategy typeStrategy;
4710

4711
            /**
4712
             * The pool strategy to use.
4713
             */
4714
            private final PoolStrategy poolStrategy;
4715

4716
            /**
4717
             * The class file locator to use.
4718
             */
4719
            private final ClassFileLocator classFileLocator;
4720

4721
            /**
4722
             * The class file version to use for multi-release jars, or {@code null}.
4723
             */
4724
            @MaybeNull
4725
            @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
4726
            private final ClassFileVersion classFileVersion;
4727

4728
            /**
4729
             * The listener to use.
4730
             */
4731
            private final Listener listener;
4732

4733
            /**
4734
             * The error handler to use.
4735
             */
4736
            private final ErrorHandler errorHandler;
4737

4738
            /**
4739
             * The dispatcher factory to use.
4740
             */
4741
            private final Dispatcher.Factory dispatcherFactory;
4742

4743
            /**
4744
             * A matcher for types to exclude from transformation.
4745
             */
4746
            private final ElementMatcher.Junction<? super TypeDescription> ignoredTypeMatcher;
4747

4748
            /**
4749
             * Creates a new default plugin engine that rebases types and fails fast and on unresolved types and on live initializers.
4750
             */
4751
            public Default() {
4752
                this(new ByteBuddy());
1✔
4753
            }
1✔
4754

4755
            /**
4756
             * Creates a new default plugin engine that rebases types and fails fast and on unresolved types and on live initializers.
4757
             *
4758
             * @param byteBuddy The Byte Buddy instance to use.
4759
             */
4760
            public Default(ByteBuddy byteBuddy) {
4761
                this(byteBuddy, TypeStrategy.Default.REBASE);
1✔
4762
            }
1✔
4763

4764
            /**
4765
             * Creates a new default plugin engine.
4766
             *
4767
             * @param byteBuddy    The Byte Buddy instance to use.
4768
             * @param typeStrategy The type strategy to use.
4769
             */
4770
            protected Default(ByteBuddy byteBuddy, TypeStrategy typeStrategy) {
4771
                this(byteBuddy,
1✔
4772
                        typeStrategy,
4773
                        PoolStrategy.Default.FAST,
4774
                        ClassFileLocator.NoOp.INSTANCE,
4775
                        null,
4776
                        Listener.NoOp.INSTANCE,
4777
                        new ErrorHandler.Compound(ErrorHandler.Failing.FAIL_FAST,
4778
                                ErrorHandler.Enforcing.ALL_TYPES_RESOLVED,
4779
                                ErrorHandler.Enforcing.NO_LIVE_INITIALIZERS),
4780
                        Dispatcher.ForSerialTransformation.Factory.INSTANCE,
4781
                        none());
1✔
4782
            }
1✔
4783

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

4817
            /**
4818
             * Creates a plugin engine from an {@link EntryPoint}.
4819
             *
4820
             * @param entryPoint            The entry point to resolve into a plugin engine.
4821
             * @param classFileVersion      The class file version to assume.
4822
             * @param methodNameTransformer The method name transformer to use.
4823
             * @return An appropriate plugin engine.
4824
             */
4825
            public static Engine of(EntryPoint entryPoint, ClassFileVersion classFileVersion, MethodNameTransformer methodNameTransformer) {
4826
                return new Default(entryPoint.byteBuddy(classFileVersion), new TypeStrategy.ForEntryPoint(entryPoint, methodNameTransformer));
1✔
4827
            }
4828

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

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

4873
            /**
4874
             * {@inheritDoc}
4875
             */
4876
            public Engine with(ByteBuddy byteBuddy) {
4877
                return new Default(byteBuddy,
1✔
4878
                        typeStrategy,
4879
                        poolStrategy,
4880
                        classFileLocator,
4881
                        classFileVersion,
4882
                        listener,
4883
                        errorHandler,
4884
                        dispatcherFactory,
4885
                        ignoredTypeMatcher);
4886
            }
4887

4888
            /**
4889
             * {@inheritDoc}
4890
             */
4891
            public Engine with(TypeStrategy typeStrategy) {
4892
                return new Default(byteBuddy,
1✔
4893
                        typeStrategy,
4894
                        poolStrategy,
4895
                        classFileLocator,
4896
                        classFileVersion,
4897
                        listener,
4898
                        errorHandler,
4899
                        dispatcherFactory,
4900
                        ignoredTypeMatcher);
4901
            }
4902

4903
            /**
4904
             * {@inheritDoc}
4905
             */
4906
            public Engine with(PoolStrategy poolStrategy) {
4907
                return new Default(byteBuddy,
1✔
4908
                        typeStrategy,
4909
                        poolStrategy,
4910
                        classFileLocator,
4911
                        classFileVersion,
4912
                        listener,
4913
                        errorHandler,
4914
                        dispatcherFactory,
4915
                        ignoredTypeMatcher);
4916
            }
4917

4918
            /**
4919
             * {@inheritDoc}
4920
             */
4921
            public Engine with(ClassFileLocator classFileLocator) {
4922
                return new Default(byteBuddy,
1✔
4923
                        typeStrategy,
4924
                        poolStrategy,
4925
                        new ClassFileLocator.Compound(this.classFileLocator, classFileLocator),
4926
                        classFileVersion,
4927
                        listener,
4928
                        errorHandler,
4929
                        dispatcherFactory,
4930
                        ignoredTypeMatcher);
4931
            }
4932

4933
            /**
4934
             * {@inheritDoc}
4935
             */
4936
            public Engine with(@MaybeNull ClassFileVersion classFileVersion) {
4937
                return new Default(byteBuddy,
1✔
4938
                        typeStrategy,
4939
                        poolStrategy,
4940
                        classFileLocator,
4941
                        classFileVersion,
4942
                        listener,
4943
                        errorHandler,
4944
                        dispatcherFactory,
4945
                        ignoredTypeMatcher);
4946
            }
4947

4948
            /**
4949
             * {@inheritDoc}
4950
             */
4951
            public Engine with(Listener listener) {
4952
                return new Default(byteBuddy,
1✔
4953
                        typeStrategy,
4954
                        poolStrategy,
4955
                        classFileLocator,
4956
                        classFileVersion,
4957
                        new Listener.Compound(this.listener, listener),
4958
                        errorHandler,
4959
                        dispatcherFactory,
4960
                        ignoredTypeMatcher);
4961
            }
4962

4963
            /**
4964
             * {@inheritDoc}
4965
             */
4966
            public Engine withoutErrorHandlers() {
4967
                return new Default(byteBuddy,
1✔
4968
                        typeStrategy,
4969
                        poolStrategy,
4970
                        classFileLocator,
4971
                        classFileVersion,
4972
                        listener,
4973
                        Listener.NoOp.INSTANCE,
4974
                        dispatcherFactory,
4975
                        ignoredTypeMatcher);
4976
            }
4977

4978
            /**
4979
             * {@inheritDoc}
4980
             */
4981
            public Engine withErrorHandlers(List<? extends ErrorHandler> errorHandlers) {
4982
                return new Default(byteBuddy,
1✔
4983
                        typeStrategy,
4984
                        poolStrategy,
4985
                        classFileLocator,
4986
                        classFileVersion,
4987
                        listener,
4988
                        new ErrorHandler.Compound(errorHandlers),
4989
                        dispatcherFactory,
4990
                        ignoredTypeMatcher);
4991
            }
4992

4993
            /**
4994
             * {@inheritDoc}
4995
             */
4996
            public Engine with(Dispatcher.Factory dispatcherFactory) {
4997
                return new Default(byteBuddy,
1✔
4998
                        typeStrategy,
4999
                        poolStrategy,
5000
                        classFileLocator,
5001
                        classFileVersion,
5002
                        listener,
5003
                        errorHandler,
5004
                        dispatcherFactory,
5005
                        ignoredTypeMatcher);
5006
            }
5007

5008
            /**
5009
             * {@inheritDoc}
5010
             */
5011
            public Engine ignore(ElementMatcher<? super TypeDescription> matcher) {
5012
                return new Default(byteBuddy,
1✔
5013
                        typeStrategy,
5014
                        poolStrategy,
5015
                        classFileLocator,
5016
                        classFileVersion,
5017
                        listener,
5018
                        errorHandler,
5019
                        dispatcherFactory,
5020
                        ignoredTypeMatcher.<TypeDescription>or(matcher));
1✔
5021
            }
5022

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

5143
            /**
5144
             * A class file locator that shadows a given {@link Source.Element}'s type with the explicit element.
5145
             * This avoids that caching yields the wrong class file in case of multi-release jars.
5146
             */
5147
            @HashCodeAndEqualsPlugin.Enhance
5148
            protected static class SourceEntryPrependingClassFileLocator implements ClassFileLocator {
5149

5150
                /**
5151
                 * The name of the represented type.
5152
                 */
5153
                private final String name;
5154

5155
                /**
5156
                 * The corresponding source element.
5157
                 */
5158
                private final Source.Element element;
5159

5160
                /**
5161
                 * The actual class file locator to query for all other types.
5162
                 */
5163
                private final ClassFileLocator delegate;
5164

5165
                /**
5166
                 * Creates a class file locator that prepends a {@link Source.Element}.
5167
                 *
5168
                 * @param name     The name of the represented type.
5169
                 * @param element  The corresponding source element.
5170
                 * @param delegate The actual class file locator to query for all other types.
5171
                 */
5172
                protected SourceEntryPrependingClassFileLocator(String name, Source.Element element, ClassFileLocator delegate) {
1✔
5173
                    this.name = name;
1✔
5174
                    this.element = element;
1✔
5175
                    this.delegate = delegate;
1✔
5176
                }
1✔
5177

5178
                /**
5179
                 * {@inheritDoc}
5180
                 */
5181
                public Resolution locate(String name) throws IOException {
5182
                    if (name.endsWith(this.name)) {
1✔
5183
                        InputStream inputStream = element.getInputStream();
1✔
5184
                        try {
5185
                            return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
1✔
5186
                        } finally {
5187
                            inputStream.close();
1✔
5188
                        }
5189
                    } else {
5190
                        return delegate.locate(name);
×
5191
                    }
5192
                }
5193

5194
                /**
5195
                 * {@inheritDoc}
5196
                 */
5197
                public void close() throws IOException {
5198
                    delegate.close();
×
5199
                }
×
5200
            }
5201

5202
            /**
5203
             * A preprocessor for a parallel plugin engine.
5204
             */
5205
            private class Preprocessor implements Callable<Callable<? extends Dispatcher.Materializable>> {
5206

5207
                /**
5208
                 * The processed element.
5209
                 */
5210
                private final Source.Element element;
5211

5212
                /**
5213
                 * The name of the processed type.
5214
                 */
5215
                private final String typeName;
5216

5217
                /**
5218
                 * The class file locator to use.
5219
                 */
5220
                private final ClassFileLocator classFileLocator;
5221

5222
                /**
5223
                 * The multi-release class file version or {@code null} for a regular class.
5224
                 */
5225
                @MaybeNull
5226
                @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
5227
                private final ClassFileVersion classFileVersion;
5228

5229
                /**
5230
                 * The type pool to use.
5231
                 */
5232
                private final TypePool typePool;
5233

5234
                /**
5235
                 * The listener to notify.
5236
                 */
5237
                private final Listener listener;
5238

5239
                /**
5240
                 * The plugins to apply.
5241
                 */
5242
                private final List<Plugin> plugins;
5243

5244
                /**
5245
                 * The plugins with preprocessors to preprocess.
5246
                 */
5247
                private final List<WithPreprocessor> preprocessors;
5248

5249
                /**
5250
                 * Creates a new preprocessor.
5251
                 *
5252
                 * @param element          The processed element.
5253
                 * @param typeName         The name of the processed type.
5254
                 * @param classFileLocator The class file locator to use.
5255
                 * @param classFileVersion The multi-release class file version or {@code null} for a regular class.
5256
                 * @param typePool         The type pool to use.
5257
                 * @param listener         The listener to notify.
5258
                 * @param plugins          The plugins to apply.
5259
                 * @param preprocessors    The plugins with preprocessors to preprocess.
5260
                 */
5261
                private Preprocessor(Source.Element element,
5262
                                     String typeName,
5263
                                     ClassFileLocator classFileLocator,
5264
                                     @MaybeNull ClassFileVersion classFileVersion,
5265
                                     TypePool typePool,
5266
                                     Listener listener,
5267
                                     List<Plugin> plugins,
5268
                                     List<WithPreprocessor> preprocessors) {
1✔
5269
                    this.element = element;
1✔
5270
                    this.typeName = typeName;
1✔
5271
                    this.classFileLocator = classFileLocator;
1✔
5272
                    this.classFileVersion = classFileVersion;
1✔
5273
                    this.typePool = typePool;
1✔
5274
                    this.listener = listener;
1✔
5275
                    this.plugins = plugins;
1✔
5276
                    this.preprocessors = preprocessors;
1✔
5277
                }
1✔
5278

5279
                /**
5280
                 * {@inheritDoc}
5281
                 */
5282
                public Callable<Dispatcher.Materializable> call() throws Exception {
5283
                    listener.onDiscovery(typeName);
1✔
5284
                    TypePool.Resolution resolution = typePool.describe(typeName);
1✔
5285
                    if (resolution.isResolved()) {
1✔
5286
                        TypeDescription typeDescription = resolution.resolve();
1✔
5287
                        try {
5288
                            if (!ignoredTypeMatcher.matches(typeDescription)) {
1✔
5289
                                for (WithPreprocessor preprocessor : preprocessors) {
1✔
5290
                                    preprocessor.onPreprocess(typeDescription, classFileLocator);
1✔
5291
                                }
1✔
5292
                                return new Resolved(classFileVersion, typeDescription);
1✔
5293
                            } else {
5294
                                return new Ignored(typeDescription);
1✔
5295
                            }
5296
                        } catch (Throwable throwable) {
×
5297
                            listener.onComplete(typeDescription);
×
5298
                            if (throwable instanceof Exception) {
×
5299
                                throw (Exception) throwable;
×
5300
                            } else if (throwable instanceof Error) {
×
5301
                                throw (Error) throwable;
×
5302
                            } else {
5303
                                throw new IllegalStateException(throwable);
×
5304
                            }
5305
                        }
5306
                    } else {
5307
                        return new Unresolved();
1✔
5308
                    }
5309
                }
5310

5311
                /**
5312
                 * A resolved materializable.
5313
                 */
5314
                private class Resolved implements Callable<Dispatcher.Materializable> {
5315

5316
                    /**
5317
                     * The multi-release Java version number or {@code null} if a regular class.
5318
                     */
5319
                    @MaybeNull
5320
                    private final ClassFileVersion classFileVersion;
5321

5322
                    /**
5323
                     * A description of the resolved type.
5324
                     */
5325
                    private final TypeDescription typeDescription;
5326

5327
                    /**
5328
                     * Creates a new resolved materializable.
5329
                     *
5330
                     * @param classFileVersion The multi-release Java version number or {@code null} if a regular class.
5331
                     * @param typeDescription  A description of the resolved type.
5332
                     */
5333
                    private Resolved(@MaybeNull ClassFileVersion classFileVersion, TypeDescription typeDescription) {
1✔
5334
                        this.classFileVersion = classFileVersion;
1✔
5335
                        this.typeDescription = typeDescription;
1✔
5336
                    }
1✔
5337

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

5389
                /**
5390
                 * A materializable for an ignored element.
5391
                 */
5392
                private class Ignored implements Callable<Dispatcher.Materializable> {
5393

5394
                    /**
5395
                     * A description of the ignored type.
5396
                     */
5397
                    private final TypeDescription typeDescription;
5398

5399
                    /**
5400
                     * A materializable for an ignored element.
5401
                     *
5402
                     * @param typeDescription A description of the ignored type.
5403
                     */
5404
                    private Ignored(TypeDescription typeDescription) {
1✔
5405
                        this.typeDescription = typeDescription;
1✔
5406
                    }
1✔
5407

5408
                    /**
5409
                     * {@inheritDoc}
5410
                     */
5411
                    public Dispatcher.Materializable call() {
5412
                        try {
5413
                            listener.onIgnored(typeDescription, plugins);
1✔
5414
                        } finally {
5415
                            listener.onComplete(typeDescription);
1✔
5416
                        }
5417
                        return new Dispatcher.Materializable.ForRetainedElement(element);
1✔
5418
                    }
5419
                }
5420

5421
                /**
5422
                 * A materializable that represents an unresolved type.
5423
                 */
5424
                private class Unresolved implements Callable<Dispatcher.Materializable> {
1✔
5425

5426
                    /**
5427
                     * {@inheritDoc}
5428
                     */
5429
                    public Dispatcher.Materializable call() {
5430
                        listener.onUnresolved(typeName);
1✔
5431
                        return new Dispatcher.Materializable.ForUnresolvedElement(element, typeName);
1✔
5432
                    }
5433
                }
5434
            }
5435
        }
5436
    }
5437

5438
    /**
5439
     * A non-operational plugin that does not instrument any type. This plugin does not need to be closed.
5440
     */
5441
    @HashCodeAndEqualsPlugin.Enhance
5442
    class NoOp implements Plugin, Plugin.Factory {
1✔
5443

5444
        /**
5445
         * {@inheritDoc}
5446
         */
5447
        public Plugin make() {
5448
            return this;
1✔
5449
        }
5450

5451
        /**
5452
         * {@inheritDoc}
5453
         */
5454
        public boolean matches(@MaybeNull TypeDescription target) {
5455
            return false;
1✔
5456
        }
5457

5458
        /**
5459
         * {@inheritDoc}
5460
         */
5461
        public DynamicType.Builder<?> apply(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassFileLocator classFileLocator) {
5462
            throw new IllegalStateException("Cannot apply non-operational plugin");
1✔
5463
        }
5464

5465
        /**
5466
         * {@inheritDoc}
5467
         */
5468
        public void close() {
5469
            /* do nothing */
5470
        }
1✔
5471
    }
5472

5473
    /**
5474
     * An abstract base for a {@link Plugin} that matches types by a given {@link ElementMatcher}.
5475
     */
5476
    @HashCodeAndEqualsPlugin.Enhance
5477
    abstract class ForElementMatcher implements Plugin {
5478

5479
        /**
5480
         * The element matcher to apply.
5481
         */
5482
        private final ElementMatcher<? super TypeDescription> matcher;
5483

5484
        /**
5485
         * Creates a new plugin that matches types using an element matcher.
5486
         *
5487
         * @param matcher The element matcher to apply.
5488
         */
5489
        protected ForElementMatcher(ElementMatcher<? super TypeDescription> matcher) {
1✔
5490
            this.matcher = matcher;
1✔
5491
        }
1✔
5492

5493
        /**
5494
         * {@inheritDoc}
5495
         */
5496
        public boolean matches(@MaybeNull TypeDescription target) {
5497
            return matcher.matches(target);
1✔
5498
        }
5499
    }
5500
}
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