001/*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.io;
016
017import static com.google.common.base.Preconditions.checkArgument;
018import static com.google.common.base.Preconditions.checkNotNull;
019import static com.google.common.io.FileWriteMode.APPEND;
020
021import com.google.common.annotations.Beta;
022import com.google.common.annotations.GwtIncompatible;
023import com.google.common.base.Joiner;
024import com.google.common.base.Optional;
025import com.google.common.base.Predicate;
026import com.google.common.base.Splitter;
027import com.google.common.collect.ImmutableSet;
028import com.google.common.collect.Lists;
029import com.google.common.collect.TreeTraverser;
030import com.google.common.graph.SuccessorsFunction;
031import com.google.common.graph.Traverser;
032import com.google.common.hash.HashCode;
033import com.google.common.hash.HashFunction;
034import com.google.errorprone.annotations.CanIgnoreReturnValue;
035import java.io.BufferedReader;
036import java.io.BufferedWriter;
037import java.io.File;
038import java.io.FileInputStream;
039import java.io.FileNotFoundException;
040import java.io.FileOutputStream;
041import java.io.IOException;
042import java.io.InputStreamReader;
043import java.io.OutputStream;
044import java.io.OutputStreamWriter;
045import java.io.RandomAccessFile;
046import java.nio.MappedByteBuffer;
047import java.nio.channels.FileChannel;
048import java.nio.channels.FileChannel.MapMode;
049import java.nio.charset.Charset;
050import java.nio.charset.StandardCharsets;
051import java.util.ArrayList;
052import java.util.Arrays;
053import java.util.Collections;
054import java.util.List;
055
056/**
057 * Provides utility methods for working with {@linkplain File files}.
058 *
059 * <p>{@link java.nio.file.Path} users will find similar utilities in {@link MoreFiles} and the
060 * JDK's {@link java.nio.file.Files} class.
061 *
062 * @author Chris Nokleberg
063 * @author Colin Decker
064 * @since 1.0
065 */
066@GwtIncompatible
067public final class Files {
068
069  /** Maximum loop count when creating temp directories. */
070  private static final int TEMP_DIR_ATTEMPTS = 10000;
071
072  private Files() {}
073
074  /**
075   * Returns a buffered reader that reads from a file using the given character set.
076   *
077   * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
078   * java.nio.file.Files#newBufferedReader(java.nio.file.Path, Charset)}.
079   *
080   * @param file the file to read from
081   * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
082   *     helpful predefined constants
083   * @return the buffered reader
084   */
085  @Beta
086  public static BufferedReader newReader(File file, Charset charset) throws FileNotFoundException {
087    checkNotNull(file);
088    checkNotNull(charset);
089    return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
090  }
091
092  /**
093   * Returns a buffered writer that writes to a file using the given character set.
094   *
095   * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
096   * java.nio.file.Files#newBufferedWriter(java.nio.file.Path, Charset,
097   * java.nio.file.OpenOption...)}.
098   *
099   * @param file the file to write to
100   * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for
101   *     helpful predefined constants
102   * @return the buffered writer
103   */
104  @Beta
105  public static BufferedWriter newWriter(File file, Charset charset) throws FileNotFoundException {
106    checkNotNull(file);
107    checkNotNull(charset);
108    return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset));
109  }
110
111  /**
112   * Returns a new {@link ByteSource} for reading bytes from the given file.
113   *
114   * @since 14.0
115   */
116  public static ByteSource asByteSource(File file) {
117    return new FileByteSource(file);
118  }
119
120  private static final class FileByteSource extends ByteSource {
121
122    private final File file;
123
124    private FileByteSource(File file) {
125      this.file = checkNotNull(file);
126    }
127
128    @Override
129    public FileInputStream openStream() throws IOException {
130      return new FileInputStream(file);
131    }
132
133    @Override
134    public Optional<Long> sizeIfKnown() {
135      if (file.isFile()) {
136        return Optional.of(file.length());
137      } else {
138        return Optional.absent();
139      }
140    }
141
142    @Override
143    public long size() throws IOException {
144      if (!file.isFile()) {
145        throw new FileNotFoundException(file.toString());
146      }
147      return file.length();
148    }
149
150    @Override
151    public byte[] read() throws IOException {
152      Closer closer = Closer.create();
153      try {
154        FileInputStream in = closer.register(openStream());
155        return ByteStreams.toByteArray(in, in.getChannel().size());
156      } catch (Throwable e) {
157        throw closer.rethrow(e);
158      } finally {
159        closer.close();
160      }
161    }
162
163    @Override
164    public String toString() {
165      return "Files.asByteSource(" + file + ")";
166    }
167  }
168
169  /**
170   * Returns a new {@link ByteSink} for writing bytes to the given file. The given {@code modes}
171   * control how the file is opened for writing. When no mode is provided, the file will be
172   * truncated before writing. When the {@link FileWriteMode#APPEND APPEND} mode is provided, writes
173   * will append to the end of the file without truncating it.
174   *
175   * @since 14.0
176   */
177  public static ByteSink asByteSink(File file, FileWriteMode... modes) {
178    return new FileByteSink(file, modes);
179  }
180
181  private static final class FileByteSink extends ByteSink {
182
183    private final File file;
184    private final ImmutableSet<FileWriteMode> modes;
185
186    private FileByteSink(File file, FileWriteMode... modes) {
187      this.file = checkNotNull(file);
188      this.modes = ImmutableSet.copyOf(modes);
189    }
190
191    @Override
192    public FileOutputStream openStream() throws IOException {
193      return new FileOutputStream(file, modes.contains(APPEND));
194    }
195
196    @Override
197    public String toString() {
198      return "Files.asByteSink(" + file + ", " + modes + ")";
199    }
200  }
201
202  /**
203   * Returns a new {@link CharSource} for reading character data from the given file using the given
204   * character set.
205   *
206   * @since 14.0
207   */
208  public static CharSource asCharSource(File file, Charset charset) {
209    return asByteSource(file).asCharSource(charset);
210  }
211
212  /**
213   * Returns a new {@link CharSink} for writing character data to the given file using the given
214   * character set. The given {@code modes} control how the file is opened for writing. When no mode
215   * is provided, the file will be truncated before writing. When the {@link FileWriteMode#APPEND
216   * APPEND} mode is provided, writes will append to the end of the file without truncating it.
217   *
218   * @since 14.0
219   */
220  public static CharSink asCharSink(File file, Charset charset, FileWriteMode... modes) {
221    return asByteSink(file, modes).asCharSink(charset);
222  }
223
224  /**
225   * Returns a factory that will supply instances of {@link FileInputStream}
226   * that read from a file.
227   *
228   * @param file the file to read from
229   * @return the factory
230   * @deprecated Use {@link #asByteSource(File)}. This method is scheduled for
231   *     removal in Guava 18.0.
232   */
233  @Deprecated
234  public static InputSupplier<FileInputStream> newInputStreamSupplier(
235      final File file) {
236    return ByteStreams.asInputSupplier(asByteSource(file));
237  }
238
239  /**
240   * Returns a factory that will supply instances of {@link FileOutputStream}
241   * that write to a file.
242   *
243   * @param file the file to write to
244   * @return the factory
245   * @deprecated Use {@link #asByteSink(File)}. This method is scheduled for
246   *     removal in Guava 18.0.
247   */
248  @Deprecated
249  public static OutputSupplier<FileOutputStream> newOutputStreamSupplier(
250      File file) {
251    return newOutputStreamSupplier(file, false);
252  }
253
254  /**
255   * Returns a factory that will supply instances of {@link FileOutputStream}
256   * that write to or append to a file.
257   *
258   * @param file the file to write to
259   * @param append if true, the encoded characters will be appended to the file;
260   *     otherwise the file is overwritten
261   * @return the factory
262   * @deprecated Use {@link #asByteSink(File, FileWriteMode...)}, passing
263   *     {@link FileWriteMode#APPEND} for append. This method is scheduled for
264   *     removal in Guava 18.0.
265   */
266  @Deprecated
267  public static OutputSupplier<FileOutputStream> newOutputStreamSupplier(
268      final File file, final boolean append) {
269    return ByteStreams.asOutputSupplier(asByteSink(file, modes(append)));
270  }
271
272  private static FileWriteMode[] modes(boolean append) {
273    return append
274        ? new FileWriteMode[]{ FileWriteMode.APPEND }
275        : new FileWriteMode[0];
276  }
277
278  /**
279   * Returns a factory that will supply instances of
280   * {@link InputStreamReader} that read a file using the given character set.
281   *
282   * @param file the file to read from
283   * @param charset the charset used to decode the input stream; see {@link
284   *     Charsets} for helpful predefined constants
285   * @return the factory
286   * @deprecated Use {@link #asCharSource(File, Charset)}. This method is
287   *     scheduled for removal in Guava 18.0.
288   */
289  @Deprecated
290  public static InputSupplier<InputStreamReader> newReaderSupplier(File file,
291      Charset charset) {
292    return CharStreams.asInputSupplier(asCharSource(file, charset));
293  }
294
295  /**
296   * Returns a factory that will supply instances of {@link OutputStreamWriter}
297   * that write to a file using the given character set.
298   *
299   * @param file the file to write to
300   * @param charset the charset used to encode the output stream; see {@link
301   *     Charsets} for helpful predefined constants
302   * @return the factory
303   * @deprecated Use {@link #asCharSink(File, Charset)}. This method is
304   *     scheduled for removal in Guava 18.0.
305   */
306  @Deprecated
307  public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file,
308      Charset charset) {
309    return newWriterSupplier(file, charset, false);
310  }
311
312  /**
313   * Returns a factory that will supply instances of {@link OutputStreamWriter}
314   * that write to or append to a file using the given character set.
315   *
316   * @param file the file to write to
317   * @param charset the charset used to encode the output stream; see {@link
318   *     Charsets} for helpful predefined constants
319   * @param append if true, the encoded characters will be appended to the file;
320   *     otherwise the file is overwritten
321   * @return the factory
322   * @deprecated Use {@link #asCharSink(File, Charset, FileWriteMode...)},
323   *     passing {@link FileWriteMode#APPEND} for append. This method is
324   *     scheduled for removal in Guava 18.0.
325   */
326  @Deprecated
327  public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file,
328      Charset charset, boolean append) {
329    return CharStreams.asOutputSupplier(asCharSink(file, charset, modes(append)));
330  }
331
332  /**
333   * Reads all bytes from a file into a byte array.
334   *
335   * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link java.nio.file.Files#readAllBytes}.
336   *
337   * @param file the file to read from
338   * @return a byte array containing all the bytes from file
339   * @throws IllegalArgumentException if the file is bigger than the largest possible byte array
340   *     (2^31 - 1)
341   * @throws IOException if an I/O error occurs
342   */
343  @Beta
344  public static byte[] toByteArray(File file) throws IOException {
345    return asByteSource(file).read();
346  }
347
348  /**
349   * Reads all characters from a file into a {@link String}, using the given character set.
350   *
351   * @param file the file to read from
352   * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
353   *     helpful predefined constants
354   * @return a string containing all the characters from the file
355   * @throws IOException if an I/O error occurs
356   * @deprecated Prefer {@code asCharSource(file, charset).read()}. This method is scheduled to be
357   *     removed in October 2019.
358   */
359  @Beta
360  @Deprecated
361  public static String toString(File file, Charset charset) throws IOException {
362    return asCharSource(file, charset).read();
363  }
364
365  /**
366   * Overwrites a file with the contents of a byte array.
367   *
368   * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
369   * java.nio.file.Files#write(java.nio.file.Path, byte[], java.nio.file.OpenOption...)}.
370   *
371   * @param from the bytes to write
372   * @param to the destination file
373   * @throws IOException if an I/O error occurs
374   */
375  @Beta
376  public static void write(byte[] from, File to) throws IOException {
377    asByteSink(to).write(from);
378  }
379
380  /**
381   * Writes a character sequence (such as a string) to a file using the given character set.
382   *
383   * @param from the character sequence to write
384   * @param to the destination file
385   * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for
386   *     helpful predefined constants
387   * @throws IOException if an I/O error occurs
388   * @deprecated Prefer {@code asCharSink(to, charset).write(from)}. This method is scheduled to be
389   *     removed in October 2019.
390   */
391  @Beta
392  @Deprecated
393  public static void write(CharSequence from, File to, Charset charset) throws IOException {
394    asCharSink(to, charset).write(from);
395  }
396
397  /**
398   * Copies all bytes from a file to an output stream.
399   *
400   * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
401   * java.nio.file.Files#copy(java.nio.file.Path, OutputStream)}.
402   *
403   * @param from the source file
404   * @param to the output stream
405   * @throws IOException if an I/O error occurs
406   */
407  @Beta
408  public static void copy(File from, OutputStream to) throws IOException {
409    asByteSource(from).copyTo(to);
410  }
411
412  /**
413   * Copies all the bytes from one file to another.
414   *
415   * <p>Copying is not an atomic operation - in the case of an I/O error, power loss, process
416   * termination, or other problems, {@code to} may not be a complete copy of {@code from}. If you
417   * need to guard against those conditions, you should employ other file-level synchronization.
418   *
419   * <p><b>Warning:</b> If {@code to} represents an existing file, that file will be overwritten
420   * with the contents of {@code from}. If {@code to} and {@code from} refer to the <i>same</i>
421   * file, the contents of that file will be deleted.
422   *
423   * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
424   * java.nio.file.Files#copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption...)}.
425   *
426   * @param from the source file
427   * @param to the destination file
428   * @throws IOException if an I/O error occurs
429   * @throws IllegalArgumentException if {@code from.equals(to)}
430   */
431  @Beta
432  public static void copy(File from, File to) throws IOException {
433    checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to);
434    asByteSource(from).copyTo(asByteSink(to));
435  }
436
437  /**
438   * Copies all characters from a file to an appendable object, using the given character set.
439   *
440   * @param from the source file
441   * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
442   *     helpful predefined constants
443   * @param to the appendable object
444   * @throws IOException if an I/O error occurs
445   * @deprecated Prefer {@code asCharSource(from, charset).copyTo(to)}. This method is scheduled to
446   *     be removed in October 2019.
447   */
448  @Beta
449  @Deprecated
450  public
451  static void copy(File from, Charset charset, Appendable to) throws IOException {
452    asCharSource(from, charset).copyTo(to);
453  }
454
455  /**
456   * Appends a character sequence (such as a string) to a file using the given character set.
457   *
458   * @param from the character sequence to append
459   * @param to the destination file
460   * @param charset the charset used to encode the output stream; see {@link StandardCharsets} for
461   *     helpful predefined constants
462   * @throws IOException if an I/O error occurs
463   * @deprecated Prefer {@code asCharSink(to, charset, FileWriteMode.APPEND).write(from)}. This
464   *     method is scheduled to be removed in October 2019.
465   */
466  @Beta
467  @Deprecated
468  public
469  static void append(CharSequence from, File to, Charset charset) throws IOException {
470    asCharSink(to, charset, FileWriteMode.APPEND).write(from);
471  }
472
473  /**
474   * Returns true if the given files exist, are not directories, and contain the same bytes.
475   *
476   * @throws IOException if an I/O error occurs
477   */
478  @Beta
479  public static boolean equal(File file1, File file2) throws IOException {
480    checkNotNull(file1);
481    checkNotNull(file2);
482    if (file1 == file2 || file1.equals(file2)) {
483      return true;
484    }
485
486    /*
487     * Some operating systems may return zero as the length for files denoting system-dependent
488     * entities such as devices or pipes, in which case we must fall back on comparing the bytes
489     * directly.
490     */
491    long len1 = file1.length();
492    long len2 = file2.length();
493    if (len1 != 0 && len2 != 0 && len1 != len2) {
494      return false;
495    }
496    return asByteSource(file1).contentEquals(asByteSource(file2));
497  }
498
499  /**
500   * Atomically creates a new directory somewhere beneath the system's temporary directory (as
501   * defined by the {@code java.io.tmpdir} system property), and returns its name.
502   *
503   * <p>Use this method instead of {@link File#createTempFile(String, String)} when you wish to
504   * create a directory, not a regular file. A common pitfall is to call {@code createTempFile},
505   * delete the file and create a directory in its place, but this leads a race condition which can
506   * be exploited to create security vulnerabilities, especially when executable files are to be
507   * written into the directory.
508   *
509   * <p>This method assumes that the temporary volume is writable, has free inodes and free blocks,
510   * and that it will not be called thousands of times per second.
511   *
512   * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
513   * java.nio.file.Files#createTempDirectory}.
514   *
515   * @return the newly-created directory
516   * @throws IllegalStateException if the directory could not be created
517   */
518  @Beta
519  public static File createTempDir() {
520    File baseDir = new File(System.getProperty("java.io.tmpdir"));
521    @SuppressWarnings("GoodTime") // reading system time without TimeSource
522    String baseName = System.currentTimeMillis() + "-";
523
524    for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
525      File tempDir = new File(baseDir, baseName + counter);
526      if (tempDir.mkdir()) {
527        return tempDir;
528      }
529    }
530    throw new IllegalStateException(
531        "Failed to create directory within "
532            + TEMP_DIR_ATTEMPTS
533            + " attempts (tried "
534            + baseName
535            + "0 to "
536            + baseName
537            + (TEMP_DIR_ATTEMPTS - 1)
538            + ')');
539  }
540
541  /**
542   * Creates an empty file or updates the last updated timestamp on the same as the unix command of
543   * the same name.
544   *
545   * @param file the file to create or update
546   * @throws IOException if an I/O error occurs
547   */
548  @Beta
549  @SuppressWarnings("GoodTime") // reading system time without TimeSource
550  public static void touch(File file) throws IOException {
551    checkNotNull(file);
552    if (!file.createNewFile() && !file.setLastModified(System.currentTimeMillis())) {
553      throw new IOException("Unable to update modification time of " + file);
554    }
555  }
556
557  /**
558   * Creates any necessary but nonexistent parent directories of the specified file. Note that if
559   * this operation fails it may have succeeded in creating some (but not all) of the necessary
560   * parent directories.
561   *
562   * @throws IOException if an I/O error occurs, or if any necessary but nonexistent parent
563   *     directories of the specified file could not be created.
564   * @since 4.0
565   */
566  @Beta
567  public static void createParentDirs(File file) throws IOException {
568    checkNotNull(file);
569    File parent = file.getCanonicalFile().getParentFile();
570    if (parent == null) {
571      /*
572       * The given directory is a filesystem root. All zero of its ancestors exist. This doesn't
573       * mean that the root itself exists -- consider x:\ on a Windows machine without such a drive
574       * -- or even that the caller can create it, but this method makes no such guarantees even for
575       * non-root files.
576       */
577      return;
578    }
579    parent.mkdirs();
580    if (!parent.isDirectory()) {
581      throw new IOException("Unable to create parent directories of " + file);
582    }
583  }
584
585  /**
586   * Moves a file from one path to another. This method can rename a file and/or move it to a
587   * different directory. In either case {@code to} must be the target path for the file itself; not
588   * just the new name for the file or the path to the new parent directory.
589   *
590   * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link java.nio.file.Files#move}.
591   *
592   * @param from the source file
593   * @param to the destination file
594   * @throws IOException if an I/O error occurs
595   * @throws IllegalArgumentException if {@code from.equals(to)}
596   */
597  @Beta
598  public static void move(File from, File to) throws IOException {
599    checkNotNull(from);
600    checkNotNull(to);
601    checkArgument(!from.equals(to), "Source %s and destination %s must be different", from, to);
602
603    if (!from.renameTo(to)) {
604      copy(from, to);
605      if (!from.delete()) {
606        if (!to.delete()) {
607          throw new IOException("Unable to delete " + to);
608        }
609        throw new IOException("Unable to delete " + from);
610      }
611    }
612  }
613
614  /**
615   * Reads the first line from a file. The line does not include line-termination characters, but
616   * does include other leading and trailing whitespace.
617   *
618   * @param file the file to read from
619   * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
620   *     helpful predefined constants
621   * @return the first line, or null if the file is empty
622   * @throws IOException if an I/O error occurs
623   * @deprecated Prefer {@code asCharSource(file, charset).readFirstLine()}. This method is
624   *     scheduled to be removed in October 2019.
625   */
626  @Beta
627  @Deprecated
628  public
629  static String readFirstLine(File file, Charset charset) throws IOException {
630    return asCharSource(file, charset).readFirstLine();
631  }
632
633  /**
634   * Reads all of the lines from a file. The lines do not include line-termination characters, but
635   * do include other leading and trailing whitespace.
636   *
637   * <p>This method returns a mutable {@code List}. For an {@code ImmutableList}, use {@code
638   * Files.asCharSource(file, charset).readLines()}.
639   *
640   * <p><b>{@link java.nio.file.Path} equivalent:</b> {@link
641   * java.nio.file.Files#readAllLines(java.nio.file.Path, Charset)}.
642   *
643   * @param file the file to read from
644   * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
645   *     helpful predefined constants
646   * @return a mutable {@link List} containing all the lines
647   * @throws IOException if an I/O error occurs
648   */
649  @Beta
650  public static List<String> readLines(File file, Charset charset) throws IOException {
651    // don't use asCharSource(file, charset).readLines() because that returns
652    // an immutable list, which would change the behavior of this method
653    return asCharSource(file, charset)
654        .readLines(
655            new LineProcessor<List<String>>() {
656              final List<String> result = Lists.newArrayList();
657
658              @Override
659              public boolean processLine(String line) {
660                result.add(line);
661                return true;
662              }
663
664              @Override
665              public List<String> getResult() {
666                return result;
667              }
668            });
669  }
670
671  /**
672   * Streams lines from a {@link File}, stopping when our callback returns false, or we have read
673   * all of the lines.
674   *
675   * @param file the file to read from
676   * @param charset the charset used to decode the input stream; see {@link StandardCharsets} for
677   *     helpful predefined constants
678   * @param callback the {@link LineProcessor} to use to handle the lines
679   * @return the output of processing the lines
680   * @throws IOException if an I/O error occurs
681   * @deprecated Prefer {@code asCharSource(file, charset).readLines(callback)}. This method is
682   *     scheduled to be removed in October 2019.
683   */
684  @Beta
685  @Deprecated
686  @CanIgnoreReturnValue // some processors won't return a useful result
687  public
688  static <T> T readLines(File file, Charset charset, LineProcessor<T> callback) throws IOException {
689    return asCharSource(file, charset).readLines(callback);
690  }
691
692  /**
693   * Process the bytes of a file.
694   *
695   * <p>(If this seems too complicated, maybe you're looking for {@link #toByteArray}.)
696   *
697   * @param file the file to read
698   * @param processor the object to which the bytes of the file are passed.
699   * @return the result of the byte processor
700   * @throws IOException if an I/O error occurs
701   * @deprecated Prefer {@code asByteSource(file).read(processor)}. This method is scheduled to be
702   *     removed in October 2019.
703   */
704  @Beta
705  @Deprecated
706  @CanIgnoreReturnValue // some processors won't return a useful result
707  public
708  static <T> T readBytes(File file, ByteProcessor<T> processor) throws IOException {
709    return asByteSource(file).read(processor);
710  }
711
712  /**
713   * Computes the hash code of the {@code file} using {@code hashFunction}.
714   *
715   * @param file the file to read
716   * @param hashFunction the hash function to use to hash the data
717   * @return the {@link HashCode} of all of the bytes in the file
718   * @throws IOException if an I/O error occurs
719   * @since 12.0
720   * @deprecated Prefer {@code asByteSource(file).hash(hashFunction)}. This method is scheduled to
721   *     be removed in October 2019.
722   */
723  @Beta
724  @Deprecated
725  public
726  static HashCode hash(File file, HashFunction hashFunction) throws IOException {
727    return asByteSource(file).hash(hashFunction);
728  }
729
730  /**
731   * Fully maps a file read-only in to memory as per {@link
732   * FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
733   *
734   * <p>Files are mapped from offset 0 to its length.
735   *
736   * <p>This only works for files ≤ {@link Integer#MAX_VALUE} bytes.
737   *
738   * @param file the file to map
739   * @return a read-only buffer reflecting {@code file}
740   * @throws FileNotFoundException if the {@code file} does not exist
741   * @throws IOException if an I/O error occurs
742   * @see FileChannel#map(MapMode, long, long)
743   * @since 2.0
744   */
745  @Beta
746  public static MappedByteBuffer map(File file) throws IOException {
747    checkNotNull(file);
748    return map(file, MapMode.READ_ONLY);
749  }
750
751  /**
752   * Fully maps a file in to memory as per {@link
753   * FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)} using the requested {@link
754   * MapMode}.
755   *
756   * <p>Files are mapped from offset 0 to its length.
757   *
758   * <p>This only works for files ≤ {@link Integer#MAX_VALUE} bytes.
759   *
760   * @param file the file to map
761   * @param mode the mode to use when mapping {@code file}
762   * @return a buffer reflecting {@code file}
763   * @throws FileNotFoundException if the {@code file} does not exist
764   * @throws IOException if an I/O error occurs
765   * @see FileChannel#map(MapMode, long, long)
766   * @since 2.0
767   */
768  @Beta
769  public static MappedByteBuffer map(File file, MapMode mode) throws IOException {
770    return mapInternal(file, mode, -1);
771  }
772
773  /**
774   * Maps a file in to memory as per {@link FileChannel#map(java.nio.channels.FileChannel.MapMode,
775   * long, long)} using the requested {@link MapMode}.
776   *
777   * <p>Files are mapped from offset 0 to {@code size}.
778   *
779   * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist, it will be created
780   * with the requested {@code size}. Thus this method is useful for creating memory mapped files
781   * which do not yet exist.
782   *
783   * <p>This only works for files ≤ {@link Integer#MAX_VALUE} bytes.
784   *
785   * @param file the file to map
786   * @param mode the mode to use when mapping {@code file}
787   * @return a buffer reflecting {@code file}
788   * @throws IOException if an I/O error occurs
789   * @see FileChannel#map(MapMode, long, long)
790   * @since 2.0
791   */
792  @Beta
793  public static MappedByteBuffer map(File file, MapMode mode, long size) throws IOException {
794    checkArgument(size >= 0, "size (%s) may not be negative", size);
795    return mapInternal(file, mode, size);
796  }
797
798  private static MappedByteBuffer mapInternal(File file, MapMode mode, long size)
799      throws IOException {
800    checkNotNull(file);
801    checkNotNull(mode);
802
803    Closer closer = Closer.create();
804    try {
805      RandomAccessFile raf =
806          closer.register(new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw"));
807      FileChannel channel = closer.register(raf.getChannel());
808      return channel.map(mode, 0, size == -1 ? channel.size() : size);
809    } catch (Throwable e) {
810      throw closer.rethrow(e);
811    } finally {
812      closer.close();
813    }
814  }
815
816  /**
817   * Returns the lexically cleaned form of the path name, <i>usually</i> (but not always) equivalent
818   * to the original. The following heuristics are used:
819   *
820   * <ul>
821   *   <li>empty string becomes .
822   *   <li>. stays as .
823   *   <li>fold out ./
824   *   <li>fold out ../ when possible
825   *   <li>collapse multiple slashes
826   *   <li>delete trailing slashes (unless the path is just "/")
827   * </ul>
828   *
829   * <p>These heuristics do not always match the behavior of the filesystem. In particular, consider
830   * the path {@code a/../b}, which {@code simplifyPath} will change to {@code b}. If {@code a} is a
831   * symlink to {@code x}, {@code a/../b} may refer to a sibling of {@code x}, rather than the
832   * sibling of {@code a} referred to by {@code b}.
833   *
834   * @since 11.0
835   */
836  @Beta
837  public static String simplifyPath(String pathname) {
838    checkNotNull(pathname);
839    if (pathname.length() == 0) {
840      return ".";
841    }
842
843    // split the path apart
844    Iterable<String> components = Splitter.on('/').omitEmptyStrings().split(pathname);
845    List<String> path = new ArrayList<>();
846
847    // resolve ., .., and //
848    for (String component : components) {
849      switch (component) {
850        case ".":
851          continue;
852        case "..":
853          if (path.size() > 0 && !path.get(path.size() - 1).equals("..")) {
854            path.remove(path.size() - 1);
855          } else {
856            path.add("..");
857          }
858          break;
859        default:
860          path.add(component);
861          break;
862      }
863    }
864
865    // put it back together
866    String result = Joiner.on('/').join(path);
867    if (pathname.charAt(0) == '/') {
868      result = "/" + result;
869    }
870
871    while (result.startsWith("/../")) {
872      result = result.substring(3);
873    }
874    if (result.equals("/..")) {
875      result = "/";
876    } else if ("".equals(result)) {
877      result = ".";
878    }
879
880    return result;
881  }
882
883  /**
884   * Returns the <a href="http://en.wikipedia.org/wiki/Filename_extension">file extension</a> for
885   * the given file name, or the empty string if the file has no extension. The result does not
886   * include the '{@code .}'.
887   *
888   * <p><b>Note:</b> This method simply returns everything after the last '{@code .}' in the file's
889   * name as determined by {@link File#getName}. It does not account for any filesystem-specific
890   * behavior that the {@link File} API does not already account for. For example, on NTFS it will
891   * report {@code "txt"} as the extension for the filename {@code "foo.exe:.txt"} even though NTFS
892   * will drop the {@code ":.txt"} part of the name when the file is actually created on the
893   * filesystem due to NTFS's <a href="https://goo.gl/vTpJi4">Alternate Data Streams</a>.
894   *
895   * @since 11.0
896   */
897  @Beta
898  public static String getFileExtension(String fullName) {
899    checkNotNull(fullName);
900    String fileName = new File(fullName).getName();
901    int dotIndex = fileName.lastIndexOf('.');
902    return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
903  }
904
905  /**
906   * Returns the file name without its <a
907   * href="http://en.wikipedia.org/wiki/Filename_extension">file extension</a> or path. This is
908   * similar to the {@code basename} unix command. The result does not include the '{@code .}'.
909   *
910   * @param file The name of the file to trim the extension from. This can be either a fully
911   *     qualified file name (including a path) or just a file name.
912   * @return The file name without its path or extension.
913   * @since 14.0
914   */
915  @Beta
916  public static String getNameWithoutExtension(String file) {
917    checkNotNull(file);
918    String fileName = new File(file).getName();
919    int dotIndex = fileName.lastIndexOf('.');
920    return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex);
921  }
922
923  /**
924   * Returns a {@link TreeTraverser} instance for {@link File} trees.
925   *
926   * <p><b>Warning:</b> {@code File} provides no support for symbolic links, and as such there is no
927   * way to ensure that a symbolic link to a directory is not followed when traversing the tree. In
928   * this case, iterables created by this traverser could contain files that are outside of the
929   * given directory or even be infinite if there is a symbolic link loop.
930   *
931   * @since 15.0
932   * @deprecated The returned {@link TreeTraverser} type is deprecated. Use the replacement method
933   *     {@link #fileTraverser()} instead with the same semantics as this method.
934   */
935  @Deprecated
936  public static TreeTraverser<File> fileTreeTraverser() {
937    return FILE_TREE_TRAVERSER;
938  }
939
940  private static final TreeTraverser<File> FILE_TREE_TRAVERSER =
941      new TreeTraverser<File>() {
942        @Override
943        public Iterable<File> children(File file) {
944          return fileTreeChildren(file);
945        }
946
947        @Override
948        public String toString() {
949          return "Files.fileTreeTraverser()";
950        }
951      };
952
953  /**
954   * Returns a {@link Traverser} instance for the file and directory tree. The returned traverser
955   * starts from a {@link File} and will return all files and directories it encounters.
956   *
957   * <p><b>Warning:</b> {@code File} provides no support for symbolic links, and as such there is no
958   * way to ensure that a symbolic link to a directory is not followed when traversing the tree. In
959   * this case, iterables created by this traverser could contain files that are outside of the
960   * given directory or even be infinite if there is a symbolic link loop.
961   *
962   * <p>If available, consider using {@link MoreFiles#fileTraverser()} instead. It behaves the same
963   * except that it doesn't follow symbolic links and returns {@code Path} instances.
964   *
965   * <p>If the {@link File} passed to one of the {@link Traverser} methods does not exist or is not
966   * a directory, no exception will be thrown and the returned {@link Iterable} will contain a
967   * single element: that file.
968   *
969   * <p>Example: {@code Files.fileTraverser().depthFirstPreOrder(new File("/"))} may return files
970   * with the following paths: {@code ["/", "/etc", "/etc/config.txt", "/etc/fonts", "/home",
971   * "/home/alice", ...]}
972   *
973   * @since 23.5
974   */
975  @Beta
976  public static Traverser<File> fileTraverser() {
977    return Traverser.forTree(FILE_TREE);
978  }
979
980  private static final SuccessorsFunction<File> FILE_TREE =
981      new SuccessorsFunction<File>() {
982        @Override
983        public Iterable<File> successors(File file) {
984          return fileTreeChildren(file);
985        }
986      };
987
988  private static Iterable<File> fileTreeChildren(File file) {
989    // check isDirectory() just because it may be faster than listFiles() on a non-directory
990    if (file.isDirectory()) {
991      File[] files = file.listFiles();
992      if (files != null) {
993        return Collections.unmodifiableList(Arrays.asList(files));
994      }
995    }
996
997    return Collections.emptyList();
998  }
999
1000  /**
1001   * Returns a predicate that returns the result of {@link File#isDirectory} on input files.
1002   *
1003   * @since 15.0
1004   */
1005  @Beta
1006  public static Predicate<File> isDirectory() {
1007    return FilePredicate.IS_DIRECTORY;
1008  }
1009
1010  /**
1011   * Returns a predicate that returns the result of {@link File#isFile} on input files.
1012   *
1013   * @since 15.0
1014   */
1015  @Beta
1016  public static Predicate<File> isFile() {
1017    return FilePredicate.IS_FILE;
1018  }
1019
1020  private enum FilePredicate implements Predicate<File> {
1021    IS_DIRECTORY {
1022      @Override
1023      public boolean apply(File file) {
1024        return file.isDirectory();
1025      }
1026
1027      @Override
1028      public String toString() {
1029        return "Files.isDirectory()";
1030      }
1031    },
1032
1033    IS_FILE {
1034      @Override
1035      public boolean apply(File file) {
1036        return file.isFile();
1037      }
1038
1039      @Override
1040      public String toString() {
1041        return "Files.isFile()";
1042      }
1043    }
1044  }
1045}