mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-14 08:58:46 +00:00
8012930: (fs) Eliminate recursion from FileTreeWalker
Reviewed-by: chegar
This commit is contained in:
parent
79174472a1
commit
1dea05c94f
@ -25,27 +25,147 @@
|
||||
|
||||
package java.nio.file;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import sun.nio.fs.BasicFileAttributesHolder;
|
||||
|
||||
/**
|
||||
* Simple file tree walker that works in a similar manner to nftw(3C).
|
||||
* Walks a file tree, generating a sequence of events corresponding to the files
|
||||
* in the tree.
|
||||
*
|
||||
* <pre>{@code
|
||||
* Path top = ...
|
||||
* Set<FileVisitOption> options = ...
|
||||
* int maxDepth = ...
|
||||
*
|
||||
* try (FileTreeWalker walker = new FileTreeWalker(options, maxDepth)) {
|
||||
* FileTreeWalker.Event ev = walker.walk(top);
|
||||
* do {
|
||||
* process(ev);
|
||||
* ev = walker.next();
|
||||
* } while (ev != null);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @see Files#walkFileTree
|
||||
*/
|
||||
|
||||
class FileTreeWalker {
|
||||
class FileTreeWalker implements Closeable {
|
||||
private final boolean followLinks;
|
||||
private final LinkOption[] linkOptions;
|
||||
private final FileVisitor<? super Path> visitor;
|
||||
private final int maxDepth;
|
||||
private final ArrayDeque<DirectoryNode> stack = new ArrayDeque<>();
|
||||
private boolean closed;
|
||||
|
||||
FileTreeWalker(Set<FileVisitOption> options,
|
||||
FileVisitor<? super Path> visitor,
|
||||
int maxDepth)
|
||||
{
|
||||
/**
|
||||
* The element on the walking stack corresponding to a directory node.
|
||||
*/
|
||||
private static class DirectoryNode {
|
||||
private final Path dir;
|
||||
private final Object key;
|
||||
private final DirectoryStream<Path> stream;
|
||||
private final Iterator<Path> iterator;
|
||||
private boolean skipped;
|
||||
|
||||
DirectoryNode(Path dir, Object key, DirectoryStream<Path> stream) {
|
||||
this.dir = dir;
|
||||
this.key = key;
|
||||
this.stream = stream;
|
||||
this.iterator = stream.iterator();
|
||||
}
|
||||
|
||||
Path directory() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
Object key() {
|
||||
return key;
|
||||
}
|
||||
|
||||
DirectoryStream<Path> stream() {
|
||||
return stream;
|
||||
}
|
||||
|
||||
Iterator<Path> iterator() {
|
||||
return iterator;
|
||||
}
|
||||
|
||||
void skip() {
|
||||
skipped = true;
|
||||
}
|
||||
|
||||
boolean skipped() {
|
||||
return skipped;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The event types.
|
||||
*/
|
||||
static enum EventType {
|
||||
/**
|
||||
* Start of a directory
|
||||
*/
|
||||
START_DIRECTORY,
|
||||
/**
|
||||
* End of a directory
|
||||
*/
|
||||
END_DIRECTORY,
|
||||
/**
|
||||
* An entry in a directory
|
||||
*/
|
||||
ENTRY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Events returned by the {@link #walk} and {@link #next} methods.
|
||||
*/
|
||||
static class Event {
|
||||
private final EventType type;
|
||||
private final Path file;
|
||||
private final BasicFileAttributes attrs;
|
||||
private final IOException ioe;
|
||||
|
||||
private Event(EventType type, Path file, BasicFileAttributes attrs, IOException ioe) {
|
||||
this.type = type;
|
||||
this.file = file;
|
||||
this.attrs = attrs;
|
||||
this.ioe = ioe;
|
||||
}
|
||||
|
||||
Event(EventType type, Path file, BasicFileAttributes attrs) {
|
||||
this(type, file, attrs, null);
|
||||
}
|
||||
|
||||
Event(EventType type, Path file, IOException ioe) {
|
||||
this(type, file, null, ioe);
|
||||
}
|
||||
|
||||
EventType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
Path file() {
|
||||
return file;
|
||||
}
|
||||
|
||||
BasicFileAttributes attributes() {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
IOException ioeException() {
|
||||
return ioe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code FileTreeWalker}.
|
||||
*/
|
||||
FileTreeWalker(Set<FileVisitOption> options, int maxDepth) {
|
||||
boolean fl = false;
|
||||
for (FileVisitOption option: options) {
|
||||
// will throw NPE if options contains null
|
||||
@ -58,191 +178,236 @@ class FileTreeWalker {
|
||||
this.followLinks = fl;
|
||||
this.linkOptions = (fl) ? new LinkOption[0] :
|
||||
new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
|
||||
this.visitor = visitor;
|
||||
this.maxDepth = maxDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk file tree starting at the given file
|
||||
* Returns the attributes of the given file, taking into account whether
|
||||
* the walk is following sym links is not. The {@code canUseCached}
|
||||
* argument determines whether this method can use cached attributes.
|
||||
*/
|
||||
void walk(Path start) throws IOException {
|
||||
FileVisitResult result = walk(start,
|
||||
0,
|
||||
new ArrayList<AncestorDirectory>());
|
||||
Objects.requireNonNull(result, "FileVisitor returned null");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file
|
||||
* the directory to visit
|
||||
* @param depth
|
||||
* depth remaining
|
||||
* @param ancestors
|
||||
* use when cycle detection is enabled
|
||||
*/
|
||||
private FileVisitResult walk(Path file,
|
||||
int depth,
|
||||
List<AncestorDirectory> ancestors)
|
||||
private BasicFileAttributes getAttributes(Path file, boolean canUseCached)
|
||||
throws IOException
|
||||
{
|
||||
// if attributes are cached then use them if possible
|
||||
BasicFileAttributes attrs = null;
|
||||
if ((depth > 0) &&
|
||||
if (canUseCached &&
|
||||
(file instanceof BasicFileAttributesHolder) &&
|
||||
(System.getSecurityManager() == null))
|
||||
{
|
||||
BasicFileAttributes cached = ((BasicFileAttributesHolder)file).get();
|
||||
if (cached != null && (!followLinks || !cached.isSymbolicLink()))
|
||||
attrs = cached;
|
||||
if (cached != null && (!followLinks || !cached.isSymbolicLink())) {
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
IOException exc = null;
|
||||
|
||||
// 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
|
||||
if (attrs == null) {
|
||||
try {
|
||||
try {
|
||||
attrs = Files.readAttributes(file, BasicFileAttributes.class, linkOptions);
|
||||
} catch (IOException x1) {
|
||||
if (followLinks) {
|
||||
try {
|
||||
attrs = Files.readAttributes(file,
|
||||
BasicFileAttributes.class,
|
||||
LinkOption.NOFOLLOW_LINKS);
|
||||
} catch (IOException x2) {
|
||||
exc = x2;
|
||||
}
|
||||
} else {
|
||||
exc = x1;
|
||||
}
|
||||
BasicFileAttributes attrs;
|
||||
try {
|
||||
attrs = Files.readAttributes(file, BasicFileAttributes.class, linkOptions);
|
||||
} catch (IOException ioe) {
|
||||
if (!followLinks)
|
||||
throw ioe;
|
||||
|
||||
// attempt to get attrmptes without following links
|
||||
attrs = Files.readAttributes(file,
|
||||
BasicFileAttributes.class,
|
||||
LinkOption.NOFOLLOW_LINKS);
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if walking into the given directory would result in a
|
||||
* file system loop/cycle.
|
||||
*/
|
||||
private boolean wouldLoop(Path dir, Object key) {
|
||||
// if this directory and ancestor has a file key then we compare
|
||||
// them; otherwise we use less efficient isSameFile test.
|
||||
for (DirectoryNode ancestor: stack) {
|
||||
Object ancestorKey = ancestor.key();
|
||||
if (key != null && ancestorKey != null) {
|
||||
if (key.equals(ancestorKey)) {
|
||||
// cycle detected
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (Files.isSameFile(dir, ancestor.directory())) {
|
||||
// cycle detected
|
||||
return true;
|
||||
}
|
||||
} catch (IOException | SecurityException x) {
|
||||
// ignore
|
||||
}
|
||||
} catch (SecurityException x) {
|
||||
// If access to starting file is denied then SecurityException
|
||||
// is thrown, otherwise the file is ignored.
|
||||
if (depth == 0)
|
||||
throw x;
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// unable to get attributes of file
|
||||
if (exc != null) {
|
||||
return visitor.visitFileFailed(file, exc);
|
||||
/**
|
||||
* 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
|
||||
BasicFileAttributes attrs;
|
||||
try {
|
||||
attrs = getAttributes(entry, canUseCached);
|
||||
} 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 visitor.visitFile(file, attrs);
|
||||
return new Event(EventType.ENTRY, entry, attrs);
|
||||
}
|
||||
|
||||
// check for cycles when following links
|
||||
if (followLinks) {
|
||||
Object key = attrs.fileKey();
|
||||
|
||||
// if this directory and ancestor has a file key then we compare
|
||||
// them; otherwise we use less efficient isSameFile test.
|
||||
for (AncestorDirectory ancestor: ancestors) {
|
||||
Object ancestorKey = ancestor.fileKey();
|
||||
if (key != null && ancestorKey != null) {
|
||||
if (key.equals(ancestorKey)) {
|
||||
// cycle detected
|
||||
return visitor.visitFileFailed(file,
|
||||
new FileSystemLoopException(file.toString()));
|
||||
}
|
||||
} else {
|
||||
boolean isSameFile = false;
|
||||
try {
|
||||
isSameFile = Files.isSameFile(file, ancestor.file());
|
||||
} catch (IOException x) {
|
||||
// ignore
|
||||
} catch (SecurityException x) {
|
||||
// ignore
|
||||
}
|
||||
if (isSameFile) {
|
||||
// cycle detected
|
||||
return visitor.visitFileFailed(file,
|
||||
new FileSystemLoopException(file.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ancestors.add(new AncestorDirectory(file, key));
|
||||
if (followLinks && wouldLoop(entry, attrs.fileKey())) {
|
||||
return new Event(EventType.ENTRY, entry,
|
||||
new FileSystemLoopException(entry.toString()));
|
||||
}
|
||||
|
||||
// visit directory
|
||||
// file is a directory, attempt to open it
|
||||
DirectoryStream<Path> stream = null;
|
||||
try {
|
||||
DirectoryStream<Path> stream = null;
|
||||
FileVisitResult result;
|
||||
stream = Files.newDirectoryStream(entry);
|
||||
} catch (IOException ioe) {
|
||||
return new Event(EventType.ENTRY, entry, ioe);
|
||||
} catch (SecurityException se) {
|
||||
if (ignoreSecurityException)
|
||||
return null;
|
||||
throw se;
|
||||
}
|
||||
|
||||
// open the directory
|
||||
try {
|
||||
stream = Files.newDirectoryStream(file);
|
||||
} catch (IOException x) {
|
||||
return visitor.visitFileFailed(file, x);
|
||||
} catch (SecurityException x) {
|
||||
// ignore, as per spec
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
// the exception notified to the postVisitDirectory method
|
||||
|
||||
/**
|
||||
* Start walking from the given file.
|
||||
*/
|
||||
Event walk(Path file) {
|
||||
if (closed)
|
||||
throw new IllegalStateException("Closed");
|
||||
|
||||
Event ev = visit(file,
|
||||
false, // ignoreSecurityException
|
||||
false); // canUseCached
|
||||
assert ev != null;
|
||||
return ev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next Event or {@code null} if there are no more events or
|
||||
* the walker is closed.
|
||||
*/
|
||||
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 {
|
||||
Path entry = null;
|
||||
IOException ioe = null;
|
||||
|
||||
// invoke preVisitDirectory and then visit each entry
|
||||
try {
|
||||
result = visitor.preVisitDirectory(file, attrs);
|
||||
if (result != FileVisitResult.CONTINUE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// get next entry in the directory
|
||||
if (!top.skipped()) {
|
||||
Iterator<Path> iterator = top.iterator();
|
||||
try {
|
||||
for (Path entry: stream) {
|
||||
result = walk(entry, depth+1, ancestors);
|
||||
|
||||
// returning null will cause NPE to be thrown
|
||||
if (result == null || result == FileVisitResult.TERMINATE)
|
||||
return result;
|
||||
|
||||
// skip remaining siblings in this directory
|
||||
if (result == FileVisitResult.SKIP_SIBLINGS)
|
||||
break;
|
||||
if (iterator.hasNext()) {
|
||||
entry = iterator.next();
|
||||
}
|
||||
} catch (DirectoryIteratorException e) {
|
||||
// IOException will be notified to postVisitDirectory
|
||||
ioe = e.getCause();
|
||||
} catch (DirectoryIteratorException x) {
|
||||
ioe = x.getCause();
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
|
||||
// no next entry so close and pop directory, creating corresponding event
|
||||
if (entry == null) {
|
||||
try {
|
||||
stream.close();
|
||||
top.stream().close();
|
||||
} catch (IOException e) {
|
||||
// IOException will be notified to postVisitDirectory
|
||||
if (ioe == null)
|
||||
if (ioe != null) {
|
||||
ioe = e;
|
||||
} else {
|
||||
ioe.addSuppressed(e);
|
||||
}
|
||||
}
|
||||
stack.pop();
|
||||
return new Event(EventType.END_DIRECTORY, top.directory(), ioe);
|
||||
}
|
||||
|
||||
// invoke postVisitDirectory last
|
||||
return visitor.postVisitDirectory(file, ioe);
|
||||
// visit the entry
|
||||
ev = visit(entry,
|
||||
true, // ignoreSecurityException
|
||||
true); // canUseCached
|
||||
|
||||
} finally {
|
||||
// remove key from trail if doing cycle detection
|
||||
if (followLinks) {
|
||||
ancestors.remove(ancestors.size()-1);
|
||||
}
|
||||
} while (ev == null);
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the directory node that is the current top of the stack so that
|
||||
* there are no more events for the directory (including no END_DIRECTORY)
|
||||
* event. This method is a no-op if the stack is empty or the walker is
|
||||
* closed.
|
||||
*/
|
||||
void pop() {
|
||||
if (!stack.isEmpty()) {
|
||||
DirectoryNode node = stack.pop();
|
||||
try {
|
||||
node.stream().close();
|
||||
} catch (IOException ignore) { }
|
||||
}
|
||||
}
|
||||
|
||||
private static class AncestorDirectory {
|
||||
private final Path dir;
|
||||
private final Object key;
|
||||
AncestorDirectory(Path dir, Object key) {
|
||||
this.dir = dir;
|
||||
this.key = key;
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
Path file() {
|
||||
return dir;
|
||||
}
|
||||
Object fileKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the walker is open.
|
||||
*/
|
||||
boolean isOpen() {
|
||||
return !closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes/pops all directories on the stack.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
if (!closed) {
|
||||
while (!stack.isEmpty()) {
|
||||
pop();
|
||||
}
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2589,7 +2589,60 @@ public final class Files {
|
||||
{
|
||||
if (maxDepth < 0)
|
||||
throw new IllegalArgumentException("'maxDepth' is negative");
|
||||
new FileTreeWalker(options, visitor, maxDepth).walk(start);
|
||||
|
||||
/**
|
||||
* Create a FileTreeWalker to walk the file tree, invoking the visitor
|
||||
* for each event.
|
||||
*/
|
||||
try (FileTreeWalker walker = new FileTreeWalker(options, maxDepth)) {
|
||||
FileTreeWalker.Event ev = walker.walk(start);
|
||||
do {
|
||||
FileVisitResult result;
|
||||
switch (ev.type()) {
|
||||
case ENTRY :
|
||||
IOException ioe = ev.ioeException();
|
||||
if (ioe == null) {
|
||||
assert ev.attributes() != null;
|
||||
result = visitor.visitFile(ev.file(), ev.attributes());
|
||||
} else {
|
||||
result = visitor.visitFileFailed(ev.file(), ioe);
|
||||
}
|
||||
break;
|
||||
|
||||
case START_DIRECTORY :
|
||||
result = visitor.preVisitDirectory(ev.file(), ev.attributes());
|
||||
|
||||
// if SKIP_SIBLINGS and SKIP_SUBTREE is returned then
|
||||
// there shouldn't be any more events for the current
|
||||
// directory.
|
||||
if (result == FileVisitResult.SKIP_SUBTREE ||
|
||||
result == FileVisitResult.SKIP_SIBLINGS)
|
||||
walker.pop();
|
||||
break;
|
||||
|
||||
case END_DIRECTORY :
|
||||
result = visitor.postVisitDirectory(ev.file(), ev.ioeException());
|
||||
|
||||
// SKIP_SIBLINGS is a no-op for postVisitDirectory
|
||||
if (result == FileVisitResult.SKIP_SIBLINGS)
|
||||
result = FileVisitResult.CONTINUE;
|
||||
break;
|
||||
|
||||
default :
|
||||
throw new AssertionError("Should not get here");
|
||||
}
|
||||
|
||||
if (Objects.requireNonNull(result) != FileVisitResult.CONTINUE) {
|
||||
if (result == FileVisitResult.TERMINATE) {
|
||||
break;
|
||||
} else if (result == FileVisitResult.SKIP_SIBLINGS) {
|
||||
walker.skipRemainingSiblings();
|
||||
}
|
||||
}
|
||||
ev = walker.next();
|
||||
} while (ev != null);
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
@ -32,9 +32,23 @@ import java.util.*;
|
||||
|
||||
public class CreateFileTree {
|
||||
|
||||
static final Random rand = new Random();
|
||||
private static final Random rand = new Random();
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
private static boolean supportsLinks(Path dir) {
|
||||
Path link = dir.resolve("testlink");
|
||||
Path target = dir.resolve("testtarget");
|
||||
try {
|
||||
Files.createSymbolicLink(link, target);
|
||||
Files.delete(link);
|
||||
return true;
|
||||
} catch (UnsupportedOperationException x) {
|
||||
return false;
|
||||
} catch (IOException x) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Path create() throws IOException {
|
||||
Path top = Files.createTempDirectory("tree");
|
||||
List<Path> dirs = new ArrayList<Path>();
|
||||
|
||||
@ -53,7 +67,6 @@ public class CreateFileTree {
|
||||
dirs.add(subdir);
|
||||
}
|
||||
}
|
||||
assert dirs.size() >= 2;
|
||||
|
||||
// create a few regular files in the file tree
|
||||
int files = dirs.size() * 3;
|
||||
@ -64,20 +77,26 @@ public class CreateFileTree {
|
||||
}
|
||||
|
||||
// create a few sym links in the file tree so as to create cycles
|
||||
int links = 1 + rand.nextInt(5);
|
||||
for (int i=0; i<links; i++) {
|
||||
int x = rand.nextInt(dirs.size());
|
||||
int y;
|
||||
do {
|
||||
y = rand.nextInt(dirs.size());
|
||||
} while (y != x);
|
||||
String name = "link" + (i+1);
|
||||
Path link = dirs.get(x).resolve(name);
|
||||
Path target = dirs.get(y);
|
||||
Files.createSymbolicLink(link, target);
|
||||
if (supportsLinks(top)) {
|
||||
int links = 1 + rand.nextInt(5);
|
||||
for (int i=0; i<links; i++) {
|
||||
int x = rand.nextInt(dirs.size());
|
||||
int y;
|
||||
do {
|
||||
y = rand.nextInt(dirs.size());
|
||||
} while (y != x);
|
||||
String name = "link" + (i+1);
|
||||
Path link = dirs.get(x).resolve(name);
|
||||
Path target = dirs.get(y);
|
||||
Files.createSymbolicLink(link, target);
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return top;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
Path top = create();
|
||||
System.out.println(top);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,18 +21,21 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Unit test for Files.walkFileTree to test maxDepth parameter
|
||||
* @compile MaxDepth.java CreateFileTree.java
|
||||
* @run main MaxDepth
|
||||
*/
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Unit test for Files.walkFileTree to test maxDepth parameter
|
||||
*/
|
||||
|
||||
public class MaxDepth {
|
||||
public static void main(String[] args) throws Exception {
|
||||
final Path top = Paths.get(args[0]);
|
||||
final Path top = CreateFileTree.create();
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
Set<FileVisitOption> opts = Collections.emptySet();
|
||||
|
||||
@ -21,15 +21,18 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Unit test for Files.walkFileTree to test SKIP_SIBLINGS return value
|
||||
* @compile SkipSiblings.java CreateFileTree.java
|
||||
* @run main SkipSiblings
|
||||
*/
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Unit test for Files.walkFileTree to test SKIP_SIBLINGS return value.
|
||||
*/
|
||||
|
||||
public class SkipSiblings {
|
||||
|
||||
static final Random rand = new Random();
|
||||
@ -52,7 +55,7 @@ public class SkipSiblings {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path dir = Paths.get(args[0]);
|
||||
Path dir = CreateFileTree.create();
|
||||
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
@ -74,7 +77,11 @@ public class SkipSiblings {
|
||||
if (x != null)
|
||||
throw new RuntimeException(x);
|
||||
check(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
if (rand.nextBoolean()) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
} else {
|
||||
return FileVisitResult.SKIP_SIBLINGS;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
85
jdk/test/java/nio/file/Files/walkFileTree/SkipSubtree.java
Normal file
85
jdk/test/java/nio/file/Files/walkFileTree/SkipSubtree.java
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Unit test for Files.walkFileTree to test SKIP_SUBTREE return value
|
||||
* @compile SkipSubtree.java CreateFileTree.java
|
||||
* @run main SkipSubtree
|
||||
*/
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
public class SkipSubtree {
|
||||
|
||||
static final Random rand = new Random();
|
||||
static final Set<Path> skipped = new HashSet<>();
|
||||
|
||||
// check if this path should have been skipped
|
||||
static void check(Path path) {
|
||||
do {
|
||||
if (skipped.contains(path))
|
||||
throw new RuntimeException(path + " should not have been visited");
|
||||
path = path.getParent();
|
||||
} while (path != null);
|
||||
}
|
||||
|
||||
// indicates if the subtree should be skipped
|
||||
static boolean skip(Path path) {
|
||||
if (rand.nextInt(3) == 0) {
|
||||
skipped.add(path);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path dir = CreateFileTree.create();
|
||||
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
|
||||
check(dir);
|
||||
if (skip(dir))
|
||||
return FileVisitResult.SKIP_SUBTREE;
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
|
||||
check(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException x) {
|
||||
if (x != null)
|
||||
throw new RuntimeException(x);
|
||||
check(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -21,15 +21,18 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Unit test for Files.walkFileTree to test TERMINATE return value
|
||||
* @compile TerminateWalk.java CreateFileTree.java
|
||||
* @run main TerminateWalk
|
||||
*/
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.*;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Unit test for Files.walkFileTree to test TERMINATE return value
|
||||
*/
|
||||
|
||||
public class TerminateWalk {
|
||||
|
||||
static final Random rand = new Random();
|
||||
@ -47,7 +50,7 @@ public class TerminateWalk {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Path dir = Paths.get(args[0]);
|
||||
Path dir = CreateFileTree.create();
|
||||
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
|
||||
@ -23,9 +23,9 @@
|
||||
|
||||
# @test
|
||||
# @bug 4313887 6907737
|
||||
# @summary Unit test for walkFileTree method
|
||||
# @build CreateFileTree PrintFileTree SkipSiblings TerminateWalk MaxDepth
|
||||
# @run shell walk_file_tree.sh
|
||||
# @summary Tests that walkFileTree is consistent with the native find program
|
||||
# @build CreateFileTree PrintFileTree
|
||||
# @run shell find.sh
|
||||
|
||||
# if TESTJAVA isn't set then we assume an interactive run.
|
||||
|
||||
@ -76,18 +76,6 @@ if [ $? != 0 ];
|
||||
if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
|
||||
fi
|
||||
|
||||
# test SKIP_SIBLINGS
|
||||
$JAVA ${TESTVMOPTS} SkipSiblings "$ROOT"
|
||||
if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
|
||||
|
||||
# test TERMINATE
|
||||
$JAVA ${TESTVMOPTS} TerminateWalk "$ROOT"
|
||||
if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
|
||||
|
||||
# test maxDepth
|
||||
$JAVA ${TESTVMOPTS} MaxDepth "$ROOT"
|
||||
if [ $? != 0 ]; then failures=`expr $failures + 1`; fi
|
||||
|
||||
# clean-up
|
||||
rm -r "$ROOT"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user