-
-
Notifications
You must be signed in to change notification settings - Fork 280
/
ClassGraph.java
1721 lines (1606 loc) · 75 KB
/
ClassGraph.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* This file is part of ClassGraph.
*
* Author: Luke Hutchison
*
* Hosted at: https://github.com/classgraph/classgraph
*
* --
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Luke Hutchison
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
* EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
* OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.classgraph;
import java.io.File;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.regex.Pattern;
import nonapi.io.github.classgraph.classpath.SystemJarFinder;
import nonapi.io.github.classgraph.concurrency.AutoCloseableExecutorService;
import nonapi.io.github.classgraph.concurrency.InterruptionChecker;
import nonapi.io.github.classgraph.scanspec.AcceptReject;
import nonapi.io.github.classgraph.scanspec.ScanSpec;
import nonapi.io.github.classgraph.utils.JarUtils;
import nonapi.io.github.classgraph.utils.LogNode;
import nonapi.io.github.classgraph.utils.VersionFinder;
/**
* Uber-fast, ultra-lightweight Java classpath and module path scanner. Scans classfiles in the classpath and/or
* module path by parsing the classfile binary format directly rather than by using reflection.
*
* <p>
* Documentation: <a href= "https://github.com/classgraph/classgraph/wiki">
* https://github.com/classgraph/classgraph/wiki</a>
*/
public class ClassGraph {
/** The scanning specification. */
ScanSpec scanSpec = new ScanSpec();
/**
* The default number of worker threads to use while scanning. This number gave the best results on a relatively
* modern laptop with SSD, while scanning a large classpath.
*/
static final int DEFAULT_NUM_WORKER_THREADS = Math.max(
// Always scan with at least 2 threads
2, //
(int) Math.ceil(
// Num IO threads (top out at 4, since most I/O devices won't scale better than this)
Math.min(4.0, Runtime.getRuntime().availableProcessors() * 0.75) +
// Num scanning threads (higher than available processors, because some threads can be blocked)
Runtime.getRuntime().availableProcessors() * 1.25) //
);
/** If non-null, log while scanning. */
private LogNode topLevelLog;
// -------------------------------------------------------------------------------------------------------------
/** Construct a ClassGraph instance. */
public ClassGraph() {
// Initialize ScanResult, if this is the first call to ClassGraph constructor
ScanResult.init();
}
/**
* Get the version number of ClassGraph.
*
* @return the ClassGraph version, or "unknown" if it could not be determined.
*/
public static String getVersion() {
return VersionFinder.getVersion();
}
// -------------------------------------------------------------------------------------------------------------
/**
* Switches on verbose logging to System.err.
*
* @return this (for method chaining).
*/
public ClassGraph verbose() {
if (topLevelLog == null) {
topLevelLog = new LogNode();
}
return this;
}
/**
* Switches on verbose logging to System.err if verbose is true.
*
* @param verbose
* if true, enable verbose logging.
* @return this (for method chaining).
*/
public ClassGraph verbose(final boolean verbose) {
if (verbose) {
verbose();
}
return this;
}
// -------------------------------------------------------------------------------------------------------------
/**
* Enables the scanning of all classes, fields, methods, annotations, and static final field constant
* initializer values, and ignores all visibility modifiers, so that both public and non-public classes, fields
* and methods are all scanned.
*
* <p>
* Calls {@link #enableClassInfo()}, {@link #enableFieldInfo()}, {@link #enableMethodInfo()},
* {@link #enableAnnotationInfo()}, {@link #enableStaticFinalFieldConstantInitializerValues()},
* {@link #ignoreClassVisibility()}, {@link #ignoreFieldVisibility()}, and {@link #ignoreMethodVisibility()}.
*
* @return this (for method chaining).
*/
public ClassGraph enableAllInfo() {
enableClassInfo();
enableFieldInfo();
enableMethodInfo();
enableAnnotationInfo();
enableStaticFinalFieldConstantInitializerValues();
ignoreClassVisibility();
ignoreFieldVisibility();
ignoreMethodVisibility();
return this;
}
/**
* Enables the scanning of classfiles, producing {@link ClassInfo} objects in the {@link ScanResult}.
*
* @return this (for method chaining).
*/
public ClassGraph enableClassInfo() {
scanSpec.enableClassInfo = true;
return this;
}
/**
* Causes class visibility to be ignored, enabling private, package-private and protected classes to be scanned.
* By default, only public classes are scanned. (Automatically calls {@link #enableClassInfo()}.)
*
* @return this (for method chaining).
*/
public ClassGraph ignoreClassVisibility() {
enableClassInfo();
scanSpec.ignoreClassVisibility = true;
return this;
}
/**
* Enables the saving of method info during the scan. This information can be obtained using
* {@link ClassInfo#getMethodInfo()} etc. By default, method info is not scanned. (Automatically calls
* {@link #enableClassInfo()}.)
*
* @return this (for method chaining).
*/
public ClassGraph enableMethodInfo() {
enableClassInfo();
scanSpec.enableMethodInfo = true;
return this;
}
/**
* Causes method visibility to be ignored, enabling private, package-private and protected methods to be
* scanned. By default, only public methods are scanned. (Automatically calls {@link #enableClassInfo()} and
* {@link #enableMethodInfo()}.)
*
* @return this (for method chaining).
*/
public ClassGraph ignoreMethodVisibility() {
enableClassInfo();
enableMethodInfo();
scanSpec.ignoreMethodVisibility = true;
return this;
}
/**
* Enables the saving of field info during the scan. This information can be obtained using
* {@link ClassInfo#getFieldInfo()}. By default, field info is not scanned. (Automatically calls
* {@link #enableClassInfo()}.)
*
* @return this (for method chaining).
*/
public ClassGraph enableFieldInfo() {
enableClassInfo();
scanSpec.enableFieldInfo = true;
return this;
}
/**
* Causes field visibility to be ignored, enabling private, package-private and protected fields to be scanned.
* By default, only public fields are scanned. (Automatically calls {@link #enableClassInfo()} and
* {@link #enableFieldInfo()}.)
*
* @return this (for method chaining).
*/
public ClassGraph ignoreFieldVisibility() {
enableClassInfo();
enableFieldInfo();
scanSpec.ignoreFieldVisibility = true;
return this;
}
/**
* Enables the saving of static final field constant initializer values. By default, constant initializer values
* are not scanned. If this is enabled, you can obtain the constant field initializer values from
* {@link FieldInfo#getConstantInitializerValue()}.
*
* <p>
* Note that constant initializer values are usually only of primitive type, or String constants (or values that
* can be computed and reduced to one of those types at compiletime).
*
* <p>
* Also note that it is up to the compiler as to whether or not a constant-valued field is assigned as a
* constant in the field definition itself, or whether it is assigned manually in static class initializer
* blocks -- so your mileage may vary in being able to extract constant initializer values.
*
* <p>
* In fact in Kotlin, even constant initializers for non-static / non-final fields are stored in a field
* attribute in the classfile (and so these values may be picked up by ClassGraph by calling this method),
* although any field initializers for non-static fields are supposed to be ignored by the JVM according to the
* classfile spec, so the Kotlin compiler may change in future to stop generating these values, and you probably
* shouldn't rely on being able to get the initializers for non-static fields in Kotlin. (As far as non-final
* fields, javac simply does not add constant initializer values to the field attributes list for non-final
* fields, even if they are static, but the spec doesn't say whether or not the JVM should ignore constant
* initializers for non-final fields.)
*
* <p>
* Automatically calls {@link #enableClassInfo()} and {@link #enableFieldInfo()}.
*
* @return this (for method chaining).
*/
public ClassGraph enableStaticFinalFieldConstantInitializerValues() {
enableClassInfo();
enableFieldInfo();
scanSpec.enableStaticFinalFieldConstantInitializerValues = true;
return this;
}
/**
* Enables the saving of annotation info (for class, field, method and method parameter annotations) during the
* scan. This information can be obtained using {@link ClassInfo#getAnnotationInfo()},
* {@link FieldInfo#getAnnotationInfo()}, and {@link MethodParameterInfo#getAnnotationInfo()}. By default,
* annotation info is not scanned. (Automatically calls {@link #enableClassInfo()}.)
*
* @return this (for method chaining).
*/
public ClassGraph enableAnnotationInfo() {
enableClassInfo();
scanSpec.enableAnnotationInfo = true;
return this;
}
/**
* Enables the determination of inter-class dependencies, which may be read by calling
* {@link ClassInfo#getClassDependencies()}, {@link ScanResult#getClassDependencyMap()} or
* {@link ScanResult#getReverseClassDependencyMap()}. (Automatically calls {@link #enableClassInfo()},
* {@link #enableFieldInfo()}, {@link #enableMethodInfo()}, {@link #enableAnnotationInfo()},
* {@link #ignoreClassVisibility()}, {@link #ignoreFieldVisibility()} and {@link #ignoreMethodVisibility()}.)
*
* @return this (for method chaining).
*/
public ClassGraph enableInterClassDependencies() {
enableClassInfo();
enableFieldInfo();
enableMethodInfo();
enableAnnotationInfo();
ignoreClassVisibility();
ignoreFieldVisibility();
ignoreMethodVisibility();
scanSpec.enableInterClassDependencies = true;
return this;
}
// -------------------------------------------------------------------------------------------------------------
/**
* Causes only runtime visible annotations to be scanned (causes runtime invisible annotations to be ignored).
* (Automatically calls {@link #enableClassInfo()}.)
*
* @return this (for method chaining).
*/
public ClassGraph disableRuntimeInvisibleAnnotations() {
enableClassInfo();
scanSpec.disableRuntimeInvisibleAnnotations = true;
return this;
}
// -------------------------------------------------------------------------------------------------------------
/**
* Disables the scanning of jarfiles.
*
* @return this (for method chaining).
*/
public ClassGraph disableJarScanning() {
scanSpec.scanJars = false;
return this;
}
/**
* Disables the scanning of nested jarfiles (jarfiles within jarfiles).
*
* @return this (for method chaining).
*/
public ClassGraph disableNestedJarScanning() {
scanSpec.scanNestedJars = false;
return this;
}
/**
* Disables the scanning of directories.
*
* @return this (for method chaining).
*/
public ClassGraph disableDirScanning() {
scanSpec.scanDirs = false;
return this;
}
/**
* Disables the scanning of modules.
*
* @return this (for method chaining).
*/
public ClassGraph disableModuleScanning() {
scanSpec.scanModules = false;
return this;
}
// -------------------------------------------------------------------------------------------------------------
/**
* Causes ClassGraph to return classes that are not in the accepted packages, but that are directly referred to
* by classes within accepted packages as a superclass, implemented interface or annotation. (Automatically
* calls {@link #enableClassInfo()}.)
*
* @return this (for method chaining).
*/
public ClassGraph enableExternalClasses() {
enableClassInfo();
scanSpec.enableExternalClasses = true;
return this;
}
/**
* Causes classes loaded using {@link ClassInfo#loadClass()} to be are initialized after class loading (the
* default is to not initialize classes).
*
* @return this (for method chaining).
*/
public ClassGraph initializeLoadedClasses() {
scanSpec.initializeLoadedClasses = true;
return this;
}
/**
* Remove temporary files, including nested jarfiles (jarfiles within jarfiles, which have to be extracted
* during scanning in order to be read) from their temporary directory as soon as the scan has completed. The
* default is for temporary files to be removed by the {@link ScanResult} finalizer, or on JVM exit.
*
* @return this (for method chaining).
*/
public ClassGraph removeTemporaryFilesAfterScan() {
scanSpec.removeTemporaryFilesAfterScan = true;
return this;
}
// -------------------------------------------------------------------------------------------------------------
/**
* Override the automatically-detected classpath with a custom path, with path elements separated by
* File.pathSeparatorChar. Causes system ClassLoaders and the java.class.path system property to be ignored.
* Also causes modules not to be scanned.
*
* <p>
* If this method is called, nothing but the provided classpath will be scanned, i.e. this causes ClassLoaders
* to be ignored, as well as the java.class.path system property.
*
* @param overrideClasspath
* The custom classpath to use for scanning, with path elements separated by File.pathSeparatorChar.
* @return this (for method chaining).
*/
public ClassGraph overrideClasspath(final String overrideClasspath) {
if (overrideClasspath.isEmpty()) {
throw new IllegalArgumentException("Can't override classpath with an empty path");
}
for (final String classpathElement : JarUtils.smartPathSplit(overrideClasspath, scanSpec)) {
scanSpec.addClasspathOverride(classpathElement);
}
return this;
}
/**
* Override the automatically-detected classpath with a custom path. Causes system ClassLoaders and the
* java.class.path system property to be ignored. Also causes modules not to be scanned.
*
* <p>
* Works for Iterables of any type whose toString() method resolves to a classpath element string, e.g. String,
* File or Path.
*
* @param overrideClasspathElements
* The custom classpath to use for scanning, with path elements separated by File.pathSeparatorChar.
* @return this (for method chaining).
*/
public ClassGraph overrideClasspath(final Iterable<?> overrideClasspathElements) {
if (!overrideClasspathElements.iterator().hasNext()) {
throw new IllegalArgumentException("Can't override classpath with an empty path");
}
for (final Object classpathElement : overrideClasspathElements) {
scanSpec.addClasspathOverride(classpathElement);
}
return this;
}
/**
* Override the automatically-detected classpath with a custom path. Causes system ClassLoaders and the
* java.class.path system property to be ignored. Also causes modules not to be scanned.
*
* <p>
* Works for arrays of any member type whose toString() method resolves to a classpath element string, e.g.
* String, File or Path.
*
* @param overrideClasspathElements
* The custom classpath to use for scanning, with path elements separated by File.pathSeparatorChar.
* @return this (for method chaining).
*/
public ClassGraph overrideClasspath(final Object... overrideClasspathElements) {
if (overrideClasspathElements.length == 0) {
throw new IllegalArgumentException("Can't override classpath with an empty path");
}
for (final Object classpathElement : overrideClasspathElements) {
scanSpec.addClasspathOverride(classpathElement);
}
return this;
}
// -------------------------------------------------------------------------------------------------------------
/**
* Add a classpath element filter. The includeClasspathElement method should return true if the path string
* passed to it is a path you want to scan.
*/
@FunctionalInterface
public interface ClasspathElementFilter {
/**
* Whether or not to include a given classpath element in the scan.
*
* @param classpathElementPathStr
* The path string of a classpath element, normalized so that the path separator is '/'. This
* will usually be a file path, but could be a URL, or it could be a path for a nested jar, where
* the paths are separated using '!', in Java convention. "jar:" and/or "file:" will have been
* stripped from the beginning, if they were present in the classpath.
* @return true if the path string passed is a path you want to scan.
*/
boolean includeClasspathElement(String classpathElementPathStr);
}
/**
* Add a classpath element URL filter. The includeClasspathElement method should return true if the {@link URL}
* passed to it corresponds to a classpath element that you want to scan.
*/
@FunctionalInterface
public interface ClasspathElementURLFilter {
/**
* Whether or not to include a given classpath element in the scan.
*
* @param classpathElementURL
* The {@link URL} of a classpath element.
* @return true if you want to scan the {@link URL}.
*/
boolean includeClasspathElement(URL classpathElementURL);
}
/**
* Add a classpath element filter. The provided ClasspathElementFilter should return true if the path string
* passed to it is a path you want to scan.
*
* @param classpathElementFilter
* The filter function to use. This function should return true if the classpath element path should
* be scanned, and false if not.
* @return this (for method chaining).
*/
public ClassGraph filterClasspathElements(final ClasspathElementFilter classpathElementFilter) {
scanSpec.filterClasspathElements(classpathElementFilter);
return this;
}
/**
* Add a classpath element filter. The provided ClasspathElementFilter should return true if the {@link URL}
* passed to it is a URL you want to scan.
*
* @param classpathElementURLFilter
* The filter function to use. This function should return true if the classpath element {@link URL}
* should be scanned, and false if not.
* @return this (for method chaining).
*/
public ClassGraph filterClasspathElementsByURL(final ClasspathElementURLFilter classpathElementURLFilter) {
scanSpec.filterClasspathElements(classpathElementURLFilter);
return this;
}
// -------------------------------------------------------------------------------------------------------------
/**
* Add a ClassLoader to the list of ClassLoaders to scan.
*
* <p>
* This call is ignored if {@link #overrideClasspath(String)} is also called, or if this method is called before
* {@link #overrideClassLoaders(ClassLoader...)}.
*
* @param classLoader
* The additional ClassLoader to scan.
* @return this (for method chaining).
*/
public ClassGraph addClassLoader(final ClassLoader classLoader) {
scanSpec.addClassLoader(classLoader);
return this;
}
/**
* Completely override (and ignore) system ClassLoaders and the java.class.path system property. Also causes
* modules not to be scanned. Note that you may want to use this together with
* {@link #ignoreParentClassLoaders()} to extract classpath URLs from only the classloaders you specified in the
* parameter to `overrideClassLoaders`, and not their parent classloaders.
*
* <p>
* This call is ignored if {@link #overrideClasspath(String)} is called.
*
* @param overrideClassLoaders
* The ClassLoaders to scan instead of the automatically-detected ClassLoaders.
* @return this (for method chaining).
*/
public ClassGraph overrideClassLoaders(final ClassLoader... overrideClassLoaders) {
scanSpec.overrideClassLoaders(overrideClassLoaders);
return this;
}
/**
* Ignore parent classloaders (i.e. only obtain paths to scan from classloaders that are not the parent of
* another classloader).
*
* @return this (for method chaining).
*/
public ClassGraph ignoreParentClassLoaders() {
scanSpec.ignoreParentClassLoaders = true;
return this;
}
// -------------------------------------------------------------------------------------------------------------
/**
* Add a ModuleLayer to the list of ModuleLayers to scan. Use this method if you define your own ModuleLayer,
* but the scanning code is not running within that custom ModuleLayer.
*
* <p>
* This call is ignored if it is called before {@link #overrideModuleLayers(Object...)}.
*
* @param moduleLayer
* The additional ModuleLayer to scan. (The parameter is of type {@link Object} for backwards
* compatibility with JDK 7 and JDK 8, but the argument should be of type ModuleLayer.)
* @return this (for method chaining).
*/
public ClassGraph addModuleLayer(final Object moduleLayer) {
scanSpec.addModuleLayer(moduleLayer);
return this;
}
/**
* Completely override (and ignore) the visible ModuleLayers, and instead scan the requested ModuleLayers.
*
* <p>
* This call is ignored if overrideClasspath() is called.
*
* @param overrideModuleLayers
* The ModuleLayers to scan instead of the automatically-detected ModuleLayers. (The parameter is of
* type {@link Object}[] for backwards compatibility with JDK 7 and JDK 8, but the argument should be
* of type ModuleLayer[].)
* @return this (for method chaining).
*/
public ClassGraph overrideModuleLayers(final Object... overrideModuleLayers) {
scanSpec.overrideModuleLayers(overrideModuleLayers);
return this;
}
/**
* Ignore parent module layers (i.e. only scan module layers that are not the parent of another module layer).
*
* @return this (for method chaining).
*/
public ClassGraph ignoreParentModuleLayers() {
scanSpec.ignoreParentModuleLayers = true;
return this;
}
// -------------------------------------------------------------------------------------------------------------
/**
* Scan one or more specific packages and their sub-packages.
*
* <p>
* N.B. Automatically calls {@link #enableClassInfo()} -- call {@link #acceptPaths(String...)} instead if you
* only need to scan resources.
*
* @param packageNames
* The fully-qualified names of packages to scan (using '.' as a separator). May include a glob
* wildcard ({@code '*'}).
* @return this (for method chaining).
*/
public ClassGraph acceptPackages(final String... packageNames) {
enableClassInfo();
for (final String packageName : packageNames) {
final String packageNameNormalized = AcceptReject.normalizePackageOrClassName(packageName);
// Accept package
scanSpec.packageAcceptReject.addToAccept(packageNameNormalized);
final String path = AcceptReject.packageNameToPath(packageNameNormalized);
scanSpec.pathAcceptReject.addToAccept(path + "/");
if (packageNameNormalized.isEmpty()) {
scanSpec.pathAcceptReject.addToAccept("");
}
if (!packageNameNormalized.contains("*")) {
// Accept sub-packages
if (packageNameNormalized.isEmpty()) {
scanSpec.packagePrefixAcceptReject.addToAccept("");
scanSpec.pathPrefixAcceptReject.addToAccept("");
} else {
scanSpec.packagePrefixAcceptReject.addToAccept(packageNameNormalized + ".");
scanSpec.pathPrefixAcceptReject.addToAccept(path + "/");
}
}
}
return this;
}
/**
* Use {@link #acceptPackages(String...)} instead.
*
* @param packageNames
* The fully-qualified names of packages to scan (using '.' as a separator). May include a glob
* wildcard ({@code '*'}).
* @return this (for method chaining).
* @deprecated Use {@link #acceptPackages(String...)} instead.
*/
@Deprecated
public ClassGraph whitelistPackages(final String... packageNames) {
return acceptPackages(packageNames);
}
/**
* Scan one or more specific paths, and their sub-directories or nested paths.
*
* @param paths
* The paths to scan, relative to the package root of the classpath element (with '/' as a
* separator). May include a glob wildcard ({@code '*'}).
* @return this (for method chaining).
*/
public ClassGraph acceptPaths(final String... paths) {
for (final String path : paths) {
final String pathNormalized = AcceptReject.normalizePath(path);
// Accept path
final String packageName = AcceptReject.pathToPackageName(pathNormalized);
scanSpec.packageAcceptReject.addToAccept(packageName);
scanSpec.pathAcceptReject.addToAccept(pathNormalized + "/");
if (pathNormalized.isEmpty()) {
scanSpec.pathAcceptReject.addToAccept("");
}
if (!pathNormalized.contains("*")) {
// Accept sub-directories / nested paths
if (pathNormalized.isEmpty()) {
scanSpec.packagePrefixAcceptReject.addToAccept("");
scanSpec.pathPrefixAcceptReject.addToAccept("");
} else {
scanSpec.packagePrefixAcceptReject.addToAccept(packageName + ".");
scanSpec.pathPrefixAcceptReject.addToAccept(pathNormalized + "/");
}
}
}
return this;
}
/**
* Use {@link #acceptPaths(String...)} instead.
*
* @param paths
* The paths to scan, relative to the package root of the classpath element (with '/' as a
* separator). May include a glob wildcard ({@code '*'}).
* @return this (for method chaining).
* @deprecated Use {@link #acceptPaths(String...)} instead.
*/
@Deprecated
public ClassGraph whitelistPaths(final String... paths) {
return acceptPaths(paths);
}
/**
* Scan one or more specific packages, without recursively scanning sub-packages unless they are themselves
* accepted.
*
* <p>
* N.B. Automatically calls {@link #enableClassInfo()} -- call {@link #acceptPathsNonRecursive(String...)}
* instead if you only need to scan resources.
*
* <p>
* This may be particularly useful for scanning the package root ("") without recursively scanning everything in
* the jar, dir or module.
*
* @param packageNames
* The fully-qualified names of packages to scan (with '.' as a separator). May not include a glob
* wildcard ({@code '*'}).
*
* @return this (for method chaining).
*/
public ClassGraph acceptPackagesNonRecursive(final String... packageNames) {
enableClassInfo();
for (final String packageName : packageNames) {
final String packageNameNormalized = AcceptReject.normalizePackageOrClassName(packageName);
if (packageNameNormalized.contains("*")) {
throw new IllegalArgumentException("Cannot use a glob wildcard here: " + packageNameNormalized);
}
// Accept package, but not sub-packages
scanSpec.packageAcceptReject.addToAccept(packageNameNormalized);
scanSpec.pathAcceptReject.addToAccept(AcceptReject.packageNameToPath(packageNameNormalized) + "/");
if (packageNameNormalized.isEmpty()) {
scanSpec.pathAcceptReject.addToAccept("");
}
}
return this;
}
/**
* Use {@link #acceptPackagesNonRecursive(String...)} instead.
*
* @param packageNames
* The fully-qualified names of packages to scan (with '.' as a separator). May not include a glob
* wildcard ({@code '*'}).
* @return this (for method chaining).
* @deprecated Use {@link #acceptPackagesNonRecursive(String...)} instead.
*/
@Deprecated
public ClassGraph whitelistPackagesNonRecursive(final String... packageNames) {
return acceptPackagesNonRecursive(packageNames);
}
/**
* Scan one or more specific paths, without recursively scanning sub-directories or nested paths unless they are
* themselves accepted.
*
* <p>
* This may be particularly useful for scanning the package root ("") without recursively scanning everything in
* the jar, dir or module.
*
* @param paths
* The paths to scan, relative to the package root of the classpath element (with '/' as a
* separator). May not include a glob wildcard ({@code '*'}).
* @return this (for method chaining).
*/
public ClassGraph acceptPathsNonRecursive(final String... paths) {
for (final String path : paths) {
if (path.contains("*")) {
throw new IllegalArgumentException("Cannot use a glob wildcard here: " + path);
}
final String pathNormalized = AcceptReject.normalizePath(path);
// Accept path, but not sub-directories / nested paths
scanSpec.packageAcceptReject.addToAccept(AcceptReject.pathToPackageName(pathNormalized));
scanSpec.pathAcceptReject.addToAccept(pathNormalized + "/");
if (pathNormalized.isEmpty()) {
scanSpec.pathAcceptReject.addToAccept("");
}
}
return this;
}
/**
* Use {@link #acceptPathsNonRecursive(String...)} instead.
*
* @param paths
* The paths to scan, relative to the package root of the classpath element (with '/' as a
* separator). May not include a glob wildcard ({@code '*'}).
* @return this (for method chaining).
* @deprecated Use {@link #acceptPathsNonRecursive(String...)} instead.
*/
@Deprecated
public ClassGraph whitelistPathsNonRecursive(final String... paths) {
return acceptPathsNonRecursive(paths);
}
/**
* Prevent the scanning of one or more specific packages and their sub-packages.
*
* <p>
* N.B. Automatically calls {@link #enableClassInfo()} -- call {@link #rejectPaths(String...)} instead if you
* only need to scan resources.
*
* @param packageNames
* The fully-qualified names of packages to reject (with '.' as a separator). May include a glob
* wildcard ({@code '*'}).
* @return this (for method chaining).
*/
public ClassGraph rejectPackages(final String... packageNames) {
enableClassInfo();
for (final String packageName : packageNames) {
final String packageNameNormalized = AcceptReject.normalizePackageOrClassName(packageName);
if (packageNameNormalized.isEmpty()) {
throw new IllegalArgumentException(
"Rejecting the root package (\"\") will cause nothing to be scanned");
}
// Rejecting always prevents further recursion, no need to reject sub-packages
scanSpec.packageAcceptReject.addToReject(packageNameNormalized);
final String path = AcceptReject.packageNameToPath(packageNameNormalized);
scanSpec.pathAcceptReject.addToReject(path + "/");
if (!packageNameNormalized.contains("*")) {
// Reject sub-packages (zipfile entries can occur in any order)
scanSpec.packagePrefixAcceptReject.addToReject(packageNameNormalized + ".");
scanSpec.pathPrefixAcceptReject.addToReject(path + "/");
}
}
return this;
}
/**
* Use {@link #rejectPackages(String...)} instead.
*
* @param packageNames
* The fully-qualified names of packages to reject (with '.' as a separator). May include a glob
* wildcard ({@code '*'}).
* @return this (for method chaining).
* @deprecated Use {@link #rejectPackages(String...)} instead.
*/
@Deprecated
public ClassGraph blacklistPackages(final String... packageNames) {
return rejectPackages(packageNames);
}
/**
* Prevent the scanning of one or more specific paths and their sub-directories / nested paths.
*
* @param paths
* The paths to reject (with '/' as a separator). May include a glob wildcard ({@code '*'}).
* @return this (for method chaining).
*/
public ClassGraph rejectPaths(final String... paths) {
for (final String path : paths) {
final String pathNormalized = AcceptReject.normalizePath(path);
if (pathNormalized.isEmpty()) {
throw new IllegalArgumentException(
"Rejecting the root package (\"\") will cause nothing to be scanned");
}
// Rejecting always prevents further recursion, no need to reject sub-directories / nested paths
final String packageName = AcceptReject.pathToPackageName(pathNormalized);
scanSpec.packageAcceptReject.addToReject(packageName);
scanSpec.pathAcceptReject.addToReject(pathNormalized + "/");
if (!pathNormalized.contains("*")) {
// Reject sub-directories / nested paths
scanSpec.packagePrefixAcceptReject.addToReject(packageName + ".");
scanSpec.pathPrefixAcceptReject.addToReject(pathNormalized + "/");
}
}
return this;
}
/**
* Use {@link #rejectPaths(String...)} instead.
*
* @param paths
* The paths to reject (with '/' as a separator). May include a glob wildcard ({@code '*'}).
* @return this (for method chaining).
* @deprecated Use {@link #rejectPaths(String...)} instead.
*/
@Deprecated
public ClassGraph blacklistPaths(final String... paths) {
return rejectPaths(paths);
}
/**
* Scan one or more specific classes, without scanning other classes in the same package unless the package is
* itself accepted.
*
* <p>
* N.B. Automatically calls {@link #enableClassInfo()}.
*
*
* @param classNames
* The fully-qualified names of classes to scan (using '.' as a separator). To match a class name by
* glob in any package, you must include a package glob too, e.g. {@code "*.*Suffix"}.
* @return this (for method chaining).
*/
public ClassGraph acceptClasses(final String... classNames) {
enableClassInfo();
for (final String className : classNames) {
final String classNameNormalized = AcceptReject.normalizePackageOrClassName(className);
// Accept the class itself
scanSpec.classAcceptReject.addToAccept(classNameNormalized);
scanSpec.classfilePathAcceptReject
.addToAccept(AcceptReject.classNameToClassfilePath(classNameNormalized));
final String packageName = PackageInfo.getParentPackageName(classNameNormalized);
// Record the package containing the class, so we can recurse to this point even if the package
// is not itself accepted
scanSpec.classPackageAcceptReject.addToAccept(packageName);
scanSpec.classPackagePathAcceptReject.addToAccept(AcceptReject.packageNameToPath(packageName) + "/");
}
return this;
}
/**
* Use {@link #acceptClasses(String...)} instead.
*
* @param classNames
* The fully-qualified names of classes to scan (using '.' as a separator).
* @return this (for method chaining).
* @deprecated Use {@link #acceptClasses(String...)} instead.
*/
@Deprecated
public ClassGraph whitelistClasses(final String... classNames) {
return acceptClasses(classNames);
}
/**
* Specifically reject one or more specific classes, preventing them from being scanned even if they are in a
* accepted package.
*
* <p>
* N.B. Automatically calls {@link #enableClassInfo()}.
*
* @param classNames
* The fully-qualified names of classes to reject (using '.' as a separator). To match a class name
* by glob in any package, you must include a package glob too, e.g. {@code "*.*Suffix"}.
* @return this (for method chaining).
*/
public ClassGraph rejectClasses(final String... classNames) {
enableClassInfo();
for (final String className : classNames) {
final String classNameNormalized = AcceptReject.normalizePackageOrClassName(className);
scanSpec.classAcceptReject.addToReject(classNameNormalized);
scanSpec.classfilePathAcceptReject
.addToReject(AcceptReject.classNameToClassfilePath(classNameNormalized));
}
return this;
}
/**
* Use {@link #rejectClasses(String...)} instead.
*
* @param classNames
* The fully-qualified names of classes to reject (using '.' as a separator).
* @return this (for method chaining).
* @deprecated Use {@link #rejectClasses(String...)} instead.
*/
@Deprecated
public ClassGraph blacklistClasses(final String... classNames) {
return rejectClasses(classNames);
}
/**
* Accept one or more jars. This will cause only the accepted jars to be scanned.
*
* @param jarLeafNames
* The leafnames of the jars that should be scanned (e.g. {@code "mylib.jar"}). May contain a
* wildcard glob ({@code "mylib-*.jar"}).
* @return this (for method chaining).
*/
public ClassGraph acceptJars(final String... jarLeafNames) {
for (final String jarLeafName : jarLeafNames) {
final String leafName = JarUtils.leafName(jarLeafName);
if (!leafName.equals(jarLeafName)) {
throw new IllegalArgumentException("Can only accept jars by leafname: " + jarLeafName);
}
scanSpec.jarAcceptReject.addToAccept(leafName);
}
return this;
}