001/*
002 * Copyright (C) 2007 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.io;
018
019import com.google.common.annotations.Beta;
020import com.google.common.base.Preconditions;
021
022import java.io.BufferedReader;
023import java.io.BufferedWriter;
024import java.io.Closeable;
025import java.io.File;
026import java.io.FileInputStream;
027import java.io.FileNotFoundException;
028import java.io.FileOutputStream;
029import java.io.IOException;
030import java.io.InputStream;
031import java.io.InputStreamReader;
032import java.io.OutputStream;
033import java.io.OutputStreamWriter;
034import java.io.RandomAccessFile;
035import java.nio.MappedByteBuffer;
036import java.nio.channels.FileChannel;
037import java.nio.channels.FileChannel.MapMode;
038import java.nio.charset.Charset;
039import java.security.MessageDigest;
040import java.util.List;
041import java.util.zip.Checksum;
042
043/**
044 * Provides utility methods for working with files.
045 *
046 * <p>All method parameters must be non-null unless documented otherwise.
047 *
048 * @author Chris Nokleberg
049 * @since 1
050 */
051@Beta
052public final class Files {
053
054  /** Maximum loop count when creating temp directories. */
055  private static final int TEMP_DIR_ATTEMPTS = 10000;
056
057  private Files() {}
058
059  /**
060   * Returns a buffered reader that reads from a file using the given
061   * character set.
062   *
063   * @param file the file to read from
064   * @param charset the character set used when writing the file
065   * @return the buffered reader
066   */
067  public static BufferedReader newReader(File file, Charset charset)
068      throws FileNotFoundException {
069    return new BufferedReader(
070        new InputStreamReader(new FileInputStream(file), charset));
071  }
072
073  /**
074   * Returns a buffered writer that writes to a file using the given
075   * character set.
076   *
077   * @param file the file to write to
078   * @param charset the character set used when writing the file
079   * @return the buffered writer
080   */
081  public static BufferedWriter newWriter(File file, Charset charset)
082      throws FileNotFoundException {
083   return new BufferedWriter(
084        new OutputStreamWriter(new FileOutputStream(file), charset));
085  }
086
087  /**
088   * Returns a factory that will supply instances of {@link FileInputStream}
089   * that read from a file.
090   *
091   * @param file the file to read from
092   * @return the factory
093   */
094  public static InputSupplier<FileInputStream> newInputStreamSupplier(
095      final File file) {
096    Preconditions.checkNotNull(file);
097    return new InputSupplier<FileInputStream>() {
098      @Override
099      public FileInputStream getInput() throws IOException {
100        return new FileInputStream(file);
101      }
102    };
103  }
104
105  /**
106   * Returns a factory that will supply instances of {@link FileOutputStream}
107   * that write to a file.
108   *
109   * @param file the file to write to
110   * @return the factory
111   */
112  public static OutputSupplier<FileOutputStream> newOutputStreamSupplier(
113      File file) {
114    return newOutputStreamSupplier(file, false);
115  }
116
117  /**
118   * Returns a factory that will supply instances of {@link FileOutputStream}
119   * that write to or append to a file.
120   *
121   * @param file the file to write to
122   * @param append if true, the encoded characters will be appended to the file;
123   *     otherwise the file is overwritten
124   * @return the factory
125   */
126  public static OutputSupplier<FileOutputStream> newOutputStreamSupplier(
127      final File file, final boolean append) {
128    Preconditions.checkNotNull(file);
129    return new OutputSupplier<FileOutputStream>() {
130      @Override
131      public FileOutputStream getOutput() throws IOException {
132        return new FileOutputStream(file, append);
133      }
134    };
135  }
136
137  /**
138   * Returns a factory that will supply instances of
139   * {@link InputStreamReader} that read a file using the given character set.
140   *
141   * @param file the file to read from
142   * @param charset the character set used when reading the file
143   * @return the factory
144   */
145  public static InputSupplier<InputStreamReader> newReaderSupplier(File file,
146      Charset charset) {
147    return CharStreams.newReaderSupplier(newInputStreamSupplier(file), charset);
148  }
149
150  /**
151   * Returns a factory that will supply instances of {@link OutputStreamWriter}
152   * that write to a file using the given character set.
153   *
154   * @param file the file to write to
155   * @param charset the character set used when writing the file
156   * @return the factory
157   */
158  public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file,
159      Charset charset) {
160    return newWriterSupplier(file, charset, false);
161  }
162
163  /**
164   * Returns a factory that will supply instances of {@link OutputStreamWriter}
165   * that write to or append to a file using the given character set.
166   *
167   * @param file the file to write to
168   * @param charset the character set used when writing the file
169   * @param append if true, the encoded characters will be appended to the file;
170   *     otherwise the file is overwritten
171   * @return the factory
172   */
173  public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file,
174      Charset charset, boolean append) {
175    return CharStreams.newWriterSupplier(newOutputStreamSupplier(file, append),
176        charset);
177  }
178
179  /**
180   * Reads all bytes from a file into a byte array.
181   *
182   * @param file the file to read from
183   * @return a byte array containing all the bytes from file
184   * @throws IllegalArgumentException if the file is bigger than the largest
185   *     possible byte array (2^31 - 1)
186   * @throws IOException if an I/O error occurs
187   */
188  public static byte[] toByteArray(File file) throws IOException {
189    Preconditions.checkArgument(file.length() <= Integer.MAX_VALUE);
190    if (file.length() == 0) {
191      // Some special files are length 0 but have content nonetheless.
192      return ByteStreams.toByteArray(newInputStreamSupplier(file));
193    } else {
194      // Avoid an extra allocation and copy.
195      byte[] b = new byte[(int) file.length()];
196      boolean threw = true;
197      InputStream in = new FileInputStream(file);
198      try {
199        ByteStreams.readFully(in, b);
200        threw = false;
201      } finally {
202        Closeables.close(in, threw);
203      }
204      return b;
205    }
206  }
207
208  /**
209   * Reads all characters from a file into a {@link String}, using the given
210   * character set.
211   *
212   * @param file the file to read from
213   * @param charset the character set used when reading the file
214   * @return a string containing all the characters from the file
215   * @throws IOException if an I/O error occurs
216   */
217  public static String toString(File file, Charset charset) throws IOException {
218    return new String(toByteArray(file), charset.name());
219  }
220
221  /**
222   * Copies to a file all bytes from an {@link InputStream} supplied by a
223   * factory.
224   *
225   * @param from the input factory
226   * @param to the destination file
227   * @throws IOException if an I/O error occurs
228   */
229  public static void copy(InputSupplier<? extends InputStream> from, File to)
230      throws IOException {
231    ByteStreams.copy(from, newOutputStreamSupplier(to));
232  }
233
234  /**
235   * Overwrites a file with the contents of a byte array.
236   *
237   * @param from the bytes to write
238   * @param to the destination file
239   * @throws IOException if an I/O error occurs
240   */
241  public static void write(byte[] from, File to) throws IOException {
242    ByteStreams.write(from, newOutputStreamSupplier(to));
243  }
244
245  /**
246   * Copies all bytes from a file to an {@link OutputStream} supplied by
247   * a factory.
248   *
249   * @param from the source file
250   * @param to the output factory
251   * @throws IOException if an I/O error occurs
252   */
253  public static void copy(File from, OutputSupplier<? extends OutputStream> to)
254      throws IOException {
255    ByteStreams.copy(newInputStreamSupplier(from), to);
256  }
257
258  /**
259   * Copies all bytes from a file to an output stream.
260   *
261   * @param from the source file
262   * @param to the output stream
263   * @throws IOException if an I/O error occurs
264   */
265  public static void copy(File from, OutputStream to) throws IOException {
266    ByteStreams.copy(newInputStreamSupplier(from), to);
267  }
268
269  /**
270   * Copies all the bytes from one file to another.
271   *.
272   * @param from the source file
273   * @param to the destination file
274   * @throws IOException if an I/O error occurs
275   */
276  public static void copy(File from, File to) throws IOException {
277    copy(newInputStreamSupplier(from), to);
278  }
279
280  /**
281   * Copies to a file all characters from a {@link Readable} and
282   * {@link Closeable} object supplied by a factory, using the given
283   * character set.
284   *
285   * @param from the readable supplier
286   * @param to the destination file
287   * @param charset the character set used when writing the file
288   * @throws IOException if an I/O error occurs
289   */
290  public static <R extends Readable & Closeable> void copy(
291      InputSupplier<R> from, File to, Charset charset) throws IOException {
292    CharStreams.copy(from, newWriterSupplier(to, charset));
293  }
294
295  /**
296   * Writes a character sequence (such as a string) to a file using the given
297   * character set.
298   *
299   * @param from the character sequence to write
300   * @param to the destination file
301   * @param charset the character set used when writing the file
302   * @throws IOException if an I/O error occurs
303   */
304  public static void write(CharSequence from, File to, Charset charset)
305      throws IOException {
306    write(from, to, charset, false);
307  }
308
309  /**
310   * Appends a character sequence (such as a string) to a file using the given
311   * character set.
312   *
313   * @param from the character sequence to append
314   * @param to the destination file
315   * @param charset the character set used when writing the file
316   * @throws IOException if an I/O error occurs
317   */
318  public static void append(CharSequence from, File to, Charset charset)
319      throws IOException {
320    write(from, to, charset, true);
321  }
322
323  /**
324   * Private helper method. Writes a character sequence to a file,
325   * optionally appending.
326   *
327   * @param from the character sequence to append
328   * @param to the destination file
329   * @param charset the character set used when writing the file
330   * @param append true to append, false to overwrite
331   * @throws IOException if an I/O error occurs
332   */
333  private static void write(CharSequence from, File to, Charset charset,
334      boolean append) throws IOException {
335    CharStreams.write(from, newWriterSupplier(to, charset, append));
336  }
337
338  /**
339   * Copies all characters from a file to a {@link Appendable} &
340   * {@link Closeable} object supplied by a factory, using the given
341   * character set.
342   *
343   * @param from the source file
344   * @param charset the character set used when reading the file
345   * @param to the appendable supplier
346   * @throws IOException if an I/O error occurs
347   */
348  public static <W extends Appendable & Closeable> void copy(File from,
349      Charset charset, OutputSupplier<W> to) throws IOException {
350    CharStreams.copy(newReaderSupplier(from, charset), to);
351  }
352
353  /**
354   * Copies all characters from a file to an appendable object,
355   * using the given character set.
356   *
357   * @param from the source file
358   * @param charset the character set used when reading the file
359   * @param to the appendable object
360   * @throws IOException if an I/O error occurs
361   */
362  public static void copy(File from, Charset charset, Appendable to)
363      throws IOException {
364    CharStreams.copy(newReaderSupplier(from, charset), to);
365  }
366
367  /**
368   * Returns true if the files contains the same bytes.
369   *
370   * @throws IOException if an I/O error occurs
371   */
372  public static boolean equal(File file1, File file2) throws IOException {
373    if (file1 == file2 || file1.equals(file2)) {
374      return true;
375    }
376
377    /*
378     * Some operating systems may return zero as the length for files
379     * denoting system-dependent entities such as devices or pipes, in
380     * which case we must fall back on comparing the bytes directly.
381     */
382    long len1 = file1.length();
383    long len2 = file2.length();
384    if (len1 != 0 && len2 != 0 && len1 != len2) {
385      return false;
386    }
387    return ByteStreams.equal(newInputStreamSupplier(file1),
388        newInputStreamSupplier(file2));
389  }
390
391  /**
392   * Atomically creates a new directory somewhere beneath the system's
393   * temporary directory (as defined by the {@code java.io.tmpdir} system
394   * property), and returns its name.
395   *
396   * <p>Use this method instead of {@link File#createTempFile(String, String)}
397   * when you wish to create a directory, not a regular file.  A common pitfall
398   * is to call {@code createTempFile}, delete the file and create a
399   * directory in its place, but this leads a race condition which can be
400   * exploited to create security vulnerabilities, especially when executable
401   * files are to be written into the directory.
402   *
403   * <p>This method assumes that the temporary volume is writable, has free
404   * inodes and free blocks, and that it will not be called thousands of times
405   * per second.
406   *
407   * @return the newly-created directory
408   * @throws IllegalStateException if the directory could not be created
409   */
410  public static File createTempDir() {
411    File baseDir = new File(System.getProperty("java.io.tmpdir"));
412    String baseName = System.currentTimeMillis() + "-";
413
414    for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
415      File tempDir = new File(baseDir, baseName + counter);
416      if (tempDir.mkdir()) {
417        return tempDir;
418      }
419    }
420    throw new IllegalStateException("Failed to create directory within "
421        + TEMP_DIR_ATTEMPTS + " attempts (tried "
422        + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
423  }
424
425  /**
426   * Creates an empty file or updates the last updated timestamp on the
427   * same as the unix command of the same name.
428   *
429   * @param file the file to create or update
430   * @throws IOException if an I/O error occurs
431   */
432  public static void touch(File file) throws IOException {
433    if (!file.createNewFile()
434        && !file.setLastModified(System.currentTimeMillis())) {
435      throw new IOException("Unable to update modification time of " + file);
436    }
437  }
438
439  /**
440   * Creates any necessary but nonexistent parent directories of the specified
441   * file. Note that if this operation fails it may have succeeded in creating
442   * some (but not all) of the necessary parent directories.
443   *
444   * @throws IOException if an I/O error occurs, or if any necessary but
445   *     nonexistent parent directories of the specified file could not be
446   *     created.
447   * @since 4
448   */
449  public static void createParentDirs(File file) throws IOException {
450    File parent = file.getCanonicalFile().getParentFile();
451    if (parent == null) {
452      /*
453       * The given directory is a filesystem root. All zero of its ancestors
454       * exist. This doesn't mean that the root itself exists -- consider x:\ on
455       * a Windows machine without such a drive -- or even that the caller can
456       * create it, but this method makes no such guarantees even for non-root
457       * files.
458       */
459      return;
460    }
461    parent.mkdirs();
462    if (!parent.isDirectory()) {
463      throw new IOException("Unable to create parent directories of " + file);
464    }
465  }
466
467  /**
468   * Moves the file from one path to another. This method can rename a file or
469   * move it to a different directory, like the Unix {@code mv} command.
470   *
471   * @param from the source file
472   * @param to the destination file
473   * @throws IOException if an I/O error occurs
474   */
475  public static void move(File from, File to) throws IOException {
476    Preconditions.checkNotNull(to);
477    Preconditions.checkArgument(!from.equals(to),
478        "Source %s and destination %s must be different", from, to);
479
480    if (!from.renameTo(to)) {
481      copy(from, to);
482      if (!from.delete()) {
483        if (!to.delete()) {
484          throw new IOException("Unable to delete " + to);
485        }
486        throw new IOException("Unable to delete " + from);
487      }
488    }
489  }
490
491  /**
492   * Deletes all the files within a directory. Does not delete the
493   * directory itself.
494   *
495   * <p>If the file argument is a symbolic link or there is a symbolic
496   * link in the path leading to the directory, this method will do
497   * nothing. Symbolic links within the directory are not followed.
498   *
499   * @param directory the directory to delete the contents of
500   * @throws IllegalArgumentException if the argument is not a directory
501   * @throws IOException if an I/O error occurs
502   * @see #deleteRecursively
503   */
504  public static void deleteDirectoryContents(File directory)
505      throws IOException {
506    Preconditions.checkArgument(directory.isDirectory(),
507        "Not a directory: %s", directory);
508    // Symbolic links will have different canonical and absolute paths
509    if (!directory.getCanonicalPath().equals(directory.getAbsolutePath())) {
510      return;
511    }
512    File[] files = directory.listFiles();
513    if (files == null) {
514      throw new IOException("Error listing files for " + directory);
515    }
516    for (File file : files) {
517      deleteRecursively(file);
518    }
519  }
520
521  /**
522   * Deletes a file or directory and all contents recursively.
523   *
524   * <p>If the file argument is a symbolic link the link will be deleted
525   * but not the target of the link. If the argument is a directory,
526   * symbolic links within the directory will not be followed.
527   *
528   * @param file the file to delete
529   * @throws IOException if an I/O error occurs
530   * @see #deleteDirectoryContents
531   */
532  public static void deleteRecursively(File file) throws IOException {
533    if (file.isDirectory()) {
534      deleteDirectoryContents(file);
535    }
536    if (!file.delete()) {
537      throw new IOException("Failed to delete " + file);
538    }
539  }
540
541  /**
542   * Reads the first line from a file. The line does not include
543   * line-termination characters, but does include other leading and
544   * trailing whitespace.
545   *
546   * @param file the file to read from
547   * @param charset the character set used when writing the file
548   * @return the first line, or null if the file is empty
549   * @throws IOException if an I/O error occurs
550   */
551  public static String readFirstLine(File file, Charset charset)
552      throws IOException {
553    return CharStreams.readFirstLine(Files.newReaderSupplier(file, charset));
554  }
555
556  /**
557   * Reads all of the lines from a file. The lines do not include
558   * line-termination characters, but do include other leading and
559   * trailing whitespace.
560   *
561   * @param file the file to read from
562   * @param charset the character set used when writing the file
563   * @return a mutable {@link List} containing all the lines
564   * @throws IOException if an I/O error occurs
565   */
566  public static List<String> readLines(File file, Charset charset)
567      throws IOException {
568    return CharStreams.readLines(Files.newReaderSupplier(file, charset));
569  }
570
571  /**
572   * Streams lines from a {@link File}, stopping when our callback returns
573   * false, or we have read all of the lines.
574   *
575   * @param file the file to read from
576   * @param charset the character set used when writing the file
577   * @param callback the {@link LineProcessor} to use to handle the lines
578   * @return the output of processing the lines
579   * @throws IOException if an I/O error occurs
580   */
581  public static <T> T readLines(File file, Charset charset,
582      LineProcessor<T> callback) throws IOException {
583    return CharStreams.readLines(Files.newReaderSupplier(file, charset),
584        callback);
585  }
586
587  /**
588   * Process the bytes of a file.
589   *
590   * <p>(If this seems too complicated, maybe you're looking for
591   * {@link #toByteArray}.)
592   *
593   * @param file the file to read
594   * @param processor the object to which the bytes of the file are passed.
595   * @return the result of the byte processor
596   * @throws IOException if an I/O error occurs
597   */
598  public static <T> T readBytes(File file, ByteProcessor<T> processor)
599      throws IOException {
600    return ByteStreams.readBytes(newInputStreamSupplier(file), processor);
601  }
602
603  /**
604   * Computes and returns the checksum value for a file.
605   * The checksum object is reset when this method returns successfully.
606   *
607   * @param file the file to read
608   * @param checksum the checksum object
609   * @return the result of {@link Checksum#getValue} after updating the
610   *     checksum object with all of the bytes in the file
611   * @throws IOException if an I/O error occurs
612   */
613  public static long getChecksum(File file, Checksum checksum)
614      throws IOException {
615    return ByteStreams.getChecksum(newInputStreamSupplier(file), checksum);
616  }
617
618  /**
619   * Computes and returns the digest value for a file.
620   * The digest object is reset when this method returns successfully.
621   *
622   * @param file the file to read
623   * @param md the digest object
624   * @return the result of {@link MessageDigest#digest()} after updating the
625   *     digest object with all of the bytes in this file
626   * @throws IOException if an I/O error occurs
627   */
628  public static byte[] getDigest(File file, MessageDigest md)
629      throws IOException {
630    return ByteStreams.getDigest(newInputStreamSupplier(file), md);
631  }
632
633  /**
634   * Fully maps a file read-only in to memory as per
635   * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
636   *
637   * <p>Files are mapped from offset 0 to its length.
638   *
639   * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
640   *
641   * @param file the file to map
642   * @return a read-only buffer reflecting {@code file}
643   * @throws FileNotFoundException if the {@code file} does not exist
644   * @throws IOException if an I/O error occurs
645   *
646   * @see FileChannel#map(MapMode, long, long)
647   * @since 2
648   */
649  public static MappedByteBuffer map(File file) throws IOException {
650    return map(file, MapMode.READ_ONLY);
651  }
652
653  /**
654   * Fully maps a file in to memory as per
655   * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
656   * using the requested {@link MapMode}.
657   *
658   * <p>Files are mapped from offset 0 to its length.
659   *
660   * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
661   *
662   * @param file the file to map
663   * @param mode the mode to use when mapping {@code file}
664   * @return a buffer reflecting {@code file}
665   * @throws FileNotFoundException if the {@code file} does not exist
666   * @throws IOException if an I/O error occurs
667   *
668   * @see FileChannel#map(MapMode, long, long)
669   * @since 2
670   */
671  public static MappedByteBuffer map(File file, MapMode mode)
672      throws IOException {
673    if (!file.exists()) {
674      throw new FileNotFoundException(file.toString());
675    }
676    return map(file, mode, file.length());
677  }
678
679  /**
680   * Maps a file in to memory as per
681   * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
682   * using the requested {@link MapMode}.
683   *
684   * <p>Files are mapped from offset 0 to {@code size}.
685   *
686   * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist,
687   * it will be created with the requested {@code size}. Thus this method is
688   * useful for creating memory mapped files which do not yet exist.
689   *
690   * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
691   *
692   * @param file the file to map
693   * @param mode the mode to use when mapping {@code file}
694   * @return a buffer reflecting {@code file}
695   * @throws IOException if an I/O error occurs
696   *
697   * @see FileChannel#map(MapMode, long, long)
698   * @since 2
699   */
700  public static MappedByteBuffer map(File file, MapMode mode, long size)
701      throws FileNotFoundException, IOException {
702    RandomAccessFile raf =
703        new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw");
704
705    boolean threw = true;
706    try {
707      MappedByteBuffer mbb = map(raf, mode, size);
708      threw = false;
709      return mbb;
710    } finally {
711      Closeables.close(raf, threw);
712    }
713  }
714
715  private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode,
716      long size) throws IOException {
717    FileChannel channel = raf.getChannel();
718
719    boolean threw = true;
720    try {
721      MappedByteBuffer mbb = channel.map(mode, 0, size);
722      threw = false;
723      return mbb;
724    } finally {
725      Closeables.close(channel, threw);
726    }
727  }
728}