langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java
changeset 29780 8f8e54a1fa20
parent 29779 81572cfb60a0
child 29781 a0cae57ff045
equal deleted inserted replaced
29779:81572cfb60a0 29780:8f8e54a1fa20
     1 /*
       
     2  * Copyright (c) 2009, 2014, 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.nio;
       
    27 
       
    28 import java.io.File;
       
    29 import java.io.IOException;
       
    30 import java.net.MalformedURLException;
       
    31 import java.net.URL;
       
    32 import java.nio.charset.Charset;
       
    33 import java.nio.file.FileSystem;
       
    34 import java.nio.file.FileSystems;
       
    35 import java.nio.file.FileVisitOption;
       
    36 import java.nio.file.FileVisitResult;
       
    37 import java.nio.file.Files;
       
    38 import java.nio.file.Path;
       
    39 import java.nio.file.Paths;
       
    40 import java.nio.file.SimpleFileVisitor;
       
    41 import java.nio.file.attribute.BasicFileAttributes;
       
    42 import java.util.ArrayList;
       
    43 import java.util.Arrays;
       
    44 import java.util.Collection;
       
    45 import java.util.Collections;
       
    46 import java.util.EnumSet;
       
    47 import java.util.HashMap;
       
    48 import java.util.Iterator;
       
    49 import java.util.LinkedHashSet;
       
    50 import java.util.Map;
       
    51 import java.util.Set;
       
    52 
       
    53 import javax.lang.model.SourceVersion;
       
    54 import javax.tools.FileObject;
       
    55 import javax.tools.JavaFileManager;
       
    56 import javax.tools.JavaFileObject;
       
    57 import javax.tools.JavaFileObject.Kind;
       
    58 import javax.tools.StandardLocation;
       
    59 
       
    60 import com.sun.tools.javac.util.BaseFileManager;
       
    61 import com.sun.tools.javac.util.Context;
       
    62 import com.sun.tools.javac.util.DefinedBy;
       
    63 import com.sun.tools.javac.util.DefinedBy.Api;
       
    64 import com.sun.tools.javac.util.List;
       
    65 import com.sun.tools.javac.util.ListBuffer;
       
    66 
       
    67 import static java.nio.file.FileVisitOption.*;
       
    68 
       
    69 import static javax.tools.StandardLocation.*;
       
    70 
       
    71 import static com.sun.tools.javac.main.Option.*;
       
    72 
       
    73 
       
    74 // NOTE the imports carefully for this compilation unit.
       
    75 //
       
    76 // Path:  java.nio.file.Path -- the new NIO type for which this file manager exists
       
    77 //
       
    78 // Paths: com.sun.tools.javac.file.Paths -- legacy javac type for handling path options
       
    79 //      The other Paths (java.nio.file.Paths) is not used
       
    80 
       
    81 // NOTE this and related classes depend on new API in JDK 7.
       
    82 // This requires special handling while bootstrapping the JDK build,
       
    83 // when these classes might not yet have been compiled. To workaround
       
    84 // this, the build arranges to make stubs of these classes available
       
    85 // when compiling this and related classes. The set of stub files
       
    86 // is specified in make/build.properties.
       
    87 
       
    88 /**
       
    89  *  Implementation of PathFileManager: a JavaFileManager based on the use
       
    90  *  of java.nio.file.Path.
       
    91  *
       
    92  *  <p>Just as a Path is somewhat analagous to a File, so too is this
       
    93  *  JavacPathFileManager analogous to JavacFileManager, as it relates to the
       
    94  *  support of FileObjects based on File objects (i.e. just RegularFileObject,
       
    95  *  not ZipFileObject and its variants.)
       
    96  *
       
    97  *  <p>The default values for the standard locations supported by this file
       
    98  *  manager are the same as the default values provided by JavacFileManager --
       
    99  *  i.e. as determined by the javac.file.Paths class. To override these values,
       
   100  *  call {@link #setLocation}.
       
   101  *
       
   102  *  <p>To reduce confusion with Path objects, the locations such as "class path",
       
   103  *  "source path", etc, are generically referred to here as "search paths".
       
   104  *
       
   105  *  <p><b>This is NOT part of any supported API.
       
   106  *  If you write code that depends on this, you do so at your own risk.
       
   107  *  This code and its internal interfaces are subject to change or
       
   108  *  deletion without notice.</b>
       
   109  */
       
   110 public class JavacPathFileManager extends BaseFileManager implements PathFileManager {
       
   111     protected FileSystem defaultFileSystem;
       
   112 
       
   113     /**
       
   114      * Create a JavacPathFileManager using a given context, optionally registering
       
   115      * it as the JavaFileManager for that context.
       
   116      */
       
   117     public JavacPathFileManager(Context context, boolean register, Charset charset) {
       
   118         super(charset);
       
   119         if (register)
       
   120             context.put(JavaFileManager.class, this);
       
   121         pathsForLocation = new HashMap<>();
       
   122         fileSystems = new HashMap<>();
       
   123         setContext(context);
       
   124     }
       
   125 
       
   126     /**
       
   127      * Set the context for JavacPathFileManager.
       
   128      */
       
   129     @Override
       
   130     public void setContext(Context context) {
       
   131         super.setContext(context);
       
   132     }
       
   133 
       
   134     @Override
       
   135     public FileSystem getDefaultFileSystem() {
       
   136         if (defaultFileSystem == null)
       
   137             defaultFileSystem = FileSystems.getDefault();
       
   138         return defaultFileSystem;
       
   139     }
       
   140 
       
   141     @Override
       
   142     public void setDefaultFileSystem(FileSystem fs) {
       
   143         defaultFileSystem = fs;
       
   144     }
       
   145 
       
   146     @Override @DefinedBy(Api.COMPILER)
       
   147     public void flush() throws IOException {
       
   148         contentCache.clear();
       
   149     }
       
   150 
       
   151     @Override @DefinedBy(Api.COMPILER)
       
   152     public void close() throws IOException {
       
   153         for (FileSystem fs: fileSystems.values())
       
   154             fs.close();
       
   155     }
       
   156 
       
   157     @Override @DefinedBy(Api.COMPILER)
       
   158     public ClassLoader getClassLoader(Location location) {
       
   159         nullCheck(location);
       
   160         Iterable<? extends Path> path = getLocation(location);
       
   161         if (path == null)
       
   162             return null;
       
   163         ListBuffer<URL> lb = new ListBuffer<>();
       
   164         for (Path p: path) {
       
   165             try {
       
   166                 lb.append(p.toUri().toURL());
       
   167             } catch (MalformedURLException e) {
       
   168                 throw new AssertionError(e);
       
   169             }
       
   170         }
       
   171 
       
   172         return getClassLoader(lb.toArray(new URL[lb.size()]));
       
   173     }
       
   174 
       
   175     // <editor-fold defaultstate="collapsed" desc="Location handling">
       
   176 
       
   177     @DefinedBy(Api.COMPILER)
       
   178     public boolean hasLocation(Location location) {
       
   179         return (getLocation(location) != null);
       
   180     }
       
   181 
       
   182     public Iterable<? extends Path> getLocation(Location location) {
       
   183         nullCheck(location);
       
   184         lazyInitSearchPaths();
       
   185         PathsForLocation path = pathsForLocation.get(location);
       
   186         if (path == null && !pathsForLocation.containsKey(location)) {
       
   187             setDefaultForLocation(location);
       
   188             path = pathsForLocation.get(location);
       
   189         }
       
   190         return path;
       
   191     }
       
   192 
       
   193     private Path getOutputLocation(Location location) {
       
   194         Iterable<? extends Path> paths = getLocation(location);
       
   195         return (paths == null ? null : paths.iterator().next());
       
   196     }
       
   197 
       
   198     public void setLocation(Location location, Iterable<? extends Path> searchPath)
       
   199             throws IOException
       
   200     {
       
   201         nullCheck(location);
       
   202         lazyInitSearchPaths();
       
   203         if (searchPath == null) {
       
   204             setDefaultForLocation(location);
       
   205         } else {
       
   206             if (location.isOutputLocation())
       
   207                 checkOutputPath(searchPath);
       
   208             PathsForLocation pl = new PathsForLocation();
       
   209             for (Path p: searchPath)
       
   210                 pl.add(p);  // TODO -Xlint:path warn if path not found
       
   211             pathsForLocation.put(location, pl);
       
   212         }
       
   213     }
       
   214 
       
   215     private void checkOutputPath(Iterable<? extends Path> searchPath) throws IOException {
       
   216         Iterator<? extends Path> pathIter = searchPath.iterator();
       
   217         if (!pathIter.hasNext())
       
   218             throw new IllegalArgumentException("empty path for directory");
       
   219         Path path = pathIter.next();
       
   220         if (pathIter.hasNext())
       
   221             throw new IllegalArgumentException("path too long for directory");
       
   222         if (!isDirectory(path))
       
   223             throw new IOException(path + ": not a directory");
       
   224     }
       
   225 
       
   226     private void setDefaultForLocation(Location locn) {
       
   227         Collection<Path> files = null;
       
   228         if (locn instanceof StandardLocation) {
       
   229             switch ((StandardLocation) locn) {
       
   230                 case CLASS_PATH:
       
   231                     files = locations.userClassPath();
       
   232                     break;
       
   233                 case PLATFORM_CLASS_PATH:
       
   234                     files = locations.bootClassPath();
       
   235                     break;
       
   236                 case SOURCE_PATH:
       
   237                     files = locations.sourcePath();
       
   238                     break;
       
   239                 case CLASS_OUTPUT: {
       
   240                     String arg = options.get(D);
       
   241                     files = (arg == null ? null : Collections.singleton(Paths.get(arg)));
       
   242                     break;
       
   243                 }
       
   244                 case SOURCE_OUTPUT: {
       
   245                     String arg = options.get(S);
       
   246                     files = (arg == null ? null : Collections.singleton(Paths.get(arg)));
       
   247                     break;
       
   248                 }
       
   249             }
       
   250         }
       
   251 
       
   252         PathsForLocation pl = new PathsForLocation();
       
   253         if (files != null) {
       
   254             for (Path f: files)
       
   255                 pl.add(f);
       
   256         }
       
   257         if (!pl.isEmpty())
       
   258             pathsForLocation.put(locn, pl);
       
   259     }
       
   260 
       
   261     private void lazyInitSearchPaths() {
       
   262         if (!inited) {
       
   263             setDefaultForLocation(PLATFORM_CLASS_PATH);
       
   264             setDefaultForLocation(CLASS_PATH);
       
   265             setDefaultForLocation(SOURCE_PATH);
       
   266             inited = true;
       
   267         }
       
   268     }
       
   269     // where
       
   270         private boolean inited = false;
       
   271 
       
   272     private Map<Location, PathsForLocation> pathsForLocation;
       
   273 
       
   274     private static class PathsForLocation extends LinkedHashSet<Path> {
       
   275         private static final long serialVersionUID = 6788510222394486733L;
       
   276     }
       
   277 
       
   278     // </editor-fold>
       
   279 
       
   280     // <editor-fold defaultstate="collapsed" desc="FileObject handling">
       
   281 
       
   282     @Override
       
   283     public Path getPath(FileObject fo) {
       
   284         nullCheck(fo);
       
   285         if (!(fo instanceof PathFileObject))
       
   286             throw new IllegalArgumentException();
       
   287         return ((PathFileObject) fo).getPath();
       
   288     }
       
   289 
       
   290     @Override @DefinedBy(Api.COMPILER)
       
   291     public boolean isSameFile(FileObject a, FileObject b) {
       
   292         nullCheck(a);
       
   293         nullCheck(b);
       
   294         if (!(a instanceof PathFileObject))
       
   295             throw new IllegalArgumentException("Not supported: " + a);
       
   296         if (!(b instanceof PathFileObject))
       
   297             throw new IllegalArgumentException("Not supported: " + b);
       
   298         return ((PathFileObject) a).isSameFile((PathFileObject) b);
       
   299     }
       
   300 
       
   301     @Override @DefinedBy(Api.COMPILER)
       
   302     public Iterable<JavaFileObject> list(Location location,
       
   303             String packageName, Set<Kind> kinds, boolean recurse)
       
   304             throws IOException {
       
   305         // validatePackageName(packageName);
       
   306         nullCheck(packageName);
       
   307         nullCheck(kinds);
       
   308 
       
   309         Iterable<? extends Path> paths = getLocation(location);
       
   310         if (paths == null)
       
   311             return List.nil();
       
   312         ListBuffer<JavaFileObject> results = new ListBuffer<>();
       
   313 
       
   314         for (Path path : paths)
       
   315             list(path, packageName, kinds, recurse, results);
       
   316 
       
   317         return results.toList();
       
   318     }
       
   319 
       
   320     private void list(Path path, String packageName, final Set<Kind> kinds,
       
   321             boolean recurse, final ListBuffer<JavaFileObject> results)
       
   322             throws IOException {
       
   323         if (!Files.exists(path))
       
   324             return;
       
   325 
       
   326         final Path pathDir;
       
   327         if (isDirectory(path))
       
   328             pathDir = path;
       
   329         else {
       
   330             FileSystem fs = getFileSystem(path);
       
   331             if (fs == null)
       
   332                 return;
       
   333             pathDir = fs.getRootDirectories().iterator().next();
       
   334         }
       
   335         String sep = path.getFileSystem().getSeparator();
       
   336         Path packageDir = packageName.isEmpty() ? pathDir
       
   337                 : pathDir.resolve(packageName.replace(".", sep));
       
   338         if (!Files.exists(packageDir))
       
   339             return;
       
   340 
       
   341 /* Alternate impl of list, superceded by use of Files.walkFileTree */
       
   342 //        Deque<Path> queue = new LinkedList<Path>();
       
   343 //        queue.add(packageDir);
       
   344 //
       
   345 //        Path dir;
       
   346 //        while ((dir = queue.poll()) != null) {
       
   347 //            DirectoryStream<Path> ds = dir.newDirectoryStream();
       
   348 //            try {
       
   349 //                for (Path p: ds) {
       
   350 //                    String name = p.getFileName().toString();
       
   351 //                    if (isDirectory(p)) {
       
   352 //                        if (recurse && SourceVersion.isIdentifier(name)) {
       
   353 //                            queue.add(p);
       
   354 //                        }
       
   355 //                    } else {
       
   356 //                        if (kinds.contains(getKind(name))) {
       
   357 //                            JavaFileObject fe =
       
   358 //                                PathFileObject.createDirectoryPathFileObject(this, p, pathDir);
       
   359 //                            results.append(fe);
       
   360 //                        }
       
   361 //                    }
       
   362 //                }
       
   363 //            } finally {
       
   364 //                ds.close();
       
   365 //            }
       
   366 //        }
       
   367         int maxDepth = (recurse ? Integer.MAX_VALUE : 1);
       
   368         Set<FileVisitOption> opts = EnumSet.of(FOLLOW_LINKS);
       
   369         Files.walkFileTree(packageDir, opts, maxDepth,
       
   370                 new SimpleFileVisitor<Path>() {
       
   371             @Override
       
   372             public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
       
   373                 Path name = dir.getFileName();
       
   374                 if (name == null || SourceVersion.isIdentifier(name.toString()))
       
   375                     return FileVisitResult.CONTINUE;
       
   376                 else
       
   377                     return FileVisitResult.SKIP_SUBTREE;
       
   378             }
       
   379 
       
   380             @Override
       
   381             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
       
   382                 if (attrs.isRegularFile() && kinds.contains(getKind(file.getFileName().toString()))) {
       
   383                     // WORKAROUND for .jimage files
       
   384                     if (!file.isAbsolute())
       
   385                         file = pathDir.resolve(file);
       
   386                     JavaFileObject fe =
       
   387                         PathFileObject.createDirectoryPathFileObject(
       
   388                             JavacPathFileManager.this, file, pathDir);
       
   389                     results.append(fe);
       
   390                 }
       
   391                 return FileVisitResult.CONTINUE;
       
   392             }
       
   393         });
       
   394     }
       
   395 
       
   396     @Override
       
   397     public Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths(
       
   398         Iterable<? extends Path> paths) {
       
   399         ArrayList<PathFileObject> result;
       
   400         if (paths instanceof Collection<?>)
       
   401             result = new ArrayList<>(((Collection<?>)paths).size());
       
   402         else
       
   403             result = new ArrayList<>();
       
   404         for (Path p: paths)
       
   405             result.add(PathFileObject.createSimplePathFileObject(this, nullCheck(p)));
       
   406         return result;
       
   407     }
       
   408 
       
   409     @Override
       
   410     public Iterable<? extends JavaFileObject> getJavaFileObjects(Path... paths) {
       
   411         return getJavaFileObjectsFromPaths(Arrays.asList(nullCheck(paths)));
       
   412     }
       
   413 
       
   414     @Override @DefinedBy(Api.COMPILER)
       
   415     public JavaFileObject getJavaFileForInput(Location location,
       
   416             String className, Kind kind) throws IOException {
       
   417         return getFileForInput(location, getRelativePath(className, kind));
       
   418     }
       
   419 
       
   420     @Override @DefinedBy(Api.COMPILER)
       
   421     public FileObject getFileForInput(Location location,
       
   422             String packageName, String relativeName) throws IOException {
       
   423         return getFileForInput(location, getRelativePath(packageName, relativeName));
       
   424     }
       
   425 
       
   426     private JavaFileObject getFileForInput(Location location, String relativePath)
       
   427             throws IOException {
       
   428         for (Path p: getLocation(location)) {
       
   429             if (isDirectory(p)) {
       
   430                 Path f = resolve(p, relativePath);
       
   431                 if (Files.exists(f))
       
   432                     return PathFileObject.createDirectoryPathFileObject(this, f, p);
       
   433             } else {
       
   434                 FileSystem fs = getFileSystem(p);
       
   435                 if (fs != null) {
       
   436                     Path file = getPath(fs, relativePath);
       
   437                     if (Files.exists(file))
       
   438                         return PathFileObject.createJarPathFileObject(this, file);
       
   439                 }
       
   440             }
       
   441         }
       
   442         return null;
       
   443     }
       
   444 
       
   445     @Override @DefinedBy(Api.COMPILER)
       
   446     public JavaFileObject getJavaFileForOutput(Location location,
       
   447             String className, Kind kind, FileObject sibling) throws IOException {
       
   448         return getFileForOutput(location, getRelativePath(className, kind), sibling);
       
   449     }
       
   450 
       
   451     @Override @DefinedBy(Api.COMPILER)
       
   452     public FileObject getFileForOutput(Location location, String packageName,
       
   453             String relativeName, FileObject sibling)
       
   454             throws IOException {
       
   455         return getFileForOutput(location, getRelativePath(packageName, relativeName), sibling);
       
   456     }
       
   457 
       
   458     private JavaFileObject getFileForOutput(Location location,
       
   459             String relativePath, FileObject sibling) {
       
   460         Path dir = getOutputLocation(location);
       
   461         if (dir == null) {
       
   462             if (location == CLASS_OUTPUT) {
       
   463                 Path siblingDir = null;
       
   464                 if (sibling != null && sibling instanceof PathFileObject) {
       
   465                     siblingDir = ((PathFileObject) sibling).getPath().getParent();
       
   466                 }
       
   467                 return PathFileObject.createSiblingPathFileObject(this,
       
   468                         siblingDir.resolve(getBaseName(relativePath)),
       
   469                         relativePath);
       
   470             } else if (location == SOURCE_OUTPUT) {
       
   471                 dir = getOutputLocation(CLASS_OUTPUT);
       
   472             }
       
   473         }
       
   474 
       
   475         Path file;
       
   476         if (dir != null) {
       
   477             file = resolve(dir, relativePath);
       
   478             return PathFileObject.createDirectoryPathFileObject(this, file, dir);
       
   479         } else {
       
   480             file = getPath(getDefaultFileSystem(), relativePath);
       
   481             return PathFileObject.createSimplePathFileObject(this, file);
       
   482         }
       
   483 
       
   484     }
       
   485 
       
   486     @Override @DefinedBy(Api.COMPILER)
       
   487     public String inferBinaryName(Location location, JavaFileObject fo) {
       
   488         nullCheck(fo);
       
   489         // Need to match the path semantics of list(location, ...)
       
   490         Iterable<? extends Path> paths = getLocation(location);
       
   491         if (paths == null) {
       
   492             return null;
       
   493         }
       
   494 
       
   495         if (!(fo instanceof PathFileObject))
       
   496             throw new IllegalArgumentException(fo.getClass().getName());
       
   497 
       
   498         return ((PathFileObject) fo).inferBinaryName(paths);
       
   499     }
       
   500 
       
   501     private FileSystem getFileSystem(Path p) throws IOException {
       
   502         FileSystem fs = fileSystems.get(p);
       
   503         if (fs == null) {
       
   504             fs = FileSystems.newFileSystem(p, null);
       
   505             fileSystems.put(p, fs);
       
   506         }
       
   507         return fs;
       
   508     }
       
   509 
       
   510     private Map<Path,FileSystem> fileSystems;
       
   511 
       
   512     // </editor-fold>
       
   513 
       
   514     // <editor-fold defaultstate="collapsed" desc="Utility methods">
       
   515 
       
   516     private static String getRelativePath(String className, Kind kind) {
       
   517         return className.replace(".", "/") + kind.extension;
       
   518     }
       
   519 
       
   520     private static String getRelativePath(String packageName, String relativeName) {
       
   521         return packageName.isEmpty()
       
   522                 ? relativeName : packageName.replace(".", "/") + "/" + relativeName;
       
   523     }
       
   524 
       
   525     private static String getBaseName(String relativePath) {
       
   526         int lastSep = relativePath.lastIndexOf("/");
       
   527         return relativePath.substring(lastSep + 1); // safe if "/" not found
       
   528     }
       
   529 
       
   530     private static boolean isDirectory(Path path) throws IOException {
       
   531         BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
       
   532         return attrs.isDirectory();
       
   533     }
       
   534 
       
   535     private static Path getPath(FileSystem fs, String relativePath) {
       
   536         return fs.getPath(relativePath.replace("/", fs.getSeparator()));
       
   537     }
       
   538 
       
   539     private static Path resolve(Path base, String relativePath) {
       
   540         FileSystem fs = base.getFileSystem();
       
   541         Path rp = fs.getPath(relativePath.replace("/", fs.getSeparator()));
       
   542         return base.resolve(rp);
       
   543     }
       
   544 
       
   545     // </editor-fold>
       
   546 
       
   547 }