alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (Pickler.scala)

This example Scala source code file (Pickler.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Scala tags/keywords

condpickler, io, lexer, lexer, pickler, pickler, s, t, t, t2, u, unpickled, unpickled, unpicklesuccess, writer

The Scala Pickler.scala source code

package scala.tools.nsc.io

import annotation.unchecked
import Lexer._
import java.io.Writer

/** An abstract class for writing and reading Scala objects to and
 *  from a legible representation. The presesentation follows the following grammar:
 *  {{{
 *  Pickled = `true' | `false' | `null' | NumericLit | StringLit |
 *            Labelled | Pickled `,' Pickled
 *  Labelled = StringLit `(' Pickled? `)'
 *  }}}
 *
 *  All ...Lit classes are as in JSON. @see scala.tools.nsc.io.Lexer
 *
 *  Subclasses of `Pickler` each can write and read individual classes
 *  of values.
 *
 *  @param  T   the type of values handled by this pickler.
 *
 *  These Picklers build on the work of Andrew Kennedy. They are most closely inspired by
 *  Iulian Dragos' picklers for Scala to XML. See:
 * 
 *  <a href="http://code.google.com/p/gdata-scala-client/wiki/DevelopersGuide">
 *  http://code.google.com/p/gdata-scala-client/wiki/DevelopersGuide
 *  </a>
 */
abstract class Pickler[T] {

  import Pickler._

  /** Writes value in pickled form
   *  @param  wr   the writer to which pickled form is written
   *  @param  x    the value to write
   */
  def pickle(wr: Writer, x: T)

  /** Reads value from pickled form. 
   * 
   *  @param  rd   the lexer from which lexemes are read
   *  @return An `UnpickleSuccess value if the current input corresponds to the
   *          kind of value that is unpickled by the current subclass of `Pickler`,
   *          an `UnpickleFailure` value otherwise.
   *  @throws  `Lexer.MalformedInput` if input is invalid, or if
   *          an `Unpickle
   */
  def unpickle(rd: Lexer): Unpickled[T]

  /** A pickler representing a `~`-pair of values as two consecutive pickled
   *  strings, separated by a comma.
   *  @param  that   the second pickler which together with the current pickler makes
   *                 up the pair `this ~ that` to be pickled.
   */
  def ~ [U] (that: => Pickler[U]): Pickler[T ~ U] = seqPickler(this, that)

  /** A pickler that adds a label to the current pickler, using the representation
   *   `label ( <current pickler> )`
   *  
   *  @label  the string to be added as a label.
   */
  def labelled(label: String): Pickler[T] = labelledPickler(label, this)

  /** A pickler obtained from the current pickler by a pair of transformer functions
   *  @param   in   the function that maps values handled by the current pickler to
   *                values handled by the wrapped pickler.
   *  @param   out  the function that maps values handled by the wrapped pickler to
   *                values handled by the current pickler.
   */
  def wrapped [U] (in: T => U)(out: U => T): Pickler[U] = wrappedPickler(this)(in)(out)

  /** A pickler obtained from the current pickler by also admitting `null` as
   *  a handled value, represented as the token `null`.
   *
   *  @param  fromNull    an implicit evidence parameter ensuring that the type of values
   *                      handled by this pickler contains `null`.
   */
  def orNull(implicit fromNull: Null <:< T): Pickler[T] = nullablePickler(this)

  /** A conditional pickler obtained from the current pickler.
   *  @param   cond   the condition to test to find out whether pickler can handle
   *                  some Scala value.
   */
  def cond(p: Any => Boolean): CondPickler[T] = conditionalPickler(this, p)

  /** A conditional pickler handling values of some Scala class. It adds the
   *  class name as a label to the representation of the current pickler and
   *  @param    c     the class of values handled by this pickler.
   */
  def asClass[U <: T](c: Class[U]): CondPickler[T] = this.labelled(c.getName).cond(c isInstance _)
}

object Pickler {

  var picklerDebugMode = false

  /** A base class representing unpickler result. It has two subclasses:
   *  `UnpickleSucess` for successful unpicklings and `UnpickleFailure` for failures,
   *  where a value of the given type `T` could not be unpickled from input.
   *  @param  T the type of unpickled values in case of success.
   */
  abstract class Unpickled[+T] {
    /** Transforms success values to success values using given function,
     *  leaves failures alone
     *  @param   f the function to apply.
     */
    def map[U](f: T => U): Unpickled[U] = this match {
      case UnpickleSuccess(x) => UnpickleSuccess(f(x))
      case f: UnpickleFailure => f
    }
    /** Transforms success values to successes or failures using given function,
     *  leaves failures alone.
     *  @param   f the function to apply.
     */
    def flatMap[U](f: T => Unpickled[U]): Unpickled[U] = this match {
      case UnpickleSuccess(x) => f(x)
      case f: UnpickleFailure => f
    }
    /** Tries alternate expression if current result is a failure
     *  @param alt  the alternate expression to be tried in case of failure
     */
    def orElse[U >: T](alt: => Unpickled[U]): Unpickled[U] = this match {
      case UnpickleSuccess(x) => this
      case f: UnpickleFailure => alt
    }
    
    /** Transforms failures into thrown `MalformedInput` exceptions.
     *  @throws  MalformedInput   if current result is a failure
     */
    def requireSuccess: UnpickleSuccess[T] = this match {
      case s @ UnpickleSuccess(x) => s
      case f: UnpickleFailure => 
        throw new MalformedInput(f.rd, "Unrecoverable unpickle failure:\n"+f.errMsg)
    }
  }

  /** A class representing successful unpicklings
   *  @param T        the type of the unpickled value
   *  @param result   the unpickled value
   */
  case class UnpickleSuccess[+T](result: T) extends Unpickled[T]

  /** A class representing unpickle failures
   *  @param msg      an error message describing what failed.
   *  @param rd       the lexer unpickled values were read from (can be used to get
   *                  error position, for instance).
   */
  class UnpickleFailure(msg: => String, val rd: Lexer) extends Unpickled[Nothing] {
    def errMsg = msg
    override def toString = "Failure at "+rd.tokenPos+":\n"+msg
  }

  private def errorExpected(rd: Lexer, msg: => String) = 
    new UnpickleFailure("expected: "+msg+"\n" + 
                        "found   : "+rd.token,
                        rd)

  private def nextSuccess[T](rd: Lexer, result: T) = {
    rd.nextToken()
    UnpickleSuccess(result)
  }

  /** The implicit `Pickler` value for type `T`. Equivalent to `implicitly[Pickler[T]]`.
   */
  def pkl[T: Pickler] = implicitly[Pickler[T]]

  /** A class represenenting `~`-pairs */
  case class ~[S, T](fst: S, snd: T)

  /** A wrapper class to be able to use `~` s an infix method */
  class TildeDecorator[S](x: S) {
    /** Infix method that forms a `~`-pair. */
    def ~ [T](y: T): S ~ T = new ~ (x, y)
  }

  /** An implicit wrapper that adds `~` as a method to any value. */
  implicit def tildeDecorator[S](x: S): TildeDecorator[S] = new TildeDecorator(x)

  /** A converter from binary functions to functions over `~`-pairs
   */
  implicit def fromTilde[T1, T2, R](f: (T1, T2) => R): T1 ~ T2 => R = { case x1 ~ x2 => f(x1, x2) }

  /** An converter from unctions returning Options over pair to functions returning `~`-pairs
   *  The converted function will raise a `MatchError` where the original function returned
   *  a `None`. This converter is useful for turning `unapply` methods of case classes
   *  into wrapper methods that can be passed as second argument to `wrap`.
   */
  implicit def toTilde[T1, T2, S](f: S => Option[(T1, T2)]): S => T1 ~ T2 = { x => (f(x): @unchecked) match { case Some((x1, x2)) => x1 ~ x2 } }
  
  /** Same as `p.labelled(label)`.
   */
  def labelledPickler[T](label: String, p: Pickler[T]): Pickler[T] = new Pickler[T] {
    def pickle(wr: Writer, x: T) = {
      wr.write(quoted(label)); 
      wr.write("(")
      p.pickle(wr, x)
      wr.write(")")
    }
    def unpickle(rd: Lexer): Unpickled[T] = 
      rd.token match {
        case StringLit(`label`) =>
          rd.nextToken() 
          rd.accept('(')
          val result = p.unpickle(rd).requireSuccess
          rd.accept(')')
          result
        case _ =>
          errorExpected(rd, quoted(label)+"(...)")
      }
  }

  /** Same as `p.wrap(in)(out)`
   */
  def wrappedPickler[S, T](p: Pickler[S])(in: S => T)(out: T => S) = new Pickler[T] {
    def pickle(wr: Writer, x: T) = p.pickle(wr, out(x))
    def unpickle(rd: Lexer) = p.unpickle(rd) map in
  }

  /** Same as `p.cond(condition)`
   */
  def conditionalPickler[T](p: Pickler[T], condition: Any => Boolean) = new CondPickler[T](condition) {
    def pickle(wr: Writer, x: T) = p.pickle(wr, x)
    def unpickle(rd: Lexer) = p.unpickle(rd)
  }    

  /** Same as `p ~ q`
   */
  def seqPickler[T, U](p: Pickler[T], q: => Pickler[U]) = new Pickler[T ~ U] {
    lazy val qq = q
    def pickle(wr: Writer, x: T ~ U) = {
      p.pickle(wr, x.fst)
      wr.write(',')
      q.pickle(wr, x.snd)
    }
    def unpickle(rd: Lexer) = 
      for (x <- p.unpickle(rd); y <- { rd.accept(','); qq.unpickle(rd).requireSuccess }) 
      yield x ~ y
  }

  /** Same as `p | q`
   */
  def eitherPickler[T, U <: T, V <: T](p: CondPickler[U], q: => CondPickler[V]) = 
    new CondPickler[T](x => p.canPickle(x) || q.canPickle(x)) {
      lazy val qq = q
      override def tryPickle(wr: Writer, x: Any): Boolean = 
        p.tryPickle(wr, x) || qq.tryPickle(wr, x)
      def pickle(wr: Writer, x: T) = 
        require(tryPickle(wr, x), 
                "no pickler found for "+x+" of class "+x.asInstanceOf[AnyRef].getClass.getName)
      def unpickle(rd: Lexer) = p.unpickle(rd) orElse qq.unpickle(rd)
    }

  /** Same as `p.orNull`
   */
  def nullablePickler[T](p: Pickler[T])(implicit fromNull: Null <:< T): Pickler[T] = new Pickler[T] {
    def pickle(wr: Writer, x: T) = 
      if (x == null) wr.write("null") else p.pickle(wr, x)
    def unpickle(rd: Lexer): Unpickled[T] = 
      if (rd.token == NullLit) nextSuccess(rd, fromNull(null))
      else p.unpickle(rd)
  }

  /** A conditional pickler for singleton objects. It represents these
   *  with the object's underlying class as a label.
   *  Example: Object scala.None would be represented as `scala.None$()`.
   */
  def singletonPickler[T <: AnyRef](x: T): CondPickler[T] = 
    unitPickler
      .wrapped { _ => x } { x => () } 
      .labelled (x.getClass.getName) 
      .cond (x eq _.asInstanceOf[AnyRef])

  /** A pickler the handles instances of classes that have an empty constructor.
   *  It represents than as `$new ( <name of class> )`.
   *  When unpickling, a new instance of the class is created using the empty
   *  constructor of the class via `Class.forName(<name of class>).newInstance()`.
   */
  def javaInstancePickler[T <: AnyRef]: Pickler[T] =
    (stringPickler labelled "$new")
      .wrapped { name => Class.forName(name).newInstance().asInstanceOf[T] } { _.getClass.getName }

  /** A picklers that handles iterators. It pickles all values
   *  returned by an iterator separated by commas.
   *  When unpickling, it always returns an `UnpickleSuccess` containing an iterator.
   *  This iterator returns 0 or more values that are obtained by unpickling
   *  until a closing parenthesis, bracket or brace or the end of input is encountered.
   *
   *  This means that iterator picklers should not be directly followed by `~`
   *  because the pickler would also read any values belonging to the second
   *  part of the `~`-pair.
   *
   *  What's usually done instead is that the iterator pickler is wrapped and labelled
   *  to handle other kinds of sequences.
   */
  implicit def iterPickler[T: Pickler]: Pickler[Iterator[T]] = new Pickler[Iterator[T]] {
    lazy val p = pkl[T]
    def pickle(wr: Writer, xs: Iterator[T]) {
      var first = true
      for (x <- xs) {
        if (first) first = false else wr.write(',')
        p.pickle(wr, x)
      }
    }
    def unpickle(rd: Lexer): Unpickled[Iterator[T]] = UnpickleSuccess(new Iterator[T] {
      var first = true
      def hasNext = {
        val t = rd.token
        t != EOF && t != RParen && t != RBrace && t != RBracket
      }
      def next(): T = {
        if (first) first = false else rd.accept(',')
        p.unpickle(rd).requireSuccess.result
      }
    })
  }
    
  /** A pickler that handles values that can be represented as a single token.
   *  @param   kind   the kind of token representing the value, used in error messages
   *                  for unpickling.
   *  @param  matcher A partial function from tokens to handled values. Unpickling
   *                  succeeds if the matcher function is defined on the current token.
   */
  private def tokenPickler[T](kind: String)(matcher: PartialFunction[Token, T]) = new Pickler[T] {
    def pickle(wr: Writer, x: T) = wr.write(x.toString)
    def unpickle(rd: Lexer) = 
      if (matcher isDefinedAt rd.token) nextSuccess(rd, matcher(rd.token))
      else errorExpected(rd, kind)
  }

  /** A pickler for values of type `Long`, represented as integer literals */
  implicit val longPickler: Pickler[Long] = 
    tokenPickler("integer literal") { case IntLit(s) => s.toLong }
  
  /** A pickler for values of type `Double`, represented as floating point literals */
  implicit val doublePickler: Pickler[Double] = 
    tokenPickler("floating point literal") { case FloatLit(s) => s.toDouble }
  
  /** A pickler for values of type `Byte`, represented as integer literals */
  implicit val bytePickler: Pickler[Byte] = longPickler.wrapped { _.toByte } { _.toLong }

  /** A pickler for values of type `Short`, represented as integer literals */
  implicit val shortPickler: Pickler[Short] = longPickler.wrapped { _.toShort } { _.toLong }

  /** A pickler for values of type `Int`, represented as integer literals */
  implicit val intPickler: Pickler[Int] = longPickler.wrapped { _.toInt } { _.toLong }

  /** A pickler for values of type `Float`, represented as floating point literals */
  implicit val floatPickler: Pickler[Float] = doublePickler.wrapped { _.toFloat } { _.toLong }

  /** A conditional pickler for the boolean value `true` */
  private val truePickler = 
    tokenPickler("boolean literal") { case TrueLit => true } cond { _ == true }
 
  /** A conditional pickler for the boolean value `false` */
  private val falsePickler = 
    tokenPickler("boolean literal") { case FalseLit => false } cond { _ == false }
  
  /** A pickler for values of type `Boolean`, represented as the literals `true` or `false`. */
  implicit def booleanPickler: Pickler[Boolean] = truePickler | falsePickler

  /** A pickler for values of type `Unit`, represented by the empty character string */
  implicit val unitPickler: Pickler[Unit] = new Pickler[Unit] {
    def pickle(wr: Writer, x: Unit) {}
    def unpickle(rd: Lexer): Unpickled[Unit] = UnpickleSuccess(())
  }

  /** A pickler for values of type `String`, represented as string literals */
  implicit val stringPickler: Pickler[String] = new Pickler[String] {
    def pickle(wr: Writer, x: String) = wr.write(if (x == null) "null" else quoted(x))
    def unpickle(rd: Lexer) = rd.token match {
      case StringLit(s) => nextSuccess(rd, s)
      case NullLit => nextSuccess(rd, null)
      case _ => errorExpected(rd, "string literal")
    }
  }

  /** A pickler for values of type `Char`, represented as string literals of length 1 */
  implicit val charPickler: Pickler[Char] = 
    stringPickler
      .wrapped { s => require(s.length == 1, "single character string literal expected, but "+quoted(s)+" found"); s(0) } { _.toString }

  /** A pickler for pairs, represented as `~`-pairs */
  implicit def tuple2Pickler[T1: Pickler, T2: Pickler]: Pickler[(T1, T2)] = 
    (pkl[T1] ~ pkl[T2])
      .wrapped { case x1 ~ x2 => (x1, x2) } { case (x1, x2) => x1 ~ x2 } 
      .labelled ("tuple2")

  /** A pickler for 3-tuples, represented as `~`-tuples */
  implicit def tuple3Pickler[T1, T2, T3](implicit p1: Pickler[T1], p2: Pickler[T2], p3: Pickler[T3]): Pickler[(T1, T2, T3)] = 
    (p1 ~ p2 ~ p3)
      .wrapped { case x1 ~ x2 ~ x3 => (x1, x2, x3) } { case (x1, x2, x3) => x1 ~ x2 ~ x3 } 
      .labelled ("tuple3")

  /** A pickler for 4-tuples, represented as `~`-tuples */
  implicit def tuple4Pickler[T1, T2, T3, T4](implicit p1: Pickler[T1], p2: Pickler[T2], p3: Pickler[T3], p4: Pickler[T4]): Pickler[(T1, T2, T3, T4)] = 
    (p1 ~ p2 ~ p3 ~ p4)
      .wrapped { case x1 ~ x2 ~ x3 ~ x4 => (x1, x2, x3, x4) } { case (x1, x2, x3, x4) => x1 ~ x2 ~ x3 ~ x4 } 
      .labelled ("tuple4")

  /** A conditional pickler for the `scala.None` object */
  implicit val nonePickler = singletonPickler(None)

  /** A conditional pickler for instances of class `scala.Some` */
  implicit def somePickler[T: Pickler]: CondPickler[Some[T]] = 
    pkl[T]
      .wrapped { Some(_) } { _.get }
      .asClass (classOf[Some[T]])

  /** A pickler for optional values */
  implicit def optionPickler[T: Pickler]: Pickler[Option[T]] = nonePickler | somePickler[T]
                                                                
  /** A pickler for list values */
  implicit def listPickler[T: Pickler]: Pickler[List[T]] = 
    iterPickler[T] .wrapped { _.toList } { _.iterator } .labelled ("scala.List")
                                                                
  /** A pickler for vector values */
  implicit def vectorPickler[T: Pickler]: Pickler[Vector[T]] = 
    iterPickler[T] .wrapped { Vector() ++ _ } { _.iterator } .labelled ("scala.Vector")

  /** A pickler for array values */
  implicit def array[T : ClassManifest : Pickler]: Pickler[Array[T]] = 
    iterPickler[T] .wrapped { _.toArray} { _.iterator } .labelled ("scala.Array")
}

/** A subclass of Pickler can indicate whether a particular value can be pickled by instances
 *  of this class.
 *  @param canPickle   The predicate that indicates whether a given value
 *                     can be pickled by instances of this class.
 */
abstract class CondPickler[T](val canPickle: Any => Boolean) extends Pickler[T] {
  import Pickler._

  /** Pickles given value `x` if possible, as indicated by `canPickle(x)`.
   */
  def tryPickle(wr: Writer, x: Any): Boolean = {
    val result = canPickle(x)
    if (result) pickle(wr, x.asInstanceOf[T])
    result
  }

  /** A pickler obtained from this pickler and an alternative pickler.
   *  To pickle a value, this pickler is tried first. If it cannot handle
   *  the object (as indicated by its `canPickle` test), then the
   *  alternative pickler is tried.
   *  To unpickle a value, this unpickler is tried first. If it cannot read
   *  the input (as indicated by a `UnpickleFailure` result), then the
   *  alternative pickler is tried.
   *  @param V    The handled type of the returned pickler.
   *  @param U    The handled type of the alternative pickler.
   *  @param that The alternative pickler.
   */
  def | [V >: T, U <: V] (that: => CondPickler[U]): CondPickler[V] = 
    eitherPickler[V, T, U](this, that)
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala Pickler.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.