2012-01-19 11 views
5

Desidero utilizzare il nuovo java.nio.file.Files.walkFileTree in Scala. E ho persino avuto successo:Come utilizzare java.nio.file.Files.walkFileTree in 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) 

Ma mentre questo codice funziona bene, mi sembra un po 'come portare paradigmi Java in Scala. Quindi una domanda ai veri Guru di Scala: c'è qualcosa che potrei migliorare o è solo questo?

risposta

6

Un visitatore è davvero un foreach senza il vantaggio di funzioni, quindi facciamo un foreach. Il metodo è statico, ma ci vuole come primo argomento un Path, quindi dovremo arricchire Path con un metodo foreach, che viene fatto con qualcosa di simile:

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

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

E tutto il resto è all'interno della classe TraversePath, che sembra un po 'come questo:

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

questo è sufficiente per voi a scrivere questo:

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

Naturalmente, non sarà in realtà fare nulla, quindi cerchiamo di arrivare a fare qualcosa:

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

Ecco, ora che la linea farà la stessa cosa il tuo codice ha fatto! Tuttavia, possiamo migliorarlo ulteriormente. Succede che l'foreach è l'unico metodo richiesto da Traversable, quindi possiamo estendere quella classe e ottenere tutti i metodi di una collezione Scala!

L'unico problema è che una funzione Traversable.foreach accetta un solo argomento e qui ne stiamo prendendo due. Possiamo cambiarlo in ricevere una tupla, però. Ecco il codice 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) 
} 

Disclaimer: Ho testato niente di tutto questo codice, perché non ho Java 7 installato. Ci sono probabilmente alcuni bug.

2

Si potrebbe rendere il codice un po 'più bello, ma alla fine della giornata sembrerebbe ancora il semplice vecchio modello di visitatore.

2

Ecco di Daniel sceneggiatura fatta compilabile:

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

Prendendo Daniel's answer come fondamento, ho lavorato un po 'per rendere accessibili Path con impliciti conveniente, in quanto si è abituati in collezioni. Si noti che non tutte le funzioni sono incluse.

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) 

L'API java.nio è estremamente potente ed è, secondo me, molto bastando per l'uso con Scala. Con questi impliciti (e di più, se vuoi scrivere alcune funzioni), è molto semplice eseguire compiti ancora più difficili.

si potrebbe usare questo ora scrivendo qualcosa di simile:

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 

saluti,
Danyel

0

FIles.walkFileTree esempio per confrontare due directory/sincronizzazione di due cartelle per la differenza di file

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

Estendere le idee degli altri post. Mi piace la soluzione in cui possiamo confrontare le classi dei casi. Il seguente codice restituisce solo una raccolta di stringhe per i diversi eventi che il visitatore sarebbe stato chiamato.

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

L'attuazione di Filewalker è:

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