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