--- old/src/java.base/share/classes/java/nio/file/Files.java 2015-07-02 15:16:17.824614283 +0200 +++ new/src/java.base/share/classes/java/nio/file/Files.java 2015-07-02 15:16:17.753613823 +0200 @@ -74,6 +74,7 @@ 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; @@ -3570,7 +3571,122 @@ *

The returned stream contains references to one or more open directories. * The directories are closed by closing the stream. * - *

If an {@link IOException} is thrown when accessing the directory + *

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 walk(Path start, + int maxDepth, + Consumer ioExceptionHandler, + FileVisitOption... options) + { + FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, + ioExceptionHandler, + options); + try { + Spliterator 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 depth-first, 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}. + * + *

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 descendants 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 + * sibling of the directory. + * + *

The stream is weakly consistent. 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. + * + *

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. + * + *

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}. + * + *

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. + * + *

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. + * + *

The returned stream contains references to one or more open directories. + * The directories are closed by closing the stream. + * + *

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. @@ -3591,6 +3707,10 @@ * * @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 @@ -3605,16 +3725,12 @@ FileVisitOption... options) throws IOException { - FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options); try { - Spliterator 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(); } } @@ -3647,6 +3763,10 @@ * * @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 @@ -3681,7 +3801,96 @@ *

The returned stream contains references to one or more open directories. * The directories are closed by closing the stream. * - *

If an {@link IOException} is thrown when accessing the directory + *

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 find(Path start, + int maxDepth, + BiPredicate matcher, + Consumer ioExceptionHandler, + FileVisitOption... options) + { + Objects.requireNonNull(ioExceptionHandler); + FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, ioExceptionHandler, options); + try { + Spliterator 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. + * + *

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}. + * + *

The returned stream contains references to one or more open directories. + * The directories are closed by closing the stream. + * + *

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. @@ -3705,6 +3914,10 @@ * * @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 @@ -3713,6 +3926,7 @@ * @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 */ @@ -3722,17 +3936,12 @@ FileVisitOption... options) throws IOException { - FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options); try { - Spliterator 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(); } }