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        // TODO: return if parent is null
450        parent.mkdirs();
451        if (!parent.exists()) { // TODO: change to isDirectory
452          throw new IOException("Unable to create parent directories of " + file);
453        }
454      }
455    
456      /**
457       * Moves the file from one path to another. This method can rename a file or
458       * move it to a different directory, like the Unix {@code mv} command.
459       *
460       * @param from the source file
461       * @param to the destination file
462       * @throws IOException if an I/O error occurs
463       */
464      public static void move(File from, File to) throws IOException {
465        Preconditions.checkNotNull(to);
466        Preconditions.checkArgument(!from.equals(to),
467            "Source %s and destination %s must be different", from, to);
468    
469        if (!from.renameTo(to)) {
470          copy(from, to);
471          if (!from.delete()) {
472            if (!to.delete()) {
473              throw new IOException("Unable to delete " + to);
474            }
475            throw new IOException("Unable to delete " + from);
476          }
477        }
478      }
479    
480      /**
481       * Deletes all the files within a directory. Does not delete the
482       * directory itself.
483       *
484       * <p>If the file argument is a symbolic link or there is a symbolic
485       * link in the path leading to the directory, this method will do
486       * nothing. Symbolic links within the directory are not followed.
487       *
488       * @param directory the directory to delete the contents of
489       * @throws IllegalArgumentException if the argument is not a directory
490       * @throws IOException if an I/O error occurs
491       * @see #deleteRecursively
492       */
493      public static void deleteDirectoryContents(File directory)
494          throws IOException {
495        Preconditions.checkArgument(directory.isDirectory(),
496            "Not a directory: %s", directory);
497        // Symbolic links will have different canonical and absolute paths
498        if (!directory.getCanonicalPath().equals(directory.getAbsolutePath())) {
499          return;
500        }
501        File[] files = directory.listFiles();
502        if (files == null) {
503          throw new IOException("Error listing files for " + directory);
504        }
505        for (File file : files) {
506          deleteRecursively(file);
507        }
508      }
509    
510      /**
511       * Deletes a file or directory and all contents recursively.
512       *
513       * <p>If the file argument is a symbolic link the link will be deleted
514       * but not the target of the link. If the argument is a directory,
515       * symbolic links within the directory will not be followed.
516       *
517       * @param file the file to delete
518       * @throws IOException if an I/O error occurs
519       * @see #deleteDirectoryContents
520       */
521      public static void deleteRecursively(File file) throws IOException {
522        if (file.isDirectory()) {
523          deleteDirectoryContents(file);
524        }
525        if (!file.delete()) {
526          throw new IOException("Failed to delete " + file);
527        }
528      }
529    
530      /**
531       * Reads the first line from a file. The line does not include
532       * line-termination characters, but does include other leading and
533       * trailing whitespace.
534       *
535       * @param file the file to read from
536       * @param charset the character set used when writing the file
537       * @return the first line, or null if the file is empty
538       * @throws IOException if an I/O error occurs
539       */
540      public static String readFirstLine(File file, Charset charset)
541          throws IOException {
542        return CharStreams.readFirstLine(Files.newReaderSupplier(file, charset));
543      }
544    
545      /**
546       * Reads all of the lines from a file. The lines do not include
547       * line-termination characters, but do include other leading and
548       * trailing whitespace.
549       *
550       * @param file the file to read from
551       * @param charset the character set used when writing the file
552       * @return a mutable {@link List} containing all the lines
553       * @throws IOException if an I/O error occurs
554       */
555      public static List<String> readLines(File file, Charset charset)
556          throws IOException {
557        return CharStreams.readLines(Files.newReaderSupplier(file, charset));
558      }
559    
560      /**
561       * Streams lines from a {@link File}, stopping when our callback returns
562       * false, or we have read all of the lines.
563       *
564       * @param file the file to read from
565       * @param charset the character set used when writing the file
566       * @param callback the {@link LineProcessor} to use to handle the lines
567       * @return the output of processing the lines
568       * @throws IOException if an I/O error occurs
569       */
570      public static <T> T readLines(File file, Charset charset,
571          LineProcessor<T> callback) throws IOException {
572        return CharStreams.readLines(Files.newReaderSupplier(file, charset),
573            callback);
574      }
575    
576      /**
577       * Process the bytes of a file.
578       *
579       * <p>(If this seems too complicated, maybe you're looking for
580       * {@link #toByteArray}.)
581       *
582       * @param file the file to read
583       * @param processor the object to which the bytes of the file are passed.
584       * @return the result of the byte processor
585       * @throws IOException if an I/O error occurs
586       */
587      public static <T> T readBytes(File file, ByteProcessor<T> processor)
588          throws IOException {
589        return ByteStreams.readBytes(newInputStreamSupplier(file), processor);
590      }
591    
592      /**
593       * Computes and returns the checksum value for a file.
594       * The checksum object is reset when this method returns successfully.
595       *
596       * @param file the file to read
597       * @param checksum the checksum object
598       * @return the result of {@link Checksum#getValue} after updating the
599       *     checksum object with all of the bytes in the file
600       * @throws IOException if an I/O error occurs
601       */
602      public static long getChecksum(File file, Checksum checksum)
603          throws IOException {
604        return ByteStreams.getChecksum(newInputStreamSupplier(file), checksum);
605      }
606    
607      /**
608       * Computes and returns the digest value for a file.
609       * The digest object is reset when this method returns successfully.
610       *
611       * @param file the file to read
612       * @param md the digest object
613       * @return the result of {@link MessageDigest#digest()} after updating the
614       *     digest object with all of the bytes in this file
615       * @throws IOException if an I/O error occurs
616       */
617      public static byte[] getDigest(File file, MessageDigest md)
618          throws IOException {
619        return ByteStreams.getDigest(newInputStreamSupplier(file), md);
620      }
621    
622      /**
623       * Fully maps a file read-only in to memory as per
624       * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
625       *
626       * <p>Files are mapped from offset 0 to its length.
627       *
628       * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
629       *
630       * @param file the file to map
631       * @return a read-only buffer reflecting {@code file}
632       * @throws FileNotFoundException if the {@code file} does not exist
633       * @throws IOException if an I/O error occurs
634       *
635       * @see FileChannel#map(MapMode, long, long)
636       * @since 2
637       */
638      public static MappedByteBuffer map(File file) throws IOException {
639        return map(file, MapMode.READ_ONLY);
640      }
641    
642      /**
643       * Fully maps a file in to memory as per
644       * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
645       * using the requested {@link MapMode}.
646       *
647       * <p>Files are mapped from offset 0 to its length.
648       *
649       * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
650       *
651       * @param file the file to map
652       * @param mode the mode to use when mapping {@code file}
653       * @return a buffer reflecting {@code file}
654       * @throws FileNotFoundException if the {@code file} does not exist
655       * @throws IOException if an I/O error occurs
656       *
657       * @see FileChannel#map(MapMode, long, long)
658       * @since 2
659       */
660      public static MappedByteBuffer map(File file, MapMode mode)
661          throws IOException {
662        if (!file.exists()) {
663          throw new FileNotFoundException(file.toString());
664        }
665        return map(file, mode, file.length());
666      }
667    
668      /**
669       * Maps a file in to memory as per
670       * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
671       * using the requested {@link MapMode}.
672       *
673       * <p>Files are mapped from offset 0 to {@code size}.
674       *
675       * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist,
676       * it will be created with the requested {@code size}. Thus this method is
677       * useful for creating memory mapped files which do not yet exist.
678       *
679       * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
680       *
681       * @param file the file to map
682       * @param mode the mode to use when mapping {@code file}
683       * @return a buffer reflecting {@code file}
684       * @throws IOException if an I/O error occurs
685       *
686       * @see FileChannel#map(MapMode, long, long)
687       * @since 2
688       */
689      public static MappedByteBuffer map(File file, MapMode mode, long size)
690          throws FileNotFoundException, IOException {
691        RandomAccessFile raf =
692            new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw");
693    
694        boolean threw = true;
695        try {
696          MappedByteBuffer mbb = map(raf, mode, size);
697          threw = false;
698          return mbb;
699        } finally {
700          Closeables.close(raf, threw);
701        }
702      }
703    
704      private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode,
705          long size) throws IOException {
706        FileChannel channel = raf.getChannel();
707    
708        boolean threw = true;
709        try {
710          MappedByteBuffer mbb = channel.map(mode, 0, size);
711          threw = false;
712          return mbb;
713        } finally {
714          Closeables.close(channel, threw);
715        }
716      }
717    }