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 static CopyOption[] toCopyOption(final CopyOption... copyOptions) { 040 return copyOptions == null ? PathUtils.EMPTY_COPY_OPTIONS : copyOptions.clone(); 041 } 042 043 private final CopyOption[] copyOptions; 044 private final Path sourceDirectory; 045 private final Path targetDirectory; 046 047 /** 048 * Constructs a new visitor that deletes files except for the files and directories explicitly given. 049 * 050 * @param pathCounter How to count visits. 051 * @param sourceDirectory The source directory 052 * @param targetDirectory The target directory 053 * @param copyOptions Specifies how the copying should be done. 054 */ 055 public CopyDirectoryVisitor(final PathCounters pathCounter, final Path sourceDirectory, final Path targetDirectory, final CopyOption... copyOptions) { 056 super(pathCounter); 057 this.sourceDirectory = sourceDirectory; 058 this.targetDirectory = targetDirectory; 059 this.copyOptions = toCopyOption(copyOptions); 060 } 061 062 /** 063 * Constructs a new visitor that deletes files except for the files and directories explicitly given. 064 * 065 * @param pathCounter How to count visits. 066 * @param fileFilter How to filter file paths. 067 * @param dirFilter How to filter directory paths. 068 * @param sourceDirectory The source directory 069 * @param targetDirectory The target directory 070 * @param copyOptions Specifies how the copying should be done. 071 * @since 2.9.0 072 */ 073 public CopyDirectoryVisitor(final PathCounters pathCounter, final PathFilter fileFilter, final PathFilter dirFilter, final Path sourceDirectory, 074 final Path targetDirectory, final CopyOption... copyOptions) { 075 super(pathCounter, fileFilter, dirFilter); 076 this.sourceDirectory = sourceDirectory; 077 this.targetDirectory = targetDirectory; 078 this.copyOptions = toCopyOption(copyOptions); 079 } 080 081 /** 082 * Copies the sourceFile to the targetFile. 083 * 084 * @param sourceFile the source file. 085 * @param targetFile the target file. 086 * @throws IOException if an I/O error occurs. 087 * @since 2.8.0 088 */ 089 protected void copy(final Path sourceFile, final Path targetFile) throws IOException { 090 Files.copy(sourceFile, targetFile, copyOptions); 091 } 092 093 @Override 094 public boolean equals(final Object obj) { 095 if (this == obj) { 096 return true; 097 } 098 if (!super.equals(obj)) { 099 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}