You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2021/09/26 14:59:03 UTC

[commons-io] 02/03: Sort members.

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git

commit 73c3f9878622204982962cfb492a74c563e8903c
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sun Sep 26 10:38:00 2021 -0400

    Sort members.
---
 .../java/org/apache/commons/io/ByteOrderMark.java  |  64 +--
 .../org/apache/commons/io/ByteOrderParser.java     |  12 +-
 src/main/java/org/apache/commons/io/Charsets.java  |  76 ++--
 src/main/java/org/apache/commons/io/CopyUtils.java |  82 ++--
 .../org/apache/commons/io/DirectoryWalker.java     | 372 ++++++++---------
 .../java/org/apache/commons/io/EndianUtils.java    | 462 ++++++++++-----------
 .../java/org/apache/commons/io/FileCleaner.java    | 104 ++---
 .../org/apache/commons/io/FileCleaningTracker.java | 294 ++++++-------
 .../org/apache/commons/io/FileExistsException.java |  16 +-
 .../org/apache/commons/io/FileSystemUtils.java     | 272 ++++++------
 .../java/org/apache/commons/io/FilenameUtils.java  |  32 +-
 src/main/java/org/apache/commons/io/HexDump.java   |  52 +--
 src/main/java/org/apache/commons/io/IOCase.java    | 160 +++----
 .../org/apache/commons/io/IOIndexedException.java  |  26 +-
 .../java/org/apache/commons/io/LineIterator.java   |  58 +--
 .../java/org/apache/commons/io/StreamIterator.java |  22 +-
 .../org/apache/commons/io/TaggedIOException.java   |  18 +-
 .../java/org/apache/commons/io/ThreadMonitor.java  |  48 +--
 .../java/org/apache/commons/io/file/Counters.java  |  16 +-
 .../java/org/apache/commons/io/file/PathUtils.java |   8 +-
 .../commons/io/filefilter/AndFileFilter.java       |  22 +-
 .../commons/io/filefilter/FalseFileFilter.java     |  20 +-
 .../commons/io/filefilter/FileFilterUtils.java     |  54 +--
 .../commons/io/filefilter/TrueFileFilter.java      |  12 +-
 .../commons/io/filefilter/WildcardFilter.java      |  32 +-
 .../org/apache/commons/io/function/IOFunction.java | 176 ++++----
 .../commons/io/input/AutoCloseInputStream.java     |  28 +-
 .../apache/commons/io/input/BOMInputStream.java    | 152 +++----
 .../commons/io/input/BoundedInputStream.java       | 164 ++++----
 .../org/apache/commons/io/input/BoundedReader.java |  24 +-
 .../apache/commons/io/input/BrokenInputStream.java |  26 +-
 .../commons/io/input/CharSequenceInputStream.java  | 166 ++++----
 .../commons/io/input/CharSequenceReader.java       |  48 +--
 .../org/apache/commons/io/input/ClosedReader.java  |  10 +-
 .../commons/io/input/CountingInputStream.java      |  82 ++--
 .../input/MessageDigestCalculatingInputStream.java |  32 +-
 .../apache/commons/io/input/NullInputStream.java   | 120 +++---
 .../org/apache/commons/io/input/NullReader.java    | 114 ++---
 .../apache/commons/io/input/ProxyInputStream.java  | 216 +++++-----
 .../org/apache/commons/io/input/ProxyReader.java   | 206 ++++-----
 .../apache/commons/io/input/ReaderInputStream.java | 146 +++----
 .../apache/commons/io/input/TaggedInputStream.java |  22 +-
 .../org/apache/commons/io/input/TaggedReader.java  |  22 +-
 .../apache/commons/io/input/TailerListener.java    |  26 +-
 .../commons/io/input/TailerListenerAdapter.java    |  40 +-
 .../apache/commons/io/input/TeeInputStream.java    |  16 +-
 .../commons/io/input/TimestampedObserver.java      |  18 +-
 .../io/input/UnixLineEndingInputStream.java        |  78 ++--
 .../input/UnsynchronizedByteArrayInputStream.java  |  34 +-
 .../io/input/WindowsLineEndingInputStream.java     |  82 ++--
 .../commons/io/input/XmlStreamReaderException.java |  40 +-
 .../io/input/buffer/CircularBufferInputStream.java |  30 +-
 .../io/input/buffer/CircularByteBuffer.java        | 254 +++++------
 .../io/input/buffer/PeekableInputStream.java       |  14 +-
 .../commons/io/monitor/FileAlterationListener.java |  30 +-
 .../io/monitor/FileAlterationListenerAdaptor.java  |  30 +-
 .../commons/io/monitor/FileAlterationMonitor.java  |  78 ++--
 .../commons/io/monitor/FileAlterationObserver.java | 202 ++++-----
 .../org/apache/commons/io/monitor/FileEntry.java   | 234 +++++------
 .../io/output/AbstractByteArrayOutputStream.java   | 382 ++++++++---------
 .../commons/io/output/AppendableOutputStream.java  |  18 +-
 .../commons/io/output/BrokenOutputStream.java      |  20 +-
 .../org/apache/commons/io/output/BrokenWriter.java |  24 +-
 .../commons/io/output/ByteArrayOutputStream.java   | 144 +++----
 .../commons/io/output/ChunkedOutputStream.java     |  18 +-
 .../apache/commons/io/output/ChunkedWriter.java    |  16 +-
 .../commons/io/output/ClosedOutputStream.java      |  10 +-
 .../commons/io/output/CountingOutputStream.java    |  56 +--
 .../apache/commons/io/output/NullOutputStream.java |  38 +-
 .../org/apache/commons/io/output/NullWriter.java   |  42 +-
 .../commons/io/output/ProxyOutputStream.java       | 136 +++---
 .../org/apache/commons/io/output/ProxyWriter.java  | 190 ++++-----
 .../commons/io/output/StringBuilderWriter.java     |  38 +-
 .../commons/io/output/TaggedOutputStream.java      |  22 +-
 .../org/apache/commons/io/output/TaggedWriter.java |  22 +-
 .../apache/commons/io/output/TeeOutputStream.java  |  66 +--
 .../io/output/UncheckedFilterOutputStream.java     |   8 +-
 .../UnsynchronizedByteArrayOutputStream.java       | 124 +++---
 .../commons/io/output/WriterOutputStream.java      | 230 +++++-----
 .../apache/commons/io/output/XmlStreamWriter.java  |  88 ++--
 .../io/serialization/RegexpClassNameMatcher.java   |  18 +-
 .../serialization/ValidatingObjectInputStream.java | 150 +++----
 82 files changed, 3577 insertions(+), 3577 deletions(-)

diff --git a/src/main/java/org/apache/commons/io/ByteOrderMark.java b/src/main/java/org/apache/commons/io/ByteOrderMark.java
index d2fbbfd..75da194 100644
--- a/src/main/java/org/apache/commons/io/ByteOrderMark.java
+++ b/src/main/java/org/apache/commons/io/ByteOrderMark.java
@@ -88,21 +88,27 @@ public class ByteOrderMark implements Serializable {
     }
 
     /**
-     * Gets the name of the {@link java.nio.charset.Charset} the BOM represents.
-     *
-     * @return the character set name
-     */
-    public String getCharsetName() {
-        return charsetName;
-    }
-
-    /**
-     * Gets the length of the BOM's bytes.
+     * Indicates if this BOM's bytes equals another.
      *
-     * @return the length of the BOM's bytes
+     * @param obj The object to compare to
+     * @return true if the bom's bytes are equal, otherwise
+     * false
      */
-    public int length() {
-        return bytes.length;
+    @Override
+    public boolean equals(final Object obj) {
+        if (!(obj instanceof ByteOrderMark)) {
+            return false;
+        }
+        final ByteOrderMark bom = (ByteOrderMark)obj;
+        if (bytes.length != bom.length()) {
+            return false;
+        }
+        for (int i = 0; i < bytes.length; i++) {
+            if (bytes[i] != bom.get(i)) {
+                return false;
+            }
+        }
+        return true;
     }
 
     /**
@@ -129,27 +135,12 @@ public class ByteOrderMark implements Serializable {
     }
 
     /**
-     * Indicates if this BOM's bytes equals another.
+     * Gets the name of the {@link java.nio.charset.Charset} the BOM represents.
      *
-     * @param obj The object to compare to
-     * @return true if the bom's bytes are equal, otherwise
-     * false
+     * @return the character set name
      */
-    @Override
-    public boolean equals(final Object obj) {
-        if (!(obj instanceof ByteOrderMark)) {
-            return false;
-        }
-        final ByteOrderMark bom = (ByteOrderMark)obj;
-        if (bytes.length != bom.length()) {
-            return false;
-        }
-        for (int i = 0; i < bytes.length; i++) {
-            if (bytes[i] != bom.get(i)) {
-                return false;
-            }
-        }
-        return true;
+    public String getCharsetName() {
+        return charsetName;
     }
 
     /**
@@ -168,6 +159,15 @@ public class ByteOrderMark implements Serializable {
     }
 
     /**
+     * Gets the length of the BOM's bytes.
+     *
+     * @return the length of the BOM's bytes
+     */
+    public int length() {
+        return bytes.length;
+    }
+
+    /**
      * Converts this instance to a String representation of the BOM.
      *
      * @return the length of the BOM's bytes
diff --git a/src/main/java/org/apache/commons/io/ByteOrderParser.java b/src/main/java/org/apache/commons/io/ByteOrderParser.java
index a0124c0..bcf1e5c 100644
--- a/src/main/java/org/apache/commons/io/ByteOrderParser.java
+++ b/src/main/java/org/apache/commons/io/ByteOrderParser.java
@@ -27,12 +27,6 @@ import java.nio.ByteOrder;
 public final class ByteOrderParser {
 
     /**
-     * ByteOrderUtils is a static utility class, so prevent construction with a private constructor.
-     */
-    private ByteOrderParser() {
-    }
-
-    /**
      * Parses the String argument as a {@link ByteOrder}.
      * <p>
      * Returns {@code ByteOrder.LITTLE_ENDIAN} if the given value is {@code "LITTLE_ENDIAN"}.
@@ -63,4 +57,10 @@ public final class ByteOrderParser {
                  ", " + ByteOrder.BIG_ENDIAN);
     }
 
+    /**
+     * ByteOrderUtils is a static utility class, so prevent construction with a private constructor.
+     */
+    private ByteOrderParser() {
+    }
+
 }
diff --git a/src/main/java/org/apache/commons/io/Charsets.java b/src/main/java/org/apache/commons/io/Charsets.java
index ce9b347..d2ba98e 100644
--- a/src/main/java/org/apache/commons/io/Charsets.java
+++ b/src/main/java/org/apache/commons/io/Charsets.java
@@ -74,44 +74,6 @@ public class Charsets {
     }
 
     /**
-     * Constructs a sorted map from canonical charset names to charset objects required of every implementation of the
-     * Java platform.
-     * <p>
-     * From the Java documentation <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">
-     * Standard charsets</a>:
-     * </p>
-     *
-     * @return An immutable, case-insensitive map from canonical charset names to charset objects.
-     * @see Charset#availableCharsets()
-     * @since 2.5
-     */
-    public static SortedMap<String, Charset> requiredCharsets() {
-        return STANDARD_CHARSET_MAP;
-    }
-
-    /**
-     * Returns the given Charset or the default Charset if the given Charset is null.
-     *
-     * @param charset
-     *            A charset or null.
-     * @return the given Charset or the default Charset if the given Charset is null
-     */
-    public static Charset toCharset(final Charset charset) {
-        return charset == null ? Charset.defaultCharset() : charset;
-    }
-
-    /**
-     * Returns a Charset for the named charset. If the name is null, return the default Charset.
-     *
-     * @param charsetName The name of the requested charset, may be null.
-     * @return a Charset for the named charset.
-     * @throws UnsupportedCharsetException If the named charset is unavailable (unchecked exception).
-     */
-    public static Charset toCharset(final String charsetName) throws UnsupportedCharsetException {
-        return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName);
-    }
-
-    /**
      * CharEncodingISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1.
      * <p>
      * Every implementation of the Java platform is required to support this character encoding.
@@ -193,4 +155,42 @@ public class Charsets {
      */
     @Deprecated
     public static final Charset UTF_8 = StandardCharsets.UTF_8;
+
+    /**
+     * Constructs a sorted map from canonical charset names to charset objects required of every implementation of the
+     * Java platform.
+     * <p>
+     * From the Java documentation <a href="https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">
+     * Standard charsets</a>:
+     * </p>
+     *
+     * @return An immutable, case-insensitive map from canonical charset names to charset objects.
+     * @see Charset#availableCharsets()
+     * @since 2.5
+     */
+    public static SortedMap<String, Charset> requiredCharsets() {
+        return STANDARD_CHARSET_MAP;
+    }
+
+    /**
+     * Returns the given Charset or the default Charset if the given Charset is null.
+     *
+     * @param charset
+     *            A charset or null.
+     * @return the given Charset or the default Charset if the given Charset is null
+     */
+    public static Charset toCharset(final Charset charset) {
+        return charset == null ? Charset.defaultCharset() : charset;
+    }
+
+    /**
+     * Returns a Charset for the named charset. If the name is null, return the default Charset.
+     *
+     * @param charsetName The name of the requested charset, may be null.
+     * @return a Charset for the named charset.
+     * @throws UnsupportedCharsetException If the named charset is unavailable (unchecked exception).
+     */
+    public static Charset toCharset(final String charsetName) throws UnsupportedCharsetException {
+        return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName);
+    }
 }
diff --git a/src/main/java/org/apache/commons/io/CopyUtils.java b/src/main/java/org/apache/commons/io/CopyUtils.java
index 0e1ab2f..b90789c 100644
--- a/src/main/java/org/apache/commons/io/CopyUtils.java
+++ b/src/main/java/org/apache/commons/io/CopyUtils.java
@@ -114,11 +114,6 @@ import java.nio.charset.Charset;
 public class CopyUtils {
 
     /**
-     * Instances should NOT be constructed in standard programming.
-     */
-    public CopyUtils() { }
-
-    /**
      * Copies bytes from a {@code byte[]} to an {@code OutputStream}.
      * @param input the byte array to read from
      * @param output the {@code OutputStream} to write to
@@ -177,35 +172,6 @@ public class CopyUtils {
         return count;
     }
 
-    // ----------------------------------------------------------------
-    // Reader -> Writer
-    // ----------------------------------------------------------------
-
-    /**
-     * Copies chars from a {@code Reader} to a {@code Writer}.
-     * @param input the {@code Reader} to read from
-     * @param output the {@code Writer} to write to
-     * @return the number of characters copied
-     * @throws IOException In case of an I/O problem
-     */
-    public static int copy(
-            final Reader input,
-            final Writer output)
-                throws IOException {
-        final char[] buffer = IOUtils.getCharArray();
-        int count = 0;
-        int n;
-        while (EOF != (n = input.read(buffer))) {
-            output.write(buffer, 0, n);
-            count += n;
-        }
-        return count;
-    }
-
-    // ----------------------------------------------------------------
-    // InputStream -> Writer
-    // ----------------------------------------------------------------
-
     /**
      * Copies and convert bytes from an {@code InputStream} to chars on a
      * {@code Writer}.
@@ -225,6 +191,10 @@ public class CopyUtils {
         copy(in, output);
     }
 
+    // ----------------------------------------------------------------
+    // Reader -> Writer
+    // ----------------------------------------------------------------
+
     /**
      * Copies and convert bytes from an {@code InputStream} to chars on a
      * {@code Writer}, using the specified encoding.
@@ -244,9 +214,8 @@ public class CopyUtils {
         copy(in, output);
     }
 
-
     // ----------------------------------------------------------------
-    // Reader -> OutputStream
+    // InputStream -> Writer
     // ----------------------------------------------------------------
 
     /**
@@ -294,11 +263,33 @@ public class CopyUtils {
         out.flush();
     }
 
+
     // ----------------------------------------------------------------
-    // String -> OutputStream
+    // Reader -> OutputStream
     // ----------------------------------------------------------------
 
     /**
+     * Copies chars from a {@code Reader} to a {@code Writer}.
+     * @param input the {@code Reader} to read from
+     * @param output the {@code Writer} to write to
+     * @return the number of characters copied
+     * @throws IOException In case of an I/O problem
+     */
+    public static int copy(
+            final Reader input,
+            final Writer output)
+                throws IOException {
+        final char[] buffer = IOUtils.getCharArray();
+        int count = 0;
+        int n;
+        while (EOF != (n = input.read(buffer))) {
+            output.write(buffer, 0, n);
+            count += n;
+        }
+        return count;
+    }
+
+    /**
      * Serialize chars from a {@code String} to bytes on an
      * {@code OutputStream}, and
      * flush the {@code OutputStream}.
@@ -322,6 +313,10 @@ public class CopyUtils {
         out.flush();
     }
 
+    // ----------------------------------------------------------------
+    // String -> OutputStream
+    // ----------------------------------------------------------------
+
     /**
      * Serialize chars from a {@code String} to bytes on an
      * {@code OutputStream}, and
@@ -347,10 +342,6 @@ public class CopyUtils {
         out.flush();
     }
 
-    // ----------------------------------------------------------------
-    // String -> Writer
-    // ----------------------------------------------------------------
-
     /**
      * Copies chars from a {@code String} to a {@code Writer}.
      * @param input the {@code String} to read from
@@ -362,4 +353,13 @@ public class CopyUtils {
         output.write(input);
     }
 
+    // ----------------------------------------------------------------
+    // String -> Writer
+    // ----------------------------------------------------------------
+
+    /**
+     * Instances should NOT be constructed in standard programming.
+     */
+    public CopyUtils() { }
+
 }
diff --git a/src/main/java/org/apache/commons/io/DirectoryWalker.java b/src/main/java/org/apache/commons/io/DirectoryWalker.java
index 0aa9b92..ffc4fd9 100644
--- a/src/main/java/org/apache/commons/io/DirectoryWalker.java
+++ b/src/main/java/org/apache/commons/io/DirectoryWalker.java
@@ -255,9 +255,68 @@ import org.apache.commons.io.filefilter.TrueFileFilter;
 public abstract class DirectoryWalker<T> {
 
     /**
+     * CancelException is thrown in DirectoryWalker to cancel the current
+     * processing.
+     */
+    public static class CancelException extends IOException {
+
+        /** Serialization id. */
+        private static final long serialVersionUID = 1347339620135041008L;
+
+        /** The file being processed when the exception was thrown. */
+        private final File file;
+        /** The file depth when the exception was thrown. */
+        private final int depth;
+
+        /**
+         * Constructs a {@code CancelException} with
+         * the file and depth when cancellation occurred.
+         *
+         * @param file  the file when the operation was cancelled, may be null
+         * @param depth  the depth when the operation was cancelled, may be null
+         */
+        public CancelException(final File file, final int depth) {
+            this("Operation Cancelled", file, depth);
+        }
+
+        /**
+         * Constructs a {@code CancelException} with
+         * an appropriate message and the file and depth when
+         * cancellation occurred.
+         *
+         * @param message  the detail message
+         * @param file  the file when the operation was cancelled
+         * @param depth  the depth when the operation was cancelled
+         */
+        public CancelException(final String message, final File file, final int depth) {
+            super(message);
+            this.file = file;
+            this.depth = depth;
+        }
+
+        /**
+         * Returns the depth when the operation was cancelled.
+         *
+         * @return the depth when the operation was cancelled
+         */
+        public int getDepth() {
+            return depth;
+        }
+
+        /**
+         * Returns the file when the operation was cancelled.
+         *
+         * @return the file when the operation was cancelled
+         */
+        public File getFile() {
+            return file;
+        }
+    }
+    /**
      * The file filter to use to filter files and directories.
      */
     private final FileFilter filter;
+
     /**
      * The limit on the directory depth to walk.
      */
@@ -317,70 +376,6 @@ public abstract class DirectoryWalker<T> {
     }
 
     /**
-     * Internal method that walks the directory hierarchy in a depth-first manner.
-     * <p>
-     * Users of this class do not need to call this method. This method will
-     * be called automatically by another (public) method on the specific subclass.
-     * </p>
-     * <p>
-     * Writers of subclasses should call this method to start the directory walk.
-     * Once called, this method will emit events as it walks the hierarchy.
-     * The event methods have the prefix {@code handle}.
-     * </p>
-     *
-     * @param startDirectory  the directory to start from, not null
-     * @param results  the collection of result objects, may be updated
-     * @throws NullPointerException if the start directory is null
-     * @throws IOException if an I/O Error occurs
-     */
-    protected final void walk(final File startDirectory, final Collection<T> results) throws IOException {
-        Objects.requireNonNull(startDirectory, "startDirectory");
-        try {
-            handleStart(startDirectory, results);
-            walk(startDirectory, 0, results);
-            handleEnd(results);
-        } catch(final CancelException cancel) {
-            handleCancelled(startDirectory, results, cancel);
-        }
-    }
-
-    /**
-     * Main recursive method to examine the directory hierarchy.
-     *
-     * @param directory  the directory to examine, not null
-     * @param depth  the directory level (starting directory = 0)
-     * @param results  the collection of result objects, may be updated
-     * @throws IOException if an I/O Error occurs
-     */
-    private void walk(final File directory, final int depth, final Collection<T> results) throws IOException {
-        checkIfCancelled(directory, depth, results);
-        if (handleDirectory(directory, depth, results)) {
-            handleDirectoryStart(directory, depth, results);
-            final int childDepth = depth + 1;
-            if (depthLimit < 0 || childDepth <= depthLimit) {
-                checkIfCancelled(directory, depth, results);
-                File[] childFiles = filter == null ? directory.listFiles() : directory.listFiles(filter);
-                childFiles = filterDirectoryContents(directory, depth, childFiles);
-                if (childFiles == null) {
-                    handleRestricted(directory, childDepth, results);
-                } else {
-                    for (final File childFile : childFiles) {
-                        if (childFile.isDirectory()) {
-                            walk(childFile, childDepth, results);
-                        } else {
-                            checkIfCancelled(childFile, childDepth, results);
-                            handleFile(childFile, childDepth, results);
-                            checkIfCancelled(childFile, childDepth, results);
-                        }
-                    }
-                }
-            }
-            handleDirectoryEnd(directory, depth, results);
-        }
-        checkIfCancelled(directory, depth, results);
-    }
-
-    /**
      * Checks whether the walk has been cancelled by calling {@link #handleIsCancelled},
      * throwing a {@code CancelException} if it has.
      * <p>
@@ -403,48 +398,22 @@ public abstract class DirectoryWalker<T> {
     }
 
     /**
-     * Overridable callback method invoked to determine if the entire walk
-     * operation should be immediately cancelled.
-     * <p>
-     * This method should be implemented by those subclasses that want to
-     * provide a public {@code cancel()} method available from another
-     * thread. The design pattern for the subclass should be as follows:
-     * </p>
-     * <pre>
-     *  public class FooDirectoryWalker extends DirectoryWalker {
-     *    private volatile boolean cancelled = false;
-     *
-     *    public void cancel() {
-     *        cancelled = true;
-     *    }
-     *    private void handleIsCancelled(File file, int depth, Collection results) {
-     *        return cancelled;
-     *    }
-     *    protected void handleCancelled(File startDirectory,
-     *              Collection results, CancelException cancel) {
-     *        // implement processing required when a cancellation occurs
-     *    }
-     *  }
-     * </pre>
-     * <p>
-     * If this method returns true, then the directory walk is immediately
-     * cancelled. The next callback method will be {@link #handleCancelled}.
-     * </p>
+     * Overridable callback method invoked with the contents of each directory.
      * <p>
-     * This implementation returns false.
+     * This implementation returns the files unchanged
      * </p>
      *
-     * @param file  the file or directory being processed
+     * @param directory  the current directory being processed
      * @param depth  the current directory level (starting directory = 0)
-     * @param results  the collection of result objects, may be updated
-     * @return true if the walk has been cancelled
+     * @param files the files (possibly filtered) in the directory, may be {@code null}
+     * @return the filtered list of files
      * @throws IOException if an I/O Error occurs
+     * @since 2.0
      */
     @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected boolean handleIsCancelled(
-            final File file, final int depth, final Collection<T> results) throws IOException {
-        // do nothing - overridable by subclass
-        return false;  // not cancelled
+    protected File[] filterDirectoryContents(final File directory, final int depth, final File... files) throws
+            IOException {
+        return files;
     }
 
     /**
@@ -468,21 +437,6 @@ public abstract class DirectoryWalker<T> {
     }
 
     /**
-     * Overridable callback method invoked at the start of processing.
-     * <p>
-     * This implementation does nothing.
-     * </p>
-     *
-     * @param startDirectory  the directory to start from
-     * @param results  the collection of result objects, may be updated
-     * @throws IOException if an I/O Error occurs
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void handleStart(final File startDirectory, final Collection<T> results) throws IOException {
-        // do nothing - overridable by subclass
-    }
-
-    /**
      * Overridable callback method invoked to determine if a directory should be processed.
      * <p>
      * This method returns a boolean to indicate if the directory should be examined or not.
@@ -507,39 +461,51 @@ public abstract class DirectoryWalker<T> {
     }
 
     /**
-     * Overridable callback method invoked at the start of processing each directory.
+     * Overridable callback method invoked at the end of processing each directory.
      * <p>
      * This implementation does nothing.
      * </p>
      *
-     * @param directory  the current directory being processed
+     * @param directory  the directory being processed
      * @param depth  the current directory level (starting directory = 0)
      * @param results  the collection of result objects, may be updated
      * @throws IOException if an I/O Error occurs
      */
     @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void handleDirectoryStart(final File directory, final int depth, final Collection<T> results) throws
+    protected void handleDirectoryEnd(final File directory, final int depth, final Collection<T> results) throws
             IOException {
         // do nothing - overridable by subclass
     }
 
     /**
-     * Overridable callback method invoked with the contents of each directory.
+     * Overridable callback method invoked at the start of processing each directory.
      * <p>
-     * This implementation returns the files unchanged
+     * This implementation does nothing.
      * </p>
      *
      * @param directory  the current directory being processed
      * @param depth  the current directory level (starting directory = 0)
-     * @param files the files (possibly filtered) in the directory, may be {@code null}
-     * @return the filtered list of files
+     * @param results  the collection of result objects, may be updated
      * @throws IOException if an I/O Error occurs
-     * @since 2.0
      */
     @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected File[] filterDirectoryContents(final File directory, final int depth, final File... files) throws
+    protected void handleDirectoryStart(final File directory, final int depth, final Collection<T> results) throws
             IOException {
-        return files;
+        // do nothing - overridable by subclass
+    }
+
+    /**
+     * Overridable callback method invoked at the end of processing.
+     * <p>
+     * This implementation does nothing.
+     * </p>
+     *
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    protected void handleEnd(final Collection<T> results) throws IOException {
+        // do nothing - overridable by subclass
     }
 
     /**
@@ -559,109 +525,143 @@ public abstract class DirectoryWalker<T> {
     }
 
     /**
-     * Overridable callback method invoked for each restricted directory.
+     * Overridable callback method invoked to determine if the entire walk
+     * operation should be immediately cancelled.
      * <p>
-     * This implementation does nothing.
+     * This method should be implemented by those subclasses that want to
+     * provide a public {@code cancel()} method available from another
+     * thread. The design pattern for the subclass should be as follows:
      * </p>
+     * <pre>
+     *  public class FooDirectoryWalker extends DirectoryWalker {
+     *    private volatile boolean cancelled = false;
      *
-     * @param directory  the restricted directory
+     *    public void cancel() {
+     *        cancelled = true;
+     *    }
+     *    private void handleIsCancelled(File file, int depth, Collection results) {
+     *        return cancelled;
+     *    }
+     *    protected void handleCancelled(File startDirectory,
+     *              Collection results, CancelException cancel) {
+     *        // implement processing required when a cancellation occurs
+     *    }
+     *  }
+     * </pre>
+     * <p>
+     * If this method returns true, then the directory walk is immediately
+     * cancelled. The next callback method will be {@link #handleCancelled}.
+     * </p>
+     * <p>
+     * This implementation returns false.
+     * </p>
+     *
+     * @param file  the file or directory being processed
      * @param depth  the current directory level (starting directory = 0)
      * @param results  the collection of result objects, may be updated
+     * @return true if the walk has been cancelled
      * @throws IOException if an I/O Error occurs
      */
     @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void handleRestricted(final File directory, final int depth, final Collection<T> results) throws
-            IOException {
+    protected boolean handleIsCancelled(
+            final File file, final int depth, final Collection<T> results) throws IOException {
         // do nothing - overridable by subclass
+        return false;  // not cancelled
     }
 
     /**
-     * Overridable callback method invoked at the end of processing each directory.
+     * Overridable callback method invoked for each restricted directory.
      * <p>
      * This implementation does nothing.
      * </p>
      *
-     * @param directory  the directory being processed
+     * @param directory  the restricted directory
      * @param depth  the current directory level (starting directory = 0)
      * @param results  the collection of result objects, may be updated
      * @throws IOException if an I/O Error occurs
      */
     @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void handleDirectoryEnd(final File directory, final int depth, final Collection<T> results) throws
+    protected void handleRestricted(final File directory, final int depth, final Collection<T> results) throws
             IOException {
         // do nothing - overridable by subclass
     }
 
     /**
-     * Overridable callback method invoked at the end of processing.
+     * Overridable callback method invoked at the start of processing.
      * <p>
      * This implementation does nothing.
      * </p>
      *
+     * @param startDirectory  the directory to start from
      * @param results  the collection of result objects, may be updated
      * @throws IOException if an I/O Error occurs
      */
     @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void handleEnd(final Collection<T> results) throws IOException {
+    protected void handleStart(final File startDirectory, final Collection<T> results) throws IOException {
         // do nothing - overridable by subclass
     }
 
     /**
-     * CancelException is thrown in DirectoryWalker to cancel the current
-     * processing.
+     * Internal method that walks the directory hierarchy in a depth-first manner.
+     * <p>
+     * Users of this class do not need to call this method. This method will
+     * be called automatically by another (public) method on the specific subclass.
+     * </p>
+     * <p>
+     * Writers of subclasses should call this method to start the directory walk.
+     * Once called, this method will emit events as it walks the hierarchy.
+     * The event methods have the prefix {@code handle}.
+     * </p>
+     *
+     * @param startDirectory  the directory to start from, not null
+     * @param results  the collection of result objects, may be updated
+     * @throws NullPointerException if the start directory is null
+     * @throws IOException if an I/O Error occurs
      */
-    public static class CancelException extends IOException {
-
-        /** Serialization id. */
-        private static final long serialVersionUID = 1347339620135041008L;
-
-        /** The file being processed when the exception was thrown. */
-        private final File file;
-        /** The file depth when the exception was thrown. */
-        private final int depth;
-
-        /**
-         * Constructs a {@code CancelException} with
-         * the file and depth when cancellation occurred.
-         *
-         * @param file  the file when the operation was cancelled, may be null
-         * @param depth  the depth when the operation was cancelled, may be null
-         */
-        public CancelException(final File file, final int depth) {
-            this("Operation Cancelled", file, depth);
-        }
-
-        /**
-         * Constructs a {@code CancelException} with
-         * an appropriate message and the file and depth when
-         * cancellation occurred.
-         *
-         * @param message  the detail message
-         * @param file  the file when the operation was cancelled
-         * @param depth  the depth when the operation was cancelled
-         */
-        public CancelException(final String message, final File file, final int depth) {
-            super(message);
-            this.file = file;
-            this.depth = depth;
-        }
-
-        /**
-         * Returns the file when the operation was cancelled.
-         *
-         * @return the file when the operation was cancelled
-         */
-        public File getFile() {
-            return file;
+    protected final void walk(final File startDirectory, final Collection<T> results) throws IOException {
+        Objects.requireNonNull(startDirectory, "startDirectory");
+        try {
+            handleStart(startDirectory, results);
+            walk(startDirectory, 0, results);
+            handleEnd(results);
+        } catch(final CancelException cancel) {
+            handleCancelled(startDirectory, results, cancel);
         }
+    }
 
-        /**
-         * Returns the depth when the operation was cancelled.
-         *
-         * @return the depth when the operation was cancelled
-         */
-        public int getDepth() {
-            return depth;
+    /**
+     * Main recursive method to examine the directory hierarchy.
+     *
+     * @param directory  the directory to examine, not null
+     * @param depth  the directory level (starting directory = 0)
+     * @param results  the collection of result objects, may be updated
+     * @throws IOException if an I/O Error occurs
+     */
+    private void walk(final File directory, final int depth, final Collection<T> results) throws IOException {
+        checkIfCancelled(directory, depth, results);
+        if (handleDirectory(directory, depth, results)) {
+            handleDirectoryStart(directory, depth, results);
+            final int childDepth = depth + 1;
+            if (depthLimit < 0 || childDepth <= depthLimit) {
+                checkIfCancelled(directory, depth, results);
+                File[] childFiles = filter == null ? directory.listFiles() : directory.listFiles(filter);
+                childFiles = filterDirectoryContents(directory, depth, childFiles);
+                if (childFiles == null) {
+                    handleRestricted(directory, childDepth, results);
+                } else {
+                    for (final File childFile : childFiles) {
+                        if (childFile.isDirectory()) {
+                            walk(childFile, childDepth, results);
+                        } else {
+                            checkIfCancelled(childFile, childDepth, results);
+                            handleFile(childFile, childDepth, results);
+                            checkIfCancelled(childFile, childDepth, results);
+                        }
+                    }
+                }
+            }
+            handleDirectoryEnd(directory, depth, results);
         }
+        checkIfCancelled(directory, depth, results);
     }
 }
diff --git a/src/main/java/org/apache/commons/io/EndianUtils.java b/src/main/java/org/apache/commons/io/EndianUtils.java
index 849f86b..e2e8e12 100644
--- a/src/main/java/org/apache/commons/io/EndianUtils.java
+++ b/src/main/java/org/apache/commons/io/EndianUtils.java
@@ -40,136 +40,148 @@ import java.io.OutputStream;
 public class EndianUtils {
 
     /**
-     * Instances should NOT be constructed in standard programming.
+     * Reads the next byte from the input stream.
+     * @param input  the stream
+     * @return the byte
+     * @throws IOException if the end of file is reached
      */
-    public EndianUtils() {
+    private static int read(final InputStream input) throws IOException {
+        final int value = input.read();
+
+        if (EOF == value) {
+            throw new EOFException("Unexpected EOF reached");
+        }
+
+        return value;
     }
 
     // ========================================== Swapping routines
 
     /**
-     * Converts a "short" value between endian systems.
-     * @param value value to convert
-     * @return the converted value
+     * Reads a "double" value from a byte array at a given offset. The value is
+     * converted to the opposed endian system while reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
      */
-    public static short swapShort(final short value) {
-        return (short) ( ( ( ( value >> 0 ) & 0xff ) << 8 ) +
-            ( ( ( value >> 8 ) & 0xff ) << 0 ) );
+    public static double readSwappedDouble(final byte[] data, final int offset) {
+        return Double.longBitsToDouble( readSwappedLong( data, offset ) );
     }
 
     /**
-     * Converts a "int" value between endian systems.
-     * @param value value to convert
-     * @return the converted value
+     * Reads a "double" value from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
      */
-    public static int swapInteger(final int value) {
-        return
-            ( ( ( value >> 0 ) & 0xff ) << 24 ) +
-            ( ( ( value >> 8 ) & 0xff ) << 16 ) +
-            ( ( ( value >> 16 ) & 0xff ) << 8 ) +
-            ( ( ( value >> 24 ) & 0xff ) << 0 );
+    public static double readSwappedDouble(final InputStream input) throws IOException {
+        return Double.longBitsToDouble(readSwappedLong(input));
     }
 
     /**
-     * Converts a "long" value between endian systems.
-     * @param value value to convert
-     * @return the converted value
+     * Reads a "float" value from a byte array at a given offset. The value is
+     * converted to the opposed endian system while reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
      */
-    public static long swapLong(final long value) {
-        return
-            ( ( ( value >> 0 ) & 0xff ) << 56 ) +
-            ( ( ( value >> 8 ) & 0xff ) << 48 ) +
-            ( ( ( value >> 16 ) & 0xff ) << 40 ) +
-            ( ( ( value >> 24 ) & 0xff ) << 32 ) +
-            ( ( ( value >> 32 ) & 0xff ) << 24 ) +
-            ( ( ( value >> 40 ) & 0xff ) << 16 ) +
-            ( ( ( value >> 48 ) & 0xff ) << 8 ) +
-            ( ( ( value >> 56 ) & 0xff ) << 0 );
+    public static float readSwappedFloat(final byte[] data, final int offset) {
+        return Float.intBitsToFloat( readSwappedInteger( data, offset ) );
     }
 
     /**
-     * Converts a "float" value between endian systems.
-     * @param value value to convert
-     * @return the converted value
+     * Reads a "float" value from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
      */
-    public static float swapFloat(final float value) {
-        return Float.intBitsToFloat( swapInteger( Float.floatToIntBits( value ) ) );
+    public static float readSwappedFloat(final InputStream input) throws IOException {
+        return Float.intBitsToFloat(readSwappedInteger(input));
     }
 
     /**
-     * Converts a "double" value between endian systems.
-     * @param value value to convert
-     * @return the converted value
+     * Reads a "int" value from a byte array at a given offset. The value is
+     * converted to the opposed endian system while reading.
+     * @param data source byte array
+     * @param offset starting offset in the byte array
+     * @return the value read
      */
-    public static double swapDouble(final double value) {
-        return Double.longBitsToDouble( swapLong( Double.doubleToLongBits( value ) ) );
+    public static int readSwappedInteger(final byte[] data, final int offset) {
+        return ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
+            ( ( data[ offset + 1 ] & 0xff ) << 8 ) +
+            ( ( data[ offset + 2 ] & 0xff ) << 16 ) +
+            ( ( data[ offset + 3 ] & 0xff ) << 24 ) );
     }
 
     // ========================================== Swapping read/write routines
 
     /**
-     * Writes a "short" value to a byte array at a given offset. The value is
-     * converted to the opposed endian system while writing.
-     * @param data target byte array
-     * @param offset starting offset in the byte array
-     * @param value value to write
+     * Reads a "int" value from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
      */
-    public static void writeSwappedShort(final byte[] data, final int offset, final short value) {
-        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );
-        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );
+    public static int readSwappedInteger(final InputStream input) throws IOException {
+        final int value1 = read(input);
+        final int value2 = read(input);
+        final int value3 = read(input);
+        final int value4 = read(input);
+
+        return ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16) + ((value4 & 0xff) << 24);
     }
 
     /**
-     * Reads a "short" value from a byte array at a given offset. The value is
+     * Reads a "long" value from a byte array at a given offset. The value is
      * converted to the opposed endian system while reading.
      * @param data source byte array
      * @param offset starting offset in the byte array
      * @return the value read
      */
-    public static short readSwappedShort(final byte[] data, final int offset) {
-        return (short)( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
-            ( ( data[ offset + 1 ] & 0xff ) << 8 ) );
+    public static long readSwappedLong(final byte[] data, final int offset) {
+        final long low = readSwappedInteger(data, offset);
+        final long high = readSwappedInteger(data, offset + 4);
+        return (high << 32) + (0xffffffffL & low);
     }
 
     /**
-     * Reads an unsigned short (16-bit) value from a byte array at a given
-     * offset. The value is converted to the opposed endian system while
-     * reading.
-     * @param data source byte array
-     * @param offset starting offset in the byte array
-     * @return the value read
+     * Reads a "long" value from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
      */
-    public static int readSwappedUnsignedShort(final byte[] data, final int offset) {
-        return ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
-            ( ( data[ offset + 1 ] & 0xff ) << 8 ) );
+    public static long readSwappedLong(final InputStream input) throws IOException {
+        final byte[] bytes = new byte[8];
+        for (int i = 0; i < 8; i++) {
+            bytes[i] = (byte) read(input);
+        }
+        return readSwappedLong(bytes, 0);
     }
 
     /**
-     * Writes a "int" value to a byte array at a given offset. The value is
-     * converted to the opposed endian system while writing.
-     * @param data target byte array
+     * Reads a "short" value from a byte array at a given offset. The value is
+     * converted to the opposed endian system while reading.
+     * @param data source byte array
      * @param offset starting offset in the byte array
-     * @param value value to write
+     * @return the value read
      */
-    public static void writeSwappedInteger(final byte[] data, final int offset, final int value) {
-        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );
-        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );
-        data[ offset + 2 ] = (byte)( ( value >> 16 ) & 0xff );
-        data[ offset + 3 ] = (byte)( ( value >> 24 ) & 0xff );
+    public static short readSwappedShort(final byte[] data, final int offset) {
+        return (short)( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
+            ( ( data[ offset + 1 ] & 0xff ) << 8 ) );
     }
 
     /**
-     * Reads a "int" value from a byte array at a given offset. The value is
+     * Reads a "short" value from an InputStream. The value is
      * converted to the opposed endian system while reading.
-     * @param data source byte array
-     * @param offset starting offset in the byte array
-     * @return the value read
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
      */
-    public static int readSwappedInteger(final byte[] data, final int offset) {
-        return ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
-            ( ( data[ offset + 1 ] & 0xff ) << 8 ) +
-            ( ( data[ offset + 2 ] & 0xff ) << 16 ) +
-            ( ( data[ offset + 3 ] & 0xff ) << 24 ) );
+    public static short readSwappedShort(final InputStream input) throws IOException {
+        return (short) (((read(input) & 0xff) << 0) + ((read(input) & 0xff) << 8));
     }
 
     /**
@@ -191,56 +203,108 @@ public class EndianUtils {
     }
 
     /**
-     * Writes a "long" value to a byte array at a given offset. The value is
-     * converted to the opposed endian system while writing.
-     * @param data target byte array
-     * @param offset starting offset in the byte array
-     * @param value value to write
+     * Reads a unsigned integer (32-bit) from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
      */
-    public static void writeSwappedLong(final byte[] data, final int offset, final long value) {
-        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );
-        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );
-        data[ offset + 2 ] = (byte)( ( value >> 16 ) & 0xff );
-        data[ offset + 3 ] = (byte)( ( value >> 24 ) & 0xff );
-        data[ offset + 4 ] = (byte)( ( value >> 32 ) & 0xff );
-        data[ offset + 5 ] = (byte)( ( value >> 40 ) & 0xff );
-        data[ offset + 6 ] = (byte)( ( value >> 48 ) & 0xff );
-        data[ offset + 7 ] = (byte)( ( value >> 56 ) & 0xff );
+    public static long readSwappedUnsignedInteger(final InputStream input) throws IOException {
+        final int value1 = read(input);
+        final int value2 = read(input);
+        final int value3 = read(input);
+        final int value4 = read(input);
+
+        final long low = (((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16));
+
+        final long high = value4 & 0xff;
+
+        return (high << 24) + (0xffffffffL & low);
     }
 
     /**
-     * Reads a "long" value from a byte array at a given offset. The value is
-     * converted to the opposed endian system while reading.
+     * Reads an unsigned short (16-bit) value from a byte array at a given
+     * offset. The value is converted to the opposed endian system while
+     * reading.
      * @param data source byte array
      * @param offset starting offset in the byte array
      * @return the value read
      */
-    public static long readSwappedLong(final byte[] data, final int offset) {
-        final long low = readSwappedInteger(data, offset);
-        final long high = readSwappedInteger(data, offset + 4);
-        return (high << 32) + (0xffffffffL & low);
+    public static int readSwappedUnsignedShort(final byte[] data, final int offset) {
+        return ( ( ( data[ offset + 0 ] & 0xff ) << 0 ) +
+            ( ( data[ offset + 1 ] & 0xff ) << 8 ) );
     }
 
     /**
-     * Writes a "float" value to a byte array at a given offset. The value is
-     * converted to the opposed endian system while writing.
-     * @param data target byte array
-     * @param offset starting offset in the byte array
-     * @param value value to write
+     * Reads a unsigned short (16-bit) from an InputStream. The value is
+     * converted to the opposed endian system while reading.
+     * @param input source InputStream
+     * @return the value just read
+     * @throws IOException in case of an I/O problem
      */
-    public static void writeSwappedFloat(final byte[] data, final int offset, final float value) {
-        writeSwappedInteger( data, offset, Float.floatToIntBits( value ) );
+    public static int readSwappedUnsignedShort(final InputStream input) throws IOException {
+        final int value1 = read(input);
+        final int value2 = read(input);
+
+        return (((value1 & 0xff) << 0) + ((value2 & 0xff) << 8));
     }
 
     /**
-     * Reads a "float" value from a byte array at a given offset. The value is
-     * converted to the opposed endian system while reading.
-     * @param data source byte array
-     * @param offset starting offset in the byte array
-     * @return the value read
+     * Converts a "double" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
      */
-    public static float readSwappedFloat(final byte[] data, final int offset) {
-        return Float.intBitsToFloat( readSwappedInteger( data, offset ) );
+    public static double swapDouble(final double value) {
+        return Double.longBitsToDouble( swapLong( Double.doubleToLongBits( value ) ) );
+    }
+
+    /**
+     * Converts a "float" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
+     */
+    public static float swapFloat(final float value) {
+        return Float.intBitsToFloat( swapInteger( Float.floatToIntBits( value ) ) );
+    }
+
+    /**
+     * Converts a "int" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
+     */
+    public static int swapInteger(final int value) {
+        return
+            ( ( ( value >> 0 ) & 0xff ) << 24 ) +
+            ( ( ( value >> 8 ) & 0xff ) << 16 ) +
+            ( ( ( value >> 16 ) & 0xff ) << 8 ) +
+            ( ( ( value >> 24 ) & 0xff ) << 0 );
+    }
+
+    /**
+     * Converts a "long" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
+     */
+    public static long swapLong(final long value) {
+        return
+            ( ( ( value >> 0 ) & 0xff ) << 56 ) +
+            ( ( ( value >> 8 ) & 0xff ) << 48 ) +
+            ( ( ( value >> 16 ) & 0xff ) << 40 ) +
+            ( ( ( value >> 24 ) & 0xff ) << 32 ) +
+            ( ( ( value >> 32 ) & 0xff ) << 24 ) +
+            ( ( ( value >> 40 ) & 0xff ) << 16 ) +
+            ( ( ( value >> 48 ) & 0xff ) << 8 ) +
+            ( ( ( value >> 56 ) & 0xff ) << 0 );
+    }
+
+    /**
+     * Converts a "short" value between endian systems.
+     * @param value value to convert
+     * @return the converted value
+     */
+    public static short swapShort(final short value) {
+        return (short) ( ( ( ( value >> 0 ) & 0xff ) << 8 ) +
+            ( ( ( value >> 8 ) & 0xff ) << 0 ) );
     }
 
     /**
@@ -255,51 +319,50 @@ public class EndianUtils {
     }
 
     /**
-     * Reads a "double" value from a byte array at a given offset. The value is
-     * converted to the opposed endian system while reading.
-     * @param data source byte array
-     * @param offset starting offset in the byte array
-     * @return the value read
+     * Writes a "double" value to an OutputStream. The value is
+     * converted to the opposed endian system while writing.
+     * @param output target OutputStream
+     * @param value value to write
+     * @throws IOException in case of an I/O problem
      */
-    public static double readSwappedDouble(final byte[] data, final int offset) {
-        return Double.longBitsToDouble( readSwappedLong( data, offset ) );
+    public static void writeSwappedDouble(final OutputStream output, final double value) throws IOException {
+        writeSwappedLong(output, Double.doubleToLongBits(value));
     }
 
     /**
-     * Writes a "short" value to an OutputStream. The value is
+     * Writes a "float" value to a byte array at a given offset. The value is
      * converted to the opposed endian system while writing.
-     * @param output target OutputStream
+     * @param data target byte array
+     * @param offset starting offset in the byte array
      * @param value value to write
-     * @throws IOException in case of an I/O problem
      */
-    public static void writeSwappedShort(final OutputStream output, final short value) throws IOException {
-        output.write((byte) ((value >> 0) & 0xff));
-        output.write((byte) ((value >> 8) & 0xff));
+    public static void writeSwappedFloat(final byte[] data, final int offset, final float value) {
+        writeSwappedInteger( data, offset, Float.floatToIntBits( value ) );
     }
 
     /**
-     * Reads a "short" value from an InputStream. The value is
-     * converted to the opposed endian system while reading.
-     * @param input source InputStream
-     * @return the value just read
+     * Writes a "float" value to an OutputStream. The value is
+     * converted to the opposed endian system while writing.
+     * @param output target OutputStream
+     * @param value value to write
      * @throws IOException in case of an I/O problem
      */
-    public static short readSwappedShort(final InputStream input) throws IOException {
-        return (short) (((read(input) & 0xff) << 0) + ((read(input) & 0xff) << 8));
+    public static void writeSwappedFloat(final OutputStream output, final float value) throws IOException {
+        writeSwappedInteger(output, Float.floatToIntBits(value));
     }
 
     /**
-     * Reads a unsigned short (16-bit) from an InputStream. The value is
-     * converted to the opposed endian system while reading.
-     * @param input source InputStream
-     * @return the value just read
-     * @throws IOException in case of an I/O problem
+     * Writes a "int" value to a byte array at a given offset. The value is
+     * converted to the opposed endian system while writing.
+     * @param data target byte array
+     * @param offset starting offset in the byte array
+     * @param value value to write
      */
-    public static int readSwappedUnsignedShort(final InputStream input) throws IOException {
-        final int value1 = read(input);
-        final int value2 = read(input);
-
-        return (((value1 & 0xff) << 0) + ((value2 & 0xff) << 8));
+    public static void writeSwappedInteger(final byte[] data, final int offset, final int value) {
+        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );
+        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );
+        data[ offset + 2 ] = (byte)( ( value >> 16 ) & 0xff );
+        data[ offset + 3 ] = (byte)( ( value >> 24 ) & 0xff );
     }
 
     /**
@@ -317,39 +380,21 @@ public class EndianUtils {
     }
 
     /**
-     * Reads a "int" value from an InputStream. The value is
-     * converted to the opposed endian system while reading.
-     * @param input source InputStream
-     * @return the value just read
-     * @throws IOException in case of an I/O problem
-     */
-    public static int readSwappedInteger(final InputStream input) throws IOException {
-        final int value1 = read(input);
-        final int value2 = read(input);
-        final int value3 = read(input);
-        final int value4 = read(input);
-
-        return ((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16) + ((value4 & 0xff) << 24);
-    }
-
-    /**
-     * Reads a unsigned integer (32-bit) from an InputStream. The value is
-     * converted to the opposed endian system while reading.
-     * @param input source InputStream
-     * @return the value just read
-     * @throws IOException in case of an I/O problem
+     * Writes a "long" value to a byte array at a given offset. The value is
+     * converted to the opposed endian system while writing.
+     * @param data target byte array
+     * @param offset starting offset in the byte array
+     * @param value value to write
      */
-    public static long readSwappedUnsignedInteger(final InputStream input) throws IOException {
-        final int value1 = read(input);
-        final int value2 = read(input);
-        final int value3 = read(input);
-        final int value4 = read(input);
-
-        final long low = (((value1 & 0xff) << 0) + ((value2 & 0xff) << 8) + ((value3 & 0xff) << 16));
-
-        final long high = value4 & 0xff;
-
-        return (high << 24) + (0xffffffffL & low);
+    public static void writeSwappedLong(final byte[] data, final int offset, final long value) {
+        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );
+        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );
+        data[ offset + 2 ] = (byte)( ( value >> 16 ) & 0xff );
+        data[ offset + 3 ] = (byte)( ( value >> 24 ) & 0xff );
+        data[ offset + 4 ] = (byte)( ( value >> 32 ) & 0xff );
+        data[ offset + 5 ] = (byte)( ( value >> 40 ) & 0xff );
+        data[ offset + 6 ] = (byte)( ( value >> 48 ) & 0xff );
+        data[ offset + 7 ] = (byte)( ( value >> 56 ) & 0xff );
     }
 
     /**
@@ -371,77 +416,32 @@ public class EndianUtils {
     }
 
     /**
-     * Reads a "long" value from an InputStream. The value is
-     * converted to the opposed endian system while reading.
-     * @param input source InputStream
-     * @return the value just read
-     * @throws IOException in case of an I/O problem
-     */
-    public static long readSwappedLong(final InputStream input) throws IOException {
-        final byte[] bytes = new byte[8];
-        for (int i = 0; i < 8; i++) {
-            bytes[i] = (byte) read(input);
-        }
-        return readSwappedLong(bytes, 0);
-    }
-
-    /**
-     * Writes a "float" value to an OutputStream. The value is
+     * Writes a "short" value to a byte array at a given offset. The value is
      * converted to the opposed endian system while writing.
-     * @param output target OutputStream
+     * @param data target byte array
+     * @param offset starting offset in the byte array
      * @param value value to write
-     * @throws IOException in case of an I/O problem
      */
-    public static void writeSwappedFloat(final OutputStream output, final float value) throws IOException {
-        writeSwappedInteger(output, Float.floatToIntBits(value));
-    }
-
-    /**
-     * Reads a "float" value from an InputStream. The value is
-     * converted to the opposed endian system while reading.
-     * @param input source InputStream
-     * @return the value just read
-     * @throws IOException in case of an I/O problem
-     */
-    public static float readSwappedFloat(final InputStream input) throws IOException {
-        return Float.intBitsToFloat(readSwappedInteger(input));
+    public static void writeSwappedShort(final byte[] data, final int offset, final short value) {
+        data[ offset + 0 ] = (byte)( ( value >> 0 ) & 0xff );
+        data[ offset + 1 ] = (byte)( ( value >> 8 ) & 0xff );
     }
 
     /**
-     * Writes a "double" value to an OutputStream. The value is
+     * Writes a "short" value to an OutputStream. The value is
      * converted to the opposed endian system while writing.
      * @param output target OutputStream
      * @param value value to write
      * @throws IOException in case of an I/O problem
      */
-    public static void writeSwappedDouble(final OutputStream output, final double value) throws IOException {
-        writeSwappedLong(output, Double.doubleToLongBits(value));
-    }
-
-    /**
-     * Reads a "double" value from an InputStream. The value is
-     * converted to the opposed endian system while reading.
-     * @param input source InputStream
-     * @return the value just read
-     * @throws IOException in case of an I/O problem
-     */
-    public static double readSwappedDouble(final InputStream input) throws IOException {
-        return Double.longBitsToDouble(readSwappedLong(input));
+    public static void writeSwappedShort(final OutputStream output, final short value) throws IOException {
+        output.write((byte) ((value >> 0) & 0xff));
+        output.write((byte) ((value >> 8) & 0xff));
     }
 
     /**
-     * Reads the next byte from the input stream.
-     * @param input  the stream
-     * @return the byte
-     * @throws IOException if the end of file is reached
+     * Instances should NOT be constructed in standard programming.
      */
-    private static int read(final InputStream input) throws IOException {
-        final int value = input.read();
-
-        if (EOF == value) {
-            throw new EOFException("Unexpected EOF reached");
-        }
-
-        return value;
+    public EndianUtils() {
     }
 }
diff --git a/src/main/java/org/apache/commons/io/FileCleaner.java b/src/main/java/org/apache/commons/io/FileCleaner.java
index 43c3cf2..634bffa 100644
--- a/src/main/java/org/apache/commons/io/FileCleaner.java
+++ b/src/main/java/org/apache/commons/io/FileCleaner.java
@@ -42,6 +42,58 @@ public class FileCleaner {
     static final FileCleaningTracker theInstance = new FileCleaningTracker();
 
     /**
+     * Call this method to cause the file cleaner thread to terminate when
+     * there are no more objects being tracked for deletion.
+     * <p>
+     * In a simple environment, you don't need this method as the file cleaner
+     * thread will simply exit when the JVM exits. In a more complex environment,
+     * with multiple class loaders (such as an application server), you should be
+     * aware that the file cleaner thread will continue running even if the class
+     * loader it was started from terminates. This can constitute a memory leak.
+     * <p>
+     * For example, suppose that you have developed a web application, which
+     * contains the commons-io jar file in your WEB-INF/lib directory. In other
+     * words, the FileCleaner class is loaded through the class loader of your
+     * web application. If the web application is terminated, but the servlet
+     * container is still running, then the file cleaner thread will still exist,
+     * posing a memory leak.
+     * <p>
+     * This method allows the thread to be terminated. Simply call this method
+     * in the resource cleanup code, such as
+     * {@code javax.servlet.ServletContextListener.contextDestroyed(javax.servlet.ServletContextEvent)}.
+     * One called, no new objects can be tracked by the file cleaner.
+     * @deprecated Use {@link FileCleaningTracker#exitWhenFinished()}.
+     */
+    @Deprecated
+    public static synchronized void exitWhenFinished() {
+        theInstance.exitWhenFinished();
+    }
+
+    /**
+     * Returns the singleton instance, which is used by the deprecated, static methods.
+     * This is mainly useful for code, which wants to support the new
+     * {@link FileCleaningTracker} class while maintain compatibility with the
+     * deprecated {@link FileCleaner}.
+     *
+     * @return the singleton instance
+     */
+    public static FileCleaningTracker getInstance() {
+        return theInstance;
+    }
+
+    /**
+     * Retrieve the number of files currently being tracked, and therefore
+     * awaiting deletion.
+     *
+     * @return the number of files being tracked
+     * @deprecated Use {@link FileCleaningTracker#getTrackCount()}.
+     */
+    @Deprecated
+    public static int getTrackCount() {
+        return theInstance.getTrackCount();
+    }
+
+    /**
      * Track the specified file, using the provided marker, deleting the file
      * when the marker instance is garbage collected.
      * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
@@ -102,56 +154,4 @@ public class FileCleaner {
     public static void track(final String path, final Object marker, final FileDeleteStrategy deleteStrategy) {
         theInstance.track(path, marker, deleteStrategy);
     }
-
-    /**
-     * Retrieve the number of files currently being tracked, and therefore
-     * awaiting deletion.
-     *
-     * @return the number of files being tracked
-     * @deprecated Use {@link FileCleaningTracker#getTrackCount()}.
-     */
-    @Deprecated
-    public static int getTrackCount() {
-        return theInstance.getTrackCount();
-    }
-
-    /**
-     * Call this method to cause the file cleaner thread to terminate when
-     * there are no more objects being tracked for deletion.
-     * <p>
-     * In a simple environment, you don't need this method as the file cleaner
-     * thread will simply exit when the JVM exits. In a more complex environment,
-     * with multiple class loaders (such as an application server), you should be
-     * aware that the file cleaner thread will continue running even if the class
-     * loader it was started from terminates. This can constitute a memory leak.
-     * <p>
-     * For example, suppose that you have developed a web application, which
-     * contains the commons-io jar file in your WEB-INF/lib directory. In other
-     * words, the FileCleaner class is loaded through the class loader of your
-     * web application. If the web application is terminated, but the servlet
-     * container is still running, then the file cleaner thread will still exist,
-     * posing a memory leak.
-     * <p>
-     * This method allows the thread to be terminated. Simply call this method
-     * in the resource cleanup code, such as
-     * {@code javax.servlet.ServletContextListener.contextDestroyed(javax.servlet.ServletContextEvent)}.
-     * One called, no new objects can be tracked by the file cleaner.
-     * @deprecated Use {@link FileCleaningTracker#exitWhenFinished()}.
-     */
-    @Deprecated
-    public static synchronized void exitWhenFinished() {
-        theInstance.exitWhenFinished();
-    }
-
-    /**
-     * Returns the singleton instance, which is used by the deprecated, static methods.
-     * This is mainly useful for code, which wants to support the new
-     * {@link FileCleaningTracker} class while maintain compatibility with the
-     * deprecated {@link FileCleaner}.
-     *
-     * @return the singleton instance
-     */
-    public static FileCleaningTracker getInstance() {
-        return theInstance;
-    }
 }
diff --git a/src/main/java/org/apache/commons/io/FileCleaningTracker.java b/src/main/java/org/apache/commons/io/FileCleaningTracker.java
index 32bc50b..7016ef3 100644
--- a/src/main/java/org/apache/commons/io/FileCleaningTracker.java
+++ b/src/main/java/org/apache/commons/io/FileCleaningTracker.java
@@ -46,6 +46,87 @@ public class FileCleaningTracker {
     // Note: fields are package protected to allow use by test cases
 
     /**
+     * The reaper thread.
+     */
+    private final class Reaper extends Thread {
+        /** Constructs a new Reaper */
+        Reaper() {
+            super("File Reaper");
+            setPriority(Thread.MAX_PRIORITY);
+            setDaemon(true);
+        }
+
+        /**
+         * Run the reaper thread that will delete files as their associated
+         * marker objects are reclaimed by the garbage collector.
+         */
+        @Override
+        public void run() {
+            // thread exits when exitWhenFinished is true and there are no more tracked objects
+            while (!exitWhenFinished || !trackers.isEmpty()) {
+                try {
+                    // Wait for a tracker to remove.
+                    final Tracker tracker = (Tracker) q.remove(); // cannot return null
+                    trackers.remove(tracker);
+                    if (!tracker.delete()) {
+                        deleteFailures.add(tracker.getPath());
+                    }
+                    tracker.clear();
+                } catch (final InterruptedException e) {
+                    continue;
+                }
+            }
+        }
+    }
+    /**
+     * Inner class which acts as the reference for a file pending deletion.
+     */
+    private static final class Tracker extends PhantomReference<Object> {
+
+        /**
+         * The full path to the file being tracked.
+         */
+        private final String path;
+        /**
+         * The strategy for deleting files.
+         */
+        private final FileDeleteStrategy deleteStrategy;
+
+        /**
+         * Constructs an instance of this class from the supplied parameters.
+         *
+         * @param path  the full path to the file to be tracked, not null
+         * @param deleteStrategy  the strategy to delete the file, null means normal
+         * @param marker  the marker object used to track the file, not null
+         * @param queue  the queue on to which the tracker will be pushed, not null
+         */
+        Tracker(final String path, final FileDeleteStrategy deleteStrategy, final Object marker,
+                final ReferenceQueue<? super Object> queue) {
+            super(marker, queue);
+            this.path = path;
+            this.deleteStrategy = deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy;
+        }
+
+        /**
+         * Deletes the file associated with this tracker instance.
+         *
+         * @return {@code true} if the file was deleted successfully;
+         *         {@code false} otherwise.
+         */
+        public boolean delete() {
+            return deleteStrategy.deleteQuietly(new File(path));
+        }
+
+        /**
+         * Return the path.
+         *
+         * @return the path
+         */
+        public String getPath() {
+            return path;
+        }
+    }
+    /**
      * Queue of {@code Tracker} instances being watched.
      */
     ReferenceQueue<Object> q = new ReferenceQueue<>();
@@ -57,72 +138,18 @@ public class FileCleaningTracker {
      * Collection of File paths that failed to delete.
      */
     final List<String> deleteFailures = Collections.synchronizedList(new ArrayList<>());
+
     /**
      * Whether to terminate the thread when the tracking is complete.
      */
     volatile boolean exitWhenFinished;
+
     /**
      * The thread that will clean up registered files.
      */
     Thread reaper;
 
     /**
-     * Track the specified file, using the provided marker, deleting the file
-     * when the marker instance is garbage collected.
-     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
-     *
-     * @param file  the file to be tracked, not null
-     * @param marker  the marker object used to track the file, not null
-     * @throws NullPointerException if the file is null
-     */
-    public void track(final File file, final Object marker) {
-        track(file, marker, null);
-    }
-
-    /**
-     * Track the specified file, using the provided marker, deleting the file
-     * when the marker instance is garbage collected.
-     * The specified deletion strategy is used.
-     *
-     * @param file  the file to be tracked, not null
-     * @param marker  the marker object used to track the file, not null
-     * @param deleteStrategy  the strategy to delete the file, null means normal
-     * @throws NullPointerException if the file is null
-     */
-    public void track(final File file, final Object marker, final FileDeleteStrategy deleteStrategy) {
-        Objects.requireNonNull(file, "file");
-        addTracker(file.getPath(), marker, deleteStrategy);
-    }
-
-    /**
-     * Track the specified file, using the provided marker, deleting the file
-     * when the marker instance is garbage collected.
-     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
-     *
-     * @param path  the full path to the file to be tracked, not null
-     * @param marker  the marker object used to track the file, not null
-     * @throws NullPointerException if the path is null
-     */
-    public void track(final String path, final Object marker) {
-        track(path, marker, null);
-    }
-
-    /**
-     * Track the specified file, using the provided marker, deleting the file
-     * when the marker instance is garbage collected.
-     * The specified deletion strategy is used.
-     *
-     * @param path  the full path to the file to be tracked, not null
-     * @param marker  the marker object used to track the file, not null
-     * @param deleteStrategy  the strategy to delete the file, null means normal
-     * @throws NullPointerException if the path is null
-     */
-    public void track(final String path, final Object marker, final FileDeleteStrategy deleteStrategy) {
-        Objects.requireNonNull(path, "path");
-        addTracker(path, marker, deleteStrategy);
-    }
-
-    /**
      * Adds a tracker to the list of trackers.
      *
      * @param path  the full path to the file to be tracked, not null
@@ -143,26 +170,6 @@ public class FileCleaningTracker {
     }
 
     /**
-     * Retrieve the number of files currently being tracked, and therefore
-     * awaiting deletion.
-     *
-     * @return the number of files being tracked
-     */
-    public int getTrackCount() {
-        return trackers.size();
-    }
-
-    /**
-     * Return the file paths that failed to delete.
-     *
-     * @return the file paths that failed to delete
-     * @since 2.0
-     */
-    public List<String> getDeleteFailures() {
-        return deleteFailures;
-    }
-
-    /**
      * Call this method to cause the file cleaner thread to terminate when
      * there are no more objects being tracked for deletion.
      * <p>
@@ -195,86 +202,79 @@ public class FileCleaningTracker {
     }
 
     /**
-     * The reaper thread.
+     * Return the file paths that failed to delete.
+     *
+     * @return the file paths that failed to delete
+     * @since 2.0
      */
-    private final class Reaper extends Thread {
-        /** Constructs a new Reaper */
-        Reaper() {
-            super("File Reaper");
-            setPriority(Thread.MAX_PRIORITY);
-            setDaemon(true);
-        }
-
-        /**
-         * Run the reaper thread that will delete files as their associated
-         * marker objects are reclaimed by the garbage collector.
-         */
-        @Override
-        public void run() {
-            // thread exits when exitWhenFinished is true and there are no more tracked objects
-            while (!exitWhenFinished || !trackers.isEmpty()) {
-                try {
-                    // Wait for a tracker to remove.
-                    final Tracker tracker = (Tracker) q.remove(); // cannot return null
-                    trackers.remove(tracker);
-                    if (!tracker.delete()) {
-                        deleteFailures.add(tracker.getPath());
-                    }
-                    tracker.clear();
-                } catch (final InterruptedException e) {
-                    continue;
-                }
-            }
-        }
+    public List<String> getDeleteFailures() {
+        return deleteFailures;
     }
 
     /**
-     * Inner class which acts as the reference for a file pending deletion.
+     * Retrieve the number of files currently being tracked, and therefore
+     * awaiting deletion.
+     *
+     * @return the number of files being tracked
      */
-    private static final class Tracker extends PhantomReference<Object> {
+    public int getTrackCount() {
+        return trackers.size();
+    }
 
-        /**
-         * The full path to the file being tracked.
-         */
-        private final String path;
-        /**
-         * The strategy for deleting files.
-         */
-        private final FileDeleteStrategy deleteStrategy;
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
+     *
+     * @param file  the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @throws NullPointerException if the file is null
+     */
+    public void track(final File file, final Object marker) {
+        track(file, marker, null);
+    }
 
-        /**
-         * Constructs an instance of this class from the supplied parameters.
-         *
-         * @param path  the full path to the file to be tracked, not null
-         * @param deleteStrategy  the strategy to delete the file, null means normal
-         * @param marker  the marker object used to track the file, not null
-         * @param queue  the queue on to which the tracker will be pushed, not null
-         */
-        Tracker(final String path, final FileDeleteStrategy deleteStrategy, final Object marker,
-                final ReferenceQueue<? super Object> queue) {
-            super(marker, queue);
-            this.path = path;
-            this.deleteStrategy = deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy;
-        }
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The specified deletion strategy is used.
+     *
+     * @param file  the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @param deleteStrategy  the strategy to delete the file, null means normal
+     * @throws NullPointerException if the file is null
+     */
+    public void track(final File file, final Object marker, final FileDeleteStrategy deleteStrategy) {
+        Objects.requireNonNull(file, "file");
+        addTracker(file.getPath(), marker, deleteStrategy);
+    }
 
-        /**
-         * Return the path.
-         *
-         * @return the path
-         */
-        public String getPath() {
-            return path;
-        }
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The {@link FileDeleteStrategy#NORMAL normal} deletion strategy will be used.
+     *
+     * @param path  the full path to the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @throws NullPointerException if the path is null
+     */
+    public void track(final String path, final Object marker) {
+        track(path, marker, null);
+    }
 
-        /**
-         * Deletes the file associated with this tracker instance.
-         *
-         * @return {@code true} if the file was deleted successfully;
-         *         {@code false} otherwise.
-         */
-        public boolean delete() {
-            return deleteStrategy.deleteQuietly(new File(path));
-        }
+    /**
+     * Track the specified file, using the provided marker, deleting the file
+     * when the marker instance is garbage collected.
+     * The specified deletion strategy is used.
+     *
+     * @param path  the full path to the file to be tracked, not null
+     * @param marker  the marker object used to track the file, not null
+     * @param deleteStrategy  the strategy to delete the file, null means normal
+     * @throws NullPointerException if the path is null
+     */
+    public void track(final String path, final Object marker, final FileDeleteStrategy deleteStrategy) {
+        Objects.requireNonNull(path, "path");
+        addTracker(path, marker, deleteStrategy);
     }
 
 }
diff --git a/src/main/java/org/apache/commons/io/FileExistsException.java b/src/main/java/org/apache/commons/io/FileExistsException.java
index 56c348b..2264256 100644
--- a/src/main/java/org/apache/commons/io/FileExistsException.java
+++ b/src/main/java/org/apache/commons/io/FileExistsException.java
@@ -38,21 +38,21 @@ public class FileExistsException extends IOException {
     }
 
     /**
-     * Constructs an instance with the specified message.
+     * Constructs an instance with the specified file.
      *
-     * @param message The error message
+     * @param file The file that exists
      */
-    public FileExistsException(final String message) {
-        super(message);
+    public FileExistsException(final File file) {
+        super("File " + file + " exists");
     }
 
     /**
-     * Constructs an instance with the specified file.
+     * Constructs an instance with the specified message.
      *
-     * @param file The file that exists
+     * @param message The error message
      */
-    public FileExistsException(final File file) {
-        super("File " + file + " exists");
+    public FileExistsException(final String message) {
+        super(message);
     }
 
 }
diff --git a/src/main/java/org/apache/commons/io/FileSystemUtils.java b/src/main/java/org/apache/commons/io/FileSystemUtils.java
index e35c820..3d612cd 100644
--- a/src/main/java/org/apache/commons/io/FileSystemUtils.java
+++ b/src/main/java/org/apache/commons/io/FileSystemUtils.java
@@ -108,12 +108,6 @@ public class FileSystemUtils {
     }
 
     /**
-     * Instances should NOT be constructed in standard programming.
-     */
-    public FileSystemUtils() {
-    }
-
-    /**
      * Returns the free space on a drive or volume by invoking
      * the command line.
      * This method does not normalize the result, and typically returns
@@ -145,6 +139,45 @@ public class FileSystemUtils {
     }
 
     /**
+     * Returns the free space for the working directory
+     * in kibibytes (1024 bytes) by invoking the command line.
+     * <p>
+     * Identical to:
+     * <pre>
+     * freeSpaceKb(FileUtils.current().getAbsolutePath())
+     * </pre>
+     * @return the amount of free drive space on the drive or volume in kilobytes
+     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IOException if an error occurs when finding the free space
+     * @since 2.0
+     * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
+     */
+    @Deprecated
+    public static long freeSpaceKb() throws IOException {
+        return freeSpaceKb(-1);
+    }
+
+    /**
+     * Returns the free space for the working directory
+     * in kibibytes (1024 bytes) by invoking the command line.
+     * <p>
+     * Identical to:
+     * <pre>
+     * freeSpaceKb(FileUtils.current().getAbsolutePath())
+     * </pre>
+     * @param timeout The timeout amount in milliseconds or no timeout if the value
+     *  is zero or less
+     * @return the amount of free drive space on the drive or volume in kilobytes
+     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IOException if an error occurs when finding the free space
+     * @since 2.0
+     * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
+     */
+    @Deprecated
+    public static long freeSpaceKb(final long timeout) throws IOException {
+        return freeSpaceKb(FileUtils.current().getAbsolutePath(), timeout);
+    }
+    /**
      * Returns the free space on a drive or volume in kibibytes (1024 bytes)
      * by invoking the command line.
      * <pre>
@@ -173,6 +206,7 @@ public class FileSystemUtils {
     public static long freeSpaceKb(final String path) throws IOException {
         return freeSpaceKb(path, -1);
     }
+
     /**
      * Returns the free space on a drive or volume in kibibytes (1024 bytes)
      * by invoking the command line.
@@ -206,43 +240,9 @@ public class FileSystemUtils {
     }
 
     /**
-     * Returns the free space for the working directory
-     * in kibibytes (1024 bytes) by invoking the command line.
-     * <p>
-     * Identical to:
-     * <pre>
-     * freeSpaceKb(FileUtils.current().getAbsolutePath())
-     * </pre>
-     * @return the amount of free drive space on the drive or volume in kilobytes
-     * @throws IllegalStateException if an error occurred in initialisation
-     * @throws IOException if an error occurs when finding the free space
-     * @since 2.0
-     * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
-     */
-    @Deprecated
-    public static long freeSpaceKb() throws IOException {
-        return freeSpaceKb(-1);
-    }
-
-    /**
-     * Returns the free space for the working directory
-     * in kibibytes (1024 bytes) by invoking the command line.
-     * <p>
-     * Identical to:
-     * <pre>
-     * freeSpaceKb(FileUtils.current().getAbsolutePath())
-     * </pre>
-     * @param timeout The timeout amount in milliseconds or no timeout if the value
-     *  is zero or less
-     * @return the amount of free drive space on the drive or volume in kilobytes
-     * @throws IllegalStateException if an error occurred in initialisation
-     * @throws IOException if an error occurs when finding the free space
-     * @since 2.0
-     * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
+     * Instances should NOT be constructed in standard programming.
      */
-    @Deprecated
-    public static long freeSpaceKb(final long timeout) throws IOException {
-        return freeSpaceKb(FileUtils.current().getAbsolutePath(), timeout);
+    public FileSystemUtils() {
     }
 
     /**
@@ -285,6 +285,64 @@ public class FileSystemUtils {
     }
 
     /**
+     * Find free space on the *nix platform using the 'df' command.
+     *
+     * @param path  the path to get free space for
+     * @param kb  whether to normalize to kilobytes
+     * @param posix  whether to use the POSIX standard format flag
+     * @param timeout The timeout amount in milliseconds or no timeout if the value
+     *  is zero or less
+     * @return the amount of free drive space on the volume
+     * @throws IOException if an error occurs
+     */
+    long freeSpaceUnix(final String path, final boolean kb, final boolean posix, final Duration timeout)
+            throws IOException {
+        if (path.isEmpty()) {
+            throw new IllegalArgumentException("Path must not be empty");
+        }
+
+        // build and run the 'dir' command
+        String flags = "-";
+        if (kb) {
+            flags += "k";
+        }
+        if (posix) {
+            flags += "P";
+        }
+        final String[] cmdAttribs =
+            flags.length() > 1 ? new String[] {DF, flags, path} : new String[] {DF, path};
+
+        // perform the command, asking for up to 3 lines (header, interesting, overflow)
+        final List<String> lines = performCommand(cmdAttribs, 3, timeout);
+        if (lines.size() < 2) {
+            // unknown problem, throw exception
+            throw new IOException(
+                    "Command line '" + DF + "' did not return info as expected " +
+                    "for path '" + path + "'- response was " + lines);
+        }
+        final String line2 = lines.get(1); // the line we're interested in
+
+        // Now, we tokenize the string. The fourth element is what we want.
+        StringTokenizer tok = new StringTokenizer(line2, " ");
+        if (tok.countTokens() < 4) {
+            // could be long Filesystem, thus data on third line
+            if ((tok.countTokens() != 1) || (lines.size() < 3)) {
+                throw new IOException(
+                        "Command line '" + DF + "' did not return data as expected " +
+                        "for path '" + path + "'- check path is valid");
+            }
+            final String line3 = lines.get(2); // the line may be interested in
+            tok = new StringTokenizer(line3, " ");
+        } else {
+            tok.nextToken(); // Ignore Filesystem
+        }
+        tok.nextToken(); // Ignore 1K-blocks
+        tok.nextToken(); // Ignore Used
+        final String freeSpace = tok.nextToken();
+        return parseBytes(freeSpace, path);
+    }
+
+    /**
      * Find free space on the Windows platform using the 'dir' command.
      *
      * @param path  the path to get free space for, including the colon
@@ -325,6 +383,42 @@ public class FileSystemUtils {
     }
 
     /**
+     * Opens the process to the operating system.
+     *
+     * @param cmdAttribs  the command line parameters
+     * @return the process
+     * @throws IOException if an error occurs
+     */
+    Process openProcess(final String[] cmdAttribs) throws IOException {
+        return Runtime.getRuntime().exec(cmdAttribs);
+    }
+
+    /**
+     * Parses the bytes from a string.
+     *
+     * @param freeSpace  the free space string
+     * @param path  the path
+     * @return the number of bytes
+     * @throws IOException if an error occurs
+     */
+    long parseBytes(final String freeSpace, final String path) throws IOException {
+        try {
+            final long bytes = Long.parseLong(freeSpace);
+            if (bytes < 0) {
+                throw new IOException(
+                        "Command line '" + DF + "' did not find free space in response " +
+                        "for path '" + path + "'- check path is valid");
+            }
+            return bytes;
+
+        } catch (final NumberFormatException ex) {
+            throw new IOException(
+                    "Command line '" + DF + "' did not return numeric data as expected " +
+                    "for path '" + path + "'- check path is valid", ex);
+        }
+    }
+
+    /**
      * Parses the Windows dir response last line
      *
      * @param line  the line to parse
@@ -377,89 +471,6 @@ public class FileSystemUtils {
     }
 
     /**
-     * Find free space on the *nix platform using the 'df' command.
-     *
-     * @param path  the path to get free space for
-     * @param kb  whether to normalize to kilobytes
-     * @param posix  whether to use the POSIX standard format flag
-     * @param timeout The timeout amount in milliseconds or no timeout if the value
-     *  is zero or less
-     * @return the amount of free drive space on the volume
-     * @throws IOException if an error occurs
-     */
-    long freeSpaceUnix(final String path, final boolean kb, final boolean posix, final Duration timeout)
-            throws IOException {
-        if (path.isEmpty()) {
-            throw new IllegalArgumentException("Path must not be empty");
-        }
-
-        // build and run the 'dir' command
-        String flags = "-";
-        if (kb) {
-            flags += "k";
-        }
-        if (posix) {
-            flags += "P";
-        }
-        final String[] cmdAttribs =
-            flags.length() > 1 ? new String[] {DF, flags, path} : new String[] {DF, path};
-
-        // perform the command, asking for up to 3 lines (header, interesting, overflow)
-        final List<String> lines = performCommand(cmdAttribs, 3, timeout);
-        if (lines.size() < 2) {
-            // unknown problem, throw exception
-            throw new IOException(
-                    "Command line '" + DF + "' did not return info as expected " +
-                    "for path '" + path + "'- response was " + lines);
-        }
-        final String line2 = lines.get(1); // the line we're interested in
-
-        // Now, we tokenize the string. The fourth element is what we want.
-        StringTokenizer tok = new StringTokenizer(line2, " ");
-        if (tok.countTokens() < 4) {
-            // could be long Filesystem, thus data on third line
-            if ((tok.countTokens() != 1) || (lines.size() < 3)) {
-                throw new IOException(
-                        "Command line '" + DF + "' did not return data as expected " +
-                        "for path '" + path + "'- check path is valid");
-            }
-            final String line3 = lines.get(2); // the line may be interested in
-            tok = new StringTokenizer(line3, " ");
-        } else {
-            tok.nextToken(); // Ignore Filesystem
-        }
-        tok.nextToken(); // Ignore 1K-blocks
-        tok.nextToken(); // Ignore Used
-        final String freeSpace = tok.nextToken();
-        return parseBytes(freeSpace, path);
-    }
-
-    /**
-     * Parses the bytes from a string.
-     *
-     * @param freeSpace  the free space string
-     * @param path  the path
-     * @return the number of bytes
-     * @throws IOException if an error occurs
-     */
-    long parseBytes(final String freeSpace, final String path) throws IOException {
-        try {
-            final long bytes = Long.parseLong(freeSpace);
-            if (bytes < 0) {
-                throw new IOException(
-                        "Command line '" + DF + "' did not find free space in response " +
-                        "for path '" + path + "'- check path is valid");
-            }
-            return bytes;
-
-        } catch (final NumberFormatException ex) {
-            throw new IOException(
-                    "Command line '" + DF + "' did not return numeric data as expected " +
-                    "for path '" + path + "'- check path is valid", ex);
-        }
-    }
-
-    /**
      * Performs an OS command.
      *
      * @param cmdAttribs  the command line parameters
@@ -551,15 +562,4 @@ public class FileSystemUtils {
         }
     }
 
-    /**
-     * Opens the process to the operating system.
-     *
-     * @param cmdAttribs  the command line parameters
-     * @return the process
-     * @throws IOException if an error occurs
-     */
-    Process openProcess(final String[] cmdAttribs) throws IOException {
-        return Runtime.getRuntime().exec(cmdAttribs);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/FilenameUtils.java b/src/main/java/org/apache/commons/io/FilenameUtils.java
index 3b8888b..ad58b33 100644
--- a/src/main/java/org/apache/commons/io/FilenameUtils.java
+++ b/src/main/java/org/apache/commons/io/FilenameUtils.java
@@ -511,6 +511,22 @@ public class FilenameUtils {
     }
 
     /**
+     * Flips the Windows name separator to Linux and vice-versa.
+     *
+     * @param ch The Windows or Linux name separator.
+     * @return The Windows or Linux name separator.
+     */
+    static char flipSeparator(final char ch) {
+        if (ch == UNIX_NAME_SEPARATOR) {
+            return WINDOWS_NAME_SEPARATOR;
+        }
+        if (ch == WINDOWS_NAME_SEPARATOR) {
+            return UNIX_NAME_SEPARATOR;
+        }
+        throw new IllegalArgumentException(String.valueOf(ch));
+    }
+
+    /**
      * Special handling for NTFS ADS: Don't accept colon in the fileName.
      *
      * @param fileName a file name
@@ -1175,22 +1191,6 @@ public class FilenameUtils {
     }
 
     /**
-     * Flips the Windows name separator to Linux and vice-versa.
-     *
-     * @param ch The Windows or Linux name separator.
-     * @return The Windows or Linux name separator.
-     */
-    static char flipSeparator(final char ch) {
-        if (ch == UNIX_NAME_SEPARATOR) {
-            return WINDOWS_NAME_SEPARATOR;
-        }
-        if (ch == WINDOWS_NAME_SEPARATOR) {
-            return UNIX_NAME_SEPARATOR;
-        }
-        throw new IllegalArgumentException(String.valueOf(ch));
-    }
-
-    /**
      * Determines if Windows file system is in use.
      *
      * @return true if the system is Windows
diff --git a/src/main/java/org/apache/commons/io/HexDump.java b/src/main/java/org/apache/commons/io/HexDump.java
index 86b49b6..bd6a812 100644
--- a/src/main/java/org/apache/commons/io/HexDump.java
+++ b/src/main/java/org/apache/commons/io/HexDump.java
@@ -32,11 +32,21 @@ import java.nio.charset.Charset;
 public class HexDump {
 
     /**
-     * Instances should NOT be constructed in standard programming.
+     * The line-separator (initializes to "line.separator" system property.
      */
-    public HexDump() {
-    }
+    public static final String EOL =
+            System.getProperty("line.separator");
 
+    private static final char[] _hexcodes =
+            {
+                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+                'A', 'B', 'C', 'D', 'E', 'F'
+            };
+
+    private static final int[] _shifts =
+            {
+                28, 24, 20, 16, 12, 8, 4, 0
+            };
     /**
      * Dump an array of bytes to an OutputStream. The output is formatted
      * for human inspection, with a hexadecimal offset followed by the
@@ -113,21 +123,19 @@ public class HexDump {
             display_offset += chars_read;
         }
     }
-
     /**
-     * The line-separator (initializes to "line.separator" system property.
+     * Dump a byte value into a StringBuilder.
+     *
+     * @param _cbuffer the StringBuilder to dump the value in
+     * @param value  the byte value to be dumped
+     * @return StringBuilder containing the dumped value.
      */
-    public static final String EOL =
-            System.getProperty("line.separator");
-    private static final char[] _hexcodes =
-            {
-                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
-                'A', 'B', 'C', 'D', 'E', 'F'
-            };
-    private static final int[] _shifts =
-            {
-                28, 24, 20, 16, 12, 8, 4, 0
-            };
+    private static StringBuilder dump(final StringBuilder _cbuffer, final byte value) {
+        for (int j = 0; j < 2; j++) {
+            _cbuffer.append(_hexcodes[value >> _shifts[j + 6] & 15]);
+        }
+        return _cbuffer;
+    }
 
     /**
      * Dump a long value into a StringBuilder.
@@ -145,17 +153,9 @@ public class HexDump {
     }
 
     /**
-     * Dump a byte value into a StringBuilder.
-     *
-     * @param _cbuffer the StringBuilder to dump the value in
-     * @param value  the byte value to be dumped
-     * @return StringBuilder containing the dumped value.
+     * Instances should NOT be constructed in standard programming.
      */
-    private static StringBuilder dump(final StringBuilder _cbuffer, final byte value) {
-        for (int j = 0; j < 2; j++) {
-            _cbuffer.append(_hexcodes[value >> _shifts[j + 6] & 15]);
-        }
-        return _cbuffer;
+    public HexDump() {
     }
 
 }
diff --git a/src/main/java/org/apache/commons/io/IOCase.java b/src/main/java/org/apache/commons/io/IOCase.java
index 6226ea5..201d5f3 100644
--- a/src/main/java/org/apache/commons/io/IOCase.java
+++ b/src/main/java/org/apache/commons/io/IOCase.java
@@ -64,6 +64,25 @@ public enum IOCase {
      */
     SYSTEM("System", !FilenameUtils.isSystemWindows());
 
+    /** Serialization version. */
+    private static final long serialVersionUID = -6343169151696340687L;
+
+    /**
+     * Factory method to create an IOCase from a name.
+     *
+     * @param name  the name to find
+     * @return the IOCase object
+     * @throws IllegalArgumentException if the name is invalid
+     */
+    public static IOCase forName(final String name) {
+        for (final IOCase ioCase : IOCase.values()) {
+            if (ioCase.getName().equals(name)) {
+                return ioCase;
+            }
+        }
+        throw new IllegalArgumentException("Invalid IOCase name: " + name);
+    }
+
     /**
      * Tests for cases sensitivity in a null-safe manner.
      *
@@ -87,9 +106,6 @@ public enum IOCase {
         return value != null ? value : defaultValue;
     }
 
-    /** Serialization version. */
-    private static final long serialVersionUID = -6343169151696340687L;
-
     /** The enumeration name. */
     private final String name;
 
@@ -97,22 +113,6 @@ public enum IOCase {
     private final transient boolean sensitive;
 
     /**
-     * Factory method to create an IOCase from a name.
-     *
-     * @param name  the name to find
-     * @return the IOCase object
-     * @throws IllegalArgumentException if the name is invalid
-     */
-    public static IOCase forName(final String name) {
-        for (final IOCase ioCase : IOCase.values()) {
-            if (ioCase.getName().equals(name)) {
-                return ioCase;
-            }
-        }
-        throw new IllegalArgumentException("Invalid IOCase name: " + name);
-    }
-
-    /**
      * Constructs a new instance.
      *
      * @param name  the name
@@ -124,34 +124,6 @@ public enum IOCase {
     }
 
     /**
-     * Replaces the enumeration from the stream with a real one.
-     * This ensures that the correct flag is set for SYSTEM.
-     *
-     * @return the resolved object
-     */
-    private Object readResolve() {
-        return forName(name);
-    }
-
-    /**
-     * Gets the name of the constant.
-     *
-     * @return the name of the constant
-     */
-    public String getName() {
-        return name;
-    }
-
-    /**
-     * Does the object represent case sensitive comparison.
-     *
-     * @return true if case sensitive
-     */
-    public boolean isCaseSensitive() {
-        return sensitive;
-    }
-
-    /**
      * Compares two strings using the case-sensitivity rule.
      * <p>
      * This method mimics {@link String#compareTo} but takes case-sensitivity
@@ -170,39 +142,6 @@ public enum IOCase {
     }
 
     /**
-     * Compares two strings using the case-sensitivity rule.
-     * <p>
-     * This method mimics {@link String#equals} but takes case-sensitivity
-     * into account.
-     * </p>
-     *
-     * @param str1  the first string to compare, not null
-     * @param str2  the second string to compare, not null
-     * @return true if equal using the case rules
-     * @throws NullPointerException if either string is null
-     */
-    public boolean checkEquals(final String str1, final String str2) {
-        Objects.requireNonNull(str1, "str1");
-        Objects.requireNonNull(str2, "str2");
-        return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2);
-    }
-
-    /**
-     * Checks if one string starts with another using the case-sensitivity rule.
-     * <p>
-     * This method mimics {@link String#startsWith(String)} but takes case-sensitivity
-     * into account.
-     * </p>
-     *
-     * @param str  the string to check
-     * @param start  the start to compare against
-     * @return true if equal using the case rules, false if either input is null
-     */
-    public boolean checkStartsWith(final String str, final String start) {
-        return str != null && start != null && str.regionMatches(!sensitive, 0, start, 0, start.length());
-    }
-
-    /**
      * Checks if one string ends with another using the case-sensitivity rule.
      * <p>
      * This method mimics {@link String#endsWith} but takes case-sensitivity
@@ -222,6 +161,24 @@ public enum IOCase {
     }
 
     /**
+     * Compares two strings using the case-sensitivity rule.
+     * <p>
+     * This method mimics {@link String#equals} but takes case-sensitivity
+     * into account.
+     * </p>
+     *
+     * @param str1  the first string to compare, not null
+     * @param str2  the second string to compare, not null
+     * @return true if equal using the case rules
+     * @throws NullPointerException if either string is null
+     */
+    public boolean checkEquals(final String str1, final String str2) {
+        Objects.requireNonNull(str1, "str1");
+        Objects.requireNonNull(str2, "str2");
+        return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2);
+    }
+
+    /**
      * Checks if one string contains another starting at a specific index using the
      * case-sensitivity rule.
      * <p>
@@ -267,6 +224,49 @@ public enum IOCase {
     }
 
     /**
+     * Checks if one string starts with another using the case-sensitivity rule.
+     * <p>
+     * This method mimics {@link String#startsWith(String)} but takes case-sensitivity
+     * into account.
+     * </p>
+     *
+     * @param str  the string to check
+     * @param start  the start to compare against
+     * @return true if equal using the case rules, false if either input is null
+     */
+    public boolean checkStartsWith(final String str, final String start) {
+        return str != null && start != null && str.regionMatches(!sensitive, 0, start, 0, start.length());
+    }
+
+    /**
+     * Gets the name of the constant.
+     *
+     * @return the name of the constant
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Does the object represent case sensitive comparison.
+     *
+     * @return true if case sensitive
+     */
+    public boolean isCaseSensitive() {
+        return sensitive;
+    }
+
+    /**
+     * Replaces the enumeration from the stream with a real one.
+     * This ensures that the correct flag is set for SYSTEM.
+     *
+     * @return the resolved object
+     */
+    private Object readResolve() {
+        return forName(name);
+    }
+
+    /**
      * Gets a string describing the sensitivity.
      *
      * @return a string describing the sensitivity
diff --git a/src/main/java/org/apache/commons/io/IOIndexedException.java b/src/main/java/org/apache/commons/io/IOIndexedException.java
index ce45521..71253d4 100644
--- a/src/main/java/org/apache/commons/io/IOIndexedException.java
+++ b/src/main/java/org/apache/commons/io/IOIndexedException.java
@@ -27,19 +27,6 @@ import java.io.IOException;
 public class IOIndexedException extends IOException {
 
     private static final long serialVersionUID = 1L;
-    private final int index;
-
-    /**
-     * Creates a new exception.
-     *
-     * @param index index of this exception.
-     * @param cause cause exceptions.
-     */
-    public IOIndexedException(final int index, final Throwable cause) {
-        super(toMessage(index, cause), cause);
-        this.index = index;
-    }
-
     /**
      * Converts input to a suitable String for exception message.
      *
@@ -55,6 +42,19 @@ public class IOIndexedException extends IOException {
         return String.format("%s #%,d: %s", name, index, msg);
     }
 
+    private final int index;
+
+    /**
+     * Creates a new exception.
+     *
+     * @param index index of this exception.
+     * @param cause cause exceptions.
+     */
+    public IOIndexedException(final int index, final Throwable cause) {
+        super(toMessage(index, cause), cause);
+        this.index = index;
+    }
+
     /**
      * The index of this exception.
      *
diff --git a/src/main/java/org/apache/commons/io/LineIterator.java b/src/main/java/org/apache/commons/io/LineIterator.java
index e49592f..aff8b1f 100644
--- a/src/main/java/org/apache/commons/io/LineIterator.java
+++ b/src/main/java/org/apache/commons/io/LineIterator.java
@@ -51,10 +51,23 @@ public class LineIterator implements Iterator<String>, Closeable {
 
     // N.B. This class deliberately does not implement Iterable, see https://issues.apache.org/jira/browse/IO-181
 
+    /**
+     * Closes a {@code LineIterator} quietly.
+     *
+     * @param iterator The iterator to close, or {@code null}.
+     * @deprecated As of 2.6 deprecated without replacement. Please use the try-with-resources statement or handle
+     * suppressed exceptions manually.
+     * @see Throwable#addSuppressed(java.lang.Throwable)
+     */
+    @Deprecated
+    public static void closeQuietly(final LineIterator iterator) {
+        IOUtils.closeQuietly(iterator);
+    }
     /** The reader that is being read. */
     private final BufferedReader bufferedReader;
     /** The current line. */
     private String cachedLine;
+
     /** A flag indicating if the iterator has been fully read. */
     private boolean finished;
 
@@ -76,6 +89,22 @@ public class LineIterator implements Iterator<String>, Closeable {
     }
 
     /**
+     * Closes the underlying {@code Reader}.
+     * This method is useful if you only want to process the first few
+     * lines of a larger file. If you do not close the iterator
+     * then the {@code Reader} remains open.
+     * This method can safely be called multiple times.
+     *
+     * @throws IOException if closing the underlying {@code Reader} fails.
+     */
+    @Override
+    public void close() throws IOException {
+        finished = true;
+        cachedLine = null;
+        IOUtils.close(bufferedReader);
+    }
+
+    /**
      * Indicates whether the {@code Reader} has more lines.
      * If there is an {@code IOException} then {@link #close()} will
      * be called on this instance.
@@ -146,22 +175,6 @@ public class LineIterator implements Iterator<String>, Closeable {
     }
 
     /**
-     * Closes the underlying {@code Reader}.
-     * This method is useful if you only want to process the first few
-     * lines of a larger file. If you do not close the iterator
-     * then the {@code Reader} remains open.
-     * This method can safely be called multiple times.
-     *
-     * @throws IOException if closing the underlying {@code Reader} fails.
-     */
-    @Override
-    public void close() throws IOException {
-        finished = true;
-        cachedLine = null;
-        IOUtils.close(bufferedReader);
-    }
-
-    /**
      * Unsupported.
      *
      * @throws UnsupportedOperationException always
@@ -171,17 +184,4 @@ public class LineIterator implements Iterator<String>, Closeable {
         throw new UnsupportedOperationException("remove not supported");
     }
 
-    /**
-     * Closes a {@code LineIterator} quietly.
-     *
-     * @param iterator The iterator to close, or {@code null}.
-     * @deprecated As of 2.6 deprecated without replacement. Please use the try-with-resources statement or handle
-     * suppressed exceptions manually.
-     * @see Throwable#addSuppressed(java.lang.Throwable)
-     */
-    @Deprecated
-    public static void closeQuietly(final LineIterator iterator) {
-        IOUtils.closeQuietly(iterator);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/StreamIterator.java b/src/main/java/org/apache/commons/io/StreamIterator.java
index b5cc1c4..1d8a3ec 100644
--- a/src/main/java/org/apache/commons/io/StreamIterator.java
+++ b/src/main/java/org/apache/commons/io/StreamIterator.java
@@ -49,13 +49,22 @@ class StreamIterator<E> implements Iterator<E>, Closeable {
         return new StreamIterator<>(stream).iterator;
     }
 
+    private final Iterator<E> iterator;
+
+    private final Stream<E> stream;
     private StreamIterator(final Stream<E> stream) {
         this.stream = Objects.requireNonNull(stream, "stream");
         this.iterator = stream.iterator();
     }
 
-    private final Iterator<E> iterator;
-    private final Stream<E> stream;
+    /**
+     * Closes the underlying stream.
+     */
+    @Override
+    public void close() {
+        stream.close();
+
+    }
 
     @Override
     public boolean hasNext() {
@@ -75,13 +84,4 @@ class StreamIterator<E> implements Iterator<E>, Closeable {
         return next;
     }
 
-    /**
-     * Closes the underlying stream.
-     */
-    @Override
-    public void close() {
-        stream.close();
-
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/TaggedIOException.java b/src/main/java/org/apache/commons/io/TaggedIOException.java
index 4234e9e..43d7730 100644
--- a/src/main/java/org/apache/commons/io/TaggedIOException.java
+++ b/src/main/java/org/apache/commons/io/TaggedIOException.java
@@ -114,15 +114,6 @@ public class TaggedIOException extends IOExceptionWithCause {
     }
 
     /**
-     * Returns the serializable tag object.
-     *
-     * @return tag object
-     */
-    public Serializable getTag() {
-        return tag;
-    }
-
-    /**
      * Returns the wrapped exception. The only difference to the overridden
      * {@link Throwable#getCause()} method is the narrower return type.
      *
@@ -133,4 +124,13 @@ public class TaggedIOException extends IOExceptionWithCause {
         return (IOException) super.getCause();
     }
 
+    /**
+     * Returns the serializable tag object.
+     *
+     * @return tag object
+     */
+    public Serializable getTag() {
+        return tag;
+    }
+
 }
diff --git a/src/main/java/org/apache/commons/io/ThreadMonitor.java b/src/main/java/org/apache/commons/io/ThreadMonitor.java
index bd1fd16..0db54d0 100644
--- a/src/main/java/org/apache/commons/io/ThreadMonitor.java
+++ b/src/main/java/org/apache/commons/io/ThreadMonitor.java
@@ -41,8 +41,26 @@ import java.time.Instant;
  */
 class ThreadMonitor implements Runnable {
 
-    private final Thread thread;
-    private final Duration timeout;
+    private static int getNanosOfMiili(final Duration duration) {
+        return duration.getNano() % 1_000_000;
+    }
+    /**
+     * Sleeps for a guaranteed minimum duration unless interrupted.
+     *
+     * This method exists because Thread.sleep(100) can sleep for 0, 70, 100 or 200ms or anything else it deems appropriate.
+     * Read {@link Thread#sleep(long, int)}} for further interesting details.
+     *
+     * @param duration the sleep duration.
+     * @throws InterruptedException if interrupted.
+     */
+    static void sleep(final Duration duration) throws InterruptedException {
+        final Instant finishInstant = Instant.now().plus(duration);
+        Duration remainingDuration = duration;
+        do {
+            Thread.sleep(remainingDuration.toMillis(), getNanosOfMiili(remainingDuration));
+            remainingDuration = Duration.between(Instant.now(), finishInstant);
+        } while (!remainingDuration.isNegative());
+    }
 
     /**
      * Starts monitoring the current thread.
@@ -82,6 +100,10 @@ class ThreadMonitor implements Runnable {
         }
     }
 
+    private final Thread thread;
+
+    private final Duration timeout;
+
     /**
      * Constructs a new monitor.
      *
@@ -107,26 +129,4 @@ class ThreadMonitor implements Runnable {
             // timeout not reached
         }
     }
-
-    /**
-     * Sleeps for a guaranteed minimum duration unless interrupted.
-     *
-     * This method exists because Thread.sleep(100) can sleep for 0, 70, 100 or 200ms or anything else it deems appropriate.
-     * Read {@link Thread#sleep(long, int)}} for further interesting details.
-     *
-     * @param duration the sleep duration.
-     * @throws InterruptedException if interrupted.
-     */
-    static void sleep(final Duration duration) throws InterruptedException {
-        final Instant finishInstant = Instant.now().plus(duration);
-        Duration remainingDuration = duration;
-        do {
-            Thread.sleep(remainingDuration.toMillis(), getNanosOfMiili(remainingDuration));
-            remainingDuration = Duration.between(Instant.now(), finishInstant);
-        } while (!remainingDuration.isNegative());
-    }
-
-    private static int getNanosOfMiili(final Duration duration) {
-        return duration.getNano() % 1_000_000;
-    }
 }
diff --git a/src/main/java/org/apache/commons/io/file/Counters.java b/src/main/java/org/apache/commons/io/file/Counters.java
index 17f7682..daf08cf 100644
--- a/src/main/java/org/apache/commons/io/file/Counters.java
+++ b/src/main/java/org/apache/commons/io/file/Counters.java
@@ -155,13 +155,13 @@ public class Counters {
         }
 
         @Override
-        public String toString() {
-            return value.toString();
+        public void reset() {
+            value = BigInteger.ZERO;
         }
 
         @Override
-        public void reset() {
-            value = BigInteger.ZERO;
+        public String toString() {
+            return value.toString();
         }
     }
 
@@ -277,13 +277,13 @@ public class Counters {
         }
 
         @Override
-        public String toString() {
-            return Long.toString(value);
+        public void reset() {
+            value = 0L;
         }
 
         @Override
-        public void reset() {
-            value = 0L;
+        public String toString() {
+            return Long.toString(value);
         }
     }
 
diff --git a/src/main/java/org/apache/commons/io/file/PathUtils.java b/src/main/java/org/apache/commons/io/file/PathUtils.java
index 7a573ee..9d65243 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -79,10 +79,6 @@ import org.apache.commons.io.filefilter.IOFileFilter;
  */
 public final class PathUtils {
 
-    private static final OpenOption[] OPEN_OPTIONS_TRUNCATE = new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
-
-    private static final OpenOption[] OPEN_OPTIONS_APPEND = new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.APPEND};
-
     /**
      * Private worker/holder that computes and tracks relative path names and their equality. We reuse the sorted relative
      * lists when comparing directories.
@@ -145,6 +141,10 @@ public final class PathUtils {
         }
     }
 
+    private static final OpenOption[] OPEN_OPTIONS_TRUNCATE = new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+
+    private static final OpenOption[] OPEN_OPTIONS_APPEND = new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.APPEND};
+
     /**
      * Empty {@link CopyOption} array.
      *
diff --git a/src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java
index fa3b282..8a8d030 100644
--- a/src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java
@@ -73,6 +73,17 @@ public class AndFileFilter
     }
 
     /**
+     * Constructs a new instance for the give filters.
+     * @param fileFilters filters to OR.
+     *
+     * @since 2.9.0
+     */
+    public AndFileFilter(final IOFileFilter... fileFilters) {
+        this(Objects.requireNonNull(fileFilters, "fileFilters").length);
+        addFileFilter(fileFilters);
+    }
+
+    /**
      * Constructs a new file filter that ANDs the result of other filters.
      *
      * @param filter1  the first filter, must second be null
@@ -86,17 +97,6 @@ public class AndFileFilter
     }
 
     /**
-     * Constructs a new instance for the give filters.
-     * @param fileFilters filters to OR.
-     *
-     * @since 2.9.0
-     */
-    public AndFileFilter(final IOFileFilter... fileFilters) {
-        this(Objects.requireNonNull(fileFilters, "fileFilters").length);
-        addFileFilter(fileFilters);
-    }
-
-    /**
      * Constructs a new instance of {@code AndFileFilter}
      * with the specified list of filters.
      *
diff --git a/src/main/java/org/apache/commons/io/filefilter/FalseFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/FalseFileFilter.java
index 51545a1..8119f53 100644
--- a/src/main/java/org/apache/commons/io/filefilter/FalseFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/FalseFileFilter.java
@@ -90,24 +90,24 @@ public class FalseFileFilter implements IOFileFilter, Serializable {
     }
 
     @Override
-    public IOFileFilter negate() {
-        return TrueFileFilter.INSTANCE;
-    }
-
-    @Override
-    public String toString() {
-        return TO_STRING;
-    }
-
-    @Override
     public IOFileFilter and(final IOFileFilter fileFilter) {
         // FALSE AND expression <=> FALSE
         return INSTANCE;
     }
 
     @Override
+    public IOFileFilter negate() {
+        return TrueFileFilter.INSTANCE;
+    }
+
+    @Override
     public IOFileFilter or(final IOFileFilter fileFilter) {
         // FALSE OR expression <=> expression
         return fileFilter;
     }
+
+    @Override
+    public String toString() {
+        return TO_STRING;
+    }
 }
diff --git a/src/main/java/org/apache/commons/io/filefilter/FileFilterUtils.java b/src/main/java/org/apache/commons/io/filefilter/FileFilterUtils.java
index 89ed47d..5f72405 100644
--- a/src/main/java/org/apache/commons/io/filefilter/FileFilterUtils.java
+++ b/src/main/java/org/apache/commons/io/filefilter/FileFilterUtils.java
@@ -251,33 +251,6 @@ public class FileFilterUtils {
 
     /**
      * <p>
-     * Applies an {@link IOFileFilter} to the provided {@link File} stream and collects the accepted files.
-     * </p>
-     *
-     * @param filter the filter to apply to the stream of files.
-     * @param stream the stream of files on which to apply the filter.
-     * @param collector how to collect the end result.
-     *
-     * @param <R> the return type.
-     * @param <A> the mutable accumulation type of the reduction operation (often hidden as an implementation detail)
-     * @return a subset of files from the stream that is accepted by the filter.
-     * @throws IllegalArgumentException if the filter is {@code null}.
-     */
-    private static <R, A> R filterFiles(final IOFileFilter filter, final Stream<File> stream,
-        final Collector<? super File, A, R> collector) {
-        //Objects.requireNonNull(filter, "filter");
-        Objects.requireNonNull(collector, "collector");
-        if (filter == null) {
-            throw new IllegalArgumentException("file filter is null");
-        }
-        if (stream == null) {
-            return Stream.<File>empty().collect(collector);
-        }
-        return stream.filter(filter::accept).collect(collector);
-    }
-
-    /**
-     * <p>
      * Applies an {@link IOFileFilter} to the provided {@link File}
      * objects. The resulting array is a subset of the original file list that
      * matches the provided filter.
@@ -308,6 +281,33 @@ public class FileFilterUtils {
 
     /**
      * <p>
+     * Applies an {@link IOFileFilter} to the provided {@link File} stream and collects the accepted files.
+     * </p>
+     *
+     * @param filter the filter to apply to the stream of files.
+     * @param stream the stream of files on which to apply the filter.
+     * @param collector how to collect the end result.
+     *
+     * @param <R> the return type.
+     * @param <A> the mutable accumulation type of the reduction operation (often hidden as an implementation detail)
+     * @return a subset of files from the stream that is accepted by the filter.
+     * @throws IllegalArgumentException if the filter is {@code null}.
+     */
+    private static <R, A> R filterFiles(final IOFileFilter filter, final Stream<File> stream,
+        final Collector<? super File, A, R> collector) {
+        //Objects.requireNonNull(filter, "filter");
+        Objects.requireNonNull(collector, "collector");
+        if (filter == null) {
+            throw new IllegalArgumentException("file filter is null");
+        }
+        if (stream == null) {
+            return Stream.<File>empty().collect(collector);
+        }
+        return stream.filter(filter::accept).collect(collector);
+    }
+
+    /**
+     * <p>
      * Applies an {@link IOFileFilter} to the provided {@link File}
      * objects. The resulting list is a subset of the original files that
      * matches the provided filter.
diff --git a/src/main/java/org/apache/commons/io/filefilter/TrueFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/TrueFileFilter.java
index a3fa9fd..aa4001b 100644
--- a/src/main/java/org/apache/commons/io/filefilter/TrueFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/TrueFileFilter.java
@@ -89,6 +89,12 @@ public class TrueFileFilter implements IOFileFilter, Serializable {
     }
 
     @Override
+    public IOFileFilter and(final IOFileFilter fileFilter) {
+        // TRUE AND expression <=> expression
+        return fileFilter;
+    }
+
+    @Override
     public IOFileFilter negate() {
         return FalseFileFilter.INSTANCE;
     }
@@ -100,12 +106,6 @@ public class TrueFileFilter implements IOFileFilter, Serializable {
     }
 
     @Override
-    public IOFileFilter and(final IOFileFilter fileFilter) {
-        // TRUE AND expression <=> expression
-        return fileFilter;
-    }
-
-    @Override
     public String toString() {
         return TO_STRING;
     }
diff --git a/src/main/java/org/apache/commons/io/filefilter/WildcardFilter.java b/src/main/java/org/apache/commons/io/filefilter/WildcardFilter.java
index 399a6c4..38bb6c6 100644
--- a/src/main/java/org/apache/commons/io/filefilter/WildcardFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/WildcardFilter.java
@@ -141,46 +141,46 @@ public class WildcardFilter extends AbstractFileFilter implements Serializable {
 
     /**
      * Checks to see if the file name matches one of the wildcards.
-     * @param file the file to check
      *
+     * @param dir  the file directory
+     * @param name  the file name
      * @return true if the file name matches one of the wildcards
-     * @since 2.9.0
      */
     @Override
-    public FileVisitResult accept(final Path file, final BasicFileAttributes attributes) {
-        if (Files.isDirectory(file)) {
-            return FileVisitResult.TERMINATE;
+    public boolean accept(final File dir, final String name) {
+        if (dir != null && new File(dir, name).isDirectory()) {
+            return false;
         }
 
         for (final String wildcard : wildcards) {
-            if (FilenameUtils.wildcardMatch(Objects.toString(file.getFileName(), null), wildcard)) {
-                return FileVisitResult.CONTINUE;
+            if (FilenameUtils.wildcardMatch(name, wildcard)) {
+                return true;
             }
         }
 
-        return FileVisitResult.TERMINATE;
+        return false;
     }
 
     /**
      * Checks to see if the file name matches one of the wildcards.
+     * @param file the file to check
      *
-     * @param dir  the file directory
-     * @param name  the file name
      * @return true if the file name matches one of the wildcards
+     * @since 2.9.0
      */
     @Override
-    public boolean accept(final File dir, final String name) {
-        if (dir != null && new File(dir, name).isDirectory()) {
-            return false;
+    public FileVisitResult accept(final Path file, final BasicFileAttributes attributes) {
+        if (Files.isDirectory(file)) {
+            return FileVisitResult.TERMINATE;
         }
 
         for (final String wildcard : wildcards) {
-            if (FilenameUtils.wildcardMatch(name, wildcard)) {
-                return true;
+            if (FilenameUtils.wildcardMatch(Objects.toString(file.getFileName(), null), wildcard)) {
+                return FileVisitResult.CONTINUE;
             }
         }
 
-        return false;
+        return FileVisitResult.TERMINATE;
     }
 
 }
diff --git a/src/main/java/org/apache/commons/io/function/IOFunction.java b/src/main/java/org/apache/commons/io/function/IOFunction.java
index 4129f57..ab2a337 100644
--- a/src/main/java/org/apache/commons/io/function/IOFunction.java
+++ b/src/main/java/org/apache/commons/io/function/IOFunction.java
@@ -34,6 +34,92 @@ import java.util.function.Supplier;
 public interface IOFunction<T, R> {
 
     /**
+     * Returns a {@link IOFunction} that always returns its input argument.
+     *
+     * @param <T> the type of the input and output objects to the function
+     * @return a function that always returns its input argument
+     */
+    static <T> IOFunction<T, T> identity() {
+        return t -> t;
+    }
+
+    /**
+     * Returns a composed {@link IOFunction} that first applies this function to
+     * its input, and then applies the {@code after} consumer to the result.
+     * If evaluation of either function throws an exception, it is relayed to
+     * the caller of the composed function.
+     *
+     * @param after the consumer to apply after this function is applied
+     * @return a composed function that first applies this function and then
+     * applies the {@code after} consumer
+     * @throws NullPointerException if after is null
+     *
+     * @see #compose(IOFunction)
+     */
+    default IOConsumer<T> andThen(final Consumer<? super R> after) {
+        Objects.requireNonNull(after, "after");
+        return (final T t) -> after.accept(apply(t));
+    }
+
+    /**
+     * Returns a composed {@link IOFunction} that first applies this function to
+     * its input, and then applies the {@code after} function to the result.
+     * If evaluation of either function throws an exception, it is relayed to
+     * the caller of the composed function.
+     *
+     * @param <V> the type of output of the {@code after} function, and of the
+     *           composed function
+     * @param after the function to apply after this function is applied
+     * @return a composed function that first applies this function and then
+     * applies the {@code after} function
+     * @throws NullPointerException if after is null
+     *
+     * @see #compose(IOFunction)
+     */
+    default <V> IOFunction<T, V> andThen(final Function<? super R, ? extends V> after) {
+        Objects.requireNonNull(after, "after");
+        return (final T t) -> after.apply(apply(t));
+    }
+
+    /**
+     * Returns a composed {@link IOFunction} that first applies this function to
+     * its input, and then applies the {@code after} consumer to the result.
+     * If evaluation of either function throws an exception, it is relayed to
+     * the caller of the composed function.
+     *
+     * @param after the consumer to apply after this function is applied
+     * @return a composed function that first applies this function and then
+     * applies the {@code after} consumer
+     * @throws NullPointerException if after is null
+     *
+     * @see #compose(IOFunction)
+     */
+    default IOConsumer<T> andThen(final IOConsumer<? super R> after) {
+        Objects.requireNonNull(after, "after");
+        return (final T t) -> after.accept(apply(t));
+    }
+
+    /**
+     * Returns a composed {@link IOFunction} that first applies this function to
+     * its input, and then applies the {@code after} function to the result.
+     * If evaluation of either function throws an exception, it is relayed to
+     * the caller of the composed function.
+     *
+     * @param <V> the type of output of the {@code after} function, and of the
+     *           composed function
+     * @param after the function to apply after this function is applied
+     * @return a composed function that first applies this function and then
+     * applies the {@code after} function
+     * @throws NullPointerException if after is null
+     *
+     * @see #compose(IOFunction)
+     */
+    default <V> IOFunction<T, V> andThen(final IOFunction<? super R, ? extends V> after) {
+        Objects.requireNonNull(after, "after");
+        return (final T t) -> after.apply(apply(t));
+    }
+
+    /**
      * Applies this function to the given argument.
      *
      * @param t the function argument
@@ -58,7 +144,7 @@ public interface IOFunction<T, R> {
      *
      * @see #andThen(IOFunction)
      */
-    default <V> IOFunction<V, R> compose(final IOFunction<? super V, ? extends T> before) {
+    default <V> IOFunction<V, R> compose(final Function<? super V, ? extends T> before) {
         Objects.requireNonNull(before, "before");
         return (final V v) -> apply(before.apply(v));
     }
@@ -78,7 +164,7 @@ public interface IOFunction<T, R> {
      *
      * @see #andThen(IOFunction)
      */
-    default <V> IOFunction<V, R> compose(final Function<? super V, ? extends T> before) {
+    default <V> IOFunction<V, R> compose(final IOFunction<? super V, ? extends T> before) {
         Objects.requireNonNull(before, "before");
         return (final V v) -> apply(before.apply(v));
     }
@@ -118,90 +204,4 @@ public interface IOFunction<T, R> {
         Objects.requireNonNull(before, "before");
         return () -> apply(before.get());
     }
-
-    /**
-     * Returns a composed {@link IOFunction} that first applies this function to
-     * its input, and then applies the {@code after} function to the result.
-     * If evaluation of either function throws an exception, it is relayed to
-     * the caller of the composed function.
-     *
-     * @param <V> the type of output of the {@code after} function, and of the
-     *           composed function
-     * @param after the function to apply after this function is applied
-     * @return a composed function that first applies this function and then
-     * applies the {@code after} function
-     * @throws NullPointerException if after is null
-     *
-     * @see #compose(IOFunction)
-     */
-    default <V> IOFunction<T, V> andThen(final IOFunction<? super R, ? extends V> after) {
-        Objects.requireNonNull(after, "after");
-        return (final T t) -> after.apply(apply(t));
-    }
-
-    /**
-     * Returns a composed {@link IOFunction} that first applies this function to
-     * its input, and then applies the {@code after} function to the result.
-     * If evaluation of either function throws an exception, it is relayed to
-     * the caller of the composed function.
-     *
-     * @param <V> the type of output of the {@code after} function, and of the
-     *           composed function
-     * @param after the function to apply after this function is applied
-     * @return a composed function that first applies this function and then
-     * applies the {@code after} function
-     * @throws NullPointerException if after is null
-     *
-     * @see #compose(IOFunction)
-     */
-    default <V> IOFunction<T, V> andThen(final Function<? super R, ? extends V> after) {
-        Objects.requireNonNull(after, "after");
-        return (final T t) -> after.apply(apply(t));
-    }
-
-    /**
-     * Returns a composed {@link IOFunction} that first applies this function to
-     * its input, and then applies the {@code after} consumer to the result.
-     * If evaluation of either function throws an exception, it is relayed to
-     * the caller of the composed function.
-     *
-     * @param after the consumer to apply after this function is applied
-     * @return a composed function that first applies this function and then
-     * applies the {@code after} consumer
-     * @throws NullPointerException if after is null
-     *
-     * @see #compose(IOFunction)
-     */
-    default IOConsumer<T> andThen(final IOConsumer<? super R> after) {
-        Objects.requireNonNull(after, "after");
-        return (final T t) -> after.accept(apply(t));
-    }
-
-    /**
-     * Returns a composed {@link IOFunction} that first applies this function to
-     * its input, and then applies the {@code after} consumer to the result.
-     * If evaluation of either function throws an exception, it is relayed to
-     * the caller of the composed function.
-     *
-     * @param after the consumer to apply after this function is applied
-     * @return a composed function that first applies this function and then
-     * applies the {@code after} consumer
-     * @throws NullPointerException if after is null
-     *
-     * @see #compose(IOFunction)
-     */
-    default IOConsumer<T> andThen(final Consumer<? super R> after) {
-        Objects.requireNonNull(after, "after");
-        return (final T t) -> after.accept(apply(t));
-    }
-
-    /**
-     * Returns a {@link IOFunction} that always returns its input argument.
-     *
-     * @param <T> the type of the input and output objects to the function
-     * @return a function that always returns its input argument
-     */
-    static <T> IOFunction<T, T> identity() {
-        return t -> t;
-    }
 }
diff --git a/src/main/java/org/apache/commons/io/input/AutoCloseInputStream.java b/src/main/java/org/apache/commons/io/input/AutoCloseInputStream.java
index 86b3530..bf3d317 100644
--- a/src/main/java/org/apache/commons/io/input/AutoCloseInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/AutoCloseInputStream.java
@@ -48,6 +48,20 @@ public class AutoCloseInputStream extends ProxyInputStream {
     }
 
     /**
+     * Automatically closes the stream if the end of stream was reached.
+     *
+     * @param n number of bytes read, or -1 if no more bytes are available
+     * @throws IOException if the stream could not be closed
+     * @since 2.0
+     */
+    @Override
+    protected void afterRead(final int n) throws IOException {
+        if (n == EOF) {
+            close();
+        }
+    }
+
+    /**
      * Closes the underlying input stream and replaces the reference to it
      * with a {@link ClosedInputStream} instance.
      * <p>
@@ -67,20 +81,6 @@ public class AutoCloseInputStream extends ProxyInputStream {
     }
 
     /**
-     * Automatically closes the stream if the end of stream was reached.
-     *
-     * @param n number of bytes read, or -1 if no more bytes are available
-     * @throws IOException if the stream could not be closed
-     * @since 2.0
-     */
-    @Override
-    protected void afterRead(final int n) throws IOException {
-        if (n == EOF) {
-            close();
-        }
-    }
-
-    /**
      * Ensures that the stream is closed before it gets garbage-collected.
      * As mentioned in {@link #close()}, this is a no-op if the stream has
      * already been closed.
diff --git a/src/main/java/org/apache/commons/io/input/BOMInputStream.java b/src/main/java/org/apache/commons/io/input/BOMInputStream.java
index e948378..2cfe523 100644
--- a/src/main/java/org/apache/commons/io/input/BOMInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/BOMInputStream.java
@@ -88,6 +88,14 @@ import org.apache.commons.io.IOUtils;
  * @since 2.0
  */
 public class BOMInputStream extends ProxyInputStream {
+    /**
+     * Compares ByteOrderMark objects in descending length order.
+     */
+    private static final Comparator<ByteOrderMark> ByteOrderMarkLengthComparator = (bom1, bom2) -> {
+        final int len1 = bom1.length();
+        final int len2 = bom2.length();
+        return Integer.compare(len2, len1);
+    };
     private final boolean include;
     /**
      * BOMs are sorted from longest to shortest.
@@ -98,6 +106,7 @@ public class BOMInputStream extends ProxyInputStream {
     private int fbLength;
     private int fbIndex;
     private int markFbIndex;
+
     private boolean markedAtStart;
 
     /**
@@ -123,27 +132,6 @@ public class BOMInputStream extends ProxyInputStream {
     }
 
     /**
-     * Constructs a new BOM InputStream that excludes the specified BOMs.
-     *
-     * @param delegate
-     *            the InputStream to delegate to
-     * @param boms
-     *            The BOMs to detect and exclude
-     */
-    public BOMInputStream(final InputStream delegate, final ByteOrderMark... boms) {
-        this(delegate, false, boms);
-    }
-
-    /**
-     * Compares ByteOrderMark objects in descending length order.
-     */
-    private static final Comparator<ByteOrderMark> ByteOrderMarkLengthComparator = (bom1, bom2) -> {
-        final int len1 = bom1.length();
-        final int len2 = bom2.length();
-        return Integer.compare(len2, len1);
-    };
-
-    /**
      * Constructs a new BOM InputStream that detects the specified BOMs and optionally includes them.
      *
      * @param delegate
@@ -167,33 +155,29 @@ public class BOMInputStream extends ProxyInputStream {
     }
 
     /**
-     * Indicates whether the stream contains one of the specified BOMs.
+     * Constructs a new BOM InputStream that excludes the specified BOMs.
      *
-     * @return true if the stream has one of the specified BOMs, otherwise false if it does not
-     * @throws IOException
-     *             if an error reading the first bytes of the stream occurs
+     * @param delegate
+     *            the InputStream to delegate to
+     * @param boms
+     *            The BOMs to detect and exclude
      */
-    public boolean hasBOM() throws IOException {
-        return getBOM() != null;
+    public BOMInputStream(final InputStream delegate, final ByteOrderMark... boms) {
+        this(delegate, false, boms);
     }
 
     /**
-     * Indicates whether the stream contains the specified BOM.
+     * Find a BOM with the specified bytes.
      *
-     * @param bom
-     *            The BOM to check for
-     * @return true if the stream has the specified BOM, otherwise false if it does not
-     * @throws IllegalArgumentException
-     *             if the BOM is not one the stream is configured to detect
-     * @throws IOException
-     *             if an error reading the first bytes of the stream occurs
+     * @return The matched BOM or null if none matched
      */
-    public boolean hasBOM(final ByteOrderMark bom) throws IOException {
-        if (!boms.contains(bom)) {
-            throw new IllegalArgumentException("Stream not configure to detect " + bom);
+    private ByteOrderMark find() {
+        for (final ByteOrderMark bom : boms) {
+            if (matches(bom)) {
+                return bom;
+            }
         }
-        getBOM();
-        return byteOrderMark != null && byteOrderMark.equals(bom);
+        return null;
     }
 
     /**
@@ -244,31 +228,46 @@ public class BOMInputStream extends ProxyInputStream {
     }
 
     /**
-     * This method reads and either preserves or skips the first bytes in the stream. It behaves like the single-byte
-     * {@code read()} method, either returning a valid byte or -1 to indicate that the initial bytes have been
-     * processed already.
+     * Indicates whether the stream contains one of the specified BOMs.
      *
-     * @return the byte read (excluding BOM) or -1 if the end of stream
+     * @return true if the stream has one of the specified BOMs, otherwise false if it does not
      * @throws IOException
-     *             if an I/O error occurs
+     *             if an error reading the first bytes of the stream occurs
      */
-    private int readFirstBytes() throws IOException {
-        getBOM();
-        return fbIndex < fbLength ? firstBytes[fbIndex++] : EOF;
+    public boolean hasBOM() throws IOException {
+        return getBOM() != null;
     }
 
     /**
-     * Find a BOM with the specified bytes.
+     * Indicates whether the stream contains the specified BOM.
      *
-     * @return The matched BOM or null if none matched
+     * @param bom
+     *            The BOM to check for
+     * @return true if the stream has the specified BOM, otherwise false if it does not
+     * @throws IllegalArgumentException
+     *             if the BOM is not one the stream is configured to detect
+     * @throws IOException
+     *             if an error reading the first bytes of the stream occurs
      */
-    private ByteOrderMark find() {
-        for (final ByteOrderMark bom : boms) {
-            if (matches(bom)) {
-                return bom;
-            }
+    public boolean hasBOM(final ByteOrderMark bom) throws IOException {
+        if (!boms.contains(bom)) {
+            throw new IllegalArgumentException("Stream not configure to detect " + bom);
         }
-        return null;
+        getBOM();
+        return byteOrderMark != null && byteOrderMark.equals(bom);
+    }
+
+    /**
+     * Invokes the delegate's {@code mark(int)} method.
+     *
+     * @param readlimit
+     *            read ahead limit
+     */
+    @Override
+    public synchronized void mark(final int readlimit) {
+        markFbIndex = fbIndex;
+        markedAtStart = firstBytes == null;
+        in.mark(readlimit);
     }
 
     /**
@@ -309,6 +308,20 @@ public class BOMInputStream extends ProxyInputStream {
     }
 
     /**
+     * Invokes the delegate's {@code read(byte[])} method, detecting and optionally skipping BOM.
+     *
+     * @param buf
+     *            the buffer to read the bytes into
+     * @return the number of bytes read (excluding BOM) or -1 if the end of stream
+     * @throws IOException
+     *             if an I/O error occurs
+     */
+    @Override
+    public int read(final byte[] buf) throws IOException {
+        return read(buf, 0, buf.length);
+    }
+
+    /**
      * Invokes the delegate's {@code read(byte[], int, int)} method, detecting and optionally skipping BOM.
      *
      * @param buf
@@ -338,30 +351,17 @@ public class BOMInputStream extends ProxyInputStream {
     }
 
     /**
-     * Invokes the delegate's {@code read(byte[])} method, detecting and optionally skipping BOM.
+     * This method reads and either preserves or skips the first bytes in the stream. It behaves like the single-byte
+     * {@code read()} method, either returning a valid byte or -1 to indicate that the initial bytes have been
+     * processed already.
      *
-     * @param buf
-     *            the buffer to read the bytes into
-     * @return the number of bytes read (excluding BOM) or -1 if the end of stream
+     * @return the byte read (excluding BOM) or -1 if the end of stream
      * @throws IOException
      *             if an I/O error occurs
      */
-    @Override
-    public int read(final byte[] buf) throws IOException {
-        return read(buf, 0, buf.length);
-    }
-
-    /**
-     * Invokes the delegate's {@code mark(int)} method.
-     *
-     * @param readlimit
-     *            read ahead limit
-     */
-    @Override
-    public synchronized void mark(final int readlimit) {
-        markFbIndex = fbIndex;
-        markedAtStart = firstBytes == null;
-        in.mark(readlimit);
+    private int readFirstBytes() throws IOException {
+        getBOM();
+        return fbIndex < fbLength ? firstBytes[fbIndex++] : EOF;
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/io/input/BoundedInputStream.java b/src/main/java/org/apache/commons/io/input/BoundedInputStream.java
index 6babddb..f09a6ee 100644
--- a/src/main/java/org/apache/commons/io/input/BoundedInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/BoundedInputStream.java
@@ -54,6 +54,16 @@ public class BoundedInputStream extends InputStream {
 
     /**
      * Creates a new {@code BoundedInputStream} that wraps the given input
+     * stream and is unlimited.
+     *
+     * @param in The wrapped input stream
+     */
+    public BoundedInputStream(final InputStream in) {
+        this(in, EOF);
+    }
+
+    /**
+     * Creates a new {@code BoundedInputStream} that wraps the given input
      * stream and limits it to a certain size.
      *
      * @param in The wrapped input stream
@@ -67,13 +77,57 @@ public class BoundedInputStream extends InputStream {
     }
 
     /**
-     * Creates a new {@code BoundedInputStream} that wraps the given input
-     * stream and is unlimited.
+     * {@inheritDoc}
+     */
+    @Override
+    public int available() throws IOException {
+        if (max>=0 && pos>=max) {
+            return 0;
+        }
+        return in.available();
+    }
+
+    /**
+     * Invokes the delegate's {@code close()} method
+     * if {@link #isPropagateClose()} is {@code true}.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void close() throws IOException {
+        if (propagateClose) {
+            in.close();
+        }
+    }
+
+    /**
+     * Indicates whether the {@link #close()} method
+     * should propagate to the underling {@link InputStream}.
      *
-     * @param in The wrapped input stream
+     * @return {@code true} if calling {@link #close()}
+     * propagates to the {@code close()} method of the
+     * underlying stream or {@code false} if it does not.
      */
-    public BoundedInputStream(final InputStream in) {
-        this(in, EOF);
+    public boolean isPropagateClose() {
+        return propagateClose;
+    }
+
+    /**
+     * Invokes the delegate's {@code mark(int)} method.
+     * @param readlimit read ahead limit
+     */
+    @Override
+    public synchronized void mark(final int readlimit) {
+        in.mark(readlimit);
+        mark = pos;
+    }
+
+    /**
+     * Invokes the delegate's {@code markSupported()} method.
+     * @return true if mark is supported, otherwise false
+     */
+    @Override
+    public boolean markSupported() {
+        return in.markSupported();
     }
 
     /**
@@ -131,52 +185,6 @@ public class BoundedInputStream extends InputStream {
     }
 
     /**
-     * Invokes the delegate's {@code skip(long)} method.
-     * @param n the number of bytes to skip
-     * @return the actual number of bytes skipped
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public long skip(final long n) throws IOException {
-        final long toSkip = max>=0 ? Math.min(n, max-pos) : n;
-        final long skippedBytes = in.skip(toSkip);
-        pos+=skippedBytes;
-        return skippedBytes;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int available() throws IOException {
-        if (max>=0 && pos>=max) {
-            return 0;
-        }
-        return in.available();
-    }
-
-    /**
-     * Invokes the delegate's {@code toString()} method.
-     * @return the delegate's {@code toString()}
-     */
-    @Override
-    public String toString() {
-        return in.toString();
-    }
-
-    /**
-     * Invokes the delegate's {@code close()} method
-     * if {@link #isPropagateClose()} is {@code true}.
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        if (propagateClose) {
-            in.close();
-        }
-    }
-
-    /**
      * Invokes the delegate's {@code reset()} method.
      * @throws IOException if an I/O error occurs.
      */
@@ -187,37 +195,6 @@ public class BoundedInputStream extends InputStream {
     }
 
     /**
-     * Invokes the delegate's {@code mark(int)} method.
-     * @param readlimit read ahead limit
-     */
-    @Override
-    public synchronized void mark(final int readlimit) {
-        in.mark(readlimit);
-        mark = pos;
-    }
-
-    /**
-     * Invokes the delegate's {@code markSupported()} method.
-     * @return true if mark is supported, otherwise false
-     */
-    @Override
-    public boolean markSupported() {
-        return in.markSupported();
-    }
-
-    /**
-     * Indicates whether the {@link #close()} method
-     * should propagate to the underling {@link InputStream}.
-     *
-     * @return {@code true} if calling {@link #close()}
-     * propagates to the {@code close()} method of the
-     * underlying stream or {@code false} if it does not.
-     */
-    public boolean isPropagateClose() {
-        return propagateClose;
-    }
-
-    /**
      * Set whether the {@link #close()} method
      * should propagate to the underling {@link InputStream}.
      *
@@ -229,4 +206,27 @@ public class BoundedInputStream extends InputStream {
     public void setPropagateClose(final boolean propagateClose) {
         this.propagateClose = propagateClose;
     }
+
+    /**
+     * Invokes the delegate's {@code skip(long)} method.
+     * @param n the number of bytes to skip
+     * @return the actual number of bytes skipped
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public long skip(final long n) throws IOException {
+        final long toSkip = max>=0 ? Math.min(n, max-pos) : n;
+        final long skippedBytes = in.skip(toSkip);
+        pos+=skippedBytes;
+        return skippedBytes;
+    }
+
+    /**
+     * Invokes the delegate's {@code toString()} method.
+     * @return the delegate's {@code toString()}
+     */
+    @Override
+    public String toString() {
+        return in.toString();
+    }
 }
diff --git a/src/main/java/org/apache/commons/io/input/BoundedReader.java b/src/main/java/org/apache/commons/io/input/BoundedReader.java
index 00e9736..6bb59a5 100644
--- a/src/main/java/org/apache/commons/io/input/BoundedReader.java
+++ b/src/main/java/org/apache/commons/io/input/BoundedReader.java
@@ -70,18 +70,6 @@ public class BoundedReader extends Reader {
     }
 
     /**
-     * Resets the target to the latest mark,
-     *
-     * @throws IOException If an I/O error occurs while calling the underlying reader's reset method
-     * @see java.io.Reader#reset()
-     */
-    @Override
-    public void reset() throws IOException {
-        charsRead = markedAt;
-        target.reset();
-    }
-
-    /**
      * marks the target stream
      *
      * @param readAheadLimit The number of characters that can be read while still retaining the ability to do #reset().
@@ -143,4 +131,16 @@ public class BoundedReader extends Reader {
         }
         return len;
     }
+
+    /**
+     * Resets the target to the latest mark,
+     *
+     * @throws IOException If an I/O error occurs while calling the underlying reader's reset method
+     * @see java.io.Reader#reset()
+     */
+    @Override
+    public void reset() throws IOException {
+        charsRead = markedAt;
+        target.reset();
+    }
 }
diff --git a/src/main/java/org/apache/commons/io/input/BrokenInputStream.java b/src/main/java/org/apache/commons/io/input/BrokenInputStream.java
index 907e37c..03131d0 100644
--- a/src/main/java/org/apache/commons/io/input/BrokenInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/BrokenInputStream.java
@@ -42,6 +42,13 @@ public class BrokenInputStream extends InputStream {
     private final IOException exception;
 
     /**
+     * Creates a new stream that always throws an {@link IOException}
+     */
+    public BrokenInputStream() {
+        this(new IOException("Broken input stream"));
+    }
+
+    /**
      * Creates a new stream that always throws the given exception.
      *
      * @param exception the exception to be thrown
@@ -51,43 +58,34 @@ public class BrokenInputStream extends InputStream {
     }
 
     /**
-     * Creates a new stream that always throws an {@link IOException}
-     */
-    public BrokenInputStream() {
-        this(new IOException("Broken input stream"));
-    }
-
-    /**
      * Throws the configured exception.
      *
      * @return nothing
      * @throws IOException always thrown
      */
     @Override
-    public int read() throws IOException {
+    public int available() throws IOException {
         throw exception;
     }
 
     /**
      * Throws the configured exception.
      *
-     * @return nothing
      * @throws IOException always thrown
      */
     @Override
-    public int available() throws IOException {
+    public void close() throws IOException {
         throw exception;
     }
 
     /**
      * Throws the configured exception.
      *
-     * @param n ignored
      * @return nothing
      * @throws IOException always thrown
      */
     @Override
-    public long skip(final long n) throws IOException {
+    public int read() throws IOException {
         throw exception;
     }
 
@@ -104,10 +102,12 @@ public class BrokenInputStream extends InputStream {
     /**
      * Throws the configured exception.
      *
+     * @param n ignored
+     * @return nothing
      * @throws IOException always thrown
      */
     @Override
-    public void close() throws IOException {
+    public long skip(final long n) throws IOException {
         throw exception;
     }
 
diff --git a/src/main/java/org/apache/commons/io/input/CharSequenceInputStream.java b/src/main/java/org/apache/commons/io/input/CharSequenceInputStream.java
index b627c3c..ddce28c 100644
--- a/src/main/java/org/apache/commons/io/input/CharSequenceInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/CharSequenceInputStream.java
@@ -53,6 +53,18 @@ public class CharSequenceInputStream extends InputStream {
     private int mark_bbuf; // position in bbuf
 
     /**
+     * Constructor, calls {@link #CharSequenceInputStream(CharSequence, Charset, int)}
+     * with a buffer size of 2048.
+     *
+     * @param cs the input character sequence
+     * @param charset the character set name to use
+     * @throws IllegalArgumentException if the buffer is not large enough to hold a complete character
+     */
+    public CharSequenceInputStream(final CharSequence cs, final Charset charset) {
+        this(cs, charset, BUFFER_SIZE);
+    }
+
+    /**
      * Constructor.
      *
      * @param cs the input character sequence
@@ -78,39 +90,47 @@ public class CharSequenceInputStream extends InputStream {
     }
 
     /**
-     * Constructor, calls {@link #CharSequenceInputStream(CharSequence, Charset, int)}.
+     * Constructor, calls {@link #CharSequenceInputStream(CharSequence, String, int)}
+     * with a buffer size of 2048.
      *
      * @param cs the input character sequence
      * @param charset the character set name to use
-     * @param bufferSize the buffer size to use.
      * @throws IllegalArgumentException if the buffer is not large enough to hold a complete character
      */
-    public CharSequenceInputStream(final CharSequence cs, final String charset, final int bufferSize) {
-        this(cs, Charset.forName(charset), bufferSize);
+    public CharSequenceInputStream(final CharSequence cs, final String charset) {
+        this(cs, charset, BUFFER_SIZE);
     }
 
     /**
-     * Constructor, calls {@link #CharSequenceInputStream(CharSequence, Charset, int)}
-     * with a buffer size of 2048.
+     * Constructor, calls {@link #CharSequenceInputStream(CharSequence, Charset, int)}.
      *
      * @param cs the input character sequence
      * @param charset the character set name to use
+     * @param bufferSize the buffer size to use.
      * @throws IllegalArgumentException if the buffer is not large enough to hold a complete character
      */
-    public CharSequenceInputStream(final CharSequence cs, final Charset charset) {
-        this(cs, charset, BUFFER_SIZE);
+    public CharSequenceInputStream(final CharSequence cs, final String charset, final int bufferSize) {
+        this(cs, Charset.forName(charset), bufferSize);
     }
 
     /**
-     * Constructor, calls {@link #CharSequenceInputStream(CharSequence, String, int)}
-     * with a buffer size of 2048.
+     * Return an estimate of the number of bytes remaining in the byte stream.
+     * @return the count of bytes that can be read without blocking (or returning EOF).
      *
-     * @param cs the input character sequence
-     * @param charset the character set name to use
-     * @throws IllegalArgumentException if the buffer is not large enough to hold a complete character
+     * @throws IOException if an error occurs (probably not possible)
      */
-    public CharSequenceInputStream(final CharSequence cs, final String charset) {
-        this(cs, charset, BUFFER_SIZE);
+    @Override
+    public int available() throws IOException {
+        // The cached entries are in bbuf; since encoding always creates at least one byte
+        // per character, we can add the two to get a better estimate (e.g. if bbuf is empty)
+        // Note that the previous implementation (2.4) could return zero even though there were
+        // encoded bytes still available.
+        return this.bbuf.remaining() + this.cbuf.remaining();
+    }
+
+    @Override
+    public void close() throws IOException {
+        // noop
     }
 
     /**
@@ -128,6 +148,43 @@ public class CharSequenceInputStream extends InputStream {
         this.bbuf.flip();
     }
 
+    /**
+     * {@inheritDoc}
+     * @param readlimit max read limit (ignored)
+     */
+    @Override
+    public synchronized void mark(final int readlimit) {
+        this.mark_cbuf = this.cbuf.position();
+        this.mark_bbuf = this.bbuf.position();
+        this.cbuf.mark();
+        this.bbuf.mark();
+        // It would be nice to be able to use mark & reset on the cbuf and bbuf;
+        // however the bbuf is re-used so that won't work
+    }
+
+    @Override
+    public boolean markSupported() {
+        return true;
+    }
+
+    @Override
+    public int read() throws IOException {
+        for (;;) {
+            if (this.bbuf.hasRemaining()) {
+                return this.bbuf.get() & 0xFF;
+            }
+            fillBuffer();
+            if (!this.bbuf.hasRemaining() && !this.cbuf.hasRemaining()) {
+                return EOF;
+            }
+        }
+    }
+
+    @Override
+    public int read(final byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
     @Override
     public int read(final byte[] array, int off, int len) throws IOException {
         Objects.requireNonNull(array, "array");
@@ -160,72 +217,6 @@ public class CharSequenceInputStream extends InputStream {
     }
 
     @Override
-    public int read() throws IOException {
-        for (;;) {
-            if (this.bbuf.hasRemaining()) {
-                return this.bbuf.get() & 0xFF;
-            }
-            fillBuffer();
-            if (!this.bbuf.hasRemaining() && !this.cbuf.hasRemaining()) {
-                return EOF;
-            }
-        }
-    }
-
-    @Override
-    public int read(final byte[] b) throws IOException {
-        return read(b, 0, b.length);
-    }
-
-    @Override
-    public long skip(long n) throws IOException {
-        /*
-         * This could be made more efficient by using position to skip within the current buffer.
-         */
-        long skipped = 0;
-        while (n > 0 && available() > 0) {
-            this.read();
-            n--;
-            skipped++;
-        }
-        return skipped;
-    }
-
-    /**
-     * Return an estimate of the number of bytes remaining in the byte stream.
-     * @return the count of bytes that can be read without blocking (or returning EOF).
-     *
-     * @throws IOException if an error occurs (probably not possible)
-     */
-    @Override
-    public int available() throws IOException {
-        // The cached entries are in bbuf; since encoding always creates at least one byte
-        // per character, we can add the two to get a better estimate (e.g. if bbuf is empty)
-        // Note that the previous implementation (2.4) could return zero even though there were
-        // encoded bytes still available.
-        return this.bbuf.remaining() + this.cbuf.remaining();
-    }
-
-    @Override
-    public void close() throws IOException {
-        // noop
-    }
-
-    /**
-     * {@inheritDoc}
-     * @param readlimit max read limit (ignored)
-     */
-    @Override
-    public synchronized void mark(final int readlimit) {
-        this.mark_cbuf = this.cbuf.position();
-        this.mark_bbuf = this.bbuf.position();
-        this.cbuf.mark();
-        this.bbuf.mark();
-        // It would be nice to be able to use mark & reset on the cbuf and bbuf;
-        // however the bbuf is re-used so that won't work
-    }
-
-    @Override
     public synchronized void reset() throws IOException {
         /*
          * This is not the most efficient implementation, as it re-encodes from the beginning.
@@ -262,8 +253,17 @@ public class CharSequenceInputStream extends InputStream {
     }
 
     @Override
-    public boolean markSupported() {
-        return true;
+    public long skip(long n) throws IOException {
+        /*
+         * This could be made more efficient by using position to skip within the current buffer.
+         */
+        long skipped = 0;
+        while (n > 0 && available() > 0) {
+            this.read();
+            n--;
+            skipped++;
+        }
+        return skipped;
     }
 
 }
diff --git a/src/main/java/org/apache/commons/io/input/CharSequenceReader.java b/src/main/java/org/apache/commons/io/input/CharSequenceReader.java
index 7a7fcd3..89448e6 100644
--- a/src/main/java/org/apache/commons/io/input/CharSequenceReader.java
+++ b/src/main/java/org/apache/commons/io/input/CharSequenceReader.java
@@ -135,12 +135,12 @@ public class CharSequenceReader extends Reader implements Serializable {
     }
 
     /**
-     * Returns the index in the character sequence to start reading from, taking into account its length.
-     *
-     * @return The start index in the character sequence (inclusive).
+     * Close resets the file back to the start and removes any marked position.
      */
-    private int start() {
-        return Math.min(charSequence.length(), start);
+    @Override
+    public void close() {
+        idx = start;
+        mark = start;
     }
 
     /**
@@ -157,25 +157,6 @@ public class CharSequenceReader extends Reader implements Serializable {
     }
 
     /**
-     * Close resets the file back to the start and removes any marked position.
-     */
-    @Override
-    public void close() {
-        idx = start;
-        mark = start;
-    }
-
-    /**
-     * Tells whether this stream is ready to be read.
-     *
-     * @return {@code true} if more characters from the character sequence are available, or {@code false} otherwise.
-     */
-    @Override
-    public boolean ready() {
-        return idx < end();
-    }
-
-    /**
      * Mark the current position.
      *
      * @param readAheadLimit ignored
@@ -261,6 +242,16 @@ public class CharSequenceReader extends Reader implements Serializable {
     }
 
     /**
+     * Tells whether this stream is ready to be read.
+     *
+     * @return {@code true} if more characters from the character sequence are available, or {@code false} otherwise.
+     */
+    @Override
+    public boolean ready() {
+        return idx < end();
+    }
+
+    /**
      * Reset the reader to the last marked position (or the beginning if
      * mark has not been called).
      */
@@ -291,6 +282,15 @@ public class CharSequenceReader extends Reader implements Serializable {
     }
 
     /**
+     * Returns the index in the character sequence to start reading from, taking into account its length.
+     *
+     * @return The start index in the character sequence (inclusive).
+     */
+    private int start() {
+        return Math.min(charSequence.length(), start);
+    }
+
+    /**
      * Return a String representation of the underlying
      * character sequence.
      *
diff --git a/src/main/java/org/apache/commons/io/input/ClosedReader.java b/src/main/java/org/apache/commons/io/input/ClosedReader.java
index ac5b0df..71e21d2 100644
--- a/src/main/java/org/apache/commons/io/input/ClosedReader.java
+++ b/src/main/java/org/apache/commons/io/input/ClosedReader.java
@@ -49,6 +49,11 @@ public class ClosedReader extends Reader {
     @Deprecated
     public static final ClosedReader CLOSED_READER = INSTANCE;
 
+    @Override
+    public void close() throws IOException {
+        // noop
+    }
+
     /**
      * Returns -1 to indicate that the stream is closed.
      *
@@ -62,9 +67,4 @@ public class ClosedReader extends Reader {
         return EOF;
     }
 
-    @Override
-    public void close() throws IOException {
-        // noop
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/input/CountingInputStream.java b/src/main/java/org/apache/commons/io/input/CountingInputStream.java
index 17d7c29..7054b8f 100644
--- a/src/main/java/org/apache/commons/io/input/CountingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/CountingInputStream.java
@@ -45,22 +45,6 @@ public class CountingInputStream extends ProxyInputStream {
 
 
     /**
-     * Skips the stream over the specified number of bytes, adding the skipped
-     * amount to the count.
-     *
-     * @param length  the number of bytes to skip
-     * @return the actual number of bytes skipped
-     * @throws IOException if an I/O error occurs.
-     * @see java.io.InputStream#skip(long)
-     */
-    @Override
-    public synchronized long skip(final long length) throws IOException {
-        final long skip = super.skip(length);
-        this.count += skip;
-        return skip;
-    }
-
-    /**
      * Adds the number of read bytes to the count.
      *
      * @param n number of bytes read, or -1 if no more bytes are available
@@ -76,6 +60,20 @@ public class CountingInputStream extends ProxyInputStream {
     /**
      * The number of bytes that have passed through this stream.
      * <p>
+     * NOTE: This method is an alternative for {@code getCount()}
+     * and was added because that method returns an integer which will
+     * result in incorrect count for files over 2GB.
+     *
+     * @return the number of bytes accumulated
+     * @since 1.3
+     */
+    public synchronized long getByteCount() {
+        return this.count;
+    }
+
+    /**
+     * The number of bytes that have passed through this stream.
+     * <p>
      * NOTE: From v1.3 this method throws an ArithmeticException if the
      * count is greater than can be expressed by an {@code int}.
      * See {@link #getByteCount()} for a method using a {@code long}.
@@ -94,6 +92,22 @@ public class CountingInputStream extends ProxyInputStream {
     /**
      * Set the byte count back to 0.
      * <p>
+     * NOTE: This method is an alternative for {@code resetCount()}
+     * and was added because that method returns an integer which will
+     * result in incorrect count for files over 2GB.
+     *
+     * @return the count previous to resetting
+     * @since 1.3
+     */
+    public synchronized long resetByteCount() {
+        final long tmp = this.count;
+        this.count = 0;
+        return tmp;
+    }
+
+    /**
+     * Set the byte count back to 0.
+     * <p>
      * NOTE: From v1.3 this method throws an ArithmeticException if the
      * count is greater than can be expressed by an {@code int}.
      * See {@link #resetByteCount()} for a method using a {@code long}.
@@ -110,33 +124,19 @@ public class CountingInputStream extends ProxyInputStream {
     }
 
     /**
-     * The number of bytes that have passed through this stream.
-     * <p>
-     * NOTE: This method is an alternative for {@code getCount()}
-     * and was added because that method returns an integer which will
-     * result in incorrect count for files over 2GB.
-     *
-     * @return the number of bytes accumulated
-     * @since 1.3
-     */
-    public synchronized long getByteCount() {
-        return this.count;
-    }
-
-    /**
-     * Set the byte count back to 0.
-     * <p>
-     * NOTE: This method is an alternative for {@code resetCount()}
-     * and was added because that method returns an integer which will
-     * result in incorrect count for files over 2GB.
+     * Skips the stream over the specified number of bytes, adding the skipped
+     * amount to the count.
      *
-     * @return the count previous to resetting
-     * @since 1.3
+     * @param length  the number of bytes to skip
+     * @return the actual number of bytes skipped
+     * @throws IOException if an I/O error occurs.
+     * @see java.io.InputStream#skip(long)
      */
-    public synchronized long resetByteCount() {
-        final long tmp = this.count;
-        this.count = 0;
-        return tmp;
+    @Override
+    public synchronized long skip(final long length) throws IOException {
+        final long skip = super.skip(length);
+        this.count += skip;
+        return skip;
     }
 
 }
diff --git a/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java b/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java
index 1e9d46f..62c2b75 100644
--- a/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java
@@ -46,18 +46,30 @@ public class MessageDigestCalculatingInputStream extends ObservableInputStream {
         }
 
         @Override
-        public void data(final int input) throws IOException {
-            messageDigest.update((byte) input);
+        public void data(final byte[] input, final int offset, final int length) throws IOException {
+            messageDigest.update(input, offset, length);
         }
 
         @Override
-        public void data(final byte[] input, final int offset, final int length) throws IOException {
-            messageDigest.update(input, offset, length);
+        public void data(final int input) throws IOException {
+            messageDigest.update((byte) input);
         }
     }
 
     private final MessageDigest messageDigest;
 
+    /**
+     * Creates a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the
+     * "MD5" algorithm.
+     *
+     * @param inputStream the stream to calculate the message digest for
+     * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified
+     *         algorithm.
+     */
+    public MessageDigestCalculatingInputStream(final InputStream inputStream) throws NoSuchAlgorithmException {
+        this(inputStream, MessageDigest.getInstance("MD5"));
+    }
+
     /** Creates a new instance, which calculates a signature on the given stream,
      * using the given {@link MessageDigest}.
      * @param inputStream the stream to calculate the message digest for
@@ -82,18 +94,6 @@ public class MessageDigestCalculatingInputStream extends ObservableInputStream {
         this(inputStream, MessageDigest.getInstance(algorithm));
     }
 
-    /**
-     * Creates a new instance, which calculates a signature on the given stream, using a {@link MessageDigest} with the
-     * "MD5" algorithm.
-     *
-     * @param inputStream the stream to calculate the message digest for
-     * @throws NoSuchAlgorithmException if no Provider supports a MessageDigestSpi implementation for the specified
-     *         algorithm.
-     */
-    public MessageDigestCalculatingInputStream(final InputStream inputStream) throws NoSuchAlgorithmException {
-        this(inputStream, MessageDigest.getInstance("MD5"));
-    }
-
     /** Returns the {@link MessageDigest}, which is being used for generating the
      * checksum.
      * <em>Note</em>: The checksum will only reflect the data, which has been read so far.
diff --git a/src/main/java/org/apache/commons/io/input/NullInputStream.java b/src/main/java/org/apache/commons/io/input/NullInputStream.java
index 34363ec..438d602 100644
--- a/src/main/java/org/apache/commons/io/input/NullInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/NullInputStream.java
@@ -120,24 +120,6 @@ public class NullInputStream extends InputStream {
     }
 
     /**
-     * Return the current position.
-     *
-     * @return the current position.
-     */
-    public long getPosition() {
-        return position;
-    }
-
-    /**
-     * Return the size this {@link InputStream} emulates.
-     *
-     * @return The size of the input stream to emulate.
-     */
-    public long getSize() {
-        return size;
-    }
-
-    /**
      * Return the number of bytes that can be read.
      *
      * @return The number of bytes that can be read.
@@ -168,6 +150,40 @@ public class NullInputStream extends InputStream {
     }
 
     /**
+     * Handle End of File.
+     *
+     * @return {@code -1} if {@code throwEofException} is
+     * set to {@code false}
+     * @throws EOFException if {@code throwEofException} is set
+     * to {@code true}.
+     */
+    private int doEndOfFile() throws EOFException {
+        eof = true;
+        if (throwEofException) {
+            throw new EOFException();
+        }
+        return EOF;
+    }
+
+    /**
+     * Return the current position.
+     *
+     * @return the current position.
+     */
+    public long getPosition() {
+        return position;
+    }
+
+    /**
+     * Return the size this {@link InputStream} emulates.
+     *
+     * @return The size of the input stream to emulate.
+     */
+    public long getSize() {
+        return size;
+    }
+
+    /**
      * Mark the current position.
      *
      * @param readlimit The number of bytes before this marked position
@@ -194,6 +210,32 @@ public class NullInputStream extends InputStream {
     }
 
     /**
+     * Return a byte value for the  {@code read()} method.
+     * <p>
+     * This implementation returns zero.
+     *
+     * @return This implementation always returns zero.
+     */
+    protected int processByte() {
+        // do nothing - overridable by subclass
+        return 0;
+    }
+
+    /**
+     * Process the bytes for the {@code read(byte[], offset, length)}
+     * method.
+     * <p>
+     * This implementation leaves the byte array unchanged.
+     *
+     * @param bytes The byte array
+     * @param offset The offset to start at.
+     * @param length The number of bytes.
+     */
+    protected void processBytes(final byte[] bytes, final int offset, final int length) {
+        // do nothing - overridable by subclass
+    }
+
+    /**
      * Read a byte.
      *
      * @return Either The byte value returned by {@code processByte()}
@@ -315,46 +357,4 @@ public class NullInputStream extends InputStream {
         return returnLength;
     }
 
-    /**
-     * Return a byte value for the  {@code read()} method.
-     * <p>
-     * This implementation returns zero.
-     *
-     * @return This implementation always returns zero.
-     */
-    protected int processByte() {
-        // do nothing - overridable by subclass
-        return 0;
-    }
-
-    /**
-     * Process the bytes for the {@code read(byte[], offset, length)}
-     * method.
-     * <p>
-     * This implementation leaves the byte array unchanged.
-     *
-     * @param bytes The byte array
-     * @param offset The offset to start at.
-     * @param length The number of bytes.
-     */
-    protected void processBytes(final byte[] bytes, final int offset, final int length) {
-        // do nothing - overridable by subclass
-    }
-
-    /**
-     * Handle End of File.
-     *
-     * @return {@code -1} if {@code throwEofException} is
-     * set to {@code false}
-     * @throws EOFException if {@code throwEofException} is set
-     * to {@code true}.
-     */
-    private int doEndOfFile() throws EOFException {
-        eof = true;
-        if (throwEofException) {
-            throw new EOFException();
-        }
-        return EOF;
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/input/NullReader.java b/src/main/java/org/apache/commons/io/input/NullReader.java
index bf6cd0a..6e214eb 100644
--- a/src/main/java/org/apache/commons/io/input/NullReader.java
+++ b/src/main/java/org/apache/commons/io/input/NullReader.java
@@ -119,6 +119,35 @@ public class NullReader extends Reader {
     }
 
     /**
+     * Closes this Reader - resets the internal state to
+     * the initial values.
+     *
+     * @throws IOException If an error occurs.
+     */
+    @Override
+    public void close() throws IOException {
+        eof = false;
+        position = 0;
+        mark = -1;
+    }
+
+    /**
+     * Handles End of File.
+     *
+     * @return {@code -1} if {@code throwEofException} is
+     * set to {@code false}
+     * @throws EOFException if {@code throwEofException} is set
+     * to {@code true}.
+     */
+    private int doEndOfFile() throws EOFException {
+        eof = true;
+        if (throwEofException) {
+            throw new EOFException();
+        }
+        return EOF;
+    }
+
+    /**
      * Returns the current position.
      *
      * @return the current position.
@@ -137,19 +166,6 @@ public class NullReader extends Reader {
     }
 
     /**
-     * Closes this Reader - resets the internal state to
-     * the initial values.
-     *
-     * @throws IOException If an error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        eof = false;
-        position = 0;
-        mark = -1;
-    }
-
-    /**
      * Marks the current position.
      *
      * @param readlimit The number of characters before this marked position
@@ -176,6 +192,34 @@ public class NullReader extends Reader {
     }
 
     /**
+     * Returns a character value for the  {@code read()} method.
+     * <p>
+     * This implementation returns zero.
+     * </p>
+     *
+     * @return This implementation always returns zero.
+     */
+    protected int processChar() {
+        // do nothing - overridable by subclass
+        return 0;
+    }
+
+    /**
+     * Process the characters for the {@code read(char[], offset, length)}
+     * method.
+     * <p>
+     * This implementation leaves the character array unchanged.
+     * </p>
+     *
+     * @param chars The character array
+     * @param offset The offset to start at.
+     * @param length The number of characters.
+     */
+    protected void processChars(final char[] chars, final int offset, final int length) {
+        // do nothing - overridable by subclass
+    }
+
+    /**
      * Reads a character.
      *
      * @return Either The character value returned by {@code processChar()}
@@ -297,48 +341,4 @@ public class NullReader extends Reader {
         return returnLength;
     }
 
-    /**
-     * Returns a character value for the  {@code read()} method.
-     * <p>
-     * This implementation returns zero.
-     * </p>
-     *
-     * @return This implementation always returns zero.
-     */
-    protected int processChar() {
-        // do nothing - overridable by subclass
-        return 0;
-    }
-
-    /**
-     * Process the characters for the {@code read(char[], offset, length)}
-     * method.
-     * <p>
-     * This implementation leaves the character array unchanged.
-     * </p>
-     *
-     * @param chars The character array
-     * @param offset The offset to start at.
-     * @param length The number of characters.
-     */
-    protected void processChars(final char[] chars, final int offset, final int length) {
-        // do nothing - overridable by subclass
-    }
-
-    /**
-     * Handles End of File.
-     *
-     * @return {@code -1} if {@code throwEofException} is
-     * set to {@code false}
-     * @throws EOFException if {@code throwEofException} is set
-     * to {@code true}.
-     */
-    private int doEndOfFile() throws EOFException {
-        eof = true;
-        if (throwEofException) {
-            throw new EOFException();
-        }
-        return EOF;
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/input/ProxyInputStream.java b/src/main/java/org/apache/commons/io/input/ProxyInputStream.java
index fdea363..b1b8ec5 100644
--- a/src/main/java/org/apache/commons/io/input/ProxyInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/ProxyInputStream.java
@@ -51,6 +51,106 @@ public abstract class ProxyInputStream extends FilterInputStream {
     }
 
     /**
+     * Invoked by the read methods after the proxied call has returned
+     * successfully. The number of bytes returned to the caller (or -1 if
+     * the end of stream was reached) is given as an argument.
+     * <p>
+     * Subclasses can override this method to add common post-processing
+     * functionality without having to override all the read methods.
+     * The default implementation does nothing.
+     * <p>
+     * Note this method is <em>not</em> called from {@link #skip(long)} or
+     * {@link #reset()}. You need to explicitly override those methods if
+     * you want to add post-processing steps also to them.
+     *
+     * @since 2.0
+     * @param n number of bytes read, or -1 if the end of stream was reached
+     * @throws IOException if the post-processing fails
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    protected void afterRead(final int n) throws IOException {
+        // no-op
+    }
+
+    /**
+     * Invokes the delegate's {@code available()} method.
+     * @return the number of available bytes
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public int available() throws IOException {
+        try {
+            return super.available();
+        } catch (final IOException e) {
+            handleIOException(e);
+            return 0;
+        }
+    }
+
+    /**
+     * Invoked by the read methods before the call is proxied. The number
+     * of bytes that the caller wanted to read (1 for the {@link #read()}
+     * method, buffer length for {@link #read(byte[])}, etc.) is given as
+     * an argument.
+     * <p>
+     * Subclasses can override this method to add common pre-processing
+     * functionality without having to override all the read methods.
+     * The default implementation does nothing.
+     * <p>
+     * Note this method is <em>not</em> called from {@link #skip(long)} or
+     * {@link #reset()}. You need to explicitly override those methods if
+     * you want to add pre-processing steps also to them.
+     *
+     * @since 2.0
+     * @param n number of bytes that the caller asked to be read
+     * @throws IOException if the pre-processing fails
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    protected void beforeRead(final int n) throws IOException {
+        // no-op
+    }
+
+    /**
+     * Invokes the delegate's {@code close()} method.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void close() throws IOException {
+        IOUtils.close(in, this::handleIOException);
+    }
+
+    /**
+     * Handle any IOExceptions thrown.
+     * <p>
+     * This method provides a point to implement custom exception
+     * handling. The default behavior is to re-throw the exception.
+     * @param e The IOException thrown
+     * @throws IOException if an I/O error occurs.
+     * @since 2.0
+     */
+    protected void handleIOException(final IOException e) throws IOException {
+        throw e;
+    }
+
+    /**
+     * Invokes the delegate's {@code mark(int)} method.
+     * @param readlimit read ahead limit
+     */
+    @Override
+    public synchronized void mark(final int readlimit) {
+        in.mark(readlimit);
+    }
+
+    /**
+     * Invokes the delegate's {@code markSupported()} method.
+     * @return true if mark is supported, otherwise false
+     */
+    @Override
+    public boolean markSupported() {
+        return in.markSupported();
+    }
+
+    /**
      * Invokes the delegate's {@code read()} method.
      * @return the byte read or -1 if the end of stream
      * @throws IOException if an I/O error occurs.
@@ -109,132 +209,32 @@ public abstract class ProxyInputStream extends FilterInputStream {
     }
 
     /**
-     * Invokes the delegate's {@code skip(long)} method.
-     * @param ln the number of bytes to skip
-     * @return the actual number of bytes skipped
+     * Invokes the delegate's {@code reset()} method.
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public long skip(final long ln) throws IOException {
+    public synchronized void reset() throws IOException {
         try {
-            return in.skip(ln);
+            in.reset();
         } catch (final IOException e) {
             handleIOException(e);
-            return 0;
         }
     }
 
     /**
-     * Invokes the delegate's {@code available()} method.
-     * @return the number of available bytes
+     * Invokes the delegate's {@code skip(long)} method.
+     * @param ln the number of bytes to skip
+     * @return the actual number of bytes skipped
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public int available() throws IOException {
+    public long skip(final long ln) throws IOException {
         try {
-            return super.available();
+            return in.skip(ln);
         } catch (final IOException e) {
             handleIOException(e);
             return 0;
         }
     }
 
-    /**
-     * Invokes the delegate's {@code close()} method.
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        IOUtils.close(in, this::handleIOException);
-    }
-
-    /**
-     * Invokes the delegate's {@code mark(int)} method.
-     * @param readlimit read ahead limit
-     */
-    @Override
-    public synchronized void mark(final int readlimit) {
-        in.mark(readlimit);
-    }
-
-    /**
-     * Invokes the delegate's {@code reset()} method.
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public synchronized void reset() throws IOException {
-        try {
-            in.reset();
-        } catch (final IOException e) {
-            handleIOException(e);
-        }
-    }
-
-    /**
-     * Invokes the delegate's {@code markSupported()} method.
-     * @return true if mark is supported, otherwise false
-     */
-    @Override
-    public boolean markSupported() {
-        return in.markSupported();
-    }
-
-    /**
-     * Invoked by the read methods before the call is proxied. The number
-     * of bytes that the caller wanted to read (1 for the {@link #read()}
-     * method, buffer length for {@link #read(byte[])}, etc.) is given as
-     * an argument.
-     * <p>
-     * Subclasses can override this method to add common pre-processing
-     * functionality without having to override all the read methods.
-     * The default implementation does nothing.
-     * <p>
-     * Note this method is <em>not</em> called from {@link #skip(long)} or
-     * {@link #reset()}. You need to explicitly override those methods if
-     * you want to add pre-processing steps also to them.
-     *
-     * @since 2.0
-     * @param n number of bytes that the caller asked to be read
-     * @throws IOException if the pre-processing fails
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void beforeRead(final int n) throws IOException {
-        // no-op
-    }
-
-    /**
-     * Invoked by the read methods after the proxied call has returned
-     * successfully. The number of bytes returned to the caller (or -1 if
-     * the end of stream was reached) is given as an argument.
-     * <p>
-     * Subclasses can override this method to add common post-processing
-     * functionality without having to override all the read methods.
-     * The default implementation does nothing.
-     * <p>
-     * Note this method is <em>not</em> called from {@link #skip(long)} or
-     * {@link #reset()}. You need to explicitly override those methods if
-     * you want to add post-processing steps also to them.
-     *
-     * @since 2.0
-     * @param n number of bytes read, or -1 if the end of stream was reached
-     * @throws IOException if the post-processing fails
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void afterRead(final int n) throws IOException {
-        // no-op
-    }
-
-    /**
-     * Handle any IOExceptions thrown.
-     * <p>
-     * This method provides a point to implement custom exception
-     * handling. The default behavior is to re-throw the exception.
-     * @param e The IOException thrown
-     * @throws IOException if an I/O error occurs.
-     * @since 2.0
-     */
-    protected void handleIOException(final IOException e) throws IOException {
-        throw e;
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/input/ProxyReader.java b/src/main/java/org/apache/commons/io/input/ProxyReader.java
index 8ed16ae..f05f5b0 100644
--- a/src/main/java/org/apache/commons/io/input/ProxyReader.java
+++ b/src/main/java/org/apache/commons/io/input/ProxyReader.java
@@ -48,6 +48,100 @@ public abstract class ProxyReader extends FilterReader {
     }
 
     /**
+     * Invoked by the read methods after the proxied call has returned
+     * successfully. The number of chars returned to the caller (or -1 if
+     * the end of stream was reached) is given as an argument.
+     * <p>
+     * Subclasses can override this method to add common post-processing
+     * functionality without having to override all the read methods.
+     * The default implementation does nothing.
+     * <p>
+     * Note this method is <em>not</em> called from {@link #skip(long)} or
+     * {@link #reset()}. You need to explicitly override those methods if
+     * you want to add post-processing steps also to them.
+     *
+     * @since 2.0
+     * @param n number of chars read, or -1 if the end of stream was reached
+     * @throws IOException if the post-processing fails
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    protected void afterRead(final int n) throws IOException {
+        // noop
+    }
+
+    /**
+     * Invoked by the read methods before the call is proxied. The number
+     * of chars that the caller wanted to read (1 for the {@link #read()}
+     * method, buffer length for {@link #read(char[])}, etc.) is given as
+     * an argument.
+     * <p>
+     * Subclasses can override this method to add common pre-processing
+     * functionality without having to override all the read methods.
+     * The default implementation does nothing.
+     * <p>
+     * Note this method is <em>not</em> called from {@link #skip(long)} or
+     * {@link #reset()}. You need to explicitly override those methods if
+     * you want to add pre-processing steps also to them.
+     *
+     * @since 2.0
+     * @param n number of chars that the caller asked to be read
+     * @throws IOException if the pre-processing fails
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    protected void beforeRead(final int n) throws IOException {
+        // noop
+    }
+
+    /**
+     * Invokes the delegate's {@code close()} method.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void close() throws IOException {
+        try {
+            in.close();
+        } catch (final IOException e) {
+            handleIOException(e);
+        }
+    }
+
+    /**
+     * Handle any IOExceptions thrown.
+     * <p>
+     * This method provides a point to implement custom exception
+     * handling. The default behavior is to re-throw the exception.
+     * @param e The IOException thrown
+     * @throws IOException if an I/O error occurs.
+     * @since 2.0
+     */
+    protected void handleIOException(final IOException e) throws IOException {
+        throw e;
+    }
+
+    /**
+     * Invokes the delegate's {@code mark(int)} method.
+     * @param idx read ahead limit
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public synchronized void mark(final int idx) throws IOException {
+        try {
+            in.mark(idx);
+        } catch (final IOException e) {
+            handleIOException(e);
+        }
+    }
+
+    /**
+     * Invokes the delegate's {@code markSupported()} method.
+     * @return true if mark is supported, otherwise false
+     */
+    @Override
+    public boolean markSupported() {
+        return in.markSupported();
+    }
+
+    /**
      * Invokes the delegate's {@code read()} method.
      * @return the character read or -1 if the end of stream
      * @throws IOException if an I/O error occurs.
@@ -126,22 +220,6 @@ public abstract class ProxyReader extends FilterReader {
     }
 
     /**
-     * Invokes the delegate's {@code skip(long)} method.
-     * @param ln the number of bytes to skip
-     * @return the number of bytes to skipped or EOF if the end of stream
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public long skip(final long ln) throws IOException {
-        try {
-            return in.skip(ln);
-        } catch (final IOException e) {
-            handleIOException(e);
-            return 0;
-        }
-    }
-
-    /**
      * Invokes the delegate's {@code ready()} method.
      * @return true if the stream is ready to be read
      * @throws IOException if an I/O error occurs.
@@ -157,110 +235,32 @@ public abstract class ProxyReader extends FilterReader {
     }
 
     /**
-     * Invokes the delegate's {@code close()} method.
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        try {
-            in.close();
-        } catch (final IOException e) {
-            handleIOException(e);
-        }
-    }
-
-    /**
-     * Invokes the delegate's {@code mark(int)} method.
-     * @param idx read ahead limit
+     * Invokes the delegate's {@code reset()} method.
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public synchronized void mark(final int idx) throws IOException {
+    public synchronized void reset() throws IOException {
         try {
-            in.mark(idx);
+            in.reset();
         } catch (final IOException e) {
             handleIOException(e);
         }
     }
 
     /**
-     * Invokes the delegate's {@code reset()} method.
+     * Invokes the delegate's {@code skip(long)} method.
+     * @param ln the number of bytes to skip
+     * @return the number of bytes to skipped or EOF if the end of stream
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public synchronized void reset() throws IOException {
+    public long skip(final long ln) throws IOException {
         try {
-            in.reset();
+            return in.skip(ln);
         } catch (final IOException e) {
             handleIOException(e);
+            return 0;
         }
     }
 
-    /**
-     * Invokes the delegate's {@code markSupported()} method.
-     * @return true if mark is supported, otherwise false
-     */
-    @Override
-    public boolean markSupported() {
-        return in.markSupported();
-    }
-
-    /**
-     * Invoked by the read methods before the call is proxied. The number
-     * of chars that the caller wanted to read (1 for the {@link #read()}
-     * method, buffer length for {@link #read(char[])}, etc.) is given as
-     * an argument.
-     * <p>
-     * Subclasses can override this method to add common pre-processing
-     * functionality without having to override all the read methods.
-     * The default implementation does nothing.
-     * <p>
-     * Note this method is <em>not</em> called from {@link #skip(long)} or
-     * {@link #reset()}. You need to explicitly override those methods if
-     * you want to add pre-processing steps also to them.
-     *
-     * @since 2.0
-     * @param n number of chars that the caller asked to be read
-     * @throws IOException if the pre-processing fails
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void beforeRead(final int n) throws IOException {
-        // noop
-    }
-
-    /**
-     * Invoked by the read methods after the proxied call has returned
-     * successfully. The number of chars returned to the caller (or -1 if
-     * the end of stream was reached) is given as an argument.
-     * <p>
-     * Subclasses can override this method to add common post-processing
-     * functionality without having to override all the read methods.
-     * The default implementation does nothing.
-     * <p>
-     * Note this method is <em>not</em> called from {@link #skip(long)} or
-     * {@link #reset()}. You need to explicitly override those methods if
-     * you want to add post-processing steps also to them.
-     *
-     * @since 2.0
-     * @param n number of chars read, or -1 if the end of stream was reached
-     * @throws IOException if the post-processing fails
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void afterRead(final int n) throws IOException {
-        // noop
-    }
-
-    /**
-     * Handle any IOExceptions thrown.
-     * <p>
-     * This method provides a point to implement custom exception
-     * handling. The default behavior is to re-throw the exception.
-     * @param e The IOException thrown
-     * @throws IOException if an I/O error occurs.
-     * @since 2.0
-     */
-    protected void handleIOException(final IOException e) throws IOException {
-        throw e;
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/input/ReaderInputStream.java b/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
index 061a507..5e4b3db 100644
--- a/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
@@ -106,31 +106,26 @@ public class ReaderInputStream extends InputStream {
     private boolean endOfInput;
 
     /**
-     * Constructs a new {@link ReaderInputStream}.
+     * Constructs a new {@link ReaderInputStream} that uses the default character encoding
+     * with a default input buffer size of {@value #DEFAULT_BUFFER_SIZE} characters.
      *
      * @param reader the target {@link Reader}
-     * @param encoder the charset encoder
-     * @since 2.1
+     * @deprecated 2.5 use {@link #ReaderInputStream(Reader, Charset)} instead
      */
-    public ReaderInputStream(final Reader reader, final CharsetEncoder encoder) {
-        this(reader, encoder, DEFAULT_BUFFER_SIZE);
+    @Deprecated
+    public ReaderInputStream(final Reader reader) {
+        this(reader, Charset.defaultCharset());
     }
 
     /**
-     * Constructs a new {@link ReaderInputStream}.
+     * Constructs a new {@link ReaderInputStream} with a default input buffer size of
+     * {@value #DEFAULT_BUFFER_SIZE} characters.
      *
      * @param reader the target {@link Reader}
-     * @param encoder the charset encoder
-     * @param bufferSize the size of the input buffer in number of characters
-     * @since 2.1
+     * @param charset the charset encoding
      */
-    public ReaderInputStream(final Reader reader, final CharsetEncoder encoder, final int bufferSize) {
-        this.reader = reader;
-        this.encoder = encoder;
-        this.encoderIn = CharBuffer.allocate(bufferSize);
-        this.encoderIn.flip();
-        this.encoderOut = ByteBuffer.allocate(128);
-        this.encoderOut.flip();
+    public ReaderInputStream(final Reader reader, final Charset charset) {
+        this(reader, charset, DEFAULT_BUFFER_SIZE);
     }
 
     /**
@@ -151,25 +146,31 @@ public class ReaderInputStream extends InputStream {
     }
 
     /**
-     * Constructs a new {@link ReaderInputStream} with a default input buffer size of
-     * {@value #DEFAULT_BUFFER_SIZE} characters.
+     * Constructs a new {@link ReaderInputStream}.
      *
      * @param reader the target {@link Reader}
-     * @param charset the charset encoding
+     * @param encoder the charset encoder
+     * @since 2.1
      */
-    public ReaderInputStream(final Reader reader, final Charset charset) {
-        this(reader, charset, DEFAULT_BUFFER_SIZE);
+    public ReaderInputStream(final Reader reader, final CharsetEncoder encoder) {
+        this(reader, encoder, DEFAULT_BUFFER_SIZE);
     }
 
     /**
      * Constructs a new {@link ReaderInputStream}.
      *
      * @param reader the target {@link Reader}
-     * @param charsetName the name of the charset encoding
+     * @param encoder the charset encoder
      * @param bufferSize the size of the input buffer in number of characters
+     * @since 2.1
      */
-    public ReaderInputStream(final Reader reader, final String charsetName, final int bufferSize) {
-        this(reader, Charset.forName(charsetName), bufferSize);
+    public ReaderInputStream(final Reader reader, final CharsetEncoder encoder, final int bufferSize) {
+        this.reader = reader;
+        this.encoder = encoder;
+        this.encoderIn = CharBuffer.allocate(bufferSize);
+        this.encoderIn.flip();
+        this.encoderOut = ByteBuffer.allocate(128);
+        this.encoderOut.flip();
     }
 
     /**
@@ -184,15 +185,24 @@ public class ReaderInputStream extends InputStream {
     }
 
     /**
-     * Constructs a new {@link ReaderInputStream} that uses the default character encoding
-     * with a default input buffer size of {@value #DEFAULT_BUFFER_SIZE} characters.
+     * Constructs a new {@link ReaderInputStream}.
      *
      * @param reader the target {@link Reader}
-     * @deprecated 2.5 use {@link #ReaderInputStream(Reader, Charset)} instead
+     * @param charsetName the name of the charset encoding
+     * @param bufferSize the size of the input buffer in number of characters
      */
-    @Deprecated
-    public ReaderInputStream(final Reader reader) {
-        this(reader, Charset.defaultCharset());
+    public ReaderInputStream(final Reader reader, final String charsetName, final int bufferSize) {
+        this(reader, Charset.forName(charsetName), bufferSize);
+    }
+
+    /**
+     * Close the stream. This method will cause the underlying {@link Reader}
+     * to be closed.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void close() throws IOException {
+        reader.close();
     }
 
     /**
@@ -222,6 +232,39 @@ public class ReaderInputStream extends InputStream {
     }
 
     /**
+     * Read a single byte.
+     *
+     * @return either the byte read or {@code -1} if the end of the stream
+     *         has been reached
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public int read() throws IOException {
+        for (;;) {
+            if (encoderOut.hasRemaining()) {
+                return encoderOut.get() & 0xFF;
+            }
+            fillBuffer();
+            if (endOfInput && !encoderOut.hasRemaining()) {
+                return EOF;
+            }
+        }
+    }
+
+    /**
+     * Read the specified number of bytes into an array.
+     *
+     * @param b the byte array to read into
+     * @return the number of bytes read or {@code -1}
+     *         if the end of the stream has been reached
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public int read(final byte[] b) throws IOException {
+        return read(b, 0, b.length);
+    }
+
+    /**
      * Read the specified number of bytes into an array.
      *
      * @param array the byte array to read into
@@ -257,47 +300,4 @@ public class ReaderInputStream extends InputStream {
         }
         return read == 0 && endOfInput ? EOF : read;
     }
-
-    /**
-     * Read the specified number of bytes into an array.
-     *
-     * @param b the byte array to read into
-     * @return the number of bytes read or {@code -1}
-     *         if the end of the stream has been reached
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public int read(final byte[] b) throws IOException {
-        return read(b, 0, b.length);
-    }
-
-    /**
-     * Read a single byte.
-     *
-     * @return either the byte read or {@code -1} if the end of the stream
-     *         has been reached
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public int read() throws IOException {
-        for (;;) {
-            if (encoderOut.hasRemaining()) {
-                return encoderOut.get() & 0xFF;
-            }
-            fillBuffer();
-            if (endOfInput && !encoderOut.hasRemaining()) {
-                return EOF;
-            }
-        }
-    }
-
-    /**
-     * Close the stream. This method will cause the underlying {@link Reader}
-     * to be closed.
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        reader.close();
-    }
 }
diff --git a/src/main/java/org/apache/commons/io/input/TaggedInputStream.java b/src/main/java/org/apache/commons/io/input/TaggedInputStream.java
index d30dc84..add4eb8 100644
--- a/src/main/java/org/apache/commons/io/input/TaggedInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/TaggedInputStream.java
@@ -79,6 +79,17 @@ public class TaggedInputStream extends ProxyInputStream {
     }
 
     /**
+     * Tags any IOExceptions thrown, wrapping and re-throwing.
+     *
+     * @param e The IOException thrown
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    protected void handleIOException(final IOException e) throws IOException {
+        throw new TaggedIOException(e, tag);
+    }
+
+    /**
      * Tests if the given exception was caused by this stream.
      *
      * @param exception an exception
@@ -103,15 +114,4 @@ public class TaggedInputStream extends ProxyInputStream {
         TaggedIOException.throwCauseIfTaggedWith(throwable, tag);
     }
 
-    /**
-     * Tags any IOExceptions thrown, wrapping and re-throwing.
-     *
-     * @param e The IOException thrown
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    protected void handleIOException(final IOException e) throws IOException {
-        throw new TaggedIOException(e, tag);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/input/TaggedReader.java b/src/main/java/org/apache/commons/io/input/TaggedReader.java
index 6805f87..fff91e5 100644
--- a/src/main/java/org/apache/commons/io/input/TaggedReader.java
+++ b/src/main/java/org/apache/commons/io/input/TaggedReader.java
@@ -78,6 +78,17 @@ public class TaggedReader extends ProxyReader {
     }
 
     /**
+     * Tags any IOExceptions thrown, wrapping and re-throwing.
+     *
+     * @param e The IOException thrown
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    protected void handleIOException(final IOException e) throws IOException {
+        throw new TaggedIOException(e, tag);
+    }
+
+    /**
      * Tests if the given exception was caused by this reader.
      *
      * @param exception an exception
@@ -99,15 +110,4 @@ public class TaggedReader extends ProxyReader {
         TaggedIOException.throwCauseIfTaggedWith(throwable, tag);
     }
 
-    /**
-     * Tags any IOExceptions thrown, wrapping and re-throwing.
-     *
-     * @param e The IOException thrown
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    protected void handleIOException(final IOException e) throws IOException {
-        throw new TaggedIOException(e, tag);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/input/TailerListener.java b/src/main/java/org/apache/commons/io/input/TailerListener.java
index de8de20..ae86685 100644
--- a/src/main/java/org/apache/commons/io/input/TailerListener.java
+++ b/src/main/java/org/apache/commons/io/input/TailerListener.java
@@ -24,13 +24,6 @@ package org.apache.commons.io.input;
 public interface TailerListener {
 
     /**
-     * The tailer will call this method during construction,
-     * giving the listener a method of stopping the tailer.
-     * @param tailer the tailer.
-     */
-    void init(Tailer tailer);
-
-    /**
      * This method is called if the tailed file is not found.
      * <p>
      * <b>Note:</b> this is called from the tailer thread.
@@ -50,21 +43,28 @@ public interface TailerListener {
     void fileRotated();
 
     /**
-     * Handles a line from a Tailer.
+     * Handles an Exception.
      * <p>
      * <b>Note:</b> this is called from the tailer thread.
      * </p>
-     * @param line the line.
+     * @param ex the exception.
      */
-    void handle(String line);
+    void handle(Exception ex);
 
     /**
-     * Handles an Exception.
+     * Handles a line from a Tailer.
      * <p>
      * <b>Note:</b> this is called from the tailer thread.
      * </p>
-     * @param ex the exception.
+     * @param line the line.
      */
-    void handle(Exception ex);
+    void handle(String line);
+
+    /**
+     * The tailer will call this method during construction,
+     * giving the listener a method of stopping the tailer.
+     * @param tailer the tailer.
+     */
+    void init(Tailer tailer);
 
 }
diff --git a/src/main/java/org/apache/commons/io/input/TailerListenerAdapter.java b/src/main/java/org/apache/commons/io/input/TailerListenerAdapter.java
index fbf0b44..6b8760c 100644
--- a/src/main/java/org/apache/commons/io/input/TailerListenerAdapter.java
+++ b/src/main/java/org/apache/commons/io/input/TailerListenerAdapter.java
@@ -24,12 +24,16 @@ package org.apache.commons.io.input;
 public class TailerListenerAdapter implements TailerListener {
 
     /**
-     * The tailer will call this method during construction,
-     * giving the listener a method of stopping the tailer.
-     * @param tailer the tailer.
+     * Called each time the Tailer reaches the end of the file.
+     *
+     * <b>Note:</b> this is called from the tailer thread.
+     *
+     * Note: a future version of commons-io will pull this method up to the TailerListener interface,
+     * for now clients must subclass this class to use this feature.
+     *
+     * @since 2.5
      */
-    @Override
-    public void init(final Tailer tailer) {
+    public void endOfFileReached() {
         // noop
     }
 
@@ -53,34 +57,30 @@ public class TailerListenerAdapter implements TailerListener {
     }
 
     /**
-     * Handles a line from a Tailer.
-     * @param line the line.
+     * Handles an Exception .
+     * @param ex the exception.
      */
     @Override
-    public void handle(final String line) {
+    public void handle(final Exception ex) {
         // noop
     }
 
     /**
-     * Handles an Exception .
-     * @param ex the exception.
+     * Handles a line from a Tailer.
+     * @param line the line.
      */
     @Override
-    public void handle(final Exception ex) {
+    public void handle(final String line) {
         // noop
     }
 
     /**
-     * Called each time the Tailer reaches the end of the file.
-     *
-     * <b>Note:</b> this is called from the tailer thread.
-     *
-     * Note: a future version of commons-io will pull this method up to the TailerListener interface,
-     * for now clients must subclass this class to use this feature.
-     *
-     * @since 2.5
+     * The tailer will call this method during construction,
+     * giving the listener a method of stopping the tailer.
+     * @param tailer the tailer.
      */
-    public void endOfFileReached() {
+    @Override
+    public void init(final Tailer tailer) {
         // noop
     }
 }
diff --git a/src/main/java/org/apache/commons/io/input/TeeInputStream.java b/src/main/java/org/apache/commons/io/input/TeeInputStream.java
index 27d32b8..ae1abff 100644
--- a/src/main/java/org/apache/commons/io/input/TeeInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/TeeInputStream.java
@@ -119,16 +119,14 @@ public class TeeInputStream extends ProxyInputStream {
      * to the associated output stream.
      *
      * @param bts byte buffer
-     * @param st start offset within the buffer
-     * @param end maximum number of bytes to read
      * @return number of bytes read, or -1 if the stream has ended
      * @throws IOException if the stream could not be read (or written)
      */
     @Override
-    public int read(final byte[] bts, final int st, final int end) throws IOException {
-        final int n = super.read(bts, st, end);
+    public int read(final byte[] bts) throws IOException {
+        final int n = super.read(bts);
         if (n != EOF) {
-            branch.write(bts, st, n);
+            branch.write(bts, 0, n);
         }
         return n;
     }
@@ -138,14 +136,16 @@ public class TeeInputStream extends ProxyInputStream {
      * to the associated output stream.
      *
      * @param bts byte buffer
+     * @param st start offset within the buffer
+     * @param end maximum number of bytes to read
      * @return number of bytes read, or -1 if the stream has ended
      * @throws IOException if the stream could not be read (or written)
      */
     @Override
-    public int read(final byte[] bts) throws IOException {
-        final int n = super.read(bts);
+    public int read(final byte[] bts, final int st, final int end) throws IOException {
+        final int n = super.read(bts, st, end);
         if (n != EOF) {
-            branch.write(bts, 0, n);
+            branch.write(bts, st, n);
         }
         return n;
     }
diff --git a/src/main/java/org/apache/commons/io/input/TimestampedObserver.java b/src/main/java/org/apache/commons/io/input/TimestampedObserver.java
index c9af849..ef8fc88 100644
--- a/src/main/java/org/apache/commons/io/input/TimestampedObserver.java
+++ b/src/main/java/org/apache/commons/io/input/TimestampedObserver.java
@@ -60,6 +60,15 @@ public class TimestampedObserver extends Observer {
     }
 
     /**
+     * Gets the instant for when this instance was created.
+     *
+     * @return the instant for when this instance was created.
+     */
+    public Instant getOpenInstant() {
+        return openInstant;
+    }
+
+    /**
      * Gets the Duration between creation and close.
      *
      * @return the Duration between creation and close.
@@ -77,15 +86,6 @@ public class TimestampedObserver extends Observer {
         return Duration.between(openInstant, Instant.now());
     }
 
-    /**
-     * Gets the instant for when this instance was created.
-     *
-     * @return the instant for when this instance was created.
-     */
-    public Instant getOpenInstant() {
-        return openInstant;
-    }
-
     @Override
     public String toString() {
         return "TimestampedObserver [openInstant=" + openInstant + ", closeInstant=" + closeInstant + "]";
diff --git a/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java b/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java
index e4bfe71..40dd692 100644
--- a/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java
@@ -52,19 +52,37 @@ public class UnixLineEndingInputStream extends InputStream {
     }
 
     /**
-     * Reads the next item from the target, updating internal flags in the process
-     * @return the next int read from the target stream
+     * Closes the stream. Also closes the underlying stream.
      * @throws IOException upon error
      */
-    private int readWithUpdate() throws IOException {
-        final int target = this.target.read();
-        eofSeen = target == EOF;
-        if (eofSeen) {
-            return target;
+    @Override
+    public void close() throws IOException {
+        super.close();
+        target.close();
+    }
+
+    /**
+     * Handles the EOF-handling at the end of the stream
+     * @param previousWasSlashR Indicates if the last seen was a \r
+     * @return The next char to output to the stream
+     */
+    private int eofGame(final boolean previousWasSlashR) {
+        if (previousWasSlashR || !ensureLineFeedAtEndOfFile) {
+            return EOF;
         }
-        slashNSeen = target == LF;
-        slashRSeen = target == CR;
-        return target;
+        if (!slashNSeen) {
+            slashNSeen = true;
+            return LF;
+        }
+        return EOF;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void mark(final int readlimit) {
+        throw UnsupportedOperationExceptions.mark();
     }
 
     /**
@@ -92,36 +110,18 @@ public class UnixLineEndingInputStream extends InputStream {
     }
 
     /**
-     * Handles the EOF-handling at the end of the stream
-     * @param previousWasSlashR Indicates if the last seen was a \r
-     * @return The next char to output to the stream
-     */
-    private int eofGame(final boolean previousWasSlashR) {
-        if (previousWasSlashR || !ensureLineFeedAtEndOfFile) {
-            return EOF;
-        }
-        if (!slashNSeen) {
-            slashNSeen = true;
-            return LF;
-        }
-        return EOF;
-    }
-
-    /**
-     * Closes the stream. Also closes the underlying stream.
+     * Reads the next item from the target, updating internal flags in the process
+     * @return the next int read from the target stream
      * @throws IOException upon error
      */
-    @Override
-    public void close() throws IOException {
-        super.close();
-        target.close();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public synchronized void mark(final int readlimit) {
-        throw UnsupportedOperationExceptions.mark();
+    private int readWithUpdate() throws IOException {
+        final int target = this.target.read();
+        eofSeen = target == EOF;
+        if (eofSeen) {
+            return target;
+        }
+        slashNSeen = target == LF;
+        slashRSeen = target == CR;
+        return target;
     }
 }
diff --git a/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java b/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java
index 0537230..4f96e2e 100644
--- a/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java
@@ -118,6 +118,17 @@ public class UnsynchronizedByteArrayInputStream extends InputStream {
         return offset < eod ? eod - offset : 0;
     }
 
+    @SuppressWarnings("sync-override")
+    @Override
+    public void mark(final int readlimit) {
+        this.markedOffset = this.offset;
+    }
+
+    @Override
+    public boolean markSupported() {
+        return true;
+    }
+
     @Override
     public int read() {
         return offset < eod ? data[offset++] & 0xff : END_OF_STREAM;
@@ -152,6 +163,12 @@ public class UnsynchronizedByteArrayInputStream extends InputStream {
         return actualLen;
     }
 
+    @SuppressWarnings("sync-override")
+    @Override
+    public void reset() {
+        this.offset = this.markedOffset;
+    }
+
     @Override
     public long skip(final long n) {
         if (n < 0) {
@@ -166,21 +183,4 @@ public class UnsynchronizedByteArrayInputStream extends InputStream {
         offset += actualSkip;
         return actualSkip;
     }
-
-    @Override
-    public boolean markSupported() {
-        return true;
-    }
-
-    @SuppressWarnings("sync-override")
-    @Override
-    public void mark(final int readlimit) {
-        this.markedOffset = this.offset;
-    }
-
-    @SuppressWarnings("sync-override")
-    @Override
-    public void reset() {
-        this.offset = this.markedOffset;
-    }
 }
diff --git a/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java b/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java
index 921b334..bbf1797 100644
--- a/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java
@@ -54,43 +54,13 @@ public class WindowsLineEndingInputStream  extends InputStream {
     }
 
     /**
-     * Reads the next item from the target, updating internal flags in the process
-     * @return the next int read from the target stream
+     * Closes the stream. Also closes the underlying stream.
      * @throws IOException upon error
      */
-    private int readWithUpdate() throws IOException {
-        final int target = this.target.read();
-        eofSeen = target == EOF;
-        if (eofSeen) {
-            return target;
-        }
-        slashRSeen = target == CR;
-        slashNSeen = target == LF;
-        return target;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public int read() throws IOException {
-        if (eofSeen) {
-            return eofGame();
-        }
-        if (injectSlashN) {
-            injectSlashN = false;
-            return LF;
-        }
-        final boolean prevWasSlashR = slashRSeen;
-        final int target = readWithUpdate();
-        if (eofSeen) {
-            return eofGame();
-        }
-        if ((target == LF) && !prevWasSlashR) {
-            injectSlashN = true;
-            return CR;
-        }
-        return target;
+    public void close() throws IOException {
+        super.close();
+        target.close();
     }
 
     /**
@@ -114,20 +84,50 @@ public class WindowsLineEndingInputStream  extends InputStream {
     }
 
     /**
-     * Closes the stream. Also closes the underlying stream.
-     * @throws IOException upon error
+     * {@inheritDoc}
      */
     @Override
-    public void close() throws IOException {
-        super.close();
-        target.close();
+    public synchronized void mark(final int readlimit) {
+        throw UnsupportedOperationExceptions.mark();
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public synchronized void mark(final int readlimit) {
-        throw UnsupportedOperationExceptions.mark();
+    public int read() throws IOException {
+        if (eofSeen) {
+            return eofGame();
+        }
+        if (injectSlashN) {
+            injectSlashN = false;
+            return LF;
+        }
+        final boolean prevWasSlashR = slashRSeen;
+        final int target = readWithUpdate();
+        if (eofSeen) {
+            return eofGame();
+        }
+        if ((target == LF) && !prevWasSlashR) {
+            injectSlashN = true;
+            return CR;
+        }
+        return target;
+    }
+
+    /**
+     * Reads the next item from the target, updating internal flags in the process
+     * @return the next int read from the target stream
+     * @throws IOException upon error
+     */
+    private int readWithUpdate() throws IOException {
+        final int target = this.target.read();
+        eofSeen = target == EOF;
+        if (eofSeen) {
+            return target;
+        }
+        slashRSeen = target == CR;
+        slashNSeen = target == LF;
+        return target;
     }
 }
diff --git a/src/main/java/org/apache/commons/io/input/XmlStreamReaderException.java b/src/main/java/org/apache/commons/io/input/XmlStreamReaderException.java
index ca61be7..7570329 100644
--- a/src/main/java/org/apache/commons/io/input/XmlStreamReaderException.java
+++ b/src/main/java/org/apache/commons/io/input/XmlStreamReaderException.java
@@ -96,21 +96,15 @@ public class XmlStreamReaderException extends IOException {
     }
 
     /**
-     * Returns the encoding guess based on the first bytes of the InputStream.
-     *
-     * @return the encoding guess, null if it couldn't be guessed.
-     */
-    public String getXmlGuessEncoding() {
-        return xmlGuessEncoding;
-    }
-
-    /**
-     * Returns the encoding found in the XML prolog of the InputStream.
+     * Returns the encoding in the content-type used to attempt determining the
+     * encoding.
      *
-     * @return the encoding of the XML prolog, null if none.
+     * @return the encoding in the content-type, null if there was not
+     *         content-type, no encoding in it or the encoding detection did not
+     *         involve HTTP.
      */
-    public String getXmlEncoding() {
-        return xmlEncoding;
+    public String getContentTypeEncoding() {
+        return contentTypeEncoding;
     }
 
     /**
@@ -125,14 +119,20 @@ public class XmlStreamReaderException extends IOException {
     }
 
     /**
-     * Returns the encoding in the content-type used to attempt determining the
-     * encoding.
+     * Returns the encoding found in the XML prolog of the InputStream.
      *
-     * @return the encoding in the content-type, null if there was not
-     *         content-type, no encoding in it or the encoding detection did not
-     *         involve HTTP.
+     * @return the encoding of the XML prolog, null if none.
      */
-    public String getContentTypeEncoding() {
-        return contentTypeEncoding;
+    public String getXmlEncoding() {
+        return xmlEncoding;
+    }
+
+    /**
+     * Returns the encoding guess based on the first bytes of the InputStream.
+     *
+     * @return the encoding guess, null if it couldn't be guessed.
+     */
+    public String getXmlGuessEncoding() {
+        return xmlGuessEncoding;
     }
 }
diff --git a/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java b/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java
index 7853533..c8393a1 100644
--- a/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java
@@ -43,6 +43,16 @@ public class CircularBufferInputStream extends InputStream {
     private boolean eof;
 
     /**
+     * Creates a new instance, which filters the given input stream, and uses a reasonable default buffer size
+     * ({@link IOUtils#DEFAULT_BUFFER_SIZE}).
+     *
+     * @param inputStream The input stream, which is being buffered.
+     */
+    public CircularBufferInputStream(final InputStream inputStream) {
+        this(inputStream, IOUtils.DEFAULT_BUFFER_SIZE);
+    }
+
+    /**
      * Creates a new instance, which filters the given input stream, and uses the given buffer size.
      *
      * @param inputStream The input stream, which is being buffered.
@@ -58,14 +68,11 @@ public class CircularBufferInputStream extends InputStream {
         this.eof = false;
     }
 
-    /**
-     * Creates a new instance, which filters the given input stream, and uses a reasonable default buffer size
-     * ({@link IOUtils#DEFAULT_BUFFER_SIZE}).
-     *
-     * @param inputStream The input stream, which is being buffered.
-     */
-    public CircularBufferInputStream(final InputStream inputStream) {
-        this(inputStream, IOUtils.DEFAULT_BUFFER_SIZE);
+    @Override
+    public void close() throws IOException {
+        in.close();
+        eof = true;
+        buffer.clear();
     }
 
     /**
@@ -137,11 +144,4 @@ public class CircularBufferInputStream extends InputStream {
         }
         return result;
     }
-
-    @Override
-    public void close() throws IOException {
-        in.close();
-        eof = true;
-        buffer.clear();
-    }
 }
diff --git a/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java b/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java
index 78998c3..98a4897 100644
--- a/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java
+++ b/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java
@@ -34,18 +34,6 @@ public class CircularByteBuffer {
     private int currentNumberOfBytes;
 
     /**
-     * Creates a new instance with the given buffer size.
-     *
-     * @param size the size of buffer to create
-     */
-    public CircularByteBuffer(final int size) {
-        buffer = IOUtils.byteArray(size);
-        startOffset = 0;
-        endOffset = 0;
-        currentNumberOfBytes = 0;
-    }
-
-    /**
      * Creates a new instance with a reasonable default buffer size ({@link IOUtils#DEFAULT_BUFFER_SIZE}).
      */
     public CircularByteBuffer() {
@@ -53,64 +41,15 @@ public class CircularByteBuffer {
     }
 
     /**
-     * Returns the next byte from the buffer, removing it at the same time, so
-     * that following invocations won't return it again.
-     *
-     * @return The byte, which is being returned.
-     * @throws IllegalStateException The buffer is empty. Use {@link #hasBytes()},
-     *                               or {@link #getCurrentNumberOfBytes()}, to prevent this exception.
-     */
-    public byte read() {
-        if (currentNumberOfBytes <= 0) {
-            throw new IllegalStateException("No bytes available.");
-        }
-        final byte b = buffer[startOffset];
-        --currentNumberOfBytes;
-        if (++startOffset == buffer.length) {
-            startOffset = 0;
-        }
-        return b;
-    }
-
-    /**
-     * Returns the given number of bytes from the buffer by storing them in
-     * the given byte array at the given offset.
+     * Creates a new instance with the given buffer size.
      *
-     * @param targetBuffer The byte array, where to add bytes.
-     * @param targetOffset The offset, where to store bytes in the byte array.
-     * @param length The number of bytes to return.
-     * @throws NullPointerException     The byte array {@code pBuffer} is null.
-     * @throws IllegalArgumentException Either of {@code pOffset}, or {@code length} is negative,
-     *                                  or the length of the byte array {@code targetBuffer} is too small.
-     * @throws IllegalStateException    The buffer doesn't hold the given number
-     *                                  of bytes. Use {@link #getCurrentNumberOfBytes()} to prevent this
-     *                                  exception.
+     * @param size the size of buffer to create
      */
-    public void read(final byte[] targetBuffer, final int targetOffset, final int length) {
-        Objects.requireNonNull(targetBuffer, "targetBuffer");
-        if (targetOffset < 0 || targetOffset >= targetBuffer.length) {
-            throw new IllegalArgumentException("Invalid offset: " + targetOffset);
-        }
-        if (length < 0 || length > buffer.length) {
-            throw new IllegalArgumentException("Invalid length: " + length);
-        }
-        if (targetOffset + length > targetBuffer.length) {
-            throw new IllegalArgumentException("The supplied byte array contains only "
-                    + targetBuffer.length + " bytes, but offset, and length would require "
-                    + (targetOffset + length - 1));
-        }
-        if (currentNumberOfBytes < length) {
-            throw new IllegalStateException("Currently, there are only " + currentNumberOfBytes
-                    + "in the buffer, not " + length);
-        }
-        int offset = targetOffset;
-        for (int i = 0; i < length; i++) {
-            targetBuffer[offset++] = buffer[startOffset];
-            --currentNumberOfBytes;
-            if (++startOffset == buffer.length) {
-                startOffset = 0;
-            }
-        }
+    public CircularByteBuffer(final int size) {
+        buffer = IOUtils.byteArray(size);
+        startOffset = 0;
+        endOffset = 0;
+        currentNumberOfBytes = 0;
     }
 
     /**
@@ -133,44 +72,6 @@ public class CircularByteBuffer {
     }
 
     /**
-     * Returns, whether the next bytes in the buffer are exactly those, given by
-     * {@code sourceBuffer}, {@code offset}, and {@code length}. No bytes are being
-     * removed from the buffer. If the result is true, then the following invocations
-     * of {@link #read()} are guaranteed to return exactly those bytes.
-     *
-     * @param sourceBuffer the buffer to compare against
-     * @param offset start offset
-     * @param length length to compare
-     * @return True, if the next invocations of {@link #read()} will return the
-     * bytes at offsets {@code pOffset}+0, {@code pOffset}+1, ...,
-     * {@code pOffset}+{@code length}-1 of byte array {@code pBuffer}.
-     * @throws IllegalArgumentException Either of {@code pOffset}, or {@code length} is negative.
-     * @throws NullPointerException     The byte array {@code pBuffer} is null.
-     */
-    public boolean peek(final byte[] sourceBuffer, final int offset, final int length) {
-        Objects.requireNonNull(sourceBuffer, "Buffer");
-        if (offset < 0 || offset >= sourceBuffer.length) {
-            throw new IllegalArgumentException("Invalid offset: " + offset);
-        }
-        if (length < 0 || length > buffer.length) {
-            throw new IllegalArgumentException("Invalid length: " + length);
-        }
-        if (length < currentNumberOfBytes) {
-            return false;
-        }
-        int localOffset = startOffset;
-        for (int i = 0; i < length; i++) {
-            if (buffer[localOffset] != sourceBuffer[i + offset]) {
-                return false;
-            }
-            if (++localOffset == buffer.length) {
-                localOffset = 0;
-            }
-        }
-        return true;
-    }
-
-    /**
      * Adds the given bytes to the buffer. This is the same as invoking {@link #add(byte)}
      * for the bytes at offsets {@code offset+0}, {@code offset+1}, ...,
      * {@code offset+length-1} of byte array {@code targetBuffer}.
@@ -204,6 +105,42 @@ public class CircularByteBuffer {
     }
 
     /**
+     * Removes all bytes from the buffer.
+     */
+    public void clear() {
+        startOffset = 0;
+        endOffset = 0;
+        currentNumberOfBytes = 0;
+    }
+
+    /**
+     * Returns the number of bytes, that are currently present in the buffer.
+     *
+     * @return the number of bytes
+     */
+    public int getCurrentNumberOfBytes() {
+        return currentNumberOfBytes;
+    }
+
+    /**
+     * Returns the number of bytes, that can currently be added to the buffer.
+     *
+     * @return the number of bytes that can be added
+     */
+    public int getSpace() {
+        return buffer.length - currentNumberOfBytes;
+    }
+
+    /**
+     * Returns, whether the buffer is currently holding, at least, a single byte.
+     *
+     * @return true if the buffer is not empty
+     */
+    public boolean hasBytes() {
+        return currentNumberOfBytes > 0;
+    }
+
+    /**
      * Returns, whether there is currently room for a single byte in the buffer.
      * Same as {@link #hasSpace(int) hasSpace(1)}.
      *
@@ -228,38 +165,101 @@ public class CircularByteBuffer {
     }
 
     /**
-     * Returns, whether the buffer is currently holding, at least, a single byte.
+     * Returns, whether the next bytes in the buffer are exactly those, given by
+     * {@code sourceBuffer}, {@code offset}, and {@code length}. No bytes are being
+     * removed from the buffer. If the result is true, then the following invocations
+     * of {@link #read()} are guaranteed to return exactly those bytes.
      *
-     * @return true if the buffer is not empty
+     * @param sourceBuffer the buffer to compare against
+     * @param offset start offset
+     * @param length length to compare
+     * @return True, if the next invocations of {@link #read()} will return the
+     * bytes at offsets {@code pOffset}+0, {@code pOffset}+1, ...,
+     * {@code pOffset}+{@code length}-1 of byte array {@code pBuffer}.
+     * @throws IllegalArgumentException Either of {@code pOffset}, or {@code length} is negative.
+     * @throws NullPointerException     The byte array {@code pBuffer} is null.
      */
-    public boolean hasBytes() {
-        return currentNumberOfBytes > 0;
+    public boolean peek(final byte[] sourceBuffer, final int offset, final int length) {
+        Objects.requireNonNull(sourceBuffer, "Buffer");
+        if (offset < 0 || offset >= sourceBuffer.length) {
+            throw new IllegalArgumentException("Invalid offset: " + offset);
+        }
+        if (length < 0 || length > buffer.length) {
+            throw new IllegalArgumentException("Invalid length: " + length);
+        }
+        if (length < currentNumberOfBytes) {
+            return false;
+        }
+        int localOffset = startOffset;
+        for (int i = 0; i < length; i++) {
+            if (buffer[localOffset] != sourceBuffer[i + offset]) {
+                return false;
+            }
+            if (++localOffset == buffer.length) {
+                localOffset = 0;
+            }
+        }
+        return true;
     }
 
     /**
-     * Returns the number of bytes, that can currently be added to the buffer.
+     * Returns the next byte from the buffer, removing it at the same time, so
+     * that following invocations won't return it again.
      *
-     * @return the number of bytes that can be added
+     * @return The byte, which is being returned.
+     * @throws IllegalStateException The buffer is empty. Use {@link #hasBytes()},
+     *                               or {@link #getCurrentNumberOfBytes()}, to prevent this exception.
      */
-    public int getSpace() {
-        return buffer.length - currentNumberOfBytes;
+    public byte read() {
+        if (currentNumberOfBytes <= 0) {
+            throw new IllegalStateException("No bytes available.");
+        }
+        final byte b = buffer[startOffset];
+        --currentNumberOfBytes;
+        if (++startOffset == buffer.length) {
+            startOffset = 0;
+        }
+        return b;
     }
 
     /**
-     * Returns the number of bytes, that are currently present in the buffer.
+     * Returns the given number of bytes from the buffer by storing them in
+     * the given byte array at the given offset.
      *
-     * @return the number of bytes
-     */
-    public int getCurrentNumberOfBytes() {
-        return currentNumberOfBytes;
-    }
-
-    /**
-     * Removes all bytes from the buffer.
+     * @param targetBuffer The byte array, where to add bytes.
+     * @param targetOffset The offset, where to store bytes in the byte array.
+     * @param length The number of bytes to return.
+     * @throws NullPointerException     The byte array {@code pBuffer} is null.
+     * @throws IllegalArgumentException Either of {@code pOffset}, or {@code length} is negative,
+     *                                  or the length of the byte array {@code targetBuffer} is too small.
+     * @throws IllegalStateException    The buffer doesn't hold the given number
+     *                                  of bytes. Use {@link #getCurrentNumberOfBytes()} to prevent this
+     *                                  exception.
      */
-    public void clear() {
-        startOffset = 0;
-        endOffset = 0;
-        currentNumberOfBytes = 0;
+    public void read(final byte[] targetBuffer, final int targetOffset, final int length) {
+        Objects.requireNonNull(targetBuffer, "targetBuffer");
+        if (targetOffset < 0 || targetOffset >= targetBuffer.length) {
+            throw new IllegalArgumentException("Invalid offset: " + targetOffset);
+        }
+        if (length < 0 || length > buffer.length) {
+            throw new IllegalArgumentException("Invalid length: " + length);
+        }
+        if (targetOffset + length > targetBuffer.length) {
+            throw new IllegalArgumentException("The supplied byte array contains only "
+                    + targetBuffer.length + " bytes, but offset, and length would require "
+                    + (targetOffset + length - 1));
+        }
+        if (currentNumberOfBytes < length) {
+            throw new IllegalStateException("Currently, there are only " + currentNumberOfBytes
+                    + "in the buffer, not " + length);
+        }
+        int offset = targetOffset;
+        for (int i = 0; i < length; i++) {
+            targetBuffer[offset++] = buffer[startOffset];
+            --currentNumberOfBytes;
+            if (++startOffset == buffer.length) {
+                startOffset = 0;
+            }
+        }
     }
 }
diff --git a/src/main/java/org/apache/commons/io/input/buffer/PeekableInputStream.java b/src/main/java/org/apache/commons/io/input/buffer/PeekableInputStream.java
index df15e70..6e9b88c 100644
--- a/src/main/java/org/apache/commons/io/input/buffer/PeekableInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/buffer/PeekableInputStream.java
@@ -27,22 +27,22 @@ import java.util.Objects;
 public class PeekableInputStream extends CircularBufferInputStream {
 
     /**
-     * Creates a new instance, which filters the given input stream, and uses the given buffer size.
+     * Creates a new instance, which filters the given input stream, and uses a reasonable default buffer size (8192).
      *
      * @param inputStream The input stream, which is being buffered.
-     * @param bufferSize The size of the {@link CircularByteBuffer}, which is used internally.
      */
-    public PeekableInputStream(final InputStream inputStream, final int bufferSize) {
-        super(inputStream, bufferSize);
+    public PeekableInputStream(final InputStream inputStream) {
+        super(inputStream);
     }
 
     /**
-     * Creates a new instance, which filters the given input stream, and uses a reasonable default buffer size (8192).
+     * Creates a new instance, which filters the given input stream, and uses the given buffer size.
      *
      * @param inputStream The input stream, which is being buffered.
+     * @param bufferSize The size of the {@link CircularByteBuffer}, which is used internally.
      */
-    public PeekableInputStream(final InputStream inputStream) {
-        super(inputStream);
+    public PeekableInputStream(final InputStream inputStream, final int bufferSize) {
+        super(inputStream, bufferSize);
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/io/monitor/FileAlterationListener.java b/src/main/java/org/apache/commons/io/monitor/FileAlterationListener.java
index 0d01ab4..fd3f40a 100644
--- a/src/main/java/org/apache/commons/io/monitor/FileAlterationListener.java
+++ b/src/main/java/org/apache/commons/io/monitor/FileAlterationListener.java
@@ -29,11 +29,11 @@ import java.io.File;
 public interface FileAlterationListener {
 
     /**
-     * File system observer started checking event.
+     * Directory changed Event.
      *
-     * @param observer The file system observer
+     * @param directory The directory changed
      */
-    void onStart(final FileAlterationObserver observer);
+    void onDirectoryChange(final File directory);
 
     /**
      * Directory created Event.
@@ -43,18 +43,18 @@ public interface FileAlterationListener {
     void onDirectoryCreate(final File directory);
 
     /**
-     * Directory changed Event.
+     * Directory deleted Event.
      *
-     * @param directory The directory changed
+     * @param directory The directory deleted
      */
-    void onDirectoryChange(final File directory);
+    void onDirectoryDelete(final File directory);
 
     /**
-     * Directory deleted Event.
+     * File changed Event.
      *
-     * @param directory The directory deleted
+     * @param file The file changed
      */
-    void onDirectoryDelete(final File directory);
+    void onFileChange(final File file);
 
     /**
      * File created Event.
@@ -64,18 +64,18 @@ public interface FileAlterationListener {
     void onFileCreate(final File file);
 
     /**
-     * File changed Event.
+     * File deleted Event.
      *
-     * @param file The file changed
+     * @param file The file deleted
      */
-    void onFileChange(final File file);
+    void onFileDelete(final File file);
 
     /**
-     * File deleted Event.
+     * File system observer started checking event.
      *
-     * @param file The file deleted
+     * @param observer The file system observer
      */
-    void onFileDelete(final File file);
+    void onStart(final FileAlterationObserver observer);
 
     /**
      * File system observer finished checking event.
diff --git a/src/main/java/org/apache/commons/io/monitor/FileAlterationListenerAdaptor.java b/src/main/java/org/apache/commons/io/monitor/FileAlterationListenerAdaptor.java
index fa7a245..32633eb 100644
--- a/src/main/java/org/apache/commons/io/monitor/FileAlterationListenerAdaptor.java
+++ b/src/main/java/org/apache/commons/io/monitor/FileAlterationListenerAdaptor.java
@@ -28,12 +28,12 @@ import java.io.File;
 public class FileAlterationListenerAdaptor implements FileAlterationListener {
 
     /**
-     * File system observer started checking event.
+     * Directory changed Event.
      *
-     * @param observer The file system observer (ignored)
+     * @param directory The directory changed (ignored)
      */
     @Override
-    public void onStart(final FileAlterationObserver observer) {
+    public void onDirectoryChange(final File directory) {
         // noop
     }
 
@@ -48,22 +48,22 @@ public class FileAlterationListenerAdaptor implements FileAlterationListener {
     }
 
     /**
-     * Directory changed Event.
+     * Directory deleted Event.
      *
-     * @param directory The directory changed (ignored)
+     * @param directory The directory deleted (ignored)
      */
     @Override
-    public void onDirectoryChange(final File directory) {
+    public void onDirectoryDelete(final File directory) {
         // noop
     }
 
     /**
-     * Directory deleted Event.
+     * File changed Event.
      *
-     * @param directory The directory deleted (ignored)
+     * @param file The file changed (ignored)
      */
     @Override
-    public void onDirectoryDelete(final File directory) {
+    public void onFileChange(final File file) {
         // noop
     }
 
@@ -78,22 +78,22 @@ public class FileAlterationListenerAdaptor implements FileAlterationListener {
     }
 
     /**
-     * File changed Event.
+     * File deleted Event.
      *
-     * @param file The file changed (ignored)
+     * @param file The file deleted (ignored)
      */
     @Override
-    public void onFileChange(final File file) {
+    public void onFileDelete(final File file) {
         // noop
     }
 
     /**
-     * File deleted Event.
+     * File system observer started checking event.
      *
-     * @param file The file deleted (ignored)
+     * @param observer The file system observer (ignored)
      */
     @Override
-    public void onFileDelete(final File file) {
+    public void onStart(final FileAlterationObserver observer) {
         // noop
     }
 
diff --git a/src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java b/src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java
index 126f940..df539aa 100644
--- a/src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java
+++ b/src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java
@@ -93,32 +93,33 @@ public final class FileAlterationMonitor implements Runnable {
     }
 
     /**
-     * Returns the interval.
+     * Adds a file system observer to this monitor.
      *
-     * @return the interval
+     * @param observer The file system observer to add
      */
-    public long getInterval() {
-        return intervalMillis;
+    public void addObserver(final FileAlterationObserver observer) {
+        if (observer != null) {
+            observers.add(observer);
+        }
     }
 
     /**
-     * Sets the thread factory.
+     * Returns the interval.
      *
-     * @param threadFactory the thread factory
+     * @return the interval
      */
-    public synchronized void setThreadFactory(final ThreadFactory threadFactory) {
-        this.threadFactory = threadFactory;
+    public long getInterval() {
+        return intervalMillis;
     }
 
     /**
-     * Adds a file system observer to this monitor.
+     * Returns the set of {@link FileAlterationObserver} registered with
+     * this monitor.
      *
-     * @param observer The file system observer to add
+     * @return The set of {@link FileAlterationObserver}
      */
-    public void addObserver(final FileAlterationObserver observer) {
-        if (observer != null) {
-            observers.add(observer);
-        }
+    public Iterable<FileAlterationObserver> getObservers() {
+        return observers;
     }
 
     /**
@@ -135,13 +136,32 @@ public final class FileAlterationMonitor implements Runnable {
     }
 
     /**
-     * Returns the set of {@link FileAlterationObserver} registered with
-     * this monitor.
+     * Runs this monitor.
+     */
+    @Override
+    public void run() {
+        while (running) {
+            for (final FileAlterationObserver observer : observers) {
+                observer.checkAndNotify();
+            }
+            if (!running) {
+                break;
+            }
+            try {
+                Thread.sleep(intervalMillis);
+            } catch (final InterruptedException ignored) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Sets the thread factory.
      *
-     * @return The set of {@link FileAlterationObserver}
+     * @param threadFactory the thread factory
      */
-    public Iterable<FileAlterationObserver> getObservers() {
-        return observers;
+    public synchronized void setThreadFactory(final ThreadFactory threadFactory) {
+        this.threadFactory = threadFactory;
     }
 
     /**
@@ -197,24 +217,4 @@ public final class FileAlterationMonitor implements Runnable {
             observer.destroy();
         }
     }
-
-    /**
-     * Runs this monitor.
-     */
-    @Override
-    public void run() {
-        while (running) {
-            for (final FileAlterationObserver observer : observers) {
-                observer.checkAndNotify();
-            }
-            if (!running) {
-                break;
-            }
-            try {
-                Thread.sleep(intervalMillis);
-            } catch (final InterruptedException ignored) {
-                // ignore
-            }
-        }
-    }
 }
diff --git a/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java b/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java
index efab3cd..05f766f 100644
--- a/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java
+++ b/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java
@@ -130,36 +130,6 @@ public class FileAlterationObserver implements Serializable {
     /**
      * Constructs an observer for the specified directory.
      *
-     * @param directoryName the name of the directory to observe
-     */
-    public FileAlterationObserver(final String directoryName) {
-        this(new File(directoryName));
-    }
-
-    /**
-     * Constructs an observer for the specified directory and file filter.
-     *
-     * @param directoryName the name of the directory to observe
-     * @param fileFilter The file filter or null if none
-     */
-    public FileAlterationObserver(final String directoryName, final FileFilter fileFilter) {
-        this(new File(directoryName), fileFilter);
-    }
-
-    /**
-     * Constructs an observer for the specified directory, file filter and file comparator.
-     *
-     * @param directoryName the name of the directory to observe
-     * @param fileFilter The file filter or null if none
-     * @param ioCase what case sensitivity to use comparing file names, null means system sensitive
-     */
-    public FileAlterationObserver(final String directoryName, final FileFilter fileFilter, final IOCase ioCase) {
-        this(new File(directoryName), fileFilter, ioCase);
-    }
-
-    /**
-     * Constructs an observer for the specified directory.
-     *
      * @param directory the directory to observe
      */
     public FileAlterationObserver(final File directory) {
@@ -217,79 +187,47 @@ public class FileAlterationObserver implements Serializable {
     }
 
     /**
-     * Returns the directory being observed.
+     * Constructs an observer for the specified directory.
      *
-     * @return the directory being observed
+     * @param directoryName the name of the directory to observe
      */
-    public File getDirectory() {
-        return rootEntry.getFile();
+    public FileAlterationObserver(final String directoryName) {
+        this(new File(directoryName));
     }
 
     /**
-     * Returns the fileFilter.
+     * Constructs an observer for the specified directory and file filter.
      *
-     * @return the fileFilter
-     * @since 2.1
+     * @param directoryName the name of the directory to observe
+     * @param fileFilter The file filter or null if none
      */
-    public FileFilter getFileFilter() {
-        return fileFilter;
+    public FileAlterationObserver(final String directoryName, final FileFilter fileFilter) {
+        this(new File(directoryName), fileFilter);
     }
 
     /**
-     * Adds a file system listener.
+     * Constructs an observer for the specified directory, file filter and file comparator.
      *
-     * @param listener The file system listener
+     * @param directoryName the name of the directory to observe
+     * @param fileFilter The file filter or null if none
+     * @param ioCase what case sensitivity to use comparing file names, null means system sensitive
      */
-    public void addListener(final FileAlterationListener listener) {
-        if (listener != null) {
-            listeners.add(listener);
-        }
+    public FileAlterationObserver(final String directoryName, final FileFilter fileFilter, final IOCase ioCase) {
+        this(new File(directoryName), fileFilter, ioCase);
     }
 
     /**
-     * Removes a file system listener.
+     * Adds a file system listener.
      *
      * @param listener The file system listener
      */
-    public void removeListener(final FileAlterationListener listener) {
+    public void addListener(final FileAlterationListener listener) {
         if (listener != null) {
-            while (listeners.remove(listener)) {
-                // empty
-            }
+            listeners.add(listener);
         }
     }
 
     /**
-     * Returns the set of registered file system listeners.
-     *
-     * @return The file system listeners
-     */
-    public Iterable<FileAlterationListener> getListeners() {
-        return listeners;
-    }
-
-    /**
-     * Initializes the observer.
-     *
-     * @throws Exception if an error occurs
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    public void initialize() throws Exception {
-        rootEntry.refresh(rootEntry.getFile());
-        rootEntry.setChildren(doListFiles(rootEntry.getFile(), rootEntry));
-    }
-
-    /**
-     * Final processing.
-     *
-     * @throws Exception if an error occurs
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    public void destroy() throws Exception {
-        // noop
-    }
-
-    /**
      * Checks whether the file and its children have been created, modified or deleted.
      */
     public void checkAndNotify() {
@@ -363,18 +301,13 @@ public class FileAlterationObserver implements Serializable {
     }
 
     /**
-     * Lists the files
-     * @param file The file to list files for
-     * @param entry the parent entry
-     * @return The child files
+     * Final processing.
+     *
+     * @throws Exception if an error occurs
      */
-    private FileEntry[] doListFiles(final File file, final FileEntry entry) {
-        final File[] files = listFiles(file);
-        final FileEntry[] children = files.length > 0 ? new FileEntry[files.length] : FileEntry.EMPTY_FILE_ENTRY_ARRAY;
-        for (int i = 0; i < files.length; i++) {
-            children[i] = createFileEntry(entry, files[i]);
-        }
-        return children;
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    public void destroy() throws Exception {
+        // noop
     }
 
     /**
@@ -397,6 +330,36 @@ public class FileAlterationObserver implements Serializable {
     }
 
     /**
+     * Fires directory/file delete events to the registered listeners.
+     *
+     * @param entry The file entry
+     */
+    private void doDelete(final FileEntry entry) {
+        for (final FileAlterationListener listener : listeners) {
+            if (entry.isDirectory()) {
+                listener.onDirectoryDelete(entry.getFile());
+            } else {
+                listener.onFileDelete(entry.getFile());
+            }
+        }
+    }
+
+    /**
+     * Lists the files
+     * @param file The file to list files for
+     * @param entry the parent entry
+     * @return The child files
+     */
+    private FileEntry[] doListFiles(final File file, final FileEntry entry) {
+        final File[] files = listFiles(file);
+        final FileEntry[] children = files.length > 0 ? new FileEntry[files.length] : FileEntry.EMPTY_FILE_ENTRY_ARRAY;
+        for (int i = 0; i < files.length; i++) {
+            children[i] = createFileEntry(entry, files[i]);
+        }
+        return children;
+    }
+
+    /**
      * Fires directory/file change events to the registered listeners.
      *
      * @param entry The previous file system entry
@@ -415,18 +378,42 @@ public class FileAlterationObserver implements Serializable {
     }
 
     /**
-     * Fires directory/file delete events to the registered listeners.
+     * Returns the directory being observed.
      *
-     * @param entry The file entry
+     * @return the directory being observed
      */
-    private void doDelete(final FileEntry entry) {
-        for (final FileAlterationListener listener : listeners) {
-            if (entry.isDirectory()) {
-                listener.onDirectoryDelete(entry.getFile());
-            } else {
-                listener.onFileDelete(entry.getFile());
-            }
-        }
+    public File getDirectory() {
+        return rootEntry.getFile();
+    }
+
+    /**
+     * Returns the fileFilter.
+     *
+     * @return the fileFilter
+     * @since 2.1
+     */
+    public FileFilter getFileFilter() {
+        return fileFilter;
+    }
+
+    /**
+     * Returns the set of registered file system listeners.
+     *
+     * @return The file system listeners
+     */
+    public Iterable<FileAlterationListener> getListeners() {
+        return listeners;
+    }
+
+    /**
+     * Initializes the observer.
+     *
+     * @throws Exception if an error occurs
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    public void initialize() throws Exception {
+        rootEntry.refresh(rootEntry.getFile());
+        rootEntry.setChildren(doListFiles(rootEntry.getFile(), rootEntry));
     }
 
     /**
@@ -451,6 +438,19 @@ public class FileAlterationObserver implements Serializable {
     }
 
     /**
+     * Removes a file system listener.
+     *
+     * @param listener The file system listener
+     */
+    public void removeListener(final FileAlterationListener listener) {
+        if (listener != null) {
+            while (listeners.remove(listener)) {
+                // empty
+            }
+        }
+    }
+
+    /**
      * Returns a String representation of this observer.
      *
      * @return a String representation of this observer
diff --git a/src/main/java/org/apache/commons/io/monitor/FileEntry.java b/src/main/java/org/apache/commons/io/monitor/FileEntry.java
index b38f6a2..e58e76e 100644
--- a/src/main/java/org/apache/commons/io/monitor/FileEntry.java
+++ b/src/main/java/org/apache/commons/io/monitor/FileEntry.java
@@ -86,216 +86,216 @@ public class FileEntry implements Serializable {
     }
 
     /**
-     * Refreshes the attributes from the {@link File}, indicating
-     * whether the file has changed.
-     * <p>
-     * This implementation refreshes the {@code name}, {@code exists},
-     * {@code directory}, {@code lastModified} and {@code length}
-     * properties.
-     * </p>
-     * <p>
-     * The {@code exists}, {@code directory}, {@code lastModified}
-     * and {@code length} properties are compared for changes
-     * </p>
+     * Gets the directory's files.
      *
-     * @param file the file instance to compare to
-     * @return {@code true} if the file has changed, otherwise {@code false}
+     * @return This directory's files or an empty
+     * array if the file is not a directory or the
+     * directory is empty
      */
-    public boolean refresh(final File file) {
-        // cache original values
-        final boolean origExists = exists;
-        final FileTime origLastModified = lastModified;
-        final boolean origDirectory = directory;
-        final long origLength = length;
-
-        // refresh the values
-        name = file.getName();
-        exists = Files.exists(file.toPath());
-        directory = exists && file.isDirectory();
-        try {
-            lastModified = exists ? FileUtils.lastModifiedFileTime(file) : FileTimes.EPOCH;
-        } catch (final IOException e) {
-            lastModified = FileTimes.EPOCH;
-        }
-        length = exists && !directory ? file.length() : 0;
-
-        // Return if there are changes
-        return exists != origExists || !lastModified.equals(origLastModified) || directory != origDirectory
-            || length != origLength;
+    public FileEntry[] getChildren() {
+        return children != null ? children : EMPTY_FILE_ENTRY_ARRAY;
     }
 
     /**
-     * Creates a new child instance.
-     * <p>
-     * Custom implementations should override this method to return
-     * a new instance of the appropriate type.
-     * </p>
+     * Gets the file being monitored.
      *
-     * @param file The child file
-     * @return a new child instance
+     * @return the file being monitored
      */
-    public FileEntry newChildInstance(final File file) {
-        return new FileEntry(this, file);
+    public File getFile() {
+        return file;
     }
 
     /**
-     * Gets the parent entry.
+     * Gets the last modified time from the last time it
+     * was checked.
      *
-     * @return the parent entry
+     * @return the last modified time in milliseconds.
      */
-    public FileEntry getParent() {
-        return parent;
+    public long getLastModified() {
+        return lastModified.toMillis();
     }
 
     /**
-     * Gets the level
+     * Gets the last modified time from the last time it was checked.
      *
-     * @return the level
+     * @return the last modified time.
+     * @since 2.12.0
      */
-    public int getLevel() {
-        return parent == null ? 0 : parent.getLevel() + 1;
+    public FileTime getLastModifiedFileTime() {
+        return lastModified;
     }
 
     /**
-     * Gets the directory's files.
+     * Gets the length.
      *
-     * @return This directory's files or an empty
-     * array if the file is not a directory or the
-     * directory is empty
+     * @return the length
      */
-    public FileEntry[] getChildren() {
-        return children != null ? children : EMPTY_FILE_ENTRY_ARRAY;
+    public long getLength() {
+        return length;
     }
 
     /**
-     * Sets the directory's files.
+     * Gets the level
      *
-     * @param children This directory's files, may be null
+     * @return the level
      */
-    public void setChildren(final FileEntry... children) {
-        this.children = children;
+    public int getLevel() {
+        return parent == null ? 0 : parent.getLevel() + 1;
     }
 
     /**
-     * Gets the file being monitored.
+     * Gets the file name.
      *
-     * @return the file being monitored
+     * @return the file name
      */
-    public File getFile() {
-        return file;
+    public String getName() {
+        return name;
     }
 
     /**
-     * Gets the file name.
+     * Gets the parent entry.
      *
-     * @return the file name
+     * @return the parent entry
      */
-    public String getName() {
-        return name;
+    public FileEntry getParent() {
+        return parent;
     }
 
     /**
-     * Sets the file name.
+     * Tests whether the file is a directory or not.
      *
-     * @param name the file name
+     * @return whether the file is a directory or not
      */
-    public void setName(final String name) {
-        this.name = name;
+    public boolean isDirectory() {
+        return directory;
     }
 
     /**
-     * Gets the last modified time from the last time it
+     * Tests whether the file existed the last time it
      * was checked.
      *
-     * @return the last modified time in milliseconds.
+     * @return whether the file existed
      */
-    public long getLastModified() {
-        return lastModified.toMillis();
+    public boolean isExists() {
+        return exists;
     }
 
     /**
-     * Gets the last modified time from the last time it was checked.
+     * Creates a new child instance.
+     * <p>
+     * Custom implementations should override this method to return
+     * a new instance of the appropriate type.
+     * </p>
      *
-     * @return the last modified time.
-     * @since 2.12.0
+     * @param file The child file
+     * @return a new child instance
      */
-    public FileTime getLastModifiedFileTime() {
-        return lastModified;
+    public FileEntry newChildInstance(final File file) {
+        return new FileEntry(this, file);
     }
 
     /**
-     * Sets the last modified time from the last time it
-     * was checked.
+     * Refreshes the attributes from the {@link File}, indicating
+     * whether the file has changed.
+     * <p>
+     * This implementation refreshes the {@code name}, {@code exists},
+     * {@code directory}, {@code lastModified} and {@code length}
+     * properties.
+     * </p>
+     * <p>
+     * The {@code exists}, {@code directory}, {@code lastModified}
+     * and {@code length} properties are compared for changes
+     * </p>
      *
-     * @param lastModified The last modified time in milliseconds.
+     * @param file the file instance to compare to
+     * @return {@code true} if the file has changed, otherwise {@code false}
      */
-    public void setLastModified(final long lastModified) {
-        this.lastModified = FileTime.fromMillis(lastModified);
+    public boolean refresh(final File file) {
+        // cache original values
+        final boolean origExists = exists;
+        final FileTime origLastModified = lastModified;
+        final boolean origDirectory = directory;
+        final long origLength = length;
+
+        // refresh the values
+        name = file.getName();
+        exists = Files.exists(file.toPath());
+        directory = exists && file.isDirectory();
+        try {
+            lastModified = exists ? FileUtils.lastModifiedFileTime(file) : FileTimes.EPOCH;
+        } catch (final IOException e) {
+            lastModified = FileTimes.EPOCH;
+        }
+        length = exists && !directory ? file.length() : 0;
+
+        // Return if there are changes
+        return exists != origExists || !lastModified.equals(origLastModified) || directory != origDirectory
+            || length != origLength;
     }
 
     /**
-     * Sets the last modified time from the last time it was checked.
+     * Sets the directory's files.
      *
-     * @param lastModified The last modified time.
-     * @since 2.12.0
+     * @param children This directory's files, may be null
      */
-    public void setLastModified(final FileTime lastModified) {
-        this.lastModified = lastModified;
+    public void setChildren(final FileEntry... children) {
+        this.children = children;
     }
 
     /**
-     * Gets the length.
+     * Sets whether the file is a directory or not.
      *
-     * @return the length
+     * @param directory whether the file is a directory or not
      */
-    public long getLength() {
-        return length;
+    public void setDirectory(final boolean directory) {
+        this.directory = directory;
     }
 
     /**
-     * Sets the length.
+     * Sets whether the file existed the last time it
+     * was checked.
      *
-     * @param length the length
+     * @param exists whether the file exists or not
      */
-    public void setLength(final long length) {
-        this.length = length;
+    public void setExists(final boolean exists) {
+        this.exists = exists;
     }
 
     /**
-     * Tests whether the file existed the last time it
-     * was checked.
+     * Sets the last modified time from the last time it was checked.
      *
-     * @return whether the file existed
+     * @param lastModified The last modified time.
+     * @since 2.12.0
      */
-    public boolean isExists() {
-        return exists;
+    public void setLastModified(final FileTime lastModified) {
+        this.lastModified = lastModified;
     }
 
     /**
-     * Sets whether the file existed the last time it
+     * Sets the last modified time from the last time it
      * was checked.
      *
-     * @param exists whether the file exists or not
+     * @param lastModified The last modified time in milliseconds.
      */
-    public void setExists(final boolean exists) {
-        this.exists = exists;
+    public void setLastModified(final long lastModified) {
+        this.lastModified = FileTime.fromMillis(lastModified);
     }
 
     /**
-     * Tests whether the file is a directory or not.
+     * Sets the length.
      *
-     * @return whether the file is a directory or not
+     * @param length the length
      */
-    public boolean isDirectory() {
-        return directory;
+    public void setLength(final long length) {
+        this.length = length;
     }
 
     /**
-     * Sets whether the file is a directory or not.
+     * Sets the file name.
      *
-     * @param directory whether the file is a directory or not
+     * @param name the file name
      */
-    public void setDirectory(final boolean directory) {
-        this.directory = directory;
+    public void setName(final String name) {
+        this.name = name;
     }
 }
diff --git a/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java
index 733e025..c47cfa2 100644
--- a/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java
@@ -59,8 +59,27 @@ import org.apache.commons.io.input.ClosedInputStream;
  */
 public abstract class AbstractByteArrayOutputStream extends OutputStream {
 
-    static final int DEFAULT_SIZE = 1024;
+    /**
+     * Constructor for an InputStream subclass.
+     *
+     * @param <T> the type of the InputStream.
+     */
+    @FunctionalInterface
+    protected interface InputStreamConstructor<T extends InputStream> {
+
+        /**
+         * Constructs an InputStream subclass.
+         *
+         * @param buf the buffer
+         * @param offset the offset into the buffer
+         * @param length the length of the buffer
+         *
+         * @return the InputStream subclass.
+         */
+        T construct(final byte[] buf, final int offset, final int length);
+    }
 
+    static final int DEFAULT_SIZE = 1024;
     /** The list of buffers, which grows and never reduces. */
     private final List<byte[]> buffers = new ArrayList<>();
     /** The index of the current buffer. */
@@ -71,10 +90,24 @@ public abstract class AbstractByteArrayOutputStream extends OutputStream {
     private byte[] currentBuffer;
     /** The total count of bytes written. */
     protected int count;
+
     /** Flag to indicate if the buffers can be reused after reset */
     private boolean reuseBuffers = true;
 
     /**
+     * Closing a {@code ByteArrayOutputStream} has no effect. The methods in
+     * this class can be called after the stream has been closed without
+     * generating an {@code IOException}.
+     *
+     * @throws IOException never (this method should not declare this exception
+     * but it has to now due to backwards compatibility)
+     */
+    @Override
+    public void close() throws IOException {
+        //nop
+    }
+
+    /**
      * Makes a new buffer available either by allocating
      * a new one or re-cycling an existing one.
      *
@@ -105,120 +138,6 @@ public abstract class AbstractByteArrayOutputStream extends OutputStream {
     }
 
     /**
-     * Writes the bytes to the byte array.
-     * @param b the bytes to write
-     * @param off The start offset
-     * @param len The number of bytes to write
-     */
-    @Override
-    public abstract void write(final byte[] b, final int off, final int len);
-
-    /**
-     * Writes the bytes to the byte array.
-     * @param b the bytes to write
-     * @param off The start offset
-     * @param len The number of bytes to write
-     */
-    protected void writeImpl(final byte[] b, final int off, final int len) {
-        final int newcount = count + len;
-        int remaining = len;
-        int inBufferPos = count - filledBufferSum;
-        while (remaining > 0) {
-            final int part = Math.min(remaining, currentBuffer.length - inBufferPos);
-            System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
-            remaining -= part;
-            if (remaining > 0) {
-                needNewBuffer(newcount);
-                inBufferPos = 0;
-            }
-        }
-        count = newcount;
-    }
-
-    /**
-     * Write a byte to byte array.
-     * @param b the byte to write
-     */
-    @Override
-    public abstract void write(final int b);
-
-    /**
-     * Write a byte to byte array.
-     * @param b the byte to write
-     */
-    protected void writeImpl(final int b) {
-        int inBufferPos = count - filledBufferSum;
-        if (inBufferPos == currentBuffer.length) {
-            needNewBuffer(count + 1);
-            inBufferPos = 0;
-        }
-        currentBuffer[inBufferPos] = (byte) b;
-        count++;
-    }
-
-
-    /**
-     * Writes the entire contents of the specified input stream to this
-     * byte stream. Bytes from the input stream are read directly into the
-     * internal buffers of this streams.
-     *
-     * @param in the input stream to read from
-     * @return total number of bytes read from the input stream
-     *         (and written to this stream)
-     * @throws IOException if an I/O error occurs while reading the input stream
-     * @since 1.4
-     */
-    public abstract int write(final InputStream in) throws IOException;
-
-    /**
-     * Writes the entire contents of the specified input stream to this
-     * byte stream. Bytes from the input stream are read directly into the
-     * internal buffers of this streams.
-     *
-     * @param in the input stream to read from
-     * @return total number of bytes read from the input stream
-     *         (and written to this stream)
-     * @throws IOException if an I/O error occurs while reading the input stream
-     * @since 2.7
-     */
-    protected int writeImpl(final InputStream in) throws IOException {
-        int readCount = 0;
-        int inBufferPos = count - filledBufferSum;
-        int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
-        while (n != EOF) {
-            readCount += n;
-            inBufferPos += n;
-            count += n;
-            if (inBufferPos == currentBuffer.length) {
-                needNewBuffer(currentBuffer.length);
-                inBufferPos = 0;
-            }
-            n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
-        }
-        return readCount;
-    }
-
-    /**
-     * Returns the current size of the byte array.
-     *
-     * @return the current size of the byte array
-     */
-    public abstract int size();
-
-    /**
-     * Closing a {@code ByteArrayOutputStream} has no effect. The methods in
-     * this class can be called after the stream has been closed without
-     * generating an {@code IOException}.
-     *
-     * @throws IOException never (this method should not declare this exception
-     * but it has to now due to backwards compatibility)
-     */
-    @Override
-    public void close() throws IOException {
-        //nop
-    }
-
-    /**
      * @see java.io.ByteArrayOutputStream#reset()
      */
     public abstract void reset();
@@ -242,34 +161,47 @@ public abstract class AbstractByteArrayOutputStream extends OutputStream {
         }
     }
 
+
     /**
-     * Writes the entire contents of this byte stream to the
-     * specified output stream.
+     * Returns the current size of the byte array.
      *
-     * @param out  the output stream to write to
-     * @throws IOException if an I/O error occurs, such as if the stream is closed
-     * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
+     * @return the current size of the byte array
      */
-    public abstract void writeTo(final OutputStream out) throws IOException;
+    public abstract int size();
 
     /**
-     * Writes the entire contents of this byte stream to the
-     * specified output stream.
+     * Gets the current contents of this byte stream as a byte array.
+     * The result is independent of this stream.
      *
-     * @param out  the output stream to write to
-     * @throws IOException if an I/O error occurs, such as if the stream is closed
-     * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
+     * @return the current contents of this output stream, as a byte array
+     * @see java.io.ByteArrayOutputStream#toByteArray()
      */
-    protected void writeToImpl(final OutputStream out) throws IOException {
+    public abstract byte[] toByteArray();
+
+    /**
+     * Gets the current contents of this byte stream as a byte array.
+     * The result is independent of this stream.
+     *
+     * @return the current contents of this output stream, as a byte array
+     * @see java.io.ByteArrayOutputStream#toByteArray()
+     */
+    protected byte[] toByteArrayImpl() {
         int remaining = count;
+        if (remaining == 0) {
+            return IOUtils.EMPTY_BYTE_ARRAY;
+        }
+        final byte[] newbuf = IOUtils.byteArray(remaining);
+        int pos = 0;
         for (final byte[] buf : buffers) {
             final int c = Math.min(buf.length, remaining);
-            out.write(buf, 0, c);
+            System.arraycopy(buf, 0, newbuf, pos, c);
+            pos += c;
             remaining -= c;
             if (remaining == 0) {
                 break;
             }
         }
+        return newbuf;
     }
 
     /**
@@ -320,61 +252,6 @@ public abstract class AbstractByteArrayOutputStream extends OutputStream {
     }
 
     /**
-     * Constructor for an InputStream subclass.
-     *
-     * @param <T> the type of the InputStream.
-     */
-    @FunctionalInterface
-    protected interface InputStreamConstructor<T extends InputStream> {
-
-        /**
-         * Constructs an InputStream subclass.
-         *
-         * @param buf the buffer
-         * @param offset the offset into the buffer
-         * @param length the length of the buffer
-         *
-         * @return the InputStream subclass.
-         */
-        T construct(final byte[] buf, final int offset, final int length);
-    }
-
-    /**
-     * Gets the current contents of this byte stream as a byte array.
-     * The result is independent of this stream.
-     *
-     * @return the current contents of this output stream, as a byte array
-     * @see java.io.ByteArrayOutputStream#toByteArray()
-     */
-    public abstract byte[] toByteArray();
-
-    /**
-     * Gets the current contents of this byte stream as a byte array.
-     * The result is independent of this stream.
-     *
-     * @return the current contents of this output stream, as a byte array
-     * @see java.io.ByteArrayOutputStream#toByteArray()
-     */
-    protected byte[] toByteArrayImpl() {
-        int remaining = count;
-        if (remaining == 0) {
-            return IOUtils.EMPTY_BYTE_ARRAY;
-        }
-        final byte[] newbuf = IOUtils.byteArray(remaining);
-        int pos = 0;
-        for (final byte[] buf : buffers) {
-            final int c = Math.min(buf.length, remaining);
-            System.arraycopy(buf, 0, newbuf, pos, c);
-            pos += c;
-            remaining -= c;
-            if (remaining == 0) {
-                break;
-            }
-        }
-        return newbuf;
-    }
-
-    /**
      * Gets the current contents of this byte stream as a string
      * using the platform default charset.
      * @return the contents of the byte array as a String
@@ -392,26 +269,149 @@ public abstract class AbstractByteArrayOutputStream extends OutputStream {
      * Gets the current contents of this byte stream as a string
      * using the specified encoding.
      *
-     * @param enc  the name of the character encoding
+     * @param charset  the character encoding
      * @return the string converted from the byte array
-     * @throws UnsupportedEncodingException if the encoding is not supported
      * @see java.io.ByteArrayOutputStream#toString(String)
+     * @since 2.5
      */
-    public String toString(final String enc) throws UnsupportedEncodingException {
-        return new String(toByteArray(), enc);
+    public String toString(final Charset charset) {
+        return new String(toByteArray(), charset);
     }
 
     /**
      * Gets the current contents of this byte stream as a string
      * using the specified encoding.
      *
-     * @param charset  the character encoding
+     * @param enc  the name of the character encoding
      * @return the string converted from the byte array
+     * @throws UnsupportedEncodingException if the encoding is not supported
      * @see java.io.ByteArrayOutputStream#toString(String)
-     * @since 2.5
      */
-    public String toString(final Charset charset) {
-        return new String(toByteArray(), charset);
+    public String toString(final String enc) throws UnsupportedEncodingException {
+        return new String(toByteArray(), enc);
+    }
+
+    /**
+     * Writes the bytes to the byte array.
+     * @param b the bytes to write
+     * @param off The start offset
+     * @param len The number of bytes to write
+     */
+    @Override
+    public abstract void write(final byte[] b, final int off, final int len);
+
+    /**
+     * Writes the entire contents of the specified input stream to this
+     * byte stream. Bytes from the input stream are read directly into the
+     * internal buffers of this streams.
+     *
+     * @param in the input stream to read from
+     * @return total number of bytes read from the input stream
+     *         (and written to this stream)
+     * @throws IOException if an I/O error occurs while reading the input stream
+     * @since 1.4
+     */
+    public abstract int write(final InputStream in) throws IOException;
+
+    /**
+     * Write a byte to byte array.
+     * @param b the byte to write
+     */
+    @Override
+    public abstract void write(final int b);
+
+    /**
+     * Writes the bytes to the byte array.
+     * @param b the bytes to write
+     * @param off The start offset
+     * @param len The number of bytes to write
+     */
+    protected void writeImpl(final byte[] b, final int off, final int len) {
+        final int newcount = count + len;
+        int remaining = len;
+        int inBufferPos = count - filledBufferSum;
+        while (remaining > 0) {
+            final int part = Math.min(remaining, currentBuffer.length - inBufferPos);
+            System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
+            remaining -= part;
+            if (remaining > 0) {
+                needNewBuffer(newcount);
+                inBufferPos = 0;
+            }
+        }
+        count = newcount;
+    }
+
+    /**
+     * Writes the entire contents of the specified input stream to this
+     * byte stream. Bytes from the input stream are read directly into the
+     * internal buffers of this streams.
+     *
+     * @param in the input stream to read from
+     * @return total number of bytes read from the input stream
+     *         (and written to this stream)
+     * @throws IOException if an I/O error occurs while reading the input stream
+     * @since 2.7
+     */
+    protected int writeImpl(final InputStream in) throws IOException {
+        int readCount = 0;
+        int inBufferPos = count - filledBufferSum;
+        int n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
+        while (n != EOF) {
+            readCount += n;
+            inBufferPos += n;
+            count += n;
+            if (inBufferPos == currentBuffer.length) {
+                needNewBuffer(currentBuffer.length);
+                inBufferPos = 0;
+            }
+            n = in.read(currentBuffer, inBufferPos, currentBuffer.length - inBufferPos);
+        }
+        return readCount;
+    }
+
+    /**
+     * Write a byte to byte array.
+     * @param b the byte to write
+     */
+    protected void writeImpl(final int b) {
+        int inBufferPos = count - filledBufferSum;
+        if (inBufferPos == currentBuffer.length) {
+            needNewBuffer(count + 1);
+            inBufferPos = 0;
+        }
+        currentBuffer[inBufferPos] = (byte) b;
+        count++;
+    }
+
+    /**
+     * Writes the entire contents of this byte stream to the
+     * specified output stream.
+     *
+     * @param out  the output stream to write to
+     * @throws IOException if an I/O error occurs, such as if the stream is closed
+     * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
+     */
+    public abstract void writeTo(final OutputStream out) throws IOException;
+
+    /**
+     * Writes the entire contents of this byte stream to the
+     * specified output stream.
+     *
+     * @param out  the output stream to write to
+     * @throws IOException if an I/O error occurs, such as if the stream is closed
+     * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
+     */
+    protected void writeToImpl(final OutputStream out) throws IOException {
+        int remaining = count;
+        for (final byte[] buf : buffers) {
+            final int c = Math.min(buf.length, remaining);
+            out.write(buf, 0, c);
+            remaining -= c;
+            if (remaining == 0) {
+                break;
+            }
+        }
     }
 
 }
diff --git a/src/main/java/org/apache/commons/io/output/AppendableOutputStream.java b/src/main/java/org/apache/commons/io/output/AppendableOutputStream.java
index 50bb8e1..5d1852d 100644
--- a/src/main/java/org/apache/commons/io/output/AppendableOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/AppendableOutputStream.java
@@ -46,6 +46,15 @@ public class AppendableOutputStream <T extends Appendable> extends OutputStream
     }
 
     /**
+     * Return the target appendable.
+     *
+     * @return the target appendable
+     */
+    public T getAppendable() {
+        return appendable;
+    }
+
+    /**
      * Write a character to the underlying appendable.
      *
      * @param b the character to write
@@ -56,13 +65,4 @@ public class AppendableOutputStream <T extends Appendable> extends OutputStream
         appendable.append((char)b);
     }
 
-    /**
-     * Return the target appendable.
-     *
-     * @return the target appendable
-     */
-    public T getAppendable() {
-        return appendable;
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/output/BrokenOutputStream.java b/src/main/java/org/apache/commons/io/output/BrokenOutputStream.java
index 70befe2..4c4ec40 100644
--- a/src/main/java/org/apache/commons/io/output/BrokenOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/BrokenOutputStream.java
@@ -37,6 +37,13 @@ public class BrokenOutputStream extends OutputStream {
     private final IOException exception;
 
     /**
+     * Creates a new stream that always throws an {@link IOException}
+     */
+    public BrokenOutputStream() {
+        this(new IOException("Broken output stream"));
+    }
+
+    /**
      * Creates a new stream that always throws the given exception.
      *
      * @param exception the exception to be thrown
@@ -46,20 +53,12 @@ public class BrokenOutputStream extends OutputStream {
     }
 
     /**
-     * Creates a new stream that always throws an {@link IOException}
-     */
-    public BrokenOutputStream() {
-        this(new IOException("Broken output stream"));
-    }
-
-    /**
      * Throws the configured exception.
      *
-     * @param b ignored
      * @throws IOException always thrown
      */
     @Override
-    public void write(final int b) throws IOException {
+    public void close() throws IOException {
         throw exception;
     }
 
@@ -76,10 +75,11 @@ public class BrokenOutputStream extends OutputStream {
     /**
      * Throws the configured exception.
      *
+     * @param b ignored
      * @throws IOException always thrown
      */
     @Override
-    public void close() throws IOException {
+    public void write(final int b) throws IOException {
         throw exception;
     }
 
diff --git a/src/main/java/org/apache/commons/io/output/BrokenWriter.java b/src/main/java/org/apache/commons/io/output/BrokenWriter.java
index f2b659b..d49eff3 100644
--- a/src/main/java/org/apache/commons/io/output/BrokenWriter.java
+++ b/src/main/java/org/apache/commons/io/output/BrokenWriter.java
@@ -42,6 +42,13 @@ public class BrokenWriter extends Writer {
     private final IOException exception;
 
     /**
+     * Creates a new writer that always throws an {@link IOException}
+     */
+    public BrokenWriter() {
+        this(new IOException("Broken writer"));
+    }
+
+    /**
      * Creates a new writer that always throws the given exception.
      *
      * @param exception the exception to be thrown
@@ -51,22 +58,12 @@ public class BrokenWriter extends Writer {
     }
 
     /**
-     * Creates a new writer that always throws an {@link IOException}
-     */
-    public BrokenWriter() {
-        this(new IOException("Broken writer"));
-    }
-
-    /**
      * Throws the configured exception.
      *
-     * @param cbuf ignored
-     * @param off ignored
-     * @param len ignored
      * @throws IOException always thrown
      */
     @Override
-    public void write(final char[] cbuf, final int off, final int len) throws IOException {
+    public void close() throws IOException {
         throw exception;
     }
 
@@ -83,10 +80,13 @@ public class BrokenWriter extends Writer {
     /**
      * Throws the configured exception.
      *
+     * @param cbuf ignored
+     * @param off ignored
+     * @param len ignored
      * @throws IOException always thrown
      */
     @Override
-    public void close() throws IOException {
+    public void write(final char[] cbuf, final int off, final int len) throws IOException {
         throw exception;
     }
 
diff --git a/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
index 7032004..f5ecca5 100644
--- a/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ByteArrayOutputStream.java
@@ -27,76 +27,6 @@ import java.io.OutputStream;
 public class ByteArrayOutputStream extends AbstractByteArrayOutputStream {
 
     /**
-     * Creates a new byte array output stream. The buffer capacity is
-     * initially {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
-     */
-    public ByteArrayOutputStream() {
-        this(DEFAULT_SIZE);
-    }
-
-    /**
-     * Creates a new byte array output stream, with a buffer capacity of
-     * the specified size, in bytes.
-     *
-     * @param size  the initial size
-     * @throws IllegalArgumentException if size is negative
-     */
-    public ByteArrayOutputStream(final int size) {
-        if (size < 0) {
-            throw new IllegalArgumentException(
-                "Negative initial size: " + size);
-        }
-        synchronized (this) {
-            needNewBuffer(size);
-        }
-    }
-
-    @Override
-    public void write(final byte[] b, final int off, final int len) {
-        if ((off < 0)
-                || (off > b.length)
-                || (len < 0)
-                || ((off + len) > b.length)
-                || ((off + len) < 0)) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (len == 0) {
-            return;
-        }
-        synchronized (this) {
-            writeImpl(b, off, len);
-        }
-    }
-
-    @Override
-    public synchronized void write(final int b) {
-        writeImpl(b);
-    }
-
-    @Override
-    public synchronized int write(final InputStream in) throws IOException {
-        return writeImpl(in);
-    }
-
-    @Override
-    public synchronized int size() {
-        return count;
-    }
-
-    /**
-     * @see java.io.ByteArrayOutputStream#reset()
-     */
-    @Override
-    public synchronized void reset() {
-        resetImpl();
-    }
-
-    @Override
-    public synchronized void writeTo(final OutputStream out) throws IOException {
-        writeToImpl(out);
-    }
-
-    /**
      * Fetches entire contents of an {@code InputStream} and represent
      * same data as result InputStream.
      * <p>
@@ -154,13 +84,83 @@ public class ByteArrayOutputStream extends AbstractByteArrayOutputStream {
         }
     }
 
+    /**
+     * Creates a new byte array output stream. The buffer capacity is
+     * initially {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
+     */
+    public ByteArrayOutputStream() {
+        this(DEFAULT_SIZE);
+    }
+
+    /**
+     * Creates a new byte array output stream, with a buffer capacity of
+     * the specified size, in bytes.
+     *
+     * @param size  the initial size
+     * @throws IllegalArgumentException if size is negative
+     */
+    public ByteArrayOutputStream(final int size) {
+        if (size < 0) {
+            throw new IllegalArgumentException(
+                "Negative initial size: " + size);
+        }
+        synchronized (this) {
+            needNewBuffer(size);
+        }
+    }
+
+    /**
+     * @see java.io.ByteArrayOutputStream#reset()
+     */
     @Override
-    public synchronized InputStream toInputStream() {
-        return toInputStream(java.io.ByteArrayInputStream::new);
+    public synchronized void reset() {
+        resetImpl();
+    }
+
+    @Override
+    public synchronized int size() {
+        return count;
     }
 
     @Override
     public synchronized byte[] toByteArray() {
         return toByteArrayImpl();
     }
+
+    @Override
+    public synchronized InputStream toInputStream() {
+        return toInputStream(java.io.ByteArrayInputStream::new);
+    }
+
+    @Override
+    public void write(final byte[] b, final int off, final int len) {
+        if ((off < 0)
+                || (off > b.length)
+                || (len < 0)
+                || ((off + len) > b.length)
+                || ((off + len) < 0)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        synchronized (this) {
+            writeImpl(b, off, len);
+        }
+    }
+
+    @Override
+    public synchronized int write(final InputStream in) throws IOException {
+        return writeImpl(in);
+    }
+
+    @Override
+    public synchronized void write(final int b) {
+        writeImpl(b);
+    }
+
+    @Override
+    public synchronized void writeTo(final OutputStream out) throws IOException {
+        writeToImpl(out);
+    }
 }
diff --git a/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java b/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
index 394b84e..e319454 100644
--- a/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ChunkedOutputStream.java
@@ -40,6 +40,15 @@ public class ChunkedOutputStream extends FilterOutputStream {
     private final int chunkSize;
 
     /**
+     * Creates a new stream that uses a chunk size of {@link #DEFAULT_CHUNK_SIZE}.
+     *
+     * @param stream the stream to wrap
+     */
+    public ChunkedOutputStream(final OutputStream stream) {
+        this(stream, DEFAULT_CHUNK_SIZE);
+    }
+
+    /**
      * Creates a new stream that uses the specified chunk size.
      *
      * @param stream the stream to wrap
@@ -55,15 +64,6 @@ public class ChunkedOutputStream extends FilterOutputStream {
     }
 
     /**
-     * Creates a new stream that uses a chunk size of {@link #DEFAULT_CHUNK_SIZE}.
-     *
-     * @param stream the stream to wrap
-     */
-    public ChunkedOutputStream(final OutputStream stream) {
-        this(stream, DEFAULT_CHUNK_SIZE);
-    }
-
-    /**
      * Writes the data buffer in chunks to the underlying stream
      *
      * @param data the data to write
diff --git a/src/main/java/org/apache/commons/io/output/ChunkedWriter.java b/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
index 7b9a16a..409c78e 100644
--- a/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
+++ b/src/main/java/org/apache/commons/io/output/ChunkedWriter.java
@@ -40,6 +40,14 @@ public class ChunkedWriter extends FilterWriter {
     private final int chunkSize;
 
     /**
+     * Creates a new writer that uses a chunk size of {@link #DEFAULT_CHUNK_SIZE}
+     * @param writer the writer to wrap
+     */
+    public ChunkedWriter(final Writer writer) {
+        this(writer, DEFAULT_CHUNK_SIZE);
+    }
+
+    /**
      * Creates a new writer that uses the specified chunk size.
      *
      * @param writer the writer to wrap
@@ -55,14 +63,6 @@ public class ChunkedWriter extends FilterWriter {
     }
 
     /**
-     * Creates a new writer that uses a chunk size of {@link #DEFAULT_CHUNK_SIZE}
-     * @param writer the writer to wrap
-     */
-    public ChunkedWriter(final Writer writer) {
-        this(writer, DEFAULT_CHUNK_SIZE);
-    }
-
-    /**
      * writes the data buffer in chunks to the underlying writer
      * @param data The data
      * @param srcOffset the offset
diff --git a/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java b/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java
index 051d145..8050e46 100644
--- a/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ClosedOutputStream.java
@@ -48,21 +48,21 @@ public class ClosedOutputStream extends OutputStream {
     /**
      * Throws an {@link IOException} to indicate that the stream is closed.
      *
-     * @param b ignored
      * @throws IOException always thrown
      */
     @Override
-    public void write(final int b) throws IOException {
-        throw new IOException("write(" + b + ") failed: stream is closed");
+    public void flush() throws IOException {
+        throw new IOException("flush() failed: stream is closed");
     }
 
     /**
      * Throws an {@link IOException} to indicate that the stream is closed.
      *
+     * @param b ignored
      * @throws IOException always thrown
      */
     @Override
-    public void flush() throws IOException {
-        throw new IOException("flush() failed: stream is closed");
+    public void write(final int b) throws IOException {
+        throw new IOException("write(" + b + ") failed: stream is closed");
     }
 }
diff --git a/src/main/java/org/apache/commons/io/output/CountingOutputStream.java b/src/main/java/org/apache/commons/io/output/CountingOutputStream.java
index 486b789..78506eb 100644
--- a/src/main/java/org/apache/commons/io/output/CountingOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/CountingOutputStream.java
@@ -56,33 +56,29 @@ public class CountingOutputStream extends ProxyOutputStream {
     /**
      * The number of bytes that have passed through this stream.
      * <p>
-     * NOTE: From v1.3 this method throws an ArithmeticException if the
-     * count is greater than can be expressed by an {@code int}.
-     * See {@link #getByteCount()} for a method using a {@code long}.
+     * NOTE: This method is an alternative for {@code getCount()}.
+     * It was added because that method returns an integer which will
+     * result in incorrect count for files over 2GB.
      *
      * @return the number of bytes accumulated
-     * @throws ArithmeticException if the byte count is too large
+     * @since 1.3
      */
-    public int getCount() {
-        final long result = getByteCount();
-        if (result > Integer.MAX_VALUE) {
-            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
-        }
-        return (int) result;
+    public synchronized long getByteCount() {
+        return this.count;
     }
 
     /**
-     * Set the byte count back to 0.
+     * The number of bytes that have passed through this stream.
      * <p>
      * NOTE: From v1.3 this method throws an ArithmeticException if the
      * count is greater than can be expressed by an {@code int}.
-     * See {@link #resetByteCount()} for a method using a {@code long}.
+     * See {@link #getByteCount()} for a method using a {@code long}.
      *
-     * @return the count previous to resetting
+     * @return the number of bytes accumulated
      * @throws ArithmeticException if the byte count is too large
      */
-    public int resetCount() {
-        final long result = resetByteCount();
+    public int getCount() {
+        final long result = getByteCount();
         if (result > Integer.MAX_VALUE) {
             throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
         }
@@ -90,33 +86,37 @@ public class CountingOutputStream extends ProxyOutputStream {
     }
 
     /**
-     * The number of bytes that have passed through this stream.
+     * Set the byte count back to 0.
      * <p>
-     * NOTE: This method is an alternative for {@code getCount()}.
+     * NOTE: This method is an alternative for {@code resetCount()}.
      * It was added because that method returns an integer which will
      * result in incorrect count for files over 2GB.
      *
-     * @return the number of bytes accumulated
+     * @return the count previous to resetting
      * @since 1.3
      */
-    public synchronized long getByteCount() {
-        return this.count;
+    public synchronized long resetByteCount() {
+        final long tmp = this.count;
+        this.count = 0;
+        return tmp;
     }
 
     /**
      * Set the byte count back to 0.
      * <p>
-     * NOTE: This method is an alternative for {@code resetCount()}.
-     * It was added because that method returns an integer which will
-     * result in incorrect count for files over 2GB.
+     * NOTE: From v1.3 this method throws an ArithmeticException if the
+     * count is greater than can be expressed by an {@code int}.
+     * See {@link #resetByteCount()} for a method using a {@code long}.
      *
      * @return the count previous to resetting
-     * @since 1.3
+     * @throws ArithmeticException if the byte count is too large
      */
-    public synchronized long resetByteCount() {
-        final long tmp = this.count;
-        this.count = 0;
-        return tmp;
+    public int resetCount() {
+        final long result = resetByteCount();
+        if (result > Integer.MAX_VALUE) {
+            throw new ArithmeticException("The byte count " + result + " is too large to be converted to an int");
+        }
+        return (int) result;
     }
 
 }
diff --git a/src/main/java/org/apache/commons/io/output/NullOutputStream.java b/src/main/java/org/apache/commons/io/output/NullOutputStream.java
index b9cac75..0fd2a53 100644
--- a/src/main/java/org/apache/commons/io/output/NullOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/NullOutputStream.java
@@ -28,17 +28,6 @@ import java.io.OutputStream;
 public class NullOutputStream extends OutputStream {
 
     /**
-     * Deprecated in favor of {@link #NULL_OUTPUT_STREAM}.
-     *
-     * TODO: Will be private in 3.0.
-     *
-     * @deprecated Use {@link #NULL_OUTPUT_STREAM}.
-     */
-    @Deprecated
-    public NullOutputStream() {
-    }
-
-    /**
      * The singleton instance.
      *
      * @since 2.12.0
@@ -54,35 +43,46 @@ public class NullOutputStream extends OutputStream {
     public static final NullOutputStream NULL_OUTPUT_STREAM = INSTANCE;
 
     /**
+     * Deprecated in favor of {@link #NULL_OUTPUT_STREAM}.
+     *
+     * TODO: Will be private in 3.0.
+     *
+     * @deprecated Use {@link #NULL_OUTPUT_STREAM}.
+     */
+    @Deprecated
+    public NullOutputStream() {
+    }
+
+    /**
      * Does nothing - output to {@code /dev/null}.
      *
      * @param b The bytes to write
-     * @param off The start offset
-     * @param len The number of bytes to write
+     * @throws IOException never
      */
     @Override
-    public void write(final byte[] b, final int off, final int len) {
+    public void write(final byte[] b) throws IOException {
         // To /dev/null
     }
 
     /**
      * Does nothing - output to {@code /dev/null}.
      *
-     * @param b The byte to write
+     * @param b The bytes to write
+     * @param off The start offset
+     * @param len The number of bytes to write
      */
     @Override
-    public void write(final int b) {
+    public void write(final byte[] b, final int off, final int len) {
         // To /dev/null
     }
 
     /**
      * Does nothing - output to {@code /dev/null}.
      *
-     * @param b The bytes to write
-     * @throws IOException never
+     * @param b The byte to write
      */
     @Override
-    public void write(final byte[] b) throws IOException {
+    public void write(final int b) {
         // To /dev/null
     }
 
diff --git a/src/main/java/org/apache/commons/io/output/NullWriter.java b/src/main/java/org/apache/commons/io/output/NullWriter.java
index ccfccc5..602763b 100644
--- a/src/main/java/org/apache/commons/io/output/NullWriter.java
+++ b/src/main/java/org/apache/commons/io/output/NullWriter.java
@@ -62,13 +62,11 @@ public class NullWriter extends Writer {
     /**
      * Does nothing - output to {@code /dev/null}.
      * @param csq The character sequence to write
-     * @param start The index of the first character to write
-     * @param end  The index of the first character to write (exclusive)
      * @return this writer
      * @since 2.0
      */
     @Override
-    public Writer append(final CharSequence csq, final int start, final int end) {
+    public Writer append(final CharSequence csq) {
         //to /dev/null
         return this;
     }
@@ -76,21 +74,26 @@ public class NullWriter extends Writer {
     /**
      * Does nothing - output to {@code /dev/null}.
      * @param csq The character sequence to write
+     * @param start The index of the first character to write
+     * @param end  The index of the first character to write (exclusive)
      * @return this writer
      * @since 2.0
      */
     @Override
-    public Writer append(final CharSequence csq) {
+    public Writer append(final CharSequence csq, final int start, final int end) {
         //to /dev/null
         return this;
     }
 
-    /**
-     * Does nothing - output to {@code /dev/null}.
-     * @param idx The character to write
-     */
+    /** @see java.io.Writer#close() */
     @Override
-    public void write(final int idx) {
+    public void close() {
+        //to /dev/null
+    }
+
+    /** @see java.io.Writer#flush() */
+    @Override
+    public void flush() {
         //to /dev/null
     }
 
@@ -116,6 +119,15 @@ public class NullWriter extends Writer {
 
     /**
      * Does nothing - output to {@code /dev/null}.
+     * @param idx The character to write
+     */
+    @Override
+    public void write(final int idx) {
+        //to /dev/null
+    }
+
+    /**
+     * Does nothing - output to {@code /dev/null}.
      * @param str The string to write
      */
     @Override
@@ -134,16 +146,4 @@ public class NullWriter extends Writer {
         //to /dev/null
     }
 
-    /** @see java.io.Writer#flush() */
-    @Override
-    public void flush() {
-        //to /dev/null
-    }
-
-    /** @see java.io.Writer#close() */
-    @Override
-    public void close() {
-        //to /dev/null
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/output/ProxyOutputStream.java b/src/main/java/org/apache/commons/io/output/ProxyOutputStream.java
index 1679114..cc60832 100644
--- a/src/main/java/org/apache/commons/io/output/ProxyOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/ProxyOutputStream.java
@@ -46,22 +46,78 @@ public class ProxyOutputStream extends FilterOutputStream {
     }
 
     /**
-     * Invokes the delegate's {@code write(int)} method.
-     * @param idx the byte to write
+     * Invoked by the write methods after the proxied call has returned
+     * successfully. The number of bytes written (1 for the
+     * {@link #write(int)} method, buffer length for {@link #write(byte[])},
+     * etc.) is given as an argument.
+     * <p>
+     * Subclasses can override this method to add common post-processing
+     * functionality without having to override all the write methods.
+     * The default implementation does nothing.
+     *
+     * @since 2.0
+     * @param n number of bytes written
+     * @throws IOException if the post-processing fails
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    protected void afterWrite(final int n) throws IOException {
+        // noop
+    }
+
+    /**
+     * Invoked by the write methods before the call is proxied. The number
+     * of bytes to be written (1 for the {@link #write(int)} method, buffer
+     * length for {@link #write(byte[])}, etc.) is given as an argument.
+     * <p>
+     * Subclasses can override this method to add common pre-processing
+     * functionality without having to override all the write methods.
+     * The default implementation does nothing.
+     *
+     * @since 2.0
+     * @param n number of bytes to be written
+     * @throws IOException if the pre-processing fails
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    protected void beforeWrite(final int n) throws IOException {
+        // noop
+    }
+
+    /**
+     * Invokes the delegate's {@code close()} method.
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public void write(final int idx) throws IOException {
+    public void close() throws IOException {
+        IOUtils.close(out, this::handleIOException);
+    }
+
+    /**
+     * Invokes the delegate's {@code flush()} method.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void flush() throws IOException {
         try {
-            beforeWrite(1);
-            out.write(idx);
-            afterWrite(1);
+            out.flush();
         } catch (final IOException e) {
             handleIOException(e);
         }
     }
 
     /**
+     * Handle any IOExceptions thrown.
+     * <p>
+     * This method provides a point to implement custom exception
+     * handling. The default behavior is to re-throw the exception.
+     * @param e The IOException thrown
+     * @throws IOException if an I/O error occurs.
+     * @since 2.0
+     */
+    protected void handleIOException(final IOException e) throws IOException {
+        throw e;
+    }
+
+    /**
      * Invokes the delegate's {@code write(byte[])} method.
      * @param bts the bytes to write
      * @throws IOException if an I/O error occurs.
@@ -97,75 +153,19 @@ public class ProxyOutputStream extends FilterOutputStream {
     }
 
     /**
-     * Invokes the delegate's {@code flush()} method.
+     * Invokes the delegate's {@code write(int)} method.
+     * @param idx the byte to write
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public void flush() throws IOException {
+    public void write(final int idx) throws IOException {
         try {
-            out.flush();
+            beforeWrite(1);
+            out.write(idx);
+            afterWrite(1);
         } catch (final IOException e) {
             handleIOException(e);
         }
     }
 
-    /**
-     * Invokes the delegate's {@code close()} method.
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        IOUtils.close(out, this::handleIOException);
-    }
-
-    /**
-     * Invoked by the write methods before the call is proxied. The number
-     * of bytes to be written (1 for the {@link #write(int)} method, buffer
-     * length for {@link #write(byte[])}, etc.) is given as an argument.
-     * <p>
-     * Subclasses can override this method to add common pre-processing
-     * functionality without having to override all the write methods.
-     * The default implementation does nothing.
-     *
-     * @since 2.0
-     * @param n number of bytes to be written
-     * @throws IOException if the pre-processing fails
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void beforeWrite(final int n) throws IOException {
-        // noop
-    }
-
-    /**
-     * Invoked by the write methods after the proxied call has returned
-     * successfully. The number of bytes written (1 for the
-     * {@link #write(int)} method, buffer length for {@link #write(byte[])},
-     * etc.) is given as an argument.
-     * <p>
-     * Subclasses can override this method to add common post-processing
-     * functionality without having to override all the write methods.
-     * The default implementation does nothing.
-     *
-     * @since 2.0
-     * @param n number of bytes written
-     * @throws IOException if the post-processing fails
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void afterWrite(final int n) throws IOException {
-        // noop
-    }
-
-    /**
-     * Handle any IOExceptions thrown.
-     * <p>
-     * This method provides a point to implement custom exception
-     * handling. The default behavior is to re-throw the exception.
-     * @param e The IOException thrown
-     * @throws IOException if an I/O error occurs.
-     * @since 2.0
-     */
-    protected void handleIOException(final IOException e) throws IOException {
-        throw e;
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/output/ProxyWriter.java b/src/main/java/org/apache/commons/io/output/ProxyWriter.java
index 1d50e97..8dd1549 100644
--- a/src/main/java/org/apache/commons/io/output/ProxyWriter.java
+++ b/src/main/java/org/apache/commons/io/output/ProxyWriter.java
@@ -41,6 +41,26 @@ public class ProxyWriter extends FilterWriter {
     }
 
     /**
+     * Invoked by the write methods after the proxied call has returned
+     * successfully. The number of chars written (1 for the
+     * {@link #write(int)} method, buffer length for {@link #write(char[])},
+     * etc.) is given as an argument.
+     * <p>
+     * Subclasses can override this method to add common post-processing
+     * functionality without having to override all the write methods.
+     * The default implementation does nothing.
+     * </p>
+     *
+     * @since 2.0
+     * @param n number of chars written
+     * @throws IOException if the post-processing fails
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    protected void afterWrite(final int n) throws IOException {
+        // noop
+    }
+
+    /**
      * Invokes the delegate's {@code append(char)} method.
      * @param c The character to write
      * @return this writer
@@ -60,20 +80,19 @@ public class ProxyWriter extends FilterWriter {
     }
 
     /**
-     * Invokes the delegate's {@code append(CharSequence, int, int)} method.
+     * Invokes the delegate's {@code append(CharSequence)} method.
      * @param csq The character sequence to write
-     * @param start The index of the first character to write
-     * @param end  The index of the first character to write (exclusive)
      * @return this writer
      * @throws IOException if an I/O error occurs.
      * @since 2.0
      */
     @Override
-    public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
+    public Writer append(final CharSequence csq) throws IOException {
         try {
-            beforeWrite(end - start);
-            out.append(csq, start, end);
-            afterWrite(end - start);
+            final int len = IOUtils.length(csq);
+            beforeWrite(len);
+            out.append(csq);
+            afterWrite(len);
         } catch (final IOException e) {
             handleIOException(e);
         }
@@ -81,19 +100,20 @@ public class ProxyWriter extends FilterWriter {
     }
 
     /**
-     * Invokes the delegate's {@code append(CharSequence)} method.
+     * Invokes the delegate's {@code append(CharSequence, int, int)} method.
      * @param csq The character sequence to write
+     * @param start The index of the first character to write
+     * @param end  The index of the first character to write (exclusive)
      * @return this writer
      * @throws IOException if an I/O error occurs.
      * @since 2.0
      */
     @Override
-    public Writer append(final CharSequence csq) throws IOException {
+    public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
         try {
-            final int len = IOUtils.length(csq);
-            beforeWrite(len);
-            out.append(csq);
-            afterWrite(len);
+            beforeWrite(end - start);
+            out.append(csq, start, end);
+            afterWrite(end - start);
         } catch (final IOException e) {
             handleIOException(e);
         }
@@ -101,22 +121,62 @@ public class ProxyWriter extends FilterWriter {
     }
 
     /**
-     * Invokes the delegate's {@code write(int)} method.
-     * @param c the character to write
+     * Invoked by the write methods before the call is proxied. The number
+     * of chars to be written (1 for the {@link #write(int)} method, buffer
+     * length for {@link #write(char[])}, etc.) is given as an argument.
+     * <p>
+     * Subclasses can override this method to add common pre-processing
+     * functionality without having to override all the write methods.
+     * The default implementation does nothing.
+     * </p>
+     *
+     * @since 2.0
+     * @param n number of chars to be written
+     * @throws IOException if the pre-processing fails
+     */
+    @SuppressWarnings("unused") // Possibly thrown from subclasses.
+    protected void beforeWrite(final int n) throws IOException {
+        // noop
+    }
+
+    /**
+     * Invokes the delegate's {@code close()} method.
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public void write(final int c) throws IOException {
+    public void close() throws IOException {
+        IOUtils.close(out, this::handleIOException);
+    }
+
+    /**
+     * Invokes the delegate's {@code flush()} method.
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void flush() throws IOException {
         try {
-            beforeWrite(1);
-            out.write(c);
-            afterWrite(1);
+            out.flush();
         } catch (final IOException e) {
             handleIOException(e);
         }
     }
 
     /**
+     * Handle any IOExceptions thrown.
+     * <p>
+     * This method provides a point to implement custom exception
+     * handling. The default behavior is to re-throw the exception.
+     * </p>
+     *
+     * @param e The IOException thrown
+     * @throws IOException if an I/O error occurs.
+     * @since 2.0
+     */
+    protected void handleIOException(final IOException e) throws IOException {
+        throw e;
+    }
+
+    /**
      * Invokes the delegate's {@code write(char[])} method.
      * @param cbuf the characters to write
      * @throws IOException if an I/O error occurs.
@@ -152,6 +212,22 @@ public class ProxyWriter extends FilterWriter {
     }
 
     /**
+     * Invokes the delegate's {@code write(int)} method.
+     * @param c the character to write
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void write(final int c) throws IOException {
+        try {
+            beforeWrite(1);
+            out.write(c);
+            afterWrite(1);
+        } catch (final IOException e) {
+            handleIOException(e);
+        }
+    }
+
+    /**
      * Invokes the delegate's {@code write(String)} method.
      * @param str the string to write
      * @throws IOException if an I/O error occurs.
@@ -186,80 +262,4 @@ public class ProxyWriter extends FilterWriter {
         }
     }
 
-    /**
-     * Invokes the delegate's {@code flush()} method.
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void flush() throws IOException {
-        try {
-            out.flush();
-        } catch (final IOException e) {
-            handleIOException(e);
-        }
-    }
-
-    /**
-     * Invokes the delegate's {@code close()} method.
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        IOUtils.close(out, this::handleIOException);
-    }
-
-    /**
-     * Invoked by the write methods before the call is proxied. The number
-     * of chars to be written (1 for the {@link #write(int)} method, buffer
-     * length for {@link #write(char[])}, etc.) is given as an argument.
-     * <p>
-     * Subclasses can override this method to add common pre-processing
-     * functionality without having to override all the write methods.
-     * The default implementation does nothing.
-     * </p>
-     *
-     * @since 2.0
-     * @param n number of chars to be written
-     * @throws IOException if the pre-processing fails
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void beforeWrite(final int n) throws IOException {
-        // noop
-    }
-
-    /**
-     * Invoked by the write methods after the proxied call has returned
-     * successfully. The number of chars written (1 for the
-     * {@link #write(int)} method, buffer length for {@link #write(char[])},
-     * etc.) is given as an argument.
-     * <p>
-     * Subclasses can override this method to add common post-processing
-     * functionality without having to override all the write methods.
-     * The default implementation does nothing.
-     * </p>
-     *
-     * @since 2.0
-     * @param n number of chars written
-     * @throws IOException if the post-processing fails
-     */
-    @SuppressWarnings("unused") // Possibly thrown from subclasses.
-    protected void afterWrite(final int n) throws IOException {
-        // noop
-    }
-
-    /**
-     * Handle any IOExceptions thrown.
-     * <p>
-     * This method provides a point to implement custom exception
-     * handling. The default behavior is to re-throw the exception.
-     * </p>
-     *
-     * @param e The IOException thrown
-     * @throws IOException if an I/O error occurs.
-     * @since 2.0
-     */
-    protected void handleIOException(final IOException e) throws IOException {
-        throw e;
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java b/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
index 261714e..bd9530d 100644
--- a/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
+++ b/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
@@ -119,15 +119,22 @@ public class StringBuilderWriter extends Writer implements Serializable {
 
 
     /**
-     * Writes a String to the {@link StringBuilder}.
+     * Returns the underlying builder.
      *
-     * @param value The value to write
+     * @return The underlying builder
+     */
+    public StringBuilder getBuilder() {
+        return builder;
+    }
+
+    /**
+     * Returns {@link StringBuilder#toString()}.
+     *
+     * @return The contents of the String builder.
      */
     @Override
-    public void write(final String value) {
-        if (value != null) {
-            builder.append(value);
-        }
+    public String toString() {
+        return builder.toString();
     }
 
     /**
@@ -145,21 +152,14 @@ public class StringBuilderWriter extends Writer implements Serializable {
     }
 
     /**
-     * Returns the underlying builder.
-     *
-     * @return The underlying builder
-     */
-    public StringBuilder getBuilder() {
-        return builder;
-    }
-
-    /**
-     * Returns {@link StringBuilder#toString()}.
+     * Writes a String to the {@link StringBuilder}.
      *
-     * @return The contents of the String builder.
+     * @param value The value to write
      */
     @Override
-    public String toString() {
-        return builder.toString();
+    public void write(final String value) {
+        if (value != null) {
+            builder.append(value);
+        }
     }
 }
diff --git a/src/main/java/org/apache/commons/io/output/TaggedOutputStream.java b/src/main/java/org/apache/commons/io/output/TaggedOutputStream.java
index 50f373f..eeb23a0 100644
--- a/src/main/java/org/apache/commons/io/output/TaggedOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/TaggedOutputStream.java
@@ -79,6 +79,17 @@ public class TaggedOutputStream extends ProxyOutputStream {
     }
 
     /**
+     * Tags any IOExceptions thrown, wrapping and re-throwing.
+     *
+     * @param e The IOException thrown
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    protected void handleIOException(final IOException e) throws IOException {
+        throw new TaggedIOException(e, tag);
+    }
+
+    /**
      * Tests if the given exception was caused by this stream.
      *
      * @param exception an exception
@@ -103,15 +114,4 @@ public class TaggedOutputStream extends ProxyOutputStream {
         TaggedIOException.throwCauseIfTaggedWith(exception, tag);
     }
 
-    /**
-     * Tags any IOExceptions thrown, wrapping and re-throwing.
-     *
-     * @param e The IOException thrown
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    protected void handleIOException(final IOException e) throws IOException {
-        throw new TaggedIOException(e, tag);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/output/TaggedWriter.java b/src/main/java/org/apache/commons/io/output/TaggedWriter.java
index 00f9f03..eb6f79d 100644
--- a/src/main/java/org/apache/commons/io/output/TaggedWriter.java
+++ b/src/main/java/org/apache/commons/io/output/TaggedWriter.java
@@ -79,6 +79,17 @@ public class TaggedWriter extends ProxyWriter {
     }
 
     /**
+     * Tags any IOExceptions thrown, wrapping and re-throwing.
+     *
+     * @param e The IOException thrown
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    protected void handleIOException(final IOException e) throws IOException {
+        throw new TaggedIOException(e, tag);
+    }
+
+    /**
      * Tests if the given exception was caused by this writer.
      *
      * @param exception an exception
@@ -103,15 +114,4 @@ public class TaggedWriter extends ProxyWriter {
         TaggedIOException.throwCauseIfTaggedWith(exception, tag);
     }
 
-    /**
-     * Tags any IOExceptions thrown, wrapping and re-throwing.
-     *
-     * @param e The IOException thrown
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    protected void handleIOException(final IOException e) throws IOException {
-        throw new TaggedIOException(e, tag);
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/output/TeeOutputStream.java b/src/main/java/org/apache/commons/io/output/TeeOutputStream.java
index dc70af8..b1456a4 100644
--- a/src/main/java/org/apache/commons/io/output/TeeOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/TeeOutputStream.java
@@ -44,6 +44,39 @@ public class TeeOutputStream extends ProxyOutputStream {
     }
 
     /**
+     * Closes both output streams.
+     * <p>
+     * If closing the main output stream throws an exception, attempt to close the branch output stream.
+     * </p>
+     *
+     * <p>
+     * If closing the main and branch output streams both throw exceptions, which exceptions is thrown by this method is
+     * currently unspecified and subject to change.
+     * </p>
+     *
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void close() throws IOException {
+        try {
+            super.close();
+        } finally {
+            this.branch.close();
+        }
+    }
+
+    /**
+     * Flushes both streams.
+     *
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void flush() throws IOException {
+        super.flush();
+        this.branch.flush();
+    }
+
+    /**
      * Writes the bytes to both streams.
      *
      * @param b the bytes to write
@@ -81,37 +114,4 @@ public class TeeOutputStream extends ProxyOutputStream {
         this.branch.write(b);
     }
 
-    /**
-     * Flushes both streams.
-     *
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void flush() throws IOException {
-        super.flush();
-        this.branch.flush();
-    }
-
-    /**
-     * Closes both output streams.
-     * <p>
-     * If closing the main output stream throws an exception, attempt to close the branch output stream.
-     * </p>
-     *
-     * <p>
-     * If closing the main and branch output streams both throw exceptions, which exceptions is thrown by this method is
-     * currently unspecified and subject to change.
-     * </p>
-     *
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void close() throws IOException {
-        try {
-            super.close();
-        } finally {
-            this.branch.close();
-        }
-    }
-
 }
diff --git a/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java b/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java
index 9a2eb1d..2cbe0b8 100644
--- a/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java
@@ -64,10 +64,6 @@ public class UncheckedFilterOutputStream extends FilterOutputStream {
         }
     }
 
-    private void uncheck(final IOException e) {
-        throw new UncheckedIOException(e);
-    }
-
     /**
      * Calls this method's super and rethrow {@link IOException} as {@link UncheckedIOException}.
      */
@@ -80,6 +76,10 @@ public class UncheckedFilterOutputStream extends FilterOutputStream {
         }
     }
 
+    private void uncheck(final IOException e) {
+        throw new UncheckedIOException(e);
+    }
+
     /**
      * Calls this method's super and rethrow {@link IOException} as {@link UncheckedIOException}.
      */
diff --git a/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
index 6c4fbec..d69ce85 100644
--- a/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
@@ -31,66 +31,6 @@ import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
 public final class UnsynchronizedByteArrayOutputStream extends AbstractByteArrayOutputStream {
 
     /**
-     * Creates a new byte array output stream. The buffer capacity is initially
-     * {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
-     */
-    public UnsynchronizedByteArrayOutputStream() {
-        this(DEFAULT_SIZE);
-    }
-
-    /**
-     * Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes.
-     *
-     * @param size the initial size
-     * @throws IllegalArgumentException if size is negative
-     */
-    public UnsynchronizedByteArrayOutputStream(final int size) {
-        if (size < 0) {
-            throw new IllegalArgumentException("Negative initial size: " + size);
-        }
-        needNewBuffer(size);
-    }
-
-    @Override
-    public void write(final byte[] b, final int off, final int len) {
-        if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
-            throw new IndexOutOfBoundsException(String.format("offset=%,d, length=%,d", off, len));
-        }
-        if (len == 0) {
-            return;
-        }
-        writeImpl(b, off, len);
-    }
-
-    @Override
-    public void write(final int b) {
-        writeImpl(b);
-    }
-
-    @Override
-    public int write(final InputStream in) throws IOException {
-        return writeImpl(in);
-    }
-
-    @Override
-    public int size() {
-        return count;
-    }
-
-    /**
-     * @see java.io.ByteArrayOutputStream#reset()
-     */
-    @Override
-    public void reset() {
-        resetImpl();
-    }
-
-    @Override
-    public void writeTo(final OutputStream out) throws IOException {
-        writeToImpl(out);
-    }
-
-    /**
      * Fetches entire contents of an {@code InputStream} and represent same data as result InputStream.
      * <p>
      * This method is useful where,
@@ -137,13 +77,73 @@ public final class UnsynchronizedByteArrayOutputStream extends AbstractByteArray
         }
     }
 
+    /**
+     * Creates a new byte array output stream. The buffer capacity is initially
+     * {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
+     */
+    public UnsynchronizedByteArrayOutputStream() {
+        this(DEFAULT_SIZE);
+    }
+
+    /**
+     * Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes.
+     *
+     * @param size the initial size
+     * @throws IllegalArgumentException if size is negative
+     */
+    public UnsynchronizedByteArrayOutputStream(final int size) {
+        if (size < 0) {
+            throw new IllegalArgumentException("Negative initial size: " + size);
+        }
+        needNewBuffer(size);
+    }
+
+    /**
+     * @see java.io.ByteArrayOutputStream#reset()
+     */
     @Override
-    public InputStream toInputStream() {
-        return toInputStream(UnsynchronizedByteArrayInputStream::new);
+    public void reset() {
+        resetImpl();
+    }
+
+    @Override
+    public int size() {
+        return count;
     }
 
     @Override
     public byte[] toByteArray() {
         return toByteArrayImpl();
     }
+
+    @Override
+    public InputStream toInputStream() {
+        return toInputStream(UnsynchronizedByteArrayInputStream::new);
+    }
+
+    @Override
+    public void write(final byte[] b, final int off, final int len) {
+        if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) {
+            throw new IndexOutOfBoundsException(String.format("offset=%,d, length=%,d", off, len));
+        }
+        if (len == 0) {
+            return;
+        }
+        writeImpl(b, off, len);
+    }
+
+    @Override
+    public int write(final InputStream in) throws IOException {
+        return writeImpl(in);
+    }
+
+    @Override
+    public void write(final int b) {
+        writeImpl(b);
+    }
+
+    @Override
+    public void writeTo(final OutputStream out) throws IOException {
+        writeToImpl(out);
+    }
 }
diff --git a/src/main/java/org/apache/commons/io/output/WriterOutputStream.java b/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
index 1a30612..ae6d1a5 100644
--- a/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
@@ -74,8 +74,43 @@ import java.nio.charset.CodingErrorAction;
 public class WriterOutputStream extends OutputStream {
     private static final int BUFFER_SIZE = 1024;
 
+    /**
+     * Check if the JDK in use properly supports the given charset.
+     *
+     * @param charset the charset to check the support for
+     */
+    private static void checkIbmJdkWithBrokenUTF16(final Charset charset){
+        if (!"UTF-16".equals(charset.name())) {
+            return;
+        }
+        final String TEST_STRING_2 = "v\u00e9s";
+        final byte[] bytes = TEST_STRING_2.getBytes(charset);
+
+        final CharsetDecoder charsetDecoder2 = charset.newDecoder();
+        final ByteBuffer bb2 = ByteBuffer.allocate(16);
+        final CharBuffer cb2 = CharBuffer.allocate(TEST_STRING_2.length());
+        final int len = bytes.length;
+        for (int i = 0; i < len; i++) {
+            bb2.put(bytes[i]);
+            bb2.flip();
+            try {
+                charsetDecoder2.decode(bb2, cb2, i == (len - 1));
+            } catch ( final IllegalArgumentException e){
+                throw new UnsupportedOperationException("UTF-16 requested when running on an IBM JDK with broken UTF-16 support. " +
+                        "Please find a JDK that supports UTF-16 if you intend to use UF-16 with WriterOutputStream");
+            }
+            bb2.compact();
+        }
+        cb2.rewind();
+        if (!TEST_STRING_2.equals(cb2.toString())){
+            throw new UnsupportedOperationException("UTF-16 requested when running on an IBM JDK with broken UTF-16 support. " +
+                    "Please find a JDK that supports UTF-16 if you intend to use UF-16 with WriterOutputStream");
+        }
+
+    }
     private final Writer writer;
     private final CharsetDecoder decoder;
+
     private final boolean writeImmediately;
 
     /**
@@ -93,38 +128,28 @@ public class WriterOutputStream extends OutputStream {
     private final CharBuffer decoderOut;
 
     /**
-     * Constructs a new {@link WriterOutputStream} with a default output buffer size of {@value #BUFFER_SIZE}
-     * characters. The output buffer will only be flushed when it overflows or when {@link #flush()} or {@link #close()}
-     * is called.
+     * Constructs a new {@link WriterOutputStream} that uses the default character encoding and with a default output
+     * buffer size of {@value #BUFFER_SIZE} characters. The output buffer will only be flushed when it overflows or when
+     * {@link #flush()} or {@link #close()} is called.
      *
      * @param writer the target {@link Writer}
-     * @param decoder the charset decoder
-     * @since 2.1
+     * @deprecated 2.5 use {@link #WriterOutputStream(Writer, Charset)} instead
      */
-    public WriterOutputStream(final Writer writer, final CharsetDecoder decoder) {
-        this(writer, decoder, BUFFER_SIZE, false);
+    @Deprecated
+    public WriterOutputStream(final Writer writer) {
+        this(writer, Charset.defaultCharset(), BUFFER_SIZE, false);
     }
 
     /**
-     * Constructs a new {@link WriterOutputStream}.
+     * Constructs a new {@link WriterOutputStream} with a default output buffer size of {@value #BUFFER_SIZE}
+     * characters. The output buffer will only be flushed when it overflows or when {@link #flush()} or {@link #close()}
+     * is called.
      *
      * @param writer the target {@link Writer}
-     * @param decoder the charset decoder
-     * @param bufferSize the size of the output buffer in number of characters
-     * @param writeImmediately If {@code true} the output buffer will be flushed after each
-     *                         write operation, i.e. all available data will be written to the
-     *                         underlying {@link Writer} immediately. If {@code false}, the
-     *                         output buffer will only be flushed when it overflows or when
-     *                         {@link #flush()} or {@link #close()} is called.
-     * @since 2.1
+     * @param charset the charset encoding
      */
-    public WriterOutputStream(final Writer writer, final CharsetDecoder decoder, final int bufferSize,
-                              final boolean writeImmediately) {
-        checkIbmJdkWithBrokenUTF16( decoder.charset());
-        this.writer = writer;
-        this.decoder = decoder;
-        this.writeImmediately = writeImmediately;
-        decoderOut = CharBuffer.allocate(bufferSize);
+    public WriterOutputStream(final Writer writer, final Charset charset) {
+        this(writer, charset, BUFFER_SIZE, false);
     }
 
     /**
@@ -156,27 +181,33 @@ public class WriterOutputStream extends OutputStream {
      * is called.
      *
      * @param writer the target {@link Writer}
-     * @param charset the charset encoding
+     * @param decoder the charset decoder
+     * @since 2.1
      */
-    public WriterOutputStream(final Writer writer, final Charset charset) {
-        this(writer, charset, BUFFER_SIZE, false);
+    public WriterOutputStream(final Writer writer, final CharsetDecoder decoder) {
+        this(writer, decoder, BUFFER_SIZE, false);
     }
 
     /**
      * Constructs a new {@link WriterOutputStream}.
      *
      * @param writer the target {@link Writer}
-     * @param charsetName the name of the charset encoding
+     * @param decoder the charset decoder
      * @param bufferSize the size of the output buffer in number of characters
      * @param writeImmediately If {@code true} the output buffer will be flushed after each
      *                         write operation, i.e. all available data will be written to the
      *                         underlying {@link Writer} immediately. If {@code false}, the
      *                         output buffer will only be flushed when it overflows or when
      *                         {@link #flush()} or {@link #close()} is called.
+     * @since 2.1
      */
-    public WriterOutputStream(final Writer writer, final String charsetName, final int bufferSize,
+    public WriterOutputStream(final Writer writer, final CharsetDecoder decoder, final int bufferSize,
                               final boolean writeImmediately) {
-        this(writer, Charset.forName(charsetName), bufferSize, writeImmediately);
+        checkIbmJdkWithBrokenUTF16( decoder.charset());
+        this.writer = writer;
+        this.decoder = decoder;
+        this.writeImmediately = writeImmediately;
+        decoderOut = CharBuffer.allocate(bufferSize);
     }
 
     /**
@@ -192,60 +223,33 @@ public class WriterOutputStream extends OutputStream {
     }
 
     /**
-     * Constructs a new {@link WriterOutputStream} that uses the default character encoding and with a default output
-     * buffer size of {@value #BUFFER_SIZE} characters. The output buffer will only be flushed when it overflows or when
-     * {@link #flush()} or {@link #close()} is called.
+     * Constructs a new {@link WriterOutputStream}.
      *
      * @param writer the target {@link Writer}
-     * @deprecated 2.5 use {@link #WriterOutputStream(Writer, Charset)} instead
-     */
-    @Deprecated
-    public WriterOutputStream(final Writer writer) {
-        this(writer, Charset.defaultCharset(), BUFFER_SIZE, false);
-    }
-
-    /**
-     * Write bytes from the specified byte array to the stream.
-     *
-     * @param b the byte array containing the bytes to write
-     * @param off the start offset in the byte array
-     * @param len the number of bytes to write
-     * @throws IOException if an I/O error occurs.
-     */
-    @Override
-    public void write(final byte[] b, int off, int len) throws IOException {
-        while (len > 0) {
-            final int c = Math.min(len, decoderIn.remaining());
-            decoderIn.put(b, off, c);
-            processInput(false);
-            len -= c;
-            off += c;
-        }
-        if (writeImmediately) {
-            flushOutput();
-        }
-    }
-
-    /**
-     * Write bytes from the specified byte array to the stream.
-     *
-     * @param b the byte array containing the bytes to write
-     * @throws IOException if an I/O error occurs.
+     * @param charsetName the name of the charset encoding
+     * @param bufferSize the size of the output buffer in number of characters
+     * @param writeImmediately If {@code true} the output buffer will be flushed after each
+     *                         write operation, i.e. all available data will be written to the
+     *                         underlying {@link Writer} immediately. If {@code false}, the
+     *                         output buffer will only be flushed when it overflows or when
+     *                         {@link #flush()} or {@link #close()} is called.
      */
-    @Override
-    public void write(final byte[] b) throws IOException {
-        write(b, 0, b.length);
+    public WriterOutputStream(final Writer writer, final String charsetName, final int bufferSize,
+                              final boolean writeImmediately) {
+        this(writer, Charset.forName(charsetName), bufferSize, writeImmediately);
     }
 
     /**
-     * Write a single byte to the stream.
-     *
-     * @param b the byte to write
+     * Close the stream. Any remaining content accumulated in the output buffer
+     * will be written to the underlying {@link Writer}. After that
+     * {@link Writer#close()} will be called.
      * @throws IOException if an I/O error occurs.
      */
     @Override
-    public void write(final int b) throws IOException {
-        write(new byte[] {(byte) b}, 0, 1);
+    public void close() throws IOException {
+        processInput(true);
+        flushOutput();
+        writer.close();
     }
 
     /**
@@ -261,16 +265,15 @@ public class WriterOutputStream extends OutputStream {
     }
 
     /**
-     * Close the stream. Any remaining content accumulated in the output buffer
-     * will be written to the underlying {@link Writer}. After that
-     * {@link Writer#close()} will be called.
+     * Flush the output.
+     *
      * @throws IOException if an I/O error occurs.
      */
-    @Override
-    public void close() throws IOException {
-        processInput(true);
-        flushOutput();
-        writer.close();
+    private void flushOutput() throws IOException {
+        if (decoderOut.position() > 0) {
+            writer.write(decoderOut.array(), 0, decoderOut.position());
+            decoderOut.rewind();
+        }
     }
 
     /**
@@ -300,49 +303,46 @@ public class WriterOutputStream extends OutputStream {
     }
 
     /**
-     * Flush the output.
+     * Write bytes from the specified byte array to the stream.
      *
+     * @param b the byte array containing the bytes to write
      * @throws IOException if an I/O error occurs.
      */
-    private void flushOutput() throws IOException {
-        if (decoderOut.position() > 0) {
-            writer.write(decoderOut.array(), 0, decoderOut.position());
-            decoderOut.rewind();
-        }
+    @Override
+    public void write(final byte[] b) throws IOException {
+        write(b, 0, b.length);
     }
 
     /**
-     * Check if the JDK in use properly supports the given charset.
+     * Write bytes from the specified byte array to the stream.
      *
-     * @param charset the charset to check the support for
+     * @param b the byte array containing the bytes to write
+     * @param off the start offset in the byte array
+     * @param len the number of bytes to write
+     * @throws IOException if an I/O error occurs.
      */
-    private static void checkIbmJdkWithBrokenUTF16(final Charset charset){
-        if (!"UTF-16".equals(charset.name())) {
-            return;
-        }
-        final String TEST_STRING_2 = "v\u00e9s";
-        final byte[] bytes = TEST_STRING_2.getBytes(charset);
-
-        final CharsetDecoder charsetDecoder2 = charset.newDecoder();
-        final ByteBuffer bb2 = ByteBuffer.allocate(16);
-        final CharBuffer cb2 = CharBuffer.allocate(TEST_STRING_2.length());
-        final int len = bytes.length;
-        for (int i = 0; i < len; i++) {
-            bb2.put(bytes[i]);
-            bb2.flip();
-            try {
-                charsetDecoder2.decode(bb2, cb2, i == (len - 1));
-            } catch ( final IllegalArgumentException e){
-                throw new UnsupportedOperationException("UTF-16 requested when running on an IBM JDK with broken UTF-16 support. " +
-                        "Please find a JDK that supports UTF-16 if you intend to use UF-16 with WriterOutputStream");
-            }
-            bb2.compact();
+    @Override
+    public void write(final byte[] b, int off, int len) throws IOException {
+        while (len > 0) {
+            final int c = Math.min(len, decoderIn.remaining());
+            decoderIn.put(b, off, c);
+            processInput(false);
+            len -= c;
+            off += c;
         }
-        cb2.rewind();
-        if (!TEST_STRING_2.equals(cb2.toString())){
-            throw new UnsupportedOperationException("UTF-16 requested when running on an IBM JDK with broken UTF-16 support. " +
-                    "Please find a JDK that supports UTF-16 if you intend to use UF-16 with WriterOutputStream");
+        if (writeImmediately) {
+            flushOutput();
         }
+    }
 
+    /**
+     * Write a single byte to the stream.
+     *
+     * @param b the byte to write
+     * @throws IOException if an I/O error occurs.
+     */
+    @Override
+    public void write(final int b) throws IOException {
+        write(new byte[] {(byte) b}, 0, 1);
     }
 }
diff --git a/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java b/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
index 881f1d7..0dcdd87 100644
--- a/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
+++ b/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
@@ -41,6 +41,8 @@ import org.apache.commons.io.input.XmlStreamReader;
 public class XmlStreamWriter extends Writer {
     private static final int BUFFER_SIZE = IOUtils.DEFAULT_BUFFER_SIZE;
 
+    static final Pattern ENCODING_PATTERN = XmlStreamReader.ENCODING_PATTERN;
+
     private final OutputStream out;
 
     private final String defaultEncoding;
@@ -52,28 +54,6 @@ public class XmlStreamWriter extends Writer {
     private String encoding;
 
     /**
-     * Constructs a new XML stream writer for the specified output stream
-     * with a default encoding of UTF-8.
-     *
-     * @param out The output stream
-     */
-    public XmlStreamWriter(final OutputStream out) {
-        this(out, null);
-    }
-
-    /**
-     * Constructs a new XML stream writer for the specified output stream
-     * with the specified default encoding.
-     *
-     * @param out The output stream
-     * @param defaultEncoding The default encoding if not encoding could be detected
-     */
-    public XmlStreamWriter(final OutputStream out, final String defaultEncoding) {
-        this.out = out;
-        this.defaultEncoding = defaultEncoding != null ? defaultEncoding : "UTF-8";
-    }
-
-    /**
      * Constructs a new XML stream writer for the specified file
      * with a default encoding of UTF-8.
      *
@@ -100,21 +80,25 @@ public class XmlStreamWriter extends Writer {
     }
 
     /**
-     * Returns the detected encoding.
+     * Constructs a new XML stream writer for the specified output stream
+     * with a default encoding of UTF-8.
      *
-     * @return the detected encoding
+     * @param out The output stream
      */
-    public String getEncoding() {
-        return encoding;
+    public XmlStreamWriter(final OutputStream out) {
+        this(out, null);
     }
 
     /**
-     * Returns the default encoding.
+     * Constructs a new XML stream writer for the specified output stream
+     * with the specified default encoding.
      *
-     * @return the default encoding
+     * @param out The output stream
+     * @param defaultEncoding The default encoding if not encoding could be detected
      */
-    public String getDefaultEncoding() {
-        return defaultEncoding;
+    public XmlStreamWriter(final OutputStream out, final String defaultEncoding) {
+        this.out = out;
+        this.defaultEncoding = defaultEncoding != null ? defaultEncoding : "UTF-8";
     }
 
     /**
@@ -133,18 +117,6 @@ public class XmlStreamWriter extends Writer {
     }
 
     /**
-     * Flushes the underlying writer.
-     *
-     * @throws IOException if an error occurs flushing the underlying writer
-     */
-    @Override
-    public void flush() throws IOException {
-        if (writer != null) {
-            writer.flush();
-        }
-    }
-
-    /**
      * Detects the encoding.
      *
      * @param cbuf the buffer to write the characters from
@@ -200,6 +172,36 @@ public class XmlStreamWriter extends Writer {
     }
 
     /**
+     * Flushes the underlying writer.
+     *
+     * @throws IOException if an error occurs flushing the underlying writer
+     */
+    @Override
+    public void flush() throws IOException {
+        if (writer != null) {
+            writer.flush();
+        }
+    }
+
+    /**
+     * Returns the default encoding.
+     *
+     * @return the default encoding
+     */
+    public String getDefaultEncoding() {
+        return defaultEncoding;
+    }
+
+    /**
+     * Returns the detected encoding.
+     *
+     * @return the detected encoding
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
      * Writes the characters to the underlying writer, detecting encoding.
      *
      * @param cbuf the buffer to write the characters from
@@ -215,6 +217,4 @@ public class XmlStreamWriter extends Writer {
             writer.write(cbuf, off, len);
         }
     }
-
-    static final Pattern ENCODING_PATTERN = XmlStreamReader.ENCODING_PATTERN;
 }
diff --git a/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java b/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java
index a7f9b28..535f94f 100644
--- a/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java
+++ b/src/main/java/org/apache/commons/io/serialization/RegexpClassNameMatcher.java
@@ -31,15 +31,6 @@ final class RegexpClassNameMatcher implements ClassNameMatcher {
     private final Pattern pattern; // Class is thread-safe
 
     /**
-     * Constructs an object based on the specified regular expression.
-     *
-     * @param regex a regular expression for evaluating acceptable class names
-     */
-    public RegexpClassNameMatcher(final String regex) {
-        this(Pattern.compile(regex));
-    }
-
-    /**
      * Constructs an object based on the specified pattern.
      *
      * @param pattern a pattern for evaluating acceptable class names
@@ -52,6 +43,15 @@ final class RegexpClassNameMatcher implements ClassNameMatcher {
         this.pattern = pattern;
     }
 
+    /**
+     * Constructs an object based on the specified regular expression.
+     *
+     * @param regex a regular expression for evaluating acceptable class names
+     */
+    public RegexpClassNameMatcher(final String regex) {
+        this(Pattern.compile(regex));
+    }
+
     @Override
     public boolean matches(final String className) {
         return pattern.matcher(className).matches();
diff --git a/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java b/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
index 80a99e8..f4fea5e 100644
--- a/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
+++ b/src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
@@ -59,48 +59,6 @@ public class ValidatingObjectInputStream extends ObjectInputStream {
         super(input);
     }
 
-    /** Check that the classname conforms to requirements.
-     * @param name The class name
-     * @throws InvalidClassException when a non-accepted class is encountered
-     */
-    private void validateClassName(final String name) throws InvalidClassException {
-        // Reject has precedence over accept
-        for (final ClassNameMatcher m : rejectMatchers) {
-            if (m.matches(name)) {
-                invalidClassNameFound(name);
-            }
-        }
-
-        boolean ok = false;
-        for (final ClassNameMatcher m : acceptMatchers) {
-            if (m.matches(name)) {
-                ok = true;
-                break;
-            }
-        }
-        if (!ok) {
-            invalidClassNameFound(name);
-        }
-    }
-
-    /**
-     * Called to throw {@code InvalidClassException} if an invalid
-     * class name is found during deserialization. Can be overridden, for example
-     * to log those class names.
-     *
-     * @param className name of the invalid class
-     * @throws InvalidClassException if the specified class is not allowed
-     */
-    protected void invalidClassNameFound(final String className) throws InvalidClassException {
-        throw new InvalidClassException("Class name not accepted: " + className);
-    }
-
-    @Override
-    protected Class<?> resolveClass(final ObjectStreamClass osc) throws IOException, ClassNotFoundException {
-        validateClassName(osc.getName());
-        return super.resolveClass(osc);
-    }
-
     /**
      * Accept the specified classes for deserialization, unless they
      * are otherwise rejected.
@@ -116,16 +74,26 @@ public class ValidatingObjectInputStream extends ObjectInputStream {
     }
... 148 lines suppressed ...