Skip to content

Commit

Permalink
glob: add support for automatic path prefix
Browse files Browse the repository at this point in the history
Support automatic path prefix with glob (`pathGlob` and `pathRegex`).
In this mode, pattern is prefixed with `File`-instance's path.

Standard java PatternMatch names `regex` and `glob` behaves
as they do in java world.

Default is `pathGlob`, that default behaviour is similar
with  Unix `ls` or with
[Python 3.5+ glob](https://docs.python.org/3/library/glob.html)

Please see pathikrit#114 for background information.
  • Loading branch information
jaa127 committed Feb 15, 2017
1 parent fea16b4 commit f8f7e0b
Show file tree
Hide file tree
Showing 5 changed files with 422 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## v3.0.0

* [PR #116](https://github.com/pathikrit/better-files/pull/116): Glob with automatic path
* [Issue #107](https://github.com/pathikrit/better-files/issues/107): Handle Byte-order markers
* [PR #113](https://github.com/pathikrit/better-files/pull/113): File anchor util
* [Issue #105](https://github.com/pathikrit/better-files/issues/105): Remove dependency on scala.io
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,10 @@ val matches: Iterator[File] = dir.glob("**/*.{java,scala}")
// above code is equivalent to:
dir.listRecursively.filter(f => f.extension == Some(".java") || f.extension == Some(".scala"))
```
List `*.txt` files:
````scala
val txts = dir.glob("*.txt")
````
You can even use more advanced regex syntax instead of [glob syntax](http://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob):
```scala
val matches = dir.glob("^\\w*$")(syntax = File.PathMatcherSyntax.regex)
Expand Down
42 changes: 39 additions & 3 deletions core/src/main/scala/better/files/File.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import java.nio.file._
import java.nio.file.attribute._
import java.security.{DigestInputStream, MessageDigest}
import java.time.Instant
import java.util.regex.Pattern
import java.util.zip._
import javax.xml.bind.DatatypeConverter

Expand Down Expand Up @@ -471,8 +472,41 @@ class File private(val path: Path) {
def walk(maxDepth: Int = Int.MaxValue)(implicit visitOptions: File.VisitOptions = File.VisitOptions.default): Files =
Files.walk(path, maxDepth, visitOptions: _*) //TODO: that ignores I/O errors?

def pathMatcher(syntax: File.PathMatcherSyntax)(pattern: String): PathMatcher =
fileSystem.getPathMatcher(s"${syntax.name}:$pattern")

/**
* Combine glob with path so that it will match relative path without leading glob-pattern.
*/
protected def pathGlob(glob: String) = {
val escapedPath = (path.toString + fileSystem.getSeparator)
.replaceAllLiterally("""\\""", """\\\\""")
.replaceAllLiterally("*", "\\*")
.replaceAllLiterally("?", "\\?")
.replaceAllLiterally("{", "\\{")
.replaceAllLiterally("}", "\\}")
.replaceAllLiterally("[", "\\[")
.replaceAllLiterally("]", "\\]")

"glob:" + escapedPath + glob
}

/**
* Combine regex with path so that it will match relative path without leading regex-pattern.
*/
protected def pathRegex(regex: String) = {
val pathWithSep = path.toString + fileSystem.getSeparator
"regex:" + Pattern.quote(pathWithSep) + regex
}

def pathMatcher(syntax: File.PathMatcherSyntax)(pattern: String): PathMatcher = {
if (syntax.name == "pathGlob") {
fileSystem.getPathMatcher(pathGlob(pattern))
} else if (syntax.name == "pathRegex") {
fileSystem.getPathMatcher(pathRegex(pattern))
} else {
fileSystem.getPathMatcher(s"${syntax.name}:$pattern")
}
}


/**
* Util to glob from this file's path
Expand Down Expand Up @@ -997,7 +1031,9 @@ object File {
object PathMatcherSyntax {
val glob = new PathMatcherSyntax("glob")
val regex = new PathMatcherSyntax("regex")
val default = glob
val pathGlob = new PathMatcherSyntax("pathGlob")
val pathRegex = new PathMatcherSyntax("pathRegex")
val default = pathGlob
def other(syntax: String) = new PathMatcherSyntax(syntax)
}

Expand Down
4 changes: 2 additions & 2 deletions core/src/test/scala/better/files/FileSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ class FileSpec extends FlatSpec with BeforeAndAfterEach with Matchers {
}

it should "glob" in {
a1.glob("**/*.txt").map(_.name).toSeq.sorted shouldEqual Seq("t1.txt", "t2.txt")
a1.glob("*.txt").map(_.name).toSeq.sorted shouldEqual Seq("t1.txt", "t2.txt")
//a1.glob("*.txt").map(_.name).toSeq shouldEqual Seq("t1.txt", "t2.txt")
testRoot.glob("**/*.txt").map(_.name).toSeq.sorted shouldEqual Seq("t1.txt", "t2.txt")
val path = testRoot.path.toString.ensuring(testRoot.path.isAbsolute)
Expand All @@ -182,7 +182,7 @@ class FileSpec extends FlatSpec with BeforeAndAfterEach with Matchers {
("core"/"src"/"test").walk(maxDepth = 1) should have length 2
("core"/"src"/"test").walk(maxDepth = 0) should have length 1
("core"/"src"/"test").walk() should have length (("core"/"src"/"test").listRecursively.length + 1L)
ls_r("core"/"src"/"test") should have length 4
ls_r("core"/"src"/"test") should have length 5
}

it should "support names/extensions" in {
Expand Down
Loading

0 comments on commit f8f7e0b

Please sign in to comment.