View Javadoc
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    *      http://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  
18  package org.apache.commons.io.file;
19  
20  import java.io.IOException;
21  import java.nio.file.CopyOption;
22  import java.nio.file.FileVisitResult;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.nio.file.ProviderMismatchException;
26  import java.nio.file.attribute.BasicFileAttributes;
27  import java.util.Arrays;
28  import java.util.Objects;
29  
30  import org.apache.commons.io.file.Counters.PathCounters;
31  
32  /**
33   * Copies a source directory to a target directory.
34   *
35   * @since 2.7
36   */
37  public class CopyDirectoryVisitor extends CountingPathVisitor {
38  
39      private static CopyOption[] toCopyOption(final CopyOption... copyOptions) {
40          return copyOptions == null ? PathUtils.EMPTY_COPY_OPTIONS : copyOptions.clone();
41      }
42  
43      private final CopyOption[] copyOptions;
44      private final Path sourceDirectory;
45      private final Path targetDirectory;
46  
47      /**
48       * Constructs a new visitor that deletes files except for the files and directories explicitly given.
49       *
50       * @param pathCounter How to count visits.
51       * @param sourceDirectory The source directory
52       * @param targetDirectory The target directory
53       * @param copyOptions Specifies how the copying should be done.
54       */
55      public CopyDirectoryVisitor(final PathCounters pathCounter, final Path sourceDirectory, final Path targetDirectory, final CopyOption... copyOptions) {
56          super(pathCounter);
57          this.sourceDirectory = sourceDirectory;
58          this.targetDirectory = targetDirectory;
59          this.copyOptions = toCopyOption(copyOptions);
60      }
61  
62      /**
63       * Constructs a new visitor that deletes files except for the files and directories explicitly given.
64       *
65       * @param pathCounter How to count visits.
66       * @param fileFilter How to filter file paths.
67       * @param dirFilter How to filter directory paths.
68       * @param sourceDirectory The source directory
69       * @param targetDirectory The target directory
70       * @param copyOptions Specifies how the copying should be done.
71       * @since 2.9.0
72       */
73      public CopyDirectoryVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter, final Path sourceDirectory,
74          final Path targetDirectory, final CopyOption... copyOptions) {
75          super(pathCounter, fileFilter, dirFilter);
76          this.sourceDirectory = sourceDirectory;
77          this.targetDirectory = targetDirectory;
78          this.copyOptions = toCopyOption(copyOptions);
79      }
80  
81      /**
82       * Copies the sourceFile to the targetFile.
83       *
84       * @param sourceFile the source file.
85       * @param targetFile the target file.
86       * @throws IOException if an I/O error occurs.
87       * @since 2.8.0
88       */
89      protected void copy(final Path sourceFile, final Path targetFile) throws IOException {
90          Files.copy(sourceFile, targetFile, copyOptions);
91      }
92  
93      @Override
94      public boolean equals(final Object obj) {
95          if (this == obj) {
96              return true;
97          }
98          if (!super.equals(obj)) {
99              return false;
100         }
101         if (getClass() != obj.getClass()) {
102             return false;
103         }
104         final CopyDirectoryVisitor other = (CopyDirectoryVisitor) obj;
105         return Arrays.equals(copyOptions, other.copyOptions) && Objects.equals(sourceDirectory, other.sourceDirectory)
106             && Objects.equals(targetDirectory, other.targetDirectory);
107     }
108 
109     /**
110      * Gets the copy options.
111      *
112      * @return the copy options.
113      * @since 2.8.0
114      */
115     public CopyOption[] getCopyOptions() {
116         return copyOptions.clone();
117     }
118 
119     /**
120      * Gets the source directory.
121      *
122      * @return the source directory.
123      * @since 2.8.0
124      */
125     public Path getSourceDirectory() {
126         return sourceDirectory;
127     }
128 
129     /**
130      * Gets the target directory.
131      *
132      * @return the target directory.
133      * @since 2.8.0
134      */
135     public Path getTargetDirectory() {
136         return targetDirectory;
137     }
138 
139     @Override
140     public int hashCode() {
141         final int prime = 31;
142         int result = super.hashCode();
143         result = prime * result + Arrays.hashCode(copyOptions);
144         result = prime * result + Objects.hash(sourceDirectory, targetDirectory);
145         return result;
146     }
147 
148     @Override
149     public FileVisitResult preVisitDirectory(final Path directory, final BasicFileAttributes attributes)
150         throws IOException {
151         final Path newTargetDir = resolveRelativeAsString(directory);
152         if (Files.notExists(newTargetDir)) {
153             Files.createDirectory(newTargetDir);
154         }
155         return super.preVisitDirectory(directory, attributes);
156     }
157 
158     /**
159      * Relativizes against {@code sourceDirectory}, then resolves against {@code targetDirectory}.
160      *
161      * We have to call {@link Path#toString()} relative value because we cannot use paths belonging to different
162      * FileSystems in the Path methods, usually this leads to {@link ProviderMismatchException}.
163      *
164      * @param directory the directory to relativize.
165      * @return a new path, relativized against sourceDirectory, then resolved against targetDirectory.
166      */
167     private Path resolveRelativeAsString(final Path directory) {
168         return targetDirectory.resolve(sourceDirectory.relativize(directory).toString());
169     }
170 
171     @Override
172     public FileVisitResult visitFile(final Path sourceFile, final BasicFileAttributes attributes) throws IOException {
173         final Path targetFile = resolveRelativeAsString(sourceFile);
174         copy(sourceFile, targetFile);
175         return super.visitFile(targetFile, attributes);
176     }
177 
178 }