1 /*
2  * Copyright (c) 2005, 2019, 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.File;
29 import java.io.IOException;
30 import java.io.UncheckedIOException;
31 import java.lang.module.Configuration;
32 import java.lang.module.ModuleFinder;
33 import java.net.MalformedURLException;
34 import java.net.URI;
35 import java.net.URISyntaxException;
36 import java.net.URL;
37 import java.nio.CharBuffer;
38 import java.nio.charset.Charset;
39 import java.nio.file.FileSystem;
40 import java.nio.file.FileSystems;
41 import java.nio.file.FileVisitOption;
42 import java.nio.file.FileVisitResult;
43 import java.nio.file.Files;
44 import java.nio.file.InvalidPathException;
45 import java.nio.file.LinkOption;
46 import java.nio.file.Path;
47 import java.nio.file.Paths;
48 import java.nio.file.ProviderNotFoundException;
49 import java.nio.file.SimpleFileVisitor;
50 import java.nio.file.attribute.BasicFileAttributes;
51 import java.nio.file.spi.FileSystemProvider;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collection;
55 import java.util.Collections;
56 import java.util.Comparator;
57 import java.util.EnumSet;
58 import java.util.HashMap;
59 import java.util.Iterator;
60 import java.util.Map;
61 import java.util.Objects;
62 import java.util.ServiceLoader;
63 import java.util.Set;
64 import java.util.stream.Collectors;
65 import java.util.stream.Stream;
66 
67 import javax.lang.model.SourceVersion;
68 import javax.tools.FileObject;
69 import javax.tools.JavaFileManager;
70 import javax.tools.JavaFileObject;
71 import javax.tools.StandardJavaFileManager;
72 
73 import com.sun.tools.javac.file.RelativePath.RelativeDirectory;
74 import com.sun.tools.javac.file.RelativePath.RelativeFile;
75 import com.sun.tools.javac.main.Option;
76 import com.sun.tools.javac.resources.CompilerProperties.Errors;
77 import com.sun.tools.javac.util.Assert;
78 import com.sun.tools.javac.util.Context;
79 import com.sun.tools.javac.util.Context.Factory;
80 import com.sun.tools.javac.util.DefinedBy;
81 import com.sun.tools.javac.util.DefinedBy.Api;
82 import com.sun.tools.javac.util.List;
83 import com.sun.tools.javac.util.ListBuffer;
84 
85 import static java.nio.file.FileVisitOption.FOLLOW_LINKS;
86 
87 import static javax.tools.StandardLocation.*;
88 
89 /**
90  * This class provides access to the source, class and other files
91  * used by the compiler and related tools.
92  *
93  * <p><b>This is NOT part of any supported API.
94  * If you write code that depends on this, you do so at your own risk.
95  * This code and its internal interfaces are subject to change or
96  * deletion without notice.</b>
97  */
98 public class JavacFileManager extends BaseFileManager implements StandardJavaFileManager {
99 
toArray(CharBuffer buffer)100     public static char[] toArray(CharBuffer buffer) {
101         if (buffer.hasArray())
102             return buffer.compact().flip().array();
103         else
104             return buffer.toString().toCharArray();
105     }
106 
107     private FSInfo fsInfo;
108 
109     private static final Set<JavaFileObject.Kind> SOURCE_OR_CLASS =
110         Set.of(JavaFileObject.Kind.SOURCE, JavaFileObject.Kind.CLASS);
111 
112     protected boolean symbolFileEnabled;
113 
114     private PathFactory pathFactory = Paths::get;
115 
116     protected enum SortFiles implements Comparator<Path> {
117         FORWARD {
118             @Override
compare(Path f1, Path f2)119             public int compare(Path f1, Path f2) {
120                 return f1.getFileName().compareTo(f2.getFileName());
121             }
122         },
123         REVERSE {
124             @Override
compare(Path f1, Path f2)125             public int compare(Path f1, Path f2) {
126                 return -f1.getFileName().compareTo(f2.getFileName());
127             }
128         }
129     }
130 
131     protected SortFiles sortFiles;
132 
133     /**
134      * We use a two-layered map instead of a map with a complex key because we don't want to reindex
135      * the values for every Location+RelativeDirectory pair. Once the PathsAndContainers are needed
136      * for a single Location, we should know all valid RelativeDirectory mappings. Because the
137      * indexing is costly for very large classpaths, this can result in a significant savings.
138      */
139     private Map<Location, Map<RelativeDirectory, java.util.List<PathAndContainer>>>
140         pathsAndContainersByLocationAndRelativeDirectory = new HashMap<>();
141 
142     /** Containers that have no indexing by {@link RelativeDirectory}, keyed by {@link Location}. */
143     private Map<Location, java.util.List<PathAndContainer>> nonIndexingContainersByLocation =
144         new HashMap<>();
145 
146     /**
147      * Register a Context.Factory to create a JavacFileManager.
148      */
preRegister(Context context)149     public static void preRegister(Context context) {
150         context.put(JavaFileManager.class,
151                 (Factory<JavaFileManager>)c -> new JavacFileManager(c, true, null));
152     }
153 
154     /**
155      * Create a JavacFileManager using a given context, optionally registering
156      * it as the JavaFileManager for that context.
157      */
JavacFileManager(Context context, boolean register, Charset charset)158     public JavacFileManager(Context context, boolean register, Charset charset) {
159         super(charset);
160         if (register)
161             context.put(JavaFileManager.class, this);
162         setContext(context);
163     }
164 
165     /**
166      * Set the context for JavacFileManager.
167      */
168     @Override
setContext(Context context)169     public void setContext(Context context) {
170         super.setContext(context);
171 
172         fsInfo = FSInfo.instance(context);
173 
174         symbolFileEnabled = !options.isSet("ignore.symbol.file");
175 
176         String sf = options.get("sortFiles");
177         if (sf != null) {
178             sortFiles = (sf.equals("reverse") ? SortFiles.REVERSE : SortFiles.FORWARD);
179         }
180     }
181 
182     @Override @DefinedBy(DefinedBy.Api.COMPILER)
setPathFactory(PathFactory f)183     public void setPathFactory(PathFactory f) {
184         pathFactory = Objects.requireNonNull(f);
185         locations.setPathFactory(f);
186     }
187 
getPath(String first, String... more)188     private Path getPath(String first, String... more) {
189         return pathFactory.getPath(first, more);
190     }
191 
192     /**
193      * Set whether or not to use ct.sym as an alternate to rt.jar.
194      */
setSymbolFileEnabled(boolean b)195     public void setSymbolFileEnabled(boolean b) {
196         symbolFileEnabled = b;
197     }
198 
isSymbolFileEnabled()199     public boolean isSymbolFileEnabled() {
200         return symbolFileEnabled;
201     }
202 
203     // used by tests
getJavaFileObject(String name)204     public JavaFileObject getJavaFileObject(String name) {
205         return getJavaFileObjects(name).iterator().next();
206     }
207 
208     // used by tests
getJavaFileObject(Path file)209     public JavaFileObject getJavaFileObject(Path file) {
210         return getJavaFileObjects(file).iterator().next();
211     }
212 
getFileForOutput(String classname, JavaFileObject.Kind kind, JavaFileObject sibling)213     public JavaFileObject getFileForOutput(String classname,
214                                            JavaFileObject.Kind kind,
215                                            JavaFileObject sibling)
216         throws IOException
217     {
218         return getJavaFileForOutput(CLASS_OUTPUT, classname, kind, sibling);
219     }
220 
221     @Override @DefinedBy(Api.COMPILER)
getJavaFileObjectsFromStrings(Iterable<String> names)222     public Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(Iterable<String> names) {
223         ListBuffer<Path> paths = new ListBuffer<>();
224         for (String name : names)
225             paths.append(getPath(nullCheck(name)));
226         return getJavaFileObjectsFromPaths(paths.toList());
227     }
228 
229     @Override @DefinedBy(Api.COMPILER)
getJavaFileObjects(String... names)230     public Iterable<? extends JavaFileObject> getJavaFileObjects(String... names) {
231         return getJavaFileObjectsFromStrings(Arrays.asList(nullCheck(names)));
232     }
233 
isValidName(String name)234     private static boolean isValidName(String name) {
235         // Arguably, isValidName should reject keywords (such as in SourceVersion.isName() ),
236         // but the set of keywords depends on the source level, and we don't want
237         // impls of JavaFileManager to have to be dependent on the source level.
238         // Therefore we simply check that the argument is a sequence of identifiers
239         // separated by ".".
240         for (String s : name.split("\\.", -1)) {
241             if (!SourceVersion.isIdentifier(s))
242                 return false;
243         }
244         return true;
245     }
246 
validateClassName(String className)247     private static void validateClassName(String className) {
248         if (!isValidName(className))
249             throw new IllegalArgumentException("Invalid class name: " + className);
250     }
251 
validatePackageName(String packageName)252     private static void validatePackageName(String packageName) {
253         if (packageName.length() > 0 && !isValidName(packageName))
254             throw new IllegalArgumentException("Invalid packageName name: " + packageName);
255     }
256 
testName(String name, boolean isValidPackageName, boolean isValidClassName)257     public static void testName(String name,
258                                 boolean isValidPackageName,
259                                 boolean isValidClassName)
260     {
261         try {
262             validatePackageName(name);
263             if (!isValidPackageName)
264                 throw new AssertionError("Invalid package name accepted: " + name);
265             printAscii("Valid package name: \"%s\"", name);
266         } catch (IllegalArgumentException e) {
267             if (isValidPackageName)
268                 throw new AssertionError("Valid package name rejected: " + name);
269             printAscii("Invalid package name: \"%s\"", name);
270         }
271         try {
272             validateClassName(name);
273             if (!isValidClassName)
274                 throw new AssertionError("Invalid class name accepted: " + name);
275             printAscii("Valid class name: \"%s\"", name);
276         } catch (IllegalArgumentException e) {
277             if (isValidClassName)
278                 throw new AssertionError("Valid class name rejected: " + name);
279             printAscii("Invalid class name: \"%s\"", name);
280         }
281     }
282 
printAscii(String format, Object... args)283     private static void printAscii(String format, Object... args) {
284         String message;
285         try {
286             final String ascii = "US-ASCII";
287             message = new String(String.format(null, format, args).getBytes(ascii), ascii);
288         } catch (java.io.UnsupportedEncodingException ex) {
289             throw new AssertionError(ex);
290         }
291         System.out.println(message);
292     }
293 
294     private final Map<Path, Container> containers = new HashMap<>();
295 
getContainer(Path path)296     synchronized Container getContainer(Path path) throws IOException {
297         Container fs = containers.get(path);
298 
299         if (fs != null) {
300             return fs;
301         }
302 
303         if (fsInfo.isFile(path) && path.equals(Locations.thisSystemModules)) {
304             containers.put(path, fs = new JRTImageContainer());
305             return fs;
306         }
307 
308         Path realPath = fsInfo.getCanonicalFile(path);
309 
310         fs = containers.get(realPath);
311 
312         if (fs != null) {
313             containers.put(path, fs);
314             return fs;
315         }
316 
317         BasicFileAttributes attr = null;
318 
319         try {
320             attr = Files.readAttributes(realPath, BasicFileAttributes.class);
321         } catch (IOException ex) {
322             //non-existing
323             fs = MISSING_CONTAINER;
324         }
325 
326         if (attr != null) {
327             if (attr.isDirectory()) {
328                 fs = new DirectoryContainer(realPath);
329             } else {
330                 try {
331                     fs = new ArchiveContainer(path);
332                 } catch (ProviderNotFoundException | SecurityException ex) {
333                     throw new IOException(ex);
334                 }
335             }
336         }
337 
338         containers.put(realPath, fs);
339         containers.put(path, fs);
340 
341         return fs;
342     }
343 
344     private interface Container {
345         /**
346          * Insert all files in subdirectory subdirectory of container which
347          * match fileKinds into resultList
348          */
list(Path userPath, RelativeDirectory subdirectory, Set<JavaFileObject.Kind> fileKinds, boolean recurse, ListBuffer<JavaFileObject> resultList)349         public abstract void list(Path userPath,
350                                   RelativeDirectory subdirectory,
351                                   Set<JavaFileObject.Kind> fileKinds,
352                                   boolean recurse,
353                                   ListBuffer<JavaFileObject> resultList) throws IOException;
getFileObject(Path userPath, RelativeFile name)354         public abstract JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException;
close()355         public abstract void close() throws IOException;
maintainsDirectoryIndex()356         public abstract boolean maintainsDirectoryIndex();
357 
358         /**
359          * The directories this container indexes if {@link #maintainsDirectoryIndex()}, otherwise
360          * an empty iterable.
361          */
indexedDirectories()362         public abstract Iterable<RelativeDirectory> indexedDirectories();
363     }
364 
365     private static final Container MISSING_CONTAINER =  new Container() {
366         @Override
367         public void list(Path userPath,
368                          RelativeDirectory subdirectory,
369                          Set<JavaFileObject.Kind> fileKinds,
370                          boolean recurse,
371                          ListBuffer<JavaFileObject> resultList) throws IOException {
372         }
373         @Override
374         public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException {
375             return null;
376         }
377         @Override
378         public void close() throws IOException {}
379         @Override
380         public boolean maintainsDirectoryIndex() {
381             return false;
382         }
383         @Override
384         public Iterable<RelativeDirectory> indexedDirectories() {
385             return List.nil();
386         }
387     };
388 
389     private final class JRTImageContainer implements Container {
390 
391         /**
392          * Insert all files in a subdirectory of the platform image
393          * which match fileKinds into resultList.
394          */
395         @Override
list(Path userPath, RelativeDirectory subdirectory, Set<JavaFileObject.Kind> fileKinds, boolean recurse, ListBuffer<JavaFileObject> resultList)396         public void list(Path userPath,
397                          RelativeDirectory subdirectory,
398                          Set<JavaFileObject.Kind> fileKinds,
399                          boolean recurse,
400                          ListBuffer<JavaFileObject> resultList) throws IOException {
401             try {
402                 JRTIndex.Entry e = getJRTIndex().getEntry(subdirectory);
403                 if (symbolFileEnabled && e.ctSym.hidden)
404                     return;
405                 for (Path file: e.files.values()) {
406                     if (fileKinds.contains(getKind(file))) {
407                         JavaFileObject fe
408                                 = PathFileObject.forJRTPath(JavacFileManager.this, file);
409                         resultList.append(fe);
410                     }
411                 }
412 
413                 if (recurse) {
414                     for (RelativeDirectory rd: e.subdirs) {
415                         list(userPath, rd, fileKinds, recurse, resultList);
416                     }
417                 }
418             } catch (IOException ex) {
419                 ex.printStackTrace(System.err);
420                 log.error(Errors.ErrorReadingFile(userPath, getMessage(ex)));
421             }
422         }
423 
424         @Override
getFileObject(Path userPath, RelativeFile name)425         public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException {
426             JRTIndex.Entry e = getJRTIndex().getEntry(name.dirname());
427             if (symbolFileEnabled && e.ctSym.hidden)
428                 return null;
429             Path p = e.files.get(name.basename());
430             if (p != null) {
431                 return PathFileObject.forJRTPath(JavacFileManager.this, p);
432             } else {
433                 return null;
434             }
435         }
436 
437         @Override
close()438         public void close() throws IOException {
439         }
440 
441         @Override
maintainsDirectoryIndex()442         public boolean maintainsDirectoryIndex() {
443             return false;
444         }
445 
446         @Override
indexedDirectories()447         public Iterable<RelativeDirectory> indexedDirectories() {
448             return List.nil();
449         }
450     }
451 
getJRTIndex()452     private synchronized JRTIndex getJRTIndex() {
453         if (jrtIndex == null)
454             jrtIndex = JRTIndex.getSharedInstance();
455         return jrtIndex;
456     }
457 
458     private JRTIndex jrtIndex;
459 
460     private final class DirectoryContainer implements Container {
461         private final Path directory;
462 
DirectoryContainer(Path directory)463         public DirectoryContainer(Path directory) {
464             this.directory = directory;
465         }
466 
467         /**
468          * Insert all files in subdirectory subdirectory of directory userPath
469          * which match fileKinds into resultList
470          */
471         @Override
list(Path userPath, RelativeDirectory subdirectory, Set<JavaFileObject.Kind> fileKinds, boolean recurse, ListBuffer<JavaFileObject> resultList)472         public void list(Path userPath,
473                          RelativeDirectory subdirectory,
474                          Set<JavaFileObject.Kind> fileKinds,
475                          boolean recurse,
476                          ListBuffer<JavaFileObject> resultList) throws IOException {
477             Path d;
478             try {
479                 d = subdirectory.resolveAgainst(userPath);
480             } catch (InvalidPathException ignore) {
481                 return ;
482             }
483 
484             if (!Files.exists(d)) {
485                return;
486             }
487 
488             if (!caseMapCheck(d, subdirectory)) {
489                 return;
490             }
491 
492             java.util.List<Path> files;
493             try (Stream<Path> s = Files.list(d)) {
494                 files = (sortFiles == null ? s : s.sorted(sortFiles)).collect(Collectors.toList());
495             } catch (IOException ignore) {
496                 return;
497             }
498 
499             for (Path f: files) {
500                 String fname = f.getFileName().toString();
501                 if (fname.endsWith("/"))
502                     fname = fname.substring(0, fname.length() - 1);
503                 if (Files.isDirectory(f)) {
504                     if (recurse && SourceVersion.isIdentifier(fname)) {
505                         list(userPath,
506                              new RelativeDirectory(subdirectory, fname),
507                              fileKinds,
508                              recurse,
509                              resultList);
510                     }
511                 } else {
512                     if (isValidFile(fname, fileKinds)) {
513                         try {
514                             RelativeFile file = new RelativeFile(subdirectory, fname);
515                             JavaFileObject fe = PathFileObject.forDirectoryPath(JavacFileManager.this,
516                                     file.resolveAgainst(directory), userPath, file);
517                             resultList.append(fe);
518                         } catch (InvalidPathException e) {
519                             throw new IOException("error accessing directory " + directory + e);
520                         }
521                     }
522                 }
523             }
524         }
525 
526         @Override
getFileObject(Path userPath, RelativeFile name)527         public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException {
528             try {
529                 Path f = name.resolveAgainst(userPath);
530                 if (Files.exists(f))
531                     return PathFileObject.forSimplePath(JavacFileManager.this,
532                             fsInfo.getCanonicalFile(f), f);
533             } catch (InvalidPathException ignore) {
534             }
535             return null;
536         }
537 
538         @Override
close()539         public void close() throws IOException {
540         }
541 
542         @Override
maintainsDirectoryIndex()543         public boolean maintainsDirectoryIndex() {
544             return false;
545         }
546 
547         @Override
indexedDirectories()548         public Iterable<RelativeDirectory> indexedDirectories() {
549             return List.nil();
550         }
551     }
552 
553     private static final Set<FileVisitOption> NO_FILE_VISIT_OPTIONS = Set.of();
554     private static final Set<FileVisitOption> FOLLOW_LINKS_OPTIONS = Set.of(FOLLOW_LINKS);
555 
556     private final class ArchiveContainer implements Container {
557         private final Path archivePath;
558         private final FileSystem fileSystem;
559         private final Map<RelativeDirectory, Path> packages;
560 
ArchiveContainer(Path archivePath)561         public ArchiveContainer(Path archivePath) throws IOException, ProviderNotFoundException, SecurityException {
562             this.archivePath = archivePath;
563             if (multiReleaseValue != null && archivePath.toString().endsWith(".jar")) {
564                 Map<String,String> env = Collections.singletonMap("multi-release", multiReleaseValue);
565                 FileSystemProvider jarFSProvider = fsInfo.getJarFSProvider();
566                 Assert.checkNonNull(jarFSProvider, "should have been caught before!");
567                 this.fileSystem = jarFSProvider.newFileSystem(archivePath, env);
568             } else {
569                 this.fileSystem = FileSystems.newFileSystem(archivePath, (ClassLoader)null);
570             }
571             packages = new HashMap<>();
572             for (Path root : fileSystem.getRootDirectories()) {
573                 Files.walkFileTree(root, NO_FILE_VISIT_OPTIONS, Integer.MAX_VALUE,
574                         new SimpleFileVisitor<Path>() {
575                             @Override
576                             public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
577                                 if (isValid(dir.getFileName())) {
578                                     packages.put(new RelativeDirectory(root.relativize(dir).toString()), dir);
579                                     return FileVisitResult.CONTINUE;
580                                 } else {
581                                     return FileVisitResult.SKIP_SUBTREE;
582                                 }
583                             }
584                         });
585             }
586         }
587 
588         /**
589          * Insert all files in subdirectory subdirectory of this archive
590          * which match fileKinds into resultList
591          */
592         @Override
list(Path userPath, RelativeDirectory subdirectory, Set<JavaFileObject.Kind> fileKinds, boolean recurse, ListBuffer<JavaFileObject> resultList)593         public void list(Path userPath,
594                          RelativeDirectory subdirectory,
595                          Set<JavaFileObject.Kind> fileKinds,
596                          boolean recurse,
597                          ListBuffer<JavaFileObject> resultList) throws IOException {
598             Path resolvedSubdirectory = packages.get(subdirectory);
599 
600             if (resolvedSubdirectory == null)
601                 return ;
602 
603             int maxDepth = (recurse ? Integer.MAX_VALUE : 1);
604             Files.walkFileTree(resolvedSubdirectory, FOLLOW_LINKS_OPTIONS, maxDepth,
605                     new SimpleFileVisitor<Path>() {
606                         @Override
607                         public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
608                             if (isValid(dir.getFileName())) {
609                                 return FileVisitResult.CONTINUE;
610                             } else {
611                                 return FileVisitResult.SKIP_SUBTREE;
612                             }
613                         }
614 
615                         @Override
616                         public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
617                             if (attrs.isRegularFile() && fileKinds.contains(getKind(file.getFileName().toString()))) {
618                                 JavaFileObject fe = PathFileObject.forJarPath(
619                                         JavacFileManager.this, file, archivePath);
620                                 resultList.append(fe);
621                             }
622                             return FileVisitResult.CONTINUE;
623                         }
624                     });
625 
626         }
627 
isValid(Path fileName)628         private boolean isValid(Path fileName) {
629             if (fileName == null) {
630                 return true;
631             } else {
632                 String name = fileName.toString();
633                 if (name.endsWith("/")) {
634                     name = name.substring(0, name.length() - 1);
635                 }
636                 return SourceVersion.isIdentifier(name);
637             }
638         }
639 
640         @Override
getFileObject(Path userPath, RelativeFile name)641         public JavaFileObject getFileObject(Path userPath, RelativeFile name) throws IOException {
642             RelativeDirectory root = name.dirname();
643             Path packagepath = packages.get(root);
644             if (packagepath != null) {
645                 Path relpath = packagepath.resolve(name.basename());
646                 if (Files.exists(relpath)) {
647                     return PathFileObject.forJarPath(JavacFileManager.this, relpath, userPath);
648                 }
649             }
650             return null;
651         }
652 
653         @Override
close()654         public void close() throws IOException {
655             fileSystem.close();
656         }
657 
658         @Override
maintainsDirectoryIndex()659         public boolean maintainsDirectoryIndex() {
660             return true;
661         }
662 
663         @Override
indexedDirectories()664         public Iterable<RelativeDirectory> indexedDirectories() {
665             return packages.keySet();
666         }
667     }
668 
669     /**
670      * container is a directory, a zip file, or a non-existent path.
671      */
isValidFile(String s, Set<JavaFileObject.Kind> fileKinds)672     private boolean isValidFile(String s, Set<JavaFileObject.Kind> fileKinds) {
673         JavaFileObject.Kind kind = getKind(s);
674         return fileKinds.contains(kind);
675     }
676 
677     private static final boolean fileSystemIsCaseSensitive =
678         File.separatorChar == '/';
679 
680     /** Hack to make Windows case sensitive. Test whether given path
681      *  ends in a string of characters with the same case as given name.
682      *  Ignore file separators in both path and name.
683      */
caseMapCheck(Path f, RelativePath name)684     private boolean caseMapCheck(Path f, RelativePath name) {
685         if (fileSystemIsCaseSensitive) return true;
686         // Note that toRealPath() returns the case-sensitive
687         // spelled file name.
688         String path;
689         char sep;
690         try {
691             path = f.toRealPath(LinkOption.NOFOLLOW_LINKS).toString();
692             sep = f.getFileSystem().getSeparator().charAt(0);
693         } catch (IOException ex) {
694             return false;
695         }
696         char[] pcs = path.toCharArray();
697         char[] ncs = name.path.toCharArray();
698         int i = pcs.length - 1;
699         int j = ncs.length - 1;
700         while (i >= 0 && j >= 0) {
701             while (i >= 0 && pcs[i] == sep) i--;
702             while (j >= 0 && ncs[j] == '/') j--;
703             if (i >= 0 && j >= 0) {
704                 if (pcs[i] != ncs[j]) return false;
705                 i--;
706                 j--;
707             }
708         }
709         return j < 0;
710     }
711 
712     /** Flush any output resources.
713      */
714     @Override @DefinedBy(Api.COMPILER)
flush()715     public void flush() {
716         contentCache.clear();
717         pathsAndContainersByLocationAndRelativeDirectory.clear();
718         nonIndexingContainersByLocation.clear();
719     }
720 
721     /**
722      * Close the JavaFileManager, releasing resources.
723      */
724     @Override @DefinedBy(Api.COMPILER)
close()725     public void close() throws IOException {
726         if (deferredCloseTimeout > 0) {
727             deferredClose();
728             return;
729         }
730 
731         locations.close();
732         for (Container container: containers.values()) {
733             container.close();
734         }
735         containers.clear();
736         contentCache.clear();
737     }
738 
739     @Override @DefinedBy(Api.COMPILER)
getClassLoader(Location location)740     public ClassLoader getClassLoader(Location location) {
741         checkNotModuleOrientedLocation(location);
742         Iterable<? extends File> path = getLocation(location);
743         if (path == null)
744             return null;
745         ListBuffer<URL> lb = new ListBuffer<>();
746         for (File f: path) {
747             try {
748                 lb.append(f.toURI().toURL());
749             } catch (MalformedURLException e) {
750                 throw new AssertionError(e);
751             }
752         }
753 
754         return getClassLoader(lb.toArray(new URL[lb.size()]));
755     }
756 
757     @Override @DefinedBy(Api.COMPILER)
list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse)758     public Iterable<JavaFileObject> list(Location location,
759                                          String packageName,
760                                          Set<JavaFileObject.Kind> kinds,
761                                          boolean recurse)
762         throws IOException
763     {
764         checkNotModuleOrientedLocation(location);
765         // validatePackageName(packageName);
766         nullCheck(packageName);
767         nullCheck(kinds);
768 
769         RelativeDirectory subdirectory = RelativeDirectory.forPackage(packageName);
770         ListBuffer<JavaFileObject> results = new ListBuffer<>();
771 
772         for (PathAndContainer pathAndContainer : pathsAndContainers(location, subdirectory)) {
773             Path directory = pathAndContainer.path;
774             Container container = pathAndContainer.container;
775             container.list(directory, subdirectory, kinds, recurse, results);
776         }
777 
778         return results.toList();
779     }
780 
781     @Override @DefinedBy(Api.COMPILER)
inferBinaryName(Location location, JavaFileObject file)782     public String inferBinaryName(Location location, JavaFileObject file) {
783         checkNotModuleOrientedLocation(location);
784         Objects.requireNonNull(file);
785         // Need to match the path semantics of list(location, ...)
786         Iterable<? extends Path> path = getLocationAsPaths(location);
787         if (path == null) {
788             return null;
789         }
790 
791         if (file instanceof PathFileObject) {
792             return ((PathFileObject) file).inferBinaryName(path);
793         } else
794             throw new IllegalArgumentException(file.getClass().getName());
795     }
796 
797     @Override @DefinedBy(Api.COMPILER)
isSameFile(FileObject a, FileObject b)798     public boolean isSameFile(FileObject a, FileObject b) {
799         nullCheck(a);
800         nullCheck(b);
801         if (a instanceof PathFileObject && b instanceof PathFileObject)
802             return ((PathFileObject) a).isSameFile((PathFileObject) b);
803         return a.equals(b);
804     }
805 
806     @Override @DefinedBy(Api.COMPILER)
hasLocation(Location location)807     public boolean hasLocation(Location location) {
808         nullCheck(location);
809         return locations.hasLocation(location);
810     }
811 
hasExplicitLocation(Location location)812     protected boolean hasExplicitLocation(Location location) {
813         nullCheck(location);
814         return locations.hasExplicitLocation(location);
815     }
816 
817     @Override @DefinedBy(Api.COMPILER)
getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind)818     public JavaFileObject getJavaFileForInput(Location location,
819                                               String className,
820                                               JavaFileObject.Kind kind)
821         throws IOException
822     {
823         checkNotModuleOrientedLocation(location);
824         // validateClassName(className);
825         nullCheck(className);
826         nullCheck(kind);
827         if (!SOURCE_OR_CLASS.contains(kind))
828             throw new IllegalArgumentException("Invalid kind: " + kind);
829         return getFileForInput(location, RelativeFile.forClass(className, kind));
830     }
831 
832     @Override @DefinedBy(Api.COMPILER)
getFileForInput(Location location, String packageName, String relativeName)833     public FileObject getFileForInput(Location location,
834                                       String packageName,
835                                       String relativeName)
836         throws IOException
837     {
838         checkNotModuleOrientedLocation(location);
839         // validatePackageName(packageName);
840         nullCheck(packageName);
841         if (!isRelativeUri(relativeName))
842             throw new IllegalArgumentException("Invalid relative name: " + relativeName);
843         RelativeFile name = packageName.length() == 0
844             ? new RelativeFile(relativeName)
845             : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName);
846         return getFileForInput(location, name);
847     }
848 
getFileForInput(Location location, RelativeFile name)849     private JavaFileObject getFileForInput(Location location, RelativeFile name) throws IOException {
850         Iterable<? extends Path> path = getLocationAsPaths(location);
851         if (path == null)
852             return null;
853 
854         for (Path file: path) {
855             JavaFileObject fo = getContainer(file).getFileObject(file, name);
856 
857             if (fo != null) {
858                 return fo;
859             }
860         }
861         return null;
862     }
863 
864     @Override @DefinedBy(Api.COMPILER)
getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling)865     public JavaFileObject getJavaFileForOutput(Location location,
866                                                String className,
867                                                JavaFileObject.Kind kind,
868                                                FileObject sibling)
869         throws IOException
870     {
871         checkOutputLocation(location);
872         // validateClassName(className);
873         nullCheck(className);
874         nullCheck(kind);
875         if (!SOURCE_OR_CLASS.contains(kind))
876             throw new IllegalArgumentException("Invalid kind: " + kind);
877         return getFileForOutput(location, RelativeFile.forClass(className, kind), sibling);
878     }
879 
880     @Override @DefinedBy(Api.COMPILER)
getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling)881     public FileObject getFileForOutput(Location location,
882                                        String packageName,
883                                        String relativeName,
884                                        FileObject sibling)
885         throws IOException
886     {
887         checkOutputLocation(location);
888         // validatePackageName(packageName);
889         nullCheck(packageName);
890         if (!isRelativeUri(relativeName))
891             throw new IllegalArgumentException("Invalid relative name: " + relativeName);
892         RelativeFile name = packageName.length() == 0
893             ? new RelativeFile(relativeName)
894             : new RelativeFile(RelativeDirectory.forPackage(packageName), relativeName);
895         return getFileForOutput(location, name, sibling);
896     }
897 
getFileForOutput(Location location, RelativeFile fileName, FileObject sibling)898     private JavaFileObject getFileForOutput(Location location,
899                                             RelativeFile fileName,
900                                             FileObject sibling)
901         throws IOException
902     {
903         Path dir;
904         if (location == CLASS_OUTPUT) {
905             if (getClassOutDir() != null) {
906                 dir = getClassOutDir();
907             } else {
908                 String baseName = fileName.basename();
909                 if (sibling != null && sibling instanceof PathFileObject) {
910                     return ((PathFileObject) sibling).getSibling(baseName);
911                 } else {
912                     Path p = getPath(baseName);
913                     Path real = fsInfo.getCanonicalFile(p);
914                     return PathFileObject.forSimplePath(this, real, p);
915                 }
916             }
917         } else if (location == SOURCE_OUTPUT) {
918             dir = (getSourceOutDir() != null ? getSourceOutDir() : getClassOutDir());
919         } else {
920             Iterable<? extends Path> path = locations.getLocation(location);
921             dir = null;
922             for (Path f: path) {
923                 dir = f;
924                 break;
925             }
926         }
927 
928         try {
929             if (dir == null) {
930                 dir = getPath(System.getProperty("user.dir"));
931             }
932             Path path = fileName.resolveAgainst(fsInfo.getCanonicalFile(dir));
933             return PathFileObject.forDirectoryPath(this, path, dir, fileName);
934         } catch (InvalidPathException e) {
935             throw new IOException("bad filename " + fileName, e);
936         }
937     }
938 
939     @Override @DefinedBy(Api.COMPILER)
getJavaFileObjectsFromFiles( Iterable<? extends File> files)940     public Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(
941         Iterable<? extends File> files)
942     {
943         ArrayList<PathFileObject> result;
944         if (files instanceof Collection<?>)
945             result = new ArrayList<>(((Collection<?>)files).size());
946         else
947             result = new ArrayList<>();
948         for (File f: files) {
949             Objects.requireNonNull(f);
950             Path p = f.toPath();
951             result.add(PathFileObject.forSimplePath(this,
952                     fsInfo.getCanonicalFile(p), p));
953         }
954         return result;
955     }
956 
957     @Override @DefinedBy(Api.COMPILER)
getJavaFileObjectsFromPaths( Collection<? extends Path> paths)958     public Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths(
959         Collection<? extends Path> paths)
960     {
961         ArrayList<PathFileObject> result;
962         if (paths instanceof Collection<?>)
963             result = new ArrayList<>(((Collection<?>)paths).size());
964         else
965             result = new ArrayList<>();
966         for (Path p: paths)
967             result.add(PathFileObject.forSimplePath(this,
968                     fsInfo.getCanonicalFile(p), p));
969         return result;
970     }
971 
972     @Override @DefinedBy(Api.COMPILER)
getJavaFileObjects(File... files)973     public Iterable<? extends JavaFileObject> getJavaFileObjects(File... files) {
974         return getJavaFileObjectsFromFiles(Arrays.asList(nullCheck(files)));
975     }
976 
977     @Override @DefinedBy(Api.COMPILER)
getJavaFileObjects(Path... paths)978     public Iterable<? extends JavaFileObject> getJavaFileObjects(Path... paths) {
979         return getJavaFileObjectsFromPaths(Arrays.asList(nullCheck(paths)));
980     }
981 
982     @Override @DefinedBy(Api.COMPILER)
setLocation(Location location, Iterable<? extends File> searchpath)983     public void setLocation(Location location,
984                             Iterable<? extends File> searchpath)
985         throws IOException
986     {
987         nullCheck(location);
988         locations.setLocation(location, asPaths(searchpath));
989         clearCachesForLocation(location);
990     }
991 
992     @Override @DefinedBy(Api.COMPILER)
setLocationFromPaths(Location location, Collection<? extends Path> searchpath)993     public void setLocationFromPaths(Location location,
994                             Collection<? extends Path> searchpath)
995         throws IOException
996     {
997         nullCheck(location);
998         locations.setLocation(location, nullCheck(searchpath));
999         clearCachesForLocation(location);
1000     }
1001 
1002     @Override @DefinedBy(Api.COMPILER)
getLocation(Location location)1003     public Iterable<? extends File> getLocation(Location location) {
1004         nullCheck(location);
1005         return asFiles(locations.getLocation(location));
1006     }
1007 
1008     @Override @DefinedBy(Api.COMPILER)
getLocationAsPaths(Location location)1009     public Collection<? extends Path> getLocationAsPaths(Location location) {
1010         nullCheck(location);
1011         return locations.getLocation(location);
1012     }
1013 
pathsAndContainers( Location location, RelativeDirectory relativeDirectory)1014     private java.util.List<PathAndContainer> pathsAndContainers(
1015             Location location, RelativeDirectory relativeDirectory) throws IOException {
1016         try {
1017             return pathsAndContainersByLocationAndRelativeDirectory.computeIfAbsent(
1018                     location, this::indexPathsAndContainersByRelativeDirectory)
1019                 .computeIfAbsent(
1020                     relativeDirectory, d -> nonIndexingContainersByLocation.get(location));
1021         } catch (UncheckedIOException e) {
1022             throw e.getCause();
1023         }
1024     }
1025 
indexPathsAndContainersByRelativeDirectory( Location location)1026     private Map<RelativeDirectory, java.util.List<PathAndContainer>> indexPathsAndContainersByRelativeDirectory(
1027             Location location) {
1028         Map<RelativeDirectory, java.util.List<PathAndContainer>> result = new HashMap<>();
1029         java.util.List<PathAndContainer> allPathsAndContainers = pathsAndContainers(location);
1030 
1031         // First collect all of the containers that don't maintain their own index on
1032         // RelativeDirectory. These need to always be included for all mappings
1033         java.util.List<PathAndContainer> nonIndexingContainers = new ArrayList<>();
1034         for (PathAndContainer pathAndContainer : allPathsAndContainers) {
1035             if (!pathAndContainer.container.maintainsDirectoryIndex()) {
1036                 nonIndexingContainers.add(pathAndContainer);
1037             }
1038         }
1039 
1040         // Next, use the container that do maintain their own RelativeDirectory index to create a
1041         // single master index.
1042         for (PathAndContainer pathAndContainer : allPathsAndContainers) {
1043             Container container = pathAndContainer.container;
1044             if (container.maintainsDirectoryIndex()) {
1045                 for (RelativeDirectory directory : container.indexedDirectories()) {
1046                     result.computeIfAbsent(directory, d -> new ArrayList<>(nonIndexingContainers))
1047                           .add(pathAndContainer);
1048                 }
1049             }
1050         }
1051         nonIndexingContainersByLocation.put(location, nonIndexingContainers);
1052 
1053         // Sorting preserves the search order used in the uncached Location path, which has
1054         // maintains consistency with the classpath order
1055         result.values().forEach(pathAndContainerList -> Collections.sort(pathAndContainerList));
1056 
1057         return result;
1058     }
1059 
1060     /**
1061      * For each {@linkplain #getLocationAsPaths(Location) path of the location}, compute the
1062      * corresponding {@link Container}.
1063      */
pathsAndContainers(Location location)1064     private java.util.List<PathAndContainer> pathsAndContainers(Location location) {
1065         Collection<? extends Path> paths = getLocationAsPaths(location);
1066         if (paths == null) {
1067             return List.nil();
1068         }
1069         java.util.List<PathAndContainer> pathsAndContainers =
1070             new ArrayList<>(paths.size());
1071         for (Path path : paths) {
1072             Container container;
1073             try {
1074                 container = getContainer(path);
1075             } catch (IOException e) {
1076                 throw new UncheckedIOException(e);
1077             }
1078             pathsAndContainers.add(new PathAndContainer(path, container, pathsAndContainers.size()));
1079         }
1080         return pathsAndContainers;
1081     }
1082 
1083     private static class PathAndContainer implements Comparable<PathAndContainer> {
1084         private final Path path;
1085         private final Container container;
1086         private final int index;
1087 
PathAndContainer(Path path, Container container, int index)1088         PathAndContainer(Path path, Container container, int index) {
1089             this.path = path;
1090             this.container = container;
1091             this.index = index;
1092         }
1093 
1094         @Override
compareTo(PathAndContainer other)1095         public int compareTo(PathAndContainer other) {
1096             return index - other.index;
1097         }
1098 
1099         @Override
equals(Object o)1100         public boolean equals(Object o) {
1101           if (o == null || !(o instanceof PathAndContainer)) {
1102             return false;
1103           }
1104           PathAndContainer that = (PathAndContainer) o;
1105           return path.equals(that.path)
1106               && container.equals(that.container)
1107               && index == this.index;
1108         }
1109 
1110         @Override
hashCode()1111         public int hashCode() {
1112           return Objects.hash(path, container, index);
1113         }
1114     }
1115 
1116     @Override @DefinedBy(Api.COMPILER)
contains(Location location, FileObject fo)1117     public boolean contains(Location location, FileObject fo) throws IOException {
1118         nullCheck(location);
1119         nullCheck(fo);
1120         Path p = asPath(fo);
1121         return locations.contains(location, p);
1122     }
1123 
getClassOutDir()1124     private Path getClassOutDir() {
1125         return locations.getOutputLocation(CLASS_OUTPUT);
1126     }
1127 
getSourceOutDir()1128     private Path getSourceOutDir() {
1129         return locations.getOutputLocation(SOURCE_OUTPUT);
1130     }
1131 
1132     @Override @DefinedBy(Api.COMPILER)
getLocationForModule(Location location, String moduleName)1133     public Location getLocationForModule(Location location, String moduleName) throws IOException {
1134         checkModuleOrientedOrOutputLocation(location);
1135         nullCheck(moduleName);
1136         if (location == SOURCE_OUTPUT && getSourceOutDir() == null)
1137             location = CLASS_OUTPUT;
1138         return locations.getLocationForModule(location, moduleName);
1139     }
1140 
1141     @Override @DefinedBy(Api.COMPILER)
getServiceLoader(Location location, Class<S> service)1142     public <S> ServiceLoader<S> getServiceLoader(Location location, Class<S> service) throws IOException {
1143         nullCheck(location);
1144         nullCheck(service);
1145         getClass().getModule().addUses(service);
1146         if (location.isModuleOrientedLocation()) {
1147             Collection<Path> paths = locations.getLocation(location);
1148             ModuleFinder finder = ModuleFinder.of(paths.toArray(new Path[paths.size()]));
1149             ModuleLayer bootLayer = ModuleLayer.boot();
1150             Configuration cf = bootLayer.configuration().resolveAndBind(ModuleFinder.of(), finder, Collections.emptySet());
1151             ModuleLayer layer = bootLayer.defineModulesWithOneLoader(cf, ClassLoader.getSystemClassLoader());
1152             return ServiceLoader.load(layer, service);
1153         } else {
1154             return ServiceLoader.load(service, getClassLoader(location));
1155         }
1156     }
1157 
1158     @Override @DefinedBy(Api.COMPILER)
getLocationForModule(Location location, JavaFileObject fo)1159     public Location getLocationForModule(Location location, JavaFileObject fo) throws IOException {
1160         checkModuleOrientedOrOutputLocation(location);
1161         if (!(fo instanceof PathFileObject))
1162             return null;
1163         Path p = Locations.normalize(((PathFileObject) fo).path);
1164             // need to find p in location
1165         return locations.getLocationForModule(location, p);
1166     }
1167 
1168     @Override @DefinedBy(Api.COMPILER)
setLocationForModule(Location location, String moduleName, Collection<? extends Path> paths)1169     public void setLocationForModule(Location location, String moduleName, Collection<? extends Path> paths)
1170             throws IOException {
1171         nullCheck(location);
1172         checkModuleOrientedOrOutputLocation(location);
1173         locations.setLocationForModule(location, nullCheck(moduleName), nullCheck(paths));
1174         clearCachesForLocation(location);
1175     }
1176 
1177     @Override @DefinedBy(Api.COMPILER)
inferModuleName(Location location)1178     public String inferModuleName(Location location) {
1179         checkNotModuleOrientedLocation(location);
1180         return locations.inferModuleName(location);
1181     }
1182 
1183     @Override @DefinedBy(Api.COMPILER)
listLocationsForModules(Location location)1184     public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
1185         checkModuleOrientedOrOutputLocation(location);
1186         return locations.listLocationsForModules(location);
1187     }
1188 
1189     @Override @DefinedBy(Api.COMPILER)
asPath(FileObject file)1190     public Path asPath(FileObject file) {
1191         if (file instanceof PathFileObject) {
1192             return ((PathFileObject) file).path;
1193         } else
1194             throw new IllegalArgumentException(file.getName());
1195     }
1196 
1197     /**
1198      * Enforces the specification of a "relative" name as used in
1199      * {@linkplain #getFileForInput(Location,String,String)
1200      * getFileForInput}.  This method must follow the rules defined in
1201      * that method, do not make any changes without consulting the
1202      * specification.
1203      */
isRelativeUri(URI uri)1204     protected static boolean isRelativeUri(URI uri) {
1205         if (uri.isAbsolute())
1206             return false;
1207         String path = uri.normalize().getPath();
1208         if (path.length() == 0 /* isEmpty() is mustang API */)
1209             return false;
1210         if (!path.equals(uri.getPath())) // implicitly checks for embedded . and ..
1211             return false;
1212         if (path.startsWith("/") || path.startsWith("./") || path.startsWith("../"))
1213             return false;
1214         return true;
1215     }
1216 
1217     // Convenience method
isRelativeUri(String u)1218     protected static boolean isRelativeUri(String u) {
1219         try {
1220             return isRelativeUri(new URI(u));
1221         } catch (URISyntaxException e) {
1222             return false;
1223         }
1224     }
1225 
1226     /**
1227      * Converts a relative file name to a relative URI.  This is
1228      * different from File.toURI as this method does not canonicalize
1229      * the file before creating the URI.  Furthermore, no schema is
1230      * used.
1231      * @param file a relative file name
1232      * @return a relative URI
1233      * @throws IllegalArgumentException if the file name is not
1234      * relative according to the definition given in {@link
1235      * javax.tools.JavaFileManager#getFileForInput}
1236      */
getRelativeName(File file)1237     public static String getRelativeName(File file) {
1238         if (!file.isAbsolute()) {
1239             String result = file.getPath().replace(File.separatorChar, '/');
1240             if (isRelativeUri(result))
1241                 return result;
1242         }
1243         throw new IllegalArgumentException("Invalid relative path: " + file);
1244     }
1245 
1246     /**
1247      * Get a detail message from an IOException.
1248      * Most, but not all, instances of IOException provide a non-null result
1249      * for getLocalizedMessage().  But some instances return null: in these
1250      * cases, fallover to getMessage(), and if even that is null, return the
1251      * name of the exception itself.
1252      * @param e an IOException
1253      * @return a string to include in a compiler diagnostic
1254      */
getMessage(IOException e)1255     public static String getMessage(IOException e) {
1256         String s = e.getLocalizedMessage();
1257         if (s != null)
1258             return s;
1259         s = e.getMessage();
1260         if (s != null)
1261             return s;
1262         return e.toString();
1263     }
1264 
checkOutputLocation(Location location)1265     private void checkOutputLocation(Location location) {
1266         Objects.requireNonNull(location);
1267         if (!location.isOutputLocation())
1268             throw new IllegalArgumentException("location is not an output location: " + location.getName());
1269     }
1270 
checkModuleOrientedOrOutputLocation(Location location)1271     private void checkModuleOrientedOrOutputLocation(Location location) {
1272         Objects.requireNonNull(location);
1273         if (!location.isModuleOrientedLocation() && !location.isOutputLocation())
1274             throw new IllegalArgumentException(
1275                     "location is not an output location or a module-oriented location: "
1276                             + location.getName());
1277     }
1278 
checkNotModuleOrientedLocation(Location location)1279     private void checkNotModuleOrientedLocation(Location location) {
1280         Objects.requireNonNull(location);
1281         if (location.isModuleOrientedLocation())
1282             throw new IllegalArgumentException("location is module-oriented: " + location.getName());
1283     }
1284 
1285     /* Converters between files and paths.
1286      * These are temporary until we can update the StandardJavaFileManager API.
1287      */
1288 
asPaths(final Iterable<? extends File> files)1289     private static Iterable<Path> asPaths(final Iterable<? extends File> files) {
1290         if (files == null)
1291             return null;
1292 
1293         return () -> new Iterator<Path>() {
1294             Iterator<? extends File> iter = files.iterator();
1295 
1296             @Override
1297             public boolean hasNext() {
1298                 return iter.hasNext();
1299             }
1300 
1301             @Override
1302             public Path next() {
1303                 return iter.next().toPath();
1304             }
1305         };
1306     }
1307 
asFiles(final Iterable<? extends Path> paths)1308     private static Iterable<File> asFiles(final Iterable<? extends Path> paths) {
1309         if (paths == null)
1310             return null;
1311 
1312         return () -> new Iterator<File>() {
1313             Iterator<? extends Path> iter = paths.iterator();
1314 
1315             @Override
1316             public boolean hasNext() {
1317                 return iter.hasNext();
1318             }
1319 
1320             @Override
1321             public File next() {
1322                 try {
1323                     return iter.next().toFile();
1324                 } catch (UnsupportedOperationException e) {
1325                     throw new IllegalStateException(e);
1326                 }
1327             }
1328         };
1329     }
1330 
1331     @Override
1332     public boolean handleOption(Option option, String value) {
1333         if (javacFileManagerOptions.contains(option)) {
1334             pathsAndContainersByLocationAndRelativeDirectory.clear();
1335             nonIndexingContainersByLocation.clear();
1336         }
1337         return super.handleOption(option, value);
1338     }
1339 
1340     private void clearCachesForLocation(Location location) {
1341         nullCheck(location);
1342         pathsAndContainersByLocationAndRelativeDirectory.remove(location);
1343         nonIndexingContainersByLocation.remove(location);
1344     }
1345 }
1346