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    
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          @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    }