flatgraph.traversal.RepeatBehaviour.scala Maven / Gradle / Ivy
package flatgraph.traversal
import RepeatBehaviour._
trait RepeatBehaviour[A] {
val searchAlgorithm: SearchAlgorithm.Value
val untilCondition: Option[A => Iterator[?]]
val whileCondition: Option[A => Iterator[?]]
val maxDepth: Option[Int]
val dedupEnabled: Boolean
def maxDepthReached(currentDepth: Int): Boolean =
maxDepth.isDefined && maxDepth.get <= currentDepth
def untilConditionReached(element: A): Boolean =
untilCondition match {
case Some(untilConditionTraversal) => untilConditionTraversal(element).hasNext
case None => false
}
def whileConditionIsDefinedAndEmpty(element: A): Boolean =
whileCondition match {
case Some(whileConditionTraversal) =>
whileConditionTraversal(element).isEmpty
case None =>
false
}
def shouldEmit(element: A, currentDepth: Int): Boolean
}
object RepeatBehaviour {
type Traversal[T] = Iterator[T]
object SearchAlgorithm extends Enumeration {
type SearchAlgorithm = Value
val DepthFirst, BreadthFirst = Value
}
def noop[A](builder: RepeatBehaviour.Builder[A]): Builder[A] = builder
class Builder[A] {
private var _shouldEmit: (A, Int) => Boolean = (_, _) => false
private var _untilCondition: Option[Traversal[A] => Traversal[?]] = None
private var _whileCondition: Option[Traversal[A] => Traversal[?]] = None
private var _maxDepth: Option[Int] = None
private var _dedupEnabled: Boolean = false
private var _searchAlgorithm: SearchAlgorithm.Value = SearchAlgorithm.DepthFirst
/** configure search algorithm to go "breadth first", rather than the default "depth first" */
def breadthFirstSearch: Builder[A] = {
_searchAlgorithm = SearchAlgorithm.BreadthFirst
this
}
/** configure search algorithm to go "breadth first", rather than the default "depth first" */
def bfs: Builder[A] = breadthFirstSearch
/** Emit all intermediate elements (along the way). */
def emit: Builder[A] = {
_shouldEmit = (_, _) => true
this
}
/** Emit intermediate elements (along the way), apart from the _first_ element */
def emitAllButFirst: Builder[A] = {
_shouldEmit = (_, depth) => depth > 0
this
}
/** Emit intermediate elements (along the way), if they meet the given condition. Note: this does not apply a filter on the final
* elements of the traversal! Quite likely that you want to reuse the given condition as a filter step at the end of your traversal...
* See `RepeatTraversalTests` for an example.
*/
def emit(condition: Traversal[A] => Traversal[?]): Builder[A] = {
_shouldEmit = (element, _) => condition(Iterator.single(element)).hasNext
this
}
/* Configure `repeat` step to stop traversing when given condition-traversal has at least one result.
* The condition-traversal is only evaluated _after_ the first iteration, for classic repeat/until behaviour */
def until(condition: Traversal[A] => Traversal[?]): Builder[A] = {
_untilCondition = Some(condition)
this
}
/** Stop traversing when given condition-traversal has no result. The condition-traversal is already evaluated at the first iteration,
* for classic while/repeat behaviour.
*
* n.b. the only reason not to call this `while` is to avoid using scala keywords, which would need to be quoted.
*/
def whilst(condition: Traversal[A] => Traversal[?]): Builder[A] = {
_whileCondition = Some(condition)
this
}
/** Maximum depth to go down in the repeat traversal. Note that there may be other conditions like until|whilst etc.
*/
def maxDepth(value: Int): Builder[A] = {
_maxDepth = Some(value)
this
}
/** Keep a 'visited' list of elements to ensure we are not going in cycles. */
def dedup: Builder[A] = {
_dedupEnabled = true
this
}
private[traversal] def build: RepeatBehaviour[A] = {
new RepeatBehaviour[A] {
override val searchAlgorithm: SearchAlgorithm.Value = _searchAlgorithm
override val untilCondition = _untilCondition.map(_.andThen(_.iterator).compose(Iterator.single))
override val whileCondition = _whileCondition.map(_.andThen(_.iterator).compose(Iterator.single))
final override val maxDepth: Option[Int] = _maxDepth
final override val dedupEnabled = _dedupEnabled
override def shouldEmit(element: A, currentDepth: Int): Boolean = _shouldEmit(element, currentDepth)
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy