001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.io.file; 019 020import java.io.File; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.io.RandomAccessFile; 025import java.math.BigInteger; 026import java.net.URI; 027import java.net.URISyntaxException; 028import java.net.URL; 029import java.nio.charset.Charset; 030import java.nio.file.AccessDeniedException; 031import java.nio.file.CopyOption; 032import java.nio.file.DirectoryStream; 033import java.nio.file.FileVisitOption; 034import java.nio.file.FileVisitResult; 035import java.nio.file.FileVisitor; 036import java.nio.file.Files; 037import java.nio.file.LinkOption; 038import java.nio.file.NoSuchFileException; 039import java.nio.file.NotDirectoryException; 040import java.nio.file.OpenOption; 041import java.nio.file.Path; 042import java.nio.file.Paths; 043import java.nio.file.StandardOpenOption; 044import java.nio.file.attribute.AclEntry; 045import java.nio.file.attribute.AclFileAttributeView; 046import java.nio.file.attribute.BasicFileAttributes; 047import java.nio.file.attribute.DosFileAttributeView; 048import java.nio.file.attribute.DosFileAttributes; 049import java.nio.file.attribute.FileAttribute; 050import java.nio.file.attribute.FileTime; 051import java.nio.file.attribute.PosixFileAttributeView; 052import java.nio.file.attribute.PosixFileAttributes; 053import java.nio.file.attribute.PosixFilePermission; 054import java.time.Duration; 055import java.time.Instant; 056import java.time.chrono.ChronoZonedDateTime; 057import java.util.ArrayList; 058import java.util.Arrays; 059import java.util.Collection; 060import java.util.Collections; 061import java.util.Comparator; 062import java.util.EnumSet; 063import java.util.List; 064import java.util.Objects; 065import java.util.Set; 066import java.util.stream.Collector; 067import java.util.stream.Collectors; 068import java.util.stream.Stream; 069 070import org.apache.commons.io.Charsets; 071import org.apache.commons.io.FileUtils; 072import org.apache.commons.io.FilenameUtils; 073import org.apache.commons.io.IOUtils; 074import org.apache.commons.io.RandomAccessFileMode; 075import org.apache.commons.io.RandomAccessFiles; 076import org.apache.commons.io.ThreadUtils; 077import org.apache.commons.io.file.Counters.PathCounters; 078import org.apache.commons.io.file.attribute.FileTimes; 079import org.apache.commons.io.filefilter.IOFileFilter; 080import org.apache.commons.io.function.IOFunction; 081import org.apache.commons.io.function.IOSupplier; 082 083/** 084 * NIO Path utilities. 085 * 086 * @since 2.7 087 */ 088public final class PathUtils { 089 090 /** 091 * Private worker/holder that computes and tracks relative path names and their equality. We reuse the sorted relative lists when comparing directories. 092 */ 093 private static final class RelativeSortedPaths { 094 095 final boolean equals; 096 // final List<Path> relativeDirList1; // might need later? 097 // final List<Path> relativeDirList2; // might need later? 098 final List<Path> relativeFileList1; 099 final List<Path> relativeFileList2; 100 101 /** 102 * Constructs and initializes a new instance by accumulating directory and file info. 103 * 104 * @param dir1 First directory to compare. 105 * @param dir2 Seconds directory to compare. 106 * @param maxDepth See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 107 * @param linkOptions Options indicating how symbolic links are handled. 108 * @param fileVisitOptions See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 109 * @throws IOException if an I/O error is thrown by a visitor method. 110 */ 111 private RelativeSortedPaths(final Path dir1, final Path dir2, final int maxDepth, final LinkOption[] linkOptions, 112 final FileVisitOption[] fileVisitOptions) throws IOException { 113 final List<Path> tmpRelativeDirList1; 114 final List<Path> tmpRelativeDirList2; 115 List<Path> tmpRelativeFileList1 = null; 116 List<Path> tmpRelativeFileList2 = null; 117 if (dir1 == null && dir2 == null) { 118 equals = true; 119 } else if (dir1 == null ^ dir2 == null) { 120 equals = false; 121 } else { 122 final boolean parentDirNotExists1 = Files.notExists(dir1, linkOptions); 123 final boolean parentDirNotExists2 = Files.notExists(dir2, linkOptions); 124 if (parentDirNotExists1 || parentDirNotExists2) { 125 equals = parentDirNotExists1 && parentDirNotExists2; 126 } else { 127 final AccumulatorPathVisitor visitor1 = accumulate(dir1, maxDepth, fileVisitOptions); 128 final AccumulatorPathVisitor visitor2 = accumulate(dir2, maxDepth, fileVisitOptions); 129 if (visitor1.getDirList().size() != visitor2.getDirList().size() || visitor1.getFileList().size() != visitor2.getFileList().size()) { 130 equals = false; 131 } else { 132 tmpRelativeDirList1 = visitor1.relativizeDirectories(dir1, true, null); 133 tmpRelativeDirList2 = visitor2.relativizeDirectories(dir2, true, null); 134 if (!tmpRelativeDirList1.equals(tmpRelativeDirList2)) { 135 equals = false; 136 } else { 137 tmpRelativeFileList1 = visitor1.relativizeFiles(dir1, true, null); 138 tmpRelativeFileList2 = visitor2.relativizeFiles(dir2, true, null); 139 equals = tmpRelativeFileList1.equals(tmpRelativeFileList2); 140 } 141 } 142 } 143 } 144 // relativeDirList1 = tmpRelativeDirList1; 145 // relativeDirList2 = tmpRelativeDirList2; 146 relativeFileList1 = tmpRelativeFileList1; 147 relativeFileList2 = tmpRelativeFileList2; 148 } 149 } 150 151 private static final OpenOption[] OPEN_OPTIONS_TRUNCATE = { StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING }; 152 153 private static final OpenOption[] OPEN_OPTIONS_APPEND = { StandardOpenOption.CREATE, StandardOpenOption.APPEND }; 154 155 /** 156 * Empty {@link CopyOption} array. 157 * 158 * @since 2.8.0 159 */ 160 public static final CopyOption[] EMPTY_COPY_OPTIONS = {}; 161 162 /** 163 * Empty {@link DeleteOption} array. 164 * 165 * @since 2.8.0 166 */ 167 public static final DeleteOption[] EMPTY_DELETE_OPTION_ARRAY = {}; 168 169 /** 170 * Empty {@link FileAttribute} array. 171 * 172 * @since 2.13.0 173 */ 174 public static final FileAttribute<?>[] EMPTY_FILE_ATTRIBUTE_ARRAY = {}; 175 176 /** 177 * Empty {@link FileVisitOption} array. 178 */ 179 public static final FileVisitOption[] EMPTY_FILE_VISIT_OPTION_ARRAY = {}; 180 181 /** 182 * Empty {@link LinkOption} array. 183 */ 184 public static final LinkOption[] EMPTY_LINK_OPTION_ARRAY = {}; 185 186 /** 187 * {@link LinkOption} array for {@link LinkOption#NOFOLLOW_LINKS}. 188 * 189 * @since 2.9.0 190 * @deprecated Use {@link #noFollowLinkOptionArray()}. 191 */ 192 @Deprecated 193 public static final LinkOption[] NOFOLLOW_LINK_OPTION_ARRAY = { LinkOption.NOFOLLOW_LINKS }; 194 195 /** 196 * A LinkOption used to follow link in this class, the inverse of {@link LinkOption#NOFOLLOW_LINKS}. 197 * 198 * @since 2.12.0 199 */ 200 static final LinkOption NULL_LINK_OPTION = null; 201 202 /** 203 * Empty {@link OpenOption} array. 204 */ 205 public static final OpenOption[] EMPTY_OPEN_OPTION_ARRAY = {}; 206 207 /** 208 * Empty {@link Path} array. 209 * 210 * @since 2.9.0 211 */ 212 public static final Path[] EMPTY_PATH_ARRAY = {}; 213 214 /** 215 * Accumulates file tree information in a {@link AccumulatorPathVisitor}. 216 * 217 * @param directory The directory to accumulate information. 218 * @param maxDepth See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 219 * @param fileVisitOptions See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 220 * @throws IOException if an I/O error is thrown by a visitor method. 221 * @return file tree information. 222 */ 223 private static AccumulatorPathVisitor accumulate(final Path directory, final int maxDepth, final FileVisitOption[] fileVisitOptions) throws IOException { 224 return visitFileTree(AccumulatorPathVisitor.withLongCounters(), directory, toFileVisitOptionSet(fileVisitOptions), maxDepth); 225 } 226 227 /** 228 * Cleans a directory including subdirectories without deleting directories. 229 * 230 * @param directory directory to clean. 231 * @return The visitation path counters. 232 * @throws IOException if an I/O error is thrown by a visitor method. 233 */ 234 public static PathCounters cleanDirectory(final Path directory) throws IOException { 235 return cleanDirectory(directory, EMPTY_DELETE_OPTION_ARRAY); 236 } 237 238 /** 239 * Cleans a directory including subdirectories without deleting directories. 240 * 241 * @param directory directory to clean. 242 * @param deleteOptions How to handle deletion. 243 * @return The visitation path counters. 244 * @throws IOException if an I/O error is thrown by a visitor method. 245 * @since 2.8.0 246 */ 247 public static PathCounters cleanDirectory(final Path directory, final DeleteOption... deleteOptions) throws IOException { 248 return visitFileTree(new CleaningPathVisitor(Counters.longPathCounters(), deleteOptions), directory).getPathCounters(); 249 } 250 251 /** 252 * Compares the given {@link Path}'s last modified time to the given file time. 253 * 254 * @param file the {@link Path} to test. 255 * @param fileTime the time reference. 256 * @param options options indicating how to handle symbolic links. 257 * @return See {@link FileTime#compareTo(FileTime)} 258 * @throws IOException if an I/O error occurs. 259 * @throws NullPointerException if the file is {@code null}. 260 */ 261 private static int compareLastModifiedTimeTo(final Path file, final FileTime fileTime, final LinkOption... options) throws IOException { 262 return getLastModifiedTime(file, options).compareTo(fileTime); 263 } 264 265 /** 266 * Copies the InputStream from the supplier with {@link Files#copy(InputStream, Path, CopyOption...)}. 267 * 268 * @param in Supplies the InputStream. 269 * @param target See {@link Files#copy(InputStream, Path, CopyOption...)}. 270 * @param copyOptions See {@link Files#copy(InputStream, Path, CopyOption...)}. 271 * @return See {@link Files#copy(InputStream, Path, CopyOption...)} 272 * @throws IOException See {@link Files#copy(InputStream, Path, CopyOption...)} 273 * @since 2.12.0 274 */ 275 public static long copy(final IOSupplier<InputStream> in, final Path target, final CopyOption... copyOptions) throws IOException { 276 try (InputStream inputStream = in.get()) { 277 return Files.copy(inputStream, target, copyOptions); 278 } 279 } 280 281 /** 282 * Copies a directory to another directory. 283 * 284 * @param sourceDirectory The source directory. 285 * @param targetDirectory The target directory. 286 * @param copyOptions Specifies how the copying should be done. 287 * @return The visitation path counters. 288 * @throws IOException if an I/O error is thrown by a visitor method. 289 */ 290 public static PathCounters copyDirectory(final Path sourceDirectory, final Path targetDirectory, final CopyOption... copyOptions) throws IOException { 291 final Path absoluteSource = sourceDirectory.toAbsolutePath(); 292 return visitFileTree(new CopyDirectoryVisitor(Counters.longPathCounters(), absoluteSource, targetDirectory, copyOptions), absoluteSource) 293 .getPathCounters(); 294 } 295 296 /** 297 * Copies a URL to a directory. 298 * 299 * @param sourceFile The source URL. 300 * @param targetFile The target file. 301 * @param copyOptions Specifies how the copying should be done. 302 * @return The target file 303 * @throws IOException if an I/O error occurs. 304 * @see Files#copy(InputStream, Path, CopyOption...) 305 */ 306 public static Path copyFile(final URL sourceFile, final Path targetFile, final CopyOption... copyOptions) throws IOException { 307 copy(sourceFile::openStream, targetFile, copyOptions); 308 return targetFile; 309 } 310 311 /** 312 * Copies a file to a directory. 313 * 314 * @param sourceFile The source file. 315 * @param targetDirectory The target directory. 316 * @param copyOptions Specifies how the copying should be done. 317 * @return The target file 318 * @throws IOException if an I/O error occurs. 319 * @see Files#copy(Path, Path, CopyOption...) 320 */ 321 public static Path copyFileToDirectory(final Path sourceFile, final Path targetDirectory, final CopyOption... copyOptions) throws IOException { 322 return Files.copy(sourceFile, targetDirectory.resolve(sourceFile.getFileName()), copyOptions); 323 } 324 325 /** 326 * Copies a URL to a directory. 327 * 328 * @param sourceFile The source URL. 329 * @param targetDirectory The target directory. 330 * @param copyOptions Specifies how the copying should be done. 331 * @return The target file 332 * @throws IOException if an I/O error occurs. 333 * @see Files#copy(InputStream, Path, CopyOption...) 334 */ 335 public static Path copyFileToDirectory(final URL sourceFile, final Path targetDirectory, final CopyOption... copyOptions) throws IOException { 336 final Path resolve = targetDirectory.resolve(FilenameUtils.getName(sourceFile.getFile())); 337 copy(sourceFile::openStream, resolve, copyOptions); 338 return resolve; 339 } 340 341 /** 342 * Counts aspects of a directory including subdirectories. 343 * 344 * @param directory directory to delete. 345 * @return The visitor used to count the given directory. 346 * @throws IOException if an I/O error is thrown by a visitor method. 347 */ 348 public static PathCounters countDirectory(final Path directory) throws IOException { 349 return visitFileTree(CountingPathVisitor.withLongCounters(), directory).getPathCounters(); 350 } 351 352 /** 353 * Counts aspects of a directory including subdirectories. 354 * 355 * @param directory directory to count. 356 * @return The visitor used to count the given directory. 357 * @throws IOException if an I/O error occurs. 358 * @since 2.12.0 359 */ 360 public static PathCounters countDirectoryAsBigInteger(final Path directory) throws IOException { 361 return visitFileTree(CountingPathVisitor.withBigIntegerCounters(), directory).getPathCounters(); 362 } 363 364 /** 365 * Creates the parent directories for the given {@code path}. 366 * <p> 367 * If the parent directory already exists, then return it. 368 * </p> 369 * 370 * @param path The path to a file (or directory). 371 * @param attrs An optional list of file attributes to set atomically when creating the directories. 372 * @return The Path for the {@code path}'s parent directory or null if the given path has no parent. 373 * @throws IOException if an I/O error occurs. 374 * @since 2.9.0 375 */ 376 public static Path createParentDirectories(final Path path, final FileAttribute<?>... attrs) throws IOException { 377 return createParentDirectories(path, LinkOption.NOFOLLOW_LINKS, attrs); 378 } 379 380 /** 381 * Creates the parent directories for the given {@code path}. 382 * <p> 383 * If the parent directory already exists, then return it. 384 * </p> 385 * 386 * @param path The path to a file (or directory). 387 * @param linkOption A {@link LinkOption} or null. 388 * @param attrs An optional list of file attributes to set atomically when creating the directories. 389 * @return The Path for the {@code path}'s parent directory or null if the given path has no parent. 390 * @throws IOException if an I/O error occurs. 391 * @since 2.12.0 392 */ 393 public static Path createParentDirectories(final Path path, final LinkOption linkOption, final FileAttribute<?>... attrs) throws IOException { 394 Path parent = getParent(path); 395 parent = linkOption == LinkOption.NOFOLLOW_LINKS ? parent : readIfSymbolicLink(parent); 396 if (parent == null) { 397 return null; 398 } 399 final boolean exists = linkOption == null ? Files.exists(parent) : Files.exists(parent, linkOption); 400 return exists ? parent : Files.createDirectories(parent, attrs); 401 } 402 403 /** 404 * Gets the current directory. 405 * 406 * @return the current directory. 407 * 408 * @since 2.9.0 409 */ 410 public static Path current() { 411 return Paths.get("."); 412 } 413 414 /** 415 * Deletes a file or directory. If the path is a directory, delete it and all subdirectories. 416 * <p> 417 * The difference between File.delete() and this method are: 418 * </p> 419 * <ul> 420 * <li>A directory to delete does not have to be empty.</li> 421 * <li>You get exceptions when a file or directory cannot be deleted; {@link java.io.File#delete()} returns a boolean. 422 * </ul> 423 * 424 * @param path file or directory to delete, must not be {@code null} 425 * @return The visitor used to delete the given directory. 426 * @throws NullPointerException if the directory is {@code null} 427 * @throws IOException if an I/O error is thrown by a visitor method or if an I/O error occurs. 428 */ 429 public static PathCounters delete(final Path path) throws IOException { 430 return delete(path, EMPTY_DELETE_OPTION_ARRAY); 431 } 432 433 /** 434 * Deletes a file or directory. If the path is a directory, delete it and all subdirectories. 435 * <p> 436 * The difference between File.delete() and this method are: 437 * </p> 438 * <ul> 439 * <li>A directory to delete does not have to be empty.</li> 440 * <li>You get exceptions when a file or directory cannot be deleted; {@link java.io.File#delete()} returns a boolean. 441 * </ul> 442 * 443 * @param path file or directory to delete, must not be {@code null} 444 * @param deleteOptions How to handle deletion. 445 * @return The visitor used to delete the given directory. 446 * @throws NullPointerException if the directory is {@code null} 447 * @throws IOException if an I/O error is thrown by a visitor method or if an I/O error occurs. 448 * @since 2.8.0 449 */ 450 public static PathCounters delete(final Path path, final DeleteOption... deleteOptions) throws IOException { 451 // File deletion through Files deletes links, not targets, so use LinkOption.NOFOLLOW_LINKS. 452 return Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS) ? deleteDirectory(path, deleteOptions) : deleteFile(path, deleteOptions); 453 } 454 455 /** 456 * Deletes a file or directory. If the path is a directory, delete it and all subdirectories. 457 * <p> 458 * The difference between File.delete() and this method are: 459 * </p> 460 * <ul> 461 * <li>A directory to delete does not have to be empty.</li> 462 * <li>You get exceptions when a file or directory cannot be deleted; {@link java.io.File#delete()} returns a boolean. 463 * </ul> 464 * 465 * @param path file or directory to delete, must not be {@code null} 466 * @param linkOptions How to handle symbolic links. 467 * @param deleteOptions How to handle deletion. 468 * @return The visitor used to delete the given directory. 469 * @throws NullPointerException if the directory is {@code null} 470 * @throws IOException if an I/O error is thrown by a visitor method or if an I/O error occurs. 471 * @since 2.9.0 472 */ 473 public static PathCounters delete(final Path path, final LinkOption[] linkOptions, final DeleteOption... deleteOptions) throws IOException { 474 // File deletion through Files deletes links, not targets, so use LinkOption.NOFOLLOW_LINKS. 475 return Files.isDirectory(path, linkOptions) ? deleteDirectory(path, linkOptions, deleteOptions) : deleteFile(path, linkOptions, deleteOptions); 476 } 477 478 /** 479 * Deletes a directory including subdirectories. 480 * 481 * @param directory directory to delete. 482 * @return The visitor used to delete the given directory. 483 * @throws IOException if an I/O error is thrown by a visitor method. 484 */ 485 public static PathCounters deleteDirectory(final Path directory) throws IOException { 486 return deleteDirectory(directory, EMPTY_DELETE_OPTION_ARRAY); 487 } 488 489 /** 490 * Deletes a directory including subdirectories. 491 * 492 * @param directory directory to delete. 493 * @param deleteOptions How to handle deletion. 494 * @return The visitor used to delete the given directory. 495 * @throws IOException if an I/O error is thrown by a visitor method. 496 * @since 2.8.0 497 */ 498 public static PathCounters deleteDirectory(final Path directory, final DeleteOption... deleteOptions) throws IOException { 499 final LinkOption[] linkOptions = PathUtils.noFollowLinkOptionArray(); 500 // POSIX ops will noop on non-POSIX. 501 return withPosixFileAttributes(getParent(directory), linkOptions, overrideReadOnly(deleteOptions), 502 pfa -> visitFileTree(new DeletingPathVisitor(Counters.longPathCounters(), linkOptions, deleteOptions), directory).getPathCounters()); 503 } 504 505 /** 506 * Deletes a directory including subdirectories. 507 * 508 * @param directory directory to delete. 509 * @param linkOptions How to handle symbolic links. 510 * @param deleteOptions How to handle deletion. 511 * @return The visitor used to delete the given directory. 512 * @throws IOException if an I/O error is thrown by a visitor method. 513 * @since 2.9.0 514 */ 515 public static PathCounters deleteDirectory(final Path directory, final LinkOption[] linkOptions, final DeleteOption... deleteOptions) throws IOException { 516 return visitFileTree(new DeletingPathVisitor(Counters.longPathCounters(), linkOptions, deleteOptions), directory).getPathCounters(); 517 } 518 519 /** 520 * Deletes the given file. 521 * 522 * @param file The file to delete. 523 * @return A visitor with path counts set to 1 file, 0 directories, and the size of the deleted file. 524 * @throws IOException if an I/O error occurs. 525 * @throws NoSuchFileException if the file is a directory. 526 */ 527 public static PathCounters deleteFile(final Path file) throws IOException { 528 return deleteFile(file, EMPTY_DELETE_OPTION_ARRAY); 529 } 530 531 /** 532 * Deletes the given file. 533 * 534 * @param file The file to delete. 535 * @param deleteOptions How to handle deletion. 536 * @return A visitor with path counts set to 1 file, 0 directories, and the size of the deleted file. 537 * @throws IOException if an I/O error occurs. 538 * @throws NoSuchFileException if the file is a directory. 539 * @since 2.8.0 540 */ 541 public static PathCounters deleteFile(final Path file, final DeleteOption... deleteOptions) throws IOException { 542 // Files.deleteIfExists() never follows links, so use LinkOption.NOFOLLOW_LINKS in other calls to Files. 543 return deleteFile(file, noFollowLinkOptionArray(), deleteOptions); 544 } 545 546 /** 547 * Deletes the given file. 548 * 549 * @param file The file to delete. 550 * @param linkOptions How to handle symbolic links. 551 * @param deleteOptions How to handle deletion. 552 * @return A visitor with path counts set to 1 file, 0 directories, and the size of the deleted file. 553 * @throws IOException if an I/O error occurs. 554 * @throws NoSuchFileException if the file is a directory. 555 * @since 2.9.0 556 */ 557 public static PathCounters deleteFile(final Path file, final LinkOption[] linkOptions, final DeleteOption... deleteOptions) 558 throws NoSuchFileException, IOException { 559 // 560 // TODO Needs clean up 561 // 562 if (Files.isDirectory(file, linkOptions)) { 563 throw new NoSuchFileException(file.toString()); 564 } 565 final PathCounters pathCounts = Counters.longPathCounters(); 566 boolean exists = exists(file, linkOptions); 567 long size = exists && !Files.isSymbolicLink(file) ? Files.size(file) : 0; 568 try { 569 if (Files.deleteIfExists(file)) { 570 pathCounts.getFileCounter().increment(); 571 pathCounts.getByteCounter().add(size); 572 return pathCounts; 573 } 574 } catch (final AccessDeniedException ignored) { 575 // Ignore and try again below. 576 } 577 final Path parent = getParent(file); 578 PosixFileAttributes posixFileAttributes = null; 579 try { 580 if (overrideReadOnly(deleteOptions)) { 581 posixFileAttributes = readPosixFileAttributes(parent, linkOptions); 582 setReadOnly(file, false, linkOptions); 583 } 584 // Read size _after_ having read/execute access on POSIX. 585 exists = exists(file, linkOptions); 586 size = exists && !Files.isSymbolicLink(file) ? Files.size(file) : 0; 587 if (Files.deleteIfExists(file)) { 588 pathCounts.getFileCounter().increment(); 589 pathCounts.getByteCounter().add(size); 590 } 591 } finally { 592 if (posixFileAttributes != null) { 593 Files.setPosixFilePermissions(parent, posixFileAttributes.permissions()); 594 } 595 } 596 return pathCounts; 597 } 598 599 /** 600 * Delegates to {@link File#deleteOnExit()}. 601 * 602 * @param path the path to delete. 603 * @since 3.13.0 604 */ 605 public static void deleteOnExit(final Path path) { 606 Objects.requireNonNull(path.toFile()).deleteOnExit(); 607 } 608 609 /** 610 * Compares the file sets of two Paths to determine if they are equal or not while considering file contents. The comparison includes all files in all 611 * subdirectories. 612 * 613 * @param path1 The first directory. 614 * @param path2 The second directory. 615 * @return Whether the two directories contain the same files while considering file contents. 616 * @throws IOException if an I/O error is thrown by a visitor method. 617 */ 618 public static boolean directoryAndFileContentEquals(final Path path1, final Path path2) throws IOException { 619 return directoryAndFileContentEquals(path1, path2, EMPTY_LINK_OPTION_ARRAY, EMPTY_OPEN_OPTION_ARRAY, EMPTY_FILE_VISIT_OPTION_ARRAY); 620 } 621 622 /** 623 * Compares the file sets of two Paths to determine if they are equal or not while considering file contents. The comparison includes all files in all 624 * subdirectories. 625 * 626 * @param path1 The first directory. 627 * @param path2 The second directory. 628 * @param linkOptions options to follow links. 629 * @param openOptions options to open files. 630 * @param fileVisitOption options to configure traversal. 631 * @return Whether the two directories contain the same files while considering file contents. 632 * @throws IOException if an I/O error is thrown by a visitor method. 633 */ 634 public static boolean directoryAndFileContentEquals(final Path path1, final Path path2, final LinkOption[] linkOptions, final OpenOption[] openOptions, 635 final FileVisitOption[] fileVisitOption) throws IOException { 636 // First walk both file trees and gather normalized paths. 637 if (path1 == null && path2 == null) { 638 return true; 639 } 640 if (path1 == null || path2 == null) { 641 return false; 642 } 643 if (notExists(path1) && notExists(path2)) { 644 return true; 645 } 646 final RelativeSortedPaths relativeSortedPaths = new RelativeSortedPaths(path1, path2, Integer.MAX_VALUE, linkOptions, fileVisitOption); 647 // If the normalized path names and counts are not the same, no need to compare contents. 648 if (!relativeSortedPaths.equals) { 649 return false; 650 } 651 // Both visitors contain the same normalized paths, we can compare file contents. 652 final List<Path> fileList1 = relativeSortedPaths.relativeFileList1; 653 final List<Path> fileList2 = relativeSortedPaths.relativeFileList2; 654 for (final Path path : fileList1) { 655 final int binarySearch = Collections.binarySearch(fileList2, path); 656 if (binarySearch <= -1) { 657 throw new IllegalStateException("Unexpected mismatch."); 658 } 659 if (!fileContentEquals(path1.resolve(path), path2.resolve(path), linkOptions, openOptions)) { 660 return false; 661 } 662 } 663 return true; 664 } 665 666 /** 667 * Compares the file sets of two Paths to determine if they are equal or not without considering file contents. The comparison includes all files in all 668 * subdirectories. 669 * 670 * @param path1 The first directory. 671 * @param path2 The second directory. 672 * @return Whether the two directories contain the same files without considering file contents. 673 * @throws IOException if an I/O error is thrown by a visitor method. 674 */ 675 public static boolean directoryContentEquals(final Path path1, final Path path2) throws IOException { 676 return directoryContentEquals(path1, path2, Integer.MAX_VALUE, EMPTY_LINK_OPTION_ARRAY, EMPTY_FILE_VISIT_OPTION_ARRAY); 677 } 678 679 /** 680 * Compares the file sets of two Paths to determine if they are equal or not without considering file contents. The comparison includes all files in all 681 * subdirectories. 682 * 683 * @param path1 The first directory. 684 * @param path2 The second directory. 685 * @param maxDepth See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 686 * @param linkOptions options to follow links. 687 * @param fileVisitOptions options to configure the traversal 688 * @return Whether the two directories contain the same files without considering file contents. 689 * @throws IOException if an I/O error is thrown by a visitor method. 690 */ 691 public static boolean directoryContentEquals(final Path path1, final Path path2, final int maxDepth, final LinkOption[] linkOptions, 692 final FileVisitOption[] fileVisitOptions) throws IOException { 693 return new RelativeSortedPaths(path1, path2, maxDepth, linkOptions, fileVisitOptions).equals; 694 } 695 696 private static boolean exists(final Path path, final LinkOption... options) { 697 Objects.requireNonNull(path, "path"); 698 return options != null ? Files.exists(path, options) : Files.exists(path); 699 } 700 701 /** 702 * Compares the file contents of two Paths to determine if they are equal or not. 703 * <p> 704 * File content is accessed through {@link Files#newInputStream(Path,OpenOption...)}. 705 * </p> 706 * 707 * @param path1 the first stream. 708 * @param path2 the second stream. 709 * @return true if the content of the streams are equal or they both don't exist, false otherwise. 710 * @throws NullPointerException if either input is null. 711 * @throws IOException if an I/O error occurs. 712 * @see org.apache.commons.io.FileUtils#contentEquals(java.io.File, java.io.File) 713 */ 714 public static boolean fileContentEquals(final Path path1, final Path path2) throws IOException { 715 return fileContentEquals(path1, path2, EMPTY_LINK_OPTION_ARRAY, EMPTY_OPEN_OPTION_ARRAY); 716 } 717 718 /** 719 * Compares the file contents of two Paths to determine if they are equal or not. 720 * <p> 721 * File content is accessed through {@link RandomAccessFileMode#create(Path)}. 722 * </p> 723 * 724 * @param path1 the first stream. 725 * @param path2 the second stream. 726 * @param linkOptions options specifying how files are followed. 727 * @param openOptions ignored. 728 * @return true if the content of the streams are equal or they both don't exist, false otherwise. 729 * @throws NullPointerException if openOptions is null. 730 * @throws IOException if an I/O error occurs. 731 * @see org.apache.commons.io.FileUtils#contentEquals(java.io.File, java.io.File) 732 */ 733 public static boolean fileContentEquals(final Path path1, final Path path2, final LinkOption[] linkOptions, final OpenOption[] openOptions) 734 throws IOException { 735 if (path1 == null && path2 == null) { 736 return true; 737 } 738 if (path1 == null || path2 == null) { 739 return false; 740 } 741 final Path nPath1 = path1.normalize(); 742 final Path nPath2 = path2.normalize(); 743 final boolean path1Exists = exists(nPath1, linkOptions); 744 if (path1Exists != exists(nPath2, linkOptions)) { 745 return false; 746 } 747 if (!path1Exists) { 748 // Two not existing files are equal? 749 // Same as FileUtils 750 return true; 751 } 752 if (Files.isDirectory(nPath1, linkOptions)) { 753 // don't compare directory contents. 754 throw new IOException("Can't compare directories, only files: " + nPath1); 755 } 756 if (Files.isDirectory(nPath2, linkOptions)) { 757 // don't compare directory contents. 758 throw new IOException("Can't compare directories, only files: " + nPath2); 759 } 760 if (Files.size(nPath1) != Files.size(nPath2)) { 761 // lengths differ, cannot be equal 762 return false; 763 } 764 if (path1.equals(path2)) { 765 // same file 766 return true; 767 } 768 // Faster: 769 try (RandomAccessFile raf1 = RandomAccessFileMode.READ_ONLY.create(path1.toRealPath(linkOptions)); 770 RandomAccessFile raf2 = RandomAccessFileMode.READ_ONLY.create(path2.toRealPath(linkOptions))) { 771 return RandomAccessFiles.contentEquals(raf1, raf2); 772 } catch (UnsupportedOperationException e) { 773 // Slower: 774 // Handle 775 // java.lang.UnsupportedOperationException 776 // at com.sun.nio.zipfs.ZipPath.toFile(ZipPath.java:656) 777 try (InputStream inputStream1 = Files.newInputStream(nPath1, openOptions); 778 InputStream inputStream2 = Files.newInputStream(nPath2, openOptions)) { 779 return IOUtils.contentEquals(inputStream1, inputStream2); 780 } 781 } 782 } 783 784 /** 785 * <p> 786 * Applies an {@link IOFileFilter} to the provided {@link File} objects. The resulting array is a subset of the original file list that matches the provided 787 * filter. 788 * </p> 789 * 790 * <p> 791 * The {@link Set} returned by this method is not guaranteed to be thread safe. 792 * </p> 793 * 794 * <pre> 795 * Set<File> allFiles = ... 796 * Set<File> javaFiles = FileFilterUtils.filterSet(allFiles, 797 * FileFilterUtils.suffixFileFilter(".java")); 798 * </pre> 799 * 800 * @param filter the filter to apply to the set of files. 801 * @param paths the array of files to apply the filter to. 802 * 803 * @return a subset of {@code files} that is accepted by the file filter. 804 * @throws NullPointerException if the filter is {@code null} 805 * @throws IllegalArgumentException if {@code files} contains a {@code null} value. 806 * 807 * @since 2.9.0 808 */ 809 public static Path[] filter(final PathFilter filter, final Path... paths) { 810 Objects.requireNonNull(filter, "filter"); 811 if (paths == null) { 812 return EMPTY_PATH_ARRAY; 813 } 814 return filterPaths(filter, Stream.of(paths), Collectors.toList()).toArray(EMPTY_PATH_ARRAY); 815 } 816 817 private static <R, A> R filterPaths(final PathFilter filter, final Stream<Path> stream, final Collector<? super Path, A, R> collector) { 818 Objects.requireNonNull(filter, "filter"); 819 Objects.requireNonNull(collector, "collector"); 820 if (stream == null) { 821 return Stream.<Path>empty().collect(collector); 822 } 823 return stream.filter(p -> { 824 try { 825 return p != null && filter.accept(p, readBasicFileAttributes(p)) == FileVisitResult.CONTINUE; 826 } catch (final IOException e) { 827 return false; 828 } 829 }).collect(collector); 830 } 831 832 /** 833 * Reads the access control list from a file attribute view. 834 * 835 * @param sourcePath the path to the file. 836 * @return a file attribute view of the given type, or null if the attribute view type is not available. 837 * @throws IOException if an I/O error occurs. 838 * @since 2.8.0 839 */ 840 public static List<AclEntry> getAclEntryList(final Path sourcePath) throws IOException { 841 final AclFileAttributeView fileAttributeView = getAclFileAttributeView(sourcePath); 842 return fileAttributeView == null ? null : fileAttributeView.getAcl(); 843 } 844 845 /** 846 * Shorthand for {@code Files.getFileAttributeView(path, AclFileAttributeView.class)}. 847 * 848 * @param path the path to the file. 849 * @param options how to handle symbolic links. 850 * @return a AclFileAttributeView, or {@code null} if the attribute view type is not available. 851 * @since 2.12.0 852 */ 853 public static AclFileAttributeView getAclFileAttributeView(final Path path, final LinkOption... options) { 854 return Files.getFileAttributeView(path, AclFileAttributeView.class, options); 855 } 856 857 /** 858 * Shorthand for {@code Files.getFileAttributeView(path, DosFileAttributeView.class)}. 859 * 860 * @param path the path to the file. 861 * @param options how to handle symbolic links. 862 * @return a DosFileAttributeView, or {@code null} if the attribute view type is not available. 863 * @since 2.12.0 864 */ 865 public static DosFileAttributeView getDosFileAttributeView(final Path path, final LinkOption... options) { 866 return Files.getFileAttributeView(path, DosFileAttributeView.class, options); 867 } 868 869 /** 870 * Gets the file's last modified time or null if the file does not exist. 871 * <p> 872 * The method provides a workaround for bug <a href="https://bugs.openjdk.java.net/browse/JDK-8177809">JDK-8177809</a> where {@link File#lastModified()} 873 * looses milliseconds and always ends in 000. This bug is in OpenJDK 8 and 9, and fixed in 11. 874 * </p> 875 * 876 * @param file the file to query. 877 * @return the file's last modified time. 878 * @throws IOException Thrown if an I/O error occurs. 879 * @since 2.12.0 880 */ 881 public static FileTime getLastModifiedFileTime(final File file) throws IOException { 882 return getLastModifiedFileTime(file.toPath(), null, EMPTY_LINK_OPTION_ARRAY); 883 } 884 885 /** 886 * Gets the file's last modified time or null if the file does not exist. 887 * 888 * @param path the file to query. 889 * @param defaultIfAbsent Returns this file time of the file does not exist, may be null. 890 * @param options options indicating how symbolic links are handled. 891 * @return the file's last modified time. 892 * @throws IOException Thrown if an I/O error occurs. 893 * @since 2.12.0 894 */ 895 public static FileTime getLastModifiedFileTime(final Path path, final FileTime defaultIfAbsent, final LinkOption... options) throws IOException { 896 return Files.exists(path) ? getLastModifiedTime(path, options) : defaultIfAbsent; 897 } 898 899 /** 900 * Gets the file's last modified time or null if the file does not exist. 901 * 902 * @param path the file to query. 903 * @param options options indicating how symbolic links are handled. 904 * @return the file's last modified time. 905 * @throws IOException Thrown if an I/O error occurs. 906 * @since 2.12.0 907 */ 908 public static FileTime getLastModifiedFileTime(final Path path, final LinkOption... options) throws IOException { 909 return getLastModifiedFileTime(path, null, options); 910 } 911 912 /** 913 * Gets the file's last modified time or null if the file does not exist. 914 * 915 * @param uri the file to query. 916 * @return the file's last modified time. 917 * @throws IOException Thrown if an I/O error occurs. 918 * @since 2.12.0 919 */ 920 public static FileTime getLastModifiedFileTime(final URI uri) throws IOException { 921 return getLastModifiedFileTime(Paths.get(uri), null, EMPTY_LINK_OPTION_ARRAY); 922 } 923 924 /** 925 * Gets the file's last modified time or null if the file does not exist. 926 * 927 * @param url the file to query. 928 * @return the file's last modified time. 929 * @throws IOException Thrown if an I/O error occurs. 930 * @throws URISyntaxException if the URL is not formatted strictly according to RFC2396 and cannot be converted to a URI. 931 * @since 2.12.0 932 */ 933 public static FileTime getLastModifiedFileTime(final URL url) throws IOException, URISyntaxException { 934 return getLastModifiedFileTime(url.toURI()); 935 } 936 937 private static FileTime getLastModifiedTime(final Path path, final LinkOption... options) throws IOException { 938 return Files.getLastModifiedTime(Objects.requireNonNull(path, "path"), options); 939 } 940 941 private static Path getParent(final Path path) { 942 return path == null ? null : path.getParent(); 943 } 944 945 /** 946 * Shorthand for {@code Files.getFileAttributeView(path, PosixFileAttributeView.class)}. 947 * 948 * @param path the path to the file. 949 * @param options how to handle symbolic links. 950 * @return a PosixFileAttributeView, or {@code null} if the attribute view type is not available. 951 * @since 2.12.0 952 */ 953 public static PosixFileAttributeView getPosixFileAttributeView(final Path path, final LinkOption... options) { 954 return Files.getFileAttributeView(path, PosixFileAttributeView.class, options); 955 } 956 957 /** 958 * Gets a {@link Path} representing the system temporary directory. 959 * 960 * @return the system temporary directory. 961 * @since 2.12.0 962 */ 963 public static Path getTempDirectory() { 964 return Paths.get(FileUtils.getTempDirectoryPath()); 965 } 966 967 /** 968 * Tests whether the given {@link Path} is a directory or not. Implemented as a null-safe delegate to 969 * {@code Files.isDirectory(Path path, LinkOption... options)}. 970 * 971 * @param path the path to the file. 972 * @param options options indicating how to handle symbolic links 973 * @return {@code true} if the file is a directory; {@code false} if the path is null, the file does not exist, is not a directory, or it cannot be 974 * determined if the file is a directory or not. 975 * @throws SecurityException In the case of the default provider, and a security manager is installed, the {@link SecurityManager#checkRead(String) 976 * checkRead} method is invoked to check read access to the directory. 977 * @since 2.9.0 978 */ 979 public static boolean isDirectory(final Path path, final LinkOption... options) { 980 return path != null && Files.isDirectory(path, options); 981 } 982 983 /** 984 * Tests whether the given file or directory is empty. 985 * 986 * @param path the file or directory to query. 987 * @return whether the file or directory is empty. 988 * @throws IOException if an I/O error occurs. 989 */ 990 public static boolean isEmpty(final Path path) throws IOException { 991 return Files.isDirectory(path) ? isEmptyDirectory(path) : isEmptyFile(path); 992 } 993 994 /** 995 * Tests whether the directory is empty. 996 * 997 * @param directory the directory to query. 998 * @return whether the directory is empty. 999 * @throws NotDirectoryException if the file could not otherwise be opened because it is not a directory <i>(optional specific exception)</i>. 1000 * @throws IOException if an I/O error occurs. 1001 * @throws SecurityException In the case of the default provider, and a security manager is installed, the {@link SecurityManager#checkRead(String) 1002 * checkRead} method is invoked to check read access to the directory. 1003 */ 1004 public static boolean isEmptyDirectory(final Path directory) throws IOException { 1005 try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory)) { 1006 return !directoryStream.iterator().hasNext(); 1007 } 1008 } 1009 1010 /** 1011 * Tests whether the given file is empty. 1012 * 1013 * @param file the file to query. 1014 * @return whether the file is empty. 1015 * @throws IOException if an I/O error occurs. 1016 * @throws SecurityException In the case of the default provider, and a security manager is installed, its {@link SecurityManager#checkRead(String) 1017 * checkRead} method denies read access to the file. 1018 */ 1019 public static boolean isEmptyFile(final Path file) throws IOException { 1020 return Files.size(file) <= 0; 1021 } 1022 1023 /** 1024 * Tests if the given {@link Path} is newer than the given time reference. 1025 * 1026 * @param file the {@link Path} to test. 1027 * @param czdt the time reference. 1028 * @param options options indicating how to handle symbolic links. 1029 * @return true if the {@link Path} exists and has been modified after the given time reference. 1030 * @throws IOException if an I/O error occurs. 1031 * @throws NullPointerException if the file is {@code null}. 1032 * @since 2.12.0 1033 */ 1034 public static boolean isNewer(final Path file, final ChronoZonedDateTime<?> czdt, final LinkOption... options) throws IOException { 1035 Objects.requireNonNull(czdt, "czdt"); 1036 return isNewer(file, czdt.toInstant(), options); 1037 } 1038 1039 /** 1040 * Tests if the given {@link Path} is newer than the given time reference. 1041 * 1042 * @param file the {@link Path} to test. 1043 * @param fileTime the time reference. 1044 * @param options options indicating how to handle symbolic links. 1045 * @return true if the {@link Path} exists and has been modified after the given time reference. 1046 * @throws IOException if an I/O error occurs. 1047 * @throws NullPointerException if the file is {@code null}. 1048 * @since 2.12.0 1049 */ 1050 public static boolean isNewer(final Path file, final FileTime fileTime, final LinkOption... options) throws IOException { 1051 if (notExists(file)) { 1052 return false; 1053 } 1054 return compareLastModifiedTimeTo(file, fileTime, options) > 0; 1055 } 1056 1057 /** 1058 * Tests if the given {@link Path} is newer than the given time reference. 1059 * 1060 * @param file the {@link Path} to test. 1061 * @param instant the time reference. 1062 * @param options options indicating how to handle symbolic links. 1063 * @return true if the {@link Path} exists and has been modified after the given time reference. 1064 * @throws IOException if an I/O error occurs. 1065 * @throws NullPointerException if the file is {@code null}. 1066 * @since 2.12.0 1067 */ 1068 public static boolean isNewer(final Path file, final Instant instant, final LinkOption... options) throws IOException { 1069 return isNewer(file, FileTime.from(instant), options); 1070 } 1071 1072 /** 1073 * Tests if the given {@link Path} is newer than the given time reference. 1074 * 1075 * @param file the {@link Path} to test. 1076 * @param timeMillis the time reference measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970) 1077 * @param options options indicating how to handle symbolic links. 1078 * @return true if the {@link Path} exists and has been modified after the given time reference. 1079 * @throws IOException if an I/O error occurs. 1080 * @throws NullPointerException if the file is {@code null}. 1081 * @since 2.9.0 1082 */ 1083 public static boolean isNewer(final Path file, final long timeMillis, final LinkOption... options) throws IOException { 1084 return isNewer(file, FileTime.fromMillis(timeMillis), options); 1085 } 1086 1087 /** 1088 * Tests if the given {@link Path} is newer than the reference {@link Path}. 1089 * 1090 * @param file the {@link File} to test. 1091 * @param reference the {@link File} of which the modification date is used. 1092 * @return true if the {@link File} exists and has been modified more recently than the reference {@link File}. 1093 * @throws IOException if an I/O error occurs. 1094 * @since 2.12.0 1095 */ 1096 public static boolean isNewer(final Path file, final Path reference) throws IOException { 1097 return isNewer(file, getLastModifiedTime(reference)); 1098 } 1099 1100 /** 1101 * Tests if the given {@link Path} is older than the given time reference. 1102 * 1103 * @param file the {@link Path} to test. 1104 * @param fileTime the time reference. 1105 * @param options options indicating how to handle symbolic links. 1106 * @return true if the {@link Path} exists and has been modified before the given time reference. 1107 * @throws IOException if an I/O error occurs. 1108 * @throws NullPointerException if the file is {@code null}. 1109 * @since 2.12.0 1110 */ 1111 public static boolean isOlder(final Path file, final FileTime fileTime, final LinkOption... options) throws IOException { 1112 if (notExists(file)) { 1113 return false; 1114 } 1115 return compareLastModifiedTimeTo(file, fileTime, options) < 0; 1116 } 1117 1118 /** 1119 * Tests if the given {@link Path} is older than the given time reference. 1120 * 1121 * @param file the {@link Path} to test. 1122 * @param instant the time reference. 1123 * @param options options indicating how to handle symbolic links. 1124 * @return true if the {@link Path} exists and has been modified before the given time reference. 1125 * @throws IOException if an I/O error occurs. 1126 * @throws NullPointerException if the file is {@code null}. 1127 * @since 2.12.0 1128 */ 1129 public static boolean isOlder(final Path file, final Instant instant, final LinkOption... options) throws IOException { 1130 return isOlder(file, FileTime.from(instant), options); 1131 } 1132 1133 /** 1134 * Tests if the given {@link Path} is older than the given time reference. 1135 * 1136 * @param file the {@link Path} to test. 1137 * @param timeMillis the time reference measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970) 1138 * @param options options indicating how to handle symbolic links. 1139 * @return true if the {@link Path} exists and has been modified before the given time reference. 1140 * @throws IOException if an I/O error occurs. 1141 * @throws NullPointerException if the file is {@code null}. 1142 * @since 2.12.0 1143 */ 1144 public static boolean isOlder(final Path file, final long timeMillis, final LinkOption... options) throws IOException { 1145 return isOlder(file, FileTime.fromMillis(timeMillis), options); 1146 } 1147 1148 /** 1149 * Tests if the given {@link Path} is older than the reference {@link Path}. 1150 * 1151 * @param file the {@link File} to test. 1152 * @param reference the {@link File} of which the modification date is used. 1153 * @return true if the {@link File} exists and has been modified before than the reference {@link File}. 1154 * @throws IOException if an I/O error occurs. 1155 * @since 2.12.0 1156 */ 1157 public static boolean isOlder(final Path file, final Path reference) throws IOException { 1158 return isOlder(file, getLastModifiedTime(reference)); 1159 } 1160 1161 /** 1162 * Tests whether the given path is on a POSIX file system. 1163 * 1164 * @param test The Path to test. 1165 * @param options options indicating how to handle symbolic links. 1166 * @return true if test is on a POSIX file system. 1167 * @since 2.12.0 1168 */ 1169 public static boolean isPosix(final Path test, final LinkOption... options) { 1170 return exists(test, options) && readPosixFileAttributes(test, options) != null; 1171 } 1172 1173 /** 1174 * Tests whether the given {@link Path} is a regular file or not. Implemented as a null-safe delegate to 1175 * {@code Files.isRegularFile(Path path, LinkOption... options)}. 1176 * 1177 * @param path the path to the file. 1178 * @param options options indicating how to handle symbolic links. 1179 * @return {@code true} if the file is a regular file; {@code false} if the path is null, the file does not exist, is not a directory, or it cannot be 1180 * determined if the file is a regular file or not. 1181 * @throws SecurityException In the case of the default provider, and a security manager is installed, the {@link SecurityManager#checkRead(String) 1182 * checkRead} method is invoked to check read access to the directory. 1183 * @since 2.9.0 1184 */ 1185 public static boolean isRegularFile(final Path path, final LinkOption... options) { 1186 return path != null && Files.isRegularFile(path, options); 1187 } 1188 1189 /** 1190 * Creates a new DirectoryStream for Paths rooted at the given directory. 1191 * <p> 1192 * If you don't use the try-with-resources construct, then you must call the stream's {@link Stream#close()} method after iteration is complete to free any 1193 * resources held for the open directory. 1194 * </p> 1195 * 1196 * @param dir the path to the directory to stream. 1197 * @param pathFilter the directory stream filter. 1198 * @return a new instance. 1199 * @throws IOException if an I/O error occurs. 1200 */ 1201 public static DirectoryStream<Path> newDirectoryStream(final Path dir, final PathFilter pathFilter) throws IOException { 1202 return Files.newDirectoryStream(dir, new DirectoryStreamFilter(pathFilter)); 1203 } 1204 1205 /** 1206 * Creates a new OutputStream by opening or creating a file, returning an output stream that may be used to write bytes to the file. 1207 * 1208 * @param path the Path. 1209 * @param append Whether or not to append. 1210 * @return a new OutputStream. 1211 * @throws IOException if an I/O error occurs. 1212 * @see Files#newOutputStream(Path, OpenOption...) 1213 * @since 2.12.0 1214 */ 1215 public static OutputStream newOutputStream(final Path path, final boolean append) throws IOException { 1216 return newOutputStream(path, EMPTY_LINK_OPTION_ARRAY, append ? OPEN_OPTIONS_APPEND : OPEN_OPTIONS_TRUNCATE); 1217 } 1218 1219 static OutputStream newOutputStream(final Path path, final LinkOption[] linkOptions, final OpenOption... openOptions) throws IOException { 1220 if (!exists(path, linkOptions)) { 1221 createParentDirectories(path, linkOptions != null && linkOptions.length > 0 ? linkOptions[0] : NULL_LINK_OPTION); 1222 } 1223 final List<OpenOption> list = new ArrayList<>(Arrays.asList(openOptions != null ? openOptions : EMPTY_OPEN_OPTION_ARRAY)); 1224 list.addAll(Arrays.asList(linkOptions != null ? linkOptions : EMPTY_LINK_OPTION_ARRAY)); 1225 return Files.newOutputStream(path, list.toArray(EMPTY_OPEN_OPTION_ARRAY)); 1226 } 1227 1228 /** 1229 * Copy of the {@link LinkOption} array for {@link LinkOption#NOFOLLOW_LINKS}. 1230 * 1231 * @return Copy of the {@link LinkOption} array for {@link LinkOption#NOFOLLOW_LINKS}. 1232 */ 1233 public static LinkOption[] noFollowLinkOptionArray() { 1234 return NOFOLLOW_LINK_OPTION_ARRAY.clone(); 1235 } 1236 1237 private static boolean notExists(final Path path, final LinkOption... options) { 1238 return Files.notExists(Objects.requireNonNull(path, "path"), options); 1239 } 1240 1241 /** 1242 * Returns true if the given options contain {@link StandardDeleteOption#OVERRIDE_READ_ONLY}. 1243 * 1244 * @param deleteOptions the array to test 1245 * @return true if the given options contain {@link StandardDeleteOption#OVERRIDE_READ_ONLY}. 1246 */ 1247 private static boolean overrideReadOnly(final DeleteOption... deleteOptions) { 1248 if (deleteOptions == null) { 1249 return false; 1250 } 1251 return Stream.of(deleteOptions).anyMatch(e -> e == StandardDeleteOption.OVERRIDE_READ_ONLY); 1252 } 1253 1254 /** 1255 * Reads the BasicFileAttributes from the given path. Returns null if the attributes can't be read. 1256 * 1257 * @param <A> The {@link BasicFileAttributes} type 1258 * @param path The Path to test. 1259 * @param type the {@link Class} of the file attributes required to read. 1260 * @param options options indicating how to handle symbolic links. 1261 * @return the file attributes or null if the attributes can't be read. 1262 * @see Files#readAttributes(Path, Class, LinkOption...) 1263 * @since 2.12.0 1264 */ 1265 public static <A extends BasicFileAttributes> A readAttributes(final Path path, final Class<A> type, final LinkOption... options) { 1266 try { 1267 return path == null ? null : Files.readAttributes(path, type, options); 1268 } catch (final UnsupportedOperationException | IOException e) { 1269 // For example, on Windows. 1270 return null; 1271 } 1272 } 1273 1274 /** 1275 * Reads the BasicFileAttributes from the given path. 1276 * 1277 * @param path the path to read. 1278 * @return the path attributes. 1279 * @throws IOException if an I/O error occurs. 1280 * @since 2.9.0 1281 */ 1282 public static BasicFileAttributes readBasicFileAttributes(final Path path) throws IOException { 1283 return Files.readAttributes(path, BasicFileAttributes.class); 1284 } 1285 1286 /** 1287 * Reads the BasicFileAttributes from the given path. Returns null if the attributes can't be read. 1288 * 1289 * @param path the path to read. 1290 * @param options options indicating how to handle symbolic links. 1291 * @return the path attributes. 1292 * @since 2.12.0 1293 */ 1294 public static BasicFileAttributes readBasicFileAttributes(final Path path, final LinkOption... options) { 1295 return readAttributes(path, BasicFileAttributes.class, options); 1296 } 1297 1298 /** 1299 * Reads the BasicFileAttributes from the given path. Returns null if the attributes can't be read. 1300 * 1301 * @param path the path to read. 1302 * @return the path attributes. 1303 * @since 2.9.0 1304 * @deprecated Use {@link #readBasicFileAttributes(Path, LinkOption...)}. 1305 */ 1306 @Deprecated 1307 public static BasicFileAttributes readBasicFileAttributesUnchecked(final Path path) { 1308 return readBasicFileAttributes(path, EMPTY_LINK_OPTION_ARRAY); 1309 } 1310 1311 /** 1312 * Reads the DosFileAttributes from the given path. Returns null if the attributes can't be read. 1313 * 1314 * @param path the path to read. 1315 * @param options options indicating how to handle symbolic links. 1316 * @return the path attributes. 1317 * @since 2.12.0 1318 */ 1319 public static DosFileAttributes readDosFileAttributes(final Path path, final LinkOption... options) { 1320 return readAttributes(path, DosFileAttributes.class, options); 1321 } 1322 1323 private static Path readIfSymbolicLink(final Path path) throws IOException { 1324 return path != null ? Files.isSymbolicLink(path) ? Files.readSymbolicLink(path) : path : null; 1325 } 1326 1327 /** 1328 * Reads the PosixFileAttributes or DosFileAttributes from the given path. Returns null if the attributes can't be read. 1329 * 1330 * @param path The Path to read. 1331 * @param options options indicating how to handle symbolic links. 1332 * @return the file attributes. 1333 * @since 2.12.0 1334 */ 1335 public static BasicFileAttributes readOsFileAttributes(final Path path, final LinkOption... options) { 1336 final PosixFileAttributes fileAttributes = readPosixFileAttributes(path, options); 1337 return fileAttributes != null ? fileAttributes : readDosFileAttributes(path, options); 1338 } 1339 1340 /** 1341 * Reads the PosixFileAttributes from the given path. Returns null instead of throwing {@link UnsupportedOperationException}. 1342 * 1343 * @param path The Path to read. 1344 * @param options options indicating how to handle symbolic links. 1345 * @return the file attributes. 1346 * @since 2.12.0 1347 */ 1348 public static PosixFileAttributes readPosixFileAttributes(final Path path, final LinkOption... options) { 1349 return readAttributes(path, PosixFileAttributes.class, options); 1350 } 1351 1352 /** 1353 * Reads the given path as a String. 1354 * 1355 * @param path The source path. 1356 * @param charset How to convert bytes to a String, null uses the default Charset. 1357 * @return a new String. 1358 * @throws IOException if an I/O error occurs reading from the stream. 1359 * @see Files#readAllBytes(Path) 1360 * @since 2.12.0 1361 */ 1362 public static String readString(final Path path, final Charset charset) throws IOException { 1363 return new String(Files.readAllBytes(path), Charsets.toCharset(charset)); 1364 } 1365 1366 /** 1367 * Relativizes all files in the given {@code collection} against a {@code parent}. 1368 * 1369 * @param collection The collection of paths to relativize. 1370 * @param parent relativizes against this parent path. 1371 * @param sort Whether to sort the result. 1372 * @param comparator How to sort. 1373 * @return A collection of relativized paths, optionally sorted. 1374 */ 1375 static List<Path> relativize(final Collection<Path> collection, final Path parent, final boolean sort, final Comparator<? super Path> comparator) { 1376 Stream<Path> stream = collection.stream().map(parent::relativize); 1377 if (sort) { 1378 stream = comparator == null ? stream.sorted() : stream.sorted(comparator); 1379 } 1380 return stream.collect(Collectors.toList()); 1381 } 1382 1383 /** 1384 * Requires that the given {@link File} exists and throws an {@link IllegalArgumentException} if it doesn't. 1385 * 1386 * @param file The {@link File} to check. 1387 * @param fileParamName The parameter name to use in the exception message in case of {@code null} input. 1388 * @param options options indicating how to handle symbolic links. 1389 * @return the given file. 1390 * @throws NullPointerException if the given {@link File} is {@code null}. 1391 * @throws IllegalArgumentException if the given {@link File} does not exist. 1392 */ 1393 private static Path requireExists(final Path file, final String fileParamName, final LinkOption... options) { 1394 Objects.requireNonNull(file, fileParamName); 1395 if (!exists(file, options)) { 1396 throw new IllegalArgumentException("File system element for parameter '" + fileParamName + "' does not exist: '" + file + "'"); 1397 } 1398 return file; 1399 } 1400 1401 private static boolean setDosReadOnly(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException { 1402 final DosFileAttributeView dosFileAttributeView = getDosFileAttributeView(path, linkOptions); 1403 if (dosFileAttributeView != null) { 1404 dosFileAttributeView.setReadOnly(readOnly); 1405 return true; 1406 } 1407 return false; 1408 } 1409 1410 /** 1411 * Sets the given {@code targetFile}'s last modified time to the value from {@code sourceFile}. 1412 * 1413 * @param sourceFile The source path to query. 1414 * @param targetFile The target path to set. 1415 * @throws NullPointerException if sourceFile is {@code null}. 1416 * @throws NullPointerException if targetFile is {@code null}. 1417 * @throws IOException if setting the last-modified time failed. 1418 * @since 2.12.0 1419 */ 1420 public static void setLastModifiedTime(final Path sourceFile, final Path targetFile) throws IOException { 1421 Objects.requireNonNull(sourceFile, "sourceFile"); 1422 Files.setLastModifiedTime(targetFile, getLastModifiedTime(sourceFile)); 1423 } 1424 1425 /** 1426 * To delete a file in POSIX, you need Write and Execute permissions on its parent directory. 1427 * 1428 * @param parent The parent path for a file element to delete which needs RW permissions. 1429 * @param enableDeleteChildren true to set permissions to delete. 1430 * @param linkOptions options indicating how handle symbolic links. 1431 * @return true if the operation was attempted and succeeded, false if parent is null. 1432 * @throws IOException if an I/O error occurs. 1433 */ 1434 private static boolean setPosixDeletePermissions(final Path parent, final boolean enableDeleteChildren, final LinkOption... linkOptions) 1435 throws IOException { 1436 // To delete a file in POSIX, you need write and execute permissions on its parent directory. 1437 // @formatter:off 1438 return setPosixPermissions(parent, enableDeleteChildren, Arrays.asList( 1439 PosixFilePermission.OWNER_WRITE, 1440 //PosixFilePermission.GROUP_WRITE, 1441 //PosixFilePermission.OTHERS_WRITE, 1442 PosixFilePermission.OWNER_EXECUTE 1443 //PosixFilePermission.GROUP_EXECUTE, 1444 //PosixFilePermission.OTHERS_EXECUTE 1445 ), linkOptions); 1446 // @formatter:on 1447 } 1448 1449 /** 1450 * Low-level POSIX permission operation to set permissions. 1451 * 1452 * @param path Set this path's permissions. 1453 * @param addPermissions true to add, false to remove. 1454 * @param updatePermissions the List of PosixFilePermission to add or remove. 1455 * @param linkOptions options indicating how handle symbolic links. 1456 * @return true if the operation was attempted and succeeded, false if parent is null. 1457 * @throws IOException if an I/O error occurs. 1458 */ 1459 private static boolean setPosixPermissions(final Path path, final boolean addPermissions, final List<PosixFilePermission> updatePermissions, 1460 final LinkOption... linkOptions) throws IOException { 1461 if (path != null) { 1462 final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path, linkOptions); 1463 if (addPermissions) { 1464 permissions.addAll(updatePermissions); 1465 } else { 1466 permissions.removeAll(updatePermissions); 1467 } 1468 Files.setPosixFilePermissions(path, permissions); 1469 return true; 1470 } 1471 return false; 1472 } 1473 1474 private static void setPosixReadOnlyFile(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException { 1475 // Not Windows 10 1476 final Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path, linkOptions); 1477 // @formatter:off 1478 final List<PosixFilePermission> readPermissions = Arrays.asList( 1479 PosixFilePermission.OWNER_READ 1480 //PosixFilePermission.GROUP_READ, 1481 //PosixFilePermission.OTHERS_READ 1482 ); 1483 final List<PosixFilePermission> writePermissions = Arrays.asList( 1484 PosixFilePermission.OWNER_WRITE 1485 //PosixFilePermission.GROUP_WRITE, 1486 //PosixFilePermission.OTHERS_WRITE 1487 ); 1488 // @formatter:on 1489 if (readOnly) { 1490 // RO: We can read, we cannot write. 1491 permissions.addAll(readPermissions); 1492 permissions.removeAll(writePermissions); 1493 } else { 1494 // Not RO: We can read, we can write. 1495 permissions.addAll(readPermissions); 1496 permissions.addAll(writePermissions); 1497 } 1498 Files.setPosixFilePermissions(path, permissions); 1499 } 1500 1501 /** 1502 * Sets the given Path to the {@code readOnly} value. 1503 * <p> 1504 * This behavior is OS dependent. 1505 * </p> 1506 * 1507 * @param path The path to set. 1508 * @param readOnly true for read-only, false for not read-only. 1509 * @param linkOptions options indicating how to handle symbolic links. 1510 * @return The given path. 1511 * @throws IOException if an I/O error occurs. 1512 * @since 2.8.0 1513 */ 1514 public static Path setReadOnly(final Path path, final boolean readOnly, final LinkOption... linkOptions) throws IOException { 1515 try { 1516 // Windows is simplest 1517 if (setDosReadOnly(path, readOnly, linkOptions)) { 1518 return path; 1519 } 1520 } catch (final IOException ignored) { 1521 // Retry with POSIX below. 1522 } 1523 final Path parent = getParent(path); 1524 if (!isPosix(parent, linkOptions)) { // Test parent because we may not the permissions to test the file. 1525 throw new IOException(String.format("DOS or POSIX file operations not available for '%s' %s", path, Arrays.toString(linkOptions))); 1526 } 1527 // POSIX 1528 if (readOnly) { 1529 // RO 1530 // File, then parent dir (if any). 1531 setPosixReadOnlyFile(path, readOnly, linkOptions); 1532 setPosixDeletePermissions(parent, false, linkOptions); 1533 } else { 1534 // RE 1535 // Parent dir (if any), then file. 1536 setPosixDeletePermissions(parent, true, linkOptions); 1537 } 1538 return path; 1539 } 1540 1541 /** 1542 * Returns the size of the given file or directory. If the provided {@link Path} is a regular file, then the file's size is returned. If the argument is a 1543 * directory, then the size of the directory is calculated recursively. 1544 * <p> 1545 * Note that overflow is not detected, and the return value may be negative if overflow occurs. See {@link #sizeOfAsBigInteger(Path)} for an alternative 1546 * method that does not overflow. 1547 * </p> 1548 * 1549 * @param path the regular file or directory to return the size of, must not be {@code null}. 1550 * @return the length of the file, or recursive size of the directory, in bytes. 1551 * @throws NullPointerException if the file is {@code null}. 1552 * @throws IllegalArgumentException if the file does not exist. 1553 * @throws IOException if an I/O error occurs. 1554 * @since 2.12.0 1555 */ 1556 public static long sizeOf(final Path path) throws IOException { 1557 requireExists(path, "path"); 1558 return Files.isDirectory(path) ? sizeOfDirectory(path) : Files.size(path); 1559 } 1560 1561 /** 1562 * Returns the size of the given file or directory. If the provided {@link Path} is a regular file, then the file's size is returned. If the argument is a 1563 * directory, then the size of the directory is calculated recursively. 1564 * 1565 * @param path the regular file or directory to return the size of (must not be {@code null}). 1566 * @return the length of the file, or recursive size of the directory, provided (in bytes). 1567 * @throws NullPointerException if the file is {@code null}. 1568 * @throws IllegalArgumentException if the file does not exist. 1569 * @throws IOException if an I/O error occurs. 1570 * @since 2.12.0 1571 */ 1572 public static BigInteger sizeOfAsBigInteger(final Path path) throws IOException { 1573 requireExists(path, "path"); 1574 return Files.isDirectory(path) ? sizeOfDirectoryAsBigInteger(path) : BigInteger.valueOf(Files.size(path)); 1575 } 1576 1577 /** 1578 * Counts the size of a directory recursively (sum of the size of all files). 1579 * <p> 1580 * Note that overflow is not detected, and the return value may be negative if overflow occurs. See {@link #sizeOfDirectoryAsBigInteger(Path)} for an 1581 * alternative method that does not overflow. 1582 * </p> 1583 * 1584 * @param directory directory to inspect, must not be {@code null}. 1585 * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total is greater than {@link Long#MAX_VALUE}. 1586 * @throws NullPointerException if the directory is {@code null}. 1587 * @throws IOException if an I/O error occurs. 1588 * @since 2.12.0 1589 */ 1590 public static long sizeOfDirectory(final Path directory) throws IOException { 1591 return countDirectory(directory).getByteCounter().getLong(); 1592 } 1593 1594 /** 1595 * Counts the size of a directory recursively (sum of the size of all files). 1596 * 1597 * @param directory directory to inspect, must not be {@code null}. 1598 * @return size of directory in bytes, 0 if directory is security restricted. 1599 * @throws NullPointerException if the directory is {@code null}. 1600 * @throws IOException if an I/O error occurs. 1601 * @since 2.12.0 1602 */ 1603 public static BigInteger sizeOfDirectoryAsBigInteger(final Path directory) throws IOException { 1604 return countDirectoryAsBigInteger(directory).getByteCounter().getBigInteger(); 1605 } 1606 1607 /** 1608 * Converts an array of {@link FileVisitOption} to a {@link Set}. 1609 * 1610 * @param fileVisitOptions input array. 1611 * @return a new Set. 1612 */ 1613 static Set<FileVisitOption> toFileVisitOptionSet(final FileVisitOption... fileVisitOptions) { 1614 return fileVisitOptions == null ? EnumSet.noneOf(FileVisitOption.class) : Stream.of(fileVisitOptions).collect(Collectors.toSet()); 1615 } 1616 1617 /** 1618 * Implements behavior similar to the Unix "touch" utility. Creates a new file with size 0, or, if the file exists, just updates the file's modified time. 1619 * this method creates parent directories if they do not exist. 1620 * 1621 * @param file the file to touch. 1622 * @return The given file. 1623 * @throws NullPointerException if the parameter is {@code null}. 1624 * @throws IOException if setting the last-modified time failed or an I/O problem occurs.\ 1625 * @since 2.12.0 1626 */ 1627 public static Path touch(final Path file) throws IOException { 1628 Objects.requireNonNull(file, "file"); 1629 if (!Files.exists(file)) { 1630 createParentDirectories(file); 1631 Files.createFile(file); 1632 } else { 1633 FileTimes.setLastModifiedTime(file); 1634 } 1635 return file; 1636 } 1637 1638 /** 1639 * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor. 1640 * 1641 * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path. 1642 * 1643 * @param visitor See {@link Files#walkFileTree(Path,FileVisitor)}. 1644 * @param directory See {@link Files#walkFileTree(Path,FileVisitor)}. 1645 * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}. 1646 * @return the given visitor. 1647 * 1648 * @throws NoSuchFileException if the directory does not exist. 1649 * @throws IOException if an I/O error is thrown by a visitor method. 1650 * @throws NullPointerException if the directory is {@code null}. 1651 */ 1652 public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final Path directory) throws IOException { 1653 Files.walkFileTree(directory, visitor); 1654 return visitor; 1655 } 1656 1657 /** 1658 * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor. 1659 * 1660 * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path. 1661 * 1662 * @param start See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 1663 * @param options See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 1664 * @param maxDepth See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 1665 * @param visitor See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 1666 * @param <T> See {@link Files#walkFileTree(Path,Set,int,FileVisitor)}. 1667 * @return the given visitor. 1668 * 1669 * @throws IOException if an I/O error is thrown by a visitor method. 1670 */ 1671 public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final Path start, final Set<FileVisitOption> options, 1672 final int maxDepth) throws IOException { 1673 Files.walkFileTree(start, options, maxDepth, visitor); 1674 return visitor; 1675 } 1676 1677 /** 1678 * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor. 1679 * 1680 * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path. 1681 * 1682 * @param visitor See {@link Files#walkFileTree(Path,FileVisitor)}. 1683 * @param first See {@link Paths#get(String,String[])}. 1684 * @param more See {@link Paths#get(String,String[])}. 1685 * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}. 1686 * @return the given visitor. 1687 * 1688 * @throws IOException if an I/O error is thrown by a visitor method. 1689 */ 1690 public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final String first, final String... more) throws IOException { 1691 return visitFileTree(visitor, Paths.get(first, more)); 1692 } 1693 1694 /** 1695 * Performs {@link Files#walkFileTree(Path,FileVisitor)} and returns the given visitor. 1696 * 1697 * Note that {@link Files#walkFileTree(Path,FileVisitor)} returns the given path. 1698 * 1699 * @param visitor See {@link Files#walkFileTree(Path,FileVisitor)}. 1700 * @param uri See {@link Paths#get(URI)}. 1701 * @param <T> See {@link Files#walkFileTree(Path,FileVisitor)}. 1702 * @return the given visitor. 1703 * 1704 * @throws IOException if an I/O error is thrown by a visitor method. 1705 */ 1706 public static <T extends FileVisitor<? super Path>> T visitFileTree(final T visitor, final URI uri) throws IOException { 1707 return visitFileTree(visitor, Paths.get(uri)); 1708 } 1709 1710 /** 1711 * Waits for the file system to propagate a file creation, with a timeout. 1712 * <p> 1713 * This method repeatedly tests {@link Files#exists(Path,LinkOption...)} until it returns true up to the maximum time given. 1714 * </p> 1715 * 1716 * @param file the file to check, must not be {@code null}. 1717 * @param timeout the maximum time to wait. 1718 * @param options options indicating how to handle symbolic links. 1719 * @return true if file exists. 1720 * @throws NullPointerException if the file is {@code null}. 1721 * @since 2.12.0 1722 */ 1723 public static boolean waitFor(final Path file, final Duration timeout, final LinkOption... options) { 1724 Objects.requireNonNull(file, "file"); 1725 final Instant finishInstant = Instant.now().plus(timeout); 1726 boolean interrupted = false; 1727 final long minSleepMillis = 100; 1728 try { 1729 while (!exists(file, options)) { 1730 final Instant now = Instant.now(); 1731 if (now.isAfter(finishInstant)) { 1732 return false; 1733 } 1734 try { 1735 ThreadUtils.sleep(Duration.ofMillis(Math.min(minSleepMillis, finishInstant.minusMillis(now.toEpochMilli()).toEpochMilli()))); 1736 } catch (final InterruptedException ignore) { 1737 interrupted = true; 1738 } catch (final Exception ex) { 1739 break; 1740 } 1741 } 1742 } finally { 1743 if (interrupted) { 1744 Thread.currentThread().interrupt(); 1745 } 1746 } 1747 return exists(file, options); 1748 } 1749 1750 /** 1751 * Returns a stream of filtered paths. 1752 * <p> 1753 * The returned {@link Stream} may wrap one or more {@link DirectoryStream}s. When you require timely disposal of file system resources, use a 1754 * {@code try}-with-resources block to ensure invocation of the stream's {@link Stream#close()} method after the stream operations are completed. Calling a 1755 * closed stream causes a {@link IllegalStateException}. 1756 * </p> 1757 * 1758 * @param start the start path 1759 * @param pathFilter the path filter 1760 * @param maxDepth the maximum depth of directories to walk. 1761 * @param readAttributes whether to call the filters with file attributes (false passes null). 1762 * @param options the options to configure the walk. 1763 * @return a filtered stream of paths. 1764 * @throws IOException if an I/O error is thrown when accessing the starting file. 1765 * @since 2.9.0 1766 */ 1767 public static Stream<Path> walk(final Path start, final PathFilter pathFilter, final int maxDepth, final boolean readAttributes, 1768 final FileVisitOption... options) throws IOException { 1769 return Files.walk(start, maxDepth, options) 1770 .filter(path -> pathFilter.accept(path, readAttributes ? readBasicFileAttributesUnchecked(path) : null) == FileVisitResult.CONTINUE); 1771 } 1772 1773 private static <R> R withPosixFileAttributes(final Path path, final LinkOption[] linkOptions, final boolean overrideReadOnly, 1774 final IOFunction<PosixFileAttributes, R> function) throws IOException { 1775 final PosixFileAttributes posixFileAttributes = overrideReadOnly ? readPosixFileAttributes(path, linkOptions) : null; 1776 try { 1777 return function.apply(posixFileAttributes); 1778 } finally { 1779 if (posixFileAttributes != null && path != null && Files.exists(path, linkOptions)) { 1780 Files.setPosixFilePermissions(path, posixFileAttributes.permissions()); 1781 } 1782 } 1783 } 1784 1785 /** 1786 * Writes the given character sequence to a file at the given path. 1787 * 1788 * @param path The target file. 1789 * @param charSequence The character sequence text. 1790 * @param charset The Charset to encode the text. 1791 * @param openOptions options How to open the file. 1792 * @return The given path. 1793 * @throws IOException if an I/O error occurs writing to or creating the file. 1794 * @throws NullPointerException if either {@code path} or {@code charSequence} is {@code null}. 1795 * @since 2.12.0 1796 */ 1797 public static Path writeString(final Path path, final CharSequence charSequence, final Charset charset, final OpenOption... openOptions) 1798 throws IOException { 1799 // Check the text is not null before opening file. 1800 Objects.requireNonNull(path, "path"); 1801 Objects.requireNonNull(charSequence, "charSequence"); 1802 Files.write(path, String.valueOf(charSequence).getBytes(Charsets.toCharset(charset)), openOptions); 1803 return path; 1804 } 1805 1806 /** 1807 * Prevents instantiation. 1808 */ 1809 private PathUtils() { 1810 // do not instantiate. 1811 } 1812 1813}