summaryrefslogtreecommitdiff
path: root/src/library/scala/util
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-11-12 14:18:44 +0000
committerPaul Phillips <paulp@improving.org>2010-11-12 14:18:44 +0000
commit505a858ea1328227f18d116926a57296fd63dddd (patch)
tree41b56325e39c7415b9a96bd200a3e67b62c237c4 /src/library/scala/util
parent91eff8e6d903a9eab2bc83c120774d623df5ad7d (diff)
downloadscala-505a858ea1328227f18d116926a57296fd63dddd.tar.gz
scala-505a858ea1328227f18d116926a57296fd63dddd.tar.bz2
scala-505a858ea1328227f18d116926a57296fd63dddd.zip
A revival of r21442, which I had reverted based...
A revival of r21442, which I had reverted based on the mistaken belief it was causing mysterious trunk issues of the day. No review.
Diffstat (limited to 'src/library/scala/util')
-rw-r--r--src/library/scala/util/control/Exception.scala139
1 files changed, 78 insertions, 61 deletions
diff --git a/src/library/scala/util/control/Exception.scala b/src/library/scala/util/control/Exception.scala
index 87aa5a9888..3ad5b30f7b 100644
--- a/src/library/scala/util/control/Exception.scala
+++ b/src/library/scala/util/control/Exception.scala
@@ -1,9 +1,12 @@
package scala.util.control
import collection.immutable.List
+import java.lang.reflect.InvocationTargetException
/** Classes representing the components of exception handling.
- * Each class is independently composable. Some common uses:
+ * Each class is independently composable. Some example usages:
+ *
+ *
*
* <pre>
* <b>import</b> scala.util.control.Exception._
@@ -13,30 +16,33 @@ import collection.immutable.List
* <b>val</b> x1 = catching(classOf[MalformedURLException]) opt new URL(s)
* <b>val</b> x2 = catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s)
* </pre>
+ *
* @author Paul Phillips
*/
object Exception {
- // We get lots of crashes using this, so for now we just use Class[_]
- // type ExClass = Class[_ <: Throwable]
-
type Catcher[+T] = PartialFunction[Throwable, T]
- type ExceptionCatcher[+T] = PartialFunction[Exception, T]
-
- // due to the magic of contravariance, Throwable => T is a subtype of
- // Exception => T, not the other way around. So we manually construct
- // a Throwable => T and simply rethrow the non-Exceptions.
- implicit def fromExceptionCatcher[T](pf: ExceptionCatcher[T]): Catcher[T] = {
- new PartialFunction[Throwable, T] {
- def isDefinedAt(x: Throwable) = x match {
- case e: Exception if pf.isDefinedAt(e) => true
- case _ => false
- }
- def apply(x: Throwable) = x match {
- case e: Exception if pf.isDefinedAt(e) => pf(e)
- case e => throw e
- }
- }
+
+ def mkCatcher[Ex <: Throwable, T](isDef: Ex => Boolean, f: Ex => T) = new Catcher[T] {
+ private def downcast(x: Throwable): Option[Ex] =
+ if (x.isInstanceOf[Ex]) Some(x.asInstanceOf[Ex]) else None
+
+ def isDefinedAt(x: Throwable) = downcast(x) exists isDef
+ def apply(x: Throwable): T = f(downcast(x).get)
+ }
+ def mkThrowableCatcher[T](isDef: Throwable => Boolean, f: Throwable => T) = mkCatcher(isDef, f)
+
+ implicit def throwableSubtypeToCatcher[Ex <: Throwable, T](pf: PartialFunction[Ex, T]) =
+ mkCatcher(pf.isDefinedAt _, pf.apply _)
+
+ /** !!! Not at all sure of every factor which goes into this,
+ * and/or whether we need multiple standard variations.
+ */
+ def shouldRethrow(x: Throwable): Boolean = x match {
+ case _: ControlThrowable => true
+ case _: InterruptedException => true
+ // case _: java.lang.Error => true ?
+ case _ => false
}
trait Described {
@@ -58,30 +64,37 @@ object Exception {
def invoke(): Unit = { body }
}
- /** A container class for catch/finally logic. */
- class Catch[+T] private[Exception](
- private[Exception] val pf: Catcher[T],
- private[Exception] val fin: Option[Finally])
- extends Described
- {
- def this(pf: Catcher[T]) = this(pf, None)
+ /** A container class for catch/finally logic.
+ *
+ * Pass a different value for rethrow if you want to probably
+ * unwisely allow catching control exceptions and other throwables
+ * which the rest of the world may expect to get through.
+ */
+ class Catch[+T](
+ val pf: Catcher[T],
+ val fin: Option[Finally] = None,
+ val rethrow: Throwable => Boolean = shouldRethrow)
+ extends Described {
+
protected val name = "Catch"
/** Create a new Catch with additional exception handling logic. */
- def or[U >: T](pf2: Catcher[U]): Catch[U] = new Catch(pf orElse pf2, fin)
+ def or[U >: T](pf2: Catcher[U]): Catch[U] = new Catch(pf orElse pf2, fin, rethrow)
def or[U >: T](other: Catch[U]): Catch[U] = or(other.pf)
/** Apply this catch logic to the supplied body. */
- def apply[U >: T](body: => U): U = {
- try { body }
- catch { case e if pf.isDefinedAt(e) => pf(e) }
- finally { fin.map(_.invoke()) }
- }
+ def apply[U >: T](body: => U): U =
+ try body
+ catch {
+ case x if rethrow(x) => throw x
+ case x if pf isDefinedAt x => pf(x)
+ }
+ finally fin map (_.invoke())
/* Create an empty Try container with this Catch and the supplied Finally */
def andFinally(body: => Unit): Catch[T] = fin match {
- case None => new Catch(pf, Some(new Finally(body)))
- case Some(f) => new Catch(pf, Some(f and body))
+ case None => new Catch(pf, Some(new Finally(body)), rethrow)
+ case Some(f) => new Catch(pf, Some(f and body), rethrow)
}
/** Apply this catch logic to the supplied body, mapping the result
@@ -97,12 +110,12 @@ object Exception {
/** Create a new Catch with the same isDefinedAt logic as this one,
* but with the supplied apply method replacing the current one. */
- def withApply[U](f: (Throwable) => U): Catch[U] = {
- val pf2 = new PartialFunction[Throwable, U] {
+ def withApply[U](f: Throwable => U): Catch[U] = {
+ val pf2 = new Catcher[U] {
def isDefinedAt(x: Throwable) = pf isDefinedAt x
def apply(x: Throwable) = f(x)
}
- new Catch(pf2, fin)
+ new Catch(pf2, fin, rethrow)
}
/** Convenience methods. */
@@ -136,27 +149,37 @@ object Exception {
override def toString() = List("Try(<body>)", catcher.toString) mkString " "
}
- final val nothingCatcher: PartialFunction[Throwable, Nothing] =
- new PartialFunction[Throwable, Nothing] {
- def isDefinedAt(x: Throwable) = false
- def apply(x: Throwable) = throw x
- }
-
- /** A Catch object which catches everything. */
- final def allCatch[T]: Catch[T] = new Catch[T]({ case x => throw x }) withDesc "<everything>"
+ final val nothingCatcher: Catcher[Nothing] = mkThrowableCatcher(_ => false, throw _)
+ final def allCatcher[T]: Catcher[T] = mkThrowableCatcher(_ => true, throw _)
/** The empty Catch object. */
final val noCatch: Catch[Nothing] = new Catch(nothingCatcher) withDesc "<nothing>"
+ /** A Catch object which catches everything. */
+ final def allCatch[T]: Catch[T] = new Catch(allCatcher[T]) withDesc "<everything>"
+
/** Creates a Catch object which will catch any of the supplied exceptions.
- * Since the returned Catch object has no specific logic defined and will simply
- * rethrow the exceptions it catches, you will typically want to call "opt" or
- * "either" on the return value, or assign custom logic by calling "withApply".
- */
+ * Since the returned Catch object has no specific logic defined and will simply
+ * rethrow the exceptions it catches, you will typically want to call "opt" or
+ * "either" on the return value, or assign custom logic by calling "withApply".
+ *
+ * Note that Catch objects automatically rethrow ControlExceptions and others
+ * which should only be caught in exceptional circumstances. If you really want
+ * to catch exactly what you specify, use "catchingPromiscuously" instead.
+ */
def catching[T](exceptions: Class[_]*): Catch[T] =
- new Catch(pfFromExceptions(exceptions : _*)) withDesc exceptions.map(_.getName).mkString(", ")
+ new Catch(pfFromExceptions(exceptions : _*)) withDesc (exceptions map (_.getName) mkString ", ")
+
def catching[T](c: Catcher[T]): Catch[T] = new Catch(c)
+ /** Creates a Catch object which will catch any of the supplied exceptions.
+ * Unlike "catching" which filters out those in shouldRethrow, this one will
+ * catch whatever you ask of it: ControlThrowable, InterruptedException,
+ * OutOfMemoryError, you name it.
+ */
+ def catchingPromiscuously[T](exceptions: Class[_]*): Catch[T] = catchingPromiscuously(pfFromExceptions(exceptions : _*))
+ def catchingPromiscuously[T](c: Catcher[T]): Catch[T] = new Catch(c, None, _ => false)
+
/** Creates a Catch object which catches and ignores any of the supplied exceptions. */
def ignoring(exceptions: Class[_]*): Catch[Unit] =
catching(exceptions: _*) withApply (_ => ())
@@ -165,6 +188,10 @@ object Exception {
def failing[T](exceptions: Class[_]*): Catch[Option[T]] =
catching(exceptions: _*) withApply (_ => None)
+ /** Creates a Catch object which maps all the supplied exceptions to the given value. */
+ def failAsValue[T](exceptions: Class[_]*)(value: => T): Catch[T] =
+ catching(exceptions: _*) withApply (_ => value)
+
/** Returns a partially constructed Catch object, which you must give
* an exception handler function as an argument to "by". Example:
* handling(ex1, ex2) by (_.printStackTrace)
@@ -180,16 +207,6 @@ object Exception {
/** Returns a Catch object with no catch logic and the argument as Finally. */
def ultimately[T](body: => Unit): Catch[T] = noCatch andFinally body
- /** Experimental */
- def saving[A](oldVal: A, newVal: A, setter: (A) => Unit): Catch[Nothing] = {
- new Catch(nothingCatcher, Some(new Finally(setter(oldVal)))) {
- override def apply[U](body: => U): U = {
- setter(newVal)
- super.apply(body)
- }
- }
- }
-
/** Creates a Catch object which unwraps any of the supplied exceptions. */
def unwrapping[T](exceptions: Class[_]*): Catch[T] = {
def unwrap(x: Throwable): Throwable =