langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java
changeset 27858 443efec4bf09
parent 27579 d1a63c99cdd5
parent 27852 2e6ad0e4fe20
child 28332 cd3ea1087d2b
equal deleted inserted replaced
27581:9fffb959eb41 27858:443efec4bf09
    31 import java.net.MalformedURLException;
    31 import java.net.MalformedURLException;
    32 import java.net.URL;
    32 import java.net.URL;
    33 import java.nio.file.Files;
    33 import java.nio.file.Files;
    34 import java.nio.file.Path;
    34 import java.nio.file.Path;
    35 import java.nio.file.Paths;
    35 import java.nio.file.Paths;
       
    36 import java.util.ArrayList;
    36 import java.util.Arrays;
    37 import java.util.Arrays;
    37 import java.util.Collection;
    38 import java.util.Collection;
    38 import java.util.Collections;
    39 import java.util.Collections;
    39 import java.util.EnumMap;
    40 import java.util.EnumMap;
    40 import java.util.EnumSet;
    41 import java.util.EnumSet;
    42 import java.util.HashSet;
    43 import java.util.HashSet;
    43 import java.util.Iterator;
    44 import java.util.Iterator;
    44 import java.util.LinkedHashSet;
    45 import java.util.LinkedHashSet;
    45 import java.util.Map;
    46 import java.util.Map;
    46 import java.util.Set;
    47 import java.util.Set;
    47 import java.util.StringTokenizer;
    48 import java.util.regex.Pattern;
    48 import java.util.stream.Collectors;
    49 import java.util.stream.Collectors;
    49 import java.util.stream.Stream;
    50 import java.util.stream.Stream;
    50 import java.util.zip.ZipFile;
    51 import java.util.zip.ZipFile;
    51 
    52 
    52 import javax.tools.JavaFileManager;
    53 import javax.tools.JavaFileManager;
   102 
   103 
   103     // Used by Locations(for now) to indicate that the PLATFORM_CLASS_PATH
   104     // Used by Locations(for now) to indicate that the PLATFORM_CLASS_PATH
   104     // should use the jrt: file system.
   105     // should use the jrt: file system.
   105     // When Locations has been converted to use java.nio.file.Path,
   106     // When Locations has been converted to use java.nio.file.Path,
   106     // Locations can use Paths.get(URI.create("jrt:"))
   107     // Locations can use Paths.get(URI.create("jrt:"))
   107     static final File JRT_MARKER_FILE = new File("JRT_MARKER_FILE");
   108     static final Path JRT_MARKER_FILE = Paths.get("JRT_MARKER_FILE");
   108 
   109 
   109     public Locations() {
   110     public Locations() {
   110         initHandlers();
   111         initHandlers();
   111     }
   112     }
   112 
   113 
   115         this.log = log;
   116         this.log = log;
   116         warn = lint.isEnabled(Lint.LintCategory.PATH);
   117         warn = lint.isEnabled(Lint.LintCategory.PATH);
   117         this.fsInfo = fsInfo;
   118         this.fsInfo = fsInfo;
   118     }
   119     }
   119 
   120 
   120     public Collection<File> bootClassPath() {
   121     public Collection<Path> bootClassPath() {
   121         return getLocation(PLATFORM_CLASS_PATH);
   122         return getLocation(PLATFORM_CLASS_PATH);
   122     }
   123     }
   123 
   124 
   124     public boolean isDefaultBootClassPath() {
   125     public boolean isDefaultBootClassPath() {
   125         BootClassPathLocationHandler h
   126         BootClassPathLocationHandler h
   126                 = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH);
   127                 = (BootClassPathLocationHandler) getHandler(PLATFORM_CLASS_PATH);
   127         return h.isDefault();
   128         return h.isDefault();
   128     }
   129     }
   129 
   130 
   130     public Collection<File> userClassPath() {
   131     public Collection<Path> userClassPath() {
   131         return getLocation(CLASS_PATH);
   132         return getLocation(CLASS_PATH);
   132     }
   133     }
   133 
   134 
   134     public Collection<File> sourcePath() {
   135     public Collection<Path> sourcePath() {
   135         Collection<File> p = getLocation(SOURCE_PATH);
   136         Collection<Path> p = getLocation(SOURCE_PATH);
   136         // TODO: this should be handled by the LocationHandler
   137         // TODO: this should be handled by the LocationHandler
   137         return p == null || p.isEmpty() ? null : p;
   138         return p == null || p.isEmpty() ? null : p;
   138     }
   139     }
   139 
   140 
   140     /**
   141     /**
   141      * Split a path into its elements. Empty path elements will be ignored.
   142      * Split a search path into its elements. Empty path elements will be ignored.
   142      *
   143      *
   143      * @param path The path to be split
   144      * @param searchPath The search path to be split
   144      * @return The elements of the path
   145      * @return The elements of the path
   145      */
   146      */
   146     private static Iterable<File> getPathEntries(String path) {
   147     private static Iterable<Path> getPathEntries(String searchPath) {
   147         return getPathEntries(path, null);
   148         return getPathEntries(searchPath, null);
   148     }
   149     }
   149 
   150 
   150     /**
   151     /**
   151      * Split a path into its elements. If emptyPathDefault is not null, all empty elements in the
   152      * Split a search path into its elements. If emptyPathDefault is not null, all empty elements in the
   152      * path, including empty elements at either end of the path, will be replaced with the value of
   153      * path, including empty elements at either end of the path, will be replaced with the value of
   153      * emptyPathDefault.
   154      * emptyPathDefault.
   154      *
   155      *
   155      * @param path The path to be split
   156      * @param searchPath The search path to be split
   156      * @param emptyPathDefault The value to substitute for empty path elements, or null, to ignore
   157      * @param emptyPathDefault The value to substitute for empty path elements, or null, to ignore
   157      * empty path elements
   158      * empty path elements
   158      * @return The elements of the path
   159      * @return The elements of the path
   159      */
   160      */
   160     private static Iterable<File> getPathEntries(String path, File emptyPathDefault) {
   161     private static Iterable<Path> getPathEntries(String searchPath, Path emptyPathDefault) {
   161         ListBuffer<File> entries = new ListBuffer<>();
   162         ListBuffer<Path> entries = new ListBuffer<>();
   162         int start = 0;
   163         for (String s: searchPath.split(Pattern.quote(File.pathSeparator), -1)) {
   163         while (start <= path.length()) {
   164             if (s.isEmpty()) {
   164             int sep = path.indexOf(File.pathSeparatorChar, start);
   165                 if (emptyPathDefault != null) {
   165             if (sep == -1) {
   166                     entries.add(emptyPathDefault);
   166                 sep = path.length();
   167                 }
   167             }
   168             } else {
   168             if (start < sep) {
   169                 entries.add(Paths.get(s));
   169                 entries.add(new File(path.substring(start, sep)));
   170             }
   170             } else if (emptyPathDefault != null) {
       
   171                 entries.add(emptyPathDefault);
       
   172             }
       
   173             start = sep + 1;
       
   174         }
   171         }
   175         return entries;
   172         return entries;
   176     }
   173     }
   177 
   174 
   178     /**
   175     /**
   179      * Utility class to help evaluate a path option. Duplicate entries are ignored, jar class paths
   176      * Utility class to help evaluate a path option. Duplicate entries are ignored, jar class paths
   180      * can be expanded.
   177      * can be expanded.
   181      */
   178      */
   182     private class SearchPath extends LinkedHashSet<File> {
   179     private class SearchPath extends LinkedHashSet<Path> {
   183 
   180 
   184         private static final long serialVersionUID = 0;
   181         private static final long serialVersionUID = 0;
   185 
   182 
   186         private boolean expandJarClassPaths = false;
   183         private boolean expandJarClassPaths = false;
   187         private final Set<File> canonicalValues = new HashSet<>();
   184         private final Set<Path> canonicalValues = new HashSet<>();
   188 
   185 
   189         public SearchPath expandJarClassPaths(boolean x) {
   186         public SearchPath expandJarClassPaths(boolean x) {
   190             expandJarClassPaths = x;
   187             expandJarClassPaths = x;
   191             return this;
   188             return this;
   192         }
   189         }
   193 
   190 
   194         /**
   191         /**
   195          * What to use when path element is the empty string
   192          * What to use when path element is the empty string
   196          */
   193          */
   197         private File emptyPathDefault = null;
   194         private Path emptyPathDefault = null;
   198 
   195 
   199         public SearchPath emptyPathDefault(File x) {
   196         public SearchPath emptyPathDefault(Path x) {
   200             emptyPathDefault = x;
   197             emptyPathDefault = x;
   201             return this;
   198             return this;
   202         }
   199         }
   203 
   200 
   204         public SearchPath addDirectories(String dirs, boolean warn) {
   201         public SearchPath addDirectories(String dirs, boolean warn) {
   205             boolean prev = expandJarClassPaths;
   202             boolean prev = expandJarClassPaths;
   206             expandJarClassPaths = true;
   203             expandJarClassPaths = true;
   207             try {
   204             try {
   208                 if (dirs != null) {
   205                 if (dirs != null) {
   209                     for (File dir : getPathEntries(dirs)) {
   206                     for (Path dir : getPathEntries(dirs)) {
   210                         addDirectory(dir, warn);
   207                         addDirectory(dir, warn);
   211                     }
   208                     }
   212                 }
   209                 }
   213                 return this;
   210                 return this;
   214             } finally {
   211             } finally {
   218 
   215 
   219         public SearchPath addDirectories(String dirs) {
   216         public SearchPath addDirectories(String dirs) {
   220             return addDirectories(dirs, warn);
   217             return addDirectories(dirs, warn);
   221         }
   218         }
   222 
   219 
   223         private void addDirectory(File dir, boolean warn) {
   220         private void addDirectory(Path dir, boolean warn) {
   224             if (!dir.isDirectory()) {
   221             if (!Files.isDirectory(dir)) {
   225                 if (warn) {
   222                 if (warn) {
   226                     log.warning(Lint.LintCategory.PATH,
   223                     log.warning(Lint.LintCategory.PATH,
   227                             "dir.path.element.not.found", dir);
   224                             "dir.path.element.not.found", dir);
   228                 }
   225                 }
   229                 return;
   226                 return;
   230             }
   227             }
   231 
   228 
   232             File[] files = dir.listFiles();
   229             try (Stream<Path> s = Files.list(dir)) {
   233             if (files == null) {
   230                 s.filter(dirEntry -> isArchive(dirEntry))
   234                 return;
   231                         .forEach(dirEntry -> addFile(dirEntry, warn));
   235             }
   232             } catch (IOException ignore) {
   236 
       
   237             for (File direntry : files) {
       
   238                 if (isArchive(direntry)) {
       
   239                     addFile(direntry, warn);
       
   240                 }
       
   241             }
   233             }
   242         }
   234         }
   243 
   235 
   244         public SearchPath addFiles(String files, boolean warn) {
   236         public SearchPath addFiles(String files, boolean warn) {
   245             if (files != null) {
   237             if (files != null) {
   250 
   242 
   251         public SearchPath addFiles(String files) {
   243         public SearchPath addFiles(String files) {
   252             return addFiles(files, warn);
   244             return addFiles(files, warn);
   253         }
   245         }
   254 
   246 
   255         public SearchPath addFiles(Iterable<? extends File> files, boolean warn) {
   247         public SearchPath addFiles(Iterable<? extends Path> files, boolean warn) {
   256             if (files != null) {
   248             if (files != null) {
   257                 for (File file : files) {
   249                 for (Path file : files) {
   258                     addFile(file, warn);
   250                     addFile(file, warn);
   259                 }
   251                 }
   260             }
   252             }
   261             return this;
   253             return this;
   262         }
   254         }
   263 
   255 
   264         public SearchPath addFiles(Iterable<? extends File> files) {
   256         public SearchPath addFiles(Iterable<? extends Path> files) {
   265             return addFiles(files, warn);
   257             return addFiles(files, warn);
   266         }
   258         }
   267 
   259 
   268         public void addFile(File file, boolean warn) {
   260         public void addFile(Path file, boolean warn) {
   269             if (contains(file)) {
   261             if (contains(file)) {
   270                 // discard duplicates
   262                 // discard duplicates
   271                 return;
   263                 return;
   272             }
   264             }
   273 
   265 
   279                 }
   271                 }
   280                 super.add(file);
   272                 super.add(file);
   281                 return;
   273                 return;
   282             }
   274             }
   283 
   275 
   284             File canonFile = fsInfo.getCanonicalFile(file);
   276             Path canonFile = fsInfo.getCanonicalFile(file);
   285             if (canonicalValues.contains(canonFile)) {
   277             if (canonicalValues.contains(canonFile)) {
   286                 /* Discard duplicates and avoid infinite recursion */
   278                 /* Discard duplicates and avoid infinite recursion */
   287                 return;
   279                 return;
   288             }
   280             }
   289 
   281 
   290             if (fsInfo.isFile(file)) {
   282             if (fsInfo.isFile(file)) {
   291                 /* File is an ordinary file. */
   283                 /* File is an ordinary file. */
   292                 if (!isArchive(file) && !file.getName().endsWith(".jimage")) {
   284                 if (!isArchive(file) && !file.getFileName().toString().endsWith(".jimage")) {
   293                     /* Not a recognized extension; open it to see if
   285                     /* Not a recognized extension; open it to see if
   294                      it looks like a valid zip file. */
   286                      it looks like a valid zip file. */
   295                     try {
   287                     try {
   296                         ZipFile z = new ZipFile(file);
   288                         ZipFile z = new ZipFile(file.toFile());
   297                         z.close();
   289                         z.close();
   298                         if (warn) {
   290                         if (warn) {
   299                             log.warning(Lint.LintCategory.PATH,
   291                             log.warning(Lint.LintCategory.PATH,
   300                                     "unexpected.archive.file", file);
   292                                     "unexpected.archive.file", file);
   301                         }
   293                         }
   313             /* Now what we have left is either a directory or a file name
   305             /* Now what we have left is either a directory or a file name
   314              conforming to archive naming convention */
   306              conforming to archive naming convention */
   315             super.add(file);
   307             super.add(file);
   316             canonicalValues.add(canonFile);
   308             canonicalValues.add(canonFile);
   317 
   309 
   318             if (expandJarClassPaths && fsInfo.isFile(file) && !file.getName().endsWith(".jimage")) {
   310             if (expandJarClassPaths && fsInfo.isFile(file) && !file.getFileName().toString().endsWith(".jimage")) {
   319                 addJarClassPath(file, warn);
   311                 addJarClassPath(file, warn);
   320             }
   312             }
   321         }
   313         }
   322 
   314 
   323         // Adds referenced classpath elements from a jar's Class-Path
   315         // Adds referenced classpath elements from a jar's Class-Path
   324         // Manifest entry.  In some future release, we may want to
   316         // Manifest entry.  In some future release, we may want to
   325         // update this code to recognize URLs rather than simple
   317         // update this code to recognize URLs rather than simple
   326         // filenames, but if we do, we should redo all path-related code.
   318         // filenames, but if we do, we should redo all path-related code.
   327         private void addJarClassPath(File jarFile, boolean warn) {
   319         private void addJarClassPath(Path jarFile, boolean warn) {
   328             try {
   320             try {
   329                 for (File f : fsInfo.getJarClassPath(jarFile)) {
   321                 for (Path f : fsInfo.getJarClassPath(jarFile)) {
   330                     addFile(f, warn);
   322                     addFile(f, warn);
   331                 }
   323                 }
   332             } catch (IOException e) {
   324             } catch (IOException e) {
   333                 log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e));
   325                 log.error("error.reading.file", jarFile, JavacFileManager.getMessage(e));
   334             }
   326             }
   369         abstract boolean handleOption(Option option, String value);
   361         abstract boolean handleOption(Option option, String value);
   370 
   362 
   371         /**
   363         /**
   372          * @see StandardJavaFileManager#getLocation
   364          * @see StandardJavaFileManager#getLocation
   373          */
   365          */
   374         abstract Collection<File> getLocation();
   366         abstract Collection<Path> getLocation();
   375 
   367 
   376         /**
   368         /**
   377          * @see StandardJavaFileManager#setLocation
   369          * @see StandardJavaFileManager#setLocation
   378          */
   370          */
   379         abstract void setLocation(Iterable<? extends File> files) throws IOException;
   371         abstract void setLocation(Iterable<? extends Path> files) throws IOException;
   380     }
   372     }
   381 
   373 
   382     /**
   374     /**
   383      * General purpose implementation for output locations, such as -d/CLASS_OUTPUT and
   375      * General purpose implementation for output locations, such as -d/CLASS_OUTPUT and
   384      * -s/SOURCE_OUTPUT. All options are treated as equivalent (i.e. aliases.) The value is a single
   376      * -s/SOURCE_OUTPUT. All options are treated as equivalent (i.e. aliases.) The value is a single
   385      * file, possibly null.
   377      * file, possibly null.
   386      */
   378      */
   387     private class OutputLocationHandler extends LocationHandler {
   379     private class OutputLocationHandler extends LocationHandler {
   388 
   380 
   389         private File outputDir;
   381         private Path outputDir;
   390 
   382 
   391         OutputLocationHandler(Location location, Option... options) {
   383         OutputLocationHandler(Location location, Option... options) {
   392             super(location, options);
   384             super(location, options);
   393         }
   385         }
   394 
   386 
   400 
   392 
   401             // TODO: could/should validate outputDir exists and is a directory
   393             // TODO: could/should validate outputDir exists and is a directory
   402             // need to decide how best to report issue for benefit of
   394             // need to decide how best to report issue for benefit of
   403             // direct API call on JavaFileManager.handleOption(specifies IAE)
   395             // direct API call on JavaFileManager.handleOption(specifies IAE)
   404             // vs. command line decoding.
   396             // vs. command line decoding.
   405             outputDir = (value == null) ? null : new File(value);
   397             outputDir = (value == null) ? null : Paths.get(value);
   406             return true;
   398             return true;
   407         }
   399         }
   408 
   400 
   409         @Override
   401         @Override
   410         Collection<File> getLocation() {
   402         Collection<Path> getLocation() {
   411             return (outputDir == null) ? null : Collections.singleton(outputDir);
   403             return (outputDir == null) ? null : Collections.singleton(outputDir);
   412         }
   404         }
   413 
   405 
   414         @Override
   406         @Override
   415         void setLocation(Iterable<? extends File> files) throws IOException {
   407         void setLocation(Iterable<? extends Path> files) throws IOException {
   416             if (files == null) {
   408             if (files == null) {
   417                 outputDir = null;
   409                 outputDir = null;
   418             } else {
   410             } else {
   419                 Iterator<? extends File> pathIter = files.iterator();
   411                 Iterator<? extends Path> pathIter = files.iterator();
   420                 if (!pathIter.hasNext()) {
   412                 if (!pathIter.hasNext()) {
   421                     throw new IllegalArgumentException("empty path for directory");
   413                     throw new IllegalArgumentException("empty path for directory");
   422                 }
   414                 }
   423                 File dir = pathIter.next();
   415                 Path dir = pathIter.next();
   424                 if (pathIter.hasNext()) {
   416                 if (pathIter.hasNext()) {
   425                     throw new IllegalArgumentException("path too long for directory");
   417                     throw new IllegalArgumentException("path too long for directory");
   426                 }
   418                 }
   427                 if (!dir.exists()) {
   419                 if (!Files.exists(dir)) {
   428                     throw new FileNotFoundException(dir + ": does not exist");
   420                     throw new FileNotFoundException(dir + ": does not exist");
   429                 } else if (!dir.isDirectory()) {
   421                 } else if (!Files.isDirectory(dir)) {
   430                     throw new IOException(dir + ": not a directory");
   422                     throw new IOException(dir + ": not a directory");
   431                 }
   423                 }
   432                 outputDir = dir;
   424                 outputDir = dir;
   433             }
   425             }
   434         }
   426         }
   439      * -processorPath/ANNOTATION_PROCESS_PATH. All options are treated as equivalent (i.e. aliases.)
   431      * -processorPath/ANNOTATION_PROCESS_PATH. All options are treated as equivalent (i.e. aliases.)
   440      * The value is an ordered set of files and/or directories.
   432      * The value is an ordered set of files and/or directories.
   441      */
   433      */
   442     private class SimpleLocationHandler extends LocationHandler {
   434     private class SimpleLocationHandler extends LocationHandler {
   443 
   435 
   444         protected Collection<File> searchPath;
   436         protected Collection<Path> searchPath;
   445 
   437 
   446         SimpleLocationHandler(Location location, Option... options) {
   438         SimpleLocationHandler(Location location, Option... options) {
   447             super(location, options);
   439             super(location, options);
   448         }
   440         }
   449 
   441 
   456                     : Collections.unmodifiableCollection(createPath().addFiles(value));
   448                     : Collections.unmodifiableCollection(createPath().addFiles(value));
   457             return true;
   449             return true;
   458         }
   450         }
   459 
   451 
   460         @Override
   452         @Override
   461         Collection<File> getLocation() {
   453         Collection<Path> getLocation() {
   462             return searchPath;
   454             return searchPath;
   463         }
   455         }
   464 
   456 
   465         @Override
   457         @Override
   466         void setLocation(Iterable<? extends File> files) {
   458         void setLocation(Iterable<? extends Path> files) {
   467             SearchPath p;
   459             SearchPath p;
   468             if (files == null) {
   460             if (files == null) {
   469                 p = computePath(null);
   461                 p = computePath(null);
   470             } else {
   462             } else {
   471                 p = createPath().addFiles(files);
   463                 p = createPath().addFiles(files);
   492             super(StandardLocation.CLASS_PATH,
   484             super(StandardLocation.CLASS_PATH,
   493                     Option.CLASSPATH, Option.CP);
   485                     Option.CLASSPATH, Option.CP);
   494         }
   486         }
   495 
   487 
   496         @Override
   488         @Override
   497         Collection<File> getLocation() {
   489         Collection<Path> getLocation() {
   498             lazy();
   490             lazy();
   499             return searchPath;
   491             return searchPath;
   500         }
   492         }
   501 
   493 
   502         @Override
   494         @Override
   524 
   516 
   525         @Override
   517         @Override
   526         protected SearchPath createPath() {
   518         protected SearchPath createPath() {
   527             return new SearchPath()
   519             return new SearchPath()
   528                     .expandJarClassPaths(true) // Only search user jars for Class-Paths
   520                     .expandJarClassPaths(true) // Only search user jars for Class-Paths
   529                     .emptyPathDefault(new File("."));  // Empty path elt ==> current directory
   521                     .emptyPathDefault(Paths.get("."));  // Empty path elt ==> current directory
   530         }
   522         }
   531 
   523 
   532         private void lazy() {
   524         private void lazy() {
   533             if (searchPath == null) {
   525             if (searchPath == null) {
   534                 setLocation(null);
   526                 setLocation(null);
   543      * reverts to using default values for options that have not been set. Setting -bootclasspath or
   535      * reverts to using default values for options that have not been set. Setting -bootclasspath or
   544      * -Xbootclasspath overrides any existing value for -Xbootclasspath/p: and -Xbootclasspath/a:.
   536      * -Xbootclasspath overrides any existing value for -Xbootclasspath/p: and -Xbootclasspath/a:.
   545      */
   537      */
   546     private class BootClassPathLocationHandler extends LocationHandler {
   538     private class BootClassPathLocationHandler extends LocationHandler {
   547 
   539 
   548         private Collection<File> searchPath;
   540         private Collection<Path> searchPath;
   549         final Map<Option, String> optionValues = new EnumMap<>(Option.class);
   541         final Map<Option, String> optionValues = new EnumMap<>(Option.class);
   550 
   542 
   551         /**
   543         /**
   552          * Is the bootclasspath the default?
   544          * Is the bootclasspath the default?
   553          */
   545          */
   597                     return option;
   589                     return option;
   598             }
   590             }
   599         }
   591         }
   600 
   592 
   601         @Override
   593         @Override
   602         Collection<File> getLocation() {
   594         Collection<Path> getLocation() {
   603             lazy();
   595             lazy();
   604             return searchPath;
   596             return searchPath;
   605         }
   597         }
   606 
   598 
   607         @Override
   599         @Override
   608         void setLocation(Iterable<? extends File> files) {
   600         void setLocation(Iterable<? extends Path> files) {
   609             if (files == null) {
   601             if (files == null) {
   610                 searchPath = null;  // reset to "uninitialized"
   602                 searchPath = null;  // reset to "uninitialized"
   611             } else {
   603             } else {
   612                 isDefault = false;
   604                 isDefault = false;
   613                 SearchPath p = new SearchPath().addFiles(files, false);
   605                 SearchPath p = new SearchPath().addFiles(files, false);
   636 
   628 
   637             if (bootclasspathOpt != null) {
   629             if (bootclasspathOpt != null) {
   638                 path.addFiles(bootclasspathOpt);
   630                 path.addFiles(bootclasspathOpt);
   639             } else {
   631             } else {
   640                 // Standard system classes for this compiler's release.
   632                 // Standard system classes for this compiler's release.
   641                 Collection<File> systemClasses = systemClasses(java_home);
   633                 Collection<Path> systemClasses = systemClasses(java_home);
   642                 if (systemClasses != null) {
   634                 if (systemClasses != null) {
   643                     path.addFiles(systemClasses, false);
   635                     path.addFiles(systemClasses, false);
   644                 } else {
   636                 } else {
   645                     // fallback to the value of sun.boot.class.path
   637                     // fallback to the value of sun.boot.class.path
   646                     String files = System.getProperty("sun.boot.class.path");
   638                     String files = System.getProperty("sun.boot.class.path");
   655             // that they are.
   647             // that they are.
   656             if (extdirsOpt != null) {
   648             if (extdirsOpt != null) {
   657                 path.addDirectories(extdirsOpt);
   649                 path.addDirectories(extdirsOpt);
   658             } else {
   650             } else {
   659                 // Add lib/jfxrt.jar to the search path
   651                 // Add lib/jfxrt.jar to the search path
   660                 File jfxrt = new File(new File(java_home, "lib"), "jfxrt.jar");
   652                Path jfxrt = Paths.get(java_home, "lib", "jfxrt.jar");
   661                 if (jfxrt.exists()) {
   653                 if (Files.exists(jfxrt)) {
   662                     path.addFile(jfxrt, false);
   654                     path.addFile(jfxrt, false);
   663                 }
   655                 }
   664                 path.addDirectories(System.getProperty("java.ext.dirs"), false);
   656                 path.addDirectories(System.getProperty("java.ext.dirs"), false);
   665             }
   657             }
   666 
   658 
   676          * Return a collection of files containing system classes.
   668          * Return a collection of files containing system classes.
   677          * Returns {@code null} if not running on a modular image.
   669          * Returns {@code null} if not running on a modular image.
   678          *
   670          *
   679          * @throws UncheckedIOException if an I/O errors occurs
   671          * @throws UncheckedIOException if an I/O errors occurs
   680          */
   672          */
   681         private Collection<File> systemClasses(String java_home) throws IOException {
   673         private Collection<Path> systemClasses(String java_home) throws IOException {
   682             // Return .jimage files if available
   674             // Return .jimage files if available
   683             Path libModules = Paths.get(java_home, "lib", "modules");
   675             Path libModules = Paths.get(java_home, "lib", "modules");
   684             if (Files.exists(libModules)) {
   676             if (Files.exists(libModules)) {
   685                 try (Stream<Path> files = Files.list(libModules)) {
   677                 try (Stream<Path> files = Files.list(libModules)) {
   686                     boolean haveJImageFiles =
   678                     boolean haveJImageFiles =
   693 
   685 
   694             // Temporary: if no .jimage files, return individual modules
   686             // Temporary: if no .jimage files, return individual modules
   695             if (Files.exists(libModules.resolve("java.base"))) {
   687             if (Files.exists(libModules.resolve("java.base"))) {
   696                 return Files.list(libModules)
   688                 return Files.list(libModules)
   697                             .map(d -> d.resolve("classes"))
   689                             .map(d -> d.resolve("classes"))
   698                             .map(Path::toFile)
       
   699                             .collect(Collectors.toList());
   690                             .collect(Collectors.toList());
   700             }
   691             }
   701 
   692 
   702             // Exploded module image
   693             // Exploded module image
   703             Path modules = Paths.get(java_home, "modules");
   694             Path modules = Paths.get(java_home, "modules");
   704             if (Files.isDirectory(modules.resolve("java.base"))) {
   695             if (Files.isDirectory(modules.resolve("java.base"))) {
   705                 return Files.list(modules)
   696                 return Files.list(modules)
   706                             .map(Path::toFile)
       
   707                             .collect(Collectors.toList());
   697                             .collect(Collectors.toList());
   708             }
   698             }
   709 
   699 
   710             // not a modular image that we know about
   700             // not a modular image that we know about
   711             return null;
   701             return null;
   751     public boolean handleOption(Option option, String value) {
   741     public boolean handleOption(Option option, String value) {
   752         LocationHandler h = handlersForOption.get(option);
   742         LocationHandler h = handlersForOption.get(option);
   753         return (h == null ? false : h.handleOption(option, value));
   743         return (h == null ? false : h.handleOption(option, value));
   754     }
   744     }
   755 
   745 
   756     Collection<File> getLocation(Location location) {
   746     Collection<Path> getLocation(Location location) {
   757         LocationHandler h = getHandler(location);
   747         LocationHandler h = getHandler(location);
   758         return (h == null ? null : h.getLocation());
   748         return (h == null ? null : h.getLocation());
   759     }
   749     }
   760 
   750 
   761     File getOutputLocation(Location location) {
   751     Path getOutputLocation(Location location) {
   762         if (!location.isOutputLocation()) {
   752         if (!location.isOutputLocation()) {
   763             throw new IllegalArgumentException();
   753             throw new IllegalArgumentException();
   764         }
   754         }
   765         LocationHandler h = getHandler(location);
   755         LocationHandler h = getHandler(location);
   766         return ((OutputLocationHandler) h).outputDir;
   756         return ((OutputLocationHandler) h).outputDir;
   767     }
   757     }
   768 
   758 
   769     void setLocation(Location location, Iterable<? extends File> files) throws IOException {
   759     void setLocation(Location location, Iterable<? extends Path> files) throws IOException {
   770         LocationHandler h = getHandler(location);
   760         LocationHandler h = getHandler(location);
   771         if (h == null) {
   761         if (h == null) {
   772             if (location.isOutputLocation()) {
   762             if (location.isOutputLocation()) {
   773                 h = new OutputLocationHandler(location);
   763                 h = new OutputLocationHandler(location);
   774             } else {
   764             } else {
   785     }
   775     }
   786 
   776 
   787     /**
   777     /**
   788      * Is this the name of an archive file?
   778      * Is this the name of an archive file?
   789      */
   779      */
   790     private boolean isArchive(File file) {
   780     private boolean isArchive(Path file) {
   791         String n = StringUtils.toLowerCase(file.getName());
   781         String n = StringUtils.toLowerCase(file.getFileName().toString());
   792         return fsInfo.isFile(file)
   782         return fsInfo.isFile(file)
   793                 && (n.endsWith(".jar") || n.endsWith(".zip"));
   783                 && (n.endsWith(".jar") || n.endsWith(".zip"));
   794     }
   784     }
   795 
   785 
   796     /**
   786     /**
   797      * Utility method for converting a search path string to an array of directory and JAR file
   787      * Utility method for converting a search path string to an array of directory and JAR file
   798      * URLs.
   788      * URLs.
   799      *
   789      *
   800      * Note that this method is called by apt and the DocletInvoker.
   790      * Note that this method is called by the DocletInvoker.
   801      *
   791      *
   802      * @param path the search path string
   792      * @param path the search path string
   803      * @return the resulting array of directory and JAR file URLs
   793      * @return the resulting array of directory and JAR file URLs
   804      */
   794      */
   805     public static URL[] pathToURLs(String path) {
   795     public static URL[] pathToURLs(String path) {
   806         StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
   796         java.util.List<URL> urls = new ArrayList<>();
   807         URL[] urls = new URL[st.countTokens()];
   797         for (String s: path.split(Pattern.quote(File.pathSeparator))) {
   808         int count = 0;
   798             if (!s.isEmpty()) {
   809         while (st.hasMoreTokens()) {
   799                 URL url = fileToURL(Paths.get(s));
   810             URL url = fileToURL(new File(st.nextToken()));
   800                 if (url != null) {
   811             if (url != null) {
   801                     urls.add(url);
   812                 urls[count++] = url;
   802                 }
   813             }
   803             }
   814         }
   804         }
   815         urls = Arrays.copyOf(urls, count);
   805         return urls.toArray(new URL[urls.size()]);
   816         return urls;
       
   817     }
   806     }
   818 
   807 
   819     /**
   808     /**
   820      * Returns the directory or JAR file URL corresponding to the specified local file name.
   809      * Returns the directory or JAR file URL corresponding to the specified local file name.
   821      *
   810      *
   822      * @param file the File object
   811      * @param file the Path object
   823      * @return the resulting directory or JAR file URL, or null if unknown
   812      * @return the resulting directory or JAR file URL, or null if unknown
   824      */
   813      */
   825     private static URL fileToURL(File file) {
   814     private static URL fileToURL(Path file) {
   826         String name;
   815         Path p;
   827         try {
   816         try {
   828             name = file.getCanonicalPath();
   817             p = file.toRealPath();
   829         } catch (IOException e) {
   818         } catch (IOException e) {
   830             name = file.getAbsolutePath();
   819             p = file.toAbsolutePath();
   831         }
       
   832         name = name.replace(File.separatorChar, '/');
       
   833         if (!name.startsWith("/")) {
       
   834             name = "/" + name;
       
   835         }
       
   836         // If the file does not exist, then assume that it's a directory
       
   837         if (!file.isFile()) {
       
   838             name = name + "/";
       
   839         }
   820         }
   840         try {
   821         try {
   841             return new URL("file", "", name);
   822             return p.normalize().toUri().toURL();
   842         } catch (MalformedURLException e) {
   823         } catch (MalformedURLException e) {
   843             throw new IllegalArgumentException(file.toString());
   824             return null;
   844         }
   825         }
   845     }
   826     }
   846 }
   827 }