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.attribute.BasicFileAttributes;
026import java.util.Arrays;
027import java.util.Objects;
028
029import org.apache.commons.io.file.Counters.PathCounters;
030
031/**
032 * Copies a source directory to a target directory.
033 *
034 * @since 2.7
035 */
036public class CopyDirectoryVisitor extends CountingPathVisitor {
037
038    private static final CopyOption[] EMPTY_COPY_OPTIONS = new CopyOption[0];
039
040    private final CopyOption[] copyOptions;
041    private final Path sourceDirectory;
042    private final Path targetDirectory;
043
044    /**
045     * Constructs a new visitor that deletes files except for the files and directories explicitly given.
046     *
047     * @param pathCounter How to count visits.
048     * @param sourceDirectory The source directory
049     * @param targetDirectory The target directory
050     * @param copyOptions Specifies how the copying should be done.
051     */
052    public CopyDirectoryVisitor(final PathCounters pathCounter, final Path sourceDirectory, final Path targetDirectory,
053        final CopyOption... copyOptions) {
054        super(pathCounter);
055        this.sourceDirectory = sourceDirectory;
056        this.targetDirectory = targetDirectory;
057        this.copyOptions = copyOptions == null ? EMPTY_COPY_OPTIONS : copyOptions.clone();
058    }
059
060    /**
061     * Copies the sourceFile to the targetFile.
062     *
063     * @param sourceFile the source file.
064     * @param targetFile the target file.
065     * @throws IOException if an I/O error occurs.
066     * @since 2.8.0
067     */
068    protected void copy(final Path sourceFile, final Path targetFile) throws IOException {
069        Files.copy(sourceFile, targetFile, copyOptions);
070    }
071
072    @Override
073    public boolean equals(final Object obj) {
074        if (this == obj) {
075            return true;
076        }
077        if (!super.equals(obj)) {
078            return false;
079        }
080        if (getClass() != obj.getClass()) {
081            return false;
082        }
083        final CopyDirectoryVisitor other = (CopyDirectoryVisitor) obj;
084        return Arrays.equals(copyOptions, other.copyOptions) && Objects.equals(sourceDirectory, other.sourceDirectory)
085                && Objects.equals(targetDirectory, other.targetDirectory);
086    }
087
088    /**
089     * Gets the copy options.
090     *
091     * @return the copy options.
092     * @since 2.8.0
093     */
094    public CopyOption[] getCopyOptions() {
095        return copyOptions.clone();
096    }
097
098    /**
099     * Gets the source directory.
100     *
101     * @return the source directory.
102     * @since 2.8.0
103     */
104    public Path getSourceDirectory() {
105        return sourceDirectory;
106    }
107
108    /**
109     * Gets the target directory.
110     *
111     * @return the target directory.
112     * @since 2.8.0
113     */
114    public Path getTargetDirectory() {
115        return targetDirectory;
116    }
117
118    @Override
119    public int hashCode() {
120        final int prime = 31;
121        int result = super.hashCode();
122        result = prime * result + Arrays.hashCode(copyOptions);
123        result = prime * result + Objects.hash(sourceDirectory, targetDirectory);
124        return result;
125    }
126
127    @Override
128    public FileVisitResult preVisitDirectory(final Path directory, final BasicFileAttributes attributes)
129        throws IOException {
130        final Path newTargetDir = targetDirectory.resolve(sourceDirectory.relativize(directory));
131        if (Files.notExists(newTargetDir)) {
132            Files.createDirectory(newTargetDir);
133        }
134        return super.preVisitDirectory(directory, attributes);
135    }
136
137    @Override
138    public FileVisitResult visitFile(final Path sourceFile, final BasicFileAttributes attributes) throws IOException {
139        final Path targetFile = targetDirectory.resolve(sourceDirectory.relativize(sourceFile));
140        copy(sourceFile, targetFile);
141        return super.visitFile(targetFile, attributes);
142    }
143
144}