• 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

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

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.ClassFileVersion;
20
import net.bytebuddy.agent.builder.AgentBuilder;
21
import net.bytebuddy.build.AccessControllerPlugin;
22
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
23
import net.bytebuddy.description.NamedElement;
24
import net.bytebuddy.description.type.TypeDescription;
25
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
26
import net.bytebuddy.matcher.ElementMatcher;
27
import net.bytebuddy.utility.JavaModule;
28
import net.bytebuddy.utility.JavaType;
29
import net.bytebuddy.utility.StreamDrainer;
30
import net.bytebuddy.utility.dispatcher.JavaDispatcher;
31
import net.bytebuddy.utility.nullability.AlwaysNull;
32
import net.bytebuddy.utility.nullability.MaybeNull;
33

34
import java.io.Closeable;
35
import java.io.File;
36
import java.io.FileInputStream;
37
import java.io.IOException;
38
import java.io.InputStream;
39
import java.lang.instrument.ClassFileTransformer;
40
import java.lang.instrument.Instrumentation;
41
import java.lang.instrument.UnmodifiableClassException;
42
import java.lang.ref.WeakReference;
43
import java.lang.reflect.Field;
44
import java.lang.reflect.Method;
45
import java.net.URL;
46
import java.net.URLClassLoader;
47
import java.security.PrivilegedAction;
48
import java.security.ProtectionDomain;
49
import java.util.ArrayList;
50
import java.util.Arrays;
51
import java.util.Collection;
52
import java.util.Collections;
53
import java.util.Enumeration;
54
import java.util.HashMap;
55
import java.util.Iterator;
56
import java.util.List;
57
import java.util.Map;
58
import java.util.Set;
59
import java.util.SortedSet;
60
import java.util.TreeSet;
61
import java.util.Vector;
62
import java.util.jar.JarEntry;
63
import java.util.jar.JarFile;
64
import java.util.jar.Manifest;
65
import java.util.regex.Pattern;
66
import java.util.zip.ZipEntry;
67
import java.util.zip.ZipFile;
68

69
import static net.bytebuddy.matcher.ElementMatchers.isChildOf;
70

71
/**
72
 * Locates a class file or its byte array representation when it is given its type description.
73
 */
74
public interface ClassFileLocator extends Closeable {
75

76
    /**
77
     * The file extension for a Java class file.
78
     */
79
    String CLASS_FILE_EXTENSION = ".class";
80

81
    /**
82
     * The prefix folder for {@code META-INF/versions/} which contains multi-release files.
83
     */
84
    String META_INF_VERSIONS = "META-INF/versions/";
85

86
    /**
87
     * Locates the class file for a given type and returns the binary data of the class file.
88
     *
89
     * @param name The name of the type to locate a class file representation for.
90
     * @return Any binary representation of the type which might be illegal.
91
     * @throws java.io.IOException If reading a class file causes an error.
92
     */
93
    Resolution locate(String name) throws IOException;
94

95
    /**
96
     * Represents a class file as binary data.
97
     */
98
    interface Resolution {
99

100
        /**
101
         * Checks if this binary representation is valid.
102
         *
103
         * @return {@code true} if this binary representation is valid.
104
         */
105
        boolean isResolved();
106

107
        /**
108
         * Finds the data of this binary representation. Calling this method is only legal for resolved instances.
109
         * For non-resolved instances, an exception is thrown.
110
         *
111
         * @return The requested binary data. The returned array must not be altered.
112
         */
113
        byte[] resolve();
114

115
        /**
116
         * A canonical representation of an illegal binary representation.
117
         */
118
        @HashCodeAndEqualsPlugin.Enhance
119
        class Illegal implements Resolution {
120

121
            /**
122
             * The name of the unresolved class file.
123
             */
124
            private final String typeName;
125

126
            /**
127
             * Creates an illegal resolution for a class file.
128
             *
129
             * @param typeName The name of the unresolved class file.
130
             */
131
            public Illegal(String typeName) {
1✔
132
                this.typeName = typeName;
1✔
133
            }
1✔
134

135
            /**
136
             * {@inheritDoc}
137
             */
138
            public boolean isResolved() {
139
                return false;
1✔
140
            }
141

142
            /**
143
             * {@inheritDoc}
144
             */
145
            public byte[] resolve() {
146
                throw new IllegalStateException("Could not locate class file for " + typeName);
1✔
147
            }
148
        }
149

150
        /**
151
         * Represents a byte array as binary data.
152
         */
153
        @HashCodeAndEqualsPlugin.Enhance
154
        class Explicit implements Resolution {
155

156
            /**
157
             * The represented data.
158
             */
159
            private final byte[] binaryRepresentation;
160

161
            /**
162
             * Creates a new explicit resolution of a given array of binary data.
163
             *
164
             * @param binaryRepresentation The binary data to represent. The array must not be modified.
165
             */
166
            @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "The array is not modified by class contract.")
167
            public Explicit(byte[] binaryRepresentation) {
1✔
168
                this.binaryRepresentation = binaryRepresentation;
1✔
169
            }
1✔
170

171
            /**
172
             * {@inheritDoc}
173
             */
174
            public boolean isResolved() {
175
                return true;
1✔
176
            }
177

178
            /**
179
             * {@inheritDoc}
180
             */
181
            @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "The array is not modified by class contract.")
182
            public byte[] resolve() {
183
                return binaryRepresentation;
1✔
184
            }
185
        }
186
    }
187

188
    /**
189
     * A class file locator that cannot locate any class files.
190
     */
191
    enum NoOp implements ClassFileLocator {
1✔
192

193
        /**
194
         * The singleton instance.
195
         */
196
        INSTANCE;
1✔
197

198
        /**
199
         * {@inheritDoc}
200
         */
201
        public Resolution locate(String name) {
202
            return new Resolution.Illegal(name);
1✔
203
        }
204

205
        /**
206
         * {@inheritDoc}
207
         */
208
        public void close() {
209
            /* do nothing */
210
        }
1✔
211
    }
212

213
    /**
214
     * A class file locator that is aware of multi-release JAR file semantics.
215
     */
216
    @HashCodeAndEqualsPlugin.Enhance
217
    abstract class MultiReleaseAware implements ClassFileLocator {
218

219
        /**
220
         * The property name of a multi-release JAR file.
221
         */
222
        private static final String MULTI_RELEASE_ATTRIBUTE = "Multi-Release";
223

224
        /**
225
         * Indicates that no multi-release versions exist.
226
         */
227
        protected static final int[] NO_MULTI_RELEASE = new int[0];
1✔
228

229
        /**
230
         * Contains the existing multi-release jar folders that are available for the
231
         * current JVM version in decreasing order.
232
         */
233
        private final int[] version;
234

235
        /**
236
         * Creates a multi-release aware class file locator.
237
         *
238
         * @param version Contains the existing multi-release jar folders that are available for the
239
         *                current JVM version in decreasing order.
240
         */
241
        protected MultiReleaseAware(int[] version) {
1✔
242
            this.version = version;
1✔
243
        }
1✔
244

245
        /**
246
         * {@inheritDoc}
247
         */
248
        public Resolution locate(String name) throws IOException {
249
            String path = name.replace('.', '/') + CLASS_FILE_EXTENSION;
1✔
250
            for (int index = 0; index < version.length + 1; index++) {
1✔
251
                byte[] binaryRepresentation = doLocate(index == version.length ? path : META_INF_VERSIONS + version[index] + "/" + path);
1✔
252
                if (binaryRepresentation != null) {
1✔
253
                    return new Resolution.Explicit(binaryRepresentation);
1✔
254
                }
255
            }
256
            return new Resolution.Illegal(name);
1✔
257
        }
258

259
        /**
260
         * Resolves a possible multi-release entry, if it exists.
261
         *
262
         * @param path The path of the class file.
263
         * @return The class file's binary representation or {@code null} if it does not exist.
264
         * @throws IOException If an I/O exception occurs.
265
         */
266
        @MaybeNull
267
        protected abstract byte[] doLocate(String path) throws IOException;
268
    }
269

270
    /**
271
     * A simple class file locator that returns class files from a selection of given types.
272
     */
273
    @HashCodeAndEqualsPlugin.Enhance
274
    class Simple implements ClassFileLocator {
275

276
        /**
277
         * The class files that are known to this class file locator mapped by their type name.
278
         */
279
        private final Map<String, byte[]> classFiles;
280

281
        /**
282
         * Creates a new simple class file locator.
283
         *
284
         * @param classFiles The class files that are known to this class file locator mapped by their type name.
285
         */
286
        public Simple(Map<String, byte[]> classFiles) {
1✔
287
            this.classFiles = classFiles;
1✔
288
        }
1✔
289

290
        /**
291
         * Creates a class file locator for a single known type.
292
         *
293
         * @param typeName             The name of the type.
294
         * @param binaryRepresentation The binary representation of the type.
295
         * @return An appropriate class file locator.
296
         */
297
        public static ClassFileLocator of(String typeName, byte[] binaryRepresentation) {
298
            return new Simple(Collections.singletonMap(typeName, binaryRepresentation));
1✔
299
        }
300

301
        /**
302
         * Creates a class file locator that represents all types of a dynamic type.
303
         *
304
         * @param dynamicType The dynamic type to represent.
305
         * @return A class file locator representing the dynamic type's types.
306
         */
307
        public static ClassFileLocator of(DynamicType dynamicType) {
308
            return of(dynamicType.getAllTypes());
1✔
309
        }
310

311
        /**
312
         * Creates a class file locator that represents all types of a dynamic type.
313
         *
314
         * @param binaryRepresentations The binary representation of all types.
315
         * @return A class file locator representing the dynamic type's types.
316
         */
317
        public static ClassFileLocator of(Map<TypeDescription, byte[]> binaryRepresentations) {
318
            Map<String, byte[]> classFiles = new HashMap<String, byte[]>();
1✔
319
            for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
1✔
320
                classFiles.put(entry.getKey().getName(), entry.getValue());
1✔
321
            }
1✔
322
            return new Simple(classFiles);
1✔
323
        }
324

325
        /**
326
         * Creates a class file locator of a map of resources where class files are mapped by their path and file extension.
327
         *
328
         * @param binaryRepresentations A map of resource names to their binary representation.
329
         * @return A class file locator that finds class files within the map.
330
         */
331
        public static ClassFileLocator ofResources(Map<String, byte[]> binaryRepresentations) {
332
            Map<String, byte[]> classFiles = new HashMap<String, byte[]>();
1✔
333
            for (Map.Entry<String, byte[]> entry : binaryRepresentations.entrySet()) {
1✔
334
                if (entry.getKey().endsWith(CLASS_FILE_EXTENSION)) {
1✔
335
                    classFiles.put(entry.getKey().substring(0, entry.getKey().length() - CLASS_FILE_EXTENSION.length()).replace('/', '.'), entry.getValue());
1✔
336
                }
337
            }
1✔
338
            return new Simple(classFiles);
1✔
339
        }
340

341
        /**
342
         * {@inheritDoc}
343
         */
344
        public Resolution locate(String name) {
345
            byte[] binaryRepresentation = classFiles.get(name);
1✔
346
            return binaryRepresentation == null ? new Resolution.Illegal(name) : new Resolution.Explicit(binaryRepresentation);
1✔
347
        }
348

349
        /**
350
         * {@inheritDoc}
351
         */
352
        public void close() {
353
            /* do nothing */
354
        }
1✔
355
    }
356

357
    /**
358
     * <p>
359
     * A class file locator that queries a class loader for binary representations of class files.
360
     * </p>
361
     * <p>
362
     * <b>Important</b>: Even when calling {@link Closeable#close()} on this class file locator, no underlying
363
     * class loader is closed if it implements the {@link Closeable} interface as this is typically not intended.
364
     * </p>
365
     */
366
    @HashCodeAndEqualsPlugin.Enhance
367
    class ForClassLoader implements ClassFileLocator {
368

369
        /**
370
         * A class loader that does not define resources of its own but allows querying for resources supplied by the boot loader.
371
         */
372
        private static final ClassLoader BOOT_LOADER_PROXY = doPrivileged(BootLoaderProxyCreationAction.INSTANCE);
1✔
373

374
        /**
375
         * The class loader to query.
376
         */
377
        private final ClassLoader classLoader;
378

379
        /**
380
         * Creates a new class file locator for the given class loader.
381
         *
382
         * @param classLoader The class loader to query which must not be the bootstrap class loader, i.e. {@code null}.
383
         */
384
        protected ForClassLoader(ClassLoader classLoader) {
1✔
385
            this.classLoader = classLoader;
1✔
386
        }
1✔
387

388
        /**
389
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
390
         *
391
         * @param action The action to execute from a privileged context.
392
         * @param <T>    The type of the action's resolved value.
393
         * @return The action's resolved value.
394
         */
395
        @AccessControllerPlugin.Enhance
396
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
397
            return action.run();
×
398
        }
399

400
        /**
401
         * Creates a class file locator that queries the system class loader.
402
         *
403
         * @return A class file locator that queries the system class loader.
404
         */
405
        public static ClassFileLocator ofSystemLoader() {
406
            return new ForClassLoader(ClassLoader.getSystemClassLoader());
1✔
407
        }
408

409
        /**
410
         * Creates a class file locator that queries the plaform class loader or the extension class loader if the
411
         * current VM is not at least of version 9.
412
         *
413
         * @return A class file locator that queries the plaform class loader or the extension class loader.
414
         */
415
        public static ClassFileLocator ofPlatformLoader() {
416
            return of(ClassLoader.getSystemClassLoader().getParent());
1✔
417
        }
418

419
        /**
420
         * Creates a class file locator that queries the boot loader.
421
         *
422
         * @return A class file locator that queries the boot loader.
423
         */
424
        public static ClassFileLocator ofBootLoader() {
425
            return new ForClassLoader(BOOT_LOADER_PROXY);
1✔
426
        }
427

428
        /**
429
         * Creates a class file locator for a given class loader.
430
         *
431
         * @param classLoader The class loader to be used which might be {@code null} to represent the bootstrap loader.
432
         * @return A corresponding source locator.
433
         */
434
        public static ClassFileLocator of(@MaybeNull ClassLoader classLoader) {
435
            return new ForClassLoader(classLoader == null ? BOOT_LOADER_PROXY : classLoader);
1✔
436
        }
437

438
        /**
439
         * Attempts to create a binary representation of a loaded type by requesting data from its
440
         * {@link java.lang.ClassLoader}.
441
         *
442
         * @param type The type of interest.
443
         * @return The binary representation of the supplied type.
444
         */
445
        public static byte[] read(Class<?> type) {
446
            try {
447
                ClassLoader classLoader = type.getClassLoader();
1✔
448
                return locate(classLoader == null ? BOOT_LOADER_PROXY : classLoader, TypeDescription.ForLoadedType.getName(type)).resolve();
1✔
449
            } catch (IOException exception) {
×
450
                throw new IllegalStateException("Cannot read class file for " + type, exception);
×
451
            }
452
        }
453

454
        /**
455
         * Attempts to create a binary representation of several loaded types by requesting
456
         * data from their respective {@link java.lang.ClassLoader}s.
457
         *
458
         * @param type The types of interest.
459
         * @return A mapping of the supplied types to their binary representation.
460
         */
461
        public static Map<Class<?>, byte[]> read(Class<?>... type) {
462
            return read(Arrays.asList(type));
1✔
463
        }
464

465
        /**
466
         * Attempts to create a binary representation of several loaded types by requesting
467
         * data from their respective {@link java.lang.ClassLoader}s.
468
         *
469
         * @param types The types of interest.
470
         * @return A mapping of the supplied types to their binary representation.
471
         */
472
        public static Map<Class<?>, byte[]> read(Collection<? extends Class<?>> types) {
473
            Map<Class<?>, byte[]> result = new HashMap<Class<?>, byte[]>();
1✔
474
            for (Class<?> type : types) {
1✔
475
                result.put(type, read(type));
1✔
476
            }
1✔
477
            return result;
1✔
478
        }
479

480
        /**
481
         * Attempts to create a binary representation of several loaded types by requesting
482
         * data from their respective {@link java.lang.ClassLoader}s.
483
         *
484
         * @param type The types of interest.
485
         * @return A mapping of the supplied types' names to their binary representation.
486
         */
487
        public static Map<String, byte[]> readToNames(Class<?>... type) {
488
            return readToNames(Arrays.asList(type));
1✔
489
        }
490

491
        /**
492
         * Attempts to create a binary representation of several loaded types by requesting
493
         * data from their respective {@link java.lang.ClassLoader}s.
494
         *
495
         * @param types The types of interest.
496
         * @return A mapping of the supplied types' names to their binary representation.
497
         */
498
        public static Map<String, byte[]> readToNames(Collection<? extends Class<?>> types) {
499
            Map<String, byte[]> result = new HashMap<String, byte[]>();
1✔
500
            for (Class<?> type : types) {
1✔
501
                result.put(type.getName(), read(type));
1✔
502
            }
1✔
503
            return result;
1✔
504
        }
505

506
        /**
507
         * {@inheritDoc}
508
         */
509
        public Resolution locate(String name) throws IOException {
510
            return locate(classLoader, name);
1✔
511
        }
512

513
        /**
514
         * {@inheritDoc}
515
         */
516
        public void close() {
517
            /* do nothing */
518
        }
1✔
519

520
        /**
521
         * Locates the class file for the supplied type by requesting a resource from the class loader.
522
         *
523
         * @param classLoader The class loader to query.
524
         * @param name        The name of the type for which to locate a class file.
525
         * @return A resolution for the class file.
526
         * @throws IOException If reading the class file causes an exception.
527
         */
528
        protected static Resolution locate(ClassLoader classLoader, String name) throws IOException {
529
            InputStream inputStream = classLoader.getResourceAsStream(name.replace('.', '/') + CLASS_FILE_EXTENSION);
1✔
530
            if (inputStream != null) {
1✔
531
                try {
532
                    return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
1✔
533
                } finally {
534
                    inputStream.close();
1✔
535
                }
536
            } else {
537
                return new Resolution.Illegal(name);
1✔
538
            }
539
        }
540

541
        /**
542
         * A privileged action for creating a proxy class loader for the boot class loader.
543
         */
544
        protected enum BootLoaderProxyCreationAction implements PrivilegedAction<ClassLoader> {
1✔
545

546
            /**
547
             * The singleton instance.
548
             */
549
            INSTANCE;
1✔
550

551
            /**
552
             * {@inheritDoc}
553
             */
554
            public ClassLoader run() {
555
                return new URLClassLoader(new URL[0], ClassLoadingStrategy.BOOTSTRAP_LOADER);
1✔
556
            }
557
        }
558

559
        /**
560
         * <p>
561
         * A class file locator that queries a class loader for binary representations of class files.
562
         * The class loader is only weakly referenced.
563
         * </p>
564
         * <p>
565
         * <b>Important</b>: Even when calling {@link Closeable#close()} on this class file locator, no underlying
566
         * class loader is closed if it implements the {@link Closeable} interface as this is typically not intended.
567
         * </p>
568
         */
569
        public static class WeaklyReferenced extends WeakReference<ClassLoader> implements ClassFileLocator {
570

571
            /**
572
             * The represented class loader's hash code.
573
             */
574
            private final int hashCode;
575

576
            /**
577
             * Creates a class file locator for a class loader that is weakly referenced.
578
             *
579
             * @param classLoader The class loader to represent.
580
             */
581
            protected WeaklyReferenced(ClassLoader classLoader) {
582
                super(classLoader);
1✔
583
                hashCode = System.identityHashCode(classLoader);
1✔
584
            }
1✔
585

586
            /**
587
             * Creates a class file locator for a given class loader. If the class loader is not the bootstrap
588
             * class loader or the system class loader which cannot be collected, the class loader is only weakly
589
             * referenced.
590
             *
591
             * @param classLoader The class loader to be used. If this class loader represents the bootstrap class
592
             *                    loader which is represented by the {@code null} value, this system class loader
593
             *                    is used instead.
594
             * @return A corresponding source locator.
595
             */
596
            public static ClassFileLocator of(@MaybeNull ClassLoader classLoader) {
597
                return classLoader == null || classLoader == ClassLoader.getSystemClassLoader() || classLoader == ClassLoader.getSystemClassLoader().getParent() ? ForClassLoader.of(classLoader) : new WeaklyReferenced(classLoader);
1✔
598
            }
599

600
            /**
601
             * {@inheritDoc}
602
             */
603
            public Resolution locate(String name) throws IOException {
604
                ClassLoader classLoader = get();
1✔
605
                return classLoader == null ? new Resolution.Illegal(name) : ForClassLoader.locate(classLoader, name);
1✔
606
            }
607

608
            /**
609
             * {@inheritDoc}
610
             */
611
            public void close() {
612
                /* do nothing */
613
            }
1✔
614

615
            @Override
616
            public int hashCode() {
617
                return hashCode;
1✔
618
            }
619

620
            @Override
621
            public boolean equals(@MaybeNull Object other) {
622
                if (this == other) {
×
623
                    return true;
×
624
                } else if (other == null || getClass() != other.getClass()) {
×
625
                    return false;
×
626
                }
627
                WeaklyReferenced weaklyReferenced = (WeaklyReferenced) other;
×
628
                ClassLoader classLoader = weaklyReferenced.get();
×
629
                return classLoader != null && get() == classLoader;
×
630
            }
631
        }
632
    }
633

634
    /**
635
     * <p>
636
     * A class file locator that locates class files by querying a Java module's {@code getResourceAsStream} method.
637
     * </p>
638
     * <p>
639
     * <b>Important</b>: Even when calling {@link Closeable#close()} on this class file locator, no underlying
640
     * class loader is closed if it implements the {@link Closeable} interface as this is typically not intended.
641
     * </p>
642
     */
643
    @HashCodeAndEqualsPlugin.Enhance
644
    class ForModule implements ClassFileLocator {
645

646
        /**
647
         * An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
648
         */
649
        private static final Object[] NO_ARGUMENT = new Object[0];
1✔
650

651
        /**
652
         * The represented Java module.
653
         */
654
        private final JavaModule module;
655

656
        /**
657
         * Creates a new class file locator for a Java module.
658
         *
659
         * @param module The represented Java module.
660
         */
661
        protected ForModule(JavaModule module) {
1✔
662
            this.module = module;
1✔
663
        }
1✔
664

665
        /**
666
         * Returns a class file locator that exposes all class files of the boot module layer. This class file locator is only available
667
         * on virtual machines of version 9 or later. On earlier versions, the returned class file locator does not locate any resources.
668
         *
669
         * @return A class file locator that locates classes of the boot layer.
670
         */
671
        @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should always be wrapped for clarity")
672
        public static ClassFileLocator ofBootLayer() {
673
            try {
674
                Map<String, ClassFileLocator> bootModules = new HashMap<String, ClassFileLocator>();
×
675
                Class<?> layerType = Class.forName("java.lang.ModuleLayer");
×
676
                Method getPackages = JavaType.MODULE.load().getMethod("getPackages");
×
677
                for (Object rawModule : (Set<?>) layerType.getMethod("modules").invoke(layerType.getMethod("boot").invoke(null))) {
×
678
                    ClassFileLocator classFileLocator = ForModule.of(JavaModule.of(rawModule));
×
679
                    for (Object packageName : (Set<?>) getPackages.invoke(rawModule, NO_ARGUMENT)) {
×
680
                        bootModules.put((String) packageName, classFileLocator);
×
681
                    }
×
682
                }
×
683
                return new PackageDiscriminating(bootModules);
×
684
            } catch (Exception exception) {
×
685
                throw new IllegalStateException("Cannot process boot layer", exception);
×
686
            }
687
        }
688

689
        /**
690
         * Returns a class file locator for the provided module. If the provided module is not named, class files are located via this
691
         * unnamed module's class loader.
692
         *
693
         * @param module The module to create a class file locator for.
694
         * @return An appropriate class file locator.
695
         */
696
        public static ClassFileLocator of(JavaModule module) {
697
            return module.isNamed() ? new ForModule(module) : ForClassLoader.of(module.getClassLoader());
1✔
698
        }
699

700
        /**
701
         * {@inheritDoc}
702
         */
703
        public Resolution locate(String name) throws IOException {
704
            return locate(module, name);
1✔
705
        }
706

707
        /**
708
         * Creates a resolution for a Java module's class files.
709
         *
710
         * @param module   The Java module to query.
711
         * @param typeName The name of the type being queried.
712
         * @return A resolution for the query.
713
         * @throws IOException If an I/O exception was thrown.
714
         */
715
        protected static Resolution locate(JavaModule module, String typeName) throws IOException {
716
            InputStream inputStream = module.getResourceAsStream(typeName.replace('.', '/') + CLASS_FILE_EXTENSION);
1✔
717
            if (inputStream != null) {
1✔
718
                try {
719
                    return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
1✔
720
                } finally {
721
                    inputStream.close();
1✔
722
                }
723
            } else {
724
                return new Resolution.Illegal(typeName);
1✔
725
            }
726
        }
727

728
        /**
729
         * {@inheritDoc}
730
         */
731
        public void close() {
732
            /* do nothing */
733
        }
1✔
734

735
        /**
736
         * <p>
737
         * A class file locator for a Java module that only references this module weakly. If a module was garbage collected,
738
         * this class file locator only returns unresolved resolutions.
739
         * </p>
740
         * <p>
741
         * <b>Important</b>: Even when calling {@link Closeable#close()} on this class file locator, no underlying
742
         * class loader is closed if it implements the {@link Closeable} interface as this is typically not intended.
743
         * </p>
744
         */
745
        public static class WeaklyReferenced extends WeakReference<Object> implements ClassFileLocator {
746

747
            /**
748
             * The represented module's hash code.
749
             */
750
            private final int hashCode;
751

752
            /**
753
             * Creates a class file locator for a Java module that is weakly referenced.
754
             *
755
             * @param module The raw Java module to represent.
756
             */
757
            protected WeaklyReferenced(Object module) {
758
                super(module);
1✔
759
                hashCode = System.identityHashCode(module);
1✔
760
            }
1✔
761

762
            /**
763
             * Creates a class file locator for a Java module where the module is referenced weakly. If the module is not named, the module's class loader
764
             * is represented instead. Module's of the boot layer are not referenced weakly.
765
             *
766
             * @param module The Java module to represent.
767
             * @return A suitable class file locator.
768
             */
769
            public static ClassFileLocator of(JavaModule module) {
770
                if (module.isNamed()) {
1✔
771
                    return module.getClassLoader() == null || module.getClassLoader() == ClassLoader.getSystemClassLoader() || module.getClassLoader() == ClassLoader.getSystemClassLoader().getParent() ? new ForModule(module) : new WeaklyReferenced(module.unwrap());
1✔
772
                } else {
773
                    return ForClassLoader.WeaklyReferenced.of(module.getClassLoader());
1✔
774
                }
775
            }
776

777
            /**
778
             * {@inheritDoc}
779
             */
780
            public Resolution locate(String name) throws IOException {
781
                Object module = get();
×
782
                return module == null ? new Resolution.Illegal(name) : ForModule.locate(JavaModule.of(module), name);
×
783
            }
784

785
            /**
786
             * {@inheritDoc}
787
             */
788
            public void close() {
789
                /* do nothing */
790
            }
1✔
791

792
            /**
793
             * {@inheritDoc}
794
             */
795
            public int hashCode() {
796
                return hashCode;
1✔
797
            }
798

799
            /**
800
             * {@inheritDoc}
801
             */
802
            public boolean equals(@MaybeNull Object other) {
803
                if (this == other) {
×
804
                    return true;
×
805
                } else if (other == null || getClass() != other.getClass()) {
×
806
                    return false;
×
807
                }
808
                WeaklyReferenced weaklyReferenced = (WeaklyReferenced) other;
×
809
                Object module = weaklyReferenced.get();
×
810
                return module != null && get() == module;
×
811
            }
812
        }
813
    }
814

815
    /**
816
     * A class file locator that locates classes within a Java <i>jar</i> file.
817
     */
818
    @HashCodeAndEqualsPlugin.Enhance
819
    class ForJarFile extends MultiReleaseAware {
820

821
        /**
822
         * A list of potential locations of the runtime jar for different platforms.
823
         */
824
        private static final List<String> RUNTIME_LOCATIONS = Arrays.asList("lib/rt.jar", "../lib/rt.jar", "../Classes/classes.jar");
1✔
825

826
        /**
827
         * The jar file to read from.
828
         */
829
        private final JarFile jarFile;
830

831
        /**
832
         * Indicates if the jar file should be closed upon closing this class file locator.
833
         */
834
        @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.IGNORE)
835
        private final boolean close;
836

837
        /**
838
         * Creates a new class file locator for the given jar file. The jar file will not be closed
839
         * upon closing this class file locator.
840
         *
841
         * @param jarFile The jar file to read from.
842
         */
843
        public ForJarFile(JarFile jarFile) {
844
            this(NO_MULTI_RELEASE, jarFile, false);
1✔
845
        }
1✔
846

847
        /**
848
         * Creates a new class file locator for the given jar file.
849
         *
850
         * @param version Contains the existing multi-release jar folders that are available for the
851
         *                current JVM version in decreasing order.
852
         * @param jarFile The jar file to read from.
853
         * @param close   Indicates if the jar file should be closed upon closing this class file locator.
854
         */
855
        protected ForJarFile(int[] version, JarFile jarFile, boolean close) {
856
            super(version);
1✔
857
            this.jarFile = jarFile;
1✔
858
            this.close = close;
1✔
859
        }
1✔
860

861
        /**
862
         * Creates a new class file locator for the given jar file. Multi-release jars are not considered.
863
         *
864
         * @param file The jar file to read from.
865
         * @return A class file locator for the jar file.
866
         * @throws IOException If an I/O exception is thrown.
867
         */
868
        public static ClassFileLocator of(File file) throws IOException {
869
            return new ForJarFile(MultiReleaseAware.NO_MULTI_RELEASE, new JarFile(file, false, ZipFile.OPEN_READ), true);
1✔
870
        }
871

872
        /**
873
         * Creates a new class file locator for the given jar file. Multi-release jar files
874
         * are resolved as if executed on a JVM of the supplied version.
875
         *
876
         * @param file             The jar file to read from.
877
         * @param classFileVersion The class file version to consider when resolving class files in multi-release jars.
878
         * @return A class file locator for the jar file.
879
         * @throws IOException If an I/O exception is thrown.
880
         */
881
        public static ClassFileLocator of(File file, ClassFileVersion classFileVersion) throws IOException {
882
            return of(new JarFile(file, false, ZipFile.OPEN_READ), classFileVersion, true);
1✔
883
        }
884

885
        /**
886
         * Creates a new class file locator for the given jar file. Multi-release jar files
887
         * are resolved as if executed on a JVM of the supplied version. The jar file will not be closed
888
         * upon closing this class file locator.
889
         *
890
         * @param jarFile          The jar file to read from.
891
         * @param classFileVersion The class file version to consider when resolving class files in multi-release jars.
892
         * @return A class file locator for the jar file.
893
         * @throws IOException If an I/O exception is thrown.
894
         */
895
        public static ClassFileLocator of(JarFile jarFile, ClassFileVersion classFileVersion) throws IOException {
896
            return of(jarFile, classFileVersion, false);
×
897
        }
898

899
        /**
900
         * Creates a new class file locator for the given jar file. Multi-release jar files
901
         * are resolved as if executed on a JVM of the supplied version.
902
         *
903
         * @param jarFile          The jar file to read from.
904
         * @param classFileVersion The class file version to consider when resolving class files in multi-release jars.
905
         * @param close            Indicates if the jar file should be closed upon closing this class file locator.
906
         * @return A class file locator for the jar file.
907
         * @throws IOException If an I/O exception is thrown.
908
         */
909
        private static ClassFileLocator of(JarFile jarFile, ClassFileVersion classFileVersion, boolean close) throws IOException {
910
            if (classFileVersion.getJavaVersion() < 9) {
1✔
911
                return new ForJarFile(jarFile);
1✔
912
            } else {
913
                Manifest manifest = jarFile.getManifest();
1✔
914
                int[] version;
915
                if (manifest != null && Boolean.parseBoolean(manifest.getMainAttributes().getValue(MultiReleaseAware.MULTI_RELEASE_ATTRIBUTE))) {
1✔
916
                    SortedSet<Integer> versions = new TreeSet<Integer>();
1✔
917
                    Enumeration<JarEntry> enumeration = jarFile.entries();
1✔
918
                    while (enumeration.hasMoreElements()) {
1✔
919
                        String name = enumeration.nextElement().getName();
1✔
920
                        if (name.endsWith(CLASS_FILE_EXTENSION) && name.startsWith(META_INF_VERSIONS)) {
1✔
921
                            try {
922
                                int candidate = Integer.parseInt(name.substring(META_INF_VERSIONS.length(), name.indexOf('/', META_INF_VERSIONS.length())));
1✔
923
                                if (candidate > 7 && candidate <= classFileVersion.getJavaVersion()) {
1✔
924
                                    versions.add(candidate);
1✔
925
                                }
926
                            } catch (NumberFormatException ignored) {
×
927
                                /* do nothing */
928
                            }
1✔
929
                        }
930
                    }
1✔
931
                    version = new int[versions.size()];
1✔
932
                    Iterator<Integer> iterator = versions.iterator();
1✔
933
                    for (int index = 0; index < versions.size(); index++) {
1✔
934
                        version[versions.size() - index - 1] = iterator.next();
1✔
935
                    }
936
                } else {
1✔
937
                    version = MultiReleaseAware.NO_MULTI_RELEASE;
×
938
                }
939
                return new ForJarFile(version, jarFile, close);
1✔
940
            }
941
        }
942

943
        /**
944
         * Resolves a class file locator for the class path that reads class files directly from the file system. The resulting
945
         * class file locator does not imply classes on the boot path.
946
         *
947
         * @return A class file locator for the class path.
948
         * @throws IOException If an I/O exception occurs.
949
         */
950
        public static ClassFileLocator ofClassPath() throws IOException {
951
            return ofClassPath(System.getProperty("java.class.path"));
1✔
952
        }
953

954
        /**
955
         * <p>
956
         * Resolves a class file locator for the class path that reads class files directly from the file system.
957
         * </p>
958
         * <p>
959
         * <b>Note</b>: The resulting class file locator does not include classes of the bootstrap class loader.
960
         * </p>
961
         *
962
         * @param classPath The class path to scan with the elements separated by {@code path.separator}.
963
         * @return A class file locator for the class path.
964
         * @throws IOException If an I/O exception occurs.
965
         */
966
        public static ClassFileLocator ofClassPath(String classPath) throws IOException {
967
            ClassFileVersion classFileVersion = ClassFileVersion.ofThisVm();
1✔
968
            List<ClassFileLocator> classFileLocators = new ArrayList<ClassFileLocator>();
1✔
969
            for (String element : Pattern.compile(File.pathSeparator, Pattern.LITERAL).split(classPath)) {
1✔
970
                File file = new File(element);
1✔
971
                if (file.isDirectory()) {
1✔
972
                    classFileLocators.add(ForFolder.of(file, classFileVersion));
1✔
973
                } else if (file.isFile()) {
1✔
974
                    classFileLocators.add(of(file, classFileVersion));
1✔
975
                }
976
            }
977
            return new Compound(classFileLocators);
1✔
978
        }
979

980
        /**
981
         * Resolves a class file locator for the runtime jar. If such a file does not exist or cannot be located, a runtime exception is thrown.
982
         *
983
         * @return A class file locator for the runtime jar, if available.
984
         * @throws IOException If an I/O exception occurs.
985
         */
986
        public static ClassFileLocator ofRuntimeJar() throws IOException {
987
            String javaHome = System.getProperty("java.home").replace('\\', '/');
1✔
988
            File runtimeJar = null;
1✔
989
            for (String location : RUNTIME_LOCATIONS) {
1✔
990
                File candidate = new File(javaHome, location);
1✔
991
                if (candidate.isFile()) {
1✔
992
                    runtimeJar = candidate;
1✔
993
                    break;
1✔
994
                }
995
            }
×
996
            if (runtimeJar == null) {
1✔
997
                throw new IllegalStateException("Runtime jar does not exist in " + javaHome + " for any of " + RUNTIME_LOCATIONS);
×
998
            }
999
            return of(runtimeJar);
1✔
1000
        }
1001

1002
        /**
1003
         * {@inheritDoc}
1004
         */
1005
        @MaybeNull
1006
        @SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS", justification = "Null value indicates failed lookup.")
1007
        protected byte[] doLocate(String path) throws IOException {
1008
            ZipEntry zipEntry = jarFile.getEntry(path);
1✔
1009
            if (zipEntry == null) {
1✔
1010
                return null;
1✔
1011
            } else {
1012
                InputStream inputStream = jarFile.getInputStream(zipEntry);
1✔
1013
                try {
1014
                    return StreamDrainer.DEFAULT.drain(inputStream);
1✔
1015
                } finally {
1016
                    inputStream.close();
1✔
1017
                }
1018
            }
1019
        }
1020

1021
        /**
1022
         * {@inheritDoc}
1023
         */
1024
        public void close() throws IOException {
1025
            if (close) {
1✔
1026
                jarFile.close();
1✔
1027
            }
1028
        }
1✔
1029
    }
1030

1031
    /**
1032
     * A class file locator that locates classes within a Java <i>jmod</i> file. This class file locator should not be used
1033
     * for reading modular jar files for which {@link ForJarFile} is appropriate.
1034
     */
1035
    @HashCodeAndEqualsPlugin.Enhance
1036
    class ForModuleFile implements ClassFileLocator {
1037

1038
        /**
1039
         * The file extension of a modular Java package.
1040
         */
1041
        private static final String JMOD_FILE_EXTENSION = ".jmod";
1042

1043
        /**
1044
         * A list of potential locations of the boot path for different platforms.
1045
         */
1046
        private static final List<String> BOOT_LOCATIONS = Arrays.asList("jmods", "../jmods", "modules");
1✔
1047

1048
        /**
1049
         * The represented jmod file.
1050
         */
1051
        private final ZipFile zipFile;
1052

1053
        /**
1054
         * Creates a new class file locator for a jmod file.
1055
         *
1056
         * @param zipFile The represented jmod file.
1057
         */
1058
        public ForModuleFile(ZipFile zipFile) {
1✔
1059
            this.zipFile = zipFile;
1✔
1060
        }
1✔
1061

1062
        /**
1063
         * Creates a new class file locator for this VM's boot module path.
1064
         *
1065
         * @return A class file locator for this VM's boot module path.
1066
         * @throws IOException If an I/O error occurs.
1067
         */
1068
        public static ClassFileLocator ofBootPath() throws IOException {
1069
            String javaHome = System.getProperty("java.home").replace('\\', '/');
×
1070
            File bootPath = null;
×
1071
            for (String location : BOOT_LOCATIONS) {
×
1072
                File candidate = new File(javaHome, location);
×
1073
                if (candidate.isDirectory()) {
×
1074
                    bootPath = candidate;
×
1075
                    break;
×
1076
                }
1077
            }
×
1078
            if (bootPath == null) {
×
1079
                throw new IllegalStateException("Boot modules do not exist in " + javaHome + " for any of " + BOOT_LOCATIONS);
×
1080
            }
1081
            return ofBootPath(bootPath);
×
1082
        }
1083

1084
        /**
1085
         * Creates a new class file locator for a Java boot module path.
1086
         *
1087
         * @param bootPath The boot path folder.
1088
         * @return A class file locator for this VMs boot module path.
1089
         * @throws IOException If an I/O error occurs.
1090
         */
1091
        public static ClassFileLocator ofBootPath(File bootPath) throws IOException {
1092
            File[] module = bootPath.listFiles();
×
1093
            if (module == null) {
×
1094
                return NoOp.INSTANCE;
×
1095
            }
1096
            ClassFileVersion classFileVersion = ClassFileVersion.ofThisVm();
×
1097
            List<ClassFileLocator> classFileLocators = new ArrayList<ClassFileLocator>(module.length);
×
1098
            for (File aModule : module) {
×
1099
                if (aModule.isFile()) {
×
1100
                    classFileLocators.add(of(aModule));
×
1101
                } else if (aModule.isDirectory()) { // Relevant for locally built OpenJDK.
×
1102
                    classFileLocators.add(ForFolder.of(aModule, classFileVersion));
×
1103
                }
1104
            }
1105
            return new Compound(classFileLocators);
×
1106
        }
1107

1108
        /**
1109
         * <p>
1110
         * Resolves a class file locator for this VM's Java module path that reads class files directly from the file system.
1111
         * </p>
1112
         * <p>
1113
         * <b>Note</b>: The resulting class file locator does not include classes of the bootstrap class loader.
1114
         * </p>
1115
         *
1116
         * @return A class file locator for the class path.
1117
         * @throws IOException If an I/O exception occurs.
1118
         */
1119
        public static ClassFileLocator ofModulePath() throws IOException {
1120
            String modulePath = System.getProperty("jdk.module.path");
×
1121
            return modulePath == null ? NoOp.INSTANCE : ofModulePath(modulePath);
×
1122
        }
1123

1124
        /**
1125
         * <p>
1126
         * Resolves a class file locator for a Java module path that reads class files directly from the file system. All
1127
         * elements of the module path are resolved relative to this VM's {@code user.dir}.
1128
         * </p>
1129
         * <p>
1130
         * <b>Note</b>: The resulting class file locator does not include classes of the bootstrap class loader.
1131
         * </p>
1132
         *
1133
         * @param modulePath The module path to scan with the elements separated by {@code path.separator}.
1134
         * @return A class file locator for the class path.
1135
         * @throws IOException If an I/O exception occurs.
1136
         */
1137
        public static ClassFileLocator ofModulePath(String modulePath) throws IOException {
1138
            return ofModulePath(modulePath, System.getProperty("user.dir"));
×
1139
        }
1140

1141
        /**
1142
         * <p>
1143
         * Resolves a class file locator for a Java module path that reads class files directly from the file system.
1144
         * </p>
1145
         * <p>
1146
         * <b>Note</b>: The resulting class file locator does not include classes of the bootstrap class loader.
1147
         * </p>
1148
         *
1149
         * @param modulePath The module path to scan with the elements separated by {@code path.separator}.
1150
         * @param baseFolder The relative location of the elements on the module path.
1151
         * @return A class file locator for the class path.
1152
         * @throws IOException If an I/O exception occurs.
1153
         */
1154
        public static ClassFileLocator ofModulePath(String modulePath, String baseFolder) throws IOException {
1155
            ClassFileVersion classFileVersion = ClassFileVersion.ofThisVm();
×
1156
            List<ClassFileLocator> classFileLocators = new ArrayList<ClassFileLocator>();
×
1157
            for (String element : Pattern.compile(System.getProperty("path.separator"), Pattern.LITERAL).split(modulePath)) {
×
1158
                File file = new File(baseFolder, element);
×
1159
                if (file.isDirectory()) {
×
1160
                    File[] module = file.listFiles();
×
1161
                    if (module != null) {
×
1162
                        for (File aModule : module) {
×
1163
                            if (aModule.isDirectory()) {
×
1164
                                classFileLocators.add(ForFolder.of(aModule, classFileVersion));
×
1165
                            } else if (aModule.isFile()) {
×
1166
                                classFileLocators.add(aModule.getName().endsWith(JMOD_FILE_EXTENSION) ? of(aModule) : ForJarFile.of(aModule, classFileVersion));
×
1167
                            }
1168
                        }
1169
                    }
1170
                } else if (file.isFile()) {
×
1171
                    classFileLocators.add(file.getName().endsWith(JMOD_FILE_EXTENSION) ? of(file) : ForJarFile.of(file));
×
1172
                }
1173
            }
1174
            return new Compound(classFileLocators);
×
1175
        }
1176

1177
        /**
1178
         * Returns a class file locator for the given module file.
1179
         *
1180
         * @param file The module file.
1181
         * @return A class file locator for the given module
1182
         * @throws IOException If an I/O error occurs.
1183
         */
1184
        public static ClassFileLocator of(File file) throws IOException {
1185
            return new ForModuleFile(new ZipFile(file));
×
1186
        }
1187

1188
        /**
1189
         * {@inheritDoc}
1190
         */
1191
        public Resolution locate(String name) throws IOException {
1192
            ZipEntry zipEntry = zipFile.getEntry("classes/" + name.replace('.', '/') + CLASS_FILE_EXTENSION);
1✔
1193
            if (zipEntry == null) {
1✔
1194
                return new Resolution.Illegal(name);
1✔
1195
            } else {
1196
                InputStream inputStream = zipFile.getInputStream(zipEntry);
1✔
1197
                try {
1198
                    return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
1✔
1199
                } finally {
1200
                    inputStream.close();
1✔
1201
                }
1202
            }
1203
        }
1204

1205
        /**
1206
         * {@inheritDoc}
1207
         */
1208
        public void close() throws IOException {
1209
            zipFile.close();
1✔
1210
        }
1✔
1211
    }
1212

1213
    /**
1214
     * A class file locator that finds files from a standardized Java folder structure with
1215
     * folders donating packages and class files being saved as {@code <classname>.class} files
1216
     * within their package folder.
1217
     */
1218
    @HashCodeAndEqualsPlugin.Enhance
1219
    class ForFolder extends MultiReleaseAware {
1220

1221
        /**
1222
         * The base folder of the package structure.
1223
         */
1224
        private final File folder;
1225

1226
        /**
1227
         * Creates a new class file locator for a folder structure of class files.
1228
         *
1229
         * @param folder The base folder of the package structure.
1230
         */
1231
        public ForFolder(File folder) {
1232
            this(NO_MULTI_RELEASE, folder);
1✔
1233
        }
1✔
1234

1235
        /**
1236
         * Creates a new class file locator for a folder structure of class files.
1237
         *
1238
         * @param version Contains the existing multi-release jar folders that are available for the
1239
         *                current JVM version in decreasing order.
1240
         * @param folder  The base folder of the package structure.
1241
         */
1242
        protected ForFolder(int[] version, File folder) {
1243
            super(version);
1✔
1244
            this.folder = folder;
1✔
1245
        }
1✔
1246

1247
        /**
1248
         * Creates a new class file locator for a folder structure of class files. The created locator considers
1249
         * the provided class file version when resolving class files and if multiple versions are available
1250
         *
1251
         * @param folder           The base folder of the package structure.
1252
         * @param classFileVersion The class file version to consider for multi-release JAR files.
1253
         * @return An appropriate class file locator.
1254
         * @throws IOException If an I/O exception occurs.
1255
         */
1256
        public static ClassFileLocator of(File folder, ClassFileVersion classFileVersion) throws IOException {
1257
            if (classFileVersion.getJavaVersion() < 9) {
1✔
1258
                return new ForFolder(NO_MULTI_RELEASE, folder);
1✔
1259
            } else {
1260
                File manifest = new File(folder, JarFile.MANIFEST_NAME);
1✔
1261
                boolean multiRelease;
1262
                if (manifest.exists()) {
1✔
1263
                    InputStream inputStream = new FileInputStream(manifest);
1✔
1264
                    try {
1265
                        multiRelease = Boolean.parseBoolean(new Manifest(inputStream).getMainAttributes().getValue("Multi-Release"));
1✔
1266
                    } finally {
1267
                        inputStream.close();
1✔
1268
                    }
1269
                } else {
1✔
1270
                    multiRelease = false;
×
1271
                }
1272
                int[] version;
1273
                if (multiRelease) {
1✔
1274
                    File[] file = new File(folder, META_INF_VERSIONS).listFiles();
1✔
1275
                    if (file != null) {
1✔
1276
                        SortedSet<Integer> versions = new TreeSet<Integer>();
1✔
1277
                        for (int index = 0; index < file.length; index++) {
1✔
1278
                            try {
1279
                                int candidate = Integer.parseInt(file[index].getName());
1✔
1280
                                if (candidate > 7 && candidate <= classFileVersion.getJavaVersion()) {
1✔
1281
                                    versions.add(candidate);
1✔
1282
                                }
1283
                            } catch (NumberFormatException ignored) {
×
1284
                                /* do nothing */
1285
                            }
1✔
1286
                        }
1287
                        version = new int[versions.size()];
1✔
1288
                        Iterator<Integer> iterator = versions.iterator();
1✔
1289
                        for (int index = 0; index < versions.size(); index++) {
1✔
1290
                            version[versions.size() - index - 1] = iterator.next();
1✔
1291
                        }
1292
                    } else {
1✔
1293
                        version = NO_MULTI_RELEASE;
×
1294
                    }
1295
                } else {
1✔
1296
                    version = NO_MULTI_RELEASE;
×
1297
                }
1298
                return new ForFolder(version, folder);
1✔
1299
            }
1300
        }
1301

1302
        /**
1303
         * {@inheritDoc}
1304
         */
1305
        @MaybeNull
1306
        @SuppressFBWarnings(value = "PZLA_PREFER_ZERO_LENGTH_ARRAYS", justification = "Null value indicates failed lookup.")
1307
        protected byte[] doLocate(String path) throws IOException {
1308
            File file = new File(folder, path);
1✔
1309
            if (file.exists()) {
1✔
1310
                InputStream inputStream = new FileInputStream(file);
1✔
1311
                try {
1312
                    return StreamDrainer.DEFAULT.drain(inputStream);
1✔
1313
                } finally {
1314
                    inputStream.close();
1✔
1315
                }
1316
            } else {
1317
                return null;
1✔
1318
            }
1319
        }
1320

1321
        /**
1322
         * {@inheritDoc}
1323
         */
1324
        public void close() {
1325
            /* do nothing */
1326
        }
1✔
1327
    }
1328

1329
    /**
1330
     * A class file locator that reads class files from one or several URLs. The reading is accomplished via using an {@link URLClassLoader}.
1331
     * Doing so, boot loader resources might be located additionally to those found via the specified URLs.
1332
     */
1333
    @HashCodeAndEqualsPlugin.Enhance
1334
    class ForUrl implements ClassFileLocator {
1335

1336
        /**
1337
         * The class loader that delegates to the URLs.
1338
         */
1339
        private final ClassLoader classLoader;
1340

1341
        /**
1342
         * Creates a new class file locator for the given URLs.
1343
         *
1344
         * @param url The URLs to search for class files.
1345
         */
1346
        public ForUrl(URL... url) {
1✔
1347
            classLoader = doPrivileged(new ClassLoaderCreationAction(url));
1✔
1348
        }
1✔
1349

1350
        /**
1351
         * Creates a new class file locator for the given URLs.
1352
         *
1353
         * @param urls The URLs to search for class files.
1354
         */
1355
        public ForUrl(Collection<? extends URL> urls) {
1356
            this(urls.toArray(new URL[0]));
1✔
1357
        }
1✔
1358

1359
        /**
1360
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
1361
         *
1362
         * @param action The action to execute from a privileged context.
1363
         * @param <T>    The type of the action's resolved value.
1364
         * @return The action's resolved value.
1365
         */
1366
        @AccessControllerPlugin.Enhance
1367
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
1368
            return action.run();
×
1369
        }
1370

1371
        /**
1372
         * {@inheritDoc}
1373
         */
1374
        public Resolution locate(String name) throws IOException {
1375
            return ForClassLoader.locate(classLoader, name);
1✔
1376
        }
1377

1378
        /**
1379
         * {@inheritDoc}
1380
         */
1381
        public void close() throws IOException {
1382
            if (classLoader instanceof Closeable) {
1✔
1383
                ((Closeable) classLoader).close();
1✔
1384
            }
1385
        }
1✔
1386

1387
        /**
1388
         * An action to create a class loader with the purpose of locating classes from an URL location.
1389
         */
1390
        @HashCodeAndEqualsPlugin.Enhance
1391
        protected static class ClassLoaderCreationAction implements PrivilegedAction<ClassLoader> {
1392

1393
            /**
1394
             * The URLs to locate classes from.
1395
             */
1396
            private final URL[] url;
1397

1398
            /**
1399
             * Creates a new class loader creation action.
1400
             *
1401
             * @param url The URLs to locate classes from.
1402
             */
1403
            protected ClassLoaderCreationAction(URL[] url) {
1✔
1404
                this.url = url;
1✔
1405
            }
1✔
1406

1407
            /**
1408
             * {@inheritDoc}
1409
             */
1410
            public ClassLoader run() {
1411
                return new URLClassLoader(url, ClassLoadingStrategy.BOOTSTRAP_LOADER);
1✔
1412
            }
1413
        }
1414
    }
1415

1416
    /**
1417
     * A Java agent that allows the location of class files by emulating a retransformation. Note that this class file
1418
     * locator causes a class to be loaded in order to look up its class file. Also, this locator does deliberately not
1419
     * support the look-up of classes that represent lambda expressions.
1420
     */
1421
    @HashCodeAndEqualsPlugin.Enhance
1422
    class ForInstrumentation implements ClassFileLocator {
1423

1424
        /**
1425
         * A dispatcher for interacting with the instrumentation API.
1426
         */
1427
        private static final Dispatcher DISPATCHER = doPrivileged(JavaDispatcher.of(Dispatcher.class));
1✔
1428

1429
        /**
1430
         * The instrumentation instance to use for looking up the binary format of a type.
1431
         */
1432
        private final Instrumentation instrumentation;
1433

1434
        /**
1435
         * The delegate to load a class by its name.
1436
         */
1437
        private final ClassLoadingDelegate classLoadingDelegate;
1438

1439
        /**
1440
         * Creates an agent-based class file locator.
1441
         *
1442
         * @param instrumentation The instrumentation to be used.
1443
         * @param classLoader     The class loader to read a class from or {@code null} to use the boot loader.
1444
         */
1445
        public ForInstrumentation(Instrumentation instrumentation, @MaybeNull ClassLoader classLoader) {
1446
            this(instrumentation, ClassLoadingDelegate.Default.of(classLoader));
1✔
1447
        }
1✔
1448

1449
        /**
1450
         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
1451
         *
1452
         * @param action The action to execute from a privileged context.
1453
         * @param <T>    The type of the action's resolved value.
1454
         * @return The action's resolved value.
1455
         */
1456
        @AccessControllerPlugin.Enhance
1457
        private static <T> T doPrivileged(PrivilegedAction<T> action) {
1458
            return action.run();
×
1459
        }
1460

1461
        /**
1462
         * Creates an agent-based class file locator.
1463
         *
1464
         * @param instrumentation      The instrumentation to be used.
1465
         * @param classLoadingDelegate The delegate responsible for class loading.
1466
         */
1467
        public ForInstrumentation(Instrumentation instrumentation, ClassLoadingDelegate classLoadingDelegate) {
1✔
1468
            if (!DISPATCHER.isRetransformClassesSupported(instrumentation)) {
1✔
1469
                throw new IllegalArgumentException(instrumentation + " does not support retransformation");
1✔
1470
            }
1471
            this.instrumentation = instrumentation;
1✔
1472
            this.classLoadingDelegate = classLoadingDelegate;
1✔
1473
        }
1✔
1474

1475
        /**
1476
         * Resolves the instrumentation provided by {@code net.bytebuddy.agent.Installer}.
1477
         *
1478
         * @return The installed instrumentation instance.
1479
         */
1480
        private static Instrumentation resolveByteBuddyAgentInstrumentation() {
1481
            try {
1482
                Class<?> installer = ClassLoader.getSystemClassLoader().loadClass("net.bytebuddy.agent.Installer");
1✔
1483
                JavaModule source = JavaModule.ofType(AgentBuilder.class), target = JavaModule.ofType(installer);
1✔
1484
                if (source != null && !source.canRead(target)) {
1✔
1485
                    Class<?> module = Class.forName("java.lang.Module");
×
1486
                    module.getMethod("addReads", module).invoke(source.unwrap(), target.unwrap());
×
1487
                }
1488
                return (Instrumentation) installer.getMethod("getInstrumentation").invoke(null);
1✔
1489
            } catch (RuntimeException exception) {
×
1490
                throw exception;
×
1491
            } catch (Exception exception) {
×
1492
                throw new IllegalStateException("The Byte Buddy agent is not installed or not accessible", exception);
×
1493
            }
1494
        }
1495

1496
        /**
1497
         * Returns an agent-based class file locator for the given class loader and an already installed
1498
         * Byte Buddy-agent.
1499
         *
1500
         * @param classLoader The class loader that is expected to load the looked-up a class.
1501
         * @return A class file locator for the given class loader based on a Byte Buddy agent.
1502
         */
1503
        public static ClassFileLocator fromInstalledAgent(@MaybeNull ClassLoader classLoader) {
1504
            return new ForInstrumentation(resolveByteBuddyAgentInstrumentation(), classLoader);
1✔
1505
        }
1506

1507
        /**
1508
         * Returns a class file locator that is capable of locating a class file for the given type using the given instrumentation instance.
1509
         *
1510
         * @param instrumentation The instrumentation instance to query for a retransformation.
1511
         * @param type            The locatable type which class loader is used as a fallback.
1512
         * @return A class file locator for locating the class file of the given type.
1513
         */
1514
        public static ClassFileLocator of(Instrumentation instrumentation, Class<?> type) {
1515
            return new ForInstrumentation(instrumentation, ClassLoadingDelegate.Explicit.of(type));
1✔
1516
        }
1517

1518
        /**
1519
         * {@inheritDoc}
1520
         */
1521
        public Resolution locate(String name) {
1522
            try {
1523
                ExtractionClassFileTransformer classFileTransformer = new ExtractionClassFileTransformer(classLoadingDelegate.getClassLoader(), name);
1✔
1524
                DISPATCHER.addTransformer(instrumentation, classFileTransformer, true);
1✔
1525
                try {
1526
                    DISPATCHER.retransformClasses(instrumentation, new Class<?>[]{classLoadingDelegate.locate(name)});
1✔
1527
                    byte[] binaryRepresentation = classFileTransformer.getBinaryRepresentation();
1✔
1528
                    return binaryRepresentation == null ? new Resolution.Illegal(name) : new Resolution.Explicit(binaryRepresentation);
1✔
1529
                } finally {
1530
                    instrumentation.removeTransformer(classFileTransformer);
1✔
1531
                }
1532
            } catch (RuntimeException exception) {
×
1533
                throw exception;
×
1534
            } catch (Exception ignored) {
×
1535
                return new Resolution.Illegal(name);
×
1536
            }
1537
        }
1538

1539
        /**
1540
         * {@inheritDoc}
1541
         */
1542
        public void close() {
1543
            /* do nothing */
1544
        }
×
1545

1546
        /**
1547
         * A dispatcher to interact with the {@link Instrumentation} API.
1548
         */
1549
        @JavaDispatcher.Proxied("java.lang.instrument.Instrumentation")
1550
        protected interface Dispatcher {
1551

1552
            /**
1553
             * Invokes the {@code Instrumentation#isRetransformClassesSupported} method.
1554
             *
1555
             * @param instrumentation The instrumentation instance to invoke the method on.
1556
             * @return {@code true} if the supplied instrumentation instance supports retransformation.
1557
             */
1558
            boolean isRetransformClassesSupported(Instrumentation instrumentation);
1559

1560
            /**
1561
             * Registers a transformer.
1562
             *
1563
             * @param instrumentation      The instrumentation instance to invoke the method on.
1564
             * @param classFileTransformer The class file transformer to register.
1565
             * @param canRetransform       {@code true} if the class file transformer should be invoked upon a retransformation.
1566
             */
1567
            void addTransformer(Instrumentation instrumentation, ClassFileTransformer classFileTransformer, boolean canRetransform);
1568

1569
            /**
1570
             * Retransforms the supplied classes.
1571
             *
1572
             * @param instrumentation The instrumentation instance to invoke the method on.
1573
             * @param type            The types to retransform.
1574
             * @throws UnmodifiableClassException If any of the supplied types are unmodifiable.
1575
             */
1576
            void retransformClasses(Instrumentation instrumentation, Class<?>[] type) throws UnmodifiableClassException;
1577
        }
1578

1579
        /**
1580
         * A delegate that is queried for loading a class.
1581
         */
1582
        public interface ClassLoadingDelegate {
1583

1584
            /**
1585
             * Loads a class by its name.
1586
             *
1587
             * @param name The name of the type.
1588
             * @return The class with the given name.
1589
             * @throws ClassNotFoundException If a class cannot be found.
1590
             */
1591
            Class<?> locate(String name) throws ClassNotFoundException;
1592

1593
            /**
1594
             * Returns the underlying class loader.
1595
             *
1596
             * @return The underlying class loader.
1597
             */
1598
            @MaybeNull
1599
            ClassLoader getClassLoader();
1600

1601
            /**
1602
             * A default implementation of a class loading delegate.
1603
             */
1604
            @HashCodeAndEqualsPlugin.Enhance
1605
            class Default implements ClassLoadingDelegate {
1606

1607
                /**
1608
                 * A class loader that does not define resources of its own but allows querying for resources supplied by the boot loader.
1609
                 */
1610
                private static final ClassLoader BOOT_LOADER_PROXY = doPrivileged(BootLoaderProxyCreationAction.INSTANCE);
1✔
1611

1612
                /**
1613
                 * The underlying class loader.
1614
                 */
1615
                protected final ClassLoader classLoader;
1616

1617
                /**
1618
                 * Creates a default class loading delegate.
1619
                 *
1620
                 * @param classLoader The class loader to be queried.
1621
                 */
1622
                protected Default(ClassLoader classLoader) {
1✔
1623
                    this.classLoader = classLoader;
1✔
1624
                }
1✔
1625

1626
                /**
1627
                 * Creates a class loading delegate for the given class loader.
1628
                 *
1629
                 * @param classLoader The class loader for which to create a delegate or {@code null} to use the boot loader.
1630
                 * @return The class loading delegate for the provided class loader.
1631
                 */
1632
                public static ClassLoadingDelegate of(@MaybeNull ClassLoader classLoader) {
1633
                    return ForDelegatingClassLoader.isDelegating(classLoader) ? new ForDelegatingClassLoader(classLoader) : new Default(classLoader == null ? BOOT_LOADER_PROXY : classLoader);
1✔
1634
                }
1635

1636
                /**
1637
                 * {@inheritDoc}
1638
                 */
1639
                public Class<?> locate(String name) throws ClassNotFoundException {
1640
                    return classLoader.loadClass(name);
1✔
1641
                }
1642

1643
                /**
1644
                 * {@inheritDoc}
1645
                 */
1646
                @MaybeNull
1647
                public ClassLoader getClassLoader() {
1648
                    return classLoader == BOOT_LOADER_PROXY ? ClassLoadingStrategy.BOOTSTRAP_LOADER : classLoader;
1✔
1649
                }
1650

1651
                /**
1652
                 * A privileged action for creating a proxy class loader for the boot class loader.
1653
                 */
1654
                protected enum BootLoaderProxyCreationAction implements PrivilegedAction<ClassLoader> {
1✔
1655

1656
                    /**
1657
                     * The singleton instance.
1658
                     */
1659
                    INSTANCE;
1✔
1660

1661
                    /**
1662
                     * {@inheritDoc}
1663
                     */
1664
                    public ClassLoader run() {
1665
                        return new URLClassLoader(new URL[0], ClassLoadingStrategy.BOOTSTRAP_LOADER);
1✔
1666
                    }
1667
                }
1668
            }
1669

1670
            /**
1671
             * A class loading delegate that accounts for a {@code sun.reflect.DelegatingClassLoader} which
1672
             * cannot load its own classes by name.
1673
             */
1674
            class ForDelegatingClassLoader extends Default {
1675

1676
                /**
1677
                 * The name of the delegating class loader.
1678
                 */
1679
                private static final String DELEGATING_CLASS_LOADER_NAME = "sun.reflect.DelegatingClassLoader";
1680

1681
                /**
1682
                 * An index indicating the first element of a collection.
1683
                 */
1684
                private static final int ONLY = 0;
1685

1686
                /**
1687
                 * A dispatcher for extracting a class loader's loaded classes.
1688
                 */
1689
                private static final Dispatcher.Initializable DISPATCHER = doPrivileged(Dispatcher.CreationAction.INSTANCE);
1✔
1690

1691
                /**
1692
                 * Creates a class loading delegate for a delegating class loader.
1693
                 *
1694
                 * @param classLoader The delegating class loader.
1695
                 */
1696
                protected ForDelegatingClassLoader(ClassLoader classLoader) {
1697
                    super(classLoader);
1✔
1698
                }
1✔
1699

1700
                /**
1701
                 * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
1702
                 *
1703
                 * @param action The action to execute from a privileged context.
1704
                 * @param <T>    The type of the action's resolved value.
1705
                 * @return The action's resolved value.
1706
                 */
1707
                @AccessControllerPlugin.Enhance
1708
                private static <T> T doPrivileged(PrivilegedAction<T> action) {
1709
                    return action.run();
×
1710
                }
1711

1712
                /**
1713
                 * Checks if a class loader is a delegating class loader.
1714
                 *
1715
                 * @param classLoader The class loader to inspect or {@code null} to check the boot loader.
1716
                 * @return {@code true} if the class loader is a delegating class loader.
1717
                 */
1718
                protected static boolean isDelegating(@MaybeNull ClassLoader classLoader) {
1719
                    return classLoader != null && classLoader.getClass().getName().equals(DELEGATING_CLASS_LOADER_NAME);
1✔
1720
                }
1721

1722
                /**
1723
                 * {@inheritDoc}
1724
                 */
1725
                public Class<?> locate(String name) throws ClassNotFoundException {
1726
                    Vector<Class<?>> classes;
1727
                    try {
1728
                        classes = DISPATCHER.initialize().extract(classLoader);
1✔
1729
                    } catch (RuntimeException ignored) {
×
1730
                        return super.locate(name);
×
1731
                    }
1✔
1732
                    if (classes.size() != 1) {
1✔
1733
                        return super.locate(name);
×
1734
                    }
1735
                    Class<?> type = classes.get(ONLY);
1✔
1736
                    return TypeDescription.ForLoadedType.getName(type).equals(name) ? type : super.locate(name);
1✔
1737
                }
1738

1739
                /**
1740
                 * Representation of a Java {@link java.lang.reflect.Field}.
1741
                 */
1742
                protected interface Dispatcher {
1743

1744
                    /**
1745
                     * Reads the classes of the represented collection.
1746
                     *
1747
                     * @param classLoader The class loader to read from.
1748
                     * @return The class loader's loaded classes.
1749
                     */
1750
                    Vector<Class<?>> extract(ClassLoader classLoader);
1751

1752
                    /**
1753
                     * An uninitialized version of a dispatcher for extracting a class loader's loaded classes.
1754
                     */
1755
                    interface Initializable {
1756

1757
                        /**
1758
                         * Initializes the dispatcher.
1759
                         *
1760
                         * @return An initialized dispatcher.
1761
                         */
1762
                        Dispatcher initialize();
1763
                    }
1764

1765
                    /**
1766
                     * An action for creating a dispatcher.
1767
                     */
1768
                    enum CreationAction implements PrivilegedAction<Initializable> {
1✔
1769

1770
                        /**
1771
                         * The singleton instance.
1772
                         */
1773
                        INSTANCE;
1✔
1774

1775
                        /**
1776
                         * {@inheritDoc}
1777
                         */
1778
                        public Initializable run() {
1779
                            try {
1780
                                return new Dispatcher.Resolved(ClassLoader.class.getDeclaredField("classes"));
1✔
1781
                            } catch (Exception exception) {
×
1782
                                return new Dispatcher.Unresolved(exception.getMessage());
×
1783
                            }
1784
                        }
1785
                    }
1786

1787
                    /**
1788
                     * Represents a field that could be located.
1789
                     */
1790
                    @HashCodeAndEqualsPlugin.Enhance
1791
                    class Resolved implements Dispatcher, Initializable, PrivilegedAction<Dispatcher> {
1792

1793
                        /**
1794
                         * The represented field.
1795
                         */
1796
                        private final Field field;
1797

1798
                        /**
1799
                         * Creates a new resolved field.
1800
                         *
1801
                         * @param field the represented field.l
1802
                         */
1803
                        public Resolved(Field field) {
1✔
1804
                            this.field = field;
1✔
1805
                        }
1✔
1806

1807
                        /**
1808
                         * A proxy for {@code java.security.AccessController#doPrivileged} that is activated if available.
1809
                         *
1810
                         * @param action The action to execute from a privileged context.
1811
                         * @param <T>    The type of the action's resolved value.
1812
                         * @return The action's resolved value.
1813
                         */
1814
                        @AccessControllerPlugin.Enhance
1815
                        private static <T> T doPrivileged(PrivilegedAction<T> action) {
1816
                            return action.run();
×
1817
                        }
1818

1819
                        /**
1820
                         * {@inheritDoc}
1821
                         */
1822
                        public Dispatcher initialize() {
1823
                            return doPrivileged(this);
1✔
1824
                        }
1825

1826
                        /**
1827
                         * {@inheritDoc}
1828
                         */
1829
                        @SuppressWarnings("unchecked")
1830
                        public Vector<Class<?>> extract(ClassLoader classLoader) {
1831
                            try {
1832
                                return (Vector<Class<?>>) field.get(classLoader);
1✔
1833
                            } catch (IllegalAccessException exception) {
×
1834
                                throw new IllegalStateException("Cannot access field", exception);
×
1835
                            }
1836
                        }
1837

1838
                        /**
1839
                         * {@inheritDoc}
1840
                         */
1841
                        public Dispatcher run() {
1842
                            field.setAccessible(true);
1✔
1843
                            return this;
1✔
1844
                        }
1845
                    }
1846

1847
                    /**
1848
                     * Represents a field that could not be located.
1849
                     */
1850
                    @HashCodeAndEqualsPlugin.Enhance
1851
                    class Unresolved implements Initializable {
1852

1853
                        /**
1854
                         * The reason why this dispatcher is unavailable.
1855
                         */
1856
                        private final String message;
1857

1858
                        /**
1859
                         * Creates a representation of a non-resolved field.
1860
                         *
1861
                         * @param message The reason why this dispatcher is unavailable.
1862
                         */
1863
                        public Unresolved(String message) {
×
1864
                            this.message = message;
×
1865
                        }
×
1866

1867
                        /**
1868
                         * {@inheritDoc}
1869
                         */
1870
                        public Dispatcher initialize() {
1871
                            throw new UnsupportedOperationException("Could not locate classes vector: " + message);
×
1872
                        }
1873
                    }
1874
                }
1875
            }
1876

1877
            /**
1878
             * A class loading delegate that allows the location of explicitly registered classes that cannot
1879
             * be located by a class loader directly. This allows for locating classes that are loaded by
1880
             * an anonymous class loader which does not register its classes in a system dictionary.
1881
             */
1882
            @HashCodeAndEqualsPlugin.Enhance
1883
            class Explicit implements ClassLoadingDelegate {
1884

1885
                /**
1886
                 * A class loading delegate that is queried for classes that are not registered explicitly.
1887
                 */
1888
                private final ClassLoadingDelegate fallbackDelegate;
1889

1890
                /**
1891
                 * The map of registered classes mapped by their name.
1892
                 */
1893
                private final Map<String, Class<?>> types;
1894

1895
                /**
1896
                 * Creates a new class loading delegate with a possibility of looking up explicitly
1897
                 * registered classes.
1898
                 *
1899
                 * @param classLoader The class loader to be used for looking up classes.
1900
                 * @param types       A collection of classes that cannot be looked up explicitly.
1901
                 */
1902
                public Explicit(@MaybeNull ClassLoader classLoader, Collection<? extends Class<?>> types) {
1903
                    this(Default.of(classLoader), types);
1✔
1904
                }
1✔
1905

1906
                /**
1907
                 * Creates a new class loading delegate with a possibility of looking up explicitly
1908
                 * registered classes.
1909
                 *
1910
                 * @param fallbackDelegate The class loading delegate to query for any class that is not
1911
                 *                         registered explicitly.
1912
                 * @param types            A collection of classes that cannot be looked up explicitly.
1913
                 */
1914
                public Explicit(ClassLoadingDelegate fallbackDelegate, Collection<? extends Class<?>> types) {
1✔
1915
                    this.fallbackDelegate = fallbackDelegate;
1✔
1916
                    this.types = new HashMap<String, Class<?>>();
1✔
1917
                    for (Class<?> type : types) {
1✔
1918
                        this.types.put(TypeDescription.ForLoadedType.getName(type), type);
1✔
1919
                    }
1✔
1920
                }
1✔
1921

1922
                /**
1923
                 * Creates an explicit class loading delegate for the given type.
1924
                 *
1925
                 * @param type The type that is explicitly locatable.
1926
                 * @return A suitable class loading delegate.
1927
                 */
1928
                public static ClassLoadingDelegate of(Class<?> type) {
1929
                    return new Explicit(type.getClassLoader(), Collections.singleton(type));
1✔
1930
                }
1931

1932
                /**
1933
                 * {@inheritDoc}
1934
                 */
1935
                public Class<?> locate(String name) throws ClassNotFoundException {
1936
                    Class<?> type = types.get(name);
1✔
1937
                    return type == null ? fallbackDelegate.locate(name) : type;
1✔
1938
                }
1939

1940
                /**
1941
                 * {@inheritDoc}
1942
                 */
1943
                @MaybeNull
1944
                public ClassLoader getClassLoader() {
1945
                    return fallbackDelegate.getClassLoader();
1✔
1946
                }
1947
            }
1948
        }
1949

1950
        /**
1951
         * A non-operational class file transformer that remembers the binary format of a given class.
1952
         */
1953
        protected static class ExtractionClassFileTransformer implements ClassFileTransformer {
1954

1955
            /**
1956
             * An indicator that an attempted class file transformation did not alter the handed class file.
1957
             */
1958
            @AlwaysNull
1959
            private static final byte[] DO_NOT_TRANSFORM = null;
1✔
1960

1961
            /**
1962
             * The class loader that is expected to have loaded the looked-up a class.
1963
             */
1964
            @MaybeNull
1965
            private final ClassLoader classLoader;
1966

1967
            /**
1968
             * The name of the type to look up.
1969
             */
1970
            private final String typeName;
1971

1972
            /**
1973
             * The binary representation of the looked-up class.
1974
             */
1975
            @MaybeNull
1976
            @SuppressFBWarnings(value = "VO_VOLATILE_REFERENCE_TO_ARRAY", justification = "The array is not to be modified by contract")
1977
            private volatile byte[] binaryRepresentation;
1978

1979
            /**
1980
             * Creates a class file transformer for the purpose of extraction.
1981
             *
1982
             * @param classLoader The class loader that is expected to have loaded the looked-up a class.
1983
             * @param typeName    The name of the type to look up.
1984
             */
1985
            protected ExtractionClassFileTransformer(@MaybeNull ClassLoader classLoader, String typeName) {
1✔
1986
                this.classLoader = classLoader;
1✔
1987
                this.typeName = typeName;
1✔
1988
            }
1✔
1989

1990
            /**
1991
             * {@inheritDoc}
1992
             */
1993
            @MaybeNull
1994
            @SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"}, justification = "The array is not modified by class contract.")
1995
            public byte[] transform(@MaybeNull ClassLoader classLoader,
1996
                                    @MaybeNull String internalName,
1997
                                    @MaybeNull Class<?> redefinedType,
1998
                                    @MaybeNull ProtectionDomain protectionDomain,
1999
                                    byte[] binaryRepresentation) {
2000
                if (internalName != null && isChildOf(this.classLoader).matches(classLoader) && typeName.equals(internalName.replace('/', '.'))) {
1✔
2001
                    this.binaryRepresentation = binaryRepresentation.clone();
1✔
2002
                }
2003
                return DO_NOT_TRANSFORM;
1✔
2004
            }
2005

2006
            /**
2007
             * Returns the binary representation of the class file that was looked up. The returned array must never be modified.
2008
             *
2009
             * @return The binary representation of the class file or {@code null} if no such class file could
2010
             * be located.
2011
             */
2012
            @MaybeNull
2013
            @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "The array is not to be modified by contract")
2014
            protected byte[] getBinaryRepresentation() {
2015
                return binaryRepresentation;
1✔
2016
            }
2017
        }
2018
    }
2019

2020
    /**
2021
     * A class file locator that discriminates by a type's package.
2022
     */
2023
    @HashCodeAndEqualsPlugin.Enhance
2024
    class PackageDiscriminating implements ClassFileLocator {
2025

2026
        /**
2027
         * A mapping of package names to class file locators.
2028
         */
2029
        private final Map<String, ClassFileLocator> classFileLocators;
2030

2031
        /**
2032
         * Creates a new package-discriminating class file locator.
2033
         *
2034
         * @param classFileLocators A mapping of package names to class file locators where an empty string donates the default package.
2035
         */
2036
        public PackageDiscriminating(Map<String, ClassFileLocator> classFileLocators) {
1✔
2037
            this.classFileLocators = classFileLocators;
1✔
2038
        }
1✔
2039

2040
        /**
2041
         * {@inheritDoc}
2042
         */
2043
        public Resolution locate(String name) throws IOException {
2044
            int packageIndex = name.lastIndexOf('.');
1✔
2045
            ClassFileLocator classFileLocator = classFileLocators.get(packageIndex == -1 ? NamedElement.EMPTY_NAME : name.substring(0, packageIndex));
1✔
2046
            return classFileLocator == null ? new Resolution.Illegal(name) : classFileLocator.locate(name);
1✔
2047
        }
2048

2049
        /**
2050
         * {@inheritDoc}
2051
         */
2052
        public void close() throws IOException {
2053
            for (ClassFileLocator classFileLocator : classFileLocators.values()) {
1✔
2054
                classFileLocator.close();
1✔
2055
            }
1✔
2056
        }
1✔
2057
    }
2058

2059
    /**
2060
     * A class file locator that only applies for matched names.
2061
     */
2062
    @HashCodeAndEqualsPlugin.Enhance
2063
    class Filtering implements ClassFileLocator {
2064

2065
        /**
2066
         * The matcher to determine if the delegate matcher is considered.
2067
         */
2068
        private final ElementMatcher<? super String> matcher;
2069

2070
        /**
2071
         * The delegate class file locator.
2072
         */
2073
        private final ClassFileLocator delegate;
2074

2075
        /**
2076
         * Creates a new filtering class file locator.
2077
         *
2078
         * @param matcher  The matcher to determine if the delegate matcher is considered.
2079
         * @param delegate The delegate class file locator.
2080
         */
2081
        public Filtering(ElementMatcher<? super String> matcher, ClassFileLocator delegate) {
1✔
2082
            this.matcher = matcher;
1✔
2083
            this.delegate = delegate;
1✔
2084
        }
1✔
2085

2086
        /**
2087
         * {@inheritDoc}
2088
         */
2089
        public Resolution locate(String name) throws IOException {
2090
            return matcher.matches(name) ? delegate.locate(name) : new Resolution.Illegal(name);
1✔
2091
        }
2092

2093
        /**
2094
         * {@inheritDoc}
2095
         */
2096
        public void close() throws IOException {
2097
            delegate.close();
1✔
2098
        }
1✔
2099
    }
2100

2101
    /**
2102
     * A compound {@link ClassFileLocator} that chains several locators.
2103
     * Any class file locator is queried in the supplied order until one locator is able to provide an input
2104
     * stream of the class file.
2105
     */
2106
    @HashCodeAndEqualsPlugin.Enhance
2107
    class Compound implements ClassFileLocator, Closeable {
2108

2109
        /**
2110
         * The {@link ClassFileLocator}s which are represented by this compound
2111
         * class file locator  in the order of their application.
2112
         */
2113
        private final List<ClassFileLocator> classFileLocators;
2114

2115
        /**
2116
         * Creates a new compound class file locator.
2117
         *
2118
         * @param classFileLocator The {@link ClassFileLocator}s to be
2119
         *                         represented by this compound class file locator in the order of their application.
2120
         */
2121
        public Compound(ClassFileLocator... classFileLocator) {
2122
            this(Arrays.asList(classFileLocator));
1✔
2123
        }
1✔
2124

2125
        /**
2126
         * Creates a new compound class file locator.
2127
         *
2128
         * @param classFileLocators The {@link ClassFileLocator}s to be represented by this compound class file locator in
2129
         *                          the order of their application.
2130
         */
2131
        public Compound(List<? extends ClassFileLocator> classFileLocators) {
1✔
2132
            this.classFileLocators = new ArrayList<ClassFileLocator>();
1✔
2133
            for (ClassFileLocator classFileLocator : classFileLocators) {
1✔
2134
                if (classFileLocator instanceof Compound) {
1✔
2135
                    this.classFileLocators.addAll(((Compound) classFileLocator).classFileLocators);
1✔
2136
                } else if (!(classFileLocator instanceof NoOp)) {
1✔
2137
                    this.classFileLocators.add(classFileLocator);
1✔
2138
                }
2139
            }
1✔
2140
        }
1✔
2141

2142
        /**
2143
         * {@inheritDoc}
2144
         */
2145
        public Resolution locate(String name) throws IOException {
2146
            for (ClassFileLocator classFileLocator : classFileLocators) {
1✔
2147
                Resolution resolution = classFileLocator.locate(name);
1✔
2148
                if (resolution.isResolved()) {
1✔
2149
                    return resolution;
1✔
2150
                }
2151
            }
1✔
2152
            return new Resolution.Illegal(name);
1✔
2153
        }
2154

2155
        /**
2156
         * {@inheritDoc}
2157
         */
2158
        public void close() throws IOException {
2159
            for (ClassFileLocator classFileLocator : classFileLocators) {
1✔
2160
                classFileLocator.close();
1✔
2161
            }
1✔
2162
        }
1✔
2163
    }
2164
}
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