scala.util.control.Exception.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of scala-library Show documentation
Show all versions of scala-library Show documentation
Standard library for the SubScript extension of the Scala Programming Language
The newest version!
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2003-2013, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
package scala
package util
package control
import scala.collection.immutable.List
import scala.reflect.{ ClassTag, classTag }
import java.lang.reflect.InvocationTargetException
import scala.language.implicitConversions
/** Classes representing the components of exception handling.
* Each class is independently composable. Some example usages:
* {{{
* import scala.util.control.Exception._
* import java.net._
*
* val s = "http://www.scala-lang.org/"
* val x1 = catching(classOf[MalformedURLException]) opt new URL(s)
* val x2 = catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s)
* }}}
*
* This class differs from `scala.util.Try` in that it focuses on composing exception handlers rather than
* composing behavior. All behavior should be composed first and fed to a `Catch` object using one of the
* `opt` or `either` methods.
*
* @author Paul Phillips
*/
object Exception {
type Catcher[+T] = PartialFunction[Throwable, T]
def mkCatcher[Ex <: Throwable: ClassTag, T](isDef: Ex => Boolean, f: Ex => T) = new Catcher[T] {
private def downcast(x: Throwable): Option[Ex] =
if (classTag[Ex].runtimeClass.isAssignableFrom(x.getClass)) 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: ClassTag, 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 {
protected val name: String
private var _desc: String = ""
def desc = _desc
def withDesc(s: String): this.type = {
_desc = s
this
}
override def toString() = name + "(" + desc + ")"
}
/** A container class for finally code. */
class Finally private[Exception](body: => Unit) extends Described {
protected val name = "Finally"
def and(other: => Unit): Finally = new Finally({ body ; other })
def invoke() { body }
}
/** 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, 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 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)), rethrow)
case Some(f) => new Catch(pf, Some(f and body), rethrow)
}
/** Apply this catch logic to the supplied body, mapping the result
* into `Option[T]` - `None` if any exception was caught, `Some(T)` otherwise.
*/
def opt[U >: T](body: => U): Option[U] = toOption(Some(body))
/** Apply this catch logic to the supplied body, mapping the result
* into Either[Throwable, T] - Left(exception) if an exception was caught,
* Right(T) otherwise.
*/
def either[U >: T](body: => U): Either[Throwable, U] = toEither(Right(body))
/** Apply this catch logic to the supplied body, mapping the result
* into Try[T] - Failure if an exception was caught, Success(T) otherwise.
*/
def withTry[U >: T](body: => U): scala.util.Try[U] = toTry(Success(body))
/** Create a `Catch` object 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 Catcher[U] {
def isDefinedAt(x: Throwable) = pf isDefinedAt x
def apply(x: Throwable) = f(x)
}
new Catch(pf2, fin, rethrow)
}
/** Convenience methods. */
def toOption: Catch[Option[T]] = withApply(_ => None)
def toEither: Catch[Either[Throwable, T]] = withApply(Left(_))
def toTry: Catch[scala.util.Try[T]] = withApply(x => Failure(x))
}
final val nothingCatcher: Catcher[Nothing] = mkThrowableCatcher(_ => false, throw _)
final def nonFatalCatcher[T]: Catcher[T] = mkThrowableCatcher({ case NonFatal(_) => true; case _ => false }, throw _)
final def allCatcher[T]: Catcher[T] = mkThrowableCatcher(_ => true, throw _)
/** The empty `Catch` object. */
final val noCatch: Catch[Nothing] = new Catch(nothingCatcher) withDesc ""
/** A `Catch` object which catches everything. */
final def allCatch[T]: Catch[T] = new Catch(allCatcher[T]) withDesc ""
/** A `Catch` object witch catches non-fatal exceptions. */
final def nonFatalCatch[T]: Catch[T] = new Catch(nonFatalCatcher[T]) withDesc ""
/** 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".
*
* 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 ", ")
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 (_ => ())
/** Creates a `Catch` object which maps all the supplied exceptions to `None`. */
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)
* }}}
*/
class By[T,R](f: T => R) {
def by(x: T): R = f(x)
}
def handling[T](exceptions: Class[_]*) = {
def fun(f: Throwable => T) = catching(exceptions: _*) withApply f
new By[Throwable => T, Catch[T]](fun _)
}
/** Returns a `Catch` object with no catch logic and the argument as `Finally`. */
def ultimately[T](body: => Unit): Catch[T] = noCatch andFinally body
/** Creates a `Catch` object which unwraps any of the supplied exceptions. */
def unwrapping[T](exceptions: Class[_]*): Catch[T] = {
def unwrap(x: Throwable): Throwable =
if (wouldMatch(x, exceptions) && x.getCause != null) unwrap(x.getCause)
else x
catching(exceptions: _*) withApply (x => throw unwrap(x))
}
/** Private **/
private def wouldMatch(x: Throwable, classes: scala.collection.Seq[Class[_]]): Boolean =
classes exists (_ isAssignableFrom x.getClass)
private def pfFromExceptions(exceptions: Class[_]*): PartialFunction[Throwable, Nothing] =
{ case x if wouldMatch(x, exceptions) => throw x }
}