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