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