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.0
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.0
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       * <b>Deprecated.</b> This method suffers from poor symlink detection and race
493       * conditions. This functionality can be supported suitably only by shelling
494       * out to an operating system command such as {@code rm -rf} or {@code del
495       * /s}. This method is scheduled to be removed from Guava in Guava release
496       * 11.0.
497       *
498       * <p>Deletes all the files within a directory. Does not delete the
499       * directory itself.
500       *
501       * <p>If the file argument is a symbolic link or there is a symbolic
502       * link in the path leading to the directory, this method will do
503       * nothing. Symbolic links within the directory are not followed.
504       *
505       * @param directory the directory to delete the contents of
506       * @throws IllegalArgumentException if the argument is not a directory
507       * @throws IOException if an I/O error occurs
508       */
509      @Deprecated
510      public static void deleteDirectoryContents(File directory)
511          throws IOException {
512        Preconditions.checkArgument(directory.isDirectory(),
513            "Not a directory: %s", directory);
514        // Symbolic links will have different canonical and absolute paths
515        if (!directory.getCanonicalPath().equals(directory.getAbsolutePath())) {
516          return;
517        }
518        File[] files = directory.listFiles();
519        if (files == null) {
520          throw new IOException("Error listing files for " + directory);
521        }
522        for (File file : files) {
523          deleteRecursively(file);
524        }
525      }
526    
527      /**
528       * <b>Deprecated.</b> This method suffers from poor symlink detection and race
529       * conditions. This functionality can be supported suitably only by shelling
530       * out to an operating system command such as {@code rm -rf} or {@code del
531       * /s}. This method is scheduled to be removed from Guava in Guava release
532       * 11.0.
533       *
534       * <p>Deletes a file or directory and all contents recursively.
535       *
536       * <p>If the file argument is a symbolic link the link will be deleted
537       * but not the target of the link. If the argument is a directory,
538       * symbolic links within the directory will not be followed.
539       *
540       * @param file the file to delete
541       * @throws IOException if an I/O error occurs
542       */
543      @Deprecated
544      public static void deleteRecursively(File file) throws IOException {
545        if (file.isDirectory()) {
546          deleteDirectoryContents(file);
547        }
548        if (!file.delete()) {
549          throw new IOException("Failed to delete " + file);
550        }
551      }
552    
553      /**
554       * Reads the first line from a file. The line does not include
555       * line-termination characters, but does include other leading and
556       * trailing whitespace.
557       *
558       * @param file the file to read from
559       * @param charset the character set used when writing the file
560       * @return the first line, or null if the file is empty
561       * @throws IOException if an I/O error occurs
562       */
563      public static String readFirstLine(File file, Charset charset)
564          throws IOException {
565        return CharStreams.readFirstLine(Files.newReaderSupplier(file, charset));
566      }
567    
568      /**
569       * Reads all of the lines from a file. The lines do not include
570       * line-termination characters, but do include other leading and
571       * trailing whitespace.
572       *
573       * @param file the file to read from
574       * @param charset the character set used when writing the file
575       * @return a mutable {@link List} containing all the lines
576       * @throws IOException if an I/O error occurs
577       */
578      public static List<String> readLines(File file, Charset charset)
579          throws IOException {
580        return CharStreams.readLines(Files.newReaderSupplier(file, charset));
581      }
582    
583      /**
584       * Streams lines from a {@link File}, stopping when our callback returns
585       * false, or we have read all of the lines.
586       *
587       * @param file the file to read from
588       * @param charset the character set used when writing the file
589       * @param callback the {@link LineProcessor} to use to handle the lines
590       * @return the output of processing the lines
591       * @throws IOException if an I/O error occurs
592       */
593      public static <T> T readLines(File file, Charset charset,
594          LineProcessor<T> callback) throws IOException {
595        return CharStreams.readLines(Files.newReaderSupplier(file, charset),
596            callback);
597      }
598    
599      /**
600       * Process the bytes of a file.
601       *
602       * <p>(If this seems too complicated, maybe you're looking for
603       * {@link #toByteArray}.)
604       *
605       * @param file the file to read
606       * @param processor the object to which the bytes of the file are passed.
607       * @return the result of the byte processor
608       * @throws IOException if an I/O error occurs
609       */
610      public static <T> T readBytes(File file, ByteProcessor<T> processor)
611          throws IOException {
612        return ByteStreams.readBytes(newInputStreamSupplier(file), processor);
613      }
614    
615      /**
616       * Computes and returns the checksum value for a file.
617       * The checksum object is reset when this method returns successfully.
618       *
619       * @param file the file to read
620       * @param checksum the checksum object
621       * @return the result of {@link Checksum#getValue} after updating the
622       *     checksum object with all of the bytes in the file
623       * @throws IOException if an I/O error occurs
624       */
625      public static long getChecksum(File file, Checksum checksum)
626          throws IOException {
627        return ByteStreams.getChecksum(newInputStreamSupplier(file), checksum);
628      }
629    
630      /**
631       * Computes and returns the digest value for a file.
632       * The digest object is reset when this method returns successfully.
633       *
634       * @param file the file to read
635       * @param md the digest object
636       * @return the result of {@link MessageDigest#digest()} after updating the
637       *     digest object with all of the bytes in this file
638       * @throws IOException if an I/O error occurs
639       */
640      public static byte[] getDigest(File file, MessageDigest md)
641          throws IOException {
642        return ByteStreams.getDigest(newInputStreamSupplier(file), md);
643      }
644    
645      /**
646       * Fully maps a file read-only in to memory as per
647       * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
648       *
649       * <p>Files are mapped from offset 0 to its length.
650       *
651       * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
652       *
653       * @param file the file to map
654       * @return a read-only buffer reflecting {@code file}
655       * @throws FileNotFoundException if the {@code file} does not exist
656       * @throws IOException if an I/O error occurs
657       *
658       * @see FileChannel#map(MapMode, long, long)
659       * @since 2.0
660       */
661      public static MappedByteBuffer map(File file) throws IOException {
662        return map(file, MapMode.READ_ONLY);
663      }
664    
665      /**
666       * Fully maps a file in to memory as per
667       * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
668       * using the requested {@link MapMode}.
669       *
670       * <p>Files are mapped from offset 0 to its length.
671       *
672       * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
673       *
674       * @param file the file to map
675       * @param mode the mode to use when mapping {@code file}
676       * @return a buffer reflecting {@code file}
677       * @throws FileNotFoundException if the {@code file} does not exist
678       * @throws IOException if an I/O error occurs
679       *
680       * @see FileChannel#map(MapMode, long, long)
681       * @since 2.0
682       */
683      public static MappedByteBuffer map(File file, MapMode mode)
684          throws IOException {
685        if (!file.exists()) {
686          throw new FileNotFoundException(file.toString());
687        }
688        return map(file, mode, file.length());
689      }
690    
691      /**
692       * Maps a file in to memory as per
693       * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
694       * using the requested {@link MapMode}.
695       *
696       * <p>Files are mapped from offset 0 to {@code size}.
697       *
698       * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist,
699       * it will be created with the requested {@code size}. Thus this method is
700       * useful for creating memory mapped files which do not yet exist.
701       *
702       * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
703       *
704       * @param file the file to map
705       * @param mode the mode to use when mapping {@code file}
706       * @return a buffer reflecting {@code file}
707       * @throws IOException if an I/O error occurs
708       *
709       * @see FileChannel#map(MapMode, long, long)
710       * @since 2.0
711       */
712      public static MappedByteBuffer map(File file, MapMode mode, long size)
713          throws FileNotFoundException, IOException {
714        RandomAccessFile raf =
715            new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw");
716    
717        boolean threw = true;
718        try {
719          MappedByteBuffer mbb = map(raf, mode, size);
720          threw = false;
721          return mbb;
722        } finally {
723          Closeables.close(raf, threw);
724        }
725      }
726    
727      private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode,
728          long size) throws IOException {
729        FileChannel channel = raf.getChannel();
730    
731        boolean threw = true;
732        try {
733          MappedByteBuffer mbb = channel.map(mode, 0, size);
734          threw = false;
735          return mbb;
736        } finally {
737          Closeables.close(channel, threw);
738        }
739      }
740    
741      private static boolean sep(char[] a, int pos) {
742        return (pos >= a.length) || (a[pos] == '/');
743      }
744    
745    }