package sbt
import Path._
import IO.{pathSplit, wrapNull}
import java.io.File
import java.net.URL
import scala.collection.{generic, immutable, mutable}
final class RichFile(val asFile: File)
{
def / (component: String): File = if(component == ".") asFile else new File(asFile, component)
def exists = asFile.exists
def isDirectory = asFile.isDirectory
def lastModified = asFile.lastModified
def newerThan(other: File): Boolean = Path.newerThan(asFile, other)
def olderThan(other: File): Boolean = Path.newerThan(other, asFile)
def asURL = asFile.toURI.toURL
def absolutePath: String = asFile.getAbsolutePath
def name = asFile.getName
def ext = baseAndExt._2
def base = baseAndExt._1
def baseAndExt: (String, String) =
{
val nme = name
val dot = nme.lastIndexOf('.')
if(dot < 0) (nme, "") else (nme.substring(0, dot), nme.substring(dot+1))
}
def relativize(sub: File): Option[File] = Path.relativizeFile(asFile, sub)
def relativeTo(base: File): Option[File] = Path.relativizeFile(base, asFile)
def hash: Array[Byte] = Hash(asFile)
def hashString: String = Hash.toHex(hash)
def hashStringHalf: String = Hash.halve(hashString)
}
import java.io.File
import File.pathSeparator
trait PathLow
{
implicit def singleFileFinder(file: File): PathFinder = PathFinder(file)
}
trait PathExtra extends Alternatives with Mapper with PathLow
{
implicit def richFile(file: File): RichFile = new RichFile(file)
implicit def filesToFinder(cc: Traversable[File]): PathFinder = PathFinder.strict(cc)
}
object Path extends PathExtra
{
def apply(f: File): RichFile = new RichFile(f)
def apply(f: String): RichFile = new RichFile(new File(f))
def fileProperty(name: String): File = new File(System.getProperty(name))
def userHome: File = fileProperty("user.home")
def absolute(file: File): File = new File(file.toURI.normalize).getAbsoluteFile
def makeString(paths: Seq[File]): String = makeString(paths, pathSeparator)
def makeString(paths: Seq[File], sep: String): String = paths.map(_.getAbsolutePath).mkString(sep)
def newerThan(a: File, b: File): Boolean = a.exists && (!b.exists || a.lastModified > b.lastModified)
val sep = java.io.File.separatorChar
def relativizeFile(baseFile: File, file: File): Option[File] = relativize(baseFile, file).map { path => new File(path) }
private[sbt] def relativize(baseFile: File, file: File): Option[String] =
{
val pathString = file.getAbsolutePath
baseFileString(baseFile) flatMap
{
baseString =>
{
if(pathString.startsWith(baseString))
Some(pathString.substring(baseString.length))
else
None
}
}
}
private def baseFileString(baseFile: File): Option[String] =
if(baseFile.isDirectory)
{
val cp = baseFile.getAbsolutePath
assert(cp.length > 0)
if(cp.charAt(cp.length - 1) == File.separatorChar)
Some(cp)
else
Some(cp + File.separatorChar)
}
else
None
def toURLs(files: Seq[File]): Array[URL] = files.map(_.toURI.toURL).toArray
}
object PathFinder
{
val empty = new PathFinder { private[sbt] def addTo(fileSet: mutable.Set[File]) {} }
def strict(files: Traversable[File]): PathFinder = apply(files)
def apply(files: => Traversable[File]): PathFinder = new PathFinder {
private[sbt] def addTo(fileSet: mutable.Set[File]) = fileSet ++= files
}
def apply(file: File): PathFinder = new SingleFile(file)
}
sealed abstract class PathFinder
{
def +++(paths: PathFinder): PathFinder = new Paths(this, paths)
def ---(excludePaths: PathFinder): PathFinder = new ExcludeFiles(this, excludePaths)
def **(filter: FileFilter): PathFinder = new DescendantOrSelfPathFinder(this, filter)
def *** : PathFinder = **(AllPassFilter)
def *(filter: FileFilter): PathFinder = new ChildPathFinder(this, filter)
def / (literal: String): PathFinder = new ChildPathFinder(this, new ExactFilter(literal))
final def \ (literal: String): PathFinder = this / literal
def x_![T](mapper: File => Option[T]): Traversable[(File,T)] = x(mapper, false)
def pair[T](mapper: File => Option[T], errorIfNone: Boolean = true): Seq[(File,T)] =
x(mapper, errorIfNone)
def x[T](mapper: File => Option[T], errorIfNone: Boolean = true): Seq[(File,T)] =
{
val apply = if(errorIfNone) mapper | fail else mapper
for(file <- get; mapped <- apply(file)) yield (file, mapped)
}
def descendantsExcept(include: FileFilter, intermediateExclude: FileFilter): PathFinder =
(this ** include) --- (this ** intermediateExclude ** include)
@deprecated("Use `descendantsExcept` instead.", "0.12.0")
def descendentsExcept(include: FileFilter, intermediateExclude: FileFilter): PathFinder =
descendantsExcept(include, intermediateExclude)
final def get: Seq[File] =
{
import collection.JavaConversions._
val pathSet: mutable.Set[File] = new java.util.LinkedHashSet[File]
addTo(pathSet)
pathSet.toSeq
}
@deprecated("Use `get`") def getFiles: Seq[File] = get
final def filter(f: File => Boolean): PathFinder = PathFinder(get filter f)
final def flatMap(f: File => PathFinder): PathFinder = PathFinder(get.flatMap(p => f(p).get))
final def getURLs: Array[URL] = get.toArray.map(_.toURI.toURL)
final def getPaths: Seq[String] = get.map(_.absolutePath)
private[sbt] def addTo(fileSet: mutable.Set[File])
def distinct: PathFinder = PathFinder { get.map(p => (p.asFile.getName, p)).toMap.values }
final def absString = Path.makeString(get)
override def toString = get.mkString("\n ", "\n ","")
}
private class SingleFile(asFile: File) extends PathFinder
{
private[sbt] def addTo(fileSet: mutable.Set[File]): Unit = if(asFile.exists) fileSet += asFile
}
private abstract class FilterFiles extends PathFinder with FileFilter
{
def parent: PathFinder
def filter: FileFilter
final def accept(file: File) = filter.accept(file)
protected def handleFile(file: File, fileSet: mutable.Set[File]): Unit =
for(matchedFile <- wrapNull(file.listFiles(this)))
fileSet += new File(file, matchedFile.getName)
}
private class DescendantOrSelfPathFinder(val parent: PathFinder, val filter: FileFilter) extends FilterFiles
{
private[sbt] def addTo(fileSet: mutable.Set[File])
{
for(file <- parent.get)
{
if(accept(file))
fileSet += file
handleFileDescendant(file, fileSet)
}
}
private def handleFileDescendant(file: File, fileSet: mutable.Set[File])
{
handleFile(file, fileSet)
for(childDirectory <- wrapNull(file listFiles DirectoryFilter))
handleFileDescendant(new File(file, childDirectory.getName), fileSet)
}
}
private class ChildPathFinder(val parent: PathFinder, val filter: FileFilter) extends FilterFiles
{
private[sbt] def addTo(fileSet: mutable.Set[File]): Unit =
for(file <- parent.get)
handleFile(file, fileSet)
}
private class Paths(a: PathFinder, b: PathFinder) extends PathFinder
{
private[sbt] def addTo(fileSet: mutable.Set[File])
{
a.addTo(fileSet)
b.addTo(fileSet)
}
}
private class ExcludeFiles(include: PathFinder, exclude: PathFinder) extends PathFinder
{
private[sbt] def addTo(pathSet: mutable.Set[File])
{
val includeSet = new mutable.LinkedHashSet[File]
include.addTo(includeSet)
val excludeSet = new mutable.HashSet[File]
exclude.addTo(excludeSet)
includeSet --= excludeSet
pathSet ++= includeSet
}
}