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