Rename FileTreeWalk public parameters and builder methods.

Directory filtering now is performed in newly introduced onEnter predicate.
This commit is contained in:
Ilya Gorbunov
2015-12-04 21:23:22 +03:00
parent e24dbcefb6
commit 8deefd56db
2 changed files with 103 additions and 56 deletions
@@ -29,27 +29,54 @@ public enum class FileWalkDirection {
*
* @param start directory to walk into.
* @param direction selects top-down or bottom-up order (in other words, parents first or children first).
* @param enter is called on any entered directory before its files are visited and before it is visited itself.
* @param leave is called on any left directory after its files are visited and after it is visited itself.
* @param fail is called on a directory when it's impossible to get its file list.
* @param onEnter is called on any entered directory before its files are visited and before it is visited itself,
* if `false` is returned, directory is not visited entirely.
* @param onLeave is called on any left directory after its files are visited and after it is visited itself.
* @param onFail is called on a directory when it's impossible to get its file list.
* @param filter is called just before visiting a file, and if `false` is returned, file is not visited.
* @param maxDepth is maximum walking depth, it must be positive. With a value of 1,
* walker visits [start] and all its children, with a value of 2 also grandchildren, etc.
*/
public class FileTreeWalk(private val start: File,
private val direction: FileWalkDirection = FileWalkDirection.TOP_DOWN,
private val enter: (File) -> Unit = {},
private val leave: (File) -> Unit = {},
private val fail: (f: File, e: IOException) -> Unit = { f, e -> Unit },
private val filter: (File) -> Boolean = { true },
private val maxDepth: Int = Int.MAX_VALUE
public class FileTreeWalk private constructor(
private val start: File,
private val direction: FileWalkDirection = FileWalkDirection.TOP_DOWN,
private val onEnter: ((File) -> Boolean)?,
private val onLeave: ((File) -> Unit)?,
private val onFail: ((f: File, e: IOException) -> Unit)?,
private val filter: (File) -> Boolean = { true },
private val maxDepth: Int = Int.MAX_VALUE,
dummy: Boolean = false
) : Sequence<File> {
internal constructor(start: File, direction: FileWalkDirection = FileWalkDirection.TOP_DOWN): this(start, direction, null, null, null, dummy = false)
/*
private constructor(
start: File,
direction: FileWalkDirection = FileWalkDirection.TOP_DOWN,
onEnter: ((File) -> Boolean)? = null,
onLeave: ((File) -> Unit)? = null,
onFail: ((f: File, e: IOException) -> Unit)? = null,
maxDepth: Int = Int.MAX_VALUE
) : this(start, direction, onEnter, onLeave, onFail, maxDepth = maxDepth, dummy = false)
*/
@Deprecated("Use builder methods on an instance obtained from File.walk/walkTopDown/walkBottomUp instead of directly calling constructor.")
public constructor(
start: File,
direction: FileWalkDirection = FileWalkDirection.TOP_DOWN,
enter: (File) -> Unit = {},
leave: (File) -> Unit = {},
fail: (f: File, e: IOException) -> Unit = { f, e -> Unit },
filter: (File) -> Boolean = { true },
maxDepth: Int = Int.MAX_VALUE
) : this(start, direction, onEnter = { enter(it); true }, onLeave = leave, onFail = fail, filter = filter, maxDepth = maxDepth)
/** Returns an iterator walking through files. */
override public fun iterator(): Iterator<File> = FileTreeWalkIterator()
/** Abstract class that encapsulates file visiting in some order, beginning from a given [rootDir] */
private abstract class WalkState(val rootDir: File) {
/** Abstract class that encapsulates file visiting in some order, beginning from a given [root] */
private abstract class WalkState(val root: File) {
/** Call of this function proceeds to a next file for visiting and returns it */
abstract public fun step(): File?
}
@@ -112,7 +139,7 @@ public class FileTreeWalk(private val start: File,
// Check that file/directory matches the filter
if (!filter(file))
return gotoNext()
if (file == topState.rootDir || !file.isDirectory || state.size >= maxDepth) {
if (file == topState.root || !file.isDirectory || state.size >= maxDepth) {
// Proceed to a root directory or a simple file
return file
} else {
@@ -137,10 +164,13 @@ public class FileTreeWalk(private val start: File,
/** First all children, then root directory */
override public fun step(): File? {
if (!failed && fileList == null) {
enter(rootDir)
fileList = rootDir.listFiles()
if (onEnter?.invoke(root) == false) {
return null
}
fileList = root.listFiles()
if (fileList == null) {
fail(rootDir, AccessDeniedException(file = rootDir, reason = "Cannot list files in a directory"))
onFail?.invoke(root, AccessDeniedException(file = root, reason = "Cannot list files in a directory"))
failed = true
}
}
@@ -150,10 +180,10 @@ public class FileTreeWalk(private val start: File,
} else if (!rootVisited) {
// Then visit root
rootVisited = true
return rootDir
return root
} else {
// That's all
leave(rootDir)
onLeave?.invoke(root)
return null
}
}
@@ -172,18 +202,21 @@ public class FileTreeWalk(private val start: File,
override public fun step(): File? {
if (!rootVisited) {
// First visit root
enter(rootDir)
if (onEnter?.invoke(root) == false) {
return null
}
rootVisited = true
return rootDir
return root
} else if (fileList == null || fileIndex < fileList!!.size) {
if (fileList == null) {
// Then read an array of files, if any
fileList = rootDir.listFiles()
fileList = root.listFiles()
if (fileList == null) {
fail(rootDir, AccessDeniedException(file = rootDir, reason = "Cannot list files in a directory"))
onFail?.invoke(root, AccessDeniedException(file = root, reason = "Cannot list files in a directory"))
}
if (fileList == null || fileList!!.size == 0) {
leave(rootDir)
onLeave?.invoke(root)
return null
}
}
@@ -191,56 +224,66 @@ public class FileTreeWalk(private val start: File,
return fileList!![fileIndex++]
} else {
// That's all
leave(rootDir)
onLeave?.invoke(root)
return null
}
}
}
private inner class SingleFileState(root: File) : WalkState(root) {
private inner class SingleFileState(rootFile: File) : WalkState(rootFile) {
private var visited: Boolean = false
init {
@Suppress("DEPRECATION")
if (ASSERTIONS_ENABLED)
assert(root.isFile) { "root must be verified to be file beforehand." }
assert(rootFile.isFile) { "rootFile must be verified to be file beforehand." }
}
override fun step(): File? {
if (visited) return null
visited = true
if (!filter(rootDir)) return null
return rootDir
if (!filter(root)) return null
return root
}
}
}
/**
* Sets enter directory [function].
* Sets enter directory predicate [function].
* Enter [function] is called BEFORE the corresponding directory and its files are visited.
* If the [function] returns `false` the directory is not entered, and neither it nor its files are not visited.
*/
public fun enter(function: (File) -> Unit): FileTreeWalk {
return FileTreeWalk(start, direction, function, leave, fail, filter, maxDepth)
public fun onEnter(function: (File) -> Boolean): FileTreeWalk {
return FileTreeWalk(start, direction, onEnter = function, onLeave = onLeave, onFail = onFail, filter = filter, maxDepth = maxDepth)
}
@Deprecated("Use onEnter instead.")
public fun enter(function: (File) -> Unit): FileTreeWalk = onEnter { function(it); true }
/**
* Sets leave directory [function].
* Leave [function] is called AFTER the corresponding directory and its files are visited.
*/
public fun leave(function: (File) -> Unit): FileTreeWalk {
return FileTreeWalk(start, direction, enter, function, fail, filter, maxDepth)
public fun onLeave(function: (File) -> Unit): FileTreeWalk {
return FileTreeWalk(start, direction, onEnter, function, onFail, filter, maxDepth, false)
}
@Deprecated("Use onLeave instead.", ReplaceWith("onLeave(function)"))
public fun leave(function: (File) -> Unit): FileTreeWalk = onLeave(function)
/**
* Set fail entering directory [function].
* Fail [function] is called when walker is unable to get list of directory files.
* Enter and leave functions are called even in this case.
*/
public fun fail(function: (File, IOException) -> Unit): FileTreeWalk {
return FileTreeWalk(start, direction, enter, leave, function, filter, maxDepth)
public fun onFail(function: (File, IOException) -> Unit): FileTreeWalk {
return FileTreeWalk(start, direction, onEnter, onLeave, function, filter, maxDepth)
}
@Deprecated("Use onFail instead.", ReplaceWith("onFail(function)"))
public fun fail(function: (File, IOException) -> Unit): FileTreeWalk = onFail(function)
/**
* Sets tree filter [predicate].
@@ -248,8 +291,9 @@ public class FileTreeWalk(private val start: File,
* If it returns `false`, file is not visited, directory is not entered and all its content is also not visited.
* If it returns `true`, everything goes in the regular way.
*/
@Deprecated("Filter out directories entirely with onEnter, and all items with filter().")
public fun treeFilter(predicate: (File) -> Boolean): FileTreeWalk {
return FileTreeWalk(start, direction, enter, leave, fail, predicate, maxDepth)
return FileTreeWalk(start, direction, onEnter, onLeave, onFail, predicate, maxDepth)
}
/**
@@ -259,7 +303,7 @@ public class FileTreeWalk(private val start: File,
public fun maxDepth(depth: Int): FileTreeWalk {
if (depth <= 0)
throw IllegalArgumentException("Use positive depth value")
return FileTreeWalk(start, direction, enter, leave, fail, filter, depth)
return FileTreeWalk(start, direction, onEnter, onLeave, onFail, filter, depth)
}
}