Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/share/classes/jdk/internal/module/ModulePath.java
41159 views
1
/*
2
* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package jdk.internal.module;
27
28
import java.io.BufferedInputStream;
29
import java.io.BufferedReader;
30
import java.io.File;
31
import java.io.IOException;
32
import java.io.InputStream;
33
import java.io.InputStreamReader;
34
import java.io.UncheckedIOException;
35
import java.lang.module.FindException;
36
import java.lang.module.InvalidModuleDescriptorException;
37
import java.lang.module.ModuleDescriptor;
38
import java.lang.module.ModuleDescriptor.Builder;
39
import java.lang.module.ModuleFinder;
40
import java.lang.module.ModuleReference;
41
import java.net.URI;
42
import java.nio.file.DirectoryStream;
43
import java.nio.file.Files;
44
import java.nio.file.NoSuchFileException;
45
import java.nio.file.Path;
46
import java.nio.file.attribute.BasicFileAttributes;
47
import java.util.ArrayList;
48
import java.util.HashMap;
49
import java.util.List;
50
import java.util.Map;
51
import java.util.Objects;
52
import java.util.Optional;
53
import java.util.Set;
54
import java.util.jar.Attributes;
55
import java.util.jar.JarEntry;
56
import java.util.jar.JarFile;
57
import java.util.jar.Manifest;
58
import java.util.regex.Matcher;
59
import java.util.regex.Pattern;
60
import java.util.stream.Collectors;
61
import java.util.zip.ZipException;
62
import java.util.zip.ZipFile;
63
64
import sun.nio.cs.UTF_8;
65
66
import jdk.internal.jmod.JmodFile;
67
import jdk.internal.jmod.JmodFile.Section;
68
import jdk.internal.perf.PerfCounter;
69
70
/**
71
* A {@code ModuleFinder} that locates modules on the file system by searching
72
* a sequence of directories or packaged modules. The ModuleFinder can be
73
* created to work in either the run-time or link-time phases. In both cases it
74
* locates modular JAR and exploded modules. When created for link-time then it
75
* additionally locates modules in JMOD files. The ModuleFinder can also
76
* optionally patch any modules that it locates with a ModulePatcher.
77
*/
78
79
public class ModulePath implements ModuleFinder {
80
private static final String MODULE_INFO = "module-info.class";
81
82
// the version to use for multi-release modular JARs
83
private final Runtime.Version releaseVersion;
84
85
// true for the link phase (supports modules packaged in JMOD format)
86
private final boolean isLinkPhase;
87
88
// for patching modules, can be null
89
private final ModulePatcher patcher;
90
91
// the entries on this module path
92
private final Path[] entries;
93
private int next;
94
95
// map of module name to module reference map for modules already located
96
private final Map<String, ModuleReference> cachedModules = new HashMap<>();
97
98
99
private ModulePath(Runtime.Version version,
100
boolean isLinkPhase,
101
ModulePatcher patcher,
102
Path... entries) {
103
this.releaseVersion = version;
104
this.isLinkPhase = isLinkPhase;
105
this.patcher = patcher;
106
this.entries = entries.clone();
107
for (Path entry : this.entries) {
108
Objects.requireNonNull(entry);
109
}
110
}
111
112
/**
113
* Returns a ModuleFinder that locates modules on the file system by
114
* searching a sequence of directories and/or packaged modules. The modules
115
* may be patched by the given ModulePatcher.
116
*/
117
public static ModuleFinder of(ModulePatcher patcher, Path... entries) {
118
return new ModulePath(JarFile.runtimeVersion(), false, patcher, entries);
119
}
120
121
/**
122
* Returns a ModuleFinder that locates modules on the file system by
123
* searching a sequence of directories and/or packaged modules.
124
*/
125
public static ModuleFinder of(Path... entries) {
126
return of((ModulePatcher)null, entries);
127
}
128
129
/**
130
* Returns a ModuleFinder that locates modules on the file system by
131
* searching a sequence of directories and/or packaged modules.
132
*
133
* @param version The release version to use for multi-release JAR files
134
* @param isLinkPhase {@code true} if the link phase to locate JMOD files
135
*/
136
public static ModuleFinder of(Runtime.Version version,
137
boolean isLinkPhase,
138
Path... entries) {
139
return new ModulePath(version, isLinkPhase, null, entries);
140
}
141
142
143
@Override
144
public Optional<ModuleReference> find(String name) {
145
Objects.requireNonNull(name);
146
147
// try cached modules
148
ModuleReference m = cachedModules.get(name);
149
if (m != null)
150
return Optional.of(m);
151
152
// the module may not have been encountered yet
153
while (hasNextEntry()) {
154
scanNextEntry();
155
m = cachedModules.get(name);
156
if (m != null)
157
return Optional.of(m);
158
}
159
return Optional.empty();
160
}
161
162
@Override
163
public Set<ModuleReference> findAll() {
164
// need to ensure that all entries have been scanned
165
while (hasNextEntry()) {
166
scanNextEntry();
167
}
168
return cachedModules.values().stream().collect(Collectors.toSet());
169
}
170
171
/**
172
* Returns {@code true} if there are additional entries to scan
173
*/
174
private boolean hasNextEntry() {
175
return next < entries.length;
176
}
177
178
/**
179
* Scans the next entry on the module path. A no-op if all entries have
180
* already been scanned.
181
*
182
* @throws FindException if an error occurs scanning the next entry
183
*/
184
private void scanNextEntry() {
185
if (hasNextEntry()) {
186
187
long t0 = System.nanoTime();
188
189
Path entry = entries[next];
190
Map<String, ModuleReference> modules = scan(entry);
191
next++;
192
193
// update cache, ignoring duplicates
194
int initialSize = cachedModules.size();
195
for (Map.Entry<String, ModuleReference> e : modules.entrySet()) {
196
cachedModules.putIfAbsent(e.getKey(), e.getValue());
197
}
198
199
// update counters
200
int added = cachedModules.size() - initialSize;
201
moduleCount.add(added);
202
203
scanTime.addElapsedTimeFrom(t0);
204
}
205
}
206
207
208
/**
209
* Scan the given module path entry. If the entry is a directory then it is
210
* a directory of modules or an exploded module. If the entry is a regular
211
* file then it is assumed to be a packaged module.
212
*
213
* @throws FindException if an error occurs scanning the entry
214
*/
215
private Map<String, ModuleReference> scan(Path entry) {
216
217
BasicFileAttributes attrs;
218
try {
219
attrs = Files.readAttributes(entry, BasicFileAttributes.class);
220
} catch (NoSuchFileException e) {
221
return Map.of();
222
} catch (IOException ioe) {
223
throw new FindException(ioe);
224
}
225
226
try {
227
228
if (attrs.isDirectory()) {
229
Path mi = entry.resolve(MODULE_INFO);
230
if (!Files.exists(mi)) {
231
// assume a directory of modules
232
return scanDirectory(entry);
233
}
234
}
235
236
// packaged or exploded module
237
ModuleReference mref = readModule(entry, attrs);
238
if (mref != null) {
239
String name = mref.descriptor().name();
240
return Map.of(name, mref);
241
}
242
243
// not recognized
244
String msg;
245
if (!isLinkPhase && entry.toString().endsWith(".jmod")) {
246
msg = "JMOD format not supported at execution time";
247
} else {
248
msg = "Module format not recognized";
249
}
250
throw new FindException(msg + ": " + entry);
251
252
} catch (IOException ioe) {
253
throw new FindException(ioe);
254
}
255
}
256
257
258
/**
259
* Scans the given directory for packaged or exploded modules.
260
*
261
* @return a map of module name to ModuleReference for the modules found
262
* in the directory
263
*
264
* @throws IOException if an I/O error occurs
265
* @throws FindException if an error occurs scanning the entry or the
266
* directory contains two or more modules with the same name
267
*/
268
private Map<String, ModuleReference> scanDirectory(Path dir)
269
throws IOException
270
{
271
// The map of name -> mref of modules found in this directory.
272
Map<String, ModuleReference> nameToReference = new HashMap<>();
273
274
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
275
for (Path entry : stream) {
276
BasicFileAttributes attrs;
277
try {
278
attrs = Files.readAttributes(entry, BasicFileAttributes.class);
279
} catch (NoSuchFileException ignore) {
280
// file has been removed or moved, ignore for now
281
continue;
282
}
283
284
ModuleReference mref = readModule(entry, attrs);
285
286
// module found
287
if (mref != null) {
288
// can have at most one version of a module in the directory
289
String name = mref.descriptor().name();
290
ModuleReference previous = nameToReference.put(name, mref);
291
if (previous != null) {
292
String fn1 = fileName(mref);
293
String fn2 = fileName(previous);
294
throw new FindException("Two versions of module "
295
+ name + " found in " + dir
296
+ " (" + fn1 + " and " + fn2 + ")");
297
}
298
}
299
}
300
}
301
302
return nameToReference;
303
}
304
305
306
/**
307
* Reads a packaged or exploded module, returning a {@code ModuleReference}
308
* to the module. Returns {@code null} if the entry is not recognized.
309
*
310
* @throws IOException if an I/O error occurs
311
* @throws FindException if an error occurs parsing its module descriptor
312
*/
313
private ModuleReference readModule(Path entry, BasicFileAttributes attrs)
314
throws IOException
315
{
316
try {
317
318
// exploded module
319
if (attrs.isDirectory()) {
320
return readExplodedModule(entry); // may return null
321
}
322
323
// JAR or JMOD file
324
if (attrs.isRegularFile()) {
325
String fn = entry.getFileName().toString();
326
boolean isDefaultFileSystem = isDefaultFileSystem(entry);
327
328
// JAR file
329
if (fn.endsWith(".jar")) {
330
if (isDefaultFileSystem) {
331
return readJar(entry);
332
} else {
333
// the JAR file is in a custom file system so
334
// need to copy it to the local file system
335
Path tmpdir = Files.createTempDirectory("mlib");
336
Path target = Files.copy(entry, tmpdir.resolve(fn));
337
return readJar(target);
338
}
339
}
340
341
// JMOD file
342
if (isDefaultFileSystem && isLinkPhase && fn.endsWith(".jmod")) {
343
return readJMod(entry);
344
}
345
}
346
347
return null;
348
349
} catch (InvalidModuleDescriptorException e) {
350
throw new FindException("Error reading module: " + entry, e);
351
}
352
}
353
354
/**
355
* Returns a string with the file name of the module if possible.
356
* If the module location is not a file URI then return the URI
357
* as a string.
358
*/
359
private String fileName(ModuleReference mref) {
360
URI uri = mref.location().orElse(null);
361
if (uri != null) {
362
if (uri.getScheme().equalsIgnoreCase("file")) {
363
Path file = Path.of(uri);
364
return file.getFileName().toString();
365
} else {
366
return uri.toString();
367
}
368
} else {
369
return "<unknown>";
370
}
371
}
372
373
// -- JMOD files --
374
375
private Set<String> jmodPackages(JmodFile jf) {
376
return jf.stream()
377
.filter(e -> e.section() == Section.CLASSES)
378
.map(JmodFile.Entry::name)
379
.map(this::toPackageName)
380
.flatMap(Optional::stream)
381
.collect(Collectors.toSet());
382
}
383
384
/**
385
* Returns a {@code ModuleReference} to a module in JMOD file on the
386
* file system.
387
*
388
* @throws IOException
389
* @throws InvalidModuleDescriptorException
390
*/
391
private ModuleReference readJMod(Path file) throws IOException {
392
try (JmodFile jf = new JmodFile(file)) {
393
ModuleInfo.Attributes attrs;
394
try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
395
attrs = ModuleInfo.read(in, () -> jmodPackages(jf));
396
}
397
return ModuleReferences.newJModModule(attrs, file);
398
}
399
}
400
401
402
// -- JAR files --
403
404
private static final String SERVICES_PREFIX = "META-INF/services/";
405
406
private static final Attributes.Name AUTOMATIC_MODULE_NAME
407
= new Attributes.Name("Automatic-Module-Name");
408
409
/**
410
* Returns the service type corresponding to the name of a services
411
* configuration file if it is a legal type name.
412
*
413
* For example, if called with "META-INF/services/p.S" then this method
414
* returns a container with the value "p.S".
415
*/
416
private Optional<String> toServiceName(String cf) {
417
assert cf.startsWith(SERVICES_PREFIX);
418
int index = cf.lastIndexOf("/") + 1;
419
if (index < cf.length()) {
420
String prefix = cf.substring(0, index);
421
if (prefix.equals(SERVICES_PREFIX)) {
422
String sn = cf.substring(index);
423
if (Checks.isClassName(sn))
424
return Optional.of(sn);
425
}
426
}
427
return Optional.empty();
428
}
429
430
/**
431
* Reads the next line from the given reader and trims it of comments and
432
* leading/trailing white space.
433
*
434
* Returns null if the reader is at EOF.
435
*/
436
private String nextLine(BufferedReader reader) throws IOException {
437
String ln = reader.readLine();
438
if (ln != null) {
439
int ci = ln.indexOf('#');
440
if (ci >= 0)
441
ln = ln.substring(0, ci);
442
ln = ln.trim();
443
}
444
return ln;
445
}
446
447
/**
448
* Treat the given JAR file as a module as follows:
449
*
450
* 1. The value of the Automatic-Module-Name attribute is the module name
451
* 2. The version, and the module name when the Automatic-Module-Name
452
* attribute is not present, is derived from the file ame of the JAR file
453
* 3. All packages are derived from the .class files in the JAR file
454
* 4. The contents of any META-INF/services configuration files are mapped
455
* to "provides" declarations
456
* 5. The Main-Class attribute in the main attributes of the JAR manifest
457
* is mapped to the module descriptor mainClass if possible
458
*/
459
private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
460
throws IOException
461
{
462
// Read Automatic-Module-Name attribute if present
463
Manifest man = jf.getManifest();
464
Attributes attrs = null;
465
String moduleName = null;
466
if (man != null) {
467
attrs = man.getMainAttributes();
468
if (attrs != null) {
469
moduleName = attrs.getValue(AUTOMATIC_MODULE_NAME);
470
}
471
}
472
473
// Derive the version, and the module name if needed, from JAR file name
474
String fn = jf.getName();
475
int i = fn.lastIndexOf(File.separator);
476
if (i != -1)
477
fn = fn.substring(i + 1);
478
479
// drop ".jar"
480
String name = fn.substring(0, fn.length() - 4);
481
String vs = null;
482
483
// find first occurrence of -${NUMBER}. or -${NUMBER}$
484
Matcher matcher = Patterns.DASH_VERSION.matcher(name);
485
if (matcher.find()) {
486
int start = matcher.start();
487
488
// attempt to parse the tail as a version string
489
try {
490
String tail = name.substring(start + 1);
491
ModuleDescriptor.Version.parse(tail);
492
vs = tail;
493
} catch (IllegalArgumentException ignore) { }
494
495
name = name.substring(0, start);
496
}
497
498
// Create builder, using the name derived from file name when
499
// Automatic-Module-Name not present
500
Builder builder;
501
if (moduleName != null) {
502
try {
503
builder = ModuleDescriptor.newAutomaticModule(moduleName);
504
} catch (IllegalArgumentException e) {
505
throw new FindException(AUTOMATIC_MODULE_NAME + ": " + e.getMessage());
506
}
507
} else {
508
builder = ModuleDescriptor.newAutomaticModule(cleanModuleName(name));
509
}
510
511
// module version if present
512
if (vs != null)
513
builder.version(vs);
514
515
// scan the names of the entries in the JAR file
516
Map<Boolean, Set<String>> map = jf.versionedStream()
517
.filter(e -> !e.isDirectory())
518
.map(JarEntry::getName)
519
.filter(e -> (e.endsWith(".class") ^ e.startsWith(SERVICES_PREFIX)))
520
.collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX),
521
Collectors.toSet()));
522
523
Set<String> classFiles = map.get(Boolean.FALSE);
524
Set<String> configFiles = map.get(Boolean.TRUE);
525
526
// the packages containing class files
527
Set<String> packages = classFiles.stream()
528
.map(this::toPackageName)
529
.flatMap(Optional::stream)
530
.distinct()
531
.collect(Collectors.toSet());
532
533
// all packages are exported and open
534
builder.packages(packages);
535
536
// map names of service configuration files to service names
537
Set<String> serviceNames = configFiles.stream()
538
.map(this::toServiceName)
539
.flatMap(Optional::stream)
540
.collect(Collectors.toSet());
541
542
// parse each service configuration file
543
for (String sn : serviceNames) {
544
JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
545
List<String> providerClasses = new ArrayList<>();
546
try (InputStream in = jf.getInputStream(entry)) {
547
BufferedReader reader
548
= new BufferedReader(new InputStreamReader(in, UTF_8.INSTANCE));
549
String cn;
550
while ((cn = nextLine(reader)) != null) {
551
if (!cn.isEmpty()) {
552
String pn = packageName(cn);
553
if (!packages.contains(pn)) {
554
String msg = "Provider class " + cn + " not in module";
555
throw new InvalidModuleDescriptorException(msg);
556
}
557
providerClasses.add(cn);
558
}
559
}
560
}
561
if (!providerClasses.isEmpty())
562
builder.provides(sn, providerClasses);
563
}
564
565
// Main-Class attribute if it exists
566
if (attrs != null) {
567
String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
568
if (mainClass != null) {
569
mainClass = mainClass.replace('/', '.');
570
if (Checks.isClassName(mainClass)) {
571
String pn = packageName(mainClass);
572
if (packages.contains(pn)) {
573
builder.mainClass(mainClass);
574
}
575
}
576
}
577
}
578
579
return builder.build();
580
}
581
582
/**
583
* Patterns used to derive the module name from a JAR file name.
584
*/
585
private static class Patterns {
586
static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");
587
static final Pattern NON_ALPHANUM = Pattern.compile("[^A-Za-z0-9]");
588
static final Pattern REPEATING_DOTS = Pattern.compile("(\\.)(\\1)+");
589
static final Pattern LEADING_DOTS = Pattern.compile("^\\.");
590
static final Pattern TRAILING_DOTS = Pattern.compile("\\.$");
591
}
592
593
/**
594
* Clean up candidate module name derived from a JAR file name.
595
*/
596
private static String cleanModuleName(String mn) {
597
// replace non-alphanumeric
598
mn = Patterns.NON_ALPHANUM.matcher(mn).replaceAll(".");
599
600
// collapse repeating dots
601
mn = Patterns.REPEATING_DOTS.matcher(mn).replaceAll(".");
602
603
// drop leading dots
604
if (!mn.isEmpty() && mn.charAt(0) == '.')
605
mn = Patterns.LEADING_DOTS.matcher(mn).replaceAll("");
606
607
// drop trailing dots
608
int len = mn.length();
609
if (len > 0 && mn.charAt(len-1) == '.')
610
mn = Patterns.TRAILING_DOTS.matcher(mn).replaceAll("");
611
612
return mn;
613
}
614
615
private Set<String> jarPackages(JarFile jf) {
616
return jf.versionedStream()
617
.filter(e -> !e.isDirectory())
618
.map(JarEntry::getName)
619
.map(this::toPackageName)
620
.flatMap(Optional::stream)
621
.collect(Collectors.toSet());
622
}
623
624
/**
625
* Returns a {@code ModuleReference} to a module in modular JAR file on
626
* the file system.
627
*
628
* @throws IOException
629
* @throws FindException
630
* @throws InvalidModuleDescriptorException
631
*/
632
private ModuleReference readJar(Path file) throws IOException {
633
try (JarFile jf = new JarFile(file.toFile(),
634
true, // verify
635
ZipFile.OPEN_READ,
636
releaseVersion))
637
{
638
ModuleInfo.Attributes attrs;
639
JarEntry entry = jf.getJarEntry(MODULE_INFO);
640
if (entry == null) {
641
642
// no module-info.class so treat it as automatic module
643
try {
644
ModuleDescriptor md = deriveModuleDescriptor(jf);
645
attrs = new ModuleInfo.Attributes(md, null, null, null);
646
} catch (RuntimeException e) {
647
throw new FindException("Unable to derive module descriptor for "
648
+ jf.getName(), e);
649
}
650
651
} else {
652
attrs = ModuleInfo.read(jf.getInputStream(entry),
653
() -> jarPackages(jf));
654
}
655
656
return ModuleReferences.newJarModule(attrs, patcher, file);
657
} catch (ZipException e) {
658
throw new FindException("Error reading " + file, e);
659
}
660
}
661
662
663
// -- exploded directories --
664
665
private Set<String> explodedPackages(Path dir) {
666
try {
667
return Files.find(dir, Integer.MAX_VALUE,
668
((path, attrs) -> attrs.isRegularFile() && !isHidden(path)))
669
.map(path -> dir.relativize(path))
670
.map(this::toPackageName)
671
.flatMap(Optional::stream)
672
.collect(Collectors.toSet());
673
} catch (IOException x) {
674
throw new UncheckedIOException(x);
675
}
676
}
677
678
/**
679
* Returns a {@code ModuleReference} to an exploded module on the file
680
* system or {@code null} if {@code module-info.class} not found.
681
*
682
* @throws IOException
683
* @throws InvalidModuleDescriptorException
684
*/
685
private ModuleReference readExplodedModule(Path dir) throws IOException {
686
Path mi = dir.resolve(MODULE_INFO);
687
ModuleInfo.Attributes attrs;
688
try (InputStream in = Files.newInputStream(mi)) {
689
attrs = ModuleInfo.read(new BufferedInputStream(in),
690
() -> explodedPackages(dir));
691
} catch (NoSuchFileException e) {
692
// for now
693
return null;
694
}
695
return ModuleReferences.newExplodedModule(attrs, patcher, dir);
696
}
697
698
/**
699
* Maps a type name to its package name.
700
*/
701
private static String packageName(String cn) {
702
int index = cn.lastIndexOf('.');
703
return (index == -1) ? "" : cn.substring(0, index);
704
}
705
706
/**
707
* Maps the name of an entry in a JAR or ZIP file to a package name.
708
*
709
* @throws InvalidModuleDescriptorException if the name is a class file in
710
* the top-level directory of the JAR/ZIP file (and it's not
711
* module-info.class)
712
*/
713
private Optional<String> toPackageName(String name) {
714
assert !name.endsWith("/");
715
int index = name.lastIndexOf("/");
716
if (index == -1) {
717
if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
718
String msg = name + " found in top-level directory"
719
+ " (unnamed package not allowed in module)";
720
throw new InvalidModuleDescriptorException(msg);
721
}
722
return Optional.empty();
723
}
724
725
String pn = name.substring(0, index).replace('/', '.');
726
if (Checks.isPackageName(pn)) {
727
return Optional.of(pn);
728
} else {
729
// not a valid package name
730
return Optional.empty();
731
}
732
}
733
734
/**
735
* Maps the relative path of an entry in an exploded module to a package
736
* name.
737
*
738
* @throws InvalidModuleDescriptorException if the name is a class file in
739
* the top-level directory (and it's not module-info.class)
740
*/
741
private Optional<String> toPackageName(Path file) {
742
assert file.getRoot() == null;
743
744
Path parent = file.getParent();
745
if (parent == null) {
746
String name = file.toString();
747
if (name.endsWith(".class") && !name.equals(MODULE_INFO)) {
748
String msg = name + " found in top-level directory"
749
+ " (unnamed package not allowed in module)";
750
throw new InvalidModuleDescriptorException(msg);
751
}
752
return Optional.empty();
753
}
754
755
String pn = parent.toString().replace(File.separatorChar, '.');
756
if (Checks.isPackageName(pn)) {
757
return Optional.of(pn);
758
} else {
759
// not a valid package name
760
return Optional.empty();
761
}
762
}
763
764
/**
765
* Returns true if the given file exists and is a hidden file
766
*/
767
private boolean isHidden(Path file) {
768
try {
769
return Files.isHidden(file);
770
} catch (IOException ioe) {
771
return false;
772
}
773
}
774
775
776
/**
777
* Return true if a path locates a path in the default file system
778
*/
779
private boolean isDefaultFileSystem(Path path) {
780
return path.getFileSystem().provider()
781
.getScheme().equalsIgnoreCase("file");
782
}
783
784
785
private static final PerfCounter scanTime
786
= PerfCounter.newPerfCounter("jdk.module.finder.modulepath.scanTime");
787
private static final PerfCounter moduleCount
788
= PerfCounter.newPerfCounter("jdk.module.finder.modulepath.modules");
789
}
790
791