Clover coverage report - PLT Utilities Test Coverage (plt-20120304-r5436)
Coverage timestamp: Sat Mar 3 2012 22:01:56 CST
file stats: LOC: 1,957   Methods: 187
NCLOC: 1,018   Classes: 14
 
 Source file Conditionals Statements Methods TOTAL
IOUtil.java 9.4% 15.1% 17.1% 14.5%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK*
 2   
 3    PLT Utilities BSD License
 4   
 5    Copyright (c) 2007-2010 JavaPLT group at Rice University
 6    All rights reserved.
 7   
 8    Developed by: Java Programming Languages Team
 9    Rice University
 10    http://www.cs.rice.edu/~javaplt/
 11   
 12    Redistribution and use in source and binary forms, with or without modification, are permitted
 13    provided that the following conditions are met:
 14   
 15    - Redistributions of source code must retain the above copyright notice, this list of conditions
 16    and the following disclaimer.
 17    - Redistributions in binary form must reproduce the above copyright notice, this list of
 18    conditions and the following disclaimer in the documentation and/or other materials provided
 19    with the distribution.
 20    - Neither the name of the JavaPLT group, Rice University, nor the names of the library's
 21    contributors may be used to endorse or promote products derived from this software without
 22    specific prior written permission.
 23   
 24    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 25    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 26    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND
 27    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 28    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 29    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 30    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 31    OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 32   
 33    *END_COPYRIGHT_BLOCK*/
 34   
 35    package edu.rice.cs.plt.io;
 36   
 37    import java.io.*;
 38    import java.lang.reflect.Field;
 39    import java.lang.reflect.Modifier;
 40    import java.util.ArrayList;
 41    import java.util.Date;
 42    import java.util.HashSet;
 43    import java.util.Iterator;
 44    import java.util.List;
 45    import java.util.LinkedList;
 46    import java.util.NoSuchElementException;
 47    import java.util.Set;
 48    import java.util.regex.Pattern;
 49   
 50    import edu.rice.cs.plt.debug.ThreadSnapshot;
 51    import edu.rice.cs.plt.iter.IterUtil;
 52    import edu.rice.cs.plt.iter.ReadOnlyIterator;
 53    import edu.rice.cs.plt.iter.SizedIterable;
 54    import edu.rice.cs.plt.lambda.*;
 55    import edu.rice.cs.plt.tuple.*;
 56    import edu.rice.cs.plt.recur.RecursionStack;
 57    import edu.rice.cs.plt.reflect.ReflectUtil;
 58    import edu.rice.cs.plt.text.TextUtil;
 59   
 60    import static edu.rice.cs.plt.debug.DebugUtil.error;
 61   
 62    /**
 63    * Provides additional operations on {@link File}s, {@link InputStream}s, {@link OutputStream}s,
 64    * {@link Reader}s, and {@link Writer}s not defined in the {@code java.io} package.
 65    */
 66    public final class IOUtil {
 67   
 68    /** Prevents instance creation */
 69  0 private IOUtil() {}
 70   
 71    /**
 72    * The current working directory, used as the base for relative paths. Based on System property {@code user.dir},
 73    * if defined, converted to an absolute path.
 74    */
 75    public static final File WORKING_DIRECTORY = IOUtil.attemptAbsoluteFile(new File(System.getProperty("user.dir", "")));
 76   
 77    /** A factory for Files based on a String filename. */
 78    public static final Lambda<String, File> FILE_FACTORY = new FileFactory();
 79   
 80    private static class FileFactory implements Lambda<String, File>, Serializable {
 81  21 private FileFactory() {}
 82  110 public File value(String name) { return new File(name); }
 83    };
 84   
 85    /**
 86    * Make a best attempt at evaluating {@link File#getAbsoluteFile()}. In the event of a
 87    * {@link SecurityException}, the result is just {@code f}. (Clients <em>cannot</em>
 88    * assume, then, that the result is absolute.)
 89    */
 90  65 public static File attemptAbsoluteFile(File f) {
 91  65 try { return f.getAbsoluteFile(); }
 92  0 catch (SecurityException e) { return f; }
 93    }
 94   
 95    /**
 96    * Apply {@link File#getAbsoluteFile} to all files in a list
 97    * @throws SecurityException If any of the {@code getAbsoluteFile()} invocations triggers
 98    * a {@code SecurityException}
 99    */
 100  0 public static SizedIterable<File> getAbsoluteFiles(Iterable<? extends File> files) {
 101  0 return IterUtil.mapSnapshot(files, GET_ABSOLUTE_FILE);
 102    }
 103   
 104    private static final Lambda<File, File> GET_ABSOLUTE_FILE = new Lambda<File, File>() {
 105  0 public File value(File arg) { return arg.getAbsoluteFile(); }
 106    };
 107   
 108    /** Apply {@link #attemptAbsoluteFile} to all files in a list */
 109  4 public static SizedIterable<File> attemptAbsoluteFiles(Iterable<? extends File> files) {
 110  4 return IterUtil.mapSnapshot(files, ATTEMPT_ABSOLUTE_FILE);
 111    }
 112   
 113    private static final Lambda<File, File> ATTEMPT_ABSOLUTE_FILE = new Lambda<File, File>() {
 114  37 public File value(File arg) { return attemptAbsoluteFile(arg); }
 115    };
 116   
 117    /**
 118    * Make a best attempt at evaluating {@link File#getCanonicalFile()}. In the event of an
 119    * {@link IOException} or a {@link SecurityException}, the result is instead the result of
 120    * {@link #attemptAbsoluteFile}. (Clients <em>cannot</em> assume, then, that the result
 121    * is canonical.)
 122    */
 123  0 public static File attemptCanonicalFile(File f) {
 124  0 try { return f.getCanonicalFile(); }
 125  0 catch (IOException e) { return attemptAbsoluteFile(f); }
 126  0 catch (SecurityException e) { return attemptAbsoluteFile(f); }
 127    }
 128   
 129    /**
 130    * Apply {@link File#getCanonicalFile} to all files in a list
 131    * @throws IOException If any of the {@code getCanonicalFile()} invocations triggers
 132    * an {@code IOException}
 133    * @throws SecurityException If any of the {@code getCanonicalFile()} invocations triggers
 134    * a {@code SecurityException}
 135    */
 136  0 public static SizedIterable<File> getCanonicalFiles(Iterable<? extends File> files) throws IOException {
 137  0 try { return IterUtil.mapSnapshot(files, GET_CANONICAL_FILE); }
 138    catch (WrappedException e) {
 139  0 if (e.getCause() instanceof IOException) { throw (IOException) e.getCause(); }
 140  0 else { throw e; }
 141    }
 142    }
 143   
 144    private static final Lambda<File, File> GET_CANONICAL_FILE = new Lambda<File, File>() {
 145  0 public File value(File arg) {
 146  0 try { return arg.getCanonicalFile(); }
 147  0 catch (IOException e) { throw new WrappedException(e); }
 148    }
 149    };
 150   
 151    /** Apply {@link #attemptCanonicalFile} to all files in a list */
 152  0 public static SizedIterable<File> attemptCanonicalFiles(Iterable<? extends File> files) {
 153  0 return IterUtil.mapSnapshot(files, ATTEMPT_CANONICAL_FILE);
 154    }
 155   
 156    private static final Lambda<File, File> ATTEMPT_CANONICAL_FILE = new Lambda<File, File>() {
 157  0 public File value(File arg) { return attemptAbsoluteFile(arg); }
 158    };
 159   
 160    /**
 161    * Make a best attempt at evaluating {@link File#canRead()}. In the event of a
 162    * {@link SecurityException}, the result is {@code false}.
 163    */
 164  0 public static boolean attemptCanRead(File f) {
 165  0 try { return f.canRead(); }
 166  0 catch (SecurityException e) { return false; }
 167    }
 168   
 169    /**
 170    * Make a best attempt at evaluating {@link File#canWrite()}. In the event of a
 171    * {@link SecurityException}, the result is {@code false}.
 172    */
 173  0 public static boolean attemptCanWrite(File f) {
 174  0 try { return f.canWrite(); }
 175  0 catch (SecurityException e) { return false; }
 176    }
 177   
 178    /**
 179    * Make a best attempt at evaluating {@link File#exists()}. In the event of a
 180    * {@link SecurityException}, the result is {@code false}.
 181    */
 182  3 public static boolean attemptExists(File f) {
 183  3 try { return f.exists(); }
 184  0 catch (SecurityException e) { return false; }
 185    }
 186   
 187    /**
 188    * Make a best attempt at evaluating {@link File#isDirectory()}. In the event of a
 189    * {@link SecurityException}, the result is {@code false}.
 190    */
 191  3 public static boolean attemptIsDirectory(File f) {
 192  3 try { return f.isDirectory(); }
 193  0 catch (SecurityException e) { return false; }
 194    }
 195   
 196    /**
 197    * Make a best attempt at evaluating {@link File#isFile()}. In the event of a
 198    * {@link SecurityException}, the result is {@code false}.
 199    */
 200  3 public static boolean attemptIsFile(File f) {
 201  3 try { return f.isFile(); }
 202  0 catch (SecurityException e) { return false; }
 203    }
 204   
 205    /**
 206    * Make a best attempt at evaluating {@link File#isHidden()}. In the event of a
 207    * {@link SecurityException}, the result is {@code false}.
 208    */
 209  0 public static boolean attemptIsHidden(File f) {
 210  0 try { return f.isHidden(); }
 211  0 catch (SecurityException e) { return false; }
 212    }
 213   
 214    /**
 215    * Make a best attempt at evaluating {@link File#lastModified()}. In the event of a
 216    * {@link SecurityException}, the result is {@code 0l}.
 217    */
 218  0 public static long attemptLastModified(File f) {
 219  0 try { return f.lastModified(); }
 220  0 catch (SecurityException e) { return 0l; }
 221    }
 222   
 223    /**
 224    * Make a best attempt at evaluating {@link File#length()}. In the event of a
 225    * {@link SecurityException}, the result is {@code 0l}.
 226    */
 227  0 public static long attemptLength(File f) {
 228  0 try { return f.length(); }
 229  0 catch (SecurityException e) { return 0l; }
 230    }
 231   
 232    /**
 233    * Make a best attempt at invoking {@link File#createNewFile()}. In the event of an
 234    * {@link IOException} or a {@link SecurityException}, the result is {@code false}.
 235    */
 236  0 public static boolean attemptCreateNewFile(File f) {
 237  0 try { return f.createNewFile(); }
 238  0 catch (IOException e) { return false; }
 239  0 catch (SecurityException e) { return false; }
 240    }
 241   
 242    /**
 243    * Make a best attempt at invoking {@link File#delete()}. In the event of a
 244    * {@link SecurityException}, the result is {@code false}.
 245    */
 246  0 public static boolean attemptDelete(File f) {
 247  0 try { return f.delete(); }
 248  0 catch (SecurityException e) { return false; }
 249    }
 250   
 251    /**
 252    * Make a best attempt at invoking {@link File#deleteOnExit()}. Any resulting
 253    * {@link SecurityException} will be ignored.
 254    */
 255  0 public static void attemptDeleteOnExit(File f) {
 256  0 try { f.deleteOnExit(); }
 257    catch (SecurityException e) { /* ignore */ }
 258    }
 259   
 260    /**
 261    * Make a best attempt at invoking {@link File#listFiles()}. In the event of a
 262    * {@link SecurityException}, the result is {@code null}.
 263    */
 264  0 public static File[] attemptListFiles(File f) {
 265  0 try { return f.listFiles(); }
 266  0 catch (SecurityException e) { return null; }
 267    }
 268   
 269    /**
 270    * Make a best attempt at invoking {@link File#listFiles(FileFilter)}. In the event of a
 271    * {@link SecurityException}, the result is {@code null}.
 272    */
 273  0 public static File[] attemptListFiles(File f, FileFilter filter) {
 274  0 try { return f.listFiles(filter); }
 275  0 catch (SecurityException e) { return null; }
 276    }
 277   
 278    /**
 279    * Make a best attempt at invoking {@link File#listFiles(FileFilter)}. In the event of a
 280    * {@link SecurityException}, the result is {@code null}. The given predicate is converted
 281    * to a {@code FileFilter}.
 282    */
 283  0 public static File[] attemptListFiles(File f, Predicate<? super File> filter) {
 284  0 return attemptListFiles(f, (FileFilter) asFilePredicate(filter));
 285    }
 286   
 287    /**
 288    * Make a best attempt at invoking {@link File#listFiles(FileFilter)}. In the event of a
 289    * {@link SecurityException}, the result is {@code null}. (Defined to resolve method
 290    * ambiguity when a FilePredicate is used.)
 291    */
 292  0 public static File[] attemptListFiles(File f, FilePredicate filter) {
 293  0 return attemptListFiles(f, (FileFilter) filter);
 294    }
 295   
 296    /**
 297    * Similar to {@link #attemptListFiles(File)}, but returns a non-null {@code Iterable}
 298    * rather than an array. Where {@code attemptListFiles(f)} returns {@code null},
 299    * the result here is an empty iterable.
 300    */
 301  0 public static SizedIterable<File> attemptListFilesAsIterable(File f) {
 302  0 File[] result = attemptListFiles(f);
 303  0 if (result == null) { return IterUtil.empty(); }
 304  0 else { return IterUtil.asIterable(result); }
 305    }
 306   
 307    /**
 308    * Similar to {@link #attemptListFiles(File, FileFilter)}, but returns a non-null {@code Iterable}
 309    * rather than an array. Where {@code attemptListFiles(f)} returns {@code null},
 310    * the result here is an empty iterable.
 311    */
 312  0 public static SizedIterable<File> attemptListFilesAsIterable(File f, FileFilter filter) {
 313  0 File[] result = attemptListFiles(f, filter);
 314  0 if (result == null) { return IterUtil.empty(); }
 315  0 else { return IterUtil.asIterable(result); }
 316    }
 317   
 318    /**
 319    * Similar to {@link #attemptListFiles(File, FileFilter)}, but returns a non-null {@code Iterable}
 320    * rather than an array. Where {@code attemptListFiles(f)} returns {@code null},
 321    * the result here is an empty iterable. The given predicate is converted to a {@code FileFilter}.
 322    */
 323  0 public static SizedIterable<File> attemptListFilesAsIterable(File f, Predicate<? super File> filter) {
 324  0 return attemptListFilesAsIterable(f, (FileFilter) asFilePredicate(filter));
 325    }
 326   
 327    /**
 328    * Similar to {@link #attemptListFiles(File, FileFilter)}, but returns a non-null {@code Iterable}
 329    * rather than an array. Where {@code attemptListFiles(f)} returns {@code null},
 330    * the result here is an empty iterable. (Defined to resolve method ambiguity when called with
 331    * a FilePredicate.)
 332    */
 333  0 public static SizedIterable<File> attemptListFilesAsIterable(File f, FilePredicate filter) {
 334  0 return attemptListFilesAsIterable(f, (FileFilter) filter);
 335    }
 336   
 337    /**
 338    * Make a best attempt at invoking {@link File#mkdir()}. In the event of a
 339    * {@link SecurityException}, the result is {@code false}.
 340    */
 341  0 public static boolean attemptMkdir(File f) {
 342  0 try { return f.mkdir(); }
 343  0 catch (SecurityException e) { return false; }
 344    }
 345   
 346    /**
 347    * Make a best attempt at invoking {@link File#mkdirs()}. In the event of a
 348    * {@link SecurityException}, the result is {@code false}.
 349    */
 350  0 public static boolean attemptMkdirs(File f) {
 351  0 try { return f.mkdirs(); }
 352  0 catch (SecurityException e) { return false; }
 353    }
 354   
 355    /**
 356    * Make a best attempt at invoking {@link File#renameTo}. In the event of a
 357    * {@link SecurityException}, the result is {@code false}.
 358    */
 359  0 public static boolean attemptRenameTo(File f, File dest) {
 360  0 try { return f.renameTo(dest); }
 361  0 catch (SecurityException e) { return false; }
 362    }
 363   
 364    /**
 365    * Like {@link #attemptRenameTo}, makes a best attempt at renaming {@code f}.
 366    * Before doing so, however, {@code dest}, if it exists, is deleted. (On some systems,
 367    * such as Windows, the rename will otherwise fail.)
 368    */
 369  0 public static boolean attemptMove(File f, File dest) {
 370  0 attemptDelete(dest);
 371  0 return attemptRenameTo(f, dest);
 372    }
 373   
 374    /**
 375    * Make a best attempt at invoking {@link File#setLastModified}. In the event of a
 376    * {@link SecurityException}, the result is {@code false}.
 377    */
 378  0 public static boolean attemptSetLastModified(File f, long time) {
 379  0 try { return f.setLastModified(time); }
 380  0 catch (SecurityException e) { return false; }
 381    }
 382   
 383    /**
 384    * Make a best attempt at invoking {@link File#setReadOnly}. In the event of a
 385    * {@link SecurityException}, the result is {@code false}.
 386    */
 387  0 public static boolean attemptSetReadOnly(File f) {
 388  0 try { return f.setReadOnly(); }
 389  0 catch (SecurityException e) { return false; }
 390    }
 391   
 392    /**
 393    * Converts a {@code File} to all lowercase in case-insensitive systems; otherwise, returns the file
 394    * untouched. This provides a basis for comparing files in case-insensitive file systems.
 395    * Note that a canonical file (see {@link File#getCanonicalFile} and {@link #attemptCanonicalFile})
 396    * is not necessarily in canonical case, and applying this function to a canonical file may
 397    * create a file that is no longer canonical. Where the file doesn't exist, it's even possible
 398    * that two <em>different</em> canonical files have the same canonical case representation.
 399    */
 400  15 public static File canonicalCase(File f) {
 401  15 File lowered = new File(f.getPath().toLowerCase());
 402  15 if (f.equals(lowered)) { return lowered; }
 403  0 else { return f; }
 404    }
 405   
 406    /** Apply {@link #canonicalCase} to all files in a list */
 407  0 public static SizedIterable<File> canonicalCases(Iterable<? extends File> files) {
 408  0 return IterUtil.mapSnapshot(files, CANONICAL_CASE);
 409    }
 410   
 411    private static final Lambda<File, File> CANONICAL_CASE = new Lambda<File, File>() {
 412  0 public File value(File arg) { return canonicalCase(arg); }
 413    };
 414   
 415    /**
 416    * Determine if the given file is a member (as determined by {@link File#getParentFile})
 417    * of another file. If {@code f.equals(ancestor)}, the result is {@code true}.
 418    */
 419  0 public static boolean isMember(File f, File ancestor) {
 420  0 File parent = f;
 421  0 while (parent != null) {
 422  0 if (parent.equals(ancestor)) { return true; }
 423  0 parent = parent.getParentFile();
 424    }
 425  0 return false;
 426    }
 427   
 428    /**
 429    * Create a list of files representing the full path of {@code f}. This list
 430    * has at least one element -- {@code f} -- and contains all files that are
 431    * produced by repeated invocations of {@link File#getParentFile()}. The
 432    * outermost file appears first in the list, with {@code f} appearing last.
 433    */
 434  0 public static SizedIterable<File> fullPath(File f) {
 435  0 SizedIterable<File> result = IterUtil.singleton(f);
 436  0 File parent = f.getParentFile();
 437  0 while (parent != null) {
 438  0 result = IterUtil.compose(parent, result);
 439  0 parent = parent.getParentFile();
 440    }
 441  0 return result;
 442    }
 443   
 444    /**
 445    * Attempt to delete the given {@code File}. If {@code f} is a directory, this method will first
 446    * recur on each of {@code f}'s members. In all cases, {@link #attemptDelete} will be invoked on
 447    * {@code f}, and the result of this method will be identical to that result.
 448    */
 449  0 public static boolean deleteRecursively(File f) {
 450  0 return deleteRecursively(f, new RecursionStack<File>(Wrapper.<File>factory()));
 451    }
 452   
 453    /** Helper method for {@link #deleteRecursively(File)} */
 454  0 private static boolean deleteRecursively(File f, final RecursionStack<File> stack) {
 455  0 if (f.isDirectory()) {
 456  0 try {
 457  0 final File canonicalF = f.getCanonicalFile();
 458  0 Runnable deleteMembers = new Runnable() {
 459  0 public void run() {
 460  0 for (File child : attemptListFilesAsIterable(canonicalF)) { deleteRecursively(child, stack); }
 461    }
 462    };
 463  0 stack.run(deleteMembers, canonicalF);
 464    }
 465    catch (IOException e) { /* ignore -- don't delete files */ }
 466    catch (SecurityException e) { /* ignore -- don't delete files */ }
 467    }
 468  0 return attemptDelete(f);
 469    }
 470   
 471    /**
 472    * Attempt to mark the given {@code File} for deletion. If {@code f} is a directory, this method
 473    * will also recur on each of {@code f}'s members. In all cases, {@link #attemptDeleteOnExit} will
 474    * be invoked on {@code f}. (Note that {@code f} may not actually be deleted on exit if some of its
 475    * contents change after invoking this method, or if an error occurs in listing and marking its members
 476    * for deletion.)
 477    */
 478  0 public static void deleteOnExitRecursively(File f) {
 479  0 deleteOnExitRecursively(f, new RecursionStack<File>(Wrapper.<File>factory()));
 480    }
 481   
 482    /** Helper method for {@link #deleteRecursively(File)} */
 483  0 private static void deleteOnExitRecursively(File f, final RecursionStack<File> stack) {
 484  0 attemptDeleteOnExit(f);
 485  0 if (f.isDirectory()) {
 486    /* This recursive walk has to be done AFTER calling deleteOnExit on the directory itself because
 487    Java closes the list of files to deleted on exit in reverse order. */
 488  0 try {
 489  0 final File canonicalF = f.getCanonicalFile();
 490  0 Runnable markMembers = new Runnable() {
 491  0 public void run() {
 492  0 for (File child : attemptListFilesAsIterable(canonicalF)) { deleteOnExitRecursively(child, stack); }
 493    }
 494    };
 495  0 stack.run(markMembers, canonicalF);
 496    }
 497    catch (IOException e) { /* ignore -- don't mark files */ }
 498    catch (SecurityException e) { /* ignore -- don't mark files */ }
 499    }
 500    }
 501   
 502    /**
 503    * Produce a list of the recursive contents of a file. The result is a list beginning with
 504    * {@code f}, followed (if {@code f} is a directory with a canonical path) by a recursive
 505    * listing of each of the files belonging to {@code f}. The recursion will halt cleanly in
 506    * the presence of loops in the system. If an error occurs in listing a directory, that
 507    * directory will be skipped.
 508    *
 509    * @param f A file (generally a directory) to be listed recursively
 510    */
 511  0 public static SizedIterable<File> listFilesRecursively(File f) {
 512  0 return listFilesRecursively(f, ALWAYS_ACCEPT, ALWAYS_ACCEPT);
 513    }
 514   
 515    /**
 516    * Produce a list of the recursive contents of a file. The result is a list beginning with
 517    * {@code f}, followed (if {@code f} is a directory with a canonical path}) by a recursive
 518    * listing of each of the files belonging to {@code f}. The recursion will halt cleanly in
 519    * the presence of loops in the system. If an error occurs in listing a directory, that
 520    * directory will be skipped.
 521    *
 522    * @param f A file (generally a directory) to be listed recursively
 523    * @param filter A filter for the list -- files that do not match will not be included
 524    * (but directories that do not match will still be traversed)
 525    */
 526  0 public static SizedIterable<File> listFilesRecursively(File f, FileFilter filter) {
 527  0 return listFilesRecursively(f, filter, ALWAYS_ACCEPT);
 528    }
 529   
 530    /**
 531    * Produce a list of the recursive contents of a file. The result is a list beginning with
 532    * {@code f}, followed (if {@code f} is a directory with a canonical path}) by a recursive
 533    * listing of each of the files belonging to {@code f}. The recursion will halt cleanly in
 534    * the presence of loops in the system. If an error occurs in listing a directory, that
 535    * directory will be skipped.
 536    *
 537    * @param f A file (generally a directory) to be listed recursively
 538    * @param filter A filter for the list -- files that do not match will not be included
 539    * (but directories that do not match will still be traversed)
 540    */
 541  0 public static SizedIterable<File> listFilesRecursively(File f, Predicate<? super File> filter) {
 542  0 return listFilesRecursively(f, asFilePredicate(filter), ALWAYS_ACCEPT);
 543    }
 544   
 545    /**
 546    * Produce a list of the recursive contents of a file. The result is a list beginning with
 547    * {@code f}, followed (if {@code f} is a directory with a canonical path}) by a recursive
 548    * listing of each of the files belonging to {@code f}. The recursion will halt cleanly in
 549    * the presence of loops in the system. If an error occurs in listing a directory, that
 550    * directory will be skipped. (Defined to resolve method ambiguity when a FilePredicate is
 551    * used.)
 552    *
 553    * @param f A file (generally a directory) to be listed recursively
 554    * @param filter A filter for the list -- files that do not match will not be included
 555    * (but directories that do not match will still be traversed)
 556    */
 557  0 public static SizedIterable<File> listFilesRecursively(File f, FilePredicate filter) {
 558  0 return listFilesRecursively(f, filter, ALWAYS_ACCEPT);
 559    }
 560   
 561    /**
 562    * Produce a list of the recursive contents of a file. The result is a list beginning with
 563    * {@code f}, followed (if {@code f} is a directory with a canonical path}) by a recursive
 564    * listing of each of the files belonging to {@code f}. The recursion will halt cleanly in
 565    * the presence of loops in the system.
 566    *
 567    * @param f A file (generally a directory) to be listed recursively
 568    * @param filter A filter for the list -- files that do not match will not be included
 569    * (but directories that do not match will still be traversed)
 570    * @param recursionFilter A filter controlling recursion -- directories that are rejected will
 571    * not be traversed.
 572    */
 573  0 public static SizedIterable<File> listFilesRecursively(File f, FileFilter filter, FileFilter recursionFilter) {
 574  0 return listFilesRecursively(f, filter, recursionFilter, new RecursionStack<File>(Wrapper.<File>factory()));
 575    }
 576   
 577    /**
 578    * Produce a list of the recursive contents of a file. The result is a list beginning with
 579    * {@code f}, followed (if {@code f} is a directory with a canonical path}) by a recursive
 580    * listing of each of the files belonging to {@code f}. The recursion will halt cleanly in
 581    * the presence of loops in the system.
 582    *
 583    * @param f A file (generally a directory) to be listed recursively
 584    * @param filter A filter for the list -- files that do not match will not be included
 585    * (but directories that do not match will still be traversed)
 586    * @param recursionFilter A filter controlling recursion -- directories that are rejected will
 587    * not be traversed.
 588    */
 589  0 public static SizedIterable<File> listFilesRecursively(File f, Predicate<? super File> filter,
 590    Predicate<? super File> recursionFilter) {
 591  0 return listFilesRecursively(f, asFilePredicate(filter), asFilePredicate(recursionFilter),
 592    new RecursionStack<File>(Wrapper.<File>factory()));
 593    }
 594   
 595    /**
 596    * Produce a list of the recursive contents of a file. The result is a list beginning with
 597    * {@code f}, followed (if {@code f} is a directory with a canonical path}) by a recursive
 598    * listing of each of the files belonging to {@code f}. The recursion will halt cleanly in
 599    * the presence of loops in the system. (Defined to resolve method ambiguity where two
 600    * FilePredicates are used.)
 601    *
 602    * @param f A file (generally a directory) to be listed recursively
 603    * @param filter A filter for the list -- files that do not match will not be included
 604    * (but directories that do not match will still be traversed)
 605    * @param recursionFilter A filter controlling recursion -- directories that are rejected will
 606    * not be traversed.
 607    */
 608  0 public static SizedIterable<File> listFilesRecursively(File f, FilePredicate filter, FilePredicate recursionFilter) {
 609  0 return listFilesRecursively(f, filter, recursionFilter, new RecursionStack<File>(Wrapper.<File>factory()));
 610    }
 611   
 612    /** Helper method for {@code listFilesRecursively} */
 613  0 private static SizedIterable<File> listFilesRecursively(final File f, final FileFilter filter,
 614    final FileFilter recursionFilter,
 615    final RecursionStack<File> stack) {
 616  0 SizedIterable<File> result = (filter.accept(f)) ? IterUtil.singleton(f) : IterUtil.<File>empty();
 617  0 if (f.isDirectory() && recursionFilter.accept(f)) {
 618  0 Thunk<Iterable<File>> getMembers = new Thunk<Iterable<File>>() {
 619  0 public Iterable<File> value() {
 620  0 Iterable<File> dirFiles = IterUtil.empty();
 621  0 for (File child : attemptListFilesAsIterable(f)) {
 622  0 dirFiles = IterUtil.compose(dirFiles, listFilesRecursively(child, filter, recursionFilter, stack));
 623    }
 624  0 return dirFiles;
 625    }
 626    };
 627  0 try {
 628  0 result = IterUtil.compose(result, stack.apply(getMembers, IterUtil.<File>empty(), f.getCanonicalFile()));
 629    }
 630    catch (IOException e) { /* ignore -- don't include directory's files */ }
 631    catch (SecurityException e) { /* ignore -- don't include directory's files */ }
 632    }
 633  0 return result;
 634    }
 635   
 636    /**
 637    * Reads the entire contents of a file and return it as a byte array.
 638    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during reading
 639    * @throws SecurityException If read access to the file is denied
 640    */
 641  0 public static byte[] toByteArray(File file) throws IOException {
 642  0 FileInputStream input = new FileInputStream(file);
 643  0 try { return toByteArray(input); }
 644  0 finally { input.close(); }
 645    }
 646   
 647    /**
 648    * Reads the entire contents of a file and return it as a StringBuffer. (We use a StringBuffer rather than a
 649    * StringBuilder because that is what {@link StringWriter} supports.)
 650    *
 651    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during reading
 652    * @throws SecurityException If read access to the file is denied
 653    */
 654  0 public static StringBuffer toStringBuffer(File file) throws IOException {
 655  0 FileReader reader = new FileReader(file);
 656  0 try { return toStringBuffer(reader); }
 657  0 finally { reader.close(); }
 658    }
 659   
 660    /**
 661    * Reads the entire contents of a file and return it as a String.
 662    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during reading
 663    * @throws SecurityException If read access to the file is denied
 664    */
 665  0 public static String toString(File file) throws IOException {
 666  0 FileReader reader = new FileReader(file);
 667  0 try { return toString(reader); }
 668  0 finally { reader.close(); }
 669    }
 670   
 671    /**
 672    * Get the contents of a file as a sequence of lines, accessed via an iterator. Lines are
 673    * broken as determined by {@link BufferedReader#readLine}. The result iterator's {@code next()}
 674    * method throws a {@link WrappedException} wrapping an {@link IOException} if an error occurs while
 675    * reading.
 676    * @throws IOException If the file can't be found or an error occurs when reading the first line (for lookahead)
 677    */
 678  0 public static Iterator<String> readLines(File file) throws IOException {
 679  0 FileReader reader = new FileReader(file);
 680  0 return readLines(reader);
 681    }
 682   
 683    /**
 684    * Produce an Adler-32 hash for the given file. The result is an int (32 bits).
 685    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during reading
 686    * @throws SecurityException If read access to the file is denied
 687    */
 688  0 public static int adler32Hash(File file) throws IOException {
 689  0 InputStream input = new FileInputStream(file);
 690  0 try { return adler32Hash(input); }
 691  0 finally { input.close(); }
 692    }
 693   
 694    /**
 695    * Produce a CRC-32 hash for the given file. The result is an int (32 bits).
 696    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during reading
 697    * @throws SecurityException If read access to the file is denied
 698    */
 699  0 public static int crc32Hash(File file) throws IOException {
 700  0 InputStream input = new FileInputStream(file);
 701  0 try { return crc32Hash(input); }
 702  0 finally { input.close(); }
 703    }
 704   
 705    /**
 706    * Produce an MD5 hash for the given file. The result is 16 bytes (128 bits) long.
 707    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during reading
 708    * @throws SecurityException If read access to the file is denied
 709    */
 710  0 public static byte[] md5Hash(File file) throws IOException {
 711  0 InputStream input = new FileInputStream(file);
 712  0 try { return md5Hash(input); }
 713  0 finally { input.close(); }
 714    }
 715   
 716    /**
 717    * Produce an SHA-1 hash for the given file. The result is 20 bytes (160 bits) long.
 718    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during reading
 719    * @throws SecurityException If read access to the file is denied
 720    */
 721  0 public static byte[] sha1Hash(File file) throws IOException {
 722  0 InputStream input = new FileInputStream(file);
 723  0 try { return sha1Hash(input); }
 724  0 finally { input.close(); }
 725    }
 726   
 727    /**
 728    * Produce an SHA-256 hash for the given file. The result is 32 bytes (256 bits) long.
 729    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during reading
 730    * @throws SecurityException If read access to the file is denied
 731    */
 732  0 public static byte[] sha256Hash(File file) throws IOException {
 733  0 InputStream input = new FileInputStream(file);
 734  0 try { return sha256Hash(input); }
 735  0 finally { input.close(); }
 736    }
 737   
 738    /**
 739    * Copies the contents of one file into another.
 740    * @param source the file to be copied
 741    * @param dest the file to be copied to
 742    * @throws IOException If one of the files does not exist or cannot be opened, or if an error
 743    * occurs during reading or writing
 744    * @throws SecurityException If read or write access, respectively, to the file is denied
 745    */
 746  0 public static void copyFile(File source, File dest) throws IOException {
 747  0 FileInputStream in = new FileInputStream(source);
 748  0 try {
 749  0 FileOutputStream out = new FileOutputStream(dest);
 750  0 try { copyInputStream(in, out); }
 751  0 finally { out.close(); }
 752    }
 753  0 finally { in.close(); }
 754    }
 755   
 756    /**
 757    * Copies the contents of one file into another, using the given array as an intermediate buffer.
 758    * @param source The file to be copied
 759    * @param dest The file to be copied to
 760    * @param buffer A buffer to use in copying
 761    * @throws IOException If one of the files does not exist or cannot be opened, or if an error
 762    * occurs during reading or writing
 763    * @throws SecurityException If read or write access, respectively, to the file is denied
 764    */
 765  0 public static void copyFile(File source, File dest, byte[] buffer) throws IOException {
 766  0 FileInputStream in = new FileInputStream(source);
 767  0 try {
 768  0 FileOutputStream out = new FileOutputStream(dest);
 769  0 try { copyInputStream(in, out, buffer); }
 770  0 finally { out.close(); }
 771    }
 772  0 finally { in.close(); }
 773    }
 774   
 775    /**
 776    * Writes text to the file, overwriting whatever was there.
 777    * @param file File to write to
 778    * @param text Text to write
 779    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during writing
 780    * @throws SecurityException If write access to the file is denied
 781    */
 782  0 public static void writeStringToFile(File file, String text) throws IOException {
 783  0 writeStringToFile(file, text, false);
 784    }
 785   
 786    /**
 787    * Writes text to the file, overwriting whatever was there. Ignores any exceptions that occur.
 788    * @param file File to write to
 789    * @param text Text to write
 790    * @return {@code true} iff the operation succeeded without an exception.
 791    */
 792  0 public static boolean attemptWriteStringToFile(File file, String text) {
 793  0 try { writeStringToFile(file, text); return true; }
 794  0 catch (IOException e) { return false; }
 795  0 catch (SecurityException e) { return false; }
 796    }
 797   
 798    /**
 799    * Writes text to the file.
 800    * @param file File to write to
 801    * @param text Text to write
 802    * @param append {@code true} iff the file should be opened in "append" mode (rather than
 803    * "overwrite" mode)
 804    * @throws IOException If the file does not exist or cannot be opened, or if an error occurs during writing
 805    * @throws SecurityException If write access to the file is denied
 806    */
 807  0 public static void writeStringToFile(File file, String text, boolean append) throws IOException {
 808  0 FileWriter writer = new FileWriter(file, append);
 809  0 try { writer.write(text); }
 810  0 finally { writer.close(); }
 811    }
 812   
 813    /**
 814    * Writes text to the file, ignoring any exceptions that occur.
 815    * @param file File to write to
 816    * @param text Text to write
 817    * @param append {@code true} iff the file should be opened in "append" mode (rather than
 818    * "overwrite" mode)
 819    * @return {@code true} iff the operation succeeded without an exception.
 820    */
 821  0 public static boolean attemptWriteStringToFile(File file, String text, boolean append) {
 822  0 try { writeStringToFile(file, text, append); return true; }
 823  0 catch (IOException e) { return false; }
 824  0 catch (SecurityException e) { return false; }
 825    }
 826   
 827    /**
 828    * Create a temporary file in the system temp directory (via {@link File#createTempFile(String, String)}) and
 829    * immediately mark it for deletion
 830    * @throws IOException If an exception occurs in {@link File#createTempFile(String, String)}
 831    * @throws SecurityException If write or delete access to the system temp directory is denied
 832    */
 833  0 public static File createAndMarkTempFile(String prefix, String suffix) throws IOException {
 834  0 return createAndMarkTempFile(prefix, suffix, null);
 835    }
 836   
 837    /**
 838    * Create a temporary file in the specified directory (via {@link File#createTempFile(String, String, File)}) and
 839    * immediately mark it for deletion
 840    * @throws IOException If an exception occurs in {@link File#createTempFile(String, String, File)}
 841    * @throws SecurityException If write or delete access to {@code location} is denied
 842    */
 843  0 public static File createAndMarkTempFile(String prefix, String suffix, File location) throws IOException {
 844  0 File result = File.createTempFile(prefix, suffix, location);
 845  0 attemptDeleteOnExit(result);
 846  0 return result;
 847    }
 848   
 849    /**
 850    * Create a temporary directory (named by {@link File#createTempFile}) and immediately mark it for
 851    * deletion. (Deletion will only actually occur if the directory is empty on exit, or if all files
 852    * created in the directory are also marked for deletion.)
 853    * @throws IOException If an exception occurs in {@link File#createTempFile}, or if the attempt to
 854    * create the directory is unsuccessful
 855    * @throws SecurityException If write access to the system temp directory is denied
 856    */
 857  0 public static File createAndMarkTempDirectory(String prefix, String suffix) throws IOException {
 858  0 return createAndMarkTempDirectory(prefix, suffix, null);
 859    }
 860   
 861    /**
 862    * Create a temporary directory (named by {@link File#createTempFile}) and immediately mark it for
 863    * deletion. (Deletion will only actually occur if the directory is empty on exit, or if all files
 864    * created in the directory are also marked for deletion.)
 865    * @throws IOException If an exception occurs in {@link File#createTempFile}, or if the attempt to
 866    * create the directory is unsuccessful
 867    * @throws SecurityException If write access to {@code location} is denied
 868    */
 869  0 public static File createAndMarkTempDirectory(String prefix, String suffix, File location) throws IOException {
 870  0 File result = File.createTempFile(prefix, suffix, location);
 871  0 boolean success = result.delete();
 872  0 success = success && result.mkdir();
 873  0 if (!success) { throw new IOException("Attempt to create directory failed"); }
 874  0 attemptDeleteOnExit(result);
 875  0 return result;
 876    }
 877   
 878    /** Parse a path string -- a list of file names separated by the system-dependent
 879    * path separator character (':' in Unix, ';' in Windows). Filename strings in the path
 880    * are interpreted according to the {@code File} constructor.
 881    */
 882  12 public static SizedIterable<File> parsePath(String path) {
 883  12 String[] filenames = path.split(TextUtil.regexEscape(File.pathSeparator));
 884  12 return IterUtil.mapSnapshot(IterUtil.asIterable(filenames), FILE_FACTORY);
 885    }
 886   
 887    /** Produce a path string from a list of files. Filenames in the result are delimited
 888    * by the system-dependent path separator character (':' in Unix, ';' in Windows).
 889    */
 890  11 public static String pathToString(Iterable<? extends File> path) {
 891  11 return IterUtil.toString(path, "", File.pathSeparator, "");
 892    }
 893   
 894   
 895   
 896    /**
 897    * Write the contents of {@code in} to {@code out}. Processing will continue (or block) until
 898    * the end of stream is reached.
 899    * @return The number of chars written, or {@code -1} if the end of stream has already been reached,
 900    * or {@code Integer.MAX_VALUE} if the number cannot be represented as an {@code int}.
 901    * @throws IOException If an IOException occurs during reading or writing
 902    */
 903  0 public static int copyReader(Reader source, Writer dest) throws IOException {
 904  0 return WrappedDirectReader.makeDirect(source).readAll(dest);
 905    }
 906   
 907    /**
 908    * Write the contents of {@code in} to {@code out}, using the given buffer. Processing will
 909    * continue (or block) until the end of stream is reached.
 910    * @return The number of chars written, or {@code -1} if the end of stream has already been reached,
 911    * or {@code Integer.MAX_VALUE} if the number cannot be represented as an {@code int}.
 912    * @throws IOException If an IOException occurs during reading or writing
 913    */
 914  0 public static int copyReader(Reader source, Writer dest, char[] buffer) throws IOException {
 915  0 return WrappedDirectReader.makeDirect(source).readAll(dest, buffer);
 916    }
 917   
 918    /**
 919    * Copy the given number of chars from {@code in} to {@code out}.
 920    * @return The number of chars written, or {@code -1} if the end of stream has already been reached.
 921    * May be less than {@code chars} if {@code source} does not provide all the requested data.
 922    * @throws IOException If an IOException occurs during reading or writing
 923    */
 924  0 public static int writeFromReader(Reader source, Writer dest, int chars) throws IOException {
 925  0 return WrappedDirectReader.makeDirect(source).read(dest, chars);
 926    }
 927   
 928    /**
 929    * Copy the given number of chars from {@code in} to {@code out}, using the given buffer.
 930    * @return The number of chars written, or {@code -1} if the end of stream has already been reached.
 931    * May be less than {@code chars} if {@code source} does not provide all the requested data.
 932    * @throws IOException If an IOException occurs during reading or writing
 933    */
 934  0 public static int writeFromReader(Reader source, Writer dest, int chars, char[] buffer) throws IOException {
 935  0 return WrappedDirectReader.makeDirect(source).read(dest, chars, buffer);
 936    }
 937   
 938    /**
 939    * Write the contents of {@code in} to {@code out}. Processing will continue (or block) until
 940    * the end of stream is reached.
 941    * @return The number of bytes written, or {@code -1} if the end of stream has already been reached,
 942    * or {@code Integer.MAX_VALUE} if the number cannot be represented as an {@code int}.
 943    * @throws IOException If an IOException occurs during reading or writing
 944    */
 945  45 public static int copyInputStream(InputStream source, OutputStream dest) throws IOException {
 946  45 return WrappedDirectInputStream.makeDirect(source).readAll(dest);
 947    }
 948   
 949    /**
 950    * Write the contents of {@code in} to {@code out}, using the given buffer. Processing will
 951    * continue (or block) until the end of stream is reached.
 952    * @return The number of bytes written, or {@code -1} if the end of stream has already been reached,
 953    * or {@code Integer.MAX_VALUE} if the number cannot be represented as an {@code int}.
 954    * @throws IOException If an IOException occurs during reading or writing
 955    */
 956  0 public static int copyInputStream(InputStream source, OutputStream dest, byte[] buffer) throws IOException {
 957  0 return WrappedDirectInputStream.makeDirect(source).readAll(dest, buffer);
 958    }
 959   
 960    /**
 961    * Copy the given number of bytes from {@code in} to {@code out}.
 962    * @return The number of bytes written, or {@code -1} if the end of stream has already been reached.
 963    * May be less than {@code bytes} if {@code source} does not provide all the requested data.
 964    * @throws IOException If an IOException occurs during reading or writing
 965    */
 966  0 public static int writeFromInputStream(InputStream source, OutputStream dest, int bytes) throws IOException {
 967  0 return WrappedDirectInputStream.makeDirect(source).read(dest, bytes);
 968    }
 969   
 970    /**
 971    * Copy the given number of bytes from {@code in} to {@code out}, using the given buffer.
 972    * @return The number of bytes written, or {@code -1} if the end of stream has already been reached.
 973    * May be less than {@code bytes} if {@code source} does not provide all the requested data.
 974    * @throws IOException If an IOException occurs during reading or writing
 975    */
 976  0 public static int writeFromInputStream(InputStream source, OutputStream dest, int bytes,
 977    byte[] buffer) throws IOException {
 978  0 return WrappedDirectInputStream.makeDirect(source).read(dest, bytes, buffer);
 979    }
 980   
 981    /**
 982    * Implementation of reader-to-writer copying for use by {@link DirectReader} and {@link DirectWriter}
 983    * (placed here to avoid code duplication). The method will block until an end-of-file is reached.
 984    *
 985    * @return {@code -1} if the reader is at the end of file; otherwise, the number of characters read
 986    * (or, if the number is too large, {@code Integer.MAX_VALUE})
 987    * @throws IOException If an error occurs during reading or writing
 988    * @throws IllegalArgumentException If {@code buffer} has size {@code 0}
 989    */
 990  0 protected static int doCopyReader(Reader r, Writer w, char[] buffer) throws IOException {
 991  0 if (buffer.length == 0) { throw new IllegalArgumentException(); }
 992  0 int charsRead = r.read(buffer);
 993  0 if (charsRead == -1) { return -1; }
 994    else {
 995  0 int totalCharsRead = 0;
 996  0 do {
 997  0 totalCharsRead += charsRead;
 998  0 if (totalCharsRead < 0) { totalCharsRead = Integer.MAX_VALUE; }
 999  0 w.write(buffer, 0, charsRead);
 1000  0 charsRead = r.read(buffer);
 1001  0 } while (charsRead != -1);
 1002  0 return totalCharsRead;
 1003    }
 1004    }
 1005   
 1006    /**
 1007    * Implementation of stream copying for use by {@link DirectInputStream} and {@link DirectOutputStream}
 1008    * (placed here to avoid code duplication). The method will block until an end-of-file is reached.
 1009    *
 1010    * @return {@code -1} if the reader is at the end of file; otherwise, the number of characters read
 1011    * (or, if the number is too large, {@code Integer.MAX_VALUE})
 1012    * @throws IOException If an error occurs during reading or writing
 1013    * @throws IllegalArgumentException If {@code buffer} has size {@code 0}
 1014    */
 1015  50 protected static int doCopyInputStream(InputStream in, OutputStream out, byte[] buffer) throws IOException {
 1016  0 if (buffer.length == 0) { throw new IllegalArgumentException(); }
 1017  50 int charsRead = in.read(buffer);
 1018  0 if (charsRead == -1) { return -1; }
 1019    else {
 1020  50 int totalCharsRead = 0;
 1021  50 do {
 1022  82 totalCharsRead += charsRead;
 1023  0 if (totalCharsRead < 0) { totalCharsRead = Integer.MAX_VALUE; }
 1024  82 out.write(buffer, 0, charsRead);
 1025  82 charsRead = in.read(buffer);
 1026  82 } while (charsRead != -1);
 1027  50 return totalCharsRead;
 1028    }
 1029    }
 1030   
 1031    /**
 1032    * Implementation of reader-to-writer writing for use by {@link DirectReader} and {@link DirectWriter}
 1033    * (placed here to avoid code duplication).
 1034    *
 1035    * @return {@code -1} if this reader is at the end of file; otherwise, the number of characters read
 1036    * @throws IOException If an error occurs during reading or writing
 1037    * @throws IllegalArgumentException If {@code buffer} has size {@code 0}
 1038    */
 1039  0 protected static int doWriteFromReader(Reader r, Writer w, int chars, char[] buffer) throws IOException {
 1040  0 if (buffer.length == 0 && chars > 0) { throw new IllegalArgumentException(); }
 1041  0 int charsRead = r.read(buffer, 0, (chars < buffer.length) ? chars : buffer.length);
 1042  0 if (charsRead == -1) { return -1; }
 1043    else {
 1044  0 int totalCharsRead = 0;
 1045  0 while (chars > 0 && charsRead > 0) {
 1046  0 totalCharsRead += charsRead;
 1047  0 chars -= charsRead;
 1048  0 w.write(buffer, 0, charsRead);
 1049  0 if (chars > 0) { charsRead = r.read(buffer, 0, (chars < buffer.length) ? chars : buffer.length); }
 1050    }
 1051  0 return totalCharsRead;
 1052    }
 1053    }
 1054   
 1055    /**
 1056    * Implementation of stream-to-stream writing for use by {@link DirectInputStream} and
 1057    * {@link DirectOutputStream} (placed here to avoid code duplication).
 1058    *
 1059    * @return {@code -1} if this reader is at the end of file; otherwise, the number of characters read
 1060    * @throws IOException If an error occurs during reading or writing
 1061    * @throws IllegalArgumentException If {@code buffer} has size {@code 0}
 1062    */
 1063  0 protected static int doWriteFromInputStream(InputStream in, OutputStream out, int bytes,
 1064    byte[] buffer) throws IOException {
 1065  0 if (buffer.length == 0 && bytes > 0) { throw new IllegalArgumentException(); }
 1066  0 int bytesRead = in.read(buffer, 0, (bytes < buffer.length) ? bytes : buffer.length);
 1067  0 if (bytesRead == -1) { return -1; }
 1068    else {
 1069  0 int totalBytesRead = 0;
 1070  0 while (bytes > 0 && bytesRead > 0) {
 1071  0 totalBytesRead += bytesRead;
 1072  0 bytes -= bytesRead;
 1073  0 out.write(buffer, 0, bytesRead);
 1074  0 if (bytes > 0) { bytesRead = in.read(buffer, 0, (bytes < buffer.length) ? bytes : buffer.length); }
 1075    }
 1076  0 return totalBytesRead;
 1077    }
 1078    }
 1079   
 1080    /**
 1081    * Create a byte array with the contents of the given stream. The method will not return
 1082    * until an end of stream has been reached.
 1083    */
 1084  45 public static byte[] toByteArray(InputStream stream) throws IOException {
 1085  45 ByteArrayOutputStream out = new ByteArrayOutputStream();
 1086  45 try {
 1087  45 copyInputStream(stream, out);
 1088  45 return out.toByteArray();
 1089    }
 1090  45 finally { out.close(); }
 1091    }
 1092   
 1093    /**
 1094    * Create a StringBuffer with the contents of the given {@code Reader}. The method will not return
 1095    * until an end of stream has been reached. (We use a StringBuffer rather than a
 1096    * StringBuilder because that is what {@link StringWriter} supports.)
 1097    */
 1098  0 public static StringBuffer toStringBuffer(Reader r) throws IOException {
 1099  0 StringWriter out = new StringWriter();
 1100  0 try {
 1101  0 copyReader(r, out);
 1102  0 return out.getBuffer();
 1103    }
 1104  0 finally { out.close(); }
 1105    }
 1106   
 1107    /**
 1108    * Create a String with the contents of the given {@code Reader}. The method will not return
 1109    * until an end of stream has been reached.
 1110    */
 1111  0 public static String toString(Reader r) throws IOException {
 1112  0 return toStringBuffer(r).toString();
 1113    }
 1114   
 1115    /**
 1116    * Get the contents of a reader as a sequence of lines, accessed via an iterator. Lines are
 1117    * broken as determined by {@link BufferedReader#readLine}. The Reader is closed once iteration
 1118    * has been completed. The result iterator's {@code next()} method throws a {@link WrappedException}
 1119    * wrapping an {@link IOException} if an error occurs while reading.
 1120    * @throws IOException If an error occurs when reading the first line (for lookahead).
 1121    */
 1122  0 public static Iterator<String> readLines(Reader r) throws IOException {
 1123  0 final BufferedReader br = asBuffered(r);
 1124  0 final String firstLine = br.readLine();
 1125  0 if (firstLine == null) { br.close(); }
 1126  0 return new ReadOnlyIterator<String>() {
 1127    String lookahead = firstLine;
 1128  0 public boolean hasNext() { return lookahead != null; }
 1129  0 public String next() {
 1130  0 if (lookahead == null) { throw new NoSuchElementException(); }
 1131  0 try {
 1132  0 String result = lookahead;
 1133  0 lookahead = br.readLine();
 1134  0 if (lookahead == null) { br.close(); }
 1135  0 return result;
 1136    }
 1137  0 catch (IOException e) { throw new WrappedException(e); }
 1138    }
 1139    };
 1140    }
 1141   
 1142    /**
 1143    * Produce an Adler-32 hash for the contents of the given stream. The result is an int (32 bits).
 1144    * The method will not return until an end of stream has been reached.
 1145    */
 1146  0 public static int adler32Hash(InputStream stream) throws IOException {
 1147  0 ChecksumOutputStream out = ChecksumOutputStream.makeAdler32();
 1148  0 try {
 1149  0 copyInputStream(stream, out);
 1150  0 return (int) out.getValue();
 1151    }
 1152  0 finally { out.close(); }
 1153    }
 1154   
 1155    /**
 1156    * Produce a CRC-32 hash for the contents of the given stream. The result is an int (32 bits).
 1157    * The method will not return until an end of stream has been reached.
 1158    */
 1159  0 public static int crc32Hash(InputStream stream) throws IOException {
 1160  0 ChecksumOutputStream out = ChecksumOutputStream.makeCRC32();
 1161  0 try {
 1162  0 copyInputStream(stream, out);
 1163  0 return (int) out.getValue();
 1164    }
 1165  0 finally { out.close(); }
 1166    }
 1167   
 1168    /**
 1169    * Produce an MD5 hash for the contents of the given stream. The result is 16 bytes (128 bits) long.
 1170    * The method will not return until an end of stream has been reached.
 1171    */
 1172  0 public static byte[] md5Hash(InputStream stream) throws IOException {
 1173  0 MessageDigestOutputStream out = MessageDigestOutputStream.makeMD5();
 1174  0 try {
 1175  0 copyInputStream(stream, out);
 1176  0 return out.digest();
 1177    }
 1178  0 finally { out.close(); }
 1179    }
 1180   
 1181    /**
 1182    * Produce an SHA-1 hash for the contents of the given stream. The result is 20 bytes (160 bits) long.
 1183    * The method will not return until an end of stream has been reached.
 1184    */
 1185  0 public static byte[] sha1Hash(InputStream stream) throws IOException {
 1186  0 MessageDigestOutputStream out = MessageDigestOutputStream.makeSHA1();
 1187  0 try {
 1188  0 copyInputStream(stream, out);
 1189  0 return out.digest();
 1190    }
 1191  0 finally { out.close(); }
 1192    }
 1193   
 1194    /**
 1195    * Produce an SHA-256 hash for the contents of the given stream. The result is 32 bytes (256 bits) long.
 1196    * The method will not return until an end of stream has been reached.
 1197    */
 1198  0 public static byte[] sha256Hash(InputStream stream) throws IOException {
 1199  0 MessageDigestOutputStream out = MessageDigestOutputStream.makeSHA256();
 1200  0 try {
 1201  0 copyInputStream(stream, out);
 1202  0 return out.digest();
 1203    }
 1204  0 finally { out.close(); }
 1205    }
 1206   
 1207    /** If {@code r} is a {@code BufferedReader}, cast it as such; otherwise, wrap it in a {@code BufferedReader}. */
 1208  0 public static BufferedReader asBuffered(Reader r) {
 1209  0 if (r instanceof BufferedReader) { return (BufferedReader) r; }
 1210  0 else { return new BufferedReader(r); }
 1211    }
 1212   
 1213    /** If {@code w} is a {@code BufferedWriter}, cast it as such; otherwise, wrap it in a {@code BufferedWriter}. */
 1214  0 public static BufferedWriter asBuffered(Writer w) {
 1215  0 if (w instanceof BufferedWriter) { return (BufferedWriter) w; }
 1216  0 else { return new BufferedWriter(w); }
 1217    }
 1218   
 1219    /**
 1220    * If {@code in} is a {@code BufferedInputStream}, cast it as such; otherwise, wrap it in a
 1221    * {@code BufferedInputStream}.
 1222    */
 1223  6 public static BufferedInputStream asBuffered(InputStream in) {
 1224  6 if (in instanceof BufferedInputStream) { return (BufferedInputStream) in; }
 1225  0 else { return new BufferedInputStream(in); }
 1226    }
 1227   
 1228    /**
 1229    * If {@code out} is a {@code BufferedOutputStream}, cast it as such; otherwise, wrap it in a
 1230    * {@code BufferedOutputStream}.
 1231    */
 1232  0 public static BufferedOutputStream asBuffered(OutputStream out) {
 1233  0 if (out instanceof BufferedOutputStream) { return (BufferedOutputStream) out; }
 1234  0 else { return new BufferedOutputStream(out); }
 1235    }
 1236   
 1237   
 1238    private static final Thunk<List<Closeable>> TO_CLOSE = LazyThunk.make(new Thunk<List<Closeable>>() {
 1239  1 public List<Closeable> value() {
 1240    // On the first request, register a shutdown hook to clean up the list
 1241  1 Runtime.getRuntime().addShutdownHook(new Thread() {
 1242  1 public void run() {
 1243  5 for (Closeable c : TO_CLOSE.value()) { attemptClose(c); }
 1244    }
 1245    });
 1246  1 return new LinkedList<Closeable>();
 1247    }
 1248    });
 1249   
 1250    /**
 1251    * Register the given resource to be closed on exit. {@link Closeable#close} will be invoked from a
 1252    * shutdown hook.
 1253    */
 1254  7 public static void closeOnExit(Closeable c) {
 1255  7 TO_CLOSE.value().add(c);
 1256    }
 1257   
 1258    /** Attempt to close the given resource, failing silently if an exception occurs. */
 1259  13 public static void attemptClose(Closeable c) {
 1260  13 try { c.close(); }
 1261    catch (IOException e) { /* intentionally ignore */ }
 1262    }
 1263   
 1264    /** Define a {@code FileFilter} in terms of a {@code Predicate}. */
 1265  42 public static FilePredicate asFilePredicate(Predicate<? super File> p) {
 1266  42 return new PredicateFilePredicate(p);
 1267    }
 1268   
 1269    private static final class PredicateFilePredicate implements FilePredicate, Serializable {
 1270    private final Predicate<? super File> _p;
 1271  42 public PredicateFilePredicate(Predicate<? super File> p) { _p = p; }
 1272  0 public boolean accept(File f) { return _p.contains(f); }
 1273  0 public boolean contains(File f) { return _p.contains(f); }
 1274    }
 1275   
 1276    /**
 1277    * Define a {@code FilePredicate} in terms of a {@code FileFilter} (this provides access
 1278    * to predicate operations like {@code and} and {@code or}).
 1279    */
 1280  0 public static FilePredicate asFilePredicate(FileFilter filter) {
 1281  0 return new FileFilterFilePredicate(filter);
 1282    }
 1283   
 1284    private static final class FileFilterFilePredicate implements FilePredicate, Serializable {
 1285    private final FileFilter _filter;
 1286  0 public FileFilterFilePredicate(FileFilter filter) { _filter = filter; }
 1287  0 public boolean accept(File f) { return _filter.accept(f); }
 1288  0 public boolean contains(File f) { return _filter.accept(f); }
 1289    }
 1290   
 1291    /**
 1292    * Define a {@code FilePredicate} that accepts files whose (simple) names match
 1293    * a regular expression.
 1294    */
 1295  0 public static FilePredicate regexFilePredicate(String regex) {
 1296  0 return new RegexFilePredicate(regex);
 1297    }
 1298   
 1299    /**
 1300    * Define a {@code FilePredicate} that accepts files whose (simple) names match
 1301    * a regular expression.
 1302    */
 1303  0 public static FilePredicate regexFilePredicate(Pattern regex) {
 1304  0 return new RegexFilePredicate(regex);
 1305    }
 1306   
 1307    private static final class RegexFilePredicate implements FilePredicate, Serializable {
 1308    private final Pattern _regex;
 1309  0 public RegexFilePredicate(String regex) { _regex = Pattern.compile(regex); }
 1310  0 public RegexFilePredicate(Pattern regex) { _regex = regex; }
 1311  0 public boolean accept(File f) { return _regex.matcher(f.getName()).matches(); }
 1312  0 public boolean contains(File f) { return _regex.matcher(f.getName()).matches(); }
 1313    }
 1314   
 1315    /**
 1316    * Define a {@code FilePredicate} that accepts files whose (simple) names in the
 1317    * canonical case (see {@link #canonicalCase}) match a regular expression.
 1318    */
 1319  0 public static FilePredicate regexCanonicalCaseFilePredicate(String regex) {
 1320  0 return new RegexCanonicalCaseFilePredicate(regex);
 1321    }
 1322   
 1323    private static final class RegexCanonicalCaseFilePredicate implements FilePredicate, Serializable {
 1324    private final Pattern _regex;
 1325  1 public RegexCanonicalCaseFilePredicate(String regex) { _regex = Pattern.compile(regex); }
 1326  0 public RegexCanonicalCaseFilePredicate(Pattern regex) { _regex = regex; }
 1327  7 public boolean accept(File f) { return _regex.matcher(canonicalCase(f).getName()).matches(); }
 1328  7 public boolean contains(File f) { return _regex.matcher(canonicalCase(f).getName()).matches(); }
 1329    }
 1330   
 1331    /**
 1332    * Define a {@code FilePredicate} that accepts file objects with the given extension (that is,
 1333    * for extension {@code txt}, file objects whose canonical-case names (see {@link #canonicalCase})
 1334    * end in {@code .txt}).
 1335    */
 1336  1 public static FilePredicate extensionFilePredicate(String extension) {
 1337  1 return new RegexCanonicalCaseFilePredicate(".*\\." + canonicalCase(new File(extension)).getName());
 1338    }
 1339   
 1340    /**
 1341    * Define a {@code FilePredicate} that only accepts files with the given name (where both names
 1342    * are converted to the canonical case; see {@link #canonicalCase}).
 1343    */
 1344  0 public static FilePredicate sameNameFilePredicate(String name) {
 1345  0 return new SamePathFilePredicate(new File(name));
 1346    }
 1347   
 1348    /**
 1349    * Define a {@code FilePredicate} that only accepts files with the given path and name (where
 1350    * both paths are converted to the canonical case; see {@link #canonicalCase}). If {@code path} is
 1351    * relative, any file with an absolute path that ends with {@code path} will be accepted.
 1352    * Otherwise, only a file with the exact same path is accepted.
 1353    */
 1354  0 public static FilePredicate samePathFilePredicate(File path) {
 1355  0 return new SamePathFilePredicate(path);
 1356    }
 1357   
 1358    private static final class SamePathFilePredicate implements FilePredicate, Serializable {
 1359    private final File _f;
 1360  0 public SamePathFilePredicate(File f) { _f = canonicalCase(f); }
 1361  0 public boolean accept(File f) {
 1362  0 File candidate = canonicalCase(attemptAbsoluteFile(f));
 1363  0 for (File compareTo = _f; compareTo != null; compareTo = compareTo.getParentFile()) {
 1364  0 if (candidate == null || !compareTo.getName().equals(candidate.getName())) {
 1365  0 return false;
 1366    }
 1367  0 candidate = candidate.getParentFile();
 1368    }
 1369  0 return true;
 1370    }
 1371  0 public boolean contains(File f) { return accept(f); }
 1372    }
 1373   
 1374    /**
 1375    * Define a {@code FilePredicate} that only accepts a file with the same attributes as
 1376    * {@code f} (at creation time). This is useful in detecting changes being made to a file.
 1377    * The files' modification dates, lengths, and read/write permissions are compared.
 1378    * @throws FileNotFoundException If {@code f} is not a normal file, or if access to its attributes
 1379    * is not available.
 1380    */
 1381  0 public static FilePredicate sameAttributesFilePredicate(File f) throws FileNotFoundException {
 1382  0 return new SameAttributesFilePredicate(f);
 1383    }
 1384   
 1385    private static final class SameAttributesFilePredicate implements FilePredicate, Serializable {
 1386    private final long _lastModified;
 1387    private final long _length;
 1388    private final boolean _canRead;
 1389    private final boolean _canWrite;
 1390   
 1391  0 public SameAttributesFilePredicate(File f) throws FileNotFoundException {
 1392  0 try {
 1393  0 if (!f.isFile()) { throw new FileNotFoundException(f + " is not a valid file"); }
 1394  0 _lastModified = f.lastModified();
 1395  0 if (_lastModified == 0l) {
 1396  0 throw new FileNotFoundException("Can't get valid modification date for " + f);
 1397    }
 1398  0 _length = f.length();
 1399  0 _canRead = f.canRead();
 1400  0 _canWrite = f.canWrite();
 1401    }
 1402  0 catch (SecurityException e) { throw new FileNotFoundException(e.getMessage()); }
 1403    }
 1404   
 1405  0 public boolean accept(File f) {
 1406  0 try {
 1407  0 return f.isFile() && f.lastModified() == _lastModified && f.length() == _length &&
 1408    f.canRead() == _canRead && f.canWrite() == _canWrite;
 1409    }
 1410  0 catch (SecurityException e) { return false; }
 1411    }
 1412   
 1413  0 public boolean contains(File f) { return accept(f); }
 1414    }
 1415   
 1416    /**
 1417    * Define a {@code FilePredicate} that only accepts files with contents matching a CRC-32
 1418    * hash of {@code f} (at creation time). This is useful in detecting changes being made to
 1419    * a file.
 1420    * @throws IOException If {@code f} cannot be read.
 1421    */
 1422  0 public static FilePredicate sameContentsFilePredicate(File f) throws IOException {
 1423  0 return new SameContentsFilePredicate(f);
 1424    }
 1425   
 1426    private static final class SameContentsFilePredicate implements FilePredicate, Serializable {
 1427    private final long _length;
 1428    private final int _hash;
 1429  0 public SameContentsFilePredicate(File f) throws IOException {
 1430    // as an optimization, try to check the length before using a hash, but
 1431    // don't let failures result in an exception, since it's only an optimization
 1432  0 _length = attemptLength(f);
 1433  0 _hash = crc32Hash(f);
 1434    }
 1435  0 public boolean accept(File f) {
 1436  0 long fLength = attemptLength(f);
 1437  0 if (fLength > 0l && _length > 0l && fLength != _length) { return false; }
 1438  0 try { return _hash == crc32Hash(f); }
 1439  0 catch (IOException e) { return false; }
 1440    }
 1441  0 public boolean contains(File f) { return accept(f); }
 1442    }
 1443   
 1444    /** A predicate that tests whether {@link #attemptIsFile} holds for a file. */
 1445    public static final FilePredicate IS_FILE = new IsFileFilePredicate();
 1446   
 1447    private static final class IsFileFilePredicate implements FilePredicate, Serializable {
 1448  0 public boolean accept(File f) { return attemptIsFile(f); }
 1449  0 public boolean contains(File f) { return attemptIsFile(f); }
 1450    }
 1451   
 1452    /** A predicate that tests whether {@link #attemptIsDirectory} holds for a file. */
 1453    public static final FilePredicate IS_DIRECTORY = new IsDirectoryFilePredicate();
 1454   
 1455    private static final class IsDirectoryFilePredicate implements FilePredicate, Serializable {
 1456  0 public boolean accept(File f) { return attemptIsDirectory(f); }
 1457  0 public boolean contains(File f) { return attemptIsDirectory(f); }
 1458    }
 1459   
 1460    /** A {@code FilePredicate} that always accepts. */
 1461    public static final FilePredicate ALWAYS_ACCEPT = asFilePredicate(LambdaUtil.TRUE);
 1462   
 1463    /** A {@code FilePredicate} that always rejects. */
 1464    public static final FilePredicate ALWAYS_REJECT = asFilePredicate(LambdaUtil.FALSE);
 1465   
 1466    /** Produce a conjunction of the given FileFilters. */
 1467  0 public static FilePredicate and(FileFilter... filters) {
 1468  0 return new AndFilePredicate(IterUtil.asIterable(filters));
 1469    }
 1470   
 1471    /** Produce a conjunction of the given FileFilters. */
 1472  0 public static FilePredicate and(Iterable<? extends FileFilter> filters) {
 1473  0 return new AndFilePredicate(filters);
 1474    }
 1475   
 1476    private static final class AndFilePredicate implements FilePredicate, Serializable {
 1477    private final Iterable<? extends FileFilter> _filters;
 1478  0 public AndFilePredicate(Iterable<? extends FileFilter> filters) { _filters = filters; }
 1479  0 public boolean accept(File f) {
 1480  0 for (FileFilter filter : _filters) {
 1481  0 if (!filter.accept(f)) { return false; }
 1482    }
 1483  0 return true;
 1484    }
 1485  0 public boolean contains(File f) { return accept(f); }
 1486    }
 1487   
 1488    /** Produce a disjunction of the given FileFilters. */
 1489  0 public static FilePredicate or(FileFilter... filters) {
 1490  0 return new OrFilePredicate(IterUtil.asIterable(filters));
 1491    }
 1492   
 1493    /** Produce a disjunction of the given FileFilters. */
 1494  0 public static FilePredicate or(Iterable<? extends FileFilter> filters) {
 1495  0 return new OrFilePredicate(filters);
 1496    }
 1497   
 1498    private static final class OrFilePredicate implements FilePredicate, Serializable {
 1499    private final Iterable<? extends FileFilter> _filters;
 1500  0 public OrFilePredicate(Iterable<? extends FileFilter> filters) { _filters = filters; }
 1501  0 public boolean accept(File f) {
 1502  0 for (FileFilter filter : _filters) {
 1503  0 if (filter.accept(f)) { return true; }
 1504    }
 1505  0 return false;
 1506    }
 1507  0 public boolean contains(File f) { return accept(f); }
 1508    }
 1509   
 1510    /** Produce the complement of the given FileFilter. */
 1511  0 public static FilePredicate negate(FileFilter filter) {
 1512  0 return new NegationFilePredicate(filter);
 1513    }
 1514   
 1515    private static final class NegationFilePredicate implements FilePredicate, Serializable {
 1516    private final FileFilter _filter;
 1517  0 public NegationFilePredicate(FileFilter filter) { _filter = filter; }
 1518  0 public boolean accept(File f) { return !_filter.accept(f); }
 1519  0 public boolean contains(File f) { return !_filter.accept(f); }
 1520    }
 1521   
 1522    /**
 1523    * Produce a {@code FilePredicate} that acts as a "key" representing the current state of
 1524    * the given file. A file will match only if it has the same name, absolute path, attributes,
 1525    * and contents as {@code f}. This is useful for detecting background changes to a file.
 1526    * @throws IOException If the given file does not exist, is not a normal file, or does
 1527    * not allow its attributes or contents to be accessed.
 1528    */
 1529  0 public static FilePredicate fileKey(File f) throws IOException {
 1530  0 return and(samePathFilePredicate(attemptAbsoluteFile(f)),
 1531    sameAttributesFilePredicate(f),
 1532    sameContentsFilePredicate(f));
 1533    }
 1534   
 1535   
 1536    private static final LinkedList<PrintStream> SYSTEM_OUT_STACK = new LinkedList<PrintStream>();
 1537    private static final LinkedList<PrintStream> SYSTEM_ERR_STACK = new LinkedList<PrintStream>();
 1538    private static final LinkedList<InputStream> SYSTEM_IN_STACK = new LinkedList<InputStream>();
 1539   
 1540    /**
 1541    * Replace {@code System.out} with the given stream and remember the current stream. This call
 1542    * should always be matched by a subsequent call to {@link #revertSystemOut}.
 1543    */
 1544  8 public static void replaceSystemOut(OutputStream substitute) {
 1545  8 SYSTEM_OUT_STACK.addLast(System.out);
 1546  0 if (substitute instanceof PrintStream) { System.setOut((PrintStream) substitute); }
 1547  8 else { System.setOut(new PrintStream(substitute)); }
 1548    }
 1549   
 1550    /**
 1551    * Ignore subsequent writes to {@code System.out} until {@link #revertSystemOut} is called.
 1552    * A matching revert call should be made.
 1553    */
 1554  8 public static void ignoreSystemOut() {
 1555  8 replaceSystemOut(VoidOutputStream.INSTANCE);
 1556    }
 1557   
 1558    /**
 1559    * Set {@code System.out} to its value before the last call to {@link #replaceSystemOut}. This call
 1560    * should always follow a call to {@code replaceSystemOut()}. Assuming all calls are properly
 1561    * paired, and that multiple threads do not concurrently invoke these methods, the stream after this
 1562    * call will be the stream that was replaced by {@code replaceSystemOut()}.
 1563    */
 1564  0 public static void revertSystemOut() {
 1565  0 if (SYSTEM_OUT_STACK.isEmpty()) { error.logStack("Unbalanced call to revertSystemOut"); }
 1566  0 else { System.setOut(SYSTEM_OUT_STACK.removeLast()); }
 1567    }
 1568   
 1569    /**
 1570    * Replace {@code System.err} with the given stream and remember the current stream. This call
 1571    * should always be matched by a subsequent call to {@link #revertSystemErr}.
 1572    */
 1573  8 public static void replaceSystemErr(OutputStream substitute) {
 1574  8 SYSTEM_ERR_STACK.addLast(System.err);
 1575  0 if (substitute instanceof PrintStream) { System.setErr((PrintStream) substitute); }
 1576  8 else { System.setErr(new PrintStream(substitute)); }
 1577    }
 1578   
 1579    /**
 1580    * Ignore subsequent writes to {@code System.err} until {@link #revertSystemErr} is called. A
 1581    * matching revert call should be made.
 1582    */
 1583  8 public static void ignoreSystemErr() {
 1584  8 replaceSystemErr(VoidOutputStream.INSTANCE);
 1585    }
 1586   
 1587    /**
 1588    * Set {@code System.err} to its value before the last call to {@link #replaceSystemErr}. This call
 1589    * should always follow a call to {@code replaceSystemErr()}. Assuming all calls are properly
 1590    * paired, and that multiple threads do not concurrently invoke these methods, the stream after this
 1591    * call will be the stream that was replaced by {@code replaceSystemErr()}.
 1592    */
 1593  0 public static void revertSystemErr() {
 1594  0 if (SYSTEM_ERR_STACK.isEmpty()) { error.logStack("Unbalanced call to revertSystemErr"); }
 1595  0 else { System.setErr(SYSTEM_ERR_STACK.removeLast()); }
 1596    }
 1597   
 1598    /**
 1599    * Replace {@code System.in} with the given stream and remember the current stream. This call
 1600    * should always be matched by a subsequent call to {@link #revertSystemIn}.
 1601    */
 1602  0 public static void replaceSystemIn(InputStream substitute) {
 1603  0 SYSTEM_IN_STACK.addLast(System.in);
 1604  0 System.setIn(substitute);
 1605    }
 1606   
 1607    /**
 1608    * Set {@code System.in} to its value before the last call to {@link #replaceSystemIn}. This call
 1609    * should always follow a call to {@code replaceSystemIn()}. Assuming all calls are properly
 1610    * paired, and that multiple threads do not concurrently invoke these methods, the stream after this
 1611    * call will be the stream that was replaced by {@code replaceSystemIn()}.
 1612    */
 1613  0 public static void revertSystemIn() {
 1614  0 if (SYSTEM_IN_STACK.isEmpty()) { error.logStack("Unbalanced call to revertSystemIn"); }
 1615  0 else { System.setIn(SYSTEM_IN_STACK.removeLast()); }
 1616    }
 1617   
 1618   
 1619    /**
 1620    * Classes that are known to always be serializable. (No guarantees can be made about subclasses of these,
 1621    * as subclasses can introduce arbitrary fields.)
 1622    */
 1623    private static final Set<Class<?>> SERIALIZABLE_CLASSES = new HashSet<Class<?>>();
 1624    static {
 1625    // Java API classes
 1626  21 SERIALIZABLE_CLASSES.add(String.class);
 1627  21 SERIALIZABLE_CLASSES.add(Boolean.class);
 1628  21 SERIALIZABLE_CLASSES.add(Character.class);
 1629  21 SERIALIZABLE_CLASSES.add(Byte.class);
 1630  21 SERIALIZABLE_CLASSES.add(Short.class);
 1631  21 SERIALIZABLE_CLASSES.add(Integer.class);
 1632  21 SERIALIZABLE_CLASSES.add(Long.class);
 1633  21 SERIALIZABLE_CLASSES.add(Float.class);
 1634  21 SERIALIZABLE_CLASSES.add(Double.class);
 1635  21 SERIALIZABLE_CLASSES.add(Date.class);
 1636  21 SERIALIZABLE_CLASSES.add(File.class);
 1637  21 SERIALIZABLE_CLASSES.add(StackTraceElement.class);
 1638   
 1639    // PLT classes
 1640  21 SERIALIZABLE_CLASSES.add(ThreadSnapshot.class);
 1641  21 SERIALIZABLE_CLASSES.add(Null.class);
 1642   
 1643    // primitive arrays
 1644  21 SERIALIZABLE_CLASSES.add(boolean[].class);
 1645  21 SERIALIZABLE_CLASSES.add(char[].class);
 1646  21 SERIALIZABLE_CLASSES.add(byte[].class);
 1647  21 SERIALIZABLE_CLASSES.add(short[].class);
 1648  21 SERIALIZABLE_CLASSES.add(int[].class);
 1649  21 SERIALIZABLE_CLASSES.add(long[].class);
 1650  21 SERIALIZABLE_CLASSES.add(float[].class);
 1651  21 SERIALIZABLE_CLASSES.add(double[].class);
 1652    }
 1653   
 1654   
 1655    /**
 1656    * Converts the given object to a form that will successfully serialize. Typical serializable primitives like
 1657    * null, Strings and Files are left untouched; tuples, Iterables, Throwables, and arrays are processed recursively;
 1658    * and other types are handled by invoking {@code obj.toString()}. Note that subsequent (or concurrent) mutation
 1659    * of the object may prevent successful serialization.
 1660    */
 1661  12 public static Object ensureSerializable(Object obj) {
 1662  0 if (obj == null) { return null; }
 1663  12 else if (SERIALIZABLE_CLASSES.contains(obj.getClass())) { return obj; }
 1664  0 else if (obj instanceof Object[]) { return ensureSerializable((Object[]) obj); }
 1665  0 else if (obj instanceof Iterable<?>) { return ensureSerializable((Iterable<?>) obj); }
 1666  0 else if (obj instanceof Throwable) { return ensureSerializable((Throwable) obj); }
 1667  0 else if (obj instanceof Tuple) { return ensureSerializable((Tuple) obj); }
 1668  0 else { return obj.toString(); }
 1669    }
 1670   
 1671    /**
 1672    * Convert the given (non-null) array to an array of objects that will successfully serialize. If the type of
 1673    * the array guarantees this property, or if none of the current elements requires conversion, returns {@code arr}
 1674    * unchanged. Otherwise, makes a converted copy. Note that subsequent (or concurrent) mutation
 1675    * of the array may prevent successful serialization.
 1676    */
 1677  0 public static Object[] ensureSerializable(Object[] arr) {
 1678  0 Class<?> base = ReflectUtil.arrayBaseClass(arr.getClass());
 1679  0 if (SERIALIZABLE_CLASSES.contains(base) && Modifier.isFinal(base.getModifiers())) {
 1680    // if the base type is final and known to be safe, the array's type guarantees that it will
 1681    // never contain non-serializable elements
 1682  0 return arr;
 1683    }
 1684    else {
 1685  0 boolean keep = true;
 1686  0 Object[] result = new Object[arr.length];
 1687  0 for (int i = 0; i < arr.length; i++) {
 1688  0 result[i] = ensureSerializable(arr[i]);
 1689  0 keep &= (result[i] == arr[i]);
 1690    }
 1691  0 return keep ? arr : result;
 1692    }
 1693    }
 1694   
 1695    /**
 1696    * Convert the given (non-null) Iterable to a list of objects that will successfully serialize. Discards any
 1697    * problematic fields by copying the Iterable into a List (recursively converting the elements). Infinite Iterables
 1698    * are handled by truncating the list with a {@code "..."} string. Note that subsequent (or concurrent) mutation
 1699    * of the elements may prevent successful serialization.
 1700    */
 1701  0 public static Iterable<?> ensureSerializable(Iterable<?> iter) {
 1702  0 if (IterUtil.isInfinite(iter)) { iter = IterUtil.compose(IterUtil.truncate(iter, 8), "..."); }
 1703    // can't make an exhaustive list of types that are okay, but at least we shouldn't make a new copy when
 1704    // the method is invoked on its own result
 1705  0 boolean keep = iter.getClass().equals(ArrayList.class);
 1706  0 List<Object> result = new ArrayList<Object>();
 1707  0 for (Object elt : iter) {
 1708  0 Object safe = ensureSerializable(elt);
 1709  0 keep &= (elt == safe);
 1710  0 result.add(safe);
 1711    }
 1712  0 return keep ? iter : result;
 1713    }
 1714   
 1715    /**
 1716    * Convert the given Throwable to a form that will successfully serialize. If necessary, copies the throwable
 1717    * into a {@link SerializableException}. Note that subsequent (or concurrent) mutation of the cause may prevent
 1718    * successful serialization.
 1719    */
 1720  3 public static Throwable ensureSerializable(Throwable t) {
 1721  3 Throwable safeCause = (t.getCause() == null) ? null : ensureSerializable(t.getCause());
 1722  3 if (t.getCause() == safeCause && isSafeThrowableClass(t.getClass())) { return t; }
 1723  0 else { return new SerializableException(t, safeCause); }
 1724    }
 1725   
 1726    /**
 1727    * Convert the given Exception to a form that will successfully serialize. If necessary, copies the exception
 1728    * into a {@link SerializableException}. Note that subsequent (or concurrent) mutation of the cause may prevent
 1729    * successful serialization.
 1730    */
 1731  0 public static Exception ensureSerializable(Exception e) {
 1732  0 Throwable safeCause = (e.getCause() == null) ? null : ensureSerializable(e.getCause());
 1733  0 if (e.getCause() == safeCause && isSafeThrowableClass(e.getClass())) { return e; }
 1734  0 else { return new SerializableException(e, safeCause); }
 1735    }
 1736   
 1737    /**
 1738    * Convert the given RuntimeException to a form that will successfully serialize. If necessary, copies the
 1739    * exception into a {@link SerializableException}. Note that subsequent (or concurrent) mutation of the cause
 1740    * may prevent successful serialization.
 1741    */
 1742  0 public static RuntimeException ensureSerializable(RuntimeException e) {
 1743  0 Throwable safeCause = (e.getCause() == null) ? null : ensureSerializable(e.getCause());
 1744  0 if (e.getCause() == safeCause && isSafeThrowableClass(e.getClass())) { return e; }
 1745  0 else { return new SerializableException(e, safeCause); }
 1746    }
 1747   
 1748    /** Tests whether all fields in subclasses of Throwable have guaranteed serializable types. */
 1749  3 private static boolean isSafeThrowableClass(Class<?> c) {
 1750  3 try {
 1751  3 if (!c.getMethod("getCause").getDeclaringClass().equals(Throwable.class)) {
 1752    // getCause returns an arbitrary object; the actual value of the cause field may be hidden and unsafe.
 1753    // A lot of API classes (InvocationTargetException, RemoteException, others listed in the Throwable
 1754    // javadoc as having "non-standard exception chaining mechanisms") follow a convention of setting
 1755    // Throwable.cause to null, keeping their own cause field, and overriding getCause(). This is
 1756    // okay, but there's not a nice way to detect that convention, so such classes will be treated as
 1757    // unsafe. We could make a list of special cases, but that list could grow arbitrarily long...
 1758  0 return false;
 1759    }
 1760    }
 1761  0 catch (NoSuchMethodException e) { return false; }
 1762  0 catch (SecurityException e) { return false; }
 1763   
 1764  3 Class<?> parent = c;
 1765  3 while (!parent.equals(Throwable.class) && parent != null) {
 1766  6 for (Field f : parent.getDeclaredFields()) {
 1767  6 Class<?> fType = f.getType();
 1768  0 if (!fType.isPrimitive() && !SERIALIZABLE_CLASSES.contains(f.getType())) { return false; }
 1769    }
 1770  6 parent = parent.getSuperclass();
 1771    }
 1772  3 return true;
 1773    }
 1774   
 1775    /**
 1776    * Apply {@code ensureSerializable()} to each of the elements of the given tuple. If the elements
 1777    * are unchanged and the tuple's class is known to serialize safely, returns the tuple unchanged.
 1778    */
 1779  0 public static Tuple ensureSerializable(Tuple t) {
 1780  0 if (t instanceof Null) { return t; } // valid because Null is final
 1781  0 else if (t instanceof Wrapper<?>) { return ensureSerializable((Wrapper<?>) t); }
 1782  0 else if (t instanceof Pair<?,?>) { return ensureSerializable((Pair<?,?>) t); }
 1783  0 else if (t instanceof Triple<?,?,?>) { return ensureSerializable((Triple<?,?,?>) t); }
 1784  0 else if (t instanceof Quad<?,?,?,?>) { return ensureSerializable((Quad<?,?,?,?>) t); }
 1785  0 else if (t instanceof Quint<?,?,?,?,?>) { return ensureSerializable((Quint<?,?,?,?,?>) t); }
 1786  0 else if (t instanceof Sextet<?,?,?,?,?,?>) { return ensureSerializable((Sextet<?,?,?,?,?,?>) t); }
 1787  0 else if (t instanceof Septet<?,?,?,?,?,?,?>) { return ensureSerializable((Septet<?,?,?,?,?,?,?>) t); }
 1788  0 else if (t instanceof Octet<?,?,?,?,?,?,?,?>) { return ensureSerializable((Octet<?,?,?,?,?,?,?,?>) t); }
 1789  0 else { throw new IllegalArgumentException("Unrecognized tuple type: " + t.getClass().getName()); }
 1790    }
 1791   
 1792    /**
 1793    * Apply {@code ensureSerializable()} to the given option value. If the value is
 1794    * unchanged and the option's class is known to serialize safely, returns the option unchanged.
 1795    */
 1796  0 public static Option<?> ensureSerializable(Option<?> opt) {
 1797  0 if (opt instanceof Null) { return opt; } // valid because Null is final
 1798  0 else if (opt instanceof Wrapper<?>) { return ensureSerializable((Wrapper<?>) opt); }
 1799  0 else { throw new IllegalArgumentException("Unrecognized option type: " + opt.getClass().getName()); }
 1800    }
 1801   
 1802    /**
 1803    * Apply {@code ensureSerializable()} to the given wrapped value. If the value is
 1804    * unchanged and the wrapper's class is known to serialize safely, returns the wrapper unchanged.
 1805    */
 1806  0 public static Wrapper<?> ensureSerializable(Wrapper<?> w) {
 1807  0 Object safeVal = ensureSerializable(w.value());
 1808  0 if (w.getClass().equals(Wrapper.class) && w.value() == safeVal) { return w; }
 1809  0 else { return Wrapper.make(safeVal); }
 1810    }
 1811   
 1812    /**
 1813    * Apply {@code ensureSerializable()} to each of the elements of the given tuple. If the elements
 1814    * are unchanged and the tuple's class is known to serialize safely, returns the tuple unchanged.
 1815    */
 1816  0 public static Pair<?,?> ensureSerializable(Pair<?,?> p) {
 1817  0 Object safeFirst = ensureSerializable(p.first());
 1818  0 Object safeSecond = ensureSerializable(p.second());
 1819  0 if (p.getClass().equals(Pair.class) && p.first() == safeFirst && p.second() == safeSecond) { return p; }
 1820  0 else { return Pair.make(safeFirst, safeSecond); }
 1821    }
 1822   
 1823    /**
 1824    * Apply {@code ensureSerializable()} to each of the elements of the given tuple. If the elements
 1825    * are unchanged and the tuple's class is known to serialize safely, returns the tuple unchanged.
 1826    */
 1827  0 public static Triple<?,?,?> ensureSerializable(Triple<?,?,?> t) {
 1828  0 Object safeFirst = ensureSerializable(t.first());
 1829  0 Object safeSecond = ensureSerializable(t.second());
 1830  0 Object safeThird = ensureSerializable(t.third());
 1831  0 if (t.getClass().equals(Triple.class) &&
 1832    t.first() == safeFirst &&
 1833    t.second() == safeSecond &&
 1834    t.third() == safeThird) {
 1835  0 return t;
 1836    }
 1837  0 else { return Triple.make(safeFirst, safeSecond, safeThird); }
 1838    }
 1839   
 1840    /**
 1841    * Apply {@code ensureSerializable()} to each of the elements of the given tuple. If the elements
 1842    * are unchanged and the tuple's class is known to serialize safely, returns the tuple unchanged.
 1843    */
 1844  0 public static Quad<?,?,?,?> ensureSerializable(Quad<?,?,?,?> q) {
 1845  0 Object safeFirst = ensureSerializable(q.first());
 1846  0 Object safeSecond = ensureSerializable(q.second());
 1847  0 Object safeThird = ensureSerializable(q.third());
 1848  0 Object safeFourth = ensureSerializable(q.fourth());
 1849  0 if (q.getClass().equals(Quad.class) &&
 1850    q.first() == safeFirst &&
 1851    q.second() == safeSecond &&
 1852    q.third() == safeThird &&
 1853    q.fourth() == safeFourth) {
 1854  0 return q;
 1855    }
 1856  0 else { return Quad.make(safeFirst, safeSecond, safeThird, safeFourth); }
 1857    }
 1858   
 1859    /**
 1860    * Apply {@code ensureSerializable()} to each of the elements of the given tuple. If the elements
 1861    * are unchanged and the tuple's class is known to serialize safely, returns the tuple unchanged.
 1862    */
 1863  0 public static Quint<?,?,?,?,?> ensureSerializable(Quint<?,?,?,?,?> q) {
 1864  0 Object safeFirst = ensureSerializable(q.first());
 1865  0 Object safeSecond = ensureSerializable(q.second());
 1866  0 Object safeThird = ensureSerializable(q.third());
 1867  0 Object safeFourth = ensureSerializable(q.fourth());
 1868  0 Object safeFifth = ensureSerializable(q.fifth());
 1869  0 if (q.getClass().equals(Quint.class) &&
 1870    q.first() == safeFirst &&
 1871    q.second() == safeSecond &&
 1872    q.third() == safeThird &&
 1873    q.fourth() == safeFourth &&
 1874    q.fifth() == safeFifth) {
 1875  0 return q;
 1876    }
 1877  0 else { return Quint.make(safeFirst, safeSecond, safeThird, safeFourth, safeFifth); }
 1878    }
 1879   
 1880    /**
 1881    * Apply {@code ensureSerializable()} to each of the elements of the given tuple. If the elements
 1882    * are unchanged and the tuple's class is known to serialize safely, returns the tuple unchanged.
 1883    */
 1884  0 public static Sextet<?,?,?,?,?,?> ensureSerializable(Sextet<?,?,?,?,?,?> s) {
 1885  0 Object safeFirst = ensureSerializable(s.first());
 1886  0 Object safeSecond = ensureSerializable(s.second());
 1887  0 Object safeThird = ensureSerializable(s.third());
 1888  0 Object safeFourth = ensureSerializable(s.fourth());
 1889  0 Object safeFifth = ensureSerializable(s.fifth());
 1890  0 Object safeSixth = ensureSerializable(s.sixth());
 1891  0 if (s.getClass().equals(Quint.class) &&
 1892    s.first() == safeFirst &&
 1893    s.second() == safeSecond &&
 1894    s.third() == safeThird &&
 1895    s.fourth() == safeFourth &&
 1896    s.fifth() == safeFifth &&
 1897    s.sixth() == safeSixth) {
 1898  0 return s;
 1899    }
 1900  0 else { return Sextet.make(safeFirst, safeSecond, safeThird, safeFourth, safeFifth, safeSixth); }
 1901    }
 1902   
 1903    /**
 1904    * Apply {@code ensureSerializable()} to each of the elements of the given tuple. If the elements
 1905    * are unchanged and the tuple's class is known to serialize safely, returns the tuple unchanged.
 1906    */
 1907  0 public static Septet<?,?,?,?,?,?,?> ensureSerializable(Septet<?,?,?,?,?,?,?> s) {
 1908  0 Object safeFirst = ensureSerializable(s.first());
 1909  0 Object safeSecond = ensureSerializable(s.second());
 1910  0 Object safeThird = ensureSerializable(s.third());
 1911  0 Object safeFourth = ensureSerializable(s.fourth());
 1912  0 Object safeFifth = ensureSerializable(s.fifth());
 1913  0 Object safeSixth = ensureSerializable(s.sixth());
 1914  0 Object safeSeventh = ensureSerializable(s.seventh());
 1915  0 if (s.getClass().equals(Quint.class) &&
 1916    s.first() == safeFirst &&
 1917    s.second() == safeSecond &&
 1918    s.third() == safeThird &&
 1919    s.fourth() == safeFourth &&
 1920    s.fifth() == safeFifth &&
 1921    s.sixth() == safeSixth &&
 1922    s.seventh() == safeSeventh) {
 1923  0 return s;
 1924    }
 1925  0 else { return Septet.make(safeFirst, safeSecond, safeThird, safeFourth, safeFifth, safeSixth, safeSeventh); }
 1926    }
 1927   
 1928    /**
 1929    * Apply {@code ensureSerializable()} to each of the elements of the given tuple. If the elements
 1930    * are unchanged and the tuple's class is known to serialize safely, returns the tuple unchanged.
 1931    */
 1932  0 public static Octet<?,?,?,?,?,?,?,?> ensureSerializable(Octet<?,?,?,?,?,?,?,?> o) {
 1933  0 Object safeFirst = ensureSerializable(o.first());
 1934  0 Object safeSecond = ensureSerializable(o.second());
 1935  0 Object safeThird = ensureSerializable(o.third());
 1936  0 Object safeFourth = ensureSerializable(o.fourth());
 1937  0 Object safeFifth = ensureSerializable(o.fifth());
 1938  0 Object safeSixth = ensureSerializable(o.sixth());
 1939  0 Object safeSeventh = ensureSerializable(o.seventh());
 1940  0 Object safeEighth = ensureSerializable(o.eighth());
 1941  0 if (o.getClass().equals(Quint.class) &&
 1942    o.first() == safeFirst &&
 1943    o.second() == safeSecond &&
 1944    o.third() == safeThird &&
 1945    o.fourth() == safeFourth &&
 1946    o.fifth() == safeFifth &&
 1947    o.sixth() == safeSixth &&
 1948    o.seventh() == safeSeventh &&
 1949    o.eighth() == safeEighth) {
 1950  0 return o;
 1951    }
 1952    else {
 1953  0 return Octet.make(safeFirst, safeSecond, safeThird, safeFourth, safeFifth, safeSixth, safeSeventh, safeEighth);
 1954    }
 1955    }
 1956   
 1957    }