2012-01-19 25 views
5

Quiero usar el nuevo java.nio.file.Files.walkFileTree en Scala. Y yo estaba aun exitosa:Cómo usar java.nio.file.Files.walkFileTree en Scala

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) 

Pero mientras que este código funciona bien me parece un poco como llevar paradigmas Java en Scala. Entonces, una pregunta para los verdaderos Gurús de Scala: ¿hay algo que pueda mejorar o es solo eso?

Respuesta

6

Un visitante es realmente un foreach sin el beneficio de las funciones, así que hagamos un foreach. El método es estático, sino que toma como primer argumento una Path, por lo que vamos a enriquecer Path con un método foreach, que se realiza con algo como esto:

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

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

Y todo lo demás está dentro de la clase TraversePath, que parece algo como esto:

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

esto es suficiente para que usted escriba esto:

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

Por supuesto, en realidad no hacer nada, así que vamos a conseguir que haga algo:

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) 
    } 
} 

Allí, ahora que la línea va a hacer lo mismo que hizo su código! Sin embargo, podemos mejorarlo aún más. Sucede que foreach es el único método requerido de Traversable, por lo que podemos extender esa clase y obtener todos los métodos de una colección de Scala.

El único problema es que una función Traversable.foreach toma solo un argumento, y aquí estamos tomando dos. Sin embargo, podemos cambiarlo para recibir una tupla. Aquí está el código completo:

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) 
} 

de responsabilidad: He probado ninguno de este código, porque no tengo instalado Java 7. Probablemente hay algunos errores.

2

Puede hacer que su código sea un poco más bonito, pero al final del día todavía se parecería al patrón de visitante antiguo.

2

Aquí está la escritura hecha compilables de Daniel:

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[U](f: ((Path, BasicFileAttributes)) => U) { 
    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) 
    } 
} 
val projectHome = new TraversePath(Paths.get(".")) 

projectHome foreach { 
    // use case to seamlessly deconstruct the tuple 
    case (file:Path, attr:BasicFileAttributes) => if (!file.toString.contains(".svn")) println(file) 
} 
2

Tomando como fundamento Daniel's answer, he trabajado un poco para hacer accesible con Path implícitos convenientes, ya que se utilizan en las colecciones. Tenga en cuenta que no todas las funciones están incluidas.

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

    /** 
    * foreach that takes FileVisitResult instead of Unit 
    */ 
    def foreach2(f: (Path, BasicFileAttributes) => FileVisitResult) { 
     Files.walkFileTree(path, new SimpleFileVisitor[Path] { 
      override def visitFile(file: Path, attrs: BasicFileAttributes) = f(file, attrs) 
     }) 
    } 

    def foldLeft[T](t: T)(f: (T, Path) => T) = { 
     var current = t 
     foreach((p, _) => current = f(current, p)) 
     current 
    } 

    def forall(f: Path => Boolean) = { 
     var ret = true 
     foreach2((p, _) => 
      if (!f(path)) { 
       ret = false 
       FileVisitResult.TERMINATE 
      } 
      else 
       FileVisitResult.CONTINUE 
     ) 
     ret 
    } 

    def exists(f: Path => Boolean) = { 
     var ret = false 
     foreach2((p, _) => 
      if (f(path)) { 
       ret = true 
       FileVisitResult.TERMINATE 
      } 
      else 
       FileVisitResult.CONTINUE 
     ) 
    } 

    /** 
    * Directly modifies the underlying path. 
    */ 
    def mapReal(f: Path => Path) = foreach((p, _) => Files.move(p, f(p))) 

    /** 
    * @param f map function 
    * @return a left-folded list with the map function applied to each element 
    */ 
    def map(f: Path => Path) = foldLeft(Nil: List[Path]) { 
     case (xs, p) => xs ::: f(p) :: Nil 
    } 

    def find(f: Path => Boolean) = { 
     var k = None: Option[Path] 
     foreach2((p, _) => 
      if (f(p)) { 
       k = Some(p) 
       FileVisitResult.TERMINATE 
      } else FileVisitResult.CONTINUE 
     ) 
     k 
    } 
} 

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

La API java.nio es extremadamente potente y es, en mi humilde opinión, muy bastando para su uso con Scala. Con estos implícitos (y más, si desea escribir algunas funciones), es muy simple realizar tareas aún más difíciles.

usted podría utilizar esto ahora escribiendo algo como esto:

val path1 = Paths.get(sys.props("user.home"), "workspace") 

val path2 = Paths.get(sys.props("user.home"), "workspace2") 

val list1 = path1.foldLeft(Nil: List[Path]) { 
    (xs, p) => xs ::: path1.relativize(p) :: Nil 
} 
val list2 = path2.foldLeft(Nil: List[Path]) { 
    (xs, p) => xs ::: path2.relativize(p) :: Nil 
} 
(list1 diff list2) foreach println 

Saludos,
Danyel

0

FIles.walkFileTree ejemplo para comparar dos directorios/sincronizar dos directorios de archivo diferencia

private static void compareDirectories(String srcPath, String destPath) throws IOException, InterruptedException { 
    System.out.println("sync. started...."); 
    final Path mainDir = Paths.get(srcPath); 
    final Path otherDir = Paths.get(destPath); 

    // Walk thru mainDir directory 
    Files.walkFileTree(mainDir, new FileVisitor<Path>() { 
     @Override 
     public FileVisitResult preVisitDirectory(Path path, 
       BasicFileAttributes atts) throws IOException { 
      return visitFile(path, atts); 
     } 

     @Override 
     public FileVisitResult visitFile(Path path, BasicFileAttributes mainAtts) 
       throws IOException { 
      // I've seen two implementations on windows and MacOSX. One has passed the relative path, one the absolute path. 
      // This works in both cases 
      Path relativePath = mainDir.relativize(mainDir.resolve(path)); 
      File tmpFile = new File(otherDir+"/"+relativePath); 

       if(tmpFile.exists()) { 
        BasicFileAttributes otherAtts = Files.readAttributes(otherDir.resolve(relativePath), BasicFileAttributes.class); 
        // Do your comparison logic here: we are skipping directories as all directories are traversed automatically 
        if(!new File(path.toString()).isDirectory()) { 
                //write your logic for comparing files 
         compareEntries(mainDir, otherDir, relativePath, mainAtts, otherAtts); 
        } 
        else { 
         File src = new File(path.toString()); 

                //write your logic here for comparing directories 
                compareDirectories(src,tmpFile.toPath().toString()+"/"+s); 
        } 
       } 
       else { 
              //this function will copy missing files in destPath from srcPath recursive function till depth of directory structure 
        copyFolderOrFiles(new File(path.toString()), tmpFile); 
       } 
      return FileVisitResult.CONTINUE; 
     } 

     @Override 
     public FileVisitResult postVisitDirectory(Path path, 
       IOException exc) throws IOException { 
      return FileVisitResult.CONTINUE; 
     } 

     @Override 
     public FileVisitResult visitFileFailed(Path path, IOException exc) 
       throws IOException { 
      exc.printStackTrace(); 
      // If the root directory has failed it makes no sense to continue 
      return (path.equals(mainDir))? FileVisitResult.TERMINATE:FileVisitResult.CONTINUE; 
     } 
    }); 
} 
0

Extendiéndose en las ideas de las otras publicaciones. Me gusta la solución en la que podemos hacer coincidir las clases de casos. El siguiente código simplemente devuelve una colección de cadenas para los diferentes eventos a los que se habría llamado al visitante.

FileWalker(java.nio.file.Paths.get(" your dir ")).map({ 
    case PreVisitDirectory(dir, atts) => s"about to visit dir ${dir}" 
    case PostVisitDirectory(dir, exc) => s"have visited dir ${dir}" 
    case VisitFile(file, attrs) => s"visiting file ${file}" 
    case VisitFileFailed(file, exc) => s"failed to visit ${file}" 
}) 

La implementación de Filewalker es:

import java.io.IOException 
import java.nio.file.{FileVisitResult, Files, Path, SimpleFileVisitor} 
import java.nio.file.attribute.BasicFileAttributes 

trait FileVisitEvent 
case class PreVisitDirectory(path: Path, atts: BasicFileAttributes) extends FileVisitEvent 
case class PostVisitDirectory(dir: Path, exc: IOException) extends FileVisitEvent 
case class VisitFile(file: Path, attrs: BasicFileAttributes) extends FileVisitEvent 
case class VisitFileFailed(file: Path, exc: IOException) extends FileVisitEvent 

/** 
    * Scala style walker for a directory tree 
    * 
    * Is a treversable over the tree which traverses different event types extending {{FileVisitEvent}} 
    * 
    * @param from 
    */ 
class FileWalker(from: Path) extends Traversable[FileVisitEvent] { 
    // just to simplify error handling 
    def wrapper(x: => Unit): FileVisitResult = try { 
    x 
    FileVisitResult.CONTINUE 
    } 
    catch { 
    case _ : Throwable => FileVisitResult.TERMINATE 
    } 

    override def foreach[U](f: (FileVisitEvent) => U): Unit = { 
    Files.walkFileTree(from, new SimpleFileVisitor[Path] { 
     override def preVisitDirectory(dir: Path, atts: BasicFileAttributes): FileVisitResult = 
     wrapper(f(PreVisitDirectory(dir, atts))) 

     override def postVisitDirectory(dir: Path, exc: IOException): FileVisitResult = 
     wrapper(f(PostVisitDirectory(dir, exc))) 

     override def visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult = 
     wrapper(f(VisitFile(file, attrs))) 

     override def visitFileFailed(file: Path, exc: IOException): FileVisitResult = 
     wrapper(f(VisitFileFailed(file, exc))) 
    }) 
    } 
} 

object FileWalker { 
    def apply(from : Path) = new FileWalker(from) 
} 
Cuestiones relacionadas