All Downloads are FREE. Search and download functionalities are using the official Maven repository.

overflowdb.traversal.RepeatBehaviour.scala Maven / Gradle / Ivy

package overflowdb.traversal

import RepeatBehaviour._

trait RepeatBehaviour[A] {
  val searchAlgorithm: SearchAlgorithm.Value
  val untilCondition: Option[A => Iterator[_]]
  val whileCondition: Option[A => Iterator[_]]
  val times: Option[Int]
  val dedupEnabled: Boolean

  def timesReached(currentDepth: Int): Boolean =
    times.isDefined && times.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 {

  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[this] var _shouldEmit: (A, Int) => Boolean = (_, _) => false
    private[this] var _untilCondition: Option[Traversal[A] => Traversal[_]] = None
    private[this] var _whileCondition: Option[Traversal[A] => Traversal[_]] = None
    private[this] var _times: Option[Int] = None
    private[this] var _dedupEnabled: Boolean = false
    private[this] 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
    }
    def bfs: Builder[A] = breadthFirstSearch

    /* configure `repeat` step to emit everything along the way */
    def emit: Builder[A] = {
      _shouldEmit = (_, _) => true
      this
    }

    /* configure `repeat` step to emit everything along the way, apart from the _first_ element */
    def emitAllButFirst: Builder[A] = {
      _shouldEmit = (_, depth) => depth > 0
      this
    }

    /* configure `repeat` step to emit whatever meets the given condition */
    def emit(condition: Traversal[A] => Traversal[_]): Builder[A] = {
      _shouldEmit = (element, _) => condition(Traversal.fromSingle(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
    }

    /* Configure `repeat` step to stop traversing when given condition-traversal has at least one 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
    }

    /* configure `repeat` step to perform the given amount of iterations */
    def times(value: Int): Builder[A] = {
      _times = Some(value)
      this
    }

    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(Traversal.fromSingle))
        override val whileCondition = _whileCondition.map(_.andThen(_.iterator).compose(Traversal.fromSingle))
        final override val times: Option[Int] = _times
        final override val dedupEnabled = _dedupEnabled
        override def shouldEmit(element: A, currentDepth: Int): Boolean = _shouldEmit(element, currentDepth)
      }
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy