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

com.metamx.common.scala.Walker.scala Maven / Gradle / Ivy

package com.metamx.common.scala

import java.util.concurrent.atomic.AtomicBoolean
import scala.collection.GenTraversableOnce

/**
 * Walkers provide a mechanism for walking through an underlying listish thing, exposed as a "foreach" method. They
 * do not return iterators, nor do they allow random access. This allows them to guarantee post-iteration cleanup
 * actions on the underlying resource, which will occur even if exceptions are thrown while walking.
 *
 * Walkers can be constructed such that the "foreach" method can only be called once. In that case, subsequent calls
 * should throw an IllegalStateException. Walkers can also be constructed with "foreach" methods that can be called
 * multiple times. In that case, each run should create and then clean up the resource-- saving state across runs is
 * usually counterproductive.
 */
trait Walker[+A]
{
  self =>

  def foreach(f: A => Unit)

  def map[B](f: A => B): Walker[B] = new Walker[B] {
    override def foreach(g: B => Unit) = self.foreach(a => g(f(a)))
  }

  def flatMap[B](f: A => GenTraversableOnce[B]): Walker[B] = new Walker[B] {
    override def foreach(g: B => Unit) = self.foreach(a => f(a) foreach g)
  }

  def filter(p: A => Boolean): Walker[A] = new Walker[A] {
    override def foreach(g: A => Unit) = self foreach {
      a =>
        if (p(a)) {
          g(a)
        }
    }
  }

  def withFilter(p: A => Boolean): Walker[A] = filter(p)

  def ++[B >: A](other: Walker[B]): Walker[B] = new Walker[B] {
    override def foreach(g: B => Unit) {
      self foreach g
      other foreach g
    }
  }

  def toList: List[A] = {
    val builder = List.newBuilder[A]
    foreach(builder += _)
    builder.result()
  }

  def toSet[B >: A]: Set[B] = {
    val builder = Set.newBuilder[B]
    foreach(builder += _)
    builder.result()
  }

  def foldLeft[B](zero: B)(combine: (B, A) => B): B = {
    var current = zero
    foreach {
      a =>
        current = combine(current, a)
    }
    current
  }

  def size: Long = {
    var count = 0L
    foreach(_ => count += 1)
    count
  }
}

object Walker
{
  def empty[A]: Walker[A] = Walker[A](Nil)

  def apply[A](xs: Iterable[A]): Walker[A] = new Walker[A] {
    override def foreach(f: A => Unit) = xs foreach f
  }

  def apply[A](foreachFn: (A => Unit) => Unit): Walker[A] = new Walker[A] {
    override def foreach(f: A => Unit) {
      foreachFn(f)
    }
  }

  def once[A](foreachFn: (A => Unit) => Unit): Walker[A] = new Walker[A] {
    val finished = new AtomicBoolean(false)

    override def foreach(f: A => Unit) {
      val wasFinished = finished.getAndSet(true)
      if (wasFinished) {
        throw new IllegalStateException("Cannot walk more than once")
      } else {
        foreachFn(f)
      }
    }
  }

  class WalkerTuple2Ops[A, B](walker: Walker[(A, B)])
  {
    def toMap: Map[A, B] = {
      val builder = Map.newBuilder[A, B]
      walker.foreach(builder += _)
      builder.result()
    }
  }

  implicit def WalkerTuple2Ops[A, B](walker: Walker[(A, B)]) = new WalkerTuple2Ops(walker)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy