All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.specs2.io.FileSystem.scala Maven / Gradle / Ivy

There is a newer version: 3.7
Show newest version
package org.specs2
package io

import java.io._
import java.net.{JarURLConnection, URL}
import java.util.zip._
import Paths._
import java.util.regex.Pattern.{quote, compile}
import java.util.regex.Matcher.quoteReplacement

/**
 * The FileSystem trait abstracts file system operations to allow easier mocking of file system related functionalities.
 * 

* It mixes the `FileReader` and `FileWriter` traits to provide easy read/write operations. */ private[specs2] trait FileSystem extends org.specs2.io.FileReader with org.specs2.io.FileWriter { /** * @param path glob expression, for example: `./dir/**/*.xml` * @return the list of paths represented by the "glob" definition `path` */ def filePaths(basePath: String = ".", path: String = "*", verbose: Boolean = false): Seq[String] = { val found = recurse(new File(basePath)) if (verbose) found.foreach { f => println("found file: "+f) } filterFiles(found, path, verbose) } def filterFiles(found: Seq[File], path: String, verbose: Boolean): Seq[String] = { val pattern = globToPattern(path) + (if (isDir(path)) "/*.*" else "") if (verbose) println("\nThe pattern used to match files is: "+pattern) found.collect { case f if fileMatchesPattern(f, pattern, verbose) => f.getPath }.toSeq } private def isVersionFile(f: File) = Seq(".svn", ".cvs").exists(f.getPath.contains(_)) private def fileMatchesPattern(f: File, pattern: String, verbose: Boolean = false) = { val filePath = f.getPath .replaceFirst(".:", "") // remove any letter drive on Windows .unixize if (verbose) println(filePath+" matches pattern: "+(filePath matches pattern)) filePath matches pattern } /** * @param file start file * @return a Stream with all the recursively accessible files (not directories) */ private def recurse(file: File): Stream[File] = { import Stream._ cons(file, if (file.listFiles == null) empty else file.listFiles.toStream.filterNot(isVersionFile).flatMap(recurse)).filter(_.isFile) } /** * @return the regular expression equivalent to a glob pattern (see the specs for Fragments) */ def globToPattern(glob: String): String = { val star = "" val authorizedNamePattern = "[^\\/\\?<>\\|\\" + star + ":\"]" + star glob.replace("\\", "/") .replace(".", "\\.") .replace("**/", "(" + authorizedNamePattern + "/)" + star) .replace("*", authorizedNamePattern) .replace(star, "*") } /** * @return true if the File represented by this path is a directory */ def isDir(path: String) = isDirectory(path) /** * creates a directory for a given path */ def createDir(path: String) = (new File(path)).mkdirs /** * deletes the directory and all directory content at the specified path and return the parent path of that directory */ def removeDir(path: String): String = { val dir = new File(path) if (dir.isDirectory) { if (dir.listFiles == null || dir.listFiles.isEmpty) dir.delete else { dir.listFiles.foreach { file => if (file.isFile) file.delete else removeDir(file.getPath) } dir.delete } } dir.getParent } /** @return true if the file can be read */ def canRead(path: String) = path != null && new File(path).canRead /** @return true if the file can be written */ def canWrite(path: String) = path != null && new File(path).canWrite /** @return true if the file is absolute */ def isAbsolute(path: String) = path != null && new File(path).isAbsolute /** @return true if the file is a file */ def isFile(path: String) = path != null && new File(path).isFile /** @return true if the file is a directory */ def isDirectory(path: String) = path != null && new File(path).isDirectory /** @return true if the file is hidden */ def isHidden(path: String) = path != null && new File(path).isHidden /** @return the file name */ def getName(path: String) = new File(path).getName /** @return the file absolute path */ def getAbsolutePath(path: String) = new File(path).getAbsolutePath /** @return the file canonical path */ def getCanonicalPath(path: String) = new File(path).getCanonicalPath /** @return the file parent path */ def getParent(path: String) = new File(path).getParent /** @return the files of that directory */ def listFiles(path: String): List[String] = if (new File(path).list == null) List() else new File(path).list.toList /** * copy the content of a directory to another. * @param path path of the directory to copy * @param dest destination directory path */ def copyDir(src: String, dest: String) { listFiles(src).filterNot(_.contains(".svn")).foreach { name => val path = src + "/" + name if (new File(path).isDirectory) copyDir(path, dest) else copyFile(path, dest) } } /** * copy a file to a destination directory * @param path path of the file to copy * @param dest destination directory path */ def copyFile(path: String, dest: String) { mkdirs(dest) val destFilePath = dest + "/" + new File(path).getName new File(destFilePath).createNewFile val input = new BufferedInputStream(new FileInputStream(path)) try { val output = new BufferedOutputStream(new FileOutputStream(destFilePath), 2048) try { copy(input, output) output.flush } finally output.close } finally input.close } /** * Unjar the jar (or zip file) specified by "path" to the "dest" directory. * Filters files which shouldn't be extracted with a regular expression. * @param path path of the jar file * @param dest destination directory path * @param regexFilter regular expression filtering files which shouldn't be * extracted; the expression must capture the path of * an entry as group 1 which will then be used relative * to dirPath as target path for that entry */ private def unjar(jarUrl: URL, dirPath: String, regexFilter: String) { mkdirs(dirPath) val regex = compile(regexFilter) val uis = jarUrl.openStream() val zis = new ZipInputStream(new BufferedInputStream(uis)) @annotation.tailrec def extractEntry(entry: ZipEntry) { if (entry != null) { val matcher = regex.matcher(entry.getName) if (matcher.matches) { val target = matcher.replaceFirst(s"${quoteReplacement(dirPath)}$$1") if (entry.isDirectory()) { createDir(target) } else { createFile(target) val fos = new FileOutputStream(target) val dest = new BufferedOutputStream(fos, 2048) try { copy(zis, dest) dest.flush } finally dest.close } } extractEntry(zis.getNextEntry) } } try extractEntry(zis.getNextEntry) finally zis.close } /** * Copy an input stream to an output stream. * @param input input stream * @param output output stream */ def copy(input: InputStream, output: OutputStream) { val data = new Array[Byte](2048) def readData(count: Int): Unit = { if (count != -1) { output.write(data, 0, count) output.flush readData(input.read(data, 0, 2048)) } } readData(input.read(data, 0, 2048)) } /** * Copy specs resources found either in the specs jar or in the classpath directories to an output directory * * @param base path of the base package (must not be empty!) * @param src name of the resource directory to copy (relative to the base package) * @param outputDir output directory where to copy the files to * @param origin a class that determines the location of the resources */ def copySpecResourcesDir(base: String, src: String, outputDir: String, loader: ClassLoader): Unit = Option(loader.getResource(s"$base/$src")) foreach { url => if (url.getProtocol.equalsIgnoreCase("jar")) unjar(jarOf(url), outputDir, s"^${quote(base)}(/${quote(src)}/.*)$$") else copyDir(url.getPath, new File(outputDir, src).getPath) } private def jarOf(url: URL): URL = url.openConnection.asInstanceOf[JarURLConnection].getJarFileURL /** @return true if 2 paths are the same according to their canonical representation */ def samePath(p1: String, p2: String) = new File(p1).getCanonicalPath == new File(p2).getCanonicalPath } /** * The fs object offers a simple interface to the file system (see the description of the FileSystem trait) */ private[specs2] object fs extends FileSystem





© 2015 - 2024 Weber Informatics LLC | Privacy Policy