scala/ScalaBook/chapter-10/src/main/scala/scalabook/file/matcher/FileMatcher.scala
package scalabook.file.matcher
import _root_.scalabook.file.VFile
import _root_.scalabook.file.VFS.AnyFile
import _root_.scalabook.path.Path
import util.matching.Regex
trait Matcher[T] { outer =>
def matches(t: T): Boolean
def binop(other: Matcher[T],
op: (Boolean, Boolean) => Boolean) =
new Matcher[T] {
def matches(t: T) = op(outer.matches(t), other.matches(t))
}
def &&(other: Matcher[T]) = binop(other, _ && _)
def ||(other: Matcher[T]) = binop(other, _ || _)
def unary_! = new Matcher[T] {
def matches(t: T) = !outer.matches(t)
}
}
trait FilePathMatcher extends VFileWithStar.FileMatcher {
def matches(file: AnyFile) = matchesPath(file.path)
def matchesPath(path: Path): Boolean
}
class ExtensionMatcher(ext: String) extends FilePathMatcher {
def matchesPath(path: Path) =
path.extension.toLowerCase == ext.toLowerCase
}
// This is called "Weak" because we consider only filename matching, not path matching
class WeakGlobMatcher(glob: String) extends FilePathMatcher {
val globRE = new Regex("^(?i)" + glob
.replace("\\", "\\\\")
.replace(".", "\\.")
.replace("[", "\\[").replace("]", "\\]")
.replace("(", "\\(").replace(")", "\\)")
.replace("*", ".+")
.replace("?", ".?")
.replace("$", "\\$")
.replace("^", "\\^") + "$")
// A nice optimization (and not widely adopted, not even here) is to precalculate the
// matcher and reset() it every time before using it
def matchesPath(path: Path) =
//globRE.pattern.matcher(path.name).matches
globRE.findFirstIn(path.name).isDefined
def toREString = globRE.pattern.toString
override def toString = glob
}
class VFileWithStar[T <: VFile[T]](file: T) {
import VFileWithStar.FileMatcher
def *(matcher: FileMatcher): Iterable[T] =
file.children filter matcher.matches
def **(matcher: FileMatcher): Iterable[T] = {
def deep(f: T): Iterable[T] =
f.children ++ f.children.flatMap(deep(_))
deep(file) filter matcher.matches
}
}
object VFileWithStar {
type FileMatcher = Matcher[AnyFile]
implicit def file2fileWithStar[T <: VFile[T]](f: T) = new VFileWithStar(f)
implicit def glob2Matcher(glob: String) = new WeakGlobMatcher(glob)
}