forked from kangjianwei/LearningJDK
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathModulePath.java
830 lines (699 loc) · 33.6 KB
/
ModulePath.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
/*
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.internal.module;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.lang.module.FindException;
import java.lang.module.InvalidModuleDescriptorException;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleDescriptor.Builder;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import jdk.internal.jmod.JmodFile;
import jdk.internal.jmod.JmodFile.Section;
import jdk.internal.perf.PerfCounter;
/**
* A {@code ModuleFinder} that locates modules on the file system by searching
* a sequence of directories or packaged modules. The ModuleFinder can be
* created to work in either the run-time or link-time phases. In both cases it
* locates modular JAR and exploded modules. When created for link-time then it
* additionally locates modules in JMOD files. The ModuleFinder can also
* optionally patch any modules that it locates with a ModulePatcher.
*/
// 基于指定路径的模块集,实现了模块查找器接口
public class ModulePath implements ModuleFinder {
private static final String MODULE_INFO = "module-info.class";
private static final String SERVICES_PREFIX = "META-INF/services/";
// the version to use for multi-release modular JARs
private final Runtime.Version releaseVersion;
// true for the link phase (supports modules packaged in JMOD format)
private final boolean isLinkPhase;
// for patching modules, can be null
private final ModulePatcher patcher;
// the entries on this module path
private final Path[] entries; // 待解析模块所在的位置
private int next; // 下一个待解析位置的索引
/** map of module name to module reference map for modules already located */
// 缓存在指定的路径下查找到的模块描述符;key是模块名称,value是模块模块描述符
private final Map<String, ModuleReference> cachedModules = new HashMap<>();
private static final Attributes.Name AUTOMATIC_MODULE_NAME = new Attributes.Name("Automatic-Module-Name");
private static final PerfCounter scanTime = PerfCounter.newPerfCounter("jdk.module.finder.modulepath.scanTime");
private static final PerfCounter moduleCount = PerfCounter.newPerfCounter("jdk.module.finder.modulepath.modules");
/*▼ 构造器 ████████████████████████████████████████████████████████████████████████████████┓ */
private ModulePath(Runtime.Version version, boolean isLinkPhase, ModulePatcher patcher, Path... entries) {
this.releaseVersion = version;
this.isLinkPhase = isLinkPhase;
this.patcher = patcher;
this.entries = entries.clone();
for(Path entry : this.entries) {
Objects.requireNonNull(entry);
}
}
/*▲ 构造器 ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ 工厂方法 ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Returns a ModuleFinder that locates modules on the file system by
* searching a sequence of directories and/or packaged modules.
*/
// 构造模块查找器,用于查找指定路径下的模块
public static ModuleFinder of(Path... entries) {
return of(null, entries);
}
/**
* Returns a ModuleFinder that locates modules on the file system by
* searching a sequence of directories and/or packaged modules. The modules
* may be patched by the given ModulePatcher.
*/
// 构造模块查找器,用于查找指定路径下的模块;patcher用于patch模块
public static ModuleFinder of(ModulePatcher patcher, Path... entries) {
return new ModulePath(JarFile.runtimeVersion(), false, patcher, entries);
}
/**
* Returns a ModuleFinder that locates modules on the file system by
* searching a sequence of directories and/or packaged modules.
*
* @param version The release version to use for multi-release JAR files
* @param isLinkPhase {@code true} if the link phase to locate JMOD files
*/
// 构造模块查找器,用于查找指定路径下的模块;version用来指定jar的发行版本
public static ModuleFinder of(Runtime.Version version, boolean isLinkPhase, Path... entries) {
return new ModulePath(version, isLinkPhase, null, entries);
}
/*▲ 工厂方法 ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ 查找 ████████████████████████████████████████████████████████████████████████████████┓ */
// 查找指定名称的模块,返回模块引用
@Override
public Optional<ModuleReference> find(String name) {
Objects.requireNonNull(name);
// try cached modules
ModuleReference reference = cachedModules.get(name);
if(reference != null) {
return Optional.of(reference);
}
// the module may not have been encountered yet
while(hasNextEntry()) {
scanNextEntry();
reference = cachedModules.get(name);
// 一旦找到,立即返回
if(reference != null) {
return Optional.of(reference);
}
}
return Optional.empty();
}
// 查找所有模块,返回其模块引用的集合
@Override
public Set<ModuleReference> findAll() {
// need to ensure that all entries have been scanned
while(hasNextEntry()) {
scanNextEntry();
}
return new HashSet<>(cachedModules.values());
}
/*▲ 查找 ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ exploded(未打包的目录) ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Returns a {@code ModuleReference} to an exploded module on the file system or {@code null} if {@code module-info.class} not found.
*
* @throws IOException
* @throws InvalidModuleDescriptorException
*/
// 解析常规目录(未打包,根目录下存在module-info.class信息)中的模块信息,返回一个模块引用
private ModuleReference readExplodedModule(Path dir) throws IOException {
// 找到"module-info.class"的位置
Path mi = dir.resolve(MODULE_INFO);
ModuleInfo.Attributes attrs;
try(InputStream in = Files.newInputStream(mi)) {
attrs = ModuleInfo.read(new BufferedInputStream(in), () -> explodedPackages(dir));
} catch(NoSuchFileException e) {
// for now
return null;
}
return ModuleReferences.newExplodedModule(attrs, patcher, dir);
}
// 返回目录dir下所有的包
private Set<String> explodedPackages(Path dir) {
try {
return Files.find(dir, Integer.MAX_VALUE, ((path, attrs) -> attrs.isRegularFile() && !isHidden(path))).map(path -> dir.relativize(path)).map(this::toPackageName).flatMap(Optional::stream).collect(Collectors.toSet());
} catch(IOException x) {
throw new UncheckedIOException(x);
}
}
/*▲ exploded(未打包的目录) ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ jar ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Returns a {@code ModuleReference} to a module in modular JAR file on
* the file system.
*
* @throws IOException
* @throws FindException
* @throws InvalidModuleDescriptorException
*/
// 解析jar中的模块信息,返回一个模块引用
private ModuleReference readJar(Path file) throws IOException {
try(
JarFile jarFile = new JarFile(file.toFile(), true, ZipFile.OPEN_READ, releaseVersion)
) {
ModuleInfo.Attributes attrs;
// 获取指定名称的jar实体(在multi-release jar下,会结合发行版本信息)
JarEntry entry = jarFile.getJarEntry(MODULE_INFO);
if(entry != null) {
attrs = ModuleInfo.read(jarFile.getInputStream(entry), () -> jarPackages(jarFile));
} else {
// no module-info.class so treat it as automatic module
try {
// 解析指定的jar文件
ModuleDescriptor md = deriveModuleDescriptor(jarFile);
attrs = new ModuleInfo.Attributes(md, null, null, null);
} catch(RuntimeException e) {
throw new FindException("Unable to derive module descriptor for " + jarFile.getName(), e);
}
}
return ModuleReferences.newJarModule(attrs, patcher, file);
} catch(ZipException e) {
throw new FindException("Error reading " + file, e);
}
}
// 返回jarFile中所有的包
private Set<String> jarPackages(JarFile jarFile) {
return jarFile.versionedStream().filter(e -> !e.isDirectory()).map(JarEntry::getName).map(this::toPackageName).flatMap(Optional::stream).collect(Collectors.toSet());
}
/*▲ jar ████████████████████████████████████████████████████████████████████████████████┛ */
/*▼ jmod ████████████████████████████████████████████████████████████████████████████████┓ */
/**
* Returns a {@code ModuleReference} to a module in JMOD file on the
* file system.
*
* @throws IOException
* @throws InvalidModuleDescriptorException
*/
// 解析jmod中的模块信息,返回一个模块引用
private ModuleReference readJMod(Path file) throws IOException {
try(JmodFile jf = new JmodFile(file)) {
ModuleInfo.Attributes attrs;
try(InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
attrs = ModuleInfo.read(in, () -> jmodPackages(jf));
}
return ModuleReferences.newJModModule(attrs, file);
}
}
// 返回jmodFile中所有的包
private Set<String> jmodPackages(JmodFile jmodFile) {
return jmodFile.stream().filter(e -> e.section() == Section.CLASSES).map(JmodFile.Entry::name).map(this::toPackageName).flatMap(Optional::stream).collect(Collectors.toSet());
}
/*▲ jmod ████████████████████████████████████████████████████████████████████████████████┛ */
/**
* Returns {@code true} if there are additional entries to scan
*/
// 是否存在下一个未解析的模块
private boolean hasNextEntry() {
return next<entries.length;
}
/**
* Scans the next entry on the module path. A no-op if all entries have
* already been scanned.
*
* @throws FindException if an error occurs scanning the next entry
*/
// 扫描(解析)下一个模块
private void scanNextEntry() {
if(!hasNextEntry()) {
return;
}
long t0 = System.nanoTime();
Path entry = entries[next++];
// 扫描(解析)指定路径下的模块
Map<String, ModuleReference> modules = scan(entry);
// update cache, ignoring duplicates
int initialSize = cachedModules.size();
// 更新缓存
for(Map.Entry<String, ModuleReference> e : modules.entrySet()) {
cachedModules.putIfAbsent(e.getKey(), e.getValue());
}
// update counters
int added = cachedModules.size() - initialSize;
moduleCount.add(added);
scanTime.addElapsedTimeFrom(t0);
}
/**
* Scan the given module path entry. If the entry is a directory then it is
* a directory of modules or an exploded module. If the entry is a regular
* file then it is assumed to be a packaged module.
*
* @throws FindException if an error occurs scanning the entry
*/
// 扫描(解析)指定路径下的模块
private Map<String, ModuleReference> scan(Path entry) {
BasicFileAttributes attrs;
try {
// 获取指定路径标识的文件的基础文件属性
attrs = Files.readAttributes(entry, BasicFileAttributes.class);
} catch(NoSuchFileException e) {
return Collections.emptyMap();
} catch(IOException ioe) {
throw new FindException(ioe);
}
try {
// 如果entry指向目录(未打包)
if(attrs.isDirectory()) {
// 获取module-info.class的路径
Path mi = entry.resolve(MODULE_INFO);
// 如果entry的根目录下没有module-info.class信息,则扫描未打包的目录,在其中查找模块信息
if(!Files.exists(mi)) {
// assume a directory of modules
return scanDirectory(entry);
}
}
// 解析指定的模块,该模块可能以普通目录(未打包)、jar、jmod的形式存在
ModuleReference mref = readModule(entry, attrs); // packaged or exploded module
if(mref != null) {
// 获取模块名称
String name = mref.descriptor().name();
// 返回单元素Map
return Collections.singletonMap(name, mref);
}
// not recognized
String msg;
if(!isLinkPhase && entry.toString().endsWith(".jmod")) {
msg = "JMOD format not supported at execution time";
} else {
msg = "Module format not recognized";
}
throw new FindException(msg + ": " + entry);
} catch(IOException ioe) {
throw new FindException(ioe);
}
}
/**
* Scans the given directory for packaged or exploded modules.
*
* @return a map of module name to ModuleReference for the modules found in the directory
*
* @throws IOException if an I/O error occurs
* @throws FindException if an error occurs scanning the entry or the
* directory contains two or more modules with the same name
*/
// 扫描未打包的目录,在其中查找模块信息(根目录下没发现module-info.class信息)
private Map<String, ModuleReference> scanDirectory(Path dir) throws IOException {
// The map of name -> mref of modules found in this directory.
Map<String, ModuleReference> nameToReference = new HashMap<>();
// 获取实体dir的目录流,用来搜寻目录内的子文件/目录(不会过滤任何子项)
try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
// 遍历dir内的子项,查找模块
for(Path entry : stream) {
BasicFileAttributes attrs;
try {
attrs = Files.readAttributes(entry, BasicFileAttributes.class);
} catch(NoSuchFileException ignore) {
// file has been removed or moved, ignore for now
continue;
}
ModuleReference mref = readModule(entry, attrs);
if(mref == null) {
continue;
}
/* can have at most one version of a module in the directory */
// 获取模块名称
String name = mref.descriptor().name();
// 将指定的元素(key-value)存入Map,并返回旧值,允许覆盖
ModuleReference previous = nameToReference.put(name, mref);
// 如果之前就存在同名模块
if(previous != null) {
// 如果量模块不在同一个位置,则抛出异常
String fn1 = fileName(mref);
String fn2 = fileName(previous);
throw new FindException("Two versions of module " + name + " found in " + dir + " (" + fn1 + " and " + fn2 + ")");
}
}
}
return nameToReference;
}
/**
* Reads a packaged or exploded module, returning a {@code ModuleReference} to the module.
* Returns {@code null} if the entry is not recognized.
*
* @throws IOException if an I/O error occurs
* @throws FindException if an error occurs parsing its module descriptor
*/
// 解析指定的模块,该模块可能以普通目录、jar、jmod的形式存在
private ModuleReference readModule(Path entry, BasicFileAttributes attrs) throws IOException {
try {
// exploded module
if(attrs.isDirectory()) {
// 解析常规目录中的模块(未打包),返回一个模块引用
return readExplodedModule(entry); // may return null
}
// JAR or JMOD file
if(!attrs.isRegularFile()) {
return null;
}
String fn = entry.getFileName().toString();
boolean isDefaultFileSystem = isDefaultFileSystem(entry);
// JAR file
if(fn.endsWith(".jar")) {
if(isDefaultFileSystem) {
// 解析jar中的模块信息,返回一个模块引用
return readJar(entry);
} else {
// the JAR file is in a custom file system so need to copy it to the local file system
Path tmpdir = Files.createTempDirectory("mlib");
Path target = Files.copy(entry, tmpdir.resolve(fn));
return readJar(target);
}
}
// JMOD file
if(isDefaultFileSystem && isLinkPhase && fn.endsWith(".jmod")) {
// 解析jmod中的模块信息,返回一个模块引用
return readJMod(entry);
}
return null;
} catch(InvalidModuleDescriptorException e) {
throw new FindException("Error reading module: " + entry, e);
}
}
/**
* Returns a string with the file name of the module if possible.
* If the module location is not a file URI then return the URI as a string.
*/
// 返回模块的位置信息
private String fileName(ModuleReference mref) {
// 解析出模块位置信息
URI uri = mref.location().orElse(null);
if(uri != null) {
if(uri.getScheme().equalsIgnoreCase("file")) {
return Path.of(uri).getFileName().toString();
} else {
return uri.toString();
}
} else {
return "<unknown>";
}
}
/**
* Treat the given JAR file as a module as follows:
*
* 1. The value of the Automatic-Module-Name attribute is the module name
* 2. The version, and the module name when the Automatic-Module-Name
* attribute is not present, is derived from the file ame of the JAR file
* 3. All packages are derived from the .class files in the JAR file
* 4. The contents of any META-INF/services configuration files are mapped
* to "provides" declarations
* 5. The Main-Class attribute in the main attributes of the JAR manifest
* is mapped to the module descriptor mainClass if possible
*/
// 解析指定的jar文件
private ModuleDescriptor deriveModuleDescriptor(JarFile jarFile) throws IOException {
// Read Automatic-Module-Name attribute if present
Manifest man = jarFile.getManifest();
Attributes attrs = null;
String moduleName = null;
if(man != null) {
attrs = man.getMainAttributes();
if(attrs != null) {
moduleName = attrs.getValue(AUTOMATIC_MODULE_NAME);
}
}
// Derive the version, and the module name if needed, from JAR file name
String fn = jarFile.getName();
int i = fn.lastIndexOf(File.separator);
if(i != -1) {
fn = fn.substring(i + 1);
}
// drop ".jar"
String name = fn.substring(0, fn.length() - 4);
String vs = null;
// find first occurrence of -${NUMBER}. or -${NUMBER}$
Matcher matcher = Patterns.DASH_VERSION.matcher(name);
if(matcher.find()) {
int start = matcher.start();
// attempt to parse the tail as a version string
try {
String tail = name.substring(start + 1);
ModuleDescriptor.Version.parse(tail);
vs = tail;
} catch(IllegalArgumentException ignore) {
}
name = name.substring(0, start);
}
// Create builder, using the name derived from file name when Automatic-Module-Name not present
Builder builder;
// 构造一个自动模块描述符
if(moduleName != null) {
try {
builder = ModuleDescriptor.newAutomaticModule(moduleName);
} catch(IllegalArgumentException e) {
throw new FindException(AUTOMATIC_MODULE_NAME + ": " + e.getMessage());
}
} else {
builder = ModuleDescriptor.newAutomaticModule(cleanModuleName(name));
}
// module version if present
if(vs != null) {
builder.version(vs);
}
// scan the names of the entries in the JAR file
Map<Boolean, Set<String>> map = jarFile.versionedStream().filter(e -> !e.isDirectory()).map(JarEntry::getName).filter(e -> (e.endsWith(".class") ^ e.startsWith(SERVICES_PREFIX))).collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX), Collectors.toSet()));
Set<String> classFiles = map.get(Boolean.FALSE);
Set<String> configFiles = map.get(Boolean.TRUE);
// the packages containing class files
Set<String> packages = classFiles.stream().map(this::toPackageName).flatMap(Optional::stream).distinct().collect(Collectors.toSet());
// all packages are exported and open
builder.packages(packages);
// map names of service configuration files to service names
Set<String> serviceNames = configFiles.stream().map(this::toServiceName).flatMap(Optional::stream).collect(Collectors.toSet());
// parse each service configuration file
for(String sn : serviceNames) {
JarEntry entry = jarFile.getJarEntry(SERVICES_PREFIX + sn);
List<String> providerClasses = new ArrayList<>();
try(InputStream in = jarFile.getInputStream(entry)) {
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
String cn;
while((cn = nextLine(reader)) != null) {
if(cn.length()>0) {
String pn = packageName(cn);
if(!packages.contains(pn)) {
String msg = "Provider class " + cn + " not in module";
throw new InvalidModuleDescriptorException(msg);
}
providerClasses.add(cn);
}
}
}
if(!providerClasses.isEmpty()) {
builder.provides(sn, providerClasses);
}
}
// Main-Class attribute if it exists
if(attrs != null) {
String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
if(mainClass != null) {
mainClass = mainClass.replace("/", ".");
if(Checks.isClassName(mainClass)) {
String pn = packageName(mainClass);
if(packages.contains(pn)) {
builder.mainClass(mainClass);
}
}
}
}
return builder.build();
}
/**
* Reads the next line from the given reader and trims it of comments and
* leading/trailing white space.
*
* Returns null if the reader is at EOF.
*/
private String nextLine(BufferedReader reader) throws IOException {
String ln = reader.readLine();
if(ln != null) {
int ci = ln.indexOf('#');
if(ci >= 0) {
ln = ln.substring(0, ci);
}
ln = ln.trim();
}
return ln;
}
/**
* Returns the service type corresponding to the name of a services
* configuration file if it is a legal type name.
*
* For example, if called with "META-INF/services/p.S" then this method
* returns a container with the value "p.S".
*/
private Optional<String> toServiceName(String cf) {
assert cf.startsWith(SERVICES_PREFIX);
int index = cf.lastIndexOf("/") + 1;
if(index<cf.length()) {
String prefix = cf.substring(0, index);
if(prefix.equals(SERVICES_PREFIX)) {
String sn = cf.substring(index);
if(Checks.isClassName(sn)) {
return Optional.of(sn);
}
}
}
return Optional.empty();
}
/**
* Clean up candidate module name derived from a JAR file name.
*/
private static String cleanModuleName(String mn) {
// replace non-alphanumeric
mn = Patterns.NON_ALPHANUM.matcher(mn).replaceAll(".");
// collapse repeating dots
mn = Patterns.REPEATING_DOTS.matcher(mn).replaceAll(".");
// drop leading dots
if(mn.length()>0 && mn.charAt(0) == '.') {
mn = Patterns.LEADING_DOTS.matcher(mn).replaceAll("");
}
// drop trailing dots
int len = mn.length();
if(len>0 && mn.charAt(len - 1) == '.') {
mn = Patterns.TRAILING_DOTS.matcher(mn).replaceAll("");
}
return mn;
}
/**
* Maps a type name to its package name.
*/
// 返回指定类的包名
private static String packageName(String className) {
int index = className.lastIndexOf('.');
return (index == -1) ? "" : className.substring(0, index);
}
/**
* Maps the name of an entry in a JAR or ZIP file to a package name.
*
* @throws InvalidModuleDescriptorException if the name is a class file in
* the top-level directory of the JAR/ZIP file (and it's not
* module-info.class)
*/
// 获取指定实体所在的包
private Optional<String> toPackageName(String name) {
assert !name.endsWith("/");
int index = name.lastIndexOf("/");
if(index == -1) {
if(name.endsWith(".class") && !name.equals(MODULE_INFO)) {
String msg = name + " found in top-level directory (unnamed package not allowed in module)";
throw new InvalidModuleDescriptorException(msg);
}
return Optional.empty();
}
String pn = name.substring(0, index).replace('/', '.');
if(Checks.isPackageName(pn)) {
return Optional.of(pn);
} else {
// not a valid package name
return Optional.empty();
}
}
/**
* Maps the relative path of an entry in an exploded module to a package
* name.
*
* @throws InvalidModuleDescriptorException if the name is a class file in
* the top-level directory (and it's not module-info.class)
*/
// 获取指定实体所在的包
private Optional<String> toPackageName(Path file) {
assert file.getRoot() == null;
Path parent = file.getParent();
if(parent == null) {
String name = file.toString();
if(name.endsWith(".class") && !name.equals(MODULE_INFO)) {
String msg = name + " found in top-level directory (unnamed package not allowed in module)";
throw new InvalidModuleDescriptorException(msg);
}
return Optional.empty();
}
String pn = parent.toString().replace(File.separatorChar, '.');
if(Checks.isPackageName(pn)) {
return Optional.of(pn);
} else {
// not a valid package name
return Optional.empty();
}
}
/**
* Returns true if the given file exists and is a hidden file
*/
// 判断指定的文件是否为隐藏文件
private boolean isHidden(Path file) {
try {
return Files.isHidden(file);
} catch(IOException ioe) {
return false;
}
}
/**
* Return true if a path locates a path in the default file system
*/
// 判断是否为默认文件系统(file://)
private boolean isDefaultFileSystem(Path path) {
return path.getFileSystem().provider().getScheme().equalsIgnoreCase("file");
}
/**
* Patterns used to derive the module name from a JAR file name.
*/
private static class Patterns {
static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");
static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
}
}