Scala: How to search a directory tree with SimpleFileVisitor and Files.walkFileTree

As a brief note to self, if you ever want to write some code using Scala that recursively descends through a directory tree, here’s a solution that uses the Java SimpleFileVisitor and Files.walkFileTree method. First, here’s a skeleton class for the SimpleFileVisitor part of the solution:

import java.io._
import java.nio.file._
import java.nio.file.attribute._
import java.nio.file.FileVisitResult._
import java.nio.file.FileVisitOption._
import scala.io.Source

class Finder (filePattern: String, searchPattern: String)
extends SimpleFileVisitor[Path] {

    var pathMatcher: PathMatcher = null
    var numMatches = 0

    // note: "glob:" is part of the syntax
    // https://docs.oracle.com/javase/8/docs/api/java/nio/file/FileSystem.html#getPathMatcher-java.lang.String-
    pathMatcher = FileSystems.getDefault()
        .getPathMatcher("glob:" + filePattern)

    // add code here to work on files
    override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = {
        find(file)
        return CONTINUE
    }

    // add code here to work on directories
    override def preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult = {
        println(s"DIR: ${dir.toAbsolutePath()}")
        return CONTINUE
    }

    override def visitFileFailed(file: Path, ioe: IOException): FileVisitResult = {
        System.err.println(ioe)
        return CONTINUE
    }

    // compares the glob filePattern against the file or directory name
    def find(file: Path): Unit = {
        val name: Path = file.getFileName()
        // if the `name` matches what you’re looking for, do
        // what you want here with the file
        if (name != null && pathMatcher.matches(name)) {
            numMatches += 1
            val canonFilename = file.toAbsolutePath.toString
            println(s"match: $canonFilename")
        }
    }

    def done() = {
        println(s"Searched $numMatches $filePattern files.\n")
    }
    
}

In that class, just implement the visitFile, preVisitDirectory, and visitFileFailed methods as desired.

Next, here’s a Scala object that shows how to use that SimpleFileVisitor

import java.nio.file._

object FileFinder extends App {
    val finder = new Finder(
        "*.scala",
        "App"
    )
    Files.walkFileTree(
        Paths.get("/Users/al/Projects/Scala"),
        finder
    )
    finder.done()    
}

The combination of that class and object can be read as, “Search the /Users/al/Projects/Scala directory tree for all Scala files that contain the string App.” Notice how the Finder — which extends SimpleFileVisitor — is passed into the walkFileTree method of the Files class.

Summary

There’s more that can be said about how this code works, but for today, if you need a Java or Scala solution that lets you search a directory structure for files and directories, I hope this skeleton code is helpful.