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

net.liftweb.common.Box.scala Maven / Gradle / Ivy

There is a newer version: 2.0
Show newest version
/*
 * Copyright 2007-2010 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.liftweb {
package common {

import _root_.scala.reflect.Manifest

/**
 * The Box companion object provides methods to create a Box from:
 * 
    *
  • an Option
  • *
  • a List
  • *
  • any AnyRef object
  • *
* * It also provides implicit methods to transform Option to Box, Box to Iterable, and Box to Option */ object Box { /** * Create a Box from the specified Option. * @return a Box created from an Option. Full(x) if the Option is Some(x) and Empty otherwise */ def apply[T](in: Option[T]) = in match { case Some(x) => Full(x) case _ => Empty } /** * Create a Box from the specified Option. * @return a Box created from a Box. Full(x) if the Box is Full(x) and * not null * Empty otherwise */ def apply[T](in: Box[T]) = in match { case Full(x) => legacyNullTest(x) case x: EmptyBox => x case _ => Empty } /** * Transform a List with zero or one elements to a Box. * @return a Box object containing the head of a List. Full(x) if the List contains at least one element and Empty otherwise. */ def apply[T](in: List[T]) = in match { case x :: _ => Full(x) case _ => Empty } /** * This method allows one to encapsulate any object in a Box in a null-safe manner, * treating null values to Empty. This is a parallel method to * the Scala Option's apply method. * * @return Full(in) if in is not null; Empty otherwise */ def apply[T](in: T): Box[T] = legacyNullTest(in) /** * Apply the specified PartialFunction to the specified value and return the result * in a Full Box; if the pf is undefined at that point return Empty. * @param pf the partial function to use to transform the value * @param value the value to transform * @return a Full box containing the transformed value if pf.isDefinedAt(value); Empty otherwise */ def apply[InType, OutType](pf: PartialFunction[InType, OutType])(value: InType): Box[OutType] = if (pf.isDefinedAt(value)) Full(pf(value)) else Empty /** * Apply the specified PartialFunction to the specified value and return the result * in a Full Box; if the pf is undefined at that point return Empty. * @param pf the partial function to use to transform the value * @param value the value to transform * @return a Full box containing the transformed value if pf.isDefinedAt(value); Empty otherwise */ def apply[InType, OutType](value: InType)(pf: PartialFunction[InType, OutType]): Box[OutType] = if (pf.isDefinedAt(value)) Full(pf(value)) else Empty /** * This implicit transformation allows one to use a Box as an Iterable * @return List(in) if this Box is Full(in); Nil otherwise */ implicit def box2Iterable[T](in: Box[T]): Iterable[T] = in.toList /** * This implicit transformation allows one to use an Option as a Box. * @return a Box object from an Option. Full(in) if the Option is Some(in); Empty otherwise */ implicit def option2Box[T](in: Option[T]): Box[T] = Box(in) /** * This implicit transformation allows one to use a Box as an Option. * @return Some(in) if this Box is Full(in); None otherwise */ implicit def box2Option[T](in: Box[T]): Option[T] = in.toOption /** * This method allows one to encapsulate any object in a Box in a null-safe manner, * treating null values to Empty * @return Full(in) if in is not null; Empty otherwise */ def legacyNullTest[T](in: T): Box[T] = in match { case null => Empty case _ => Full(in) } /** * Alias for legacyNullTest. * This method allows one to encapsulate any object in a Box in a null-safe manner, * returning Empty if the specified value is null. * @return Full(in) if in is not null Empty otherwise */ def !![T](in: T): Box[T] = legacyNullTest(in) /** * Create a Full box containing the specified value if "in" is an instance * of the specified class, or Empty otherwise. */ def isA[A, B](in: A, clz: Class[B]): Box[B] = (Box !! in).isA(clz) /** * Create a Full box containing the specified value if in is of * type B; Empty otherwise. */ def asA[B](in: T forSome {type T})(implicit m: Manifest[B]): Box[B] = (Box !! in).asA[B] } /** * The Box class is a container which is able to declare if it is Full (containing a single non-null value) or Empty. * It serves the same purpose as the Option class from Scala standard library but adds several features: *
    *
  • you can transform it to a Failure object if it is Empty (with the ?~ method)
  • *
  • you can chain failure messages on Failure Boxes
  • *
  • you "run" a function on your Box, with a default value: Full(1).run("zero") { (x: String, y: Int) => y.toString }
  • *
  • you can "pass" a Box to a function for side effects: Full(1) $ { x: Box[Int] => println(x openOr 0) }
  • *
*/ @serializable sealed abstract class Box[+A] extends Product { self => /** * Returns true if this Box contains no value (is Empty or Failure) * @return true if this Box contains no value */ def isEmpty: Boolean /** * Returns true if the box contains a value. * @return true if this Box contains a value */ def isDefined: Boolean = !isEmpty /** * Return the value contained in this Box if it is full; throw an exception otherwise * @return the value contained in this Box if it is full; throw an exception otherwise */ def open_! : A /** * Return the value contained in this Box if it is full; otherwise return the specified default * @return the value contained in this Box if it is full; otherwise return the specified default */ def openOr[B >: A](default: => B): B = default /** * Apply a function to the value contained in this Box if it exists and return * a new Box containing the result, or Empty otherwise. * @return the modified Box or Empty */ def map[B](f: A => B): Box[B] = Empty /** * Apply a function returning a Box to the value contained in this Box if it exists * and return the result, or Empty otherwise. * @return the modified Box or Empty */ def flatMap[B](f: A => Box[B]): Box[B] = Empty /** * Return this Box if it contains a value satisfying the specified predicate; Empty otherwise * @return this Box if it contains a value satisfying the specified predicate; Empty otherwise */ def filter(p: A => Boolean): Box[A] = this /** * Makes Box play better with Scala 2.8 for comprehensions */ def withFilter(p: A => Boolean): WithFilter = new WithFilter(p) /** * Play NiceLike with the Scala 2.8 for comprehension */ class WithFilter(p: A => Boolean) { def map[B](f: A => B): Box[B] = self.filter(p).map(f) def flatMap[B](f: A => Box[B]): Box[B] = self.filter(p).flatMap(f) def foreach[U](f: A => U): Unit = self.filter(p).foreach(f) def withFilter(q: A => Boolean): WithFilter = new WithFilter(x => p(x) && q(x)) } /** * Determine whether this Box contains a value which satisfies the specified predicate * @return true if this Box's value satisfies the specified predicate */ def exists(func: A => Boolean): Boolean = false /** * Perform a side effect by calling the specified function * with the value contained in this box. */ def foreach[U](f: A => U): Unit = {} /** * Return a Full[B] if the contents of this Box is an instance of the specified class, * otherwise return Empty */ def isA[B](cls: Class[B]): Box[B] = Empty /** * If the partial function is defined at the current Box's value * apply the partial function. */ def collect[B](pf: PartialFunction[A, B]): Box[B] /** * Return a Full[B] if the contents of this Box is of type B, otherwise return Empty */ def asA[B](implicit m: Manifest[B]): Box[B] = Empty /** * Return this Box if Full, or the specified alternative if this is Empty */ def or[B >: A](alternative: => Box[B]): Box[B] = alternative /** * Returns an Iterator over the value contained in this Box */ def elements: Iterator[A] = Iterator.empty /** * Returns an Iterator over the value contained in this Box */ def iterator: Iterator[A] = this.elements /** * Returns a List of one element if this is Full, or an empty list if Empty. */ def toList: List[A] = Nil /** * Returns the contents of this box in an Option if this is Full, or * None if this is a failure or Empty. */ def toOption: Option[A] = None /** * Transform an Empty to a Failure with the specified message. * @param msg the failure message * @return a Failure with the message if this Box is Empty */ def ?~(msg: String): Box[A] = this /** * Transform an Empty to a ParamFailure with the specified typesafe * parameter. * @param errorCode a value indicating the error * @return a ParamFailure with the specified value */ def ~>[T](errorCode: T): Box[A] = this /** * Alias for ?~ */ def failMsg(msg: String): Box[A] = ?~(msg) /** * Transform an Empty to a Failure with the specified message and chain * the new Failure to any previous Failure represented by this Box. * @param msg the failure message * @return a Failure with the message if this Box is an Empty Box. Chain the messages if it is already a Failure */ def ?~!(msg: String): Box[A] = ?~(msg) /** * Alias for ?~! */ def compoundFailMsg(msg: String): Box[A] = ?~!(msg) /** * Filter this box on the specified predicate, returning a Failure with the specified * message if the predicate is not satisfied. * @param msg the failure message * @param p a predicate * @return a Failure with the message if the predicate is not satisfied by the value contained in this Box */ def filterMsg(msg: String)(p: A => Boolean): Box[A] = filter(p) ?~ msg /** * This method calls the specified function with the value contained in this Box * @return the result of the function or a default value */ def run[T](in: T)(f: (T, A) => T) = in /** * Perform a side effect by passing this Box to the specified function * and return this Box unmodified. * @return this Box */ def pass(f: Box[A] => Unit): Box[A] = {f(this) ; this} /** * Alias for pass */ def $(f: Box[A] => Unit): Box[A] = pass(f) /** * Determines equality based upon the contents of this Box instead of the box itself. * For Full and Empty, this has the expected behavior. Equality in terms of Failure * checks for equivalence of failure causes. */ override def equals(other: Any): Boolean = (this, other) match { case (Full(x), Full(y)) => x == y case (Full(x), y) => x == y case (x, y: AnyRef) => x eq y case _ => false } /** * Apply the function f1 to the contents of this Box if available; if this * is Empty return the specified alternative. */ def choice[B](f1: A => Box[B])(alternative: => Box[B]): Box[B] = this match { case Full(x) => f1(x) case _ => alternative } /** * Returns true if the value contained in this box is equal to the specified value. */ def ===[B >: A](to: B): Boolean = false /** * Equivalent to map(f).or(Full(dflt)).open_! */ def dmap[B](dflt: => B)(f: A => B): B = dflt /** * An Either that is a Left with the given argument * left if this is empty, or a Right if this * Full with the Box's value. */ def toRight[B](left: => B): Either[B, A] = Left(left) /** * An Either that is a Right with the given * argument * right if this is empty, or a Left if this is * Fill with the Box's value */ def toLeft[B](right: => B): Either[A, B] = Right(right) } /** * Full is a Box containing a value. */ @serializable final case class Full[+A](value: A) extends Box[A] { def isEmpty: Boolean = false def open_! : A = value override def openOr[B >: A](default: => B): B = value override def or[B >: A](alternative: => Box[B]): Box[B] = this override def exists(func: A => Boolean): Boolean = func(value) override def filter(p: A => Boolean): Box[A] = if (p(value)) this else Empty override def foreach[U](f: A => U): Unit = f(value) override def map[B](f: A => B): Box[B] = Full(f(value)) override def flatMap[B](f: A => Box[B]): Box[B] = f(value) override def elements: Iterator[A] = Iterator.fromValues(value) override def toList: List[A] = List(value) override def toOption: Option[A] = Some(value) override def run[T](in: T)(f: (T, A) => T) = f(in, value) /** * An Either that is a Left with the given argument * left if this is empty, or a Right if this * Full with the Box's value. */ override def toRight[B](left: => B): Either[B, A] = Right(value) /** * An Either that is a Right with the given * argument * right if this is empty, or a Left if this is * Fill with the Box's value */ override def toLeft[B](right: => B): Either[A, B] = Left(value) override def isA[B](cls: Class[B]): Box[B] = value match { case value: AnyRef => if (cls.isAssignableFrom(value.getClass)) Full(value.asInstanceOf[B]) else Empty case _ => Empty } override def asA[B](implicit m: Manifest[B]): Box[B] = this.isA(m.erasure).asInstanceOf[Box[B]] override def ===[B >: A](to: B): Boolean = value == to override def dmap[B](dflt: => B)(f: A => B): B = f(value) /** * If the partial function is defined at the current Box's value * apply the partial function. */ final def collect[B](pf: PartialFunction[A, B]): Box[B] = { if (pf.isDefinedAt(value)) Full(pf(value)) else Empty } } /** * Singleton object representing an Empty Box */ @serializable case object Empty extends EmptyBox /** * The EmptyBox is a Box containing no value. */ @serializable sealed abstract class EmptyBox extends Box[Nothing] { def isEmpty: Boolean = true def open_! = throw new NullPointerException("Trying to open an empty Box") override def openOr[B >: Nothing](default: => B): B = default override def or[B >: Nothing](alternative: => Box[B]): Box[B] = alternative override def filter(p: Nothing => Boolean): Box[Nothing] = this override def ?~(msg: String) = Failure(msg, Empty, Empty) /** * If the partial function is defined at the current Box's value * apply the partial function. */ final override def collect[B](pf: PartialFunction[Nothing, B]): Box[B] = this } /** * Companion object used to simplify the creation of a simple Failure. */ object Failure { def apply(msg: String) = new Failure(msg, Empty, Empty) } /** * A Failure is an Empty with an additional failure message explaining the reason for its being empty. * It can also optionally provide an exception or a chain of causes represented as a list of other Failure objects */ @serializable sealed case class Failure(msg: String, exception: Box[Throwable], chain: Box[Failure]) extends EmptyBox { type A = Nothing override def open_! = throw new NullPointerException("Trying to open a Failure Box: " + msg) { override def getCause() = exception openOr null } override def ?~(msg: String) = this override def ?~!(msg: String) = Failure(msg, Empty, Full(this)) override def map[B](f: A => B): Box[B] = this override def flatMap[B](f: A => Box[B]): Box[B] = this override def isA[B](cls: Class[B]): Box[B] = this override def asA[B](implicit m: Manifest[B]): Box[B] = this private def chainList: List[Failure] = chain match { case Full(f) => f :: f.chainList case _ => Nil } def messageChain: String = (this :: chainList).map(_.msg).mkString(" <- ") override def equals(other: Any): Boolean = (this, other) match { case (Failure(x, y, z), Failure(x1, y1, z1)) => (x, y, z) == (x1, y1, z1) case (x, y: AnyRef) => x eq y case _ => false } override def ~>[T](errorCode: T): ParamFailure[T] = ParamFailure(msg, exception, chain, errorCode) } /** * A ParamFailure is a Failure with an additional typesafe parameter that can * allow an application to store other information related to the failure. */ @serializable final class ParamFailure[T](override val msg: String, override val exception: Box[Throwable], override val chain: Box[Failure], val param: T) extends Failure(msg, exception, chain) object ParamFailure { def apply[T](msg: String, exception: Box[Throwable], chain: Box[Failure], param: T) = new ParamFailure(msg, exception, chain, param) def apply[T](msg: String, param: T) = new ParamFailure(msg, Empty, Empty, param) def unapply(in: Box[_]): Option[(String, Box[Throwable], Box[Failure], Any)] = in match { case pf: ParamFailure[_] => Some((pf.msg, pf.exception, pf.chain, pf.param)) case _ => None } } } } // vim: set ts=2 sw=2 et:




© 2015 - 2024 Weber Informatics LLC | Privacy Policy