Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use java.nio.file.Files.walkFileTree in Scala

Tags:

scala

java-7

I want to use the new java.nio.file.Files.walkFileTree in Scala. And I was even successful:

class Visitor
   extends
      java.nio.file.SimpleFileVisitor [java.nio.file.Path]
   {
   override def visitFile(
      File : java.nio.file.Path,
      Attrs : java.nio.file.attribute.BasicFileAttributes) : java.nio.file.FileVisitResult =
   {
      if (! File.toString.contains(".svn"))
      {
         System.out.println(File);
      } // if

      java.nio.file.FileVisitResult.CONTINUE;
   } // visitFile
} // Visitor

java.nio.file.Files.walkFileTree (Project_Home, new Visitor)

But while this code works fine I feels a bit like carrying Java paradigms into Scala. So a question to the true Scala Gurus: Is there anything I could improve or is this just it?

like image 539
Martin Avatar asked Jan 19 '12 08:01

Martin


2 Answers

A Visitor is really a foreach without the benefit of functions, so let's make a foreach. The method is static, but it takes as first argument a Path, so we'll enrich Path with a foreach method, which is done with something like this:

import java.nio.file._
import java.nio.file.attribute.BasicFileAttributes

implicit def fromNioPath(path: Path): TraverseFiles = new TraversePath(path)

And everything else is inside the TraversePath class, which looks somewhat like this:

class TraversePath(path: Path) {
  def foreach(f: (Path, BasicFileAttributes) => Unit) {
    // ...
  }
}

This is enough for you to write this:

ProjectHome foreach ((file, _) => if (!file.toString.contains(".svn")) println(File))

Of course, it won't actually do anything, so let's get it to do something:

class TraversePath(path: Path) {
  def foreach(f: (Path, BasicFileAttributes) => Unit) {
    class Visitor extends SimpleFileVisitor[Path] {
      override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = try { 
        f(file, attrs)
        FileVisitResult.CONTINUE
      } catch { 
        case _ => FileVisitResult.TERMINATE
      }
    }
    Files.walkFileTree(path, new Visitor)
  }
}

There, now that line will do the same thing as your code did! However, we can improve it further. It happens that foreach is the only method required of Traversable, so we can extend that class, and get all the methods of a Scala collection!

The only problem is that a Traversable.foreach function takes only one argument, and here we are taking two. We can change it into receive a tuple, though. Here's the full code:

import java.nio.file._
import java.nio.file.attribute.BasicFileAttributes
import scala.collection.Traversable

// Make it extend Traversable
class TraversePath(path: Path) extends Traversable[(Path, BasicFileAttributes)] {

  // Make foreach receive a function from Tuple2 to Unit
  def foreach(f: ((Path, BasicFileAttributes)) => Unit) {
    class Visitor extends SimpleFileVisitor[Path] {
      override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = try {
        // Pass a tuple to f
        f(file -> attrs)
        FileVisitResult.CONTINUE
      } catch { 
        case _ => FileVisitResult.TERMINATE
      }
    }
    Files.walkFileTree(path, new Visitor)
  }
}

ProjectHome foreach {
  // use case to seamlessly deconstruct the tuple
  case (file, _) => if (!file.toString.contains(".svn")) println(File)
}

Disclaimer: I have tested none of this code, because I don't have Java 7 installed. There are probably some bugs.

like image 143
Daniel C. Sobral Avatar answered Sep 28 '22 02:09

Daniel C. Sobral


You could make your code a bit more pretty, but at the end of the day it would still look like the plain old visitor pattern.

like image 44
agilesteel Avatar answered Sep 28 '22 01:09

agilesteel