src/share/classes/com/sun/tools/javac/nio/JavacPathFileManager.java

Wed, 06 Apr 2011 20:33:44 -0700

author
ohair
date
Wed, 06 Apr 2011 20:33:44 -0700
changeset 962
0ff2bbd38f10
parent 847
babf86a1ac92
child 1111
d2cbb77469ed
permissions
-rw-r--r--

7033660: Update copyright year to 2011 on any files changed in 2011
Reviewed-by: dholmes

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

mercurial