CopyDirectoryVisitor.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.io.file;

  18. import java.io.IOException;
  19. import java.nio.file.CopyOption;
  20. import java.nio.file.FileVisitResult;
  21. import java.nio.file.Files;
  22. import java.nio.file.Path;
  23. import java.nio.file.ProviderMismatchException;
  24. import java.nio.file.attribute.BasicFileAttributes;
  25. import java.util.Arrays;
  26. import java.util.Objects;

  27. import org.apache.commons.io.file.Counters.PathCounters;

  28. /**
  29.  * Copies a source directory to a target directory.
  30.  *
  31.  * @since 2.7
  32.  */
  33. public class CopyDirectoryVisitor extends CountingPathVisitor {

  34.     private static CopyOption[] toCopyOption(final CopyOption... copyOptions) {
  35.         return copyOptions == null ? PathUtils.EMPTY_COPY_OPTIONS : copyOptions.clone();
  36.     }

  37.     private final CopyOption[] copyOptions;
  38.     private final Path sourceDirectory;
  39.     private final Path targetDirectory;

  40.     /**
  41.      * Constructs an instance that copies all files.
  42.      *
  43.      * @param pathCounter How to count visits.
  44.      * @param sourceDirectory The source directory
  45.      * @param targetDirectory The target directory
  46.      * @param copyOptions Specifies how the copying should be done.
  47.      */
  48.     public CopyDirectoryVisitor(final PathCounters pathCounter, final Path sourceDirectory, final Path targetDirectory, final CopyOption... copyOptions) {
  49.         super(pathCounter);
  50.         this.sourceDirectory = sourceDirectory;
  51.         this.targetDirectory = targetDirectory;
  52.         this.copyOptions = toCopyOption(copyOptions);
  53.     }

  54.     /**
  55.      * Constructs an instance that copies files matching the given file and directory filters.
  56.      *
  57.      * @param pathCounter How to count visits.
  58.      * @param fileFilter How to filter file paths.
  59.      * @param dirFilter How to filter directory paths.
  60.      * @param sourceDirectory The source directory
  61.      * @param targetDirectory The target directory
  62.      * @param copyOptions Specifies how the copying should be done.
  63.      * @since 2.9.0
  64.      */
  65.     public CopyDirectoryVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter, final Path sourceDirectory,
  66.         final Path targetDirectory, final CopyOption... copyOptions) {
  67.         super(pathCounter, fileFilter, dirFilter);
  68.         this.sourceDirectory = sourceDirectory;
  69.         this.targetDirectory = targetDirectory;
  70.         this.copyOptions = toCopyOption(copyOptions);
  71.     }

  72.     /**
  73.      * Copies the sourceFile to the targetFile.
  74.      *
  75.      * @param sourceFile the source file.
  76.      * @param targetFile the target file.
  77.      * @throws IOException if an I/O error occurs.
  78.      * @since 2.8.0
  79.      */
  80.     protected void copy(final Path sourceFile, final Path targetFile) throws IOException {
  81.         Files.copy(sourceFile, targetFile, copyOptions);
  82.     }

  83.     @Override
  84.     public boolean equals(final Object obj) {
  85.         if (this == obj) {
  86.             return true;
  87.         }
  88.         if (!super.equals(obj)) {
  89.             return false;
  90.         }
  91.         if (getClass() != obj.getClass()) {
  92.             return false;
  93.         }
  94.         final CopyDirectoryVisitor other = (CopyDirectoryVisitor) obj;
  95.         return Arrays.equals(copyOptions, other.copyOptions) && Objects.equals(sourceDirectory, other.sourceDirectory)
  96.             && Objects.equals(targetDirectory, other.targetDirectory);
  97.     }

  98.     /**
  99.      * Gets the copy options.
  100.      *
  101.      * @return the copy options.
  102.      * @since 2.8.0
  103.      */
  104.     public CopyOption[] getCopyOptions() {
  105.         return copyOptions.clone();
  106.     }

  107.     /**
  108.      * Gets the source directory.
  109.      *
  110.      * @return the source directory.
  111.      * @since 2.8.0
  112.      */
  113.     public Path getSourceDirectory() {
  114.         return sourceDirectory;
  115.     }

  116.     /**
  117.      * Gets the target directory.
  118.      *
  119.      * @return the target directory.
  120.      * @since 2.8.0
  121.      */
  122.     public Path getTargetDirectory() {
  123.         return targetDirectory;
  124.     }

  125.     @Override
  126.     public int hashCode() {
  127.         final int prime = 31;
  128.         int result = super.hashCode();
  129.         result = prime * result + Arrays.hashCode(copyOptions);
  130.         return prime * result + Objects.hash(sourceDirectory, targetDirectory);
  131.     }

  132.     @Override
  133.     public FileVisitResult preVisitDirectory(final Path directory, final BasicFileAttributes attributes)
  134.         throws IOException {
  135.         final Path newTargetDir = resolveRelativeAsString(directory);
  136.         if (Files.notExists(newTargetDir)) {
  137.             Files.createDirectory(newTargetDir);
  138.         }
  139.         return super.preVisitDirectory(directory, attributes);
  140.     }

  141.     /**
  142.      * Relativizes against {@code sourceDirectory}, then resolves against {@code targetDirectory}.
  143.      * <p>
  144.      * We call {@link Path#toString()} on the relativized value because we cannot use paths from different FileSystems which throws
  145.      * {@link ProviderMismatchException}. Special care is taken to handle differences in file system separators.
  146.      * </p>
  147.      *
  148.      * @param directory the directory to relativize.
  149.      * @return a new path, relativized against sourceDirectory, then resolved against targetDirectory.
  150.      */
  151.     private Path resolveRelativeAsString(final Path directory) {
  152.         return PathUtils.resolve(targetDirectory, sourceDirectory.relativize(directory));
  153.     }

  154.     @Override
  155.     public FileVisitResult visitFile(final Path sourceFile, final BasicFileAttributes attributes) throws IOException {
  156.         final Path targetFile = resolveRelativeAsString(sourceFile);
  157.         if (accept(sourceFile, attributes)) {
  158.             copy(sourceFile, targetFile);
  159.             updateFileCounters(targetFile, attributes);
  160.         }
  161.         return FileVisitResult.CONTINUE;
  162.     }

  163. }