- [Tim] All right, we're ready to start
learning how to walk a file tree.
Now, again, when I talk about walking a file tree,
what I mean is that we're going to visit
every file and directory that can be reached
from a starting or root directory for that tree.
For example, if we started in that Dir2
in the Dir2 folder in our project,
we'll visit the following files and directories,
but again, not necessarily in this order.
So within the Dir2 folder,
then within the Dir2 folder,
we've got file1, file2 and file3.
Two dot txt files and one dat file.
But then, also, we have the Dir3 folder.
We go into that subfolder as well
and we also have the two files in there,
file1.txt and file2.txt.
So, in this scenario, because we're going to be studying
on the Dir2 folder,
well, that's considered to be the root of its file tree.
So, we'll never going to move up a level
from the root directory.
We're only going to be moving down levels.
But you might be wondering
why would you actually want to do this.
Well, one use case might be
when the application has to find a file.
But another use case could be that
the application is searching all the files
in the directory and below for a specific stream.
Now, I mentioned in the previous video that,
when you want to copy a directory,
you have to walk the file tree
if you want to copy everything below it as well.
Otherwise, any of the files directly
within the directory itself will be copied.
And, as you actually saw earlier, in an earlier video,
when we're using the directory stream ticket contents
of a directory, only the direct descendants
are returned by default.
Now, there are other use cases,
but those are a few common ones that you might come across.
Right, so I just want to talk briefly
about the FileVisitor interface,
as you can see on the screen there now.
This is what you need to use to walk the directory tree.
So, using the methods in FileVisitor,
you can run code at each stage of its traversal process.
And here are the methods that you'll want to implement.
Firstly, we've got the preVisitDirectory method,
and this method accepts a reference to the directory,
and the BasicFileAttributes instance for the directory.
And it's actually called before entries
in the directory are visited.
We've also got a postVisitDirectory method,
and that accepts a reference to the directory
and also an exception object when necessary.
It's actually called after entries in the directory,
and all its descendants, have been visited.
Now, the exception parameter will be set
when an exception happens during the traversal
of the entries and descendants.
Now, there's a way to skip files as you're traversing.
So, not every file has to have been visited
for this method to be called.
Every file as to be visited or explicitly skipped.
Of course, if an exception is thrown and not handled,
the traversal will prematurely end
and postVisitDirectory will be called.
Next method, visitFile, probably the method
that you'll care about most.
So, this method accepts a reference to the file
and a BasicFileAttributes instance.
So, this is where you run the code that
will operate on the file.
And it's actually only called for files.
And lastly, visitFileFailed.
This method is called when a file can't be accessed
for some reason.
The exception that's thrown is passed to this method.
You can then decide what to do with it.
You can throw the method, print it,
or anything else that makes sense for the application
and operation being performed.
And this can be called for both files and directories.
So as with most interfaces like this,
the JDK's got an implementation that you can use
called SimpleFileVisitor.
You can extend this class and override
just the methods that you require.
So let's actually go ahead and create a class
that extends the SimpleFileVisitor
and traverses the file tree
with Dir2 as its root.
And what we'll do is we'll print
the name of every file and directory it visits.
So, back to IntelliJ.
Let's go ahead and do that.
So we'll create a new file.
A new Java class.
And we'll call it PrintNames.
Okay, we're going to extends
SimpleFileVisitor Path
accept that import.
And we're just going to override the visitFile method.
Just the visitFile method.
We're going to change this.
Instead of returning this super,
let's actually stop by printing out
file dot toAbsolutePath.
So I'll print out the file name.
Well, that should return Continue,
which is FileVisitResult dot Continue.
So, as you can see here,
that I've implemented the class PrintNames,
extended SimpleFileVisitor,
and I've overriden the visitFile method.
Often, it's a good idea to override
the PostVisit directory
as well as the previous directory methods as well
so that you can print the name of the directories.
So let's go ahead and do that as well.
And going to override preVisitDirectory,
and we'll give you something very similar with that code.
Let's just copy that code
and paste it there instead of the return statement.
But instead of file, of course,
that the argument is dir because it's a directory.
We changed it as well.
Right, so your method is specifying path
as the top of SimpleFileVisitor.
Now, this will also be the path of reference
passed to the methods.
Now, in this case, as you can see from
the visitFile declaration,
a path is passed as the first parameter.
When we were describing the method,
I said that it accepts a reference to a file.
Now, we couldn't be more specific than that
because the parameter top has to match
the top you specify for the FileVisitor.
Now, inside the visitFile method,
you can see that I'm putting
the absolute path for the file,
and then, we're returning the FileVisitResult
dot Continue constant.
Now, you can also return skip underscore siblings,
which means the traversal should skip
all the other entries in the same directory as the file.
And skip underscore subtree,
which you can use when you want to skip a directory
as well as terminate, which means
that you want to stop the traversal.
For example, you'd return terminate
when you found the file that you're searching for
so that the traversal's actually stopped.
Now, it would only make sense to return underscore subtree
from the PreVisit directory method.
Returning it from the other methods
is equivalent to Continue.
Also, if you return skip underscore siblings
from the PreVisit directory,
then the directory itself is also skipped,
and the postVisit directory method is never called
for that directory.
So basically, that's another way of saying
that you want to completely ignore the directory
and all its descendants and siblings.
All right, you can say that I've got
the preVisit directory method added as well
to print the absolute path to the directory or the folder.
Once again, you can say we're using a path.
In this case, it's not to a file,
that was to a directory,
and all we're doing is printing out
the absolute path folder directory.
All right, lastly, let's add the visitFile file method
or override it.
Now, if we don't implement this, and an error occurs,
then an IOException will actually be thrown.
But, instead, you can notify the user
or handle the error
and then return Continue
if you want a traversal to actually continue on
in the case of an error.
So, to do that, we're going to print out a message here,
Error accessing file
plus file dot toAbsolutePath
plus space plus e dot getMessage,
then we can return Continue.
All right, so at this point now,
with our three overridden methods.
We've got a working FileVisitor.
So now, what we need to do is start the traversal,
and we're going to do so by using the
files dot walkFileTree method.
So let's go back to our main class
and add some code to do that.
So I'll print out a message first
to indicate what we're doing.
Walking Tree for Dir2.
We'll start with a path,
Path dir2Path is equal to
FileSystems dot getDefault
dot getPath,
let's go to the fileTree.
To control it spelling correctly,
let's actually use the separator.
And I said I wasn't going to,
but let's just use that to make it easier,
and it works on both operating systems
plus Dir2.
Then we have to try here,
so it's going to be a try.
We're going to do Files dot walkFileTree
Dir2Path, being the path,
then we're going to draw new,
PrintNames being that class that we just created.
Then we need to catch an exception there.
It's going to be an IOException,
and we'll just do a printout if we get an error,
e dot getMessage.
All right, so we created a path to a Dir2 folder
that we want to walk.
But then calling, as you can see here on line 59,
the walkFileTree method,
and you can see that it's accepting, in this case,
passing two arguments to dir2Path being the name of the path
and PrintNames being the class we're using
that actually extends the SimpleFileVisitor.
And we'll just run that to make sure it works.
And you can see the Walking Tree for Dir2,
we got all the files.
We got Dir2, Dir3,
we got our two files for Dir3 in the Dir3 folder,
and also the Dir2, is we've got
file1.txt, file2.txt, and file3.dat.
So clearly, that's worked okay.
Now, there's actually a second version
of the walkFileTree that accepts four parameters.
Now, the additional two parameters
are laid to specify the number of levels to a traverse
and to pass a set of fileVisit options.
Right now, they're the only option available
is whether to follow symbolic links.
Now, when you're printing out the names,
it doesn't matter whether we use
the preVisit directory method to print directory names
or the postVisit directory method.
Now, depending on what you want to do,
it can matter, though.
For example, if you wanted to copy a file tree,
then you'd want to handle the directory
before you handle those entries
because the copy of the directory will have to exist
before you can copy entries into it.
So, in that scenario, you would actually
handle the directory using the preVisit directory method.
On the other hand, if you wanted to traverse a file tree
to delete it, then you'd actually want to handle
the directory in the postVisit directory method
because you have to delete all the entries
in the directory and all its descendants
before you can delete the directory.
So whether to handle directories, pre or post,
will actually depend on what makes sense
from the top of processing,
that you want to do.
All right, just one important note.
Here, you can't make any assumptions about the order
in which files and directories are visited.
Say, you shouldn't be making any assumptions
which you carry in that scenario.
All right, so let's finish the video here,
and we'll continue on in the next video
and create a copy files method
and look at how to stack copying files.
See you in the next video.