1 /* 2 * Copyright (c) 2003, 2018, 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 com.sun.tools.javac.file; 27 28 import java.io.Closeable; 29 import java.io.File; 30 import java.io.FileNotFoundException; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.UncheckedIOException; 34 import java.net.URI; 35 import java.net.URL; 36 import java.net.URLClassLoader; 37 import java.nio.file.DirectoryIteratorException; 38 import java.nio.file.DirectoryStream; 39 import java.nio.file.FileSystem; 40 import java.nio.file.FileSystemNotFoundException; 41 import java.nio.file.FileSystems; 42 import java.nio.file.Files; 43 import java.nio.file.InvalidPathException; 44 import java.nio.file.Path; 45 import java.nio.file.Paths; 46 import java.nio.file.ProviderNotFoundException; 47 import java.nio.file.spi.FileSystemProvider; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.Collection; 51 import java.util.Collections; 52 import java.util.EnumMap; 53 import java.util.EnumSet; 54 import java.util.HashMap; 55 import java.util.HashSet; 56 import java.util.Iterator; 57 import java.util.LinkedHashMap; 58 import java.util.LinkedHashSet; 59 import java.util.List; 60 import java.util.Map; 61 import java.util.Objects; 62 import java.util.NoSuchElementException; 63 import java.util.Set; 64 import java.util.function.Predicate; 65 import java.util.regex.Matcher; 66 import java.util.regex.Pattern; 67 import java.util.stream.Collectors; 68 import java.util.stream.Stream; 69 import java.util.jar.Attributes; 70 import java.util.jar.Manifest; 71 72 import javax.lang.model.SourceVersion; 73 import javax.tools.JavaFileManager; 74 import javax.tools.JavaFileManager.Location; 75 import javax.tools.JavaFileObject; 76 import javax.tools.StandardJavaFileManager; 77 import javax.tools.StandardJavaFileManager.PathFactory; 78 import javax.tools.StandardLocation; 79 80 import jdk.internal.jmod.JmodFile; 81 82 import com.sun.tools.javac.code.Lint; 83 import com.sun.tools.javac.code.Lint.LintCategory; 84 import com.sun.tools.javac.main.Option; 85 import com.sun.tools.javac.resources.CompilerProperties.Errors; 86 import com.sun.tools.javac.resources.CompilerProperties.Warnings; 87 import com.sun.tools.javac.util.DefinedBy; 88 import com.sun.tools.javac.util.DefinedBy.Api; 89 import com.sun.tools.javac.util.JCDiagnostic.Warning; 90 import com.sun.tools.javac.util.ListBuffer; 91 import com.sun.tools.javac.util.Log; 92 import com.sun.tools.javac.jvm.ModuleNameReader; 93 import com.sun.tools.javac.util.Iterators; 94 import com.sun.tools.javac.util.Pair; 95 import com.sun.tools.javac.util.StringUtils; 96 97 import static javax.tools.StandardLocation.PLATFORM_CLASS_PATH; 98 99 import static com.sun.tools.javac.main.Option.BOOT_CLASS_PATH; 100 import static com.sun.tools.javac.main.Option.DJAVA_ENDORSED_DIRS; 101 import static com.sun.tools.javac.main.Option.DJAVA_EXT_DIRS; 102 import static com.sun.tools.javac.main.Option.ENDORSEDDIRS; 103 import static com.sun.tools.javac.main.Option.EXTDIRS; 104 import static com.sun.tools.javac.main.Option.XBOOTCLASSPATH; 105 import static com.sun.tools.javac.main.Option.XBOOTCLASSPATH_APPEND; 106 import static com.sun.tools.javac.main.Option.XBOOTCLASSPATH_PREPEND; 107 108 /** 109 * This class converts command line arguments, environment variables and system properties (in 110 * File.pathSeparator-separated String form) into a boot class path, user class path, and source 111 * path (in {@code Collection<String>} form). 112 * 113 * <p> 114 * <b>This is NOT part of any supported API. If you write code that depends on this, you do so at 115 * your own risk. This code and its internal interfaces are subject to change or deletion without 116 * notice.</b> 117 */ 118 public class Locations { 119 120 /** 121 * The log to use for warning output 122 */ 123 private Log log; 124 125 /** 126 * Access to (possibly cached) file info 127 */ 128 private FSInfo fsInfo; 129 130 /** 131 * Whether to warn about non-existent path elements 132 */ 133 private boolean warn; 134 135 private ModuleNameReader moduleNameReader; 136 137 private PathFactory pathFactory = Paths::get; 138 139 static final Path javaHome = FileSystems.getDefault().getPath(System.getProperty("java.home")); 140 static final Path thisSystemModules = javaHome.resolve("lib").resolve("modules"); 141 142 Map<Path, FileSystem> fileSystems = new LinkedHashMap<>(); 143 List<Closeable> closeables = new ArrayList<>(); 144 private Map<String,String> fsEnv = Collections.emptyMap(); 145 Locations()146 Locations() { 147 initHandlers(); 148 } 149 getPath(String first, String... more)150 Path getPath(String first, String... more) { 151 try { 152 return pathFactory.getPath(first, more); 153 } catch (InvalidPathException ipe) { 154 throw new IllegalArgumentException(ipe); 155 } 156 } 157 close()158 public void close() throws IOException { 159 ListBuffer<IOException> list = new ListBuffer<>(); 160 closeables.forEach(closeable -> { 161 try { 162 closeable.close(); 163 } catch (IOException ex) { 164 list.add(ex); 165 } 166 }); 167 if (list.nonEmpty()) { 168 IOException ex = new IOException(); 169 for (IOException e: list) 170 ex.addSuppressed(e); 171 throw ex; 172 } 173 } 174 update(Log log, boolean warn, FSInfo fsInfo)175 void update(Log log, boolean warn, FSInfo fsInfo) { 176 this.log = log; 177 this.warn = warn; 178 this.fsInfo = fsInfo; 179 } 180 setPathFactory(PathFactory f)181 void setPathFactory(PathFactory f) { 182 pathFactory = f; 183 } 184 isDefaultBootClassPath()185 boolean isDefaultBootClassPath() { 186 BootClassPathLocationHandler h 187 = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH); 188 return h.isDefault(); 189 } 190 191 /** 192 * Split a search path into its elements. Empty path elements will be ignored. 193 * 194 * @param searchPath The search path to be split 195 * @return The elements of the path 196 */ getPathEntries(String searchPath)197 private Iterable<Path> getPathEntries(String searchPath) { 198 return getPathEntries(searchPath, null); 199 } 200 201 /** 202 * Split a search path into its elements. If emptyPathDefault is not null, all empty elements in the 203 * path, including empty elements at either end of the path, will be replaced with the value of 204 * emptyPathDefault. 205 * 206 * @param searchPath The search path to be split 207 * @param emptyPathDefault The value to substitute for empty path elements, or null, to ignore 208 * empty path elements 209 * @return The elements of the path 210 */ getPathEntries(String searchPath, Path emptyPathDefault)211 private Iterable<Path> getPathEntries(String searchPath, Path emptyPathDefault) { 212 ListBuffer<Path> entries = new ListBuffer<>(); 213 for (String s: searchPath.split(Pattern.quote(File.pathSeparator), -1)) { 214 if (s.isEmpty()) { 215 if (emptyPathDefault != null) { 216 entries.add(emptyPathDefault); 217 } 218 } else { 219 try { 220 entries.add(getPath(s)); 221 } catch (IllegalArgumentException e) { 222 if (warn) { 223 log.warning(LintCategory.PATH, Warnings.InvalidPath(s)); 224 } 225 } 226 } 227 } 228 return entries; 229 } 230 setMultiReleaseValue(String multiReleaseValue)231 public void setMultiReleaseValue(String multiReleaseValue) { 232 fsEnv = Collections.singletonMap("multi-release", multiReleaseValue); 233 } 234 contains(Collection<Path> searchPath, Path file)235 private boolean contains(Collection<Path> searchPath, Path file) throws IOException { 236 237 if (searchPath == null) { 238 return false; 239 } 240 241 Path enclosingJar = null; 242 if (file.getFileSystem().provider() == fsInfo.getJarFSProvider()) { 243 URI uri = file.toUri(); 244 if (uri.getScheme().equals("jar")) { 245 String ssp = uri.getSchemeSpecificPart(); 246 int sep = ssp.lastIndexOf("!"); 247 if (ssp.startsWith("file:") && sep > 0) { 248 enclosingJar = Paths.get(URI.create(ssp.substring(0, sep))); 249 } 250 } 251 } 252 253 Path nf = normalize(file); 254 for (Path p : searchPath) { 255 Path np = normalize(p); 256 if (np.getFileSystem() == nf.getFileSystem() 257 && Files.isDirectory(np) 258 && nf.startsWith(np)) { 259 return true; 260 } 261 if (enclosingJar != null 262 && Files.isSameFile(enclosingJar, np)) { 263 return true; 264 } 265 } 266 267 return false; 268 } 269 270 /** 271 * Utility class to help evaluate a path option. Duplicate entries are ignored, jar class paths 272 * can be expanded. 273 */ 274 private class SearchPath extends LinkedHashSet<Path> { 275 276 private static final long serialVersionUID = 0; 277 278 private boolean expandJarClassPaths = false; 279 private final Set<Path> canonicalValues = new HashSet<>(); 280 expandJarClassPaths(boolean x)281 public SearchPath expandJarClassPaths(boolean x) { 282 expandJarClassPaths = x; 283 return this; 284 } 285 286 /** 287 * What to use when path element is the empty string 288 */ 289 private Path emptyPathDefault = null; 290 emptyPathDefault(Path x)291 public SearchPath emptyPathDefault(Path x) { 292 emptyPathDefault = x; 293 return this; 294 } 295 addDirectories(String dirs, boolean warn)296 public SearchPath addDirectories(String dirs, boolean warn) { 297 boolean prev = expandJarClassPaths; 298 expandJarClassPaths = true; 299 try { 300 if (dirs != null) { 301 for (Path dir : getPathEntries(dirs)) { 302 addDirectory(dir, warn); 303 } 304 } 305 return this; 306 } finally { 307 expandJarClassPaths = prev; 308 } 309 } 310 addDirectories(String dirs)311 public SearchPath addDirectories(String dirs) { 312 return addDirectories(dirs, warn); 313 } 314 addDirectory(Path dir, boolean warn)315 private void addDirectory(Path dir, boolean warn) { 316 if (!Files.isDirectory(dir)) { 317 if (warn) { 318 log.warning(Lint.LintCategory.PATH, 319 Warnings.DirPathElementNotFound(dir)); 320 } 321 return; 322 } 323 324 try (Stream<Path> s = Files.list(dir)) { 325 s.filter(Locations.this::isArchive) 326 .forEach(dirEntry -> addFile(dirEntry, warn)); 327 } catch (IOException ignore) { 328 } 329 } 330 addFiles(String files, boolean warn)331 public SearchPath addFiles(String files, boolean warn) { 332 if (files != null) { 333 addFiles(getPathEntries(files, emptyPathDefault), warn); 334 } 335 return this; 336 } 337 addFiles(String files)338 public SearchPath addFiles(String files) { 339 return addFiles(files, warn); 340 } 341 addFiles(Iterable<? extends Path> files, boolean warn)342 public SearchPath addFiles(Iterable<? extends Path> files, boolean warn) { 343 if (files != null) { 344 for (Path file : files) { 345 addFile(file, warn); 346 } 347 } 348 return this; 349 } 350 addFiles(Iterable<? extends Path> files)351 public SearchPath addFiles(Iterable<? extends Path> files) { 352 return addFiles(files, warn); 353 } 354 addFile(Path file, boolean warn)355 public void addFile(Path file, boolean warn) { 356 if (contains(file)) { 357 // discard duplicates 358 return; 359 } 360 361 if (!fsInfo.exists(file)) { 362 /* No such file or directory exists */ 363 if (warn) { 364 log.warning(Lint.LintCategory.PATH, 365 Warnings.PathElementNotFound(file)); 366 } 367 super.add(file); 368 return; 369 } 370 371 Path canonFile = fsInfo.getCanonicalFile(file); 372 if (canonicalValues.contains(canonFile)) { 373 /* Discard duplicates and avoid infinite recursion */ 374 return; 375 } 376 377 if (fsInfo.isFile(file)) { 378 /* File is an ordinary file. */ 379 if ( !file.getFileName().toString().endsWith(".jmod") 380 && !file.endsWith("modules")) { 381 if (!isArchive(file)) { 382 /* Not a recognized extension; open it to see if 383 it looks like a valid zip file. */ 384 try { 385 FileSystems.newFileSystem(file, null).close(); 386 if (warn) { 387 log.warning(Lint.LintCategory.PATH, 388 Warnings.UnexpectedArchiveFile(file)); 389 } 390 } catch (IOException | ProviderNotFoundException e) { 391 // FIXME: include e.getLocalizedMessage in warning 392 if (warn) { 393 log.warning(Lint.LintCategory.PATH, 394 Warnings.InvalidArchiveFile(file)); 395 } 396 return; 397 } 398 } else { 399 if (fsInfo.getJarFSProvider() == null) { 400 log.error(Errors.NoZipfsForArchive(file)); 401 return ; 402 } 403 } 404 } 405 } 406 407 /* Now what we have left is either a directory or a file name 408 conforming to archive naming convention */ 409 super.add(file); 410 canonicalValues.add(canonFile); 411 412 if (expandJarClassPaths && fsInfo.isFile(file) && !file.endsWith("modules")) { 413 addJarClassPath(file, warn); 414 } 415 } 416 417 // Adds referenced classpath elements from a jar's Class-Path 418 // Manifest entry. In some future release, we may want to 419 // update this code to recognize URLs rather than simple 420 // filenames, but if we do, we should redo all path-related code. addJarClassPath(Path jarFile, boolean warn)421 private void addJarClassPath(Path jarFile, boolean warn) { 422 try { 423 for (Path f : fsInfo.getJarClassPath(jarFile)) { 424 addFile(f, warn); 425 } 426 } catch (IOException e) { 427 log.error(Errors.ErrorReadingFile(jarFile, JavacFileManager.getMessage(e))); 428 } 429 } 430 } 431 432 /** 433 * Base class for handling support for the representation of Locations. 434 * 435 * Locations are (by design) opaque handles that can easily be implemented 436 * by enums like StandardLocation. Within JavacFileManager, each Location 437 * has an associated LocationHandler, which provides much of the appropriate 438 * functionality for the corresponding Location. 439 * 440 * @see #initHandlers 441 * @see #getHandler 442 */ 443 protected static abstract class LocationHandler { 444 445 /** 446 * @see JavaFileManager#handleOption 447 */ handleOption(Option option, String value)448 abstract boolean handleOption(Option option, String value); 449 450 /** 451 * @see StandardJavaFileManager#hasLocation 452 */ isSet()453 boolean isSet() { 454 return (getPaths() != null); 455 } 456 isExplicit()457 abstract boolean isExplicit(); 458 459 /** 460 * @see StandardJavaFileManager#getLocation 461 */ getPaths()462 abstract Collection<Path> getPaths(); 463 464 /** 465 * @see StandardJavaFileManager#setLocation 466 */ setPaths(Iterable<? extends Path> paths)467 abstract void setPaths(Iterable<? extends Path> paths) throws IOException; 468 469 /** 470 * @see StandardJavaFileManager#setLocationForModule 471 */ setPathsForModule(String moduleName, Iterable<? extends Path> paths)472 abstract void setPathsForModule(String moduleName, Iterable<? extends Path> paths) 473 throws IOException; 474 475 /** 476 * @see JavaFileManager#getLocationForModule(Location, String) 477 */ getLocationForModule(String moduleName)478 Location getLocationForModule(String moduleName) throws IOException { 479 return null; 480 } 481 482 /** 483 * @see JavaFileManager#getLocationForModule(Location, JavaFileObject, String) 484 */ getLocationForModule(Path file)485 Location getLocationForModule(Path file) throws IOException { 486 return null; 487 } 488 489 /** 490 * @see JavaFileManager#inferModuleName 491 */ inferModuleName()492 String inferModuleName() { 493 return null; 494 } 495 496 /** 497 * @see JavaFileManager#listLocationsForModules 498 */ listLocationsForModules()499 Iterable<Set<Location>> listLocationsForModules() throws IOException { 500 return null; 501 } 502 503 /** 504 * @see JavaFileManager#contains 505 */ contains(Path file)506 abstract boolean contains(Path file) throws IOException; 507 } 508 509 /** 510 * A LocationHandler for a given Location, and associated set of options. 511 */ 512 private static abstract class BasicLocationHandler extends LocationHandler { 513 514 final Location location; 515 final Set<Option> options; 516 517 boolean explicit; 518 519 /** 520 * Create a handler. The location and options provide a way to map from a location or an 521 * option to the corresponding handler. 522 * 523 * @param location the location for which this is the handler 524 * @param options the options affecting this location 525 * @see #initHandlers 526 */ BasicLocationHandler(Location location, Option... options)527 protected BasicLocationHandler(Location location, Option... options) { 528 this.location = location; 529 this.options = options.length == 0 530 ? EnumSet.noneOf(Option.class) 531 : EnumSet.copyOf(Arrays.asList(options)); 532 } 533 534 @Override setPathsForModule(String moduleName, Iterable<? extends Path> files)535 void setPathsForModule(String moduleName, Iterable<? extends Path> files) throws IOException { 536 // should not happen: protected by check in JavacFileManager 537 throw new UnsupportedOperationException("not supported for " + location); 538 } 539 checkSingletonDirectory(Iterable<? extends Path> paths)540 protected Path checkSingletonDirectory(Iterable<? extends Path> paths) throws IOException { 541 Iterator<? extends Path> pathIter = paths.iterator(); 542 if (!pathIter.hasNext()) { 543 throw new IllegalArgumentException("empty path for directory"); 544 } 545 Path path = pathIter.next(); 546 if (pathIter.hasNext()) { 547 throw new IllegalArgumentException("path too long for directory"); 548 } 549 checkDirectory(path); 550 return path; 551 } 552 checkDirectory(Path path)553 protected Path checkDirectory(Path path) throws IOException { 554 Objects.requireNonNull(path); 555 if (!Files.exists(path)) { 556 throw new FileNotFoundException(path + ": does not exist"); 557 } 558 if (!Files.isDirectory(path)) { 559 throw new IOException(path + ": not a directory"); 560 } 561 return path; 562 } 563 564 @Override isExplicit()565 boolean isExplicit() { 566 return explicit; 567 } 568 569 } 570 571 /** 572 * General purpose implementation for output locations, such as -d/CLASS_OUTPUT and 573 * -s/SOURCE_OUTPUT. All options are treated as equivalent (i.e. aliases.) 574 * The value is a single file, possibly null. 575 */ 576 private class OutputLocationHandler extends BasicLocationHandler { 577 578 private Path outputDir; 579 private ModuleTable moduleTable; 580 OutputLocationHandler(Location location, Option... options)581 OutputLocationHandler(Location location, Option... options) { 582 super(location, options); 583 } 584 585 @Override handleOption(Option option, String value)586 boolean handleOption(Option option, String value) { 587 if (!options.contains(option)) { 588 return false; 589 } 590 591 explicit = true; 592 593 // TODO: could/should validate outputDir exists and is a directory 594 // need to decide how best to report issue for benefit of 595 // direct API call on JavaFileManager.handleOption(specifies IAE) 596 // vs. command line decoding. 597 outputDir = (value == null) ? null : getPath(value); 598 return true; 599 } 600 601 @Override getPaths()602 Collection<Path> getPaths() { 603 return (outputDir == null) ? null : Collections.singleton(outputDir); 604 } 605 606 @Override setPaths(Iterable<? extends Path> paths)607 void setPaths(Iterable<? extends Path> paths) throws IOException { 608 if (paths == null) { 609 outputDir = null; 610 } else { 611 explicit = true; 612 outputDir = checkSingletonDirectory(paths); 613 } 614 moduleTable = null; 615 listed = false; 616 } 617 618 @Override getLocationForModule(String name)619 Location getLocationForModule(String name) { 620 if (moduleTable == null) { 621 moduleTable = new ModuleTable(); 622 } 623 ModuleLocationHandler l = moduleTable.get(name); 624 if (l == null) { 625 Path out = outputDir.resolve(name); 626 l = new ModuleLocationHandler(this, location.getName() + "[" + name + "]", 627 name, Collections.singletonList(out), true); 628 moduleTable.add(l); 629 } 630 return l; 631 } 632 633 @Override setPathsForModule(String name, Iterable<? extends Path> paths)634 void setPathsForModule(String name, Iterable<? extends Path> paths) throws IOException { 635 Path out = checkSingletonDirectory(paths); 636 if (moduleTable == null) { 637 moduleTable = new ModuleTable(); 638 } 639 ModuleLocationHandler l = moduleTable.get(name); 640 if (l == null) { 641 l = new ModuleLocationHandler(this, location.getName() + "[" + name + "]", 642 name, Collections.singletonList(out), true); 643 moduleTable.add(l); 644 } else { 645 l.searchPath = Collections.singletonList(out); 646 moduleTable.updatePaths(l); 647 } 648 explicit = true; 649 } 650 651 @Override getLocationForModule(Path file)652 Location getLocationForModule(Path file) { 653 return (moduleTable == null) ? null : moduleTable.get(file); 654 } 655 656 private boolean listed; 657 658 @Override listLocationsForModules()659 Iterable<Set<Location>> listLocationsForModules() throws IOException { 660 if (!listed && outputDir != null) { 661 try (DirectoryStream<Path> stream = Files.newDirectoryStream(outputDir)) { 662 for (Path p : stream) { 663 getLocationForModule(p.getFileName().toString()); 664 } 665 } 666 listed = true; 667 } 668 669 if (moduleTable == null || moduleTable.isEmpty()) 670 return Collections.emptySet(); 671 672 return Collections.singleton(moduleTable.locations()); 673 } 674 675 @Override contains(Path file)676 boolean contains(Path file) throws IOException { 677 if (moduleTable != null) { 678 return moduleTable.contains(file); 679 } else { 680 return (outputDir) != null && normalize(file).startsWith(normalize(outputDir)); 681 } 682 } 683 } 684 685 /** 686 * General purpose implementation for search path locations, 687 * such as -sourcepath/SOURCE_PATH and -processorPath/ANNOTATION_PROCESSOR_PATH. 688 * All options are treated as equivalent (i.e. aliases.) 689 * The value is an ordered set of files and/or directories. 690 */ 691 private class SimpleLocationHandler extends BasicLocationHandler { 692 693 protected Collection<Path> searchPath; 694 SimpleLocationHandler(Location location, Option... options)695 SimpleLocationHandler(Location location, Option... options) { 696 super(location, options); 697 } 698 699 @Override handleOption(Option option, String value)700 boolean handleOption(Option option, String value) { 701 if (!options.contains(option)) { 702 return false; 703 } 704 705 explicit = true; 706 707 searchPath = value == null ? null 708 : Collections.unmodifiableCollection(createPath().addFiles(value)); 709 return true; 710 } 711 712 @Override getPaths()713 Collection<Path> getPaths() { 714 return searchPath; 715 } 716 717 @Override setPaths(Iterable<? extends Path> files)718 void setPaths(Iterable<? extends Path> files) { 719 SearchPath p; 720 if (files == null) { 721 p = computePath(null); 722 } else { 723 explicit = true; 724 p = createPath().addFiles(files); 725 } 726 searchPath = Collections.unmodifiableCollection(p); 727 } 728 computePath(String value)729 protected SearchPath computePath(String value) { 730 return createPath().addFiles(value); 731 } 732 createPath()733 protected SearchPath createPath() { 734 return new SearchPath(); 735 } 736 737 @Override contains(Path file)738 boolean contains(Path file) throws IOException { 739 return Locations.this.contains(searchPath, file); 740 } 741 } 742 743 /** 744 * Subtype of SimpleLocationHandler for -classpath/CLASS_PATH. 745 * If no value is given, a default is provided, based on system properties and other values. 746 */ 747 private class ClassPathLocationHandler extends SimpleLocationHandler { 748 ClassPathLocationHandler()749 ClassPathLocationHandler() { 750 super(StandardLocation.CLASS_PATH, Option.CLASS_PATH); 751 } 752 753 @Override getPaths()754 Collection<Path> getPaths() { 755 lazy(); 756 return searchPath; 757 } 758 759 @Override computePath(String value)760 protected SearchPath computePath(String value) { 761 String cp = value; 762 763 // CLASSPATH environment variable when run from `javac'. 764 if (cp == null) { 765 cp = System.getProperty("env.class.path"); 766 } 767 768 // If invoked via a java VM (not the javac launcher), use the 769 // platform class path 770 if (cp == null && System.getProperty("application.home") == null) { 771 cp = System.getProperty("java.class.path"); 772 } 773 774 // Default to current working directory. 775 if (cp == null) { 776 cp = "."; 777 } 778 779 return createPath().addFiles(cp); 780 } 781 782 @Override createPath()783 protected SearchPath createPath() { 784 return new SearchPath() 785 .expandJarClassPaths(true) // Only search user jars for Class-Paths 786 .emptyPathDefault(getPath(".")); // Empty path elt ==> current directory 787 } 788 lazy()789 private void lazy() { 790 if (searchPath == null) { 791 setPaths(null); 792 } 793 } 794 } 795 796 /** 797 * Custom subtype of LocationHandler for PLATFORM_CLASS_PATH. 798 * Various options are supported for different components of the 799 * platform class path. 800 * Setting a value with setLocation overrides all existing option values. 801 * Setting any option overrides any value set with setLocation, and 802 * reverts to using default values for options that have not been set. 803 * Setting -bootclasspath or -Xbootclasspath overrides any existing 804 * value for -Xbootclasspath/p: and -Xbootclasspath/a:. 805 */ 806 private class BootClassPathLocationHandler extends BasicLocationHandler { 807 808 private Collection<Path> searchPath; 809 final Map<Option, String> optionValues = new EnumMap<>(Option.class); 810 811 /** 812 * Is the bootclasspath the default? 813 */ 814 private boolean isDefault; 815 BootClassPathLocationHandler()816 BootClassPathLocationHandler() { 817 super(StandardLocation.PLATFORM_CLASS_PATH, 818 Option.BOOT_CLASS_PATH, Option.XBOOTCLASSPATH, 819 Option.XBOOTCLASSPATH_PREPEND, 820 Option.XBOOTCLASSPATH_APPEND, 821 Option.ENDORSEDDIRS, Option.DJAVA_ENDORSED_DIRS, 822 Option.EXTDIRS, Option.DJAVA_EXT_DIRS); 823 } 824 isDefault()825 boolean isDefault() { 826 lazy(); 827 return isDefault; 828 } 829 830 @Override handleOption(Option option, String value)831 boolean handleOption(Option option, String value) { 832 if (!options.contains(option)) { 833 return false; 834 } 835 836 explicit = true; 837 838 option = canonicalize(option); 839 optionValues.put(option, value); 840 if (option == BOOT_CLASS_PATH) { 841 optionValues.remove(XBOOTCLASSPATH_PREPEND); 842 optionValues.remove(XBOOTCLASSPATH_APPEND); 843 } 844 searchPath = null; // reset to "uninitialized" 845 return true; 846 } 847 // where 848 // TODO: would be better if option aliasing was handled at a higher 849 // level canonicalize(Option option)850 private Option canonicalize(Option option) { 851 switch (option) { 852 case XBOOTCLASSPATH: 853 return Option.BOOT_CLASS_PATH; 854 case DJAVA_ENDORSED_DIRS: 855 return Option.ENDORSEDDIRS; 856 case DJAVA_EXT_DIRS: 857 return Option.EXTDIRS; 858 default: 859 return option; 860 } 861 } 862 863 @Override getPaths()864 Collection<Path> getPaths() { 865 lazy(); 866 return searchPath; 867 } 868 869 @Override setPaths(Iterable<? extends Path> files)870 void setPaths(Iterable<? extends Path> files) { 871 if (files == null) { 872 searchPath = null; // reset to "uninitialized" 873 } else { 874 isDefault = false; 875 explicit = true; 876 SearchPath p = new SearchPath().addFiles(files, false); 877 searchPath = Collections.unmodifiableCollection(p); 878 optionValues.clear(); 879 } 880 } 881 computePath()882 SearchPath computePath() throws IOException { 883 SearchPath path = new SearchPath(); 884 885 String bootclasspathOpt = optionValues.get(BOOT_CLASS_PATH); 886 String endorseddirsOpt = optionValues.get(ENDORSEDDIRS); 887 String extdirsOpt = optionValues.get(EXTDIRS); 888 String xbootclasspathPrependOpt = optionValues.get(XBOOTCLASSPATH_PREPEND); 889 String xbootclasspathAppendOpt = optionValues.get(XBOOTCLASSPATH_APPEND); 890 path.addFiles(xbootclasspathPrependOpt); 891 892 if (endorseddirsOpt != null) { 893 path.addDirectories(endorseddirsOpt); 894 } else { 895 path.addDirectories(System.getProperty("java.endorsed.dirs"), false); 896 } 897 898 if (bootclasspathOpt != null) { 899 path.addFiles(bootclasspathOpt); 900 } else { 901 // Standard system classes for this compiler's release. 902 Collection<Path> systemClasses = systemClasses(); 903 if (systemClasses != null) { 904 path.addFiles(systemClasses, false); 905 } else { 906 // fallback to the value of sun.boot.class.path 907 String files = System.getProperty("sun.boot.class.path"); 908 path.addFiles(files, false); 909 } 910 } 911 912 path.addFiles(xbootclasspathAppendOpt); 913 914 // Strictly speaking, standard extensions are not bootstrap 915 // classes, but we treat them identically, so we'll pretend 916 // that they are. 917 if (extdirsOpt != null) { 918 path.addDirectories(extdirsOpt); 919 } else { 920 // Add lib/jfxrt.jar to the search path 921 Path jfxrt = javaHome.resolve("lib/jfxrt.jar"); 922 if (Files.exists(jfxrt)) { 923 path.addFile(jfxrt, false); 924 } 925 path.addDirectories(System.getProperty("java.ext.dirs"), false); 926 } 927 928 isDefault = 929 (xbootclasspathPrependOpt == null) 930 && (bootclasspathOpt == null) 931 && (xbootclasspathAppendOpt == null); 932 933 return path; 934 } 935 936 /** 937 * Return a collection of files containing system classes. 938 * Returns {@code null} if not running on a modular image. 939 * 940 * @throws UncheckedIOException if an I/O errors occurs 941 */ systemClasses()942 private Collection<Path> systemClasses() throws IOException { 943 // Return "modules" jimage file if available 944 if (Files.isRegularFile(thisSystemModules)) { 945 return Collections.singleton(thisSystemModules); 946 } 947 948 // Exploded module image 949 Path modules = javaHome.resolve("modules"); 950 if (Files.isDirectory(modules.resolve("java.base"))) { 951 try (Stream<Path> listedModules = Files.list(modules)) { 952 return listedModules.collect(Collectors.toList()); 953 } 954 } 955 956 // not a modular image that we know about 957 return null; 958 } 959 lazy()960 private void lazy() { 961 if (searchPath == null) { 962 try { 963 searchPath = Collections.unmodifiableCollection(computePath()); 964 } catch (IOException e) { 965 // TODO: need better handling here, e.g. javac Abort? 966 throw new UncheckedIOException(e); 967 } 968 } 969 } 970 971 @Override contains(Path file)972 boolean contains(Path file) throws IOException { 973 return Locations.this.contains(searchPath, file); 974 } 975 } 976 977 /** 978 * A LocationHander to represent modules found from a module-oriented 979 * location such as MODULE_SOURCE_PATH, UPGRADE_MODULE_PATH, 980 * SYSTEM_MODULES and MODULE_PATH. 981 * 982 * The Location can be specified to accept overriding classes from the 983 * {@code --patch-module <module>=<path> } parameter. 984 */ 985 private class ModuleLocationHandler extends LocationHandler implements Location { 986 private final LocationHandler parent; 987 private final String name; 988 private final String moduleName; 989 private final boolean output; 990 boolean explicit; 991 Collection<Path> searchPath; 992 ModuleLocationHandler(LocationHandler parent, String name, String moduleName, Collection<Path> searchPath, boolean output)993 ModuleLocationHandler(LocationHandler parent, String name, String moduleName, 994 Collection<Path> searchPath, boolean output) { 995 this.parent = parent; 996 this.name = name; 997 this.moduleName = moduleName; 998 this.searchPath = searchPath; 999 this.output = output; 1000 } 1001 1002 @Override @DefinedBy(Api.COMPILER) getName()1003 public String getName() { 1004 return name; 1005 } 1006 1007 @Override @DefinedBy(Api.COMPILER) isOutputLocation()1008 public boolean isOutputLocation() { 1009 return output; 1010 } 1011 1012 @Override // defined by LocationHandler handleOption(Option option, String value)1013 boolean handleOption(Option option, String value) { 1014 throw new UnsupportedOperationException(); 1015 } 1016 1017 @Override // defined by LocationHandler getPaths()1018 Collection<Path> getPaths() { 1019 return Collections.unmodifiableCollection(searchPath); 1020 } 1021 1022 @Override isExplicit()1023 boolean isExplicit() { 1024 return true; 1025 } 1026 1027 @Override // defined by LocationHandler setPaths(Iterable<? extends Path> paths)1028 void setPaths(Iterable<? extends Path> paths) throws IOException { 1029 // defer to the parent to determine if this is acceptable 1030 parent.setPathsForModule(moduleName, paths); 1031 } 1032 1033 @Override // defined by LocationHandler setPathsForModule(String moduleName, Iterable<? extends Path> paths)1034 void setPathsForModule(String moduleName, Iterable<? extends Path> paths) { 1035 throw new UnsupportedOperationException("not supported for " + name); 1036 } 1037 1038 @Override // defined by LocationHandler inferModuleName()1039 String inferModuleName() { 1040 return moduleName; 1041 } 1042 1043 @Override contains(Path file)1044 boolean contains(Path file) throws IOException { 1045 return Locations.this.contains(searchPath, file); 1046 } 1047 1048 @Override toString()1049 public String toString() { 1050 return name; 1051 } 1052 } 1053 1054 /** 1055 * A table of module location handlers, indexed by name and path. 1056 */ 1057 private class ModuleTable { 1058 private final Map<String, ModuleLocationHandler> nameMap = new LinkedHashMap<>(); 1059 private final Map<Path, ModuleLocationHandler> pathMap = new LinkedHashMap<>(); 1060 add(ModuleLocationHandler h)1061 void add(ModuleLocationHandler h) { 1062 nameMap.put(h.moduleName, h); 1063 for (Path p : h.searchPath) { 1064 pathMap.put(normalize(p), h); 1065 } 1066 } 1067 updatePaths(ModuleLocationHandler h)1068 void updatePaths(ModuleLocationHandler h) { 1069 // use iterator, to be able to remove old entries 1070 for (Iterator<Map.Entry<Path, ModuleLocationHandler>> iter = pathMap.entrySet().iterator(); 1071 iter.hasNext(); ) { 1072 Map.Entry<Path, ModuleLocationHandler> e = iter.next(); 1073 if (e.getValue() == h) { 1074 iter.remove(); 1075 } 1076 } 1077 for (Path p : h.searchPath) { 1078 pathMap.put(normalize(p), h); 1079 } 1080 } 1081 get(String name)1082 ModuleLocationHandler get(String name) { 1083 return nameMap.get(name); 1084 } 1085 get(Path path)1086 ModuleLocationHandler get(Path path) { 1087 while (path != null) { 1088 ModuleLocationHandler l = pathMap.get(path); 1089 1090 if (l != null) 1091 return l; 1092 1093 path = path.getParent(); 1094 } 1095 1096 return null; 1097 } 1098 clear()1099 void clear() { 1100 nameMap.clear(); 1101 pathMap.clear(); 1102 } 1103 isEmpty()1104 boolean isEmpty() { 1105 return nameMap.isEmpty(); 1106 } 1107 contains(Path file)1108 boolean contains(Path file) throws IOException { 1109 return Locations.this.contains(pathMap.keySet(), file); 1110 } 1111 locations()1112 Set<Location> locations() { 1113 return Collections.unmodifiableSet(nameMap.values().stream().collect(Collectors.toSet())); 1114 } 1115 explicitLocations()1116 Set<Location> explicitLocations() { 1117 return Collections.unmodifiableSet(nameMap.entrySet() 1118 .stream() 1119 .filter(e -> e.getValue().explicit) 1120 .map(e -> e.getValue()) 1121 .collect(Collectors.toSet())); 1122 } 1123 } 1124 1125 /** 1126 * A LocationHandler for simple module-oriented search paths, 1127 * like UPGRADE_MODULE_PATH and MODULE_PATH. 1128 */ 1129 private class ModulePathLocationHandler extends SimpleLocationHandler { 1130 private ModuleTable moduleTable; 1131 ModulePathLocationHandler(Location location, Option... options)1132 ModulePathLocationHandler(Location location, Option... options) { 1133 super(location, options); 1134 } 1135 1136 @Override handleOption(Option option, String value)1137 public boolean handleOption(Option option, String value) { 1138 if (!options.contains(option)) { 1139 return false; 1140 } 1141 setPaths(value == null ? null : getPathEntries(value)); 1142 return true; 1143 } 1144 1145 @Override getLocationForModule(String moduleName)1146 public Location getLocationForModule(String moduleName) { 1147 initModuleLocations(); 1148 return moduleTable.get(moduleName); 1149 } 1150 1151 @Override getLocationForModule(Path file)1152 public Location getLocationForModule(Path file) { 1153 initModuleLocations(); 1154 return moduleTable.get(file); 1155 } 1156 1157 @Override listLocationsForModules()1158 Iterable<Set<Location>> listLocationsForModules() { 1159 Set<Location> explicitLocations = moduleTable != null ? 1160 moduleTable.explicitLocations() : Collections.emptySet(); 1161 Iterable<Set<Location>> explicitLocationsList = !explicitLocations.isEmpty() 1162 ? Collections.singletonList(explicitLocations) 1163 : Collections.emptyList(); 1164 1165 if (searchPath == null) 1166 return explicitLocationsList; 1167 1168 Iterable<Set<Location>> searchPathLocations = 1169 () -> new ModulePathIterator(); 1170 return () -> Iterators.createCompoundIterator(Arrays.asList(explicitLocationsList, 1171 searchPathLocations), 1172 Iterable::iterator); 1173 } 1174 1175 @Override contains(Path file)1176 boolean contains(Path file) throws IOException { 1177 if (moduleTable == null) { 1178 initModuleLocations(); 1179 } 1180 return moduleTable.contains(file); 1181 } 1182 1183 @Override setPaths(Iterable<? extends Path> paths)1184 void setPaths(Iterable<? extends Path> paths) { 1185 if (paths != null) { 1186 for (Path p: paths) { 1187 checkValidModulePathEntry(p); 1188 } 1189 } 1190 super.setPaths(paths); 1191 moduleTable = null; 1192 } 1193 1194 @Override setPathsForModule(String name, Iterable<? extends Path> paths)1195 void setPathsForModule(String name, Iterable<? extends Path> paths) throws IOException { 1196 List<Path> checkedPaths = checkPaths(paths); 1197 // how far should we go to validate the paths provide a module? 1198 // e.g. contain module-info with the correct name? 1199 initModuleLocations(); 1200 ModuleLocationHandler l = moduleTable.get(name); 1201 if (l == null) { 1202 l = new ModuleLocationHandler(this, location.getName() + "[" + name + "]", 1203 name, checkedPaths, true); 1204 moduleTable.add(l); 1205 } else { 1206 l.searchPath = checkedPaths; 1207 moduleTable.updatePaths(l); 1208 } 1209 l.explicit = true; 1210 explicit = true; 1211 } 1212 checkPaths(Iterable<? extends Path> paths)1213 private List<Path> checkPaths(Iterable<? extends Path> paths) throws IOException { 1214 Objects.requireNonNull(paths); 1215 List<Path> validPaths = new ArrayList<>(); 1216 for (Path p : paths) { 1217 validPaths.add(checkDirectory(p)); 1218 } 1219 return validPaths; 1220 } 1221 initModuleLocations()1222 private void initModuleLocations() { 1223 if (moduleTable != null) { 1224 return; 1225 } 1226 1227 moduleTable = new ModuleTable(); 1228 1229 for (Set<Location> set : listLocationsForModules()) { 1230 for (Location locn : set) { 1231 if (locn instanceof ModuleLocationHandler) { 1232 ModuleLocationHandler l = (ModuleLocationHandler) locn; 1233 if (!moduleTable.nameMap.containsKey(l.moduleName)) { 1234 moduleTable.add(l); 1235 } 1236 } 1237 } 1238 } 1239 } 1240 checkValidModulePathEntry(Path p)1241 private void checkValidModulePathEntry(Path p) { 1242 if (!Files.exists(p)) { 1243 // warning may be generated later 1244 return; 1245 } 1246 1247 if (Files.isDirectory(p)) { 1248 // either an exploded module or a directory of modules 1249 return; 1250 } 1251 1252 String name = p.getFileName().toString(); 1253 int lastDot = name.lastIndexOf("."); 1254 if (lastDot > 0) { 1255 switch (name.substring(lastDot)) { 1256 case ".jar": 1257 case ".jmod": 1258 return; 1259 } 1260 } 1261 throw new IllegalArgumentException(p.toString()); 1262 } 1263 1264 class ModulePathIterator implements Iterator<Set<Location>> { 1265 Iterator<Path> pathIter = searchPath.iterator(); 1266 int pathIndex = 0; 1267 Set<Location> next = null; 1268 1269 @Override hasNext()1270 public boolean hasNext() { 1271 if (next != null) 1272 return true; 1273 1274 while (next == null) { 1275 if (pathIter.hasNext()) { 1276 Path path = pathIter.next(); 1277 if (Files.isDirectory(path)) { 1278 next = scanDirectory(path); 1279 } else { 1280 next = scanFile(path); 1281 } 1282 pathIndex++; 1283 } else 1284 return false; 1285 } 1286 return true; 1287 } 1288 1289 @Override next()1290 public Set<Location> next() { 1291 hasNext(); 1292 if (next != null) { 1293 Set<Location> result = next; 1294 next = null; 1295 return result; 1296 } 1297 throw new NoSuchElementException(); 1298 } 1299 scanDirectory(Path path)1300 private Set<Location> scanDirectory(Path path) { 1301 Set<Path> paths = new LinkedHashSet<>(); 1302 Path moduleInfoClass = null; 1303 try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) { 1304 for (Path entry: stream) { 1305 if (entry.endsWith("module-info.class")) { 1306 moduleInfoClass = entry; 1307 break; // no need to continue scanning 1308 } 1309 paths.add(entry); 1310 } 1311 } catch (DirectoryIteratorException | IOException ignore) { 1312 log.error(Errors.LocnCantReadDirectory(path)); 1313 return Collections.emptySet(); 1314 } 1315 1316 if (moduleInfoClass != null) { 1317 // It's an exploded module directly on the module path. 1318 // We can't infer module name from the directory name, so have to 1319 // read module-info.class. 1320 try { 1321 String moduleName = readModuleName(moduleInfoClass); 1322 String name = location.getName() 1323 + "[" + pathIndex + ":" + moduleName + "]"; 1324 ModuleLocationHandler l = new ModuleLocationHandler( 1325 ModulePathLocationHandler.this, name, moduleName, 1326 Collections.singletonList(path), false); 1327 return Collections.singleton(l); 1328 } catch (ModuleNameReader.BadClassFile e) { 1329 log.error(Errors.LocnBadModuleInfo(path)); 1330 return Collections.emptySet(); 1331 } catch (IOException e) { 1332 log.error(Errors.LocnCantReadFile(path)); 1333 return Collections.emptySet(); 1334 } 1335 } 1336 1337 // A directory of modules 1338 Set<Location> result = new LinkedHashSet<>(); 1339 int index = 0; 1340 for (Path entry : paths) { 1341 Pair<String,Path> module = inferModuleName(entry); 1342 if (module == null) { 1343 // diagnostic reported if necessary; skip to next 1344 continue; 1345 } 1346 String moduleName = module.fst; 1347 Path modulePath = module.snd; 1348 String name = location.getName() 1349 + "[" + pathIndex + "." + (index++) + ":" + moduleName + "]"; 1350 ModuleLocationHandler l = new ModuleLocationHandler( 1351 ModulePathLocationHandler.this, name, moduleName, 1352 Collections.singletonList(modulePath), false); 1353 result.add(l); 1354 } 1355 return result; 1356 } 1357 scanFile(Path path)1358 private Set<Location> scanFile(Path path) { 1359 Pair<String,Path> module = inferModuleName(path); 1360 if (module == null) { 1361 // diagnostic reported if necessary 1362 return Collections.emptySet(); 1363 } 1364 String moduleName = module.fst; 1365 Path modulePath = module.snd; 1366 String name = location.getName() 1367 + "[" + pathIndex + ":" + moduleName + "]"; 1368 ModuleLocationHandler l = new ModuleLocationHandler( 1369 ModulePathLocationHandler.this, name, moduleName, 1370 Collections.singletonList(modulePath), false); 1371 return Collections.singleton(l); 1372 } 1373 inferModuleName(Path p)1374 private Pair<String,Path> inferModuleName(Path p) { 1375 if (Files.isDirectory(p)) { 1376 if (Files.exists(p.resolve("module-info.class")) || 1377 Files.exists(p.resolve("module-info.sig"))) { 1378 String name = p.getFileName().toString(); 1379 if (SourceVersion.isName(name)) 1380 return new Pair<>(name, p); 1381 } 1382 return null; 1383 } 1384 1385 if (p.getFileName().toString().endsWith(".jar") && fsInfo.exists(p)) { 1386 FileSystemProvider jarFSProvider = fsInfo.getJarFSProvider(); 1387 if (jarFSProvider == null) { 1388 log.error(Errors.NoZipfsForArchive(p)); 1389 return null; 1390 } 1391 try (FileSystem fs = jarFSProvider.newFileSystem(p, fsEnv)) { 1392 Path moduleInfoClass = fs.getPath("module-info.class"); 1393 if (Files.exists(moduleInfoClass)) { 1394 String moduleName = readModuleName(moduleInfoClass); 1395 return new Pair<>(moduleName, p); 1396 } 1397 Path mf = fs.getPath("META-INF/MANIFEST.MF"); 1398 if (Files.exists(mf)) { 1399 try (InputStream in = Files.newInputStream(mf)) { 1400 Manifest man = new Manifest(in); 1401 Attributes attrs = man.getMainAttributes(); 1402 if (attrs != null) { 1403 String moduleName = attrs.getValue(new Attributes.Name("Automatic-Module-Name")); 1404 if (moduleName != null) { 1405 if (isModuleName(moduleName)) { 1406 return new Pair<>(moduleName, p); 1407 } else { 1408 log.error(Errors.LocnCantGetModuleNameForJar(p)); 1409 return null; 1410 } 1411 } 1412 } 1413 } 1414 } 1415 } catch (ModuleNameReader.BadClassFile e) { 1416 log.error(Errors.LocnBadModuleInfo(p)); 1417 return null; 1418 } catch (IOException e) { 1419 log.error(Errors.LocnCantReadFile(p)); 1420 return null; 1421 } 1422 1423 //automatic module: 1424 String fn = p.getFileName().toString(); 1425 //from ModulePath.deriveModuleDescriptor: 1426 1427 // drop .jar 1428 String mn = fn.substring(0, fn.length()-4); 1429 1430 // find first occurrence of -${NUMBER}. or -${NUMBER}$ 1431 Matcher matcher = Pattern.compile("-(\\d+(\\.|$))").matcher(mn); 1432 if (matcher.find()) { 1433 int start = matcher.start(); 1434 1435 mn = mn.substring(0, start); 1436 } 1437 1438 // finally clean up the module name 1439 mn = mn.replaceAll("[^A-Za-z0-9]", ".") // replace non-alphanumeric 1440 .replaceAll("(\\.)(\\1)+", ".") // collapse repeating dots 1441 .replaceAll("^\\.", "") // drop leading dots 1442 .replaceAll("\\.$", ""); // drop trailing dots 1443 1444 1445 if (!mn.isEmpty()) { 1446 return new Pair<>(mn, p); 1447 } 1448 1449 log.error(Errors.LocnCantGetModuleNameForJar(p)); 1450 return null; 1451 } 1452 1453 if (p.getFileName().toString().endsWith(".jmod")) { 1454 try { 1455 // check if the JMOD file is valid 1456 JmodFile.checkMagic(p); 1457 1458 // No JMOD file system. Use JarFileSystem to 1459 // workaround for now 1460 FileSystem fs = fileSystems.get(p); 1461 if (fs == null) { 1462 FileSystemProvider jarFSProvider = fsInfo.getJarFSProvider(); 1463 if (jarFSProvider == null) { 1464 log.error(Errors.LocnCantReadFile(p)); 1465 return null; 1466 } 1467 fs = jarFSProvider.newFileSystem(p, Collections.emptyMap()); 1468 try { 1469 Path moduleInfoClass = fs.getPath("classes/module-info.class"); 1470 String moduleName = readModuleName(moduleInfoClass); 1471 Path modulePath = fs.getPath("classes"); 1472 fileSystems.put(p, fs); 1473 closeables.add(fs); 1474 fs = null; // prevent fs being closed in the finally clause 1475 return new Pair<>(moduleName, modulePath); 1476 } finally { 1477 if (fs != null) 1478 fs.close(); 1479 } 1480 } 1481 } catch (ModuleNameReader.BadClassFile e) { 1482 log.error(Errors.LocnBadModuleInfo(p)); 1483 } catch (IOException e) { 1484 log.error(Errors.LocnCantReadFile(p)); 1485 return null; 1486 } 1487 } 1488 1489 if (warn && false) { // temp disable, when enabled, massage examples.not-yet.txt suitably. 1490 log.warning(Warnings.LocnUnknownFileOnModulePath(p)); 1491 } 1492 return null; 1493 } 1494 readModuleName(Path path)1495 private String readModuleName(Path path) throws IOException, ModuleNameReader.BadClassFile { 1496 if (moduleNameReader == null) 1497 moduleNameReader = new ModuleNameReader(); 1498 return moduleNameReader.readModuleName(path); 1499 } 1500 } 1501 1502 //from jdk.internal.module.Checks: 1503 /** 1504 * Returns {@code true} if the given name is a legal module name. 1505 */ isModuleName(String name)1506 private boolean isModuleName(String name) { 1507 int next; 1508 int off = 0; 1509 while ((next = name.indexOf('.', off)) != -1) { 1510 String id = name.substring(off, next); 1511 if (!SourceVersion.isName(id)) 1512 return false; 1513 off = next+1; 1514 } 1515 String last = name.substring(off); 1516 return SourceVersion.isName(last); 1517 } 1518 } 1519 1520 private class ModuleSourcePathLocationHandler extends BasicLocationHandler { 1521 private ModuleTable moduleTable; 1522 private List<Path> paths; 1523 ModuleSourcePathLocationHandler()1524 ModuleSourcePathLocationHandler() { 1525 super(StandardLocation.MODULE_SOURCE_PATH, 1526 Option.MODULE_SOURCE_PATH); 1527 } 1528 1529 @Override handleOption(Option option, String value)1530 boolean handleOption(Option option, String value) { 1531 explicit = true; 1532 init(value); 1533 return true; 1534 } 1535 init(String value)1536 void init(String value) { 1537 Collection<String> segments = new ArrayList<>(); 1538 for (String s: value.split(File.pathSeparator)) { 1539 expandBraces(s, segments); 1540 } 1541 1542 Map<String, List<Path>> map = new LinkedHashMap<>(); 1543 List<Path> noSuffixPaths = new ArrayList<>(); 1544 boolean anySuffix = false; 1545 final String MARKER = "*"; 1546 for (String seg: segments) { 1547 int markStart = seg.indexOf(MARKER); 1548 if (markStart == -1) { 1549 Path p = getPath(seg); 1550 add(map, p, null); 1551 noSuffixPaths.add(p); 1552 } else { 1553 if (markStart == 0 || !isSeparator(seg.charAt(markStart - 1))) { 1554 throw new IllegalArgumentException("illegal use of " + MARKER + " in " + seg); 1555 } 1556 Path prefix = getPath(seg.substring(0, markStart - 1)); 1557 Path suffix; 1558 int markEnd = markStart + MARKER.length(); 1559 if (markEnd == seg.length()) { 1560 suffix = null; 1561 } else if (!isSeparator(seg.charAt(markEnd)) 1562 || seg.indexOf(MARKER, markEnd) != -1) { 1563 throw new IllegalArgumentException("illegal use of " + MARKER + " in " + seg); 1564 } else { 1565 suffix = getPath(seg.substring(markEnd + 1)); 1566 anySuffix = true; 1567 } 1568 add(map, prefix, suffix); 1569 if (suffix == null) { 1570 noSuffixPaths.add(prefix); 1571 } 1572 } 1573 } 1574 1575 initModuleTable(map); 1576 paths = anySuffix ? null : noSuffixPaths; 1577 } 1578 initModuleTable(Map<String, List<Path>> map)1579 private void initModuleTable(Map<String, List<Path>> map) { 1580 moduleTable = new ModuleTable(); 1581 map.forEach((modName, modPath) -> { 1582 boolean hasModuleInfo = modPath.stream().anyMatch(checkModuleInfo); 1583 if (hasModuleInfo) { 1584 String locnName = location.getName() + "[" + modName + "]"; 1585 ModuleLocationHandler l = new ModuleLocationHandler(this, locnName, modName, 1586 modPath, false); 1587 moduleTable.add(l); 1588 } 1589 }); 1590 } 1591 //where: 1592 private final Predicate<Path> checkModuleInfo = 1593 p -> Files.exists(p.resolve("module-info.java")); 1594 1595 isSeparator(char ch)1596 private boolean isSeparator(char ch) { 1597 // allow both separators on Windows 1598 return (ch == File.separatorChar) || (ch == '/'); 1599 } 1600 add(Map<String, List<Path>> map, Path prefix, Path suffix)1601 void add(Map<String, List<Path>> map, Path prefix, Path suffix) { 1602 if (!Files.isDirectory(prefix)) { 1603 if (warn) { 1604 Warning key = Files.exists(prefix) 1605 ? Warnings.DirPathElementNotDirectory(prefix) 1606 : Warnings.DirPathElementNotFound(prefix); 1607 log.warning(Lint.LintCategory.PATH, key); 1608 } 1609 return; 1610 } 1611 try (DirectoryStream<Path> stream = Files.newDirectoryStream(prefix, path -> Files.isDirectory(path))) { 1612 for (Path entry: stream) { 1613 Path path = (suffix == null) ? entry : entry.resolve(suffix); 1614 if (Files.isDirectory(path)) { 1615 String name = entry.getFileName().toString(); 1616 List<Path> paths = map.get(name); 1617 if (paths == null) 1618 map.put(name, paths = new ArrayList<>()); 1619 paths.add(path); 1620 } 1621 } 1622 } catch (IOException e) { 1623 // TODO? What to do? 1624 System.err.println(e); 1625 } 1626 } 1627 expandBraces(String value, Collection<String> results)1628 private void expandBraces(String value, Collection<String> results) { 1629 int depth = 0; 1630 int start = -1; 1631 String prefix = null; 1632 String suffix = null; 1633 for (int i = 0; i < value.length(); i++) { 1634 switch (value.charAt(i)) { 1635 case '{': 1636 depth++; 1637 if (depth == 1) { 1638 prefix = value.substring(0, i); 1639 suffix = value.substring(getMatchingBrace(value, i) + 1); 1640 start = i + 1; 1641 } 1642 break; 1643 1644 case ',': 1645 if (depth == 1) { 1646 String elem = value.substring(start, i); 1647 expandBraces(prefix + elem + suffix, results); 1648 start = i + 1; 1649 } 1650 break; 1651 1652 case '}': 1653 switch (depth) { 1654 case 0: 1655 throw new IllegalArgumentException("mismatched braces"); 1656 1657 case 1: 1658 String elem = value.substring(start, i); 1659 expandBraces(prefix + elem + suffix, results); 1660 return; 1661 1662 default: 1663 depth--; 1664 } 1665 break; 1666 } 1667 } 1668 if (depth > 0) 1669 throw new IllegalArgumentException("mismatched braces"); 1670 results.add(value); 1671 } 1672 getMatchingBrace(String value, int offset)1673 int getMatchingBrace(String value, int offset) { 1674 int depth = 1; 1675 for (int i = offset + 1; i < value.length(); i++) { 1676 switch (value.charAt(i)) { 1677 case '{': 1678 depth++; 1679 break; 1680 1681 case '}': 1682 if (--depth == 0) 1683 return i; 1684 break; 1685 } 1686 } 1687 throw new IllegalArgumentException("mismatched braces"); 1688 } 1689 1690 @Override isSet()1691 boolean isSet() { 1692 return (moduleTable != null); 1693 } 1694 1695 @Override getPaths()1696 Collection<Path> getPaths() { 1697 if (paths == null) { 1698 // This may occur for a complex setting with --module-source-path option 1699 // i.e. one that cannot be represented by a simple series of paths. 1700 throw new IllegalStateException("paths not available"); 1701 } 1702 return paths; 1703 } 1704 1705 @Override setPaths(Iterable<? extends Path> files)1706 void setPaths(Iterable<? extends Path> files) throws IOException { 1707 Map<String, List<Path>> map = new LinkedHashMap<>(); 1708 List<Path> newPaths = new ArrayList<>(); 1709 for (Path file : files) { 1710 add(map, file, null); 1711 newPaths.add(file); 1712 } 1713 1714 initModuleTable(map); 1715 explicit = true; 1716 paths = Collections.unmodifiableList(newPaths); 1717 } 1718 1719 @Override setPathsForModule(String name, Iterable<? extends Path> paths)1720 void setPathsForModule(String name, Iterable<? extends Path> paths) throws IOException { 1721 List<Path> validPaths = checkPaths(paths); 1722 1723 if (moduleTable == null) 1724 moduleTable = new ModuleTable(); 1725 1726 ModuleLocationHandler l = moduleTable.get(name); 1727 if (l == null) { 1728 l = new ModuleLocationHandler(this, 1729 location.getName() + "[" + name + "]", 1730 name, 1731 validPaths, 1732 true); 1733 moduleTable.add(l); 1734 } else { 1735 l.searchPath = validPaths; 1736 moduleTable.updatePaths(l); 1737 } 1738 explicit = true; 1739 } 1740 checkPaths(Iterable<? extends Path> paths)1741 private List<Path> checkPaths(Iterable<? extends Path> paths) throws IOException { 1742 Objects.requireNonNull(paths); 1743 List<Path> validPaths = new ArrayList<>(); 1744 for (Path p : paths) { 1745 validPaths.add(checkDirectory(p)); 1746 } 1747 return validPaths; 1748 } 1749 1750 @Override getLocationForModule(String name)1751 Location getLocationForModule(String name) { 1752 return (moduleTable == null) ? null : moduleTable.get(name); 1753 } 1754 1755 @Override getLocationForModule(Path file)1756 Location getLocationForModule(Path file) { 1757 return (moduleTable == null) ? null : moduleTable.get(file); 1758 } 1759 1760 @Override listLocationsForModules()1761 Iterable<Set<Location>> listLocationsForModules() { 1762 if (moduleTable == null) 1763 return Collections.emptySet(); 1764 1765 return Collections.singleton(moduleTable.locations()); 1766 } 1767 1768 @Override contains(Path file)1769 boolean contains(Path file) throws IOException { 1770 return (moduleTable == null) ? false : moduleTable.contains(file); 1771 } 1772 1773 } 1774 1775 private class SystemModulesLocationHandler extends BasicLocationHandler { 1776 private Path systemJavaHome; 1777 private Path modules; 1778 private ModuleTable moduleTable; 1779 SystemModulesLocationHandler()1780 SystemModulesLocationHandler() { 1781 super(StandardLocation.SYSTEM_MODULES, Option.SYSTEM); 1782 systemJavaHome = Locations.javaHome; 1783 } 1784 1785 @Override handleOption(Option option, String value)1786 boolean handleOption(Option option, String value) { 1787 if (!options.contains(option)) { 1788 return false; 1789 } 1790 1791 explicit = true; 1792 1793 if (value == null) { 1794 systemJavaHome = Locations.javaHome; 1795 } else if (value.equals("none")) { 1796 systemJavaHome = null; 1797 } else { 1798 update(getPath(value)); 1799 } 1800 1801 modules = null; 1802 return true; 1803 } 1804 1805 @Override getPaths()1806 Collection<Path> getPaths() { 1807 return (systemJavaHome == null) ? null : Collections.singleton(systemJavaHome); 1808 } 1809 1810 @Override setPaths(Iterable<? extends Path> files)1811 void setPaths(Iterable<? extends Path> files) throws IOException { 1812 if (files == null) { 1813 systemJavaHome = null; 1814 } else { 1815 explicit = true; 1816 1817 Path dir = checkSingletonDirectory(files); 1818 update(dir); 1819 } 1820 } 1821 1822 @Override setPathsForModule(String name, Iterable<? extends Path> paths)1823 void setPathsForModule(String name, Iterable<? extends Path> paths) throws IOException { 1824 List<Path> checkedPaths = checkPaths(paths); 1825 initSystemModules(); 1826 ModuleLocationHandler l = moduleTable.get(name); 1827 if (l == null) { 1828 l = new ModuleLocationHandler(this, 1829 location.getName() + "[" + name + "]", 1830 name, 1831 checkedPaths, 1832 true); 1833 moduleTable.add(l); 1834 } else { 1835 l.searchPath = checkedPaths; 1836 moduleTable.updatePaths(l); 1837 } 1838 explicit = true; 1839 } 1840 checkPaths(Iterable<? extends Path> paths)1841 private List<Path> checkPaths(Iterable<? extends Path> paths) throws IOException { 1842 Objects.requireNonNull(paths); 1843 List<Path> validPaths = new ArrayList<>(); 1844 for (Path p : paths) { 1845 validPaths.add(checkDirectory(p)); 1846 } 1847 return validPaths; 1848 } 1849 update(Path p)1850 private void update(Path p) { 1851 if (!isCurrentPlatform(p) && !Files.exists(p.resolve("lib").resolve("jrt-fs.jar")) && 1852 !Files.exists(systemJavaHome.resolve("modules"))) 1853 throw new IllegalArgumentException(p.toString()); 1854 systemJavaHome = p; 1855 modules = null; 1856 } 1857 isCurrentPlatform(Path p)1858 private boolean isCurrentPlatform(Path p) { 1859 try { 1860 return Files.isSameFile(p, Locations.javaHome); 1861 } catch (IOException ex) { 1862 throw new IllegalArgumentException(p.toString(), ex); 1863 } 1864 } 1865 1866 @Override getLocationForModule(String name)1867 Location getLocationForModule(String name) throws IOException { 1868 initSystemModules(); 1869 return moduleTable.get(name); 1870 } 1871 1872 @Override getLocationForModule(Path file)1873 Location getLocationForModule(Path file) throws IOException { 1874 initSystemModules(); 1875 return moduleTable.get(file); 1876 } 1877 1878 @Override listLocationsForModules()1879 Iterable<Set<Location>> listLocationsForModules() throws IOException { 1880 initSystemModules(); 1881 return Collections.singleton(moduleTable.locations()); 1882 } 1883 1884 @Override contains(Path file)1885 boolean contains(Path file) throws IOException { 1886 initSystemModules(); 1887 return moduleTable.contains(file); 1888 } 1889 initSystemModules()1890 private void initSystemModules() throws IOException { 1891 if (moduleTable != null) 1892 return; 1893 1894 if (systemJavaHome == null) { 1895 moduleTable = new ModuleTable(); 1896 return; 1897 } 1898 1899 if (modules == null) { 1900 try { 1901 URI jrtURI = URI.create("jrt:/"); 1902 FileSystem jrtfs; 1903 1904 if (isCurrentPlatform(systemJavaHome)) { 1905 jrtfs = FileSystems.getFileSystem(jrtURI); 1906 } else { 1907 try { 1908 Map<String, String> attrMap = 1909 Collections.singletonMap("java.home", systemJavaHome.toString()); 1910 jrtfs = FileSystems.newFileSystem(jrtURI, attrMap); 1911 } catch (ProviderNotFoundException ex) { 1912 URL javaHomeURL = systemJavaHome.resolve("jrt-fs.jar").toUri().toURL(); 1913 ClassLoader currentLoader = Locations.class.getClassLoader(); 1914 URLClassLoader fsLoader = 1915 new URLClassLoader(new URL[] {javaHomeURL}, currentLoader); 1916 1917 jrtfs = FileSystems.newFileSystem(jrtURI, Collections.emptyMap(), fsLoader); 1918 1919 closeables.add(fsLoader); 1920 } 1921 1922 closeables.add(jrtfs); 1923 } 1924 1925 modules = jrtfs.getPath("/modules"); 1926 } catch (FileSystemNotFoundException | ProviderNotFoundException e) { 1927 modules = systemJavaHome.resolve("modules"); 1928 if (!Files.exists(modules)) 1929 throw new IOException("can't find system classes", e); 1930 } 1931 } 1932 1933 moduleTable = new ModuleTable(); 1934 try (DirectoryStream<Path> stream = Files.newDirectoryStream(modules, Files::isDirectory)) { 1935 for (Path entry : stream) { 1936 String moduleName = entry.getFileName().toString(); 1937 String name = location.getName() + "[" + moduleName + "]"; 1938 ModuleLocationHandler h = new ModuleLocationHandler(this, 1939 name, moduleName, Collections.singletonList(entry), false); 1940 moduleTable.add(h); 1941 } 1942 } 1943 } 1944 } 1945 1946 private class PatchModulesLocationHandler extends BasicLocationHandler { 1947 private final ModuleTable moduleTable = new ModuleTable(); 1948 PatchModulesLocationHandler()1949 PatchModulesLocationHandler() { 1950 super(StandardLocation.PATCH_MODULE_PATH, Option.PATCH_MODULE); 1951 } 1952 1953 @Override handleOption(Option option, String value)1954 boolean handleOption(Option option, String value) { 1955 if (!options.contains(option)) { 1956 return false; 1957 } 1958 1959 explicit = true; 1960 1961 moduleTable.clear(); 1962 1963 // Allow an extended syntax for --patch-module consisting of a series 1964 // of values separated by NULL characters. This is to facilitate 1965 // supporting deferred file manager options on the command line. 1966 // See Option.PATCH_MODULE for the code that composes these multiple 1967 // values. 1968 for (String v : value.split("\0")) { 1969 int eq = v.indexOf('='); 1970 if (eq > 0) { 1971 String moduleName = v.substring(0, eq); 1972 SearchPath mPatchPath = new SearchPath() 1973 .addFiles(v.substring(eq + 1)); 1974 String name = location.getName() + "[" + moduleName + "]"; 1975 ModuleLocationHandler h = new ModuleLocationHandler(this, name, 1976 moduleName, mPatchPath, false); 1977 moduleTable.add(h); 1978 } else { 1979 // Should not be able to get here; 1980 // this should be caught and handled in Option.PATCH_MODULE 1981 log.error(Errors.LocnInvalidArgForXpatch(value)); 1982 } 1983 } 1984 1985 return true; 1986 } 1987 1988 @Override isSet()1989 boolean isSet() { 1990 return !moduleTable.isEmpty(); 1991 } 1992 1993 @Override getPaths()1994 Collection<Path> getPaths() { 1995 throw new UnsupportedOperationException(); 1996 } 1997 1998 @Override setPaths(Iterable<? extends Path> files)1999 void setPaths(Iterable<? extends Path> files) throws IOException { 2000 throw new UnsupportedOperationException(); 2001 } 2002 2003 @Override // defined by LocationHandler setPathsForModule(String moduleName, Iterable<? extends Path> files)2004 void setPathsForModule(String moduleName, Iterable<? extends Path> files) throws IOException { 2005 throw new UnsupportedOperationException(); // not yet 2006 } 2007 2008 @Override getLocationForModule(String name)2009 Location getLocationForModule(String name) throws IOException { 2010 return moduleTable.get(name); 2011 } 2012 2013 @Override getLocationForModule(Path file)2014 Location getLocationForModule(Path file) throws IOException { 2015 return moduleTable.get(file); 2016 } 2017 2018 @Override listLocationsForModules()2019 Iterable<Set<Location>> listLocationsForModules() throws IOException { 2020 return Collections.singleton(moduleTable.locations()); 2021 } 2022 2023 @Override contains(Path file)2024 boolean contains(Path file) throws IOException { 2025 return moduleTable.contains(file); 2026 } 2027 } 2028 2029 Map<Location, LocationHandler> handlersForLocation; 2030 Map<Option, LocationHandler> handlersForOption; 2031 initHandlers()2032 void initHandlers() { 2033 handlersForLocation = new HashMap<>(); 2034 handlersForOption = new EnumMap<>(Option.class); 2035 2036 BasicLocationHandler[] handlers = { 2037 new BootClassPathLocationHandler(), 2038 new ClassPathLocationHandler(), 2039 new SimpleLocationHandler(StandardLocation.SOURCE_PATH, Option.SOURCE_PATH), 2040 new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_PATH, Option.PROCESSOR_PATH), 2041 new SimpleLocationHandler(StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH, Option.PROCESSOR_MODULE_PATH), 2042 new OutputLocationHandler(StandardLocation.CLASS_OUTPUT, Option.D), 2043 new OutputLocationHandler(StandardLocation.SOURCE_OUTPUT, Option.S), 2044 new OutputLocationHandler(StandardLocation.NATIVE_HEADER_OUTPUT, Option.H), 2045 new ModuleSourcePathLocationHandler(), 2046 new PatchModulesLocationHandler(), 2047 new ModulePathLocationHandler(StandardLocation.UPGRADE_MODULE_PATH, Option.UPGRADE_MODULE_PATH), 2048 new ModulePathLocationHandler(StandardLocation.MODULE_PATH, Option.MODULE_PATH), 2049 new SystemModulesLocationHandler(), 2050 }; 2051 2052 for (BasicLocationHandler h : handlers) { 2053 handlersForLocation.put(h.location, h); 2054 for (Option o : h.options) { 2055 handlersForOption.put(o, h); 2056 } 2057 } 2058 } 2059 handleOption(Option option, String value)2060 boolean handleOption(Option option, String value) { 2061 LocationHandler h = handlersForOption.get(option); 2062 return (h == null ? false : h.handleOption(option, value)); 2063 } 2064 hasLocation(Location location)2065 boolean hasLocation(Location location) { 2066 LocationHandler h = getHandler(location); 2067 return (h == null ? false : h.isSet()); 2068 } 2069 hasExplicitLocation(Location location)2070 boolean hasExplicitLocation(Location location) { 2071 LocationHandler h = getHandler(location); 2072 return (h == null ? false : h.isExplicit()); 2073 } 2074 getLocation(Location location)2075 Collection<Path> getLocation(Location location) { 2076 LocationHandler h = getHandler(location); 2077 return (h == null ? null : h.getPaths()); 2078 } 2079 getOutputLocation(Location location)2080 Path getOutputLocation(Location location) { 2081 if (!location.isOutputLocation()) { 2082 throw new IllegalArgumentException(); 2083 } 2084 LocationHandler h = getHandler(location); 2085 return ((OutputLocationHandler) h).outputDir; 2086 } 2087 setLocation(Location location, Iterable<? extends Path> files)2088 void setLocation(Location location, Iterable<? extends Path> files) throws IOException { 2089 LocationHandler h = getHandler(location); 2090 if (h == null) { 2091 if (location.isOutputLocation()) { 2092 h = new OutputLocationHandler(location); 2093 } else { 2094 h = new SimpleLocationHandler(location); 2095 } 2096 handlersForLocation.put(location, h); 2097 } 2098 h.setPaths(files); 2099 } 2100 getLocationForModule(Location location, String name)2101 Location getLocationForModule(Location location, String name) throws IOException { 2102 LocationHandler h = getHandler(location); 2103 return (h == null ? null : h.getLocationForModule(name)); 2104 } 2105 getLocationForModule(Location location, Path file)2106 Location getLocationForModule(Location location, Path file) throws IOException { 2107 LocationHandler h = getHandler(location); 2108 return (h == null ? null : h.getLocationForModule(file)); 2109 } 2110 setLocationForModule(Location location, String moduleName, Iterable<? extends Path> files)2111 void setLocationForModule(Location location, String moduleName, 2112 Iterable<? extends Path> files) throws IOException { 2113 LocationHandler h = getHandler(location); 2114 if (h == null) { 2115 if (location.isOutputLocation()) { 2116 h = new OutputLocationHandler(location); 2117 } else { 2118 h = new ModulePathLocationHandler(location); 2119 } 2120 handlersForLocation.put(location, h); 2121 } 2122 h.setPathsForModule(moduleName, files); 2123 } 2124 inferModuleName(Location location)2125 String inferModuleName(Location location) { 2126 LocationHandler h = getHandler(location); 2127 return (h == null ? null : h.inferModuleName()); 2128 } 2129 listLocationsForModules(Location location)2130 Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException { 2131 LocationHandler h = getHandler(location); 2132 return (h == null ? null : h.listLocationsForModules()); 2133 } 2134 contains(Location location, Path file)2135 boolean contains(Location location, Path file) throws IOException { 2136 LocationHandler h = getHandler(location); 2137 if (h == null) 2138 throw new IllegalArgumentException("unknown location"); 2139 return h.contains(file); 2140 } 2141 getHandler(Location location)2142 protected LocationHandler getHandler(Location location) { 2143 Objects.requireNonNull(location); 2144 return (location instanceof LocationHandler) 2145 ? (LocationHandler) location 2146 : handlersForLocation.get(location); 2147 } 2148 2149 /** 2150 * Is this the name of an archive file? 2151 */ isArchive(Path file)2152 private boolean isArchive(Path file) { 2153 String n = StringUtils.toLowerCase(file.getFileName().toString()); 2154 return fsInfo.isFile(file) 2155 && (n.endsWith(".jar") || n.endsWith(".zip")); 2156 } 2157 normalize(Path p)2158 static Path normalize(Path p) { 2159 try { 2160 return p.toRealPath(); 2161 } catch (IOException e) { 2162 return p.toAbsolutePath().normalize(); 2163 } 2164 } 2165 } 2166