changeset 7202:b9b26b424bfc

Merge
author mduigou
date Sat, 18 May 2013 18:55:56 -0700
parents 0f7aaabed25f 23e75751554a
children 08ebdb2b53cc
files
diffstat 43 files changed, 2120 insertions(+), 232 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/nio/file/FileTreeIterator.java	Sat May 18 18:55:56 2013 -0700
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.nio.file;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.nio.file.FileTreeWalker.Event;
+
+/**
+ * An {@code Iterator to iterate over the nodes of a file tree.
+ *
+ * <pre>{@code
+ *     try (FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options)) {
+ *         while (iterator.hasNext()) {
+ *             Event ev = iterator.next();
+ *             Path path = ev.file();
+ *             BasicFileAttributes attrs = ev.attributes();
+ *         }
+ *     }
+ * }</pre>
+ */
+
+class FileTreeIterator implements Iterator<Event>, Closeable {
+    private final FileTreeWalker walker;
+    private Event next;
+
+    /**
+     * Creates a new iterator to walk the file tree starting at the given file.
+     *
+     * @throws  IllegalArgumentException
+     *          if {@code maxDepth} is negative
+     * @throws  IOException
+     *          if an I/O errors occurs opening the starting file
+     * @throws  SecurityException
+     *          if the security manager denies access to the starting file
+     * @throws  NullPointerException
+     *          if {@code start} or {@code options} is {@ocde null} or
+     *          the options array contains a {@code null} element
+     */
+    FileTreeIterator(Path start, int maxDepth, FileVisitOption... options)
+        throws IOException
+    {
+        this.walker = new FileTreeWalker(Arrays.asList(options), maxDepth);
+        this.next = walker.walk(start);
+        assert next.type() == FileTreeWalker.EventType.ENTRY ||
+               next.type() == FileTreeWalker.EventType.START_DIRECTORY;
+
+        // IOException if there a problem accessing the starting file
+        IOException ioe = next.ioeException();
+        if (ioe != null)
+            throw ioe;
+    }
+
+    private void fetchNextIfNeeded() {
+        if (next == null) {
+            FileTreeWalker.Event ev = walker.next();
+            while (ev != null) {
+                IOException ioe = ev.ioeException();
+                if (ioe != null)
+                    throw new UncheckedIOException(ioe);
+
+                // END_DIRECTORY events are ignored
+                if (ev.type() != FileTreeWalker.EventType.END_DIRECTORY) {
+                    next = ev;
+                    return;
+                }
+                ev = walker.next();
+            }
+        }
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (!walker.isOpen())
+            throw new IllegalStateException();
+        fetchNextIfNeeded();
+        return next != null;
+    }
+
+    @Override
+    public Event next() {
+        if (!walker.isOpen())
+            throw new IllegalStateException();
+        fetchNextIfNeeded();
+        if (next == null)
+            throw new NoSuchElementException();
+        Event result = next;
+        next = null;
+        return result;
+    }
+
+    @Override
+    public void close() {
+        walker.close();
+    }
+}
--- a/src/share/classes/java/nio/file/FileTreeWalker.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/nio/file/FileTreeWalker.java	Sat May 18 18:55:56 2013 -0700
@@ -29,8 +29,8 @@
 import java.io.Closeable;
 import java.io.IOException;
 import java.util.ArrayDeque;
+import java.util.Collection;
 import java.util.Iterator;
-import java.util.Set;
 import sun.nio.fs.BasicFileAttributesHolder;
 
 /**
@@ -164,8 +164,17 @@
 
     /**
      * Creates a {@code FileTreeWalker}.
+     *
+     * @throws  IllegalArgumentException
+     *          if {@code maxDepth} is negative
+     * @throws  ClassCastException
+     *          if (@code options} contains an element that is not a
+     *          {@code FileVisitOption}
+     * @throws  NullPointerException
+     *          if {@code options} is {@ocde null} or the options
+     *          array contains a {@code null} element
      */
-    FileTreeWalker(Set<FileVisitOption> options, int maxDepth) {
+    FileTreeWalker(Collection<FileVisitOption> options, int maxDepth) {
         boolean fl = false;
         for (FileVisitOption option: options) {
             // will throw NPE if options contains null
@@ -175,6 +184,9 @@
                     throw new AssertionError("Should not get here");
             }
         }
+        if (maxDepth < 0)
+            throw new IllegalArgumentException("'maxDepth' is negative");
+
         this.followLinks = fl;
         this.linkOptions = (fl) ? new LinkOption[0] :
             new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
--- a/src/share/classes/java/nio/file/Files.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/nio/file/Files.java	Sat May 18 18:55:56 2013 -0700
@@ -29,6 +29,7 @@
 import java.nio.file.spi.FileSystemProvider;
 import java.nio.file.spi.FileTypeDetector;
 import java.nio.channels.SeekableByteChannel;
+import java.io.Closeable;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.Reader;
@@ -38,7 +39,13 @@
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.util.*;
+import java.util.function.BiPredicate;
+import java.util.stream.CloseableStream;
+import java.util.stream.DelegatingStream;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.nio.charset.Charset;
@@ -2587,9 +2594,6 @@
                                     FileVisitor<? super Path> visitor)
         throws IOException
     {
-        if (maxDepth < 0)
-            throw new IllegalArgumentException("'maxDepth' is negative");
-
         /**
          * Create a FileTreeWalker to walk the file tree, invoking the visitor
          * for each event.
@@ -3177,4 +3181,336 @@
         }
         return path;
     }
+
+    // -- Stream APIs --
+
+    /**
+     * Implementation of CloseableStream
+     */
+    private static class DelegatingCloseableStream<T> extends DelegatingStream<T>
+        implements CloseableStream<T>
+    {
+        private final Closeable closeable;
+
+        DelegatingCloseableStream(Closeable c, Stream<T> delegate) {
+            super(delegate);
+            this.closeable = c;
+        }
+
+        public void close() {
+            try {
+                closeable.close();
+            } catch (IOException ex) {
+                throw new UncheckedIOException(ex);
+            }
+        }
+    }
+
+    /**
+     * Return a lazily populated {@code CloseableStream}, the elements of
+     * which are the entries in the directory.  The listing is not recursive.
+     *
+     * <p> The elements of the stream are {@link Path} objects that are
+     * obtained as if by {@link Path#resolve(Path) resolving} the name of the
+     * directory entry against {@code dir}. Some file systems maintain special
+     * links to the directory itself and the directory's parent directory.
+     * Entries representing these links are not included.
+     *
+     * <p> The stream is <i>weakly consistent</i>. It is thread safe but does
+     * not freeze the directory while iterating, so it may (or may not)
+     * reflect updates to the directory that occur after returning from this
+     * method.
+     *
+     * <p> When not using the try-with-resources construct, then the stream's
+     * {@link CloseableStream#close close} method should be invoked after the
+     * operation is completed so as to free any resources held for the open
+     * directory. Operating on a closed stream behaves as if the end of stream
+     * has been reached. Due to read-ahead, one or more elements may be
+     * returned after the stream has been closed.
+     *
+     * <p> If an {@link IOException} is thrown when accessing the 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.
+     *
+     * @param   dir  The path to the directory
+     *
+     * @return  The {@code CloseableStream} describing the content of the
+     *          directory
+     *
+     * @throws  NotDirectoryException
+     *          if the file could not otherwise be opened because it is not
+     *          a directory <i>(optional specific exception)</i>
+     * @throws  IOException
+     *          if an I/O error occurs when opening the directory
+     * @throws  SecurityException
+     *          In the case of the default provider, and a security manager is
+     *          installed, the {@link SecurityManager#checkRead(String) checkRead}
+     *          method is invoked to check read access to the directory.
+     *
+     * @see     #newDirectoryStream(Path)
+     * @since   1.8
+     */
+    public static CloseableStream<Path> list(Path dir) throws IOException {
+        DirectoryStream<Path> ds = Files.newDirectoryStream(dir);
+        final Iterator<Path> delegate = ds.iterator();
+
+        // Re-wrap DirectoryIteratorException to UncheckedIOException
+        Iterator<Path> it = new Iterator<Path>() {
+            public boolean hasNext() {
+                try {
+                    return delegate.hasNext();
+                } catch (DirectoryIteratorException e) {
+                    throw new UncheckedIOException(e.getCause());
+                }
+            }
+            public Path next() {
+                try {
+                    return delegate.next();
+                } catch (DirectoryIteratorException e) {
+                    throw new UncheckedIOException(e.getCause());
+                }
+            }
+        };
+
+        return new DelegatingCloseableStream<>(ds,
+            StreamSupport.stream(Spliterators.spliteratorUnknownSize(it,
+                                                                     Spliterator.DISTINCT)));
+    }
+
+    /**
+     * Return a {@code CloseableStream} 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 CloseableStream} 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> When not using the try-with-resources construct, then the stream's
+     * {@link CloseableStream#close close} method should be invoked after the
+     * operation is completed so as to free any resources held for the open
+     * directory. Operate the stream after it is closed will throw an
+     * {@link java.lang.IllegalStateException}.
+     *
+     * <p> If an {@link IOException} is thrown when accessing the 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.
+     *
+     * @param   start
+     *          the starting file
+     * @param   maxDepth
+     *          the maximum number of directory levels to visit
+     * @param   options
+     *          options to configure the traversal
+     *
+     * @return  the {@link CloseableStream} of {@link Path}
+     *
+     * @throws  IllegalArgumentException
+     *          if the {@code maxDepth} parameter is negative
+     * @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.
+     * @since   1.8
+     */
+    public static CloseableStream<Path> walk(Path start, int maxDepth,
+                                             FileVisitOption... options)
+        throws IOException
+    {
+        FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options);
+        return new DelegatingCloseableStream<>(iterator,
+            StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT))
+                   .map(entry -> entry.file()));
+    }
+
+    /**
+     * Return a {@code CloseableStream} 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> This method works as if invoking it were equivalent to evaluating the
+     * expression:
+     * <blockquote><pre>
+     * walk(start, Integer.MAX_VALUE, options)
+     * </pre></blockquote>
+     * In other words, it visits all levels of the file tree.
+     *
+     * @param   start
+     *          the starting file
+     * @param   options
+     *          options to configure the traversal
+     *
+     * @return  the {@link CloseableStream} of {@link Path}
+     *
+     * @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     #walk(Path, int, FileVisitOption...)
+     * @since   1.8
+     */
+    public static CloseableStream<Path> walk(Path start,
+                                             FileVisitOption... options)
+        throws IOException
+    {
+        return walk(start, Integer.MAX_VALUE, options);
+    }
+
+    /**
+     * Return a {@code CloseableStream} 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 CloseableStream} 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> If an {@link IOException} is thrown when accessing the 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.
+     *
+     * @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   options
+     *          options to configure the traversal
+     *
+     * @return  the {@link CloseableStream} of {@link Path}
+     *
+     * @throws  IllegalArgumentException
+     *          if the {@code maxDepth} parameter is negative
+     * @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     #walk(Path, int, FileVisitOption...)
+     * @since   1.8
+     */
+    public static CloseableStream<Path> find(Path start,
+                                             int maxDepth,
+                                             BiPredicate<Path, BasicFileAttributes> matcher,
+                                             FileVisitOption... options)
+        throws IOException
+    {
+        FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options);
+        return new DelegatingCloseableStream<>(iterator,
+            StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT))
+                   .filter(entry -> matcher.test(entry.file(), entry.attributes()))
+                   .map(entry -> entry.file()));
+    }
+
+    /**
+     * Read all lines from a file as a {@code CloseableStream}.  Unlike {@link
+     * #readAllLines(Path, Charset) readAllLines}, this method does not read
+     * all lines into a {@code List}, but instead populates lazily as the stream
+     * is consumed.
+     *
+     * <p> Bytes from the file are decoded into characters using the specified
+     * charset and the same line terminators as specified by {@code
+     * readAllLines} are supported.
+     *
+     * <p> After this method returns, then any subsequent I/O exception that
+     * occurs while reading from the file or when a malformed or unmappable byte
+     * sequence is read, is wrapped in an {@link UncheckedIOException} that will
+     * be thrown form the
+     * {@link java.util.stream.Stream} method that caused the read to take
+     * place. In case an {@code IOException} is thrown when closing the file,
+     * it is also wrapped as an {@code UncheckedIOException}.
+     *
+     * <p> When not using the try-with-resources construct, then stream's
+     * {@link CloseableStream#close close} method should be invoked after
+     * operation is completed so as to free any resources held for the open
+     * file.
+     *
+     * @param   path
+     *          the path to the file
+     * @param   cs
+     *          the charset to use for decoding
+     *
+     * @return  the lines from the file as a {@code CloseableStream}
+     *
+     * @throws  IOException
+     *          if an I/O error occurs opening the file
+     * @throws  SecurityException
+     *          In the case of the default provider, and a security manager is
+     *          installed, the {@link SecurityManager#checkRead(String) checkRead}
+     *          method is invoked to check read access to the file.
+     *
+     * @see     #readAllLines(Path, Charset)
+     * @see     #newBufferedReader(Path, Charset)
+     * @see     java.io.BufferedReader#lines()
+     * @since   1.8
+     */
+    public static CloseableStream<String> lines(Path path, Charset cs)
+        throws IOException
+    {
+        BufferedReader br = Files.newBufferedReader(path, cs);
+        return new DelegatingCloseableStream<>(br, br.lines());
+    }
 }
--- a/src/share/classes/java/util/Spliterator.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/Spliterator.java	Sat May 18 18:55:56 2013 -0700
@@ -140,32 +140,32 @@
  * (in approximate order of decreasing desirability):
  * <ul>
  * <li>The source cannot be structurally interfered with.
- * </br>For example, an instance of
+ * <br>For example, an instance of
  * {@link java.util.concurrent.CopyOnWriteArrayList} is an immutable source.
  * A Spliterator created from the source reports a characteristic of
  * {@code IMMUTABLE}.</li>
  * <li>The source manages concurrent modifications.
- * </br>For example, a key set of a {@link java.util.concurrent.ConcurrentHashMap}
+ * <br>For example, a key set of a {@link java.util.concurrent.ConcurrentHashMap}
  * is a concurrent source.  A Spliterator created from the source reports a
  * characteristic of {@code CONCURRENT}.</li>
  * <li>The mutable source provides a late-binding and fail-fast Spliterator.
- * </br>Late binding narrows the window during which interference can affect
+ * <br>Late binding narrows the window during which interference can affect
  * the calculation; fail-fast detects, on a best-effort basis, that structural
  * interference has occurred after traversal has commenced and throws
  * {@link ConcurrentModificationException}.  For example, {@link ArrayList},
  * and many other non-concurrent {@code Collection} classes in the JDK, provide
  * a late-binding, fail-fast spliterator.</li>
  * <li>The mutable source provides a non-late-binding but fail-fast Spliterator.
- * </br>The source increases the likelihood of throwing
+ * <br>The source increases the likelihood of throwing
  * {@code ConcurrentModificationException} since the window of potential
  * interference is larger.</li>
  * <li>The mutable source provides a late-binding and non-fail-fast Spliterator.
- * </br>The source risks arbitrary, non-deterministic behavior after traversal
+ * <br>The source risks arbitrary, non-deterministic behavior after traversal
  * has commenced since interference is not detected.
  * </li>
  * <li>The mutable source provides a non-late-binding and non-fail-fast
  * Spliterator.
- * </br>The source increases the risk of arbitrary, non-deterministic behavior
+ * <br>The source increases the risk of arbitrary, non-deterministic behavior
  * since non-detected interference may occur after construction.
  * </li>
  * </ul>
@@ -284,6 +284,8 @@
  * is set to {@code true} then diagnostic warnings are reported if boxing of
  * primitive values occur when operating on primitive subtype specializations.
  *
+ * @param <T> the type of elements returned by this Spliterator
+ *
  * @see Collection
  * @since 1.8
  */
@@ -333,9 +335,8 @@
      * Upon non-null return:
      * <ul>
      * <li>the value reported for {@code estimateSize()} before splitting,
-     * if not already zero or {@code Long.MAX_VALUE}, must, after splitting, be
-     * greater than {@code estimateSize()} for this and the returned
-     * Spliterator; and</li>
+     * must, after splitting, be greater than or equal to {@code estimateSize()}
+     * for this and the returned Spliterator; and</li>
      * <li>if this Spliterator is {@code SUBSIZED}, then {@code estimateSize()}
      * for this spliterator before splitting must be equal to the sum of
      * {@code estimateSize()} for this and the returned Spliterator after
@@ -566,13 +567,28 @@
     public static final int SUBSIZED = 0x00004000;
 
     /**
-     * A Spliterator specialized for {@code int} values.
+     * A Spliterator specialized for primitive values.
+     *
+     * @param <T> the type of elements returned by this Spliterator.  The
+     * type must be a wrapper type for a primitive type, such as {@code Integer}
+     * for the primitive {@code int} type.
+     * @param <T_CONS> the type of primitive consumer.  The type must be a
+     * primitive specialization of {@link java.util.function.Consumer} for
+     * {@code T}, such as {@link java.util.function.IntConsumer} for
+     * {@code Integer}.
+     * @param <T_SPLITR> the type of primitive Spliterator.  The type must be
+     * a primitive specialization of Spliterator for {@code T}, such as
+     * {@link Spliterator.OfInt} for {@code Integer}.
+     *
+     * @see Spliterator.OfInt
+     * @see Spliterator.OfLong
+     * @see Spliterator.OfDouble
      * @since 1.8
      */
-    public interface OfInt extends Spliterator<Integer> {
-
+    public interface OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>
+            extends Spliterator<T> {
         @Override
-        OfInt trySplit();
+        T_SPLITR trySplit();
 
         /**
          * If a remaining element exists, performs the given action on it,
@@ -586,7 +602,7 @@
          * upon entry to this method, else {@code true}.
          * @throws NullPointerException if the specified action is null
          */
-        boolean tryAdvance(IntConsumer action);
+        boolean tryAdvance(T_CONS action);
 
         /**
          * Performs the given action for each remaining element, sequentially in
@@ -603,6 +619,24 @@
          * @param action The action
          * @throws NullPointerException if the specified action is null
          */
+        default void forEachRemaining(T_CONS action) {
+            do { } while (tryAdvance(action));
+        }
+    }
+
+    /**
+     * A Spliterator specialized for {@code int} values.
+     * @since 1.8
+     */
+    public interface OfInt extends OfPrimitive<Integer, IntConsumer, OfInt> {
+
+        @Override
+        OfInt trySplit();
+
+        @Override
+        boolean tryAdvance(IntConsumer action);
+
+        @Override
         default void forEachRemaining(IntConsumer action) {
             do { } while (tryAdvance(action));
         }
@@ -658,40 +692,15 @@
      * A Spliterator specialized for {@code long} values.
      * @since 1.8
      */
-    public interface OfLong extends Spliterator<Long> {
+    public interface OfLong extends OfPrimitive<Long, LongConsumer, OfLong> {
 
         @Override
         OfLong trySplit();
 
-        /**
-         * If a remaining element exists, performs the given action on it,
-         * returning {@code true}; else returns {@code false}.  If this
-         * Spliterator is {@link #ORDERED} the action is performed on the
-         * next element in encounter order.  Exceptions thrown by the
-         * action are relayed to the caller.
-         *
-         * @param action The action
-         * @return {@code false} if no remaining elements existed
-         * upon entry to this method, else {@code true}.
-         * @throws NullPointerException if the specified action is null
-         */
+        @Override
         boolean tryAdvance(LongConsumer action);
 
-        /**
-         * Performs the given action for each remaining element, sequentially in
-         * the current thread, until all elements have been processed or the
-         * action throws an exception.  If this Spliterator is {@link #ORDERED},
-         * actions are performed in encounter order.  Exceptions thrown by the
-         * action are relayed to the caller.
-         *
-         * @implSpec
-         * The default implementation repeatedly invokes {@link #tryAdvance}
-         * until it returns {@code false}.  It should be overridden whenever
-         * possible.
-         *
-         * @param action The action
-         * @throws NullPointerException if the specified action is null
-         */
+        @Override
         default void forEachRemaining(LongConsumer action) {
             do { } while (tryAdvance(action));
         }
@@ -747,40 +756,15 @@
      * A Spliterator specialized for {@code double} values.
      * @since 1.8
      */
-    public interface OfDouble extends Spliterator<Double> {
+    public interface OfDouble extends OfPrimitive<Double, DoubleConsumer, OfDouble> {
 
         @Override
         OfDouble trySplit();
 
-        /**
-         * If a remaining element exists, performs the given action on it,
-         * returning {@code true}; else returns {@code false}.  If this
-         * Spliterator is {@link #ORDERED} the action is performed on the
-         * next element in encounter order.  Exceptions thrown by the
-         * action are relayed to the caller.
-         *
-         * @param action The action
-         * @return {@code false} if no remaining elements existed
-         * upon entry to this method, else {@code true}.
-         * @throws NullPointerException if the specified action is null
-         */
+        @Override
         boolean tryAdvance(DoubleConsumer action);
 
-        /**
-         * Performs the given action for each remaining element, sequentially in
-         * the current thread, until all elements have been processed or the
-         * action throws an exception.  If this Spliterator is {@link #ORDERED},
-         * actions are performed in encounter order.  Exceptions thrown by the
-         * action are relayed to the caller.
-         *
-         * @implSpec
-         * The default implementation repeatedly invokes {@link #tryAdvance}
-         * until it returns {@code false}.  It should be overridden whenever
-         * possible.
-         *
-         * @param action The action
-         * @throws NullPointerException if the specified action is null
-         */
+        @Override
         default void forEachRemaining(DoubleConsumer action) {
             do { } while (tryAdvance(action));
         }
--- a/src/share/classes/java/util/function/BiConsumer.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/BiConsumer.java	Sat May 18 18:55:56 2013 -0700
@@ -24,14 +24,16 @@
  */
 package java.util.function;
 
+import java.util.Objects;
+
 /**
  * An operation which accepts two input arguments and returns no result. This is
  * the two-arity specialization of {@link Consumer}. Unlike most other
  * functional interfaces, {@code BiConsumer} is expected to operate via
  * side-effects.
  *
- * @param <T> the type of the first argument to the {@code accept} operation.
- * @param <U> the type of the second argument to the {@code accept} operation.
+ * @param <T> the type of the first argument to the {@code accept} operation
+ * @param <U> the type of the second argument to the {@code accept} operation
  *
  * @see Consumer
  * @since 1.8
@@ -47,4 +49,28 @@
      * @param u an input object
      */
     void accept(T t, U u);
+
+    /**
+     * Returns a {@code BiConsumer} which performs, in sequence, the operation
+     * represented by this object followed by the operation represented by
+     * the other {@code BiConsumer}.
+     *
+     * <p>Any exceptions thrown by either {@code accept} method are relayed
+     * to the caller; if performing this operation throws an exception, the
+     * other operation will not be performed.
+     *
+     * @param other a BiConsumer which will be chained after this BiConsumer
+     * @return a BiConsumer which performs in sequence the {@code accept} method
+     * of this BiConsumer and the {@code accept} method of the specified
+     * BiConsumer operation
+     * @throws NullPointerException if other is null
+     */
+    default BiConsumer<T, U> chain(BiConsumer<? super T, ? super U> other) {
+        Objects.requireNonNull(other);
+
+        return (l, r) -> {
+            accept(l, r);
+            other.accept(l, r);
+        };
+    }
 }
--- a/src/share/classes/java/util/function/BiFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/BiFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -24,15 +24,17 @@
  */
 package java.util.function;
 
+import java.util.Objects;
+
 /**
  * Apply a function to the input arguments, yielding an appropriate result. This
  * is the two-arity specialization of {@link Function}. A function may
  * variously provide a mapping between types, object instances or keys and
  * values or any other form of transformation upon the input.
  *
- * @param <T> the type of the first argument to the {@code apply} operation.
- * @param <U> the type of the second argument to the {@code apply} operation.
- * @param <R> the type of results returned by the {@code apply} operation.
+ * @param <T> the type of the first argument to the {@code apply} operation
+ * @param <U> the type of the second argument to the {@code apply} operation
+ * @param <R> the type of results returned by the {@code apply} operation
  *
  * @see Function
  * @since 1.8
@@ -48,4 +50,22 @@
      * @return the function result
      */
     R apply(T t, U u);
+
+    /**
+     * Returns a new function which applies this function followed by the
+     * provided function.  If either function throws an exception, it is relayed
+     * to the caller.
+     *
+     * @param <V> Type of output objects to the combined function. May be the
+     * same type as {@code <T>}, {@code <U>} or {@code <R>}
+     * @param after An additional function to be applied after this function is
+     * applied
+     * @return A function which performs this function followed by the provided
+     * function
+     * @throws NullPointerException if after is null
+     */
+    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
+        Objects.requireNonNull(after);
+        return (T t, U u) -> after.apply(apply(t, u));
+    }
 }
--- a/src/share/classes/java/util/function/BiPredicate.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/BiPredicate.java	Sat May 18 18:55:56 2013 -0700
@@ -30,8 +30,8 @@
  * Determines if the input objects match some criteria. This is the two-arity
  * specialization of {@link Predicate}.
  *
- * @param <T> the type of the first argument to {@code test}.
- * @param <U> the type of the second argument to {@code test}.
+ * @param <T> the type of the first argument to {@code test}
+ * @param <U> the type of the second argument to {@code test}
  *
  * @see Predicate
  * @since 1.8
@@ -42,9 +42,9 @@
     /**
      * Return {@code true} if the inputs match some criteria.
      *
-     * @param t an input object.
-     * @param u an input object.
-     * @return {@code true} if the inputs match some criteria.
+     * @param t an input object
+     * @param u an input object
+     * @return {@code true} if the inputs match some criteria
      */
     boolean test(T t, U u);
 
@@ -54,11 +54,12 @@
      * this predicate returns {@code false} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ANDed with this predicate.
+     * @param p a predicate which will be logically-ANDed with this predicate
      * @return a new predicate which returns {@code true} only if both
-     * predicates return {@code true}.
+     * predicates return {@code true}
+     * @throws NullPointerException if p is null
      */
-    public default BiPredicate<T, U> and(BiPredicate<? super T, ? super U> p) {
+    default BiPredicate<T, U> and(BiPredicate<? super T, ? super U> p) {
         Objects.requireNonNull(p);
         return (T t, U u) -> test(t, u) && p.test(t, u);
     }
@@ -67,9 +68,9 @@
      * Returns a predicate which negates the result of this predicate.
      *
      * @return a new predicate who's result is always the opposite of this
-     * predicate.
+     * predicate
      */
-    public default BiPredicate<T, U> negate() {
+    default BiPredicate<T, U> negate() {
         return (T t, U u) -> !test(t, u);
     }
 
@@ -79,25 +80,13 @@
      * predicate returns {@code true} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ORed with this predicate.
+     * @param p a predicate which will be logically-ORed with this predicate
      * @return a new predicate which returns {@code true} if either predicate
-     * returns {@code true}.
+     * returns {@code true}
+     * @throws NullPointerException if p is null
      */
-    public default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> p) {
+    default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> p) {
         Objects.requireNonNull(p);
         return (T t, U u) -> test(t, u) || p.test(t, u);
     }
-
-    /**
-     * Returns a predicate that evaluates to {@code true} if both or neither of
-     * the component predicates evaluate to {@code true}.
-     *
-     * @param p a predicate which will be logically-XORed with this predicate.
-     * @return a predicate that evaluates to {@code true} if both or neither of
-     * the component predicates evaluate to {@code true}.
-     */
-    public default BiPredicate<T, U> xor(BiPredicate<? super T, ? super U> p) {
-        Objects.requireNonNull(p);
-        return (T t, U u) -> test(t, u) ^ p.test(t, u);
-    }
 }
--- a/src/share/classes/java/util/function/BooleanSupplier.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/BooleanSupplier.java	Sat May 18 18:55:56 2013 -0700
@@ -40,5 +40,5 @@
      *
      * @return a {@code boolean} value
      */
-    public boolean getAsBoolean();
+    boolean getAsBoolean();
 }
--- a/src/share/classes/java/util/function/Consumer.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/Consumer.java	Sat May 18 18:55:56 2013 -0700
@@ -24,6 +24,8 @@
  */
 package java.util.function;
 
+import java.util.Objects;
+
 /**
  * An operation which accepts a single input argument and returns no result.
  * Unlike most other functional interfaces, {@code Consumer} is expected to
@@ -41,5 +43,25 @@
      *
      * @param t the input object
      */
-    public void accept(T t);
+    void accept(T t);
+
+    /**
+     * Returns a {@code Consumer} which performs, in sequence, the operation
+     * represented by this object followed by the operation represented by
+     * the other {@code Consumer}.
+     *
+     * <p>Any exceptions thrown by either {@code accept} method are relayed
+     * to the caller; if performing this operation throws an exception, the
+     * other operation will not be performed.
+     *
+     * @param other a Consumer which will be chained after this Consumer
+     * @return a Consumer which performs in sequence the {@code accept} method
+     * of this Consumer and the {@code accept} method of the specified Consumer
+     * operation
+     * @throws NullPointerException if other is null
+     */
+    default Consumer<T> chain(Consumer<? super T> other) {
+        Objects.requireNonNull(other);
+        return (T t) -> { accept(t); other.accept(t); };
+    }
 }
--- a/src/share/classes/java/util/function/DoubleBinaryOperator.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/DoubleBinaryOperator.java	Sat May 18 18:55:56 2013 -0700
@@ -43,5 +43,5 @@
      * @param right the right operand value
      * @return the result of the operation
      */
-    public double applyAsDouble(double left, double right);
+    double applyAsDouble(double left, double right);
 }
--- a/src/share/classes/java/util/function/DoubleConsumer.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/DoubleConsumer.java	Sat May 18 18:55:56 2013 -0700
@@ -24,6 +24,8 @@
  */
 package java.util.function;
 
+import java.util.Objects;
+
 /**
  * An operation which accepts a single double argument and returns no result.
  * This is the primitive type specialization of {@link Consumer} for
@@ -41,5 +43,26 @@
      *
      * @param value the input value
      */
-    public void accept(double value);
+    void accept(double value);
+
+    /**
+     * Returns a {@code DoubleConsumer} which performs, in sequence, the operation
+     * represented by this object followed by the operation represented by
+     * another {@code DoubleConsumer}.
+     *
+     * <p>Any exceptions thrown by either {@code accept} method are relayed
+     * to the caller; if performing this operation throws an exception, the
+     * other operation will not be performed.
+     *
+     * @param other a DoubleConsumer which will be chained after this
+     * DoubleConsumer
+     * @return an DoubleConsumer which performs in sequence the {@code accept} method
+     * of this DoubleConsumer and the {@code accept} method of the specified IntConsumer
+     * operation
+     * @throws NullPointerException if other is null
+     */
+    default DoubleConsumer chain(DoubleConsumer other) {
+        Objects.requireNonNull(other);
+        return (double t) -> { accept(t); other.accept(t); };
+    }
 }
--- a/src/share/classes/java/util/function/DoubleFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/DoubleFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -43,5 +43,5 @@
      * @param value the input value
      * @return the function result
      */
-    public R apply(double value);
+    R apply(double value);
 }
--- a/src/share/classes/java/util/function/DoublePredicate.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/DoublePredicate.java	Sat May 18 18:55:56 2013 -0700
@@ -40,11 +40,11 @@
     /**
      * Returns {@code true} if the input value matches some criteria.
      *
-     * @param value the value to be tested.
+     * @param value the value to be tested
      * @return {@code true} if the input value matches some criteria, otherwise
-     * {@code false}.
+     * {@code false}
      */
-    public boolean test(double value);
+    boolean test(double value);
 
     /**
      * Returns a predicate which evaluates to {@code true} only if this
@@ -52,11 +52,16 @@
      * this predicate returns {@code false} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ANDed with this predicate.
+     * <p>Any exceptions thrown by either {@code test} method are relayed
+     * to the caller; if performing first operation throws an exception, the
+     * second operation will not be performed.
+     *
+     * @param p a predicate which will be logically-ANDed with this predicate
      * @return a new predicate which returns {@code true} only if both
-     * predicates return {@code true}.
+     * predicates return {@code true}
+     * @throws NullPointerException if p is null
      */
-    public default DoublePredicate and(DoublePredicate p) {
+    default DoublePredicate and(DoublePredicate p) {
         Objects.requireNonNull(p);
         return (value) -> test(value) && p.test(value);
     }
@@ -65,9 +70,9 @@
      * Returns a predicate which negates the result of this predicate.
      *
      * @return a new predicate who's result is always the opposite of this
-     * predicate.
+     * predicate
      */
-    public default DoublePredicate negate() {
+    default DoublePredicate negate() {
         return (value) -> !test(value);
     }
 
@@ -77,25 +82,17 @@
      * predicate returns {@code true} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ANDed with this predicate.
+     * <p>Any exceptions thrown by either {@code test} method are relayed
+     * to the caller; if performing first operation throws an exception, the
+     * second operation will not be performed.
+     *
+     * @param p a predicate which will be logically-ANDed with this predicate
      * @return a new predicate which returns {@code true} if either predicate
-     * returns {@code true}.
+     * returns {@code true}
+     * @throws NullPointerException if p is null
      */
-    public default DoublePredicate or(DoublePredicate p) {
+    default DoublePredicate or(DoublePredicate p) {
         Objects.requireNonNull(p);
         return (value) -> test(value) || p.test(value);
     }
-
-    /**
-     * Returns a predicate that evaluates to {@code true} if both or neither of
-     * the component predicates evaluate to {@code true}.
-     *
-     * @param p a predicate which will be logically-XORed with this predicate.
-     * @return a predicate that evaluates to {@code true} if all or none of the
-     * component predicates evaluate to {@code true}.
-     */
-    public default DoublePredicate xor(DoublePredicate p) {
-        Objects.requireNonNull(p);
-        return (value) -> test(value) ^ p.test(value);
-    }
 }
--- a/src/share/classes/java/util/function/DoubleSupplier.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/DoubleSupplier.java	Sat May 18 18:55:56 2013 -0700
@@ -39,5 +39,5 @@
      *
      * @return a {@code double} value
      */
-    public double getAsDouble();
+    double getAsDouble();
 }
--- a/src/share/classes/java/util/function/DoubleUnaryOperator.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/DoubleUnaryOperator.java	Sat May 18 18:55:56 2013 -0700
@@ -24,6 +24,8 @@
  */
 package java.util.function;
 
+import java.util.Objects;
+
 /**
  * An operation on a {@code double} operand yielding a {@code double}
  * result. This is the primitive type specialization of {@link UnaryOperator}
@@ -42,5 +44,46 @@
      * @param operand the operand value
      * @return the operation result value
      */
-    public double applyAsDouble(double operand);
+    double applyAsDouble(double operand);
+
+    /**
+     * Compose a new function which applies the provided function followed by
+     * this function.  If either function throws an exception, it is relayed
+     * to the caller.
+     *
+     * @param before An additional function to be applied before this function
+     * is applied
+     * @return A function which performs the provided function followed by this
+     * function
+     * @throws NullPointerException if before is null
+     */
+    default DoubleUnaryOperator compose(DoubleUnaryOperator before) {
+        Objects.requireNonNull(before);
+        return (double v) -> applyAsDouble(before.applyAsDouble(v));
+    }
+
+    /**
+     * Compose a new function which applies this function followed by the
+     * provided function.  If either function throws an exception, it is relayed
+     * to the caller.
+     *
+     * @param after An additional function to be applied after this function is
+     * applied
+     * @return A function which performs this function followed by the provided
+     * function followed
+     * @throws NullPointerException if after is null
+     */
+    default DoubleUnaryOperator andThen(DoubleUnaryOperator after) {
+        Objects.requireNonNull(after);
+        return (double t) -> after.applyAsDouble(applyAsDouble(t));
+    }
+
+    /**
+     * Returns a unary operator that provides its input value as the result.
+     *
+     * @return a unary operator that provides its input value as the result
+     */
+    static DoubleUnaryOperator identity() {
+        return t -> t;
+    }
 }
--- a/src/share/classes/java/util/function/Function.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/Function.java	Sat May 18 18:55:56 2013 -0700
@@ -24,14 +24,15 @@
  */
 package java.util.function;
 
+import java.util.Objects;
 
 /**
  * Apply a function to the input argument, yielding an appropriate result.  A
  * function may variously provide a mapping between types, object instances or
  * keys and values or any other form of transformation upon the input.
  *
- * @param <T> the type of the input to the {@code apply} operation.
- * @param <R> the type of the result of the {@code apply} operation.
+ * @param <T> the type of the input to the {@code apply} operation
+ * @param <R> the type of the result of the {@code apply} operation
  *
  * @since 1.8
  */
@@ -44,5 +45,50 @@
      * @param t the input object
      * @return the function result
      */
-    public R apply(T t);
+    R apply(T t);
+
+    /**
+     * Returns a new function which applies the provided function followed by
+     * this function.  If either function throws an exception, it is relayed
+     * to the caller.
+     *
+     * @param <V> type of input objects to the combined function. May be the
+     * same type as {@code <T>} or {@code <R>}
+     * @param before an additional function to be applied before this function
+     * is applied
+     * @return a function which performs the provided function followed by this
+     * function
+     * @throws NullPointerException if before is null
+     */
+    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
+        Objects.requireNonNull(before);
+        return (V v) -> apply(before.apply(v));
+    }
+
+    /**
+     * Returns a new function which applies this function followed by the
+     * provided function.  If either function throws an exception, it is relayed
+     * to the caller.
+     *
+     * @param <V> type of output objects to the combined function. May be the
+     * same type as {@code <T>} or {@code <R>}
+     * @param after an additional function to be applied after this function is
+     * applied
+     * @return a function which performs this function followed by the provided
+     * function
+     * @throws NullPointerException if after is null
+     */
+    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
+        Objects.requireNonNull(after);
+        return (T t) -> after.apply(apply(t));
+    }
+
+    /**
+     * Returns a {@code Function} whose {@code apply} method returns its input.
+     *
+     * @param <T> the type of the input and output objects to the function
+     */
+    static <T> Function<T, T> identity() {
+        return t -> t;
+    }
 }
--- a/src/share/classes/java/util/function/IntBinaryOperator.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/IntBinaryOperator.java	Sat May 18 18:55:56 2013 -0700
@@ -44,5 +44,5 @@
      * @param right  the right operand value
      * @return the result of the operation
      */
-    public int applyAsInt(int left, int right);
+    int applyAsInt(int left, int right);
 }
--- a/src/share/classes/java/util/function/IntConsumer.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/IntConsumer.java	Sat May 18 18:55:56 2013 -0700
@@ -24,6 +24,8 @@
  */
 package java.util.function;
 
+import java.util.Objects;
+
 /**
  * An operation which accepts a single integer argument and returns no result.
  * This is the primitive type specialization of {@link Consumer} for {@code int}.
@@ -41,5 +43,26 @@
      *
      * @param value the input value
      */
-    public void accept(int value);
+    void accept(int value);
+
+    /**
+     * Returns an {@code IntConsumer} which performs, in sequence, the operation
+     * represented by this object followed by the operation represented by
+     * another {@code IntConsumer}.
+     *
+     * <p>Any exceptions thrown by either {@code accept} method are relayed
+     * to the caller; if performing this operation throws an exception, the
+     * other operation will not be performed.
+     *
+     * @param other an IntConsumer which will be chained after this
+     * IntConsumer
+     * @return an IntConsumer which performs in sequence the {@code accept} method
+     * of this IntConsumer and the {@code accept} method of the specified IntConsumer
+     * operation
+     * @throws NullPointerException if other is null
+     */
+    default IntConsumer chain(IntConsumer other) {
+        Objects.requireNonNull(other);
+        return (int t) -> { accept(t); other.accept(t); };
+    }
 }
--- a/src/share/classes/java/util/function/IntFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/IntFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -43,5 +43,5 @@
      * @param value the input value
      * @return the function result
      */
-    public R apply(int value);
+    R apply(int value);
 }
--- a/src/share/classes/java/util/function/IntPredicate.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/IntPredicate.java	Sat May 18 18:55:56 2013 -0700
@@ -39,11 +39,11 @@
     /**
      * Returns {@code true} if the input value matches some criteria.
      *
-     * @param value the value to be tested.
+     * @param value the value to be tested
      * @return {@code true} if the input value matches some criteria, otherwise
      * {@code false}
      */
-    public boolean test(int value);
+    boolean test(int value);
 
     /**
      * Returns a predicate which evaluates to {@code true} only if this
@@ -51,11 +51,16 @@
      * this predicate returns {@code false} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ANDed with this predicate.
+     * <p>Any exceptions thrown by either {@code test} method are relayed
+     * to the caller; if performing first operation throws an exception, the
+     * second operation will not be performed.
+     *
+     * @param p a predicate which will be logically-ANDed with this predicate
      * @return a new predicate which returns {@code true} only if both
-     * predicates return {@code true}.
+     * predicates return {@code true}
+     * @throws NullPointerException if p is null
      */
-    public default IntPredicate and(IntPredicate p) {
+    default IntPredicate and(IntPredicate p) {
         Objects.requireNonNull(p);
         return (value) -> test(value) && p.test(value);
     }
@@ -64,9 +69,9 @@
      * Returns a predicate which negates the result of this predicate.
      *
      * @return a new predicate who's result is always the opposite of this
-     * predicate.
+     * predicate
      */
-    public default IntPredicate negate() {
+    default IntPredicate negate() {
         return (value) -> !test(value);
     }
 
@@ -76,25 +81,17 @@
      * predicate returns {@code true} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ORed with this predicate.
+     * <p>Any exceptions thrown by either {@code test} method are relayed
+     * to the caller; if performing first operation throws an exception, the
+     * second operation will not be performed.
+     *
+     * @param p a predicate which will be logically-ORed with this predicate
      * @return a new predicate which returns {@code true} if either predicate
-     * returns {@code true}.
+     * returns {@code true}
+     * @throws NullPointerException if p is null
      */
-    public default IntPredicate or(IntPredicate p) {
+    default IntPredicate or(IntPredicate p) {
         Objects.requireNonNull(p);
         return (value) -> test(value) || p.test(value);
     }
-
-    /**
-     * Returns a predicate that evaluates to {@code true} if both or neither of
-     * the component predicates evaluate to {@code true}.
-     *
-     * @param p a predicate which will be logically-XORed with this predicate.
-     * @return a predicate that evaluates to {@code true} if both or neither of
-     * the component predicates evaluate to {@code true}
-     */
-    public default IntPredicate xor(IntPredicate p) {
-        Objects.requireNonNull(p);
-        return (value) -> test(value) ^ p.test(value);
-    }
 }
--- a/src/share/classes/java/util/function/IntSupplier.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/IntSupplier.java	Sat May 18 18:55:56 2013 -0700
@@ -39,5 +39,5 @@
      *
      * @return an {@code int} value
      */
-    public int getAsInt();
+    int getAsInt();
 }
--- a/src/share/classes/java/util/function/IntUnaryOperator.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/IntUnaryOperator.java	Sat May 18 18:55:56 2013 -0700
@@ -24,6 +24,8 @@
  */
 package java.util.function;
 
+import java.util.Objects;
+
 /**
  * An operation on a single {@code int} operand yielding an {@code int} result.
  * This is the primitive type specialization of {@link UnaryOperator} for
@@ -37,10 +39,51 @@
 
     /**
      * Returns the {@code int} value result of the operation upon the
-     * {@code int}  operand.
+     * {@code int} operand.
      *
      * @param operand the operand value
      * @return the operation result value
      */
-    public int applyAsInt(int operand);
+    int applyAsInt(int operand);
+
+    /**
+     * Compose a new function which applies the provided function followed by
+     * this function.  If either function throws an exception, it is relayed
+     * to the caller.
+     *
+     * @param before an additional function to be applied before this function
+     * is applied
+     * @return a function which performs the provided function followed by this
+     * function
+     * @throws NullPointerException if before is null
+     */
+    default IntUnaryOperator compose(IntUnaryOperator before) {
+        Objects.requireNonNull(before);
+        return (int v) -> applyAsInt(before.applyAsInt(v));
+    }
+
+    /**
+     * Compose a new function which applies this function followed by the
+     * provided function.  If either function throws an exception, it is relayed
+     * to the caller.
+     *
+     * @param after an additional function to be applied after this function is
+     * applied
+     * @return a function which performs this function followed by the provided
+     * function followed
+     * @throws NullPointerException if after is null
+     */
+    default IntUnaryOperator andThen(IntUnaryOperator after) {
+        Objects.requireNonNull(after);
+        return (int t) -> after.applyAsInt(applyAsInt(t));
+    }
+
+    /**
+     * Returns a unary operator that provides its input value as the result.
+     *
+     * @return a unary operator that provides its input value as the result
+     */
+    static IntUnaryOperator identity() {
+        return t -> t;
+    }
 }
--- a/src/share/classes/java/util/function/LongBinaryOperator.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/LongBinaryOperator.java	Sat May 18 18:55:56 2013 -0700
@@ -44,5 +44,5 @@
      * @param right  the right operand value
      * @return the result of the operation
      */
-    public long applyAsLong(long left, long right);
+    long applyAsLong(long left, long right);
 }
--- a/src/share/classes/java/util/function/LongConsumer.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/LongConsumer.java	Sat May 18 18:55:56 2013 -0700
@@ -24,6 +24,8 @@
  */
 package java.util.function;
 
+import java.util.Objects;
+
 /**
  * An operation which accepts a single long argument and returns no result.
  * This is the {@code long}-consuming primitive type specialization of
@@ -41,5 +43,26 @@
      *
      * @param value the input value
      */
-    public void accept(long value);
+    void accept(long value);
+
+    /**
+     * Returns a {@code LongConsumer} which performs, in sequence, the operation
+     * represented by this object followed by the operation represented by
+     * another {@code LongConsumer}.
+     *
+     * <p>Any exceptions thrown by either {@code accept} method are relayed
+     * to the caller; if performing this operation throws an exception, the
+     * other operation will not be performed.
+     *
+     * @param other a LongConsumer which will be chained after this
+     * LongConsumer
+     * @return a LongConsumer which performs in sequence the {@code accept} method
+     * of this LongConsumer and the {@code accept} method of the specified LongConsumer
+     * operation
+     * @throws NullPointerException if other is null
+     */
+    default LongConsumer chain(LongConsumer other) {
+        Objects.requireNonNull(other);
+        return (long t) -> { accept(t); other.accept(t); };
+    }
 }
--- a/src/share/classes/java/util/function/LongFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/LongFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -43,5 +43,5 @@
      * @param value the input value
      * @return the function result
      */
-    public R apply(long value);
+    R apply(long value);
 }
--- a/src/share/classes/java/util/function/LongPredicate.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/LongPredicate.java	Sat May 18 18:55:56 2013 -0700
@@ -39,11 +39,11 @@
     /**
      * Returns {@code true} if the input value matches some criteria.
      *
-     * @param value the value to be tested.
+     * @param value the value to be tested
      * @return {@code true} if the input value matches some criteria, otherwise
-     * {@code false}.
+     * {@code false}
      */
-    public boolean test(long value);
+    boolean test(long value);
 
     /**
      * Returns a predicate which evaluates to {@code true} only if this
@@ -51,11 +51,15 @@
      * this predicate returns {@code false} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ANDed with this predicate.
+     * <p>Any exceptions thrown by either {@code test} method are relayed
+     * to the caller; if performing first operation throws an exception, the
+     * second operation will not be performed.
+     *
+     * @param p a predicate which will be logically-ANDed with this predicate
      * @return a new predicate which returns {@code true} only if both
-     * predicates return {@code true}.
+     * predicates return {@code true}
      */
-    public default LongPredicate and(LongPredicate p) {
+    default LongPredicate and(LongPredicate p) {
         Objects.requireNonNull(p);
         return (value) -> test(value) && p.test(value);
     }
@@ -64,9 +68,9 @@
      * Returns a predicate which negates the result of this predicate.
      *
      * @return a new predicate who's result is always the opposite of this
-     * predicate.
+     * predicate
      */
-    public default LongPredicate negate() {
+    default LongPredicate negate() {
         return (value) -> !test(value);
     }
 
@@ -76,25 +80,17 @@
      * predicate returns {@code true} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ORed with this predicate.
+     * <p>Any exceptions thrown by either {@code test} method are relayed
+     * to the caller; if performing first operation throws an exception, the
+     * second operation will not be performed.
+     *
+     * @param p a predicate which will be logically-ORed with this predicate
      * @return a new predicate which returns {@code true} if either predicate
-     * returns {@code true}.
+     * returns {@code true}
+     * @throws NullPointerException if p is null
      */
-    public default LongPredicate or(LongPredicate p) {
+    default LongPredicate or(LongPredicate p) {
         Objects.requireNonNull(p);
         return (value) -> test(value) || p.test(value);
     }
-
-    /**
-     * Returns a predicate that evaluates to {@code true} if both or neither of
-     * the component predicates evaluate to {@code true}.
-     *
-     * @param p a predicate which will be logically-XORed with this predicate.
-     * @return a predicate that evaluates to {@code true} if both or neither of
-     * the component predicates evaluate to {@code true}.
-     */
-    public default LongPredicate xor(LongPredicate p) {
-        Objects.requireNonNull(p);
-        return (value) -> test(value) ^ p.test(value);
-    }
 }
--- a/src/share/classes/java/util/function/LongSupplier.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/LongSupplier.java	Sat May 18 18:55:56 2013 -0700
@@ -39,5 +39,5 @@
      *
      * @return a {@code long} value
      */
-    public long getAsLong();
+    long getAsLong();
 }
--- a/src/share/classes/java/util/function/LongUnaryOperator.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/LongUnaryOperator.java	Sat May 18 18:55:56 2013 -0700
@@ -24,6 +24,8 @@
  */
 package java.util.function;
 
+import java.util.Objects;
+
 /**
  * An operation on a single {@code long} operand yielding a {@code long} result.
  * This is the primitive type specialization of {@link UnaryOperator} for
@@ -42,5 +44,46 @@
      * @param operand the operand value
      * @return the operation result value
      */
-    public long applyAsLong(long operand);
+    long applyAsLong(long operand);
+
+    /**
+     * Compose a new function which applies the provided function followed by
+     * this function.  If either function throws an exception, it is relayed
+     * to the caller.
+     *
+     * @param before An additional function to be applied before this function
+     * is applied
+     * @return A function which performs the provided function followed by this
+     * function
+     * @throws NullPointerException if before is null
+     */
+    default LongUnaryOperator compose(LongUnaryOperator before) {
+        Objects.requireNonNull(before);
+        return (long v) -> applyAsLong(before.applyAsLong(v));
+    }
+
+    /**
+     * Compose a new function which applies this function followed by the
+     * provided function.  If either function throws an exception, it is relayed
+     * to the caller.
+     *
+     * @param after An additional function to be applied after this function is
+     * applied
+     * @return A function which performs this function followed by the provided
+     * function followed
+     * @throws NullPointerException if after is null
+     */
+    default LongUnaryOperator andThen(LongUnaryOperator after) {
+        Objects.requireNonNull(after);
+        return (long t) -> after.applyAsLong(applyAsLong(t));
+    }
+
+    /**
+     * Returns a unary operator that provides its input value as the result.
+     *
+     * @return a unary operator that provides its input value as the result
+     */
+    static LongUnaryOperator identity() {
+        return t -> t;
+    }
 }
--- a/src/share/classes/java/util/function/ObjDoubleConsumer.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/ObjDoubleConsumer.java	Sat May 18 18:55:56 2013 -0700
@@ -44,5 +44,5 @@
      * @param t an input object
      * @param value an input value
      */
-    public void accept(T t, double value);
+    void accept(T t, double value);
 }
--- a/src/share/classes/java/util/function/ObjIntConsumer.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/ObjIntConsumer.java	Sat May 18 18:55:56 2013 -0700
@@ -30,7 +30,7 @@
  * {@link BiConsumer}. Unlike most other functional interfaces,
  * {@code ObjIntConsumer} is expected to operate via side-effects.
  *
- * @param <T> Type of reference argument to {@code accept()}.
+ * @param <T> Type of reference argument to {@code accept()}
  *
  * @see BiConsumer
  * @since 1.8
@@ -44,5 +44,5 @@
      * @param t an input object
      * @param value an input value
      */
-    public void accept(T t, int value);
+    void accept(T t, int value);
 }
--- a/src/share/classes/java/util/function/ObjLongConsumer.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/ObjLongConsumer.java	Sat May 18 18:55:56 2013 -0700
@@ -30,7 +30,7 @@
  * {@link BiConsumer}. Unlike most other functional interfaces,
  * {@code ObjLongConsumer} is expected to operate via side-effects.
  *
- * @param <T> Type of reference argument to {@code accept()}.
+ * @param <T> Type of reference argument to {@code accept()}
  *
  * @see BiConsumer
  * @since 1.8
@@ -44,5 +44,5 @@
      * @param t an input object
      * @param value an input value
      */
-    public void accept(T t, long value);
+    void accept(T t, long value);
 }
--- a/src/share/classes/java/util/function/Predicate.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/Predicate.java	Sat May 18 18:55:56 2013 -0700
@@ -43,7 +43,7 @@
      * @return {@code true} if the input object matches some criteria, otherwise
      * {@code false}
      */
-    public boolean test(T t);
+    boolean test(T t);
 
     /**
      * Returns a predicate which evaluates to {@code true} only if this
@@ -51,11 +51,16 @@
      * this predicate returns {@code false} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ANDed with this predicate.
+     * <p>Any exceptions thrown by either {@code test} method are relayed
+     * to the caller; if performing first operation throws an exception, the
+     * second operation will not be performed.
+     *
+     * @param p a predicate which will be logically-ANDed with this predicate
      * @return a new predicate which returns {@code true} only if both
-     * predicates return {@code true}.
+     * predicates return {@code true}
+     * @throws NullPointerException if p is null
      */
-    public default Predicate<T> and(Predicate<? super T> p) {
+    default Predicate<T> and(Predicate<? super T> p) {
         Objects.requireNonNull(p);
         return (t) -> test(t) && p.test(t);
     }
@@ -66,7 +71,7 @@
      * @return a new predicate who's result is always the opposite of this
      * predicate.
      */
-    public default Predicate<T> negate() {
+    default Predicate<T> negate() {
         return (t) -> !test(t);
     }
 
@@ -76,25 +81,32 @@
      * predicate returns {@code true} then the remaining predicate is not
      * evaluated.
      *
-     * @param p a predicate which will be logically-ORed with this predicate.
+     * <p>Any exceptions thrown by either {@code test} method are relayed
+     * to the caller; if performing first operation throws an exception, the
+     * second operation will not be performed.
+     *
+     * @param p a predicate which will be logically-ORed with this predicate
      * @return a new predicate which returns {@code true} if either predicate
-     * returns {@code true}.
+     * returns {@code true}
+     * @throws NullPointerException if p is null
      */
-    public default Predicate<T> or(Predicate<? super T> p) {
+    default Predicate<T> or(Predicate<? super T> p) {
         Objects.requireNonNull(p);
         return (t) -> test(t) || p.test(t);
     }
 
     /**
-     * Returns a predicate that evaluates to {@code true} if both or neither of
-     * the component predicates evaluate to {@code true}.
+     * Returns a predicate who's result matches
+     * {@code Objects.equals(target, t)}.
      *
-     * @param p a predicate which will be logically-XORed with this predicte.
-     * @return a predicate that evaluates to {@code true} if both or neither of
-     * the component predicates evaluate to {@code true}.
+     * @param <T> the type of values evaluated by the predicate
+     * @param target the target value to be compared for equality
+     * @return a predicate who's result matches
+     * {@code Objects.equals(target, t)}
      */
-    public default Predicate<T> xor(Predicate<? super T> p) {
-        Objects.requireNonNull(p);
-        return (t) -> test(t) ^ p.test(t);
+    static <T> Predicate<T> isEqual(Object target) {
+        return (null == target)
+                ? Objects::isNull
+                : object -> target.equals(object);
     }
 }
--- a/src/share/classes/java/util/function/Supplier.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/Supplier.java	Sat May 18 18:55:56 2013 -0700
@@ -40,5 +40,5 @@
      *
      * @return an object
      */
-    public T get();
+    T get();
 }
--- a/src/share/classes/java/util/function/ToDoubleBiFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/ToDoubleBiFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -46,5 +46,5 @@
      * @param u an input object
      * @return the function result value
      */
-    public double applyAsDouble(T t, U u);
+    double applyAsDouble(T t, U u);
 }
--- a/src/share/classes/java/util/function/ToDoubleFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/ToDoubleFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -42,5 +42,5 @@
      * @param t the input object
      * @return the function result value
      */
-    public double applyAsDouble(T t);
+    double applyAsDouble(T t);
 }
--- a/src/share/classes/java/util/function/ToIntBiFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/ToIntBiFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -28,10 +28,10 @@
  * Apply a function to the input arguments, yielding an appropriate result.
  * This is the {@code int}-bearing specialization for {@link BiFunction}.
  *
- * @param <T> the type of the first argument to the {@code applyAsLong}
- * operation.
- * @param <U> the type of the second argument to the {@code applyAsLong}
- * operation.
+ * @param <T> the type of the first argument to the {@code applyAsInt}
+ * operation
+ * @param <U> the type of the second argument to the {@code applyAsInt}
+ * operation
  *
  * @see BiFunction
  * @since 1.8
@@ -46,5 +46,5 @@
      * @param u an input object
      * @return the function result value
      */
-    public int applyAsInt(T t, U u);
+    int applyAsInt(T t, U u);
 }
--- a/src/share/classes/java/util/function/ToIntFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/ToIntFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -42,5 +42,5 @@
      * @param t the input object
      * @return the function result value
      */
-    public int applyAsInt(T t);
+    int applyAsInt(T t);
 }
--- a/src/share/classes/java/util/function/ToLongBiFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/ToLongBiFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -46,5 +46,5 @@
      * @param u an input object
      * @return the function result value
      */
-    public long applyAsLong(T t, U u);
+    long applyAsLong(T t, U u);
 }
--- a/src/share/classes/java/util/function/ToLongFunction.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/ToLongFunction.java	Sat May 18 18:55:56 2013 -0700
@@ -42,5 +42,5 @@
      * @param t the input object
      * @return the function result value
      */
-    public long applyAsLong(T t);
+    long applyAsLong(T t);
 }
--- a/src/share/classes/java/util/function/UnaryOperator.java	Sat May 18 10:15:06 2013 +0800
+++ b/src/share/classes/java/util/function/UnaryOperator.java	Sat May 18 18:55:56 2013 -0700
@@ -36,4 +36,13 @@
  */
 @FunctionalInterface
 public interface UnaryOperator<T> extends Function<T, T> {
+
+    /**
+     * Returns a unary operator that provides its input value as the result.
+     *
+     * @return a unary operator that provides its input value as the result
+     */
+    static <T> UnaryOperator<T> identity() {
+        return t -> t;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/file/Files/FaultyFileSystem.java	Sat May 18 18:55:56 2013 -0700
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.AccessMode;
+import java.nio.file.CopyOption;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileStore;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.PathMatcher;
+import java.nio.file.WatchService;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.FileAttributeView;
+import java.nio.file.attribute.UserPrincipalLookupService;
+import java.nio.file.spi.FileSystemProvider;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.function.Supplier;
+
+/**
+ * A {@code FileSystem} that helps testing by trigger exception throwing based on filenames.
+ */
+class FaultyFileSystem extends FileSystem {
+    final Path root;
+    final boolean removeRootAfterClose;
+    final FileSystem delegate;
+    boolean isOpen;
+
+    FaultyFileSystem(Path root) throws IOException {
+        if (root == null) {
+            root = Files.createTempDirectory("faultyFS");
+            removeRootAfterClose = true;
+        } else {
+            if (! Files.isDirectory(root)) {
+                throw new IllegalArgumentException("must be a directory.");
+            }
+            removeRootAfterClose = false;
+        }
+        this.root = root;
+        delegate = root.getFileSystem();
+        isOpen = true;
+    }
+
+    private static Path unwrap(Path p) {
+        return PassThroughFileSystem.unwrap(p);
+    }
+
+    Path getRoot() {
+        return new PassThroughFileSystem.PassThroughPath(this, root);
+    }
+
+    @Override
+    public void close() throws IOException {
+        if (isOpen) {
+            if (removeRootAfterClose) {
+                TestUtil.removeAll(root);
+            }
+            isOpen = false;
+        }
+    }
+
+    @Override
+    public FileSystemProvider provider() {
+        return FaultyFSProvider.getInstance();
+    }
+
+    @Override
+    public boolean isOpen() {
+        return isOpen;
+    }
+
+    @Override
+    public boolean isReadOnly() {
+        return delegate.isReadOnly();
+    }
+
+    @Override
+    public String getSeparator() {
+        return delegate.getSeparator();
+    }
+
+    private <T> Iterable<T> SoleIterable(final T element) {
+        return new Iterable<T>() {
+            @Override
+            public Iterator<T> iterator() {
+                return new Iterator<T>() {
+                    private T soleElement = element;
+
+                    @Override
+                    public boolean hasNext() {
+                        return soleElement != null;
+                    }
+
+                    @Override
+                    public T next() {
+                        try {
+                            return soleElement;
+                        } finally {
+                            soleElement = null;
+                        }
+                    }
+                };
+            }
+        };
+    }
+
+    @Override
+    public Iterable<Path> getRootDirectories() {
+        return SoleIterable(getRoot());
+    }
+
+    @Override
+    public Iterable<FileStore> getFileStores() {
+        FileStore store;
+        try {
+            store = Files.getFileStore(root);
+        } catch (IOException ioe) {
+            store = null;
+        }
+        return SoleIterable(store);
+    }
+
+    @Override
+    public Set<String> supportedFileAttributeViews() {
+        // assume that unwrapped objects aren't exposed
+        return delegate.supportedFileAttributeViews();
+    }
+
+    @Override
+    public Path getPath(String first, String... more) {
+        return new PassThroughFileSystem.PassThroughPath(this, delegate.getPath(first, more));
+    }
+
+    @Override
+    public PathMatcher getPathMatcher(String syntaxAndPattern) {
+        final PathMatcher matcher = delegate.getPathMatcher(syntaxAndPattern);
+        return new PathMatcher() {
+            @Override
+            public boolean matches(Path path) {
+                return matcher.matches(unwrap(path));
+            }
+        };
+    }
+
+    @Override
+    public UserPrincipalLookupService getUserPrincipalLookupService() {
+        // assume that unwrapped objects aren't exposed
+        return delegate.getUserPrincipalLookupService();
+    }
+
+    @Override
+    public WatchService newWatchService() throws IOException {
+        // to keep it simple
+        throw new UnsupportedOperationException();
+    }
+
+    static class FaultyException extends IOException {
+        FaultyException() {
+            super("fault triggered.");
+        }
+    }
+
+    static class FaultyFSProvider extends FileSystemProvider {
+        private static final String SCHEME = "faulty";
+        private static volatile FaultyFileSystem delegate;
+        private static FaultyFSProvider INSTANCE = new FaultyFSProvider();
+        private boolean enabled;
+
+        private FaultyFSProvider() {}
+
+        public static FaultyFSProvider getInstance() {
+            return INSTANCE;
+        }
+
+        public void setFaultyMode(boolean enable) {
+            enabled = enable;
+        }
+
+        private void triggerEx(String filename, String... names) throws IOException {
+            if (! enabled) {
+                return;
+            }
+
+            if (filename.equals("SecurityException")) {
+                throw new SecurityException("FaultyFS", new FaultyException());
+            }
+
+            if (filename.equals("IOException")) {
+                throw new FaultyException();
+            }
+
+            for (String name: names) {
+                if (name.equals(filename)) {
+                    throw new FaultyException();
+                }
+            }
+        }
+
+        private void triggerEx(Path path, String... names) throws IOException {
+            triggerEx(path.getFileName().toString(), names);
+        }
+
+        @Override
+        public String getScheme() {
+            return SCHEME;
+        }
+
+        private void checkScheme(URI uri) {
+            if (!uri.getScheme().equalsIgnoreCase(SCHEME))
+                throw new IllegalArgumentException();
+        }
+
+        private void checkUri(URI uri) {
+            checkScheme(uri);
+            if (!uri.getSchemeSpecificPart().equals("///"))
+                throw new IllegalArgumentException();
+        }
+
+        @Override
+        public FileSystem newFileSystem(Path fakeRoot, Map<String,?> env)
+            throws IOException
+        {
+            if (env != null && env.keySet().contains("IOException")) {
+                triggerEx("IOException");
+            }
+
+            synchronized (FaultyFSProvider.class) {
+                if (delegate != null && delegate.isOpen())
+                    throw new FileSystemAlreadyExistsException();
+                FaultyFileSystem result = new FaultyFileSystem(fakeRoot);
+                delegate = result;
+                return result;
+            }
+        }
+
+        @Override
+        public FileSystem newFileSystem(URI uri, Map<String,?> env)
+            throws IOException
+        {
+            if (env != null && env.keySet().contains("IOException")) {
+                triggerEx("IOException");
+            }
+
+            checkUri(uri);
+            synchronized (FaultyFSProvider.class) {
+                if (delegate != null && delegate.isOpen())
+                    throw new FileSystemAlreadyExistsException();
+                FaultyFileSystem result = new FaultyFileSystem(null);
+                delegate = result;
+                return result;
+            }
+        }
+
+        @Override
+        public FileSystem getFileSystem(URI uri) {
+            checkUri(uri);
+            FileSystem result = delegate;
+            if (result == null)
+                throw new FileSystemNotFoundException();
+            return result;
+        }
+
+        @Override
+        public Path getPath(URI uri) {
+            checkScheme(uri);
+            if (delegate == null)
+                throw new FileSystemNotFoundException();
+
+            // only allow absolute path
+            String path = uri.getSchemeSpecificPart();
+            if (! path.startsWith("///")) {
+                throw new IllegalArgumentException();
+            }
+            return new PassThroughFileSystem.PassThroughPath(delegate, delegate.root.resolve(path.substring(3)));
+        }
+
+        @Override
+        public void setAttribute(Path file, String attribute, Object value, LinkOption... options)
+            throws IOException
+        {
+            triggerEx(file, "setAttribute");
+            Files.setAttribute(unwrap(file), attribute, value, options);
+        }
+
+        @Override
+        public Map<String,Object> readAttributes(Path file, String attributes, LinkOption... options)
+            throws IOException
+        {
+            triggerEx(file, "readAttributes");
+            return Files.readAttributes(unwrap(file), attributes, options);
+        }
+
+        @Override
+        public <V extends FileAttributeView> V getFileAttributeView(Path file,
+                                                                    Class<V> type,
+                                                                    LinkOption... options)
+        {
+            return Files.getFileAttributeView(unwrap(file), type, options);
+        }
+
+        @Override
+        public <A extends BasicFileAttributes> A readAttributes(Path file,
+                                                                Class<A> type,
+                                                                LinkOption... options)
+            throws IOException
+        {
+            triggerEx(file, "readAttributes");
+            return Files.readAttributes(unwrap(file), type, options);
+        }
+
+        @Override
+        public void delete(Path file) throws IOException {
+            triggerEx(file, "delete");
+            Files.delete(unwrap(file));
+        }
+
+        @Override
+        public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs)
+            throws IOException
+        {
+            triggerEx(target, "createSymbolicLink");
+            Files.createSymbolicLink(unwrap(link), unwrap(target), attrs);
+        }
+
+        @Override
+        public void createLink(Path link, Path existing) throws IOException {
+            triggerEx(existing, "createLink");
+            Files.createLink(unwrap(link), unwrap(existing));
+        }
+
+        @Override
+        public Path readSymbolicLink(Path link) throws IOException {
+            Path target = Files.readSymbolicLink(unwrap(link));
+            triggerEx(target, "readSymbolicLink");
+            return new PassThroughFileSystem.PassThroughPath(delegate, target);
+        }
+
+
+        @Override
+        public void copy(Path source, Path target, CopyOption... options) throws IOException {
+            triggerEx(source, "copy");
+            Files.copy(unwrap(source), unwrap(target), options);
+        }
+
+        @Override
+        public void move(Path source, Path target, CopyOption... options) throws IOException {
+            triggerEx(source, "move");
+            Files.move(unwrap(source), unwrap(target), options);
+        }
+
+        private DirectoryStream<Path> wrap(final DirectoryStream<Path> stream) {
+            return new DirectoryStream<Path>() {
+                @Override
+                public Iterator<Path> iterator() {
+                    final Iterator<Path> itr = stream.iterator();
+                    return new Iterator<Path>() {
+                        private Path next = null;
+                        @Override
+                        public boolean hasNext() {
+                            if (next == null) {
+                                if (itr.hasNext()) {
+                                    next = itr.next();
+                                } else {
+                                    return false;
+                                }
+                            }
+                            if (next != null) {
+                                try {
+                                    triggerEx(next, "DirectoryIteratorException");
+                                } catch (IOException ioe) {
+                                    throw new DirectoryIteratorException(ioe);
+                                } catch (SecurityException se) {
+                                    // ??? Does DS throw SecurityException during iteration?
+                                    next = null;
+                                    return hasNext();
+                                }
+                            }
+                            return (next != null);
+                        }
+                        @Override
+                        public Path next() {
+                            try {
+                                if (next != null || hasNext()) {
+                                    return new PassThroughFileSystem.PassThroughPath(delegate, next);
+                                } else {
+                                    throw new NoSuchElementException();
+                                }
+                            } finally {
+                                next = null;
+                            }
+                        }
+
+                        @Override
+                        public void remove() {
+                            itr.remove();
+                        }
+                    };
+                }
+                @Override
+                public void close() throws IOException {
+                    stream.close();
+                }
+            };
+        }
+
+        @Override
+        public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
+            throws IOException
+        {
+            triggerEx(dir, "newDirectoryStream");
+            return wrap(Files.newDirectoryStream(unwrap(dir), filter));
+        }
+
+        @Override
+        public void createDirectory(Path dir, FileAttribute<?>... attrs)
+            throws IOException
+        {
+            triggerEx(dir, "createDirectory");
+            Files.createDirectory(unwrap(dir), attrs);
+        }
+
+        @Override
+        public SeekableByteChannel newByteChannel(Path file,
+                                                  Set<? extends OpenOption> options,
+                                                  FileAttribute<?>... attrs)
+            throws IOException
+        {
+            triggerEx(file, "newByteChannel");
+            return Files.newByteChannel(unwrap(file), options, attrs);
+        }
+
+
+        @Override
+        public boolean isHidden(Path file) throws IOException {
+            triggerEx(file, "isHidden");
+            return Files.isHidden(unwrap(file));
+        }
+
+        @Override
+        public FileStore getFileStore(Path file) throws IOException {
+            triggerEx(file, "getFileStore");
+            return Files.getFileStore(unwrap(file));
+        }
+
+        @Override
+        public boolean isSameFile(Path file, Path other) throws IOException {
+            triggerEx(file, "isSameFile");
+            return Files.isSameFile(unwrap(file), unwrap(other));
+        }
+
+        @Override
+        public void checkAccess(Path file, AccessMode... modes)
+            throws IOException
+        {
+            triggerEx(file, "checkAccess");
+            // hack
+            if (modes.length == 0) {
+                if (Files.exists(unwrap(file)))
+                    return;
+                else
+                    throw new NoSuchFileException(file.toString());
+            }
+            throw new RuntimeException("not implemented yet");
+        }
+    }
+}
--- a/test/java/nio/file/Files/PassThroughFileSystem.java	Sat May 18 10:15:06 2013 +0800
+++ b/test/java/nio/file/Files/PassThroughFileSystem.java	Sat May 18 18:55:56 2013 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -304,7 +304,7 @@
         public DirectoryStream<Path> newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
             throws IOException
         {
-            return wrap(Files.newDirectoryStream(dir, filter));
+            return wrap(Files.newDirectoryStream(unwrap(dir), filter));
         }
 
         @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/file/Files/StreamTest.java	Sat May 18 18:55:56 2013 -0700
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @bug 8006884
+ * @summary Unit test for java.nio.file.Files
+ * @library ..
+ * @build PassThroughFileSystem FaultyFileSystem
+ * @run testng StreamTest
+ */
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.charset.Charset;
+import java.nio.charset.MalformedInputException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystemLoopException;
+import java.nio.file.FileVisitOption;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
+import java.util.Comparators;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.function.BiPredicate;
+import java.util.stream.CloseableStream;
+import java.util.stream.Collectors;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test(groups = "unit")
+public class StreamTest {
+    /**
+     * Default test folder
+     * testFolder - empty
+     *            - file
+     *            - dir - d1
+     *                  - f1
+     *                  - lnDir2 (../dir2)
+     *            - dir2
+     *            - linkDir (./dir)
+     *            - linkFile(./file)
+     */
+    static Path testFolder;
+    static boolean supportsLinks;
+    static Path[] level1;
+    static Path[] all;
+    static Path[] all_folowLinks;
+
+    @BeforeClass
+    void setupTestFolder() throws IOException {
+        testFolder = TestUtil.createTemporaryDirectory();
+        supportsLinks = TestUtil.supportsLinks(testFolder);
+        TreeSet<Path> set = new TreeSet<>();
+
+        // Level 1
+        Path empty = testFolder.resolve("empty");
+        Path file = testFolder.resolve("file");
+        Path dir = testFolder.resolve("dir");
+        Path dir2 = testFolder.resolve("dir2");
+        Files.createDirectory(empty);
+        Files.createFile(file);
+        Files.createDirectory(dir);
+        Files.createDirectory(dir2);
+        set.add(empty);
+        set.add(file);
+        set.add(dir);
+        set.add(dir2);
+        if (supportsLinks) {
+            Path tmp = testFolder.resolve("linkDir");
+            Files.createSymbolicLink(tmp, dir);
+            set.add(tmp);
+            tmp = testFolder.resolve("linkFile");
+            Files.createSymbolicLink(tmp, file);
+            set.add(tmp);
+        }
+        level1 = set.toArray(new Path[0]);
+
+        // Level 2
+        Path tmp = dir.resolve("d1");
+        Files.createDirectory(tmp);
+        set.add(tmp);
+        tmp = dir.resolve("f1");
+        Files.createFile(tmp);
+        set.add(tmp);
+        if (supportsLinks) {
+            tmp = dir.resolve("lnDir2");
+            Files.createSymbolicLink(tmp, dir2);
+            set.add(tmp);
+        }
+        // walk include starting folder
+        set.add(testFolder);
+        all = set.toArray(new Path[0]);
+
+        // Follow links
+        if (supportsLinks) {
+            tmp = testFolder.resolve("linkDir");
+            set.add(tmp.resolve("d1"));
+            set.add(tmp.resolve("f1"));
+            tmp = tmp.resolve("lnDir2");
+            set.add(tmp);
+        }
+        all_folowLinks = set.toArray(new Path[0]);
+    }
+
+    @AfterClass
+    void cleanupTestFolder() throws IOException {
+        TestUtil.removeAll(testFolder);
+    }
+
+    public void testBasic() {
+        try (CloseableStream<Path> s = Files.list(testFolder)) {
+            Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+            assertEquals(actual, level1);
+        } catch (IOException ioe) {
+            fail("Unexpected IOException");
+        }
+
+        try (CloseableStream<Path> s = Files.list(testFolder.resolve("empty"))) {
+            int count = s.mapToInt(p -> 1).reduce(0, Integer::sum);
+            assertEquals(count, 0, "Expect empty stream.");
+        } catch (IOException ioe) {
+            fail("Unexpected IOException");
+        }
+    }
+
+    public void testWalk() {
+        try (CloseableStream<Path> s = Files.walk(testFolder)) {
+            Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+            assertEquals(actual, all);
+        } catch (IOException ioe) {
+            fail("Unexpected IOException");
+        }
+    }
+
+    public void testWalkOneLevel() {
+        try (CloseableStream<Path> s = Files.walk(testFolder, 1)) {
+            Object[] actual = s.filter(path -> ! path.equals(testFolder))
+                               .sorted(Comparators.naturalOrder())
+                               .toArray();
+            assertEquals(actual, level1);
+        } catch (IOException ioe) {
+            fail("Unexpected IOException");
+        }
+    }
+
+    public void testWalkFollowLink() {
+        // If link is not supported, the directory structure won't have link.
+        // We still want to test the behavior with FOLLOW_LINKS option.
+        try (CloseableStream<Path> s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) {
+            Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+            assertEquals(actual, all_folowLinks);
+        } catch (IOException ioe) {
+            fail("Unexpected IOException");
+        }
+    }
+
+    private void validateFileSystemLoopException(Path start, Path... causes) {
+        try (CloseableStream<Path> s = Files.walk(start, FileVisitOption.FOLLOW_LINKS)) {
+            try {
+                int count = s.mapToInt(p -> 1).reduce(0, Integer::sum);
+                fail("Should got FileSystemLoopException, but got " + count + "elements.");
+            } catch (UncheckedIOException uioe) {
+                IOException ioe = uioe.getCause();
+                if (ioe instanceof FileSystemLoopException) {
+                    FileSystemLoopException fsle = (FileSystemLoopException) ioe;
+                    boolean match = false;
+                    for (Path cause: causes) {
+                        if (fsle.getFile().equals(cause.toString())) {
+                            match = true;
+                            break;
+                        }
+                    }
+                    assertTrue(match);
+                } else {
+                    fail("Unexpected UncheckedIOException cause " + ioe.toString());
+                }
+            }
+        } catch(IOException ex) {
+            fail("Unexpected IOException " + ex);
+        }
+    }
+
+    public void testWalkFollowLinkLoop() {
+        if (!supportsLinks) {
+            return;
+        }
+
+        // Loops.
+        try {
+            Path dir = testFolder.resolve("dir");
+            Path linkdir = testFolder.resolve("linkDir");
+            Path d1 = dir.resolve("d1");
+            Path cause = d1.resolve("lnSelf");
+            Files.createSymbolicLink(cause, d1);
+
+            // loop in descendant.
+            validateFileSystemLoopException(dir, cause);
+            // loop in self
+            validateFileSystemLoopException(d1, cause);
+            // start from other place via link
+            validateFileSystemLoopException(linkdir,
+                    linkdir.resolve(Paths.get("d1", "lnSelf")));
+            Files.delete(cause);
+
+            // loop to parent.
+            cause = d1.resolve("lnParent");
+            Files.createSymbolicLink(cause, dir);
+
+            // loop should be detected at test/dir/d1/lnParent/d1
+            validateFileSystemLoopException(d1, cause.resolve("d1"));
+            // loop should be detected at link
+            validateFileSystemLoopException(dir, cause);
+            // loop should be detected at test/linkdir/d1/lnParent
+            // which is test/dir we have visited via test/linkdir
+            validateFileSystemLoopException(linkdir,
+                    linkdir.resolve(Paths.get("d1", "lnParent")));
+            Files.delete(cause);
+
+            // cross loop
+            Path dir2 = testFolder.resolve("dir2");
+            cause = dir2.resolve("lnDir");
+            Files.createSymbolicLink(cause, dir);
+            validateFileSystemLoopException(dir,
+                    dir.resolve(Paths.get("lnDir2", "lnDir")));
+            validateFileSystemLoopException(dir2,
+                    dir2.resolve(Paths.get("lnDir", "lnDir2")));
+            validateFileSystemLoopException(linkdir,
+                    linkdir.resolve(Paths.get("lnDir2", "lnDir")));
+        } catch(IOException ioe) {
+            fail("Unexpected IOException " + ioe);
+        }
+    }
+
+    private static class PathBiPredicate implements BiPredicate<Path, BasicFileAttributes> {
+        private final BiPredicate<Path, BasicFileAttributes> pred;
+        private final Set<Path> visited = new TreeSet<Path>();
+
+        PathBiPredicate(BiPredicate<Path, BasicFileAttributes> pred) {
+            this.pred = Objects.requireNonNull(pred);
+        }
+
+        public boolean test(Path path, BasicFileAttributes attrs) {
+            visited.add(path);
+            return pred.test(path, attrs);
+        }
+
+        public Path[] visited() {
+            return visited.toArray(new Path[0]);
+        }
+    }
+
+    public void testFind() throws IOException {
+        PathBiPredicate pred = new PathBiPredicate((path, attrs) -> true);
+
+        try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
+            Set<Path> result = s.collect(Collectors.toCollection(TreeSet::new));
+            assertEquals(pred.visited(), all);
+            assertEquals(result.toArray(new Path[0]), pred.visited());
+        }
+
+        pred = new PathBiPredicate((path, attrs) -> attrs.isSymbolicLink());
+        try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
+            s.forEach(path -> assertTrue(Files.isSymbolicLink(path)));
+            assertEquals(pred.visited(), all);
+        }
+
+        pred = new PathBiPredicate((path, attrs) ->
+            path.getFileName().toString().startsWith("e"));
+        try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
+            s.forEach(path -> assertEquals(path.getFileName().toString(), "empty"));
+            assertEquals(pred.visited(), all);
+        }
+
+        pred = new PathBiPredicate((path, attrs) ->
+            path.getFileName().toString().startsWith("l") && attrs.isRegularFile());
+        try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, pred)) {
+            s.forEach(path -> fail("Expect empty stream"));
+            assertEquals(pred.visited(), all);
+        }
+    }
+
+    // Test borrowed from BytesAndLines
+    public void testLines() throws IOException {
+        final Charset US_ASCII = Charset.forName("US-ASCII");
+        Path tmpfile = Files.createTempFile("blah", "txt");
+
+        try {
+            // zero lines
+            assertTrue(Files.size(tmpfile) == 0, "File should be empty");
+            try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) {
+                assertEquals(s.mapToInt(l -> 1).reduce(0, Integer::sum), 0, "No line expected");
+            }
+
+            // one line
+            byte[] hi = { (byte)'h', (byte)'i' };
+            Files.write(tmpfile, hi);
+            try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) {
+                List<String> lines = s.collect(Collectors.toList());
+                assertTrue(lines.size() == 1, "One line expected");
+                assertTrue(lines.get(0).equals("hi"), "'Hi' expected");
+            }
+
+            // two lines using platform's line separator
+            List<String> expected = Arrays.asList("hi", "there");
+            Files.write(tmpfile, expected, US_ASCII);
+            assertTrue(Files.size(tmpfile) > 0, "File is empty");
+            try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) {
+                List<String> lines = s.collect(Collectors.toList());
+                assertTrue(lines.equals(expected), "Unexpected lines");
+            }
+
+            // MalformedInputException
+            byte[] bad = { (byte)0xff, (byte)0xff };
+            Files.write(tmpfile, bad);
+            try (CloseableStream<String> s = Files.lines(tmpfile, US_ASCII)) {
+                try {
+                    List<String> lines = s.collect(Collectors.toList());
+                    throw new RuntimeException("UncheckedIOException expected");
+                } catch (UncheckedIOException ex) {
+                    assertTrue(ex.getCause() instanceof MalformedInputException,
+                               "MalformedInputException expected");
+                }
+            }
+
+            // NullPointerException
+            try {
+                Files.lines(null, US_ASCII);
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException ignore) { }
+            try {
+                Files.lines(tmpfile, null);
+                throw new RuntimeException("NullPointerException expected");
+            } catch (NullPointerException ignore) { }
+
+        } finally {
+            Files.delete(tmpfile);
+        }
+    }
+
+    public void testDirectoryIteratorException() throws IOException {
+        Path dir = testFolder.resolve("dir2");
+        Path trigger = dir.resolve("DirectoryIteratorException");
+        Files.createFile(trigger);
+        FaultyFileSystem.FaultyFSProvider fsp = FaultyFileSystem.FaultyFSProvider.getInstance();
+        FaultyFileSystem fs = (FaultyFileSystem) fsp.newFileSystem(dir, null);
+
+        try {
+            fsp.setFaultyMode(false);
+            Path fakeRoot = fs.getRoot();
+            try {
+                try (CloseableStream<Path> s = Files.list(fakeRoot)) {
+                    s.forEach(path -> assertEquals(path.getFileName().toString(), "DirectoryIteratorException"));
+                }
+            } catch (UncheckedIOException uioe) {
+                fail("Unexpected exception.");
+            }
+
+            fsp.setFaultyMode(true);
+            try {
+                try (DirectoryStream<Path> ds = Files.newDirectoryStream(fakeRoot)) {
+                    Iterator<Path> itor = ds.iterator();
+                    while (itor.hasNext()) {
+                        itor.next();
+                    }
+                }
+                fail("Shoule throw DirectoryIteratorException");
+            } catch (DirectoryIteratorException die) {
+            }
+
+            try {
+                try (CloseableStream<Path> s = Files.list(fakeRoot)) {
+                    s.forEach(path -> fail("should not get here"));
+                }
+            } catch (UncheckedIOException uioe) {
+                assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException);
+            } catch (DirectoryIteratorException die) {
+                fail("Should have been converted into UncheckedIOException.");
+            }
+        } finally {
+            // Cleanup
+            if (fs != null) {
+                fs.close();
+            }
+            Files.delete(trigger);
+        }
+    }
+
+    public void testUncheckedIOException() throws IOException {
+        Path triggerFile = testFolder.resolve(Paths.get("dir2", "IOException"));
+        Files.createFile(triggerFile);
+        Path triggerDir = testFolder.resolve(Paths.get("empty", "IOException"));
+        Files.createDirectories(triggerDir);
+        Files.createFile(triggerDir.resolve("file"));
+        FaultyFileSystem.FaultyFSProvider fsp = FaultyFileSystem.FaultyFSProvider.getInstance();
+        FaultyFileSystem fs = (FaultyFileSystem) fsp.newFileSystem(testFolder, null);
+
+        try {
+            fsp.setFaultyMode(false);
+            Path fakeRoot = fs.getRoot();
+            try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir2"))) {
+                // only one file
+                s.forEach(path -> assertEquals(path.getFileName().toString(), "IOException"));
+            }
+
+            try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("empty"))) {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                // ordered as depth-first
+                assertEquals(result, new String[] { "empty", "IOException", "file"});
+            }
+
+            fsp.setFaultyMode(true);
+            try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir2"))) {
+                s.forEach(path -> fail("should have caused exception"));
+            } catch (UncheckedIOException uioe) {
+                assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException);
+            }
+
+            try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("empty"))) {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                fail("should not reach here due to IOException");
+            } catch (UncheckedIOException uioe) {
+                assertTrue(uioe.getCause() instanceof FaultyFileSystem.FaultyException);
+            }
+
+            try (CloseableStream<Path> s = Files.walk(
+                fakeRoot.resolve("empty").resolve("IOException")))
+            {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                fail("should not reach here due to IOException");
+            } catch (IOException ioe) {
+                assertTrue(ioe instanceof FaultyFileSystem.FaultyException);
+            } catch (UncheckedIOException ex) {
+                fail("Top level should be repored as is");
+            }
+         } finally {
+            // Cleanup
+            if (fs != null) {
+                fs.close();
+            }
+            Files.delete(triggerFile);
+            TestUtil.removeAll(triggerDir);
+        }
+    }
+
+    public void testSecurityException() throws IOException {
+        Path triggerFile = testFolder.resolve(Paths.get("dir", "SecurityException"));
+        Files.createFile(triggerFile);
+        Path sampleFile = testFolder.resolve(Paths.get("dir", "sample"));
+        Files.createFile(sampleFile);
+        Path triggerDir = testFolder.resolve(Paths.get("dir2", "SecurityException"));
+        Files.createDirectories(triggerDir);
+        Files.createFile(triggerDir.resolve("fileInSE"));
+        Path sample = testFolder.resolve(Paths.get("dir2", "file"));
+        Files.createFile(sample);
+        FaultyFileSystem.FaultyFSProvider fsp = FaultyFileSystem.FaultyFSProvider.getInstance();
+        FaultyFileSystem fs = (FaultyFileSystem) fsp.newFileSystem(testFolder, null);
+
+        try {
+            fsp.setFaultyMode(false);
+            Path fakeRoot = fs.getRoot();
+            // validate setting
+            try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir"))) {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                assertEqualsNoOrder(result, new String[] { "d1","f1", "lnDir2", "SecurityException", "sample" });
+            }
+
+            try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                assertEqualsNoOrder(result, new String[] { "dir2", "SecurityException", "fileInSE", "file" });
+            }
+
+            // execute test
+            fsp.setFaultyMode(true);
+            // ignore file cause SecurityException
+            try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir"))) {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                assertEqualsNoOrder(result, new String[] { "dir", "d1","f1", "lnDir2", "sample" });
+            }
+            // skip folder cause SecurityException
+            try (CloseableStream<Path> s = Files.walk(fakeRoot.resolve("dir2"))) {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                assertEqualsNoOrder(result, new String[] { "dir2", "file" });
+            }
+
+            // list instead of walk
+            try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir"))) {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                assertEqualsNoOrder(result, new String[] { "d1","f1", "lnDir2", "sample" });
+            }
+            try (CloseableStream<Path> s = Files.list(fakeRoot.resolve("dir2"))) {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                assertEqualsNoOrder(result, new String[] { "file" });
+            }
+
+            // root cause SecurityException should be reported
+            try (CloseableStream<Path> s = Files.walk(
+                fakeRoot.resolve("dir2").resolve("SecurityException")))
+            {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                fail("should not reach here due to SecurityException");
+            } catch (SecurityException se) {
+                assertTrue(se.getCause() instanceof FaultyFileSystem.FaultyException);
+            }
+
+            // Walk a file cause SecurityException, we should get SE
+            try (CloseableStream<Path> s = Files.walk(
+                fakeRoot.resolve("dir").resolve("SecurityException")))
+            {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                fail("should not reach here due to SecurityException");
+            } catch (SecurityException se) {
+                assertTrue(se.getCause() instanceof FaultyFileSystem.FaultyException);
+            }
+
+            // List a file cause SecurityException, we should get SE as cannot read attribute
+            try (CloseableStream<Path> s = Files.list(
+                fakeRoot.resolve("dir2").resolve("SecurityException")))
+            {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                fail("should not reach here due to SecurityException");
+            } catch (SecurityException se) {
+                assertTrue(se.getCause() instanceof FaultyFileSystem.FaultyException);
+            }
+
+            try (CloseableStream<Path> s = Files.list(
+                fakeRoot.resolve("dir").resolve("SecurityException")))
+            {
+                String[] result = s.map(path -> path.getFileName().toString())
+                                   .toArray(String[]::new);
+                fail("should not reach here due to SecurityException");
+            } catch (SecurityException se) {
+                assertTrue(se.getCause() instanceof FaultyFileSystem.FaultyException);
+            }
+         } finally {
+            // Cleanup
+            if (fs != null) {
+                fs.close();
+            }
+            Files.delete(triggerFile);
+            Files.delete(sampleFile);
+            Files.delete(sample);
+            TestUtil.removeAll(triggerDir);
+        }
+    }
+
+    public void testConstructException() {
+        try (CloseableStream<String> s = Files.lines(testFolder.resolve("notExist"), Charset.forName("UTF-8"))) {
+            s.forEach(l -> fail("File is not even exist!"));
+        } catch (IOException ioe) {
+            ioe.printStackTrace(System.err);
+            assertTrue(ioe instanceof NoSuchFileException);
+        }
+    }
+
+    public void testClosedStream() throws IOException {
+        try (CloseableStream<Path> s = Files.list(testFolder)) {
+            s.close();
+            Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+            assertTrue(actual.length <= level1.length);
+        }
+
+        try (CloseableStream<Path> s = Files.walk(testFolder)) {
+            s.close();
+            Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+            fail("Operate on closed stream should throw IllegalStateException");
+        } catch (IllegalStateException ex) {
+            // expected
+        }
+
+        try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE,
+                    (p, attr) -> true)) {
+            s.close();
+            Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+            fail("Operate on closed stream should throw IllegalStateException");
+        } catch (IllegalStateException ex) {
+            // expected
+        }
+    }
+}