// -----------------------------------------------------------------------------
//
// Scalax - The Scala Community Library
// Copyright (c) 2005-8 The Scalax Project. All rights reserved.
//
// The primary distribution site is http://scalax.scalaforge.org/
//
// This software is released under the terms of the Revised BSD License.
// There is NO WARRANTY. See the file LICENSE for the full text.
//
// -----------------------------------------------------------------------------
package scalax.control
import java.io._
import scala.collection.mutable._
import scalax.data._
/** Provides automatic resource management, equivalent to C#'s using, or C++
* RAII. Idiomatic usage would be as follows (in fact FileExtras provides a
* method equivalent to fileReader below):
*
* <pre>
* def fileReader(f : String) = ManagedResource(new FileReader(f))
*
* // Print the first character of a file
* for(in <- fileReader("/etc/passwd"))
* println(in.read().toChar)
* </pre>
*/
abstract class ManagedResource[+A] { self =>
type Handle
/** Should be implemented to acquire the managed resource. Clients are
* discouraged from calling this method directly, as the returned resource
* must be disposed of manually. */
def unsafeOpen() : Handle
/** Should be implemented to dispose of the managed resource. This will be
* called automatically when the ManagedResource is used in a
* for-comprehension. */
def unsafeClose(v : Handle) : Unit
def unsafeCloseQuietly(v : Handle) {
try {
unsafeClose(v)
} catch {
case e => e.printStackTrace()
}
}
/** Close ignoring Exception, but not any Throwable. */
def unsafeCloseIgnoringException(v : Handle) {
try {
unsafeClose(v)
} catch {
case e: Exception => e.printStackTrace()
}
}
/** Should be implemented to translate a Handle into the desired resource
* type. */
def translate(v : Handle) : A
def foreach(f : A => Unit) : Unit = acquireFor(f)
def flatMap[B](f : A => B) : B = acquireFor(f)
def map[B](f : A => B) : B = acquireFor(f)
/** Acquires the resource for the duration of the supplied function. */
def acquireFor[B](f : A => B) : B = {
val v = unsafeOpen()
try {
val r = f(translate(v))
unsafeClose(v)
r
} finally {
unsafeCloseQuietly(v)
}
}
def and[B](that : ManagedResource[B]) : ManagedResource[(A, B)] =
new ManagedResource[(A, B)] {
type Handle = (self.Handle, that.Handle)
def unsafeOpen() = {
val a = self.unsafeOpen()
val b =
try {
that.unsafeOpen()
} catch {
case e =>
self.unsafeClose(a)
throw e
}
(a, b)
}
def unsafeClose(v : Handle) =
try {
that.unsafeClose(v._2)
} finally {
self.unsafeClose(v._1)
}
def translate(v : Handle) =
(self.translate(v._1), that.translate(v._2))
}
}
/** The typical case of a ManagedResource, where the handle and resource are
* the same object. */
abstract class UntranslatedManagedResource[A] extends ManagedResource[A] {
type Handle = A
final def translate(v : Handle) = v
}
object ManagedResource {
/** Creates a ManagedResource for any type with a close method. Note that
* the opener argument is evaluated on demand, possibly more than once, so
* it must contain the code that actually acquires the resource. Clients
* are encouraged to write specialized methods to instantiate
* ManagedResources rather than relying on ad-hoc usage of this method. */
def apply[A <: { def close() : Unit }](opener : => A) =
new UntranslatedManagedResource[A] {
def unsafeOpen() = opener
def unsafeClose(r : A) = r.close()
}
}
/** A class which is approximately isomorphic to Seq, but which manages the
* lifecycle of the underlying data source, and is generally non-strict. */
abstract class ManagedSequence[+A] extends PartialFunction[Int, A] { self =>
type Handle
val resource : ManagedResource[Handle]
// N.B. these functions are permitted to trash the handle
protected def iterator(v : Handle) : Iterator[A]
protected def length(v : Handle) : Int = {
var i = 0
val iter = iterator(v)
while(iter.hasNext) {
iter.next
i += 1
}
i
}
protected def apply(v : Handle, i : Int) : A =
iterator(v).drop(i).next
private abstract class Child[+B] extends ManagedSequence[B] {
type Handle = self.Handle
val resource = self.resource
}
def elements = new ManagedResource[Iterator[A]] {
type Handle = resource.Handle
def unsafeOpen() = resource.unsafeOpen()
def unsafeClose(v : Handle) = resource.unsafeClose(v)
def translate(v : Handle) = iterator(resource.translate(v))
}
def length : Int = for(v <- resource) yield length(v)
def isEmpty = for(v <- resource) yield iterator(v).hasNext
def apply(i : Int) : A = for(v <- resource) yield apply(v, i)
def last = lastOption.get
def lastOption =
for(v <- resource) yield {
val iter = iterator(v)
if(iter.hasNext) {
var x = iter.next
while(iter.hasNext) x = iter.next
Some(x)
} else {
None
}
}
@deprecated
def head = first
def first = apply(0)
@deprecated
def headOption =
firstOption
def firstOption =
for(v <- resource) yield {
val iter = iterator(v)
if(iter.hasNext) Some(iter.next) else None
}
def ++[B >: A](that : ManagedSequence[B]) : ManagedSequence[B] =
new ManagedSequence[B] {
type Handle = (self.Handle, that.Handle)
val resource = self.resource and that.resource
protected def iterator(v : Handle) =
self.iterator(v._1) ++ that.iterator(v._2)
protected override def length(v : Handle) =
self.length(v._1) + that.length(v._2)
protected override def apply(v : Handle, i : Int) : B = {
if(i < 0) throw new NoSuchElementException
var j = 0
val iter = self.iterator(v._1)
while(iter.hasNext) {
val x = iter.next
if(i == j) return x
j += 1
}
that(v._2, i - j)
}
}
def ++[B >: A](that : Seq[B]) : ManagedSequence[B] =
new Child[B] {
protected def iterator(v : Handle) =
self.iterator(v) ++ that.elements
protected override def length(v : Handle) =
self.length(v) + that.length
protected override def apply(v : Handle, i : Int) : B = {
if(i < 0) throw new NoSuchElementException
var j = 0
val iter = self.iterator(v)
while(iter.hasNext) {
val x = iter.next
if(i == j) return x
j += 1
}
that(i - j)
}
}
def isDefinedAt(i : Int) = i < length
def lastIndexOf(e : Any) = {
for(r <- resource) yield {
val iter = iterator(r)
var i = 0
var pos = -1
while(iter.hasNext) {
if(iter.next == e) pos = i
i += 1
}
pos
}
}
def map[B](f : A => B) : ManagedSequence[B] =
new Child[B] {
protected def iterator(v : Handle) = self.iterator(v).map(f)
protected override def length(v : Handle) = self.length(v)
protected override def apply(v : Handle, i : Int) = f(self.apply(v, i))
}
def flatMap[B](f : A => Iterable[B]) : ManagedSequence[B] =
new Child[B] {
protected def iterator(v : Handle) =
self.iterator(v).flatMap(x => f(x).elements)
}
def filter(f : A => Boolean) : ManagedSequence[A] =
new Child[A] {
protected def iterator(v : Handle) = self.iterator(v).filter(f)
}
def take(n : Int) : ManagedSequence[A] =
new Child[A] {
protected def iterator(v : Handle) = self.iterator(v).take(n)
protected override def length(v : Handle) = {
val len = self.length(v)
if(len < n) len else n
}
protected override def apply(v : Handle, i : Int) =
if(i < n) self.apply(v, i)
else throw new NoSuchElementException
}
def drop(n : Int) : ManagedSequence[A] =
new Child[A] {
protected def iterator(v : Handle) = self.iterator(v).drop(n)
protected override def length(v : Handle) = {
val len = self.length(v)
if(len < n) 0 else len - n
}
protected override def apply(v : Handle, i : Int) =
self.apply(v, i + n)
}
def slice(from : Int, until : Int) : ManagedSequence[A] =
new Child[A] {
protected def iterator(v : Handle) =
self.iterator(v).drop(from).take(until - from)
protected override def length(v : Handle) = {
val len = self.length(v)
if(until < len) until - from else len - from
}
protected override def apply(v : Handle, i : Int) =
if(i + from < until) self.apply(v, i + from)
else throw new NoSuchElementException
}
def takeWhile(f : A => Boolean) : ManagedSequence[A] =
new Child[A] {
protected def iterator(v : Handle) = self.iterator(v).takeWhile(f)
}
def dropWhile(f : A => Boolean) : ManagedSequence[A] =
new Child[A] {
protected def iterator(v : Handle) = self.iterator(v).dropWhile(f)
}
def reverse =
for(v <- resource) yield {
var xs : List[A] = Nil
val iter = iterator(v)
while(iter.hasNext) {
xs = iter.next :: xs
}
xs
}
def contains(e : Any) =
for(v <- resource) yield {
iterator(v).contains(e)
}
def toArray[B >: A] =
for(v <- resource) yield {
val a = new FastArrayBuffer[B]
a ++= iterator(v)
a.toArray
}
def startsWith(that : Seq[Any]) =
for(v <- resource) yield {
val iter = iterator(v)
val jter = that.elements
var r = true
while(iter.hasNext && jter.hasNext)
if(iter.next != jter.next) r = false
r && !jter.hasNext
}
def endsWith(that : Seq[Any]) = {
val ilen = for(v <- resource) yield length(v)
val jlen = that.length
if(jlen > ilen) {
false
} else {
for(v <- resource) yield {
val iter = iterator(v)
iter.drop(ilen - jlen)
val jter = that.elements
var r = true
while(iter.hasNext && jter.hasNext)
if(iter.next != jter.next) r = false
r && !iter.hasNext && !jter.hasNext
}
}
}
def indexOf(that : Seq[Any]) =
for(v <- resource) yield {
val iter = iterator(v)
var i = 0
var jter = that.elements
var j = -1
while(iter.hasNext && jter.hasNext) {
if(iter.next != jter.next) {
if(j != -1) {
jter = that.elements
j = -1
}
} else if(j == -1) {
j = i
}
i += 1
}
j
}
def containsSlice(that : Seq[Any]) = indexOf(that) != -1
def foreach(f : A => Unit) = for(v <- resource) yield iterator(v).foreach(f)
def forall(f : A => Boolean) = for(v <- resource) yield iterator(v).forall(f)
def exists(f : A => Boolean) = for(v <- resource) yield iterator(v).exists(f)
def find(f : A => Boolean) = for(v <- resource) yield iterator(v).find(f)
def findIndexOf(f : A => Boolean) =
for(v <- resource) yield {
var i = 0
var r = -1
val iter = iterator(v)
while(r == -1 && iter.hasNext) {
if(f(iter.next)) r = i
i += 1
}
r
}
def indexOf(e : Any) =
for(v <- resource) yield {
var i = 0
var r = -1
val iter = iterator(v)
while(r == -1 && iter.hasNext) {
if(iter.next == e) r = i
i += 1
}
r
}
def foldLeft[B](z : B)(op : (B, A) => B) =
for(v <- resource) yield iterator(v).foldLeft(z)(op)
def foldRight[B](z : B)(op : (A, B) => B) =
for(v <- resource) yield iterator(v).foldRight(z)(op)
def reduceLeft[B >: A](op : (B, B) => B) =
for(v <- resource) yield iterator(v).reduceLeft(op)
def reduceRight[B >: A](op : (B, B) => B) =
for(v <- resource) yield iterator(v).reduceRight(op)
def copyToBuffer[B >: A](buf : Buffer[B]) =
for(v <- resource) buf ++= iterator(v)
def sameElements(that : Iterable[Any]) =
for(v <- resource) yield {
val iter = iterator(v)
val jter = that.elements
var r = true
while(iter.hasNext && jter.hasNext)
if(iter.next != jter.next) r = false
r && !iter.hasNext && !jter.hasNext
}
def sameElements(that : ManagedSequence[Any]) =
for(v <- resource; w <- that.resource) yield {
val iter = iterator(v)
val jter = that.iterator(w)
var r = true
while(iter.hasNext && jter.hasNext)
if(iter.next != jter.next) r = false
r && !iter.hasNext && !jter.hasNext
}
def toList =
for(v <- resource) yield iterator(v).toList
def mkString(start : String, sep : String, end : String) : String =
for(v <- resource) yield {
val iter = iterator(v)
val sb = new StringBuffer
sb.append(start)
if(iter.hasNext) sb.append(iter.next.toString)
while(iter.hasNext) {
sb.append(sep)
sb.append(iter.next.toString)
}
sb.append(end)
sb.toString
}
def mkString(sep : String) : String = mkString("", sep, "")
def mkString : String = mkString("", "", "")
def copyToArray[B >: A](arr : Array[B], start : Int) =
for(v <- resource) yield iterator(v).copyToArray(arr, start)
protected def stringPrefix = "ManagedSequence"
override def toString = mkString(stringPrefix+"(", ", ", ")")
}
// vim: set ts=4 sw=4 noet: