< prev index next >

src/java.base/share/classes/java/nio/file/Files.java

Print this page

        

@@ -72,10 +72,11 @@
 import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
 import java.util.function.BiPredicate;
+import java.util.function.Consumer;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
 /**
  * This class consists exclusively of static methods that operate on files,

@@ -3568,11 +3569,126 @@
      * (or directory), then it is ignored and not included in the stream.
      *
      * <p> The returned stream contains references to one or more open directories.
      * The directories are closed by closing the stream.
      *
-     * <p> If an {@link IOException} is thrown when accessing the directory
+     * <p> If an {@link IOException} is thrown when accessing a file or directory,
+     * it is passed to given {@code ioExceptionHandler}.
+     * If this handler throws an unchecked exception it will be thrown from the
+     * method that caused the access to take place. If the handler doesn't throw
+     * any exception then the elements of the stream that caused the IOException(s)
+     * by accessing them are absent from the stream. This may include entire subtrees
+     * rooted at directories that caused IOException(s) by accessing them.
+     * The 1st access to starting file takes place in this method.
+     *
+     * @apiNote
+     * This method must be used within a try-with-resources statement or similar
+     * control structure to ensure that the stream's open directories are closed
+     * promptly after the stream's operations have completed.
+     *
+     * @param   start
+     *          the starting file
+     * @param   maxDepth
+     *          the maximum number of directory levels to visit
+     * @param   ioExceptionHandler
+     *          the consumer used to handle any IOException(s) that are thrown
+     *          while walking the file tree
+     * @param   options
+     *          options to configure the traversal
+     *
+     * @return  the {@link Stream} of {@link Path}
+     *
+     * @throws  IllegalArgumentException
+     *          if the {@code maxDepth} parameter is negative
+     * @throws  NullPointerException
+     *          if {@code start} or {@code ioExceptionHandler}
+     *          or {@code options} is {@code null} or the options array contains
+     *          a {@code null} element
+     * @throws  SecurityException
+     *          If the security manager denies access to the starting file.
+     *          In the case of the default provider, the {@link
+     *          SecurityManager#checkRead(String) checkRead} method is invoked
+     *          to check read access to the directory.
+     * @since   1.9
+     * @see #walk(Path, int, FileVisitOption...)
+     */
+    public static Stream<Path> walk(Path start,
+                                    int maxDepth,
+                                    Consumer<IOException> ioExceptionHandler,
+                                    FileVisitOption... options)
+    {
+        FileTreeIterator iterator = new FileTreeIterator(start, maxDepth,
+                                                         ioExceptionHandler,
+                                                         options);
+        try {
+            Spliterator<FileTreeWalker.Event> spliterator =
+                Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT);
+            return StreamSupport.stream(spliterator, false)
+                                .onClose(iterator::close)
+                                .map(entry -> entry.file());
+        } catch (Throwable t) {
+            try {
+                iterator.close();
+            } catch (Throwable ct) {
+                t.addSuppressed(ct);
+            }
+            throw t;
+        }
+    }
+
+    /**
+     * Return a {@code Stream} that is lazily populated with {@code
+     * Path} by walking the file tree rooted at a given starting file.  The
+     * file tree is traversed <em>depth-first</em>, the elements in the stream
+     * are {@link Path} objects that are obtained as if by {@link
+     * Path#resolve(Path) resolving} the relative path against {@code start}.
+     *
+     * <p> The {@code stream} walks the file tree as elements are consumed.
+     * The {@code Stream} returned is guaranteed to have at least one
+     * element, the starting file itself. For each file visited, the stream
+     * attempts to read its {@link BasicFileAttributes}. If the file is a
+     * directory and can be opened successfully, entries in the directory, and
+     * their <em>descendants</em> will follow the directory in the stream as
+     * they are encountered. When all entries have been visited, then the
+     * directory is closed. The file tree walk then continues at the next
+     * <em>sibling</em> of the directory.
+     *
+     * <p> The stream is <i>weakly consistent</i>. It does not freeze the
+     * file tree while iterating, so it may (or may not) reflect updates to
+     * the file tree that occur after returned from this method.
+     *
+     * <p> By default, symbolic links are not automatically followed by this
+     * method. If the {@code options} parameter contains the {@link
+     * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then symbolic links are
+     * followed. When following links, and the attributes of the target cannot
+     * be read, then this method attempts to get the {@code BasicFileAttributes}
+     * of the link.
+     *
+     * <p> If the {@code options} parameter contains the {@link
+     * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then the stream keeps
+     * track of directories visited so that cycles can be detected. A cycle
+     * arises when there is an entry in a directory that is an ancestor of the
+     * directory. Cycle detection is done by recording the {@link
+     * java.nio.file.attribute.BasicFileAttributes#fileKey file-key} of directories,
+     * or if file keys are not available, by invoking the {@link #isSameFile
+     * isSameFile} method to test if a directory is the same file as an
+     * ancestor. When a cycle is detected it is treated as an I/O error with
+     * an instance of {@link FileSystemLoopException}.
+     *
+     * <p> The {@code maxDepth} parameter is the maximum number of levels of
+     * directories to visit. A value of {@code 0} means that only the starting
+     * file is visited, unless denied by the security manager. A value of
+     * {@link Integer#MAX_VALUE MAX_VALUE} may be used to indicate that all
+     * levels should be visited.
+     *
+     * <p> When a security manager is installed and it denies access to a file
+     * (or directory), then it is ignored and not included in the stream.
+     *
+     * <p> The returned stream contains references to one or more open directories.
+     * The directories are closed by closing the stream.
+     *
+     * <p> If an {@link IOException} is thrown when accessing a file or directory
      * after this method has returned, it is wrapped in an {@link
      * UncheckedIOException} which will be thrown from the method that caused
      * the access to take place.
      *
      * @apiNote

@@ -3589,10 +3705,14 @@
      *
      * @return  the {@link Stream} of {@link Path}
      *
      * @throws  IllegalArgumentException
      *          if the {@code maxDepth} parameter is negative
+     * @throws  NullPointerException
+     *          if {@code start} or {@code options}
+     *          is {@code null} or the options array contains
+     *          a {@code null} element
      * @throws  SecurityException
      *          If the security manager denies access to the starting file.
      *          In the case of the default provider, the {@link
      *          SecurityManager#checkRead(String) checkRead} method is invoked
      *          to check read access to the directory.

@@ -3603,20 +3723,16 @@
     public static Stream<Path> walk(Path start,
                                     int maxDepth,
                                     FileVisitOption... options)
         throws IOException
     {
-        FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options);
         try {
-            Spliterator<FileTreeWalker.Event> spliterator =
-                Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT);
-            return StreamSupport.stream(spliterator, false)
-                                .onClose(iterator::close)
-                                .map(entry -> entry.file());
-        } catch (Error|RuntimeException e) {
-            iterator.close();
-            throw e;
+            return walk(start, maxDepth,
+                        ioe -> { throw new UncheckedIOException(ioe); },
+                        options);
+        } catch (UncheckedIOException uioe) {
+            throw uioe.getCause();
         }
     }
 
     /**
      * Return a {@code Stream} that is lazily populated with {@code

@@ -3645,10 +3761,14 @@
      * @param   options
      *          options to configure the traversal
      *
      * @return  the {@link Stream} of {@link Path}
      *
+     * @throws  NullPointerException
+     *          if {@code start} or {@code options}
+     *          is {@code null} or the options array contains
+     *          a {@code null} element
      * @throws  SecurityException
      *          If the security manager denies access to the starting file.
      *          In the case of the default provider, the {@link
      *          SecurityManager#checkRead(String) checkRead} method is invoked
      *          to check read access to the directory.

@@ -3679,11 +3799,100 @@
      * avoiding redundant retrieval of the {@code BasicFileAttributes}.
      *
      * <p> The returned stream contains references to one or more open directories.
      * The directories are closed by closing the stream.
      *
-     * <p> If an {@link IOException} is thrown when accessing the directory
+     * <p> If an {@link IOException} is thrown when accessing a file or directory,
+     * it is passed to given {@code ioExceptionHandler}.
+     * If this handler throws an unchecked exception it will be thrown from the
+     * method that caused the access to take place. If the handler doesn't throw
+     * any exception then the elements of the stream that caused the IOException(s)
+     * by accessing them are absent from the stream. This may include entire subtrees
+     * rooted at directories that caused IOException(s) by accessing them.
+     * The 1st access to starting file takes place in this method.
+     *
+     * @apiNote
+     * This method must be used within a try-with-resources statement or similar
+     * control structure to ensure that the stream's open directories are closed
+     * promptly after the stream's operations have completed.
+     *
+     * @param   start
+     *          the starting file
+     * @param   maxDepth
+     *          the maximum number of directory levels to search
+     * @param   matcher
+     *          the function used to decide whether a file should be included
+     *          in the returned stream
+     * @param   ioExceptionHandler
+     *          the consumer used to handle any IOException(s) that are thrown
+     *          while walking the file tree
+     * @param   options
+     *          options to configure the traversal
+     *
+     * @return  the {@link Stream} of {@link Path}
+     *
+     * @throws  IllegalArgumentException
+     *          if the {@code maxDepth} parameter is negative
+     * @throws  NullPointerException
+     *          if {@code start} or {@code matcher} or {@code ioExceptionHandler}
+     *          or {@code options} is {@code null} or the options array contains
+     *          a {@code null} element
+     * @throws  SecurityException
+     *          If the security manager denies access to the starting file.
+     *          In the case of the default provider, the {@link
+     *          SecurityManager#checkRead(String) checkRead} method is invoked
+     *          to check read access to the directory.
+     *
+     * @see     #find(Path, int, BiPredicate, FileVisitOption...)
+     * @see     #walk(Path, int, FileVisitOption...)
+     * @since   1.9
+     */
+    public static Stream<Path> find(Path start,
+                                    int maxDepth,
+                                    BiPredicate<Path, BasicFileAttributes> matcher,
+                                    Consumer<IOException> ioExceptionHandler,
+                                    FileVisitOption... options)
+    {
+        Objects.requireNonNull(ioExceptionHandler);
+        FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, ioExceptionHandler, options);
+        try {
+            Spliterator<FileTreeWalker.Event> spliterator =
+                Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT);
+            return StreamSupport.stream(spliterator, false)
+                                .onClose(iterator::close)
+                                .filter(entry -> matcher.test(entry.file(), entry.attributes()))
+                                .map(FileTreeWalker.Event::file);
+        } catch (Throwable t) {
+            try {
+                iterator.close();
+            } catch (Throwable ct) {
+                t.addSuppressed(ct);
+            }
+            throw t;
+        }
+    }
+
+    /**
+     * Return a {@code Stream} that is lazily populated with {@code
+     * Path} by searching for files in a file tree rooted at a given starting
+     * file.
+     *
+     * <p> This method walks the file tree in exactly the manner specified by
+     * the {@link #walk walk} method. For each file encountered, the given
+     * {@link BiPredicate} is invoked with its {@link Path} and {@link
+     * BasicFileAttributes}. The {@code Path} object is obtained as if by
+     * {@link Path#resolve(Path) resolving} the relative path against {@code
+     * start} and is only included in the returned {@link Stream} if
+     * the {@code BiPredicate} returns true. Compare to calling {@link
+     * java.util.stream.Stream#filter filter} on the {@code Stream}
+     * returned by {@code walk} method, this method may be more efficient by
+     * avoiding redundant retrieval of the {@code BasicFileAttributes}.
+     *
+     * <p> The returned stream contains references to one or more open directories.
+     * The directories are closed by closing the stream.
+     *
+     * <p> If an {@link IOException} is thrown when accessing a file or directory
      * after returned from this method, it is wrapped in an {@link
      * UncheckedIOException} which will be thrown from the method that caused
      * the access to take place.
      *
      * @apiNote

@@ -3703,38 +3912,38 @@
      *
      * @return  the {@link Stream} of {@link Path}
      *
      * @throws  IllegalArgumentException
      *          if the {@code maxDepth} parameter is negative
+     * @throws  NullPointerException
+     *          if {@code start} or {@code matcher} or {@code options}
+     *          is {@code null} or the options array contains
+     *          a {@code null} element
      * @throws  SecurityException
      *          If the security manager denies access to the starting file.
      *          In the case of the default provider, the {@link
      *          SecurityManager#checkRead(String) checkRead} method is invoked
      *          to check read access to the directory.
      * @throws  IOException
      *          if an I/O error is thrown when accessing the starting file.
      *
+     * @see     #find(Path, int, BiPredicate, Consumer, FileVisitOption...)
      * @see     #walk(Path, int, FileVisitOption...)
      * @since   1.8
      */
     public static Stream<Path> find(Path start,
                                     int maxDepth,
                                     BiPredicate<Path, BasicFileAttributes> matcher,
                                     FileVisitOption... options)
         throws IOException
     {
-        FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options);
         try {
-            Spliterator<FileTreeWalker.Event> spliterator =
-                Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT);
-            return StreamSupport.stream(spliterator, false)
-                                .onClose(iterator::close)
-                                .filter(entry -> matcher.test(entry.file(), entry.attributes()))
-                                .map(entry -> entry.file());
-        } catch (Error|RuntimeException e) {
-            iterator.close();
-            throw e;
+            return find(start, maxDepth, matcher,
+                        ioe -> { throw new UncheckedIOException(ioe); },
+                        options);
+        } catch (UncheckedIOException uioe) {
+            throw uioe.getCause();
         }
     }
 
 
     /**
< prev index next >