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

scalax.io.CloseableIterator.scala Maven / Gradle / Ivy

package scalax.io

import java.io.Closeable
import resource.{AbstractManagedResource, ManagedResourceOperations}
import collection.{GenTraversableOnce, Iterator, TraversableOnce}
import java.util.concurrent.locks.ReentrantLock
import scala.util.control.Exception
import Exception.allCatch


trait CloseableIterator[@specialized(Byte,Char) +A] extends Iterator[A] {
  self =>
  def next(): A
  def hasNext: Boolean
  protected def doClose():List[Throwable]

  override def foreach[@specialized(Unit) U](f: A => U) =
    while (hasNext) f(next)

  final def close():List[Throwable] = doClose()

  override def take(i: Int) = lslice(0, i)
  def ltake(i: Long) = lslice(0, i)
  def ldrop(i: Long) = lslice(i,Long.MaxValue)
  def lslice(from: Long, until: Long):CloseableIterator[A] = {
    var toDrop = from
    while (toDrop > 0) {
      if (toDrop > Int.MaxValue) {
        toDrop -= Int.MaxValue
        drop(Int.MaxValue)
      } else {
        drop(toDrop.toInt)
        toDrop = 0
      }
    }

    if (until == Long.MaxValue) this
    else new CloseableIterator[A] {
      private var count = from max 0
      def next(): A = {
        count += 1
        self.next()
      }

      def hasNext: Boolean = {
        count < until && self.hasNext
      }

      def doClose = self.close()
    }
  }
}

private[io] object CloseableIteratorOps { def apply[A](iter: CloseableIterator[A]) = new CloseableIteratorOps(iter) }
private[io] class CloseableIteratorOps[+A](val iter: CloseableIterator[A]) {
  def collect[B, That](pf: PartialFunction[A, B]) = Proxy(iter.collect(pf))
  def map[B](f: A => B) = Proxy(iter.map(f))
  def flatMap[B](f: (A) => GenTraversableOnce[B]) = Proxy(iter.flatMap(f))
  def dropWhile(p: (A) => Boolean) = Proxy(iter.dropWhile(p))
  def filter(p: (A) => Boolean) = Proxy(iter.filter(p))
  def filterNot(p: (A) => Boolean) = Proxy(iter.filterNot(p))
  def takeWhile(p: A => Boolean) = Proxy(iter.takeWhile(p))
  def init = new InitIterator(iter)
  def ++[B >: A](that: => GenTraversableOnce[B]): CloseableIterator[B] = {
    val concThat = that
    new Proxy[B,Iterator[B]](iter.++(concThat)) {
      override def doClose() = {
        iter.close() ++ CloseableIterator.safeClose(concThat)
      }
    }
  }

  def modifiedSliding(size: Int, step: Int): CloseableIterator[Seq[A]] = Proxy(iter.sliding(size,step))
  def zipAll[B, A1 >: A, B1 >: B](that: Iterator[B], thisElem: A1, thatElem: B1): CloseableIterator[(A1, B1)] =
    Proxy(iter.zipAll(that,thisElem,thatElem))
  def zipWithIndex: CloseableIterator[(A, Int)] {var idx: Int} = {
    val zipped = iter.zipWithIndex
    new Proxy[(A, Int),Iterator[(A, Int)] {var idx: Int}](zipped) {
      var idx = zipped.idx
    }
  }
  def zip[B](that: Iterator[B]): CloseableIterator[(A, B)] =
    Proxy(iter.zip(that))

  def take(i: Int) = iter.lslice(0, i)
  def drop(i: Int) = iter.lslice(i, Long.MaxValue)
  def lslice(from: Long, until: Long) = iter.lslice(from,until)
  import CloseableIterator.safeClose
  class Proxy[+B, C <: Iterator[B]](private[this] val wrapped: C, otherComposingIterators: Iterator[_]*) extends CloseableIterator[B] {
    final def next() = wrapped.next
    final def hasNext: Boolean = wrapped.hasNext
    def doClose():List[Throwable] = {
      safeClose(iter) ++ otherComposingIterators.flatMap(safeClose)
    }
  }
  object Proxy {
    def apply[B](wrapped: Iterator[B]): CloseableIterator[B] = new Proxy[B, Iterator[B]](wrapped)
  }
}

object CloseableIterator {
  def withIterator[A,U](iterator: => CloseableIterator[A], context:ResourceContext)(f:CloseableIterator[A] => U): U = {
    val resourceEither = allCatch.either { iterator }
    var closeExceptions: List[Throwable] = Nil

    /** Close resource and assign any exceptions to closeException */
    def close(resource:CloseableIterator[A]) = try {
      closeExceptions = resource.close()
    } catch {
      case t => closeExceptions = List(t)
    }

    /** Handle error that occurs during resource access */
    def handleAccessError: PartialFunction[Throwable, Either[Throwable, U]] = {
      case c: scala.util.control.ControlThrowable => throw c
      case t => Left(t)
    }

    resourceEither match {
      case Left(t) =>
        context.openErrorHandler(f, t) getOrElse (throw t)
      case Right(resource) =>
        val result =
          try Right(f(resource))
          catch handleAccessError
          finally close(resource)

        val handleError = result.left.toOption ++ closeExceptions nonEmpty

        val finalResult = if (handleError) {
            context.errorHandler(f, result, closeExceptions)
        } else {
          result.right.get
        }

        if (System.identityHashCode(finalResult) == System.identityHashCode(resource)) {
            throw new AssertionError("the iterator may not escape the bounds of this block")
        }
        finalResult
    }
  }
  private[io] def safeClose(iter: Any):List[Throwable] =
    iter match {
        case ci: CloseableIterator[_] =>
          ci.close()
        case c: Closeable =>
          Exception.allCatch.either{c.close()}.left.toOption.toList
        case _ => Nil
    }

  def apply[A](iter: Iterator[A]) = iter match {
    case closeable:CloseableIterator[A] => closeable
    case _ => new CloseableIterator[A] {
        final def next(): A = iter.next
        final def hasNext: Boolean = iter.hasNext
        def doClose() = Nil
      }
  }
  def empty[A] = apply(Iterator.empty)
  def selfClosing[A](wrapped: CloseableIterator[A]) = new CloseableIterator[A] {
    final def next(): A = wrapped.next
    final def hasNext: Boolean = {
      val next = wrapped.hasNext
      if(!next) close()
      next
    }
    def doClose = wrapped.close
  }
}

private[io] class InitIterator[@specialized(Byte) +A](iter:CloseableIterator[A]) extends CloseableIterator[A] {
    private[this] var nextEl:A = _

    if(iter.hasNext) {
      nextEl = iter.next()
    }
    def hasNext = {
      if(iter.hasNext) true
      else if (!iter.hasNext ) false
      else {
        nextEl = iter.next()
        iter.hasNext
      }
    }
    def next = {
      var tmp = nextEl
      nextEl = iter.next()
      tmp
    }

    def doClose = iter.close()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy