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