blob: fea21576aa66796c463c2f6054cd4eaab91b35b8 [file] [log] [blame]
package org.purl.wf4ever.robundle.utils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.CopyOption;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
public class RecursiveCopyFileVisitor extends SimpleFileVisitor<Path> {
public enum RecursiveCopyOption implements CopyOption {
/**
* Ignore any errors, copy as much as possible. The default is to stop
* on the first IOException.
*
*/
IGNORE_ERRORS,
}
public static void copyRecursively(final Path source,
final Path destination, final CopyOption... copyOptions)
throws IOException {
final Set<CopyOption> copyOptionsSet = new HashSet<>(
Arrays.asList(copyOptions));
if (!Files.isDirectory(source)) {
throw new FileNotFoundException("Not a directory: " + source);
}
if (Files.isDirectory(destination)
&& !copyOptionsSet
.contains(StandardCopyOption.REPLACE_EXISTING)) {
throw new FileAlreadyExistsException(destination.toString());
}
Path destinationParent = destination.getParent();
if (destinationParent != null && !Files.isDirectory(destinationParent)) {
throw new FileNotFoundException("Not a directory: "
+ destinationParent);
}
RecursiveCopyFileVisitor visitor = new RecursiveCopyFileVisitor(
destination, copyOptionsSet, source);
Set<FileVisitOption> walkOptions = EnumSet
.noneOf(FileVisitOption.class);
if (!copyOptionsSet.contains(LinkOption.NOFOLLOW_LINKS)) {
walkOptions = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
}
Files.walkFileTree(source, walkOptions, Integer.MAX_VALUE, visitor);
}
private final CopyOption[] copyOptions;
private final Set<CopyOption> copyOptionsSet;
private final Path destination;
private boolean ignoreErrors;
private final LinkOption[] linkOptions;
private final Path source;
RecursiveCopyFileVisitor(Path destination, Set<CopyOption> copyOptionsSet,
Path source) {
this.destination = destination;
this.source = source;
this.copyOptionsSet = new HashSet<CopyOption>(copyOptionsSet);
HashSet<Object> linkOptionsSet = new HashSet<>();
for (CopyOption option : copyOptionsSet) {
copyOptionsSet.add(option);
if (option instanceof LinkOption) {
linkOptionsSet.add((LinkOption) option);
}
}
this.linkOptions = linkOptionsSet
.toArray(new LinkOption[(linkOptionsSet.size())]);
this.ignoreErrors = copyOptionsSet
.contains(RecursiveCopyOption.IGNORE_ERRORS);
// To avoid UnsupporteOperationException from native java.nio operations
// we strip our own options out
copyOptionsSet.removeAll(EnumSet.allOf(RecursiveCopyOption.class));
copyOptions = copyOptionsSet.toArray(new CopyOption[(copyOptionsSet
.size())]);
}
private URI pathOnly(URI uri) {
if (!uri.isAbsolute()) {
return uri;
}
String path = uri.getRawPath();
// if (! uri.isOpaque()) {
// path = uri.getPath();
// }
if (uri.getScheme().equals("jar")) {
String part = uri.getSchemeSpecificPart();
int slashPos = part.indexOf("!/");
path = part.substring(slashPos + 1, part.length());
}
if (path == null) {
throw new IllegalArgumentException("Can't extract path from URI "
+ uri);
}
if (!path.startsWith("/")) {
path = "/" + path;
}
try {
return new URI(null, null, path, null);
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Can't extract path from URI "
+ uri, e);
}
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException {
try {
if (copyOptionsSet.contains(StandardCopyOption.COPY_ATTRIBUTES)) {
// Copy file times
// Inspired by
// java.nio.file.CopyMoveHelper.copyToForeignTarget()
BasicFileAttributes attrs = Files.readAttributes(dir,
BasicFileAttributes.class, linkOptions);
BasicFileAttributeView view = Files.getFileAttributeView(
toDestination(dir), BasicFileAttributeView.class,
linkOptions);
view.setTimes(attrs.lastModifiedTime(), attrs.lastAccessTime(),
attrs.creationTime());
}
return FileVisitResult.CONTINUE;
} catch (IOException ex) {
return visitFileFailed(dir, ex);
}
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
try {
Path destinationDir = toDestination(dir);
if (copyOptionsSet.contains(StandardCopyOption.REPLACE_EXISTING)
&& Files.isDirectory(destinationDir)) {
return FileVisitResult.CONTINUE;
}
Files.copy(dir, destinationDir, copyOptions);
// Files.createDirectory(destinationDir);
// System.out.println("Created " + destinationDir + " " +
// destinationDir.toUri());
return FileVisitResult.CONTINUE;
} catch (IOException ex) {
// Eat or rethrow depending on IGNORE_ERRORS
return visitFileFailed(dir, ex);
}
}
private Path toDestination(Path path) {
if (path.equals(source)) {
// Top-level folder
return destination;
}
// Path relativize = source.relativize(path);
// return destination.resolve(relativize);
// The above does not work as ZipPath throws ProviderMisMatchException
// when given a relative filesystem Path
URI rel = pathOnly(uriWithSlash(source)).relativize(
pathOnly(path.toUri()));
if (rel.isAbsolute()) {
throw new IllegalStateException("Can't relativize " + rel);
}
URI dest = uriWithSlash(destination).resolve(rel);
return destination.getFileSystem().provider().getPath(dest);
}
private URI uriWithSlash(Path dir) {
URI uri = dir.toUri();
if (!uri.toString().endsWith("/")) {
return URI.create(uri.toString() + "/");
}
return uri;
}
@Override
public FileVisitResult visitFile(final Path file, BasicFileAttributes attrs)
throws IOException {
try {
Files.copy(file, toDestination(file), copyOptions);
return FileVisitResult.CONTINUE;
} catch (IOException ex) {
return visitFileFailed(file, ex);
}
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
if (ignoreErrors) {
return FileVisitResult.SKIP_SUBTREE;
}
// Or - throw exception
return super.visitFileFailed(file, exc);
}
}