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.IOException; 021import java.nio.file.CopyOption; 022import java.nio.file.FileVisitResult; 023import java.nio.file.Files; 024import java.nio.file.Path; 025import java.nio.file.ProviderMismatchException; 026import java.nio.file.attribute.BasicFileAttributes; 027import java.util.Arrays; 028import java.util.Objects; 029 030import org.apache.commons.io.file.Counters.PathCounters; 031 032/** 033 * Copies a source directory to a target directory. 034 * 035 * @since 2.7 036 */ 037public class CopyDirectoryVisitor extends CountingPathVisitor { 038 039 private final CopyOption[] copyOptions; 040 private final Path sourceDirectory; 041 private final Path targetDirectory; 042 043 /** 044 * Constructs a new visitor that deletes files except for the files and directories explicitly given. 045 * 046 * @param pathCounter How to count visits. 047 * @param sourceDirectory The source directory 048 * @param targetDirectory The target directory 049 * @param copyOptions Specifies how the copying should be done. 050 */ 051 public CopyDirectoryVisitor(final PathCounters pathCounter, final Path sourceDirectory, final Path targetDirectory, 052 final CopyOption... copyOptions) { 053 super(pathCounter); 054 this.sourceDirectory = sourceDirectory; 055 this.targetDirectory = targetDirectory; 056 this.copyOptions = copyOptions == null ? PathUtils.EMPTY_COPY_OPTIONS : copyOptions.clone(); 057 } 058 059 /** 060 * Constructs a new visitor that deletes files except for the files and directories explicitly given. 061 * 062 * @param pathCounter How to count visits. 063 * @param fileFilter How to filter file paths. 064 * @param dirFilter How to filter directory paths. 065 * @param sourceDirectory The source directory 066 * @param targetDirectory The target directory 067 * @param copyOptions Specifies how the copying should be done. 068 * @since 2.9.0 069 */ 070 public CopyDirectoryVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter, 071 final Path sourceDirectory, final Path targetDirectory, final CopyOption... copyOptions) { 072 super(pathCounter, fileFilter, dirFilter); 073 this.sourceDirectory = sourceDirectory; 074 this.targetDirectory = targetDirectory; 075 this.copyOptions = copyOptions == null ? PathUtils.EMPTY_COPY_OPTIONS : copyOptions.clone(); 076 } 077 078 /** 079 * Copies the sourceFile to the targetFile. 080 * 081 * @param sourceFile the source file. 082 * @param targetFile the target file. 083 * @throws IOException if an I/O error occurs. 084 * @since 2.8.0 085 */ 086 protected void copy(final Path sourceFile, final Path targetFile) throws IOException { 087 Files.copy(sourceFile, targetFile, copyOptions); 088 } 089 090 @Override 091 public boolean equals(final Object obj) { 092 if (this == obj) { 093 return true; 094 } 095 if (!super.equals(obj)) { 096 return false; 097 } 098 if (getClass() != obj.getClass()) { 099 return false; 100 } 101 final CopyDirectoryVisitor other = (CopyDirectoryVisitor) obj; 102 return Arrays.equals(copyOptions, other.copyOptions) && Objects.equals(sourceDirectory, other.sourceDirectory) 103 && Objects.equals(targetDirectory, other.targetDirectory); 104 } 105 106 /** 107 * Gets the copy options. 108 * 109 * @return the copy options. 110 * @since 2.8.0 111 */ 112 public CopyOption[] getCopyOptions() { 113 return copyOptions.clone(); 114 } 115 116 /** 117 * Gets the source directory. 118 * 119 * @return the source directory. 120 * @since 2.8.0 121 */ 122 public Path getSourceDirectory() { 123 return sourceDirectory; 124 } 125 126 /** 127 * Gets the target directory. 128 * 129 * @return the target directory. 130 * @since 2.8.0 131 */ 132 public Path getTargetDirectory() { 133 return targetDirectory; 134 } 135 136 @Override 137 public int hashCode() { 138 final int prime = 31; 139 int result = super.hashCode(); 140 result = prime * result + Arrays.hashCode(copyOptions); 141 result = prime * result + Objects.hash(sourceDirectory, targetDirectory); 142 return result; 143 } 144 145 @Override 146 public FileVisitResult preVisitDirectory(final Path directory, final BasicFileAttributes attributes) 147 throws IOException { 148 final Path newTargetDir = resolveRelativeAsString(directory); 149 if (Files.notExists(newTargetDir)) { 150 Files.createDirectory(newTargetDir); 151 } 152 return super.preVisitDirectory(directory, attributes); 153 } 154 155 /** 156 * Relativizes against {@code sourceDirectory}, then resolves against {@code targetDirectory}. 157 * 158 * We have to call {@link Path#toString()} relative value because we cannot use paths belonging to different 159 * FileSystems in the Path methods, usually this leads to {@link ProviderMismatchException}. 160 * 161 * @param directory the directory to relativize. 162 * @return a new path, relativized against sourceDirectory, then resolved against targetDirectory. 163 */ 164 private Path resolveRelativeAsString(final Path directory) { 165 return targetDirectory.resolve(sourceDirectory.relativize(directory).toString()); 166 } 167 168 @Override 169 public FileVisitResult visitFile(final Path sourceFile, final BasicFileAttributes attributes) throws IOException { 170 final Path targetFile = resolveRelativeAsString(sourceFile); 171 copy(sourceFile, targetFile); 172 return super.visitFile(targetFile, attributes); 173 } 174 175}