|
|
|
@ -12,11 +12,12 @@ import java.io.Closeable;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.util.ArrayDeque;
|
|
|
|
|
import java.util.Collection;
|
|
|
|
|
import java.util.Deque;
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This source is a copy taken from java.nio.file.FileTreeWalker which is inaccessible.
|
|
|
|
|
*
|
|
|
|
|
* Walks a file tree, generating a sequence of events corresponding to the files
|
|
|
|
|
* in the tree.
|
|
|
|
|
*
|
|
|
|
@ -38,10 +39,11 @@ import java.util.Iterator;
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
class FileTreeWalker implements Closeable {
|
|
|
|
|
|
|
|
|
|
private final boolean followLinks;
|
|
|
|
|
private final LinkOption[] linkOptions;
|
|
|
|
|
private final int maxDepth;
|
|
|
|
|
private final ArrayDeque<DirectoryNode> stack = new ArrayDeque<>();
|
|
|
|
|
private final Deque<DirectoryNode> stack = new ArrayDeque<>();
|
|
|
|
|
private boolean closed;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -89,7 +91,7 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
/**
|
|
|
|
|
* The event types.
|
|
|
|
|
*/
|
|
|
|
|
static enum EventType {
|
|
|
|
|
enum EventType {
|
|
|
|
|
/**
|
|
|
|
|
* Start of a directory
|
|
|
|
|
*/
|
|
|
|
@ -109,13 +111,13 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
*/
|
|
|
|
|
static class Event {
|
|
|
|
|
private final EventType type;
|
|
|
|
|
private final Path file;
|
|
|
|
|
private final Path path;
|
|
|
|
|
private final BasicFileAttributes attrs;
|
|
|
|
|
private final IOException ioe;
|
|
|
|
|
|
|
|
|
|
private Event(EventType type, Path file, BasicFileAttributes attrs, IOException ioe) {
|
|
|
|
|
private Event(EventType type, Path path, BasicFileAttributes attrs, IOException ioe) {
|
|
|
|
|
this.type = type;
|
|
|
|
|
this.file = file;
|
|
|
|
|
this.path = path;
|
|
|
|
|
this.attrs = attrs;
|
|
|
|
|
this.ioe = ioe;
|
|
|
|
|
}
|
|
|
|
@ -132,8 +134,8 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
return type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Path file() {
|
|
|
|
|
return file;
|
|
|
|
|
Path path() {
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BasicFileAttributes attributes() {
|
|
|
|
@ -161,9 +163,9 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
boolean fl = false;
|
|
|
|
|
for (FileVisitOption option: options) {
|
|
|
|
|
// will throw NPE if options contains null
|
|
|
|
|
switch (option) {
|
|
|
|
|
case FOLLOW_LINKS : fl = true; break;
|
|
|
|
|
default:
|
|
|
|
|
if (Objects.requireNonNull(option) == FileVisitOption.FOLLOW_LINKS) {
|
|
|
|
|
fl = true;
|
|
|
|
|
} else {
|
|
|
|
|
throw new AssertionError("Should not get here");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -171,8 +173,7 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
throw new IllegalArgumentException("'maxDepth' is negative");
|
|
|
|
|
|
|
|
|
|
this.followLinks = fl;
|
|
|
|
|
this.linkOptions = (fl) ? new LinkOption[0] :
|
|
|
|
|
new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
|
|
|
|
|
this.linkOptions = (fl) ? new LinkOption[0] : new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
|
|
|
|
|
this.maxDepth = maxDepth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -181,22 +182,18 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
* the walk is following sym links is not. The {@code canUseCached}
|
|
|
|
|
* argument determines whether this method can use cached attributes.
|
|
|
|
|
*/
|
|
|
|
|
private BasicFileAttributes getAttributes(Path file, boolean canUseCached)
|
|
|
|
|
throws IOException
|
|
|
|
|
{
|
|
|
|
|
private BasicFileAttributes getAttributes(Path file)
|
|
|
|
|
throws IOException {
|
|
|
|
|
// attempt to get attributes of file. If fails and we are following
|
|
|
|
|
// links then a link target might not exist so get attributes of link
|
|
|
|
|
BasicFileAttributes attrs;
|
|
|
|
|
try {
|
|
|
|
|
attrs = Files.readAttributes(file, BasicFileAttributes.class, linkOptions);
|
|
|
|
|
} catch (IOException ioe) {
|
|
|
|
|
if (!followLinks)
|
|
|
|
|
if (!followLinks) {
|
|
|
|
|
throw ioe;
|
|
|
|
|
|
|
|
|
|
// attempt to get attrmptes without following links
|
|
|
|
|
attrs = Files.readAttributes(file,
|
|
|
|
|
BasicFileAttributes.class,
|
|
|
|
|
LinkOption.NOFOLLOW_LINKS);
|
|
|
|
|
}
|
|
|
|
|
attrs = Files.readAttributes(file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
|
|
|
|
|
}
|
|
|
|
|
return attrs;
|
|
|
|
|
}
|
|
|
|
@ -232,70 +229,39 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
/**
|
|
|
|
|
* Visits the given file, returning the {@code Event} corresponding to that
|
|
|
|
|
* visit.
|
|
|
|
|
*
|
|
|
|
|
* The {@code ignoreSecurityException} parameter determines whether
|
|
|
|
|
* any SecurityException should be ignored or not. If a SecurityException
|
|
|
|
|
* is thrown, and is ignored, then this method returns {@code null} to
|
|
|
|
|
* mean that there is no event corresponding to a visit to the file.
|
|
|
|
|
*
|
|
|
|
|
* The {@code canUseCached} parameter determines whether cached attributes
|
|
|
|
|
* for the file can be used or not.
|
|
|
|
|
*/
|
|
|
|
|
private Event visit(Path entry, boolean ignoreSecurityException, boolean canUseCached) {
|
|
|
|
|
// need the file attributes
|
|
|
|
|
private Event visit(Path entry) {
|
|
|
|
|
BasicFileAttributes attrs;
|
|
|
|
|
try {
|
|
|
|
|
attrs = getAttributes(entry, canUseCached);
|
|
|
|
|
attrs = getAttributes(entry);
|
|
|
|
|
} catch (IOException ioe) {
|
|
|
|
|
return new Event(EventType.ENTRY, entry, ioe);
|
|
|
|
|
} catch (SecurityException se) {
|
|
|
|
|
if (ignoreSecurityException)
|
|
|
|
|
return null;
|
|
|
|
|
throw se;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// at maximum depth or file is not a directory
|
|
|
|
|
int depth = stack.size();
|
|
|
|
|
if (depth >= maxDepth || !attrs.isDirectory()) {
|
|
|
|
|
return new Event(EventType.ENTRY, entry, attrs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check for cycles when following links
|
|
|
|
|
if (followLinks && wouldLoop(entry, attrs.fileKey())) {
|
|
|
|
|
return new Event(EventType.ENTRY, entry,
|
|
|
|
|
new FileSystemLoopException(entry.toString()));
|
|
|
|
|
return new Event(EventType.ENTRY, entry, new FileSystemLoopException(entry.toString()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// file is a directory, attempt to open it
|
|
|
|
|
DirectoryStream<Path> stream = null;
|
|
|
|
|
DirectoryStream<Path> stream;
|
|
|
|
|
try {
|
|
|
|
|
stream = Files.newDirectoryStream(entry);
|
|
|
|
|
} catch (IOException ioe) {
|
|
|
|
|
return new Event(EventType.ENTRY, entry, ioe);
|
|
|
|
|
} catch (SecurityException se) {
|
|
|
|
|
if (ignoreSecurityException)
|
|
|
|
|
return null;
|
|
|
|
|
throw se;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// push a directory node to the stack and return an event
|
|
|
|
|
stack.push(new DirectoryNode(entry, attrs.fileKey(), stream));
|
|
|
|
|
return new Event(EventType.START_DIRECTORY, entry, attrs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Start walking from the given file.
|
|
|
|
|
*/
|
|
|
|
|
Event walk(Path file) {
|
|
|
|
|
if (closed)
|
|
|
|
|
if (closed) {
|
|
|
|
|
throw new IllegalStateException("Closed");
|
|
|
|
|
|
|
|
|
|
Event ev = visit(file,
|
|
|
|
|
false, // ignoreSecurityException
|
|
|
|
|
false); // canUseCached
|
|
|
|
|
assert ev != null;
|
|
|
|
|
return ev;
|
|
|
|
|
}
|
|
|
|
|
return visit(file);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -304,16 +270,11 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
*/
|
|
|
|
|
Event next() {
|
|
|
|
|
DirectoryNode top = stack.peek();
|
|
|
|
|
if (top == null)
|
|
|
|
|
return null; // stack is empty, we are done
|
|
|
|
|
|
|
|
|
|
// continue iteration of the directory at the top of the stack
|
|
|
|
|
Event ev;
|
|
|
|
|
do {
|
|
|
|
|
if (top == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
Path entry = null;
|
|
|
|
|
IOException ioe = null;
|
|
|
|
|
|
|
|
|
|
// get next entry in the directory
|
|
|
|
|
if (!top.skipped()) {
|
|
|
|
|
Iterator<Path> iterator = top.iterator();
|
|
|
|
|
try {
|
|
|
|
@ -324,9 +285,6 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
ioe = x.getCause();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// no next entry so close and pop directory,
|
|
|
|
|
// creating corresponding event
|
|
|
|
|
if (entry == null) {
|
|
|
|
|
try {
|
|
|
|
|
top.stream().close();
|
|
|
|
@ -340,15 +298,7 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
stack.pop();
|
|
|
|
|
return new Event(EventType.END_DIRECTORY, top.directory(), ioe);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// visit the entry
|
|
|
|
|
ev = visit(entry,
|
|
|
|
|
true, // ignoreSecurityException
|
|
|
|
|
true); // canUseCached
|
|
|
|
|
|
|
|
|
|
} while (ev == null);
|
|
|
|
|
|
|
|
|
|
return ev;
|
|
|
|
|
return visit(entry);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
@ -362,17 +312,9 @@ class FileTreeWalker implements Closeable {
|
|
|
|
|
DirectoryNode node = stack.pop();
|
|
|
|
|
try {
|
|
|
|
|
node.stream().close();
|
|
|
|
|
} catch (IOException ignore) { }
|
|
|
|
|
}
|
|
|
|
|
} catch (IOException ignore) {
|
|
|
|
|
// ignore
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Skips the remaining entries in the directory at the top of the stack.
|
|
|
|
|
* This method is a no-op if the stack is empty or the walker is closed.
|
|
|
|
|
*/
|
|
|
|
|
void skipRemainingSiblings() {
|
|
|
|
|
if (!stack.isEmpty()) {
|
|
|
|
|
stack.peek().skip();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|