1 /*
   2  * Copyright (c) 2007, 2009, 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 java.nio.file;
  27 
  28 import java.nio.file.spi.FileTypeDetector;
  29 import java.nio.file.attribute.*;
  30 import java.io.IOException;
  31 import java.util.*;
  32 import java.security.AccessController;
  33 import java.security.PrivilegedAction;
  34 
  35 /**
  36  * This class consists exclusively of static methods that operate on files or
  37  * directories.
  38  *
  39  * @since 1.7
  40  */
  41 
  42 public final class Files {
  43     private Files() { }
  44 
  45     // lazy loading of default and installed file type detectors
  46     private static class DefaultFileTypeDetectorHolder {
  47         static final FileTypeDetector defaultFileTypeDetector =
  48             sun.nio.fs.DefaultFileTypeDetector.create();
  49         static final List<FileTypeDetector> installeDetectors =
  50             loadInstalledDetectors();
  51 
  52         // loads all installed file type detectors
  53         private static List<FileTypeDetector> loadInstalledDetectors() {
  54             return AccessController
  55                 .doPrivileged(new PrivilegedAction<List<FileTypeDetector>>() {
  56                     @Override public List<FileTypeDetector> run() {
  57                         List<FileTypeDetector> list = new ArrayList<FileTypeDetector>();
  58                         ServiceLoader<FileTypeDetector> loader = ServiceLoader
  59                             .load(FileTypeDetector.class, ClassLoader.getSystemClassLoader());
  60                         for (FileTypeDetector detector: loader) {
  61                             list.add(detector);
  62                         }
  63                         return list;
  64                 }});
  65         }
  66     }
  67 
  68     /**
  69      * Probes the content type of a file.
  70      *
  71      * <p> This method uses the installed {@link FileTypeDetector} implementations
  72      * to probe the given file to determine its content type. Each file type
  73      * detector's {@link FileTypeDetector#probeContentType probeContentType} is
  74      * invoked, in turn, to probe the file type. If the file is recognized then
  75      * the content type is returned. If the file is not recognized by any of the
  76      * installed file type detectors then a system-default file type detector is
  77      * invoked to guess the content type.
  78      *
  79      * <p> A given invocation of the Java virtual machine maintains a system-wide
  80      * list of file type detectors. Installed file type detectors are loaded
  81      * using the service-provider loading facility defined by the {@link ServiceLoader}
  82      * class. Installed file type detectors are loaded using the system class
  83      * loader. If the system class loader cannot be found then the extension class
  84      * loader is used; If the extension class loader cannot be found then the
  85      * bootstrap class loader is used. File type detectors are typically installed
  86      * by placing them in a JAR file on the application class path or in the
  87      * extension directory, the JAR file contains a provider-configuration file
  88      * named {@code java.nio.file.spi.FileTypeDetector} in the resource directory
  89      * {@code META-INF/services}, and the file lists one or more fully-qualified
  90      * names of concrete subclass of {@code FileTypeDetector } that have a zero
  91      * argument constructor. If the process of locating or instantiating the
  92      * installed file type detectors fails then an unspecified error is thrown.
  93      * The ordering that installed providers are located is implementation
  94      * specific.
  95      *
  96      * <p> The return value of this method is the string form of the value of a
  97      * Multipurpose Internet Mail Extension (MIME) content type as
  98      * defined by <a href="http://www.ietf.org/rfc/rfc2045.txt"><i>RFC&nbsp;2045:
  99      * Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet
 100      * Message Bodies</i></a>. The string is guaranteed to be parsable according
 101      * to the grammar in the RFC.
 102      *
 103      * @param   file
 104      *          The file reference
 105      *
 106      * @return  The content type of the file, or {@code null} if the content
 107      *          type cannot be determined
 108      *
 109      * @throws  IOException
 110      *          If an I/O error occurs
 111      * @throws  SecurityException
 112      *          If a security manager is installed and it denies an unspecified
 113      *          permission required by a file type detector implementation.
 114      */
 115     public static String probeContentType(FileRef file)
 116         throws IOException
 117     {
 118         // try installed file type detectors
 119         for (FileTypeDetector detector: DefaultFileTypeDetectorHolder.installeDetectors) {
 120             String result = detector.probeContentType(file);
 121             if (result != null)
 122                 return result;
 123         }
 124 
 125         // fallback to default
 126         return DefaultFileTypeDetectorHolder.defaultFileTypeDetector
 127             .probeContentType(file);
 128     }
 129 
 130     /**
 131      * Walks a file tree.
 132      *
 133      * <p> This method walks a file tree rooted at a given starting file. The
 134      * file tree traversal is <em>depth-first</em> with the given {@link
 135      * FileVisitor} invoked for each file encountered. File tree traversal
 136      * completes when all accessible files in the tree have been visited, or a
 137      * visit method returns a result of {@link FileVisitResult#TERMINATE
 138      * TERMINATE}. Where a visit method terminates due an {@code IOException},
 139      * an uncaught error, or runtime exception, then the traversal is terminated
 140      * and the error or exception is propagated to the caller of this method.
 141      *
 142      * <p> For each file encountered this method attempts to gets its {@link
 143      * java.nio.file.attribute.BasicFileAttributes}. If the file is not a
 144      * directory then the {@link FileVisitor#visitFile visitFile} method is
 145      * invoked with the file attributes. If the file attributes cannot be read,
 146      * due to an I/O exception, then the {@link FileVisitor#visitFileFailed
 147      * visitFileFailed} method is invoked with the I/O exception.
 148      *
 149      * <p> Where the file is a directory, and the directory could not be opened,
 150      * then the {@code visitFileFailed} method is invoked with the I/O exception,
 151      * after which, the file tree walk continues, by default, at the next
 152      * <em>sibling</em> of the directory.
 153      *
 154      * <p> Where the directory is opened successfully, then the entries in the
 155      * directory, and their <em>descendants</em> are visited. When all entries
 156      * have been visited, or an I/O error occurs during iteration of the
 157      * directory, then the directory is closed and the visitor's {@link
 158      * FileVisitor#postVisitDirectory postVisitDirectory} method is invoked.
 159      * The file tree walk then continues, by default, at the next <em>sibling</em>
 160      * of the directory.
 161      *
 162      * <p> By default, symbolic links are not automatically followed by this
 163      * method. If the {@code options} parameter contains the {@link
 164      * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then symbolic links are
 165      * followed. When following links, and the attributes of the target cannot
 166      * be read, then this method attempts to get the {@code BasicFileAttributes}
 167      * of the link. If they can be read then the {@code visitFile} method is
 168      * invoked with the attributes of the link (otherwise the {@code visitFileFailed}
 169      * method is invoked as specified above).
 170      *
 171      * <p> If the {@code options} parameter contains the {@link
 172      * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then this method keeps
 173      * track of directories visited so that cycles can be detected. A cycle
 174      * arises when there is an entry in a directory that is an ancestor of the
 175      * directory. Cycle detection is done by recording the {@link
 176      * java.nio.file.attribute.BasicFileAttributes#fileKey file-key} of directories,
 177      * or if file keys are not available, by invoking the {@link Path#isSameFile
 178      * isSameFile} method to test if a directory is the same file as an
 179      * ancestor. When a cycle is detected it is treated as an I/O error, and the
 180      * {@link FileVisitor#visitFileFailed visitFileFailed} method is invoked with
 181      * an instance of {@link FileSystemLoopException}.
 182      *
 183      * <p> The {@code maxDepth} parameter is the maximum number of levels of
 184      * directories to visit. A value of {@code 0} means that only the starting
 185      * file is visited, unless denied by the security manager. A value of
 186      * {@link Integer#MAX_VALUE MAX_VALUE} may be used to indicate that all
 187      * levels should be visited. The {@code visitFile} method is invoked for all
 188      * files, including directories, encountered at {@code maxDepth}, unless the
 189      * basic file attributes cannot be read, in which case the {@code
 190      * visitFileFailed} method is invoked.
 191      *
 192      * <p> If a visitor returns a result of {@code null} then {@code
 193      * NullPointerException} is thrown.
 194      *
 195      * <p> When a security manager is installed and it denies access to a file
 196      * (or directory), then it is ignored and the visitor is not invoked for
 197      * that file (or directory).
 198      *
 199      * @param   start
 200      *          The starting file
 201      * @param   options
 202      *          Options to configure the traversal
 203      * @param   maxDepth
 204      *          The maximum number of directory levels to visit
 205      * @param   visitor
 206      *          The file visitor to invoke for each file
 207      *
 208      * @throws  IllegalArgumentException
 209      *          If the {@code maxDepth} parameter is negative
 210      * @throws  SecurityException
 211      *          If the security manager denies access to the starting file.
 212      *          In the case of the default provider, the {@link
 213      *          SecurityManager#checkRead(String) checkRead} method is invoked
 214      *          to check read access to the directory.
 215      * @throws  IOException
 216      *          If an I/O error is thrown by a visitor method
 217      */
 218     public static void walkFileTree(Path start,
 219                                     Set<FileVisitOption> options,
 220                                     int maxDepth,
 221                                     FileVisitor<? super Path> visitor)
 222         throws IOException
 223     {
 224         if (maxDepth < 0)
 225             throw new IllegalArgumentException("'maxDepth' is negative");
 226         new FileTreeWalker(options, visitor, maxDepth).walk(start);
 227     }
 228 
 229     /**
 230      * Walks a file tree.
 231      *
 232      * <p> This method works as if invoking it were equivalent to evaluating the
 233      * expression:
 234      * <blockquote><pre>
 235      * walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), Integer.MAX_VALUE, visitor)
 236      * </pre></blockquote>
 237      *
 238      * @param   start
 239      *          The starting file
 240      * @param   visitor
 241      *          The file visitor to invoke for each file
 242      *
 243      * @throws  SecurityException
 244      *          If the security manager denies access to the starting file.
 245      *          In the case of the default provider, the {@link
 246      *          SecurityManager#checkRead(String) checkRead} method is invoked
 247      *          to check read access to the directory.
 248      * @throws  IOException
 249      *          If an I/O error is thrown by a visitor method
 250      */
 251     public static void walkFileTree(Path start, FileVisitor<? super Path> visitor)
 252         throws IOException
 253     {
 254         walkFileTree(start,
 255                      EnumSet.noneOf(FileVisitOption.class),
 256                      Integer.MAX_VALUE,
 257                      visitor);
 258     }
 259 
 260     /**
 261      * Creates a directory by creating all nonexistent parent directories first.
 262      *
 263      * <p> The {@code attrs} parameter is an optional array of {@link FileAttribute
 264      * file-attributes} to set atomically when creating the nonexistent
 265      * directories. Each file attribute is identified by its {@link
 266      * FileAttribute#name name}. If more than one attribute of the same name is
 267      * included in the array then all but the last occurrence is ignored.
 268      *
 269      * <p> If this method fails, then it may do so after creating some, but not
 270      * all, of the parent directories.
 271      *
 272      * @param   dir
 273      *          the directory to create
 274      *
 275      * @param   attrs
 276      *          an optional list of file attributes to set atomically when
 277      *          creating the directory
 278      *
 279      * @throws  UnsupportedOperationException
 280      *          if the array contains an attribute that cannot be set atomically
 281      *          when creating the directory
 282      * @throws  FileAlreadyExistsException
 283      *          if {@code dir} exists but is not a directory <i>(optional specific
 284      *          exception)</i>
 285      * @throws  IOException
 286      *          if an I/O error occurs
 287      * @throws  SecurityException
 288      *          in the case of the default provider, and a security manager is
 289      *          installed, the {@link SecurityManager#checkWrite(String) checkWrite}
 290      *          method is invoked prior to attempting to create a directory and
 291      *          its {@link SecurityManager#checkRead(String) checkRead} is
 292      *          invoked for each parent directory that is checked. If {@code
 293      *          dir} is not an absolute path then its {@link Path#toAbsolutePath
 294      *          toAbsolutePath} may need to be invoked to get its absolute path.
 295      *          This may invoke the security manager's {@link
 296      *          SecurityManager#checkPropertyAccess(String) checkPropertyAccess}
 297      *          method to check access to the system property {@code user.dir}
 298      *
 299      */
 300     public static void createDirectories(Path dir, FileAttribute<?>... attrs)
 301         throws IOException
 302     {
 303         // attempt to create the directory
 304         try {
 305             createAndCheckIsDirectory(dir, attrs);
 306             return;
 307         } catch (FileAlreadyExistsException x) {
 308             // file exists and is not a directory
 309             throw x;
 310         } catch (IOException x) {
 311             // parent may not exist or other reason
 312         }
 313 
 314         // find existing parent (may require absolute path)
 315         SecurityException se = null;
 316         try {
 317             dir = dir.toAbsolutePath();
 318         } catch (SecurityException x) {
 319             // don't have permission to get absolute path
 320             se = x;
 321         }
 322         Path parent = dir.getParent();
 323         while (parent != null) {
 324             try {
 325                 parent.checkAccess();
 326                 break;
 327             } catch (NoSuchFileException x) {
 328                 // does not exist
 329             }
 330             parent = parent.getParent();
 331         }
 332         if (parent == null) {
 333             // unable to find existing parent
 334             if (se != null)
 335                 throw se;
 336             throw new IOException("Root directory does not exist");
 337         }
 338 
 339         // create directories
 340         Path child = parent;
 341         for (Path name: parent.relativize(dir)) {
 342             child = child.resolve(name);
 343             createAndCheckIsDirectory(child, attrs);
 344         }
 345     }
 346 
 347     /**
 348      * Attempts to create a directory. Does nothing if the directory already
 349      * exists.
 350      */
 351     private static void createAndCheckIsDirectory(Path dir, FileAttribute<?>... attrs)
 352         throws IOException
 353     {
 354         try {
 355             dir.createDirectory(attrs);
 356         } catch (FileAlreadyExistsException x) {
 357             boolean isDirectory = Attributes
 358                 .readBasicFileAttributes(dir, LinkOption.NOFOLLOW_LINKS).isDirectory();
 359             if (!isDirectory)
 360                 throw x;
 361         }
 362     }
 363 }
--- EOF ---