Search code examples
java-7nio2

Java 7 walkFileTree calling visitFile on directories


Say I have the following directory structure

/root/dir
/root/dir/file1.txt
/root/dir/subdir
/root/dir/subdir/file2.txt

And let's say I'll be using the following visitor:

class MyFileVisitor extends SimpleFileVisitor<Path> {

  @Override
  public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs)
  throws IOException {
    if(Files.isDirectory(file)) {
      throw new IllegalStateException("WAT!? Visiting directory: "+file.toAbsolutePath().toString());
    }
    System.out.println("Visiting file: "+file.toAbsolutePath().toString());
    return super.visitFile(file, attrs);
  }

If we use the simple overload of walkFileTree:

Files.walkFileTree(Paths.get("/root/dir"), new MyFileVisitor());  

Everything goes according to plan and we see the following output:

Visiting file: /root/dir/file1.txt
Visiting file: /root/dir/subdir/file2.txt

But when we try setting the max depth, things start to break down:

Files.walkFileTree(Paths.get("/root/dir"), EnumSet.noneOf(FileVisitOption.class), 1, new MyFileVisitor());

Output:

Visiting file: /root/dir/file1.txt

java.lang.IllegalStateException: WAT!? Visting directory: /root/dir/subdir

I'm pretty sure this is a bug, but I wanted to ask the community first: Is there something I'm missing and this is actually expected behavior? Thanks for confirming!

Details:

java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

Solution

  • I played around a little bit with your code and added a couple of lines:

    /*
     * (non-Javadoc)
     * 
     * @see java.nio.file.SimpleFileVisitor#preVisitDirectory(java.lang.Object,
     * java.nio.file.attribute.BasicFileAttributes)
     */
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
            throws IOException {
        System.out.println(Thread.currentThread().getStackTrace()[1] + " "
                + dir);
        return super.preVisitDirectory(dir, attrs);
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see java.nio.file.SimpleFileVisitor#postVisitDirectory(java.lang.Object,
     * java.io.IOException)
     */
    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc)
            throws IOException {
        System.out.println(Thread.currentThread().getStackTrace()[1] + " "
                + dir);
        return super.postVisitDirectory(dir, exc);
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see java.nio.file.SimpleFileVisitor#visitFileFailed(java.lang.Object,
     * java.io.IOException)
     */
    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc)
            throws IOException {
        System.out.println(Thread.currentThread().getStackTrace()[1] + " "
                + file);
        return super.visitFileFailed(file, exc);
    }
    

    just no-brainers to see what's going on.

    What you call a bug happens here java.nio.file.FileTreeWalker:134:

        // at maximum depth or file is not a directory
        if (depth >= maxDepth || !attrs.isDirectory()) {
            return visitor.visitFile(file, attrs);
        }
    

    Finally, what I think about it:

    1.: subdir is part of depth 1
    2.: Without the visitFile for deepest directories, you wouldn't even recognize they are there.

    So I think that's regular behaviour.

    By the way, you could use the attrs in your override of:

    @Override
    public FileVisitResult visitFile(final Path file,
            final BasicFileAttributes attrs) throws IOException {
        if (attrs.isDirectory()) {
            throw new IllegalStateException("WAT!? Visiting directory: "
                    + file.toAbsolutePath().toString());
        }
        System.out
                .println("Visiting file: " + file.toAbsolutePath().toString());
        return super.visitFile(file, attrs);
    }