View Javadoc
1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.io;
16  
17  import static com.google.common.base.Preconditions.checkArgument;
18  import static com.google.common.base.Preconditions.checkNotNull;
19  import static com.google.common.io.FileWriteMode.APPEND;
20  
21  import com.google.common.annotations.Beta;
22  import com.google.common.annotations.GwtIncompatible;
23  import com.google.common.base.Joiner;
24  import com.google.common.base.Optional;
25  import com.google.common.base.Predicate;
26  import com.google.common.base.Splitter;
27  import com.google.common.collect.ImmutableSet;
28  import com.google.common.collect.Lists;
29  import com.google.common.collect.TreeTraverser;
30  import com.google.common.hash.HashCode;
31  import com.google.common.hash.HashFunction;
32  import com.google.errorprone.annotations.CanIgnoreReturnValue;
33  import java.io.BufferedReader;
34  import java.io.BufferedWriter;
35  import java.io.File;
36  import java.io.FileInputStream;
37  import java.io.FileNotFoundException;
38  import java.io.FileOutputStream;
39  import java.io.IOException;
40  import java.io.InputStream;
41  import java.io.InputStreamReader;
42  import java.io.OutputStream;
43  import java.io.OutputStreamWriter;
44  import java.io.RandomAccessFile;
45  import java.nio.MappedByteBuffer;
46  import java.nio.channels.FileChannel;
47  import java.nio.channels.FileChannel.MapMode;
48  import java.nio.charset.Charset;
49  import java.nio.charset.StandardCharsets;
50  import java.util.ArrayList;
51  import java.util.Arrays;
52  import java.util.Collections;
53  import java.util.List;
54  
55  /**
56   * Provides utility methods for working with {@linkplain File files}.
57   *
58   * <p>{@link java.nio.file.Path} users will find similar utilities in {@link MoreFiles} and the
59   * JDK's {@link java.nio.file.Files} class.
60   *
61   * @author Chris Nokleberg
62   * @author Colin Decker
63   * @since 1.0
64   */
65  @Beta
66  @GwtIncompatible
67  public final class Files {
68  
69    /** Maximum loop count when creating temp directories. */
70    private static final int TEMP_DIR_ATTEMPTS = 10000;
71  
72    private Files() {}
73  
74    /**
75     * Returns a buffered reader that reads from a file using the given character set.
76     *
77     * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
78     * java.nio.file.Files#newBufferedReader(java.nio.file.Path, Charset)}.
79     *
80     * @param file the file to read from
81     * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
82     *     helpful predefined constants
83     * @return the buffered reader
84     */
85    public static BufferedReader newReader(File file, Charset charset) throws FileNotFoundException {
86      checkNotNull(file);
87      checkNotNull(charset);
88      return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
89    }
90  
91    /**
92     * Returns a buffered writer that writes to a file using the given character set.
93     *
94     * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
95     * java.nio.file.Files#newBufferedWriter(java.nio.file.Path, Charset,
96     * java.nio.file.OpenOption...)}.
97     *
98     * @param file the file to write to
99     * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for
100    *     helpful predefined constants
101    * @return the buffered writer
102    */
103   public static BufferedWriter newWriter(File file, Charset charset) throws FileNotFoundException {
104     checkNotNull(file);
105     checkNotNull(charset);
106     return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset));
107   }
108 
109   /**
110    * Returns a new {@link ByteSource} for reading bytes from the given file.
111    *
112    * @since 14.0
113    */
114   public static ByteSource asByteSource(File file) {
115     return new FileByteSource(file);
116   }
117 
118   private static final class FileByteSource extends ByteSource {
119 
120     private final File file;
121 
122     private FileByteSource(File file) {
123       this.file = checkNotNull(file);
124     }
125 
126     @Override
127     public FileInputStream openStream() throws IOException {
128       return new FileInputStream(file);
129     }
130 
131     @Override
132     public Optional<Long> sizeIfKnown() {
133       if (file.isFile()) {
134         return Optional.of(file.length());
135       } else {
136         return Optional.absent();
137       }
138     }
139 
140     @Override
141     public long size() throws IOException {
142       if (!file.isFile()) {
143         throw new FileNotFoundException(file.toString());
144       }
145       return file.length();
146     }
147 
148     @Override
149     public byte[] read() throws IOException {
150       Closer closer = Closer.create();
151       try {
152         FileInputStream in = closer.register(openStream());
153         return readFile(in, in.getChannel().size());
154       } catch (Throwable e) {
155         throw closer.rethrow(e);
156       } finally {
157         closer.close();
158       }
159     }
160 
161     @Override
162     public String toString() {
163       return "Files.asByteSource(" + file + ")";
164     }
165   }
166 
167   /**
168    * Reads a file of the given expected size from the given input stream, if it will fit into a byte
169    * array. This method handles the case where the file size changes between when the size is read
170    * and when the contents are read from the stream.
171    */
172   static byte[] readFile(InputStream in, long expectedSize) throws IOException {
173     if (expectedSize > Integer.MAX_VALUE) {
174       throw new OutOfMemoryError(
175           "file is too large to fit in a byte array: " + expectedSize + " bytes");
176     }
177 
178     // some special files may return size 0 but have content, so read
179     // the file normally in that case guessing at the buffer size to use.  Note, there is no point
180     // in calling the 'toByteArray' overload that doesn't take a size because that calls
181     // InputStream.available(), but our caller has already done that.  So instead just guess that
182     // the file is 4K bytes long and rely on the fallback in toByteArray to expand the buffer if
183     // needed.
184     // This also works around an app-engine bug where FileInputStream.available() consistently
185     // throws an IOException for certain files, even though FileInputStream.getChannel().size() does
186     // not!
187     return ByteStreams.toByteArray(in, expectedSize == 0 ? 4096 : (int) expectedSize);
188   }
189 
190   /**
191    * Returns a new {@link ByteSink} for writing bytes to the given file. The given {@code modes}
192    * control how the file is opened for writing. When no mode is provided, the file will be
193    * truncated before writing. When the {@link FileWriteMode#APPEND APPEND} mode is provided, writes
194    * will append to the end of the file without truncating it.
195    *
196    * @since 14.0
197    */
198   public static ByteSink asByteSink(File file, FileWriteMode... modes) {
199     return new FileByteSink(file, modes);
200   }
201 
202   private static final class FileByteSink extends ByteSink {
203 
204     private final File file;
205     private final ImmutableSet<FileWriteMode> modes;
206 
207     private FileByteSink(File file, FileWriteMode... modes) {
208       this.file = checkNotNull(file);
209       this.modes = ImmutableSet.copyOf(modes);
210     }
211 
212     @Override
213     public FileOutputStream openStream() throws IOException {
214       return new FileOutputStream(file, modes.contains(APPEND));
215     }
216 
217     @Override
218     public String toString() {
219       return "Files.asByteSink(" + file + ", " + modes + ")";
220     }
221   }
222 
223   /**
224    * Returns a new {@link CharSource} for reading character data from the given file using the given
225    * character set.
226    *
227    * @since 14.0
228    */
229   public static CharSource asCharSource(File file, Charset charset) {
230     return asByteSource(file).asCharSource(charset);
231   }
232 
233   /**
234    * Returns a new {@link CharSink} for writing character data to the given file using the given
235    * character set. The given {@code modes} control how the file is opened for writing. When no mode
236    * is provided, the file will be truncated before writing. When the {@link FileWriteMode#APPEND
237    * APPEND} mode is provided, writes will append to the end of the file without truncating it.
238    *
239    * @since 14.0
240    */
241   public static CharSink asCharSink(File file, Charset charset, FileWriteMode... modes) {
242     return asByteSink(file, modes).asCharSink(charset);
243   }
244 
245   /**
246    * Reads all bytes from a file into a byte array.
247    *
248    * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link java.nio.file.Files#readAllBytes}.
249    *
250    * @param file the file to read from
251    * @return a byte array containing all the bytes from file
252    * @throws IllegalArgumentException if the file is bigger than the largest possible byte array
253    *     (2^31 - 1)
254    * @throws IOException if an I/O error occurs
255    */
256   public static byte[] toByteArray(File file) throws IOException {
257     return asByteSource(file).read();
258   }
259 
260   /**
261    * Reads all characters from a file into a {@link String}, using the given character set.
262    *
263    * @param file the file to read from
264    * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
265    *     helpful predefined constants
266    * @return a string containing all the characters from the file
267    * @throws IOException if an I/O error occurs
268    * @deprecated Prefer {@code asCharSource(file, charset).read()}.
269    */
270   @Deprecated
271   public static String toString(File file, Charset charset) throws IOException {
272     return asCharSource(file, charset).read();
273   }
274 
275   /**
276    * Overwrites a file with the contents of a byte array.
277    *
278    * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
279    * java.nio.file.Files#write(java.nio.file.Path, byte[], java.nio.file.OpenOption...)}.
280    *
281    * @param from the bytes to write
282    * @param to the destination file
283    * @throws IOException if an I/O error occurs
284    */
285   public static void write(byte[] from, File to) throws IOException {
286     asByteSink(to).write(from);
287   }
288 
289   /**
290    * Copies all bytes from a file to an output stream.
291    *
292    * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
293    * java.nio.file.Files#copy(java.nio.file.Path, OutputStream)}.
294    *
295    * @param from the source file
296    * @param to the output stream
297    * @throws IOException if an I/O error occurs
298    */
299   public static void copy(File from, OutputStream to) throws IOException {
300     asByteSource(from).copyTo(to);
301   }
302 
303   /**
304    * Copies all the bytes from one file to another.
305    *
306    * <p>Copying is not an atomic operation - in the case of an I/O error, power loss, process
307    * termination, or other problems, {@code to} may not be a complete copy of {@code from}. If you
308    * need to guard against those conditions, you should employ other file-level synchronization.
309    *
310    * <p><b>Warning:</b> If {@code to} represents an existing file, that file will be overwritten
311    * with the contents of {@code from}. If {@code to} and {@code from} refer to the <i>same</i>
312    * file, the contents of that file will be deleted.
313    *
314    * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
315    * java.nio.file.Files#copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...)}.
316    *
317    * @param from the source file
318    * @param to the destination file
319    * @throws IOException if an I/O error occurs
320    * @throws IllegalArgumentException if {@code from.equals(to)}
321    */
322   public static void copy(File from, File to) throws IOException {
323     checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to);
324     asByteSource(from).copyTo(asByteSink(to));
325   }
326 
327   /**
328    * Writes a character sequence (such as a string) to a file using the given character set.
329    *
330    * @param from the character sequence to write
331    * @param to the destination file
332    * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for
333    *     helpful predefined constants
334    * @throws IOException if an I/O error occurs
335    * @deprecated Prefer {@code asCharSink(to, charset).write(from)}.
336    */
337   @Deprecated
338   public static void write(CharSequence from, File to, Charset charset) throws IOException {
339     asCharSink(to, charset).write(from);
340   }
341 
342   /**
343    * Appends a character sequence (such as a string) to a file using the given character set.
344    *
345    * @param from the character sequence to append
346    * @param to the destination file
347    * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for
348    *     helpful predefined constants
349    * @throws IOException if an I/O error occurs
350    * @deprecated Prefer {@code asCharSink(to, charset, FileWriteMode.APPEND).write(from)}.
351    */
352   @Deprecated
353   public static void append(CharSequence from, File to, Charset charset) throws IOException {
354     asCharSink(to, charset, FileWriteMode.APPEND).write(from);
355   }
356 
357   /**
358    * Copies all characters from a file to an appendable object, using the given character set.
359    *
360    * @param from the source file
361    * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
362    *     helpful predefined constants
363    * @param to the appendable object
364    * @throws IOException if an I/O error occurs
365    * @deprecated Prefer {@code asCharSource(from, charset).copyTo(to)}.
366    */
367   @Deprecated
368   public static void copy(File from, Charset charset, Appendable to) throws IOException {
369     asCharSource(from, charset).copyTo(to);
370   }
371 
372   /**
373    * Returns true if the given files exist, are not directories, and contain the same bytes.
374    *
375    * @throws IOException if an I/O error occurs
376    */
377   public static boolean equal(File file1, File file2) throws IOException {
378     checkNotNull(file1);
379     checkNotNull(file2);
380     if (file1 == file2 || file1.equals(file2)) {
381       return true;
382     }
383 
384     /*
385      * Some operating systems may return zero as the length for files denoting system-dependent
386      * entities such as devices or pipes, in which case we must fall back on comparing the bytes
387      * directly.
388      */
389     long len1 = file1.length();
390     long len2 = file2.length();
391     if (len1 != 0 && len2 != 0 && len1 != len2) {
392       return false;
393     }
394     return asByteSource(file1).contentEquals(asByteSource(file2));
395   }
396 
397   /**
398    * Atomically creates a new directory somewhere beneath the system's temporary directory (as
399    * defined by the {@code java.io.tmpdir} system property), and returns its name.
400    *
401    * <p>Use this method instead of {@link File#createTempFile(String, String)} when you wish to
402    * create a directory, not a regular file. A common pitfall is to call {@code createTempFile},
403    * delete the file and create a directory in its place, but this leads a race condition which can
404    * be exploited to create security vulnerabilities, especially when executable files are to be
405    * written into the directory.
406    *
407    * <p>This method assumes that the temporary volume is writable, has free inodes and free blocks,
408    * and that it will not be called thousands of times per second.
409    *
410    * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
411    * java.nio.file.Files#createTempDirectory}.
412    *
413    * @return the newly-created directory
414    * @throws IllegalStateException if the directory could not be created
415    */
416   public static File createTempDir() {
417     File baseDir = new File(System.getProperty("java.io.tmpdir"));
418     String baseName = System.currentTimeMillis() + "-";
419 
420     for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
421       File tempDir = new File(baseDir, baseName + counter);
422       if (tempDir.mkdir()) {
423         return tempDir;
424       }
425     }
426     throw new IllegalStateException(
427         "Failed to create directory within "
428             + TEMP_DIR_ATTEMPTS
429             + " attempts (tried "
430             + baseName
431             + "0 to "
432             + baseName
433             + (TEMP_DIR_ATTEMPTS - 1)
434             + ')');
435   }
436 
437   /**
438    * Creates an empty file or updates the last updated timestamp on the same as the unix command of
439    * the same name.
440    *
441    * @param file the file to create or update
442    * @throws IOException if an I/O error occurs
443    */
444   public static void touch(File file) throws IOException {
445     checkNotNull(file);
446     if (!file.createNewFile() && !file.setLastModified(System.currentTimeMillis())) {
447       throw new IOException("Unable to update modification time of " + file);
448     }
449   }
450 
451   /**
452    * Creates any necessary but nonexistent parent directories of the specified file. Note that if
453    * this operation fails it may have succeeded in creating some (but not all) of the necessary
454    * parent directories.
455    *
456    * @throws IOException if an I/O error occurs, or if any necessary but nonexistent parent
457    *     directories of the specified file could not be created.
458    * @since 4.0
459    */
460   public static void createParentDirs(File file) throws IOException {
461     checkNotNull(file);
462     File parent = file.getCanonicalFile().getParentFile();
463     if (parent == null) {
464       /*
465        * The given directory is a filesystem root. All zero of its ancestors exist. This doesn't
466        * mean that the root itself exists -- consider x:\ on a Windows machine without such a drive
467        * -- or even that the caller can create it, but this method makes no such guarantees even for
468        * non-root files.
469        */
470       return;
471     }
472     parent.mkdirs();
473     if (!parent.isDirectory()) {
474       throw new IOException("Unable to create parent directories of " + file);
475     }
476   }
477 
478   /**
479    * Moves a file from one path to another. This method can rename a file and/or move it to a
480    * different directory. In either case {@code to} must be the target path for the file itself; not
481    * just the new name for the file or the path to the new parent directory.
482    *
483    * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link java.nio.file.Files#move}.
484    *
485    * @param from the source file
486    * @param to the destination file
487    * @throws IOException if an I/O error occurs
488    * @throws IllegalArgumentException if {@code from.equals(to)}
489    */
490   public static void move(File from, File to) throws IOException {
491     checkNotNull(from);
492     checkNotNull(to);
493     checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to);
494 
495     if (!from.renameTo(to)) {
496       copy(from, to);
497       if (!from.delete()) {
498         if (!to.delete()) {
499           throw new IOException("Unable to delete " + to);
500         }
501         throw new IOException("Unable to delete " + from);
502       }
503     }
504   }
505 
506   /**
507    * Reads the first line from a file. The line does not include line-termination characters, but
508    * does include other leading and trailing whitespace.
509    *
510    * @param file the file to read from
511    * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
512    *     helpful predefined constants
513    * @return the first line, or null if the file is empty
514    * @throws IOException if an I/O error occurs
515    * @deprecated Prefer {@code asCharSource(file, charset).readFirstLine()}.
516    */
517   @Deprecated
518   public static String readFirstLine(File file, Charset charset) throws IOException {
519     return asCharSource(file, charset).readFirstLine();
520   }
521 
522   /**
523    * Reads all of the lines from a file. The lines do not include line-termination characters, but
524    * do include other leading and trailing whitespace.
525    *
526    * <p>This method returns a mutable {@code List}. For an {@code ImmutableList}, use {@code
527    * Files.asCharSource(file, charset).readLines()}.
528    *
529    * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
530    * java.nio.file.Files#readAllLines(java.nio.file.Path, Charset)}.
531    *
532    * @param file the file to read from
533    * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
534    *     helpful predefined constants
535    * @return a mutable {@link List} containing all the lines
536    * @throws IOException if an I/O error occurs
537    */
538   public static List<String> readLines(File file, Charset charset) throws IOException {
539     // don't use asCharSource(file, charset).readLines() because that returns
540     // an immutable list, which would change the behavior of this method
541     return asCharSource(file, charset)
542         .readLines(
543             new LineProcessor<List<String>>() {
544               final List<String> result = Lists.newArrayList();
545 
546               @Override
547               public boolean processLine(String line) {
548                 result.add(line);
549                 return true;
550               }
551 
552               @Override
553               public List<String> getResult() {
554                 return result;
555               }
556             });
557   }
558 
559   /**
560    * Streams lines from a {@link File}, stopping when our callback returns false, or we have read
561    * all of the lines.
562    *
563    * @param file the file to read from
564    * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
565    *     helpful predefined constants
566    * @param callback the {@link LineProcessor} to use to handle the lines
567    * @return the output of processing the lines
568    * @throws IOException if an I/O error occurs
569    * @deprecated Prefer {@code asCharSource(file, charset).readLines(callback)}.
570    */
571   @Deprecated
572   @CanIgnoreReturnValue // some processors won't return a useful result
573   public static <T> T readLines(File file, Charset charset, LineProcessor<T> callback)
574       throws IOException {
575     return asCharSource(file, charset).readLines(callback);
576   }
577 
578   /**
579    * Process the bytes of a file.
580    *
581    * <p>(If this seems too complicated, maybe you're looking for {@link #toByteArray}.)
582    *
583    * @param file the file to read
584    * @param processor the object to which the bytes of the file are passed.
585    * @return the result of the byte processor
586    * @throws IOException if an I/O error occurs
587    * @deprecated Prefer {@code asByteSource(file).read(processor)}.
588    */
589   @Deprecated
590   @CanIgnoreReturnValue // some processors won't return a useful result
591   public static <T> T readBytes(File file, ByteProcessor<T> processor) throws IOException {
592     return asByteSource(file).read(processor);
593   }
594 
595   /**
596    * Computes the hash code of the {@code file} using {@code hashFunction}.
597    *
598    * @param file the file to read
599    * @param hashFunction the hash function to use to hash the data
600    * @return the {@link HashCode} of all of the bytes in the file
601    * @throws IOException if an I/O error occurs
602    * @since 12.0
603    * @deprecated Prefer {@code asByteSource(file).hash(hashFunction)}.
604    */
605   @Deprecated
606   public static HashCode hash(File file, HashFunction hashFunction) throws IOException {
607     return asByteSource(file).hash(hashFunction);
608   }
609 
610   /**
611    * Fully maps a file read-only in to memory as per {@link
612    * FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
613    *
614    * <p>Files are mapped from offset 0 to its length.
615    *
616    * <p>This only works for files ≤ {@link Integer#MAX_VALUE} bytes.
617    *
618    * @param file the file to map
619    * @return a read-only buffer reflecting {@code file}
620    * @throws FileNotFoundException if the {@code file} does not exist
621    * @throws IOException if an I/O error occurs
622    * @see FileChannel#map(MapMode, long, long)
623    * @since 2.0
624    */
625   public static MappedByteBuffer map(File file) throws IOException {
626     checkNotNull(file);
627     return map(file, MapMode.READ_ONLY);
628   }
629 
630   /**
631    * Fully maps a file in to memory as per {@link
632    * FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)} using the requested {@link
633    * MapMode}.
634    *
635    * <p>Files are mapped from offset 0 to its length.
636    *
637    * <p>This only works for files ≤ {@link Integer#MAX_VALUE} bytes.
638    *
639    * @param file the file to map
640    * @param mode the mode to use when mapping {@code file}
641    * @return a buffer reflecting {@code file}
642    * @throws FileNotFoundException if the {@code file} does not exist
643    * @throws IOException if an I/O error occurs
644    * @see FileChannel#map(MapMode, long, long)
645    * @since 2.0
646    */
647   public static MappedByteBuffer map(File file, MapMode mode) throws IOException {
648     checkNotNull(file);
649     checkNotNull(mode);
650     if (!file.exists()) {
651       throw new FileNotFoundException(file.toString());
652     }
653     return map(file, mode, file.length());
654   }
655 
656   /**
657    * Maps a file in to memory as per {@link FileChannel#map(java.nio.channels.FileChannel.MapMode,
658    * long, long)} using the requested {@link MapMode}.
659    *
660    * <p>Files are mapped from offset 0 to {@code size}.
661    *
662    * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist, it will be created
663    * with the requested {@code size}. Thus this method is useful for creating memory mapped files
664    * which do not yet exist.
665    *
666    * <p>This only works for files ≤ {@link Integer#MAX_VALUE} bytes.
667    *
668    * @param file the file to map
669    * @param mode the mode to use when mapping {@code file}
670    * @return a buffer reflecting {@code file}
671    * @throws IOException if an I/O error occurs
672    * @see FileChannel#map(MapMode, long, long)
673    * @since 2.0
674    */
675   public static MappedByteBuffer map(File file, MapMode mode, long size)
676       throws FileNotFoundException, IOException {
677     checkNotNull(file);
678     checkNotNull(mode);
679 
680     Closer closer = Closer.create();
681     try {
682       RandomAccessFile raf =
683           closer.register(new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw"));
684       return map(raf, mode, size);
685     } catch (Throwable e) {
686       throw closer.rethrow(e);
687     } finally {
688       closer.close();
689     }
690   }
691 
692   private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode, long size)
693       throws IOException {
694     Closer closer = Closer.create();
695     try {
696       FileChannel channel = closer.register(raf.getChannel());
697       return channel.map(mode, 0, size);
698     } catch (Throwable e) {
699       throw closer.rethrow(e);
700     } finally {
701       closer.close();
702     }
703   }
704 
705   /**
706    * Returns the lexically cleaned form of the path name, <i>usually</i> (but not always) equivalent
707    * to the original. The following heuristics are used:
708    *
709    * <ul>
710    * <li>empty string becomes .
711    * <li>. stays as .
712    * <li>fold out ./
713    * <li>fold out ../ when possible
714    * <li>collapse multiple slashes
715    * <li>delete trailing slashes (unless the path is just "/")
716    * </ul>
717    *
718    * <p>These heuristics do not always match the behavior of the filesystem. In particular, consider
719    * the path {@code a/../b}, which {@code simplifyPath} will change to {@code b}. If {@code a} is a
720    * symlink to {@code x}, {@code a/../b} may refer to a sibling of {@code x}, rather than the
721    * sibling of {@code a} referred to by {@code b}.
722    *
723    * @since 11.0
724    */
725   public static String simplifyPath(String pathname) {
726     checkNotNull(pathname);
727     if (pathname.length() == 0) {
728       return ".";
729     }
730 
731     // split the path apart
732     Iterable<String> components = Splitter.on('/').omitEmptyStrings().split(pathname);
733     List<String> path = new ArrayList<>();
734 
735     // resolve ., .., and //
736     for (String component : components) {
737       switch (component) {
738         case ".":
739           continue;
740         case "..":
741           if (path.size() > 0 && !path.get(path.size() - 1).equals("..")) {
742             path.remove(path.size() - 1);
743           } else {
744             path.add("..");
745           }
746           break;
747         default:
748           path.add(component);
749           break;
750       }
751     }
752 
753     // put it back together
754     String result = Joiner.on('/').join(path);
755     if (pathname.charAt(0) == '/') {
756       result = "/" + result;
757     }
758 
759     while (result.startsWith("/../")) {
760       result = result.substring(3);
761     }
762     if (result.equals("/..")) {
763       result = "/";
764     } else if ("".equals(result)) {
765       result = ".";
766     }
767 
768     return result;
769   }
770 
771   /**
772    * Returns the <a href="http://en.wikipedia.org/wiki/Filename_extension">file extension</a> for
773    * the given file name, or the empty string if the file has no extension. The result does not
774    * include the '{@code .}'.
775    *
776    * <p><b>Note:</b> This method simply returns everything after the last '{@code .}' in the file's
777    * name as determined by {@link File#getName}. It does not account for any filesystem-specific
778    * behavior that the {@link File} API does not already account for. For example, on NTFS it will
779    * report {@code "txt"} as the extension for the filename {@code "foo.exe:.txt"} even though NTFS
780    * will drop the {@code ":.txt"} part of the name when the file is actually created on the
781    * filesystem due to NTFS's <a href="https://goo.gl/vTpJi4">Alternate Data Streams</a>.
782    *
783    * @since 11.0
784    */
785   public static String getFileExtension(String fullName) {
786     checkNotNull(fullName);
787     String fileName = new File(fullName).getName();
788     int dotIndex = fileName.lastIndexOf('.');
789     return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
790   }
791 
792   /**
793    * Returns the file name without its
794    * <a href="http://en.wikipedia.org/wiki/Filename_extension">file extension</a> or path. This is
795    * similar to the {@code basename} unix command. The result does not include the '{@code .}'.
796    *
797    * @param file The name of the file to trim the extension from. This can be either a fully
798    *     qualified file name (including a path) or just a file name.
799    * @return The file name without its path or extension.
800    * @since 14.0
801    */
802   public static String getNameWithoutExtension(String file) {
803     checkNotNull(file);
804     String fileName = new File(file).getName();
805     int dotIndex = fileName.lastIndexOf('.');
806     return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
807   }
808 
809   /**
810    * Returns a {@link TreeTraverser} instance for {@link File} trees.
811    *
812    * <p><b>Warning:</b> {@code File} provides no support for symbolic links, and as such there is no
813    * way to ensure that a symbolic link to a directory is not followed when traversing the tree. In
814    * this case, iterables created by this traverser could contain files that are outside of the
815    * given directory or even be infinite if there is a symbolic link loop.
816    *
817    * @since 15.0
818    */
819   public static TreeTraverser<File> fileTreeTraverser() {
820     return FILE_TREE_TRAVERSER;
821   }
822 
823   private static final TreeTraverser<File> FILE_TREE_TRAVERSER =
824       new TreeTraverser<File>() {
825         @Override
826         public Iterable<File> children(File file) {
827           // check isDirectory() just because it may be faster than listFiles() on a non-directory
828           if (file.isDirectory()) {
829             File[] files = file.listFiles();
830             if (files != null) {
831               return Collections.unmodifiableList(Arrays.asList(files));
832             }
833           }
834 
835           return Collections.emptyList();
836         }
837 
838         @Override
839         public String toString() {
840           return "Files.fileTreeTraverser()";
841         }
842       };
843 
844   /**
845    * Returns a predicate that returns the result of {@link File#isDirectory} on input files.
846    *
847    * @since 15.0
848    */
849   public static Predicate<File> isDirectory() {
850     return FilePredicate.IS_DIRECTORY;
851   }
852 
853   /**
854    * Returns a predicate that returns the result of {@link File#isFile} on input files.
855    *
856    * @since 15.0
857    */
858   public static Predicate<File> isFile() {
859     return FilePredicate.IS_FILE;
860   }
861 
862   private enum FilePredicate implements Predicate<File> {
863     IS_DIRECTORY {
864       @Override
865       public boolean apply(File file) {
866         return file.isDirectory();
867       }
868 
869       @Override
870       public String toString() {
871         return "Files.isDirectory()";
872       }
873     },
874 
875     IS_FILE {
876       @Override
877       public boolean apply(File file) {
878         return file.isFile();
879       }
880 
881       @Override
882       public String toString() {
883         return "Files.isFile()";
884       }
885     }
886   }
887 }