Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import scala.reflect._
- sealed trait TransformResult[+T]
- case class TransformContext(path: List[String]) {
- def append(p: String) = TransformContext(p::path)
- override def toString = path.reverse.mkString(".")
- }
- object TransformContext {
- val empty = TransformContext(List())
- }
- case class Success[+T](v: T) extends TransformResult[T]
- case class Failure(context: TransformContext, message: String) extends TransformResult[Nothing]
- trait Transform[+T] {
- def run(context: TransformContext, current: Any): TransformResult[T]
- def flatMap[U](uf: (T) => Transform[U]) = Transform.flatMap(this, uf)
- def map[U](m: T => U) = Transform.map(this, m)
- def and[U](u: Transform[U]) = Transform.and(this, u)
- def named(name: String) = Transform.named(this, name)
- def optional = Transform.optional(this)
- def transform(m: Map[String, Any]): TransformResult[T] = run(TransformContext.empty, m)
- }
- object Implicits {
- implicit class ImplicitFlattenTransform[T](val t: Transform[Transform[T]]) extends AnyVal {
- def flatten = Transform.flatten(t)
- }
- implicit class ImplicitOrTransform[T](val f: Transform[T]) extends AnyVal {
- def or(s: Transform[T]) = Transform.or(f, s)
- }
- implicit class ImplicitApplyTransform[T, U](val f: Transform[T => U]) extends AnyVal {
- def apply(t: Transform[T]) = Transform.apply(f, t)
- }
- }
- object Transform {
- def create[T](m: (TransformContext, Any) => TransformResult[T]) = new Transform[T] {
- def run(context: TransformContext, current: Any): TransformResult[T] = {
- m(context, current)
- }
- }
- def value[T](v: T) = new Transform[T] {
- def run(context: TransformContext, current: Any): TransformResult[T] = {
- Success(v)
- }
- }
- def failWith(message: String) = new Transform[Nothing] {
- def run(context: TransformContext, current: Any): TransformResult[Nothing] = {
- Failure(context, message)
- }
- }
- def flatMap[T, U](t: Transform[T], uf: (T) => Transform[U]) = new Transform[U] {
- def run(context: TransformContext, current: Any): TransformResult[U] = {
- t.run(context, current) match {
- case Success(tv) =>
- val u = uf(tv)
- u.run(context, current)
- case Failure(tctx, tmsg) => Failure(tctx, tmsg)
- }
- }
- }
- def flatten[T](t: Transform[Transform[T]]) = t.flatMap(tv => tv)
- def apply[T, U](f: Transform[T => U], t: Transform[T]) = f.flatMap(fv => t.flatMap(tv => Transform.value(fv(tv))))
- def map[T, U](t: Transform[T], m: T => U) = t.flatMap(tv => value(m(tv)))
- def and[T, U](t: Transform[T], u: Transform[U]) = t.flatMap(tv => u.flatMap(uv => value((tv, uv))))
- def or[T](f: Transform[T], s: Transform[T]) = new Transform[T] {
- def run(context: TransformContext, current: Any): TransformResult[T] = {
- f.run(context, current) match {
- case Success(tv) => Success(tv)
- case Failure(tctx, tmsg) => s.run(context, current)
- }
- }
- }
- def optional[T](t: Transform[T]) = new Transform[Option[T]] {
- def run(context: TransformContext, current: Any): TransformResult[Option[T]] = {
- t.run(context, current) match {
- case Success(tv) => Success(Some(tv))
- case Failure(_, _) => Success(None)
- }
- }
- }
- def named[T](t: Transform[T], name: String) = new Transform[T] {
- def run(context: TransformContext, current: Any): TransformResult[T] = {
- current match {
- case null => Failure(context, "current is null")
- case m : Map[String,_] =>
- m.get(name) match {
- case Some(next) =>
- val nctx = context.append(name)
- t.run(nctx, next)
- case None => Failure(context, s"Map does not contain key: $name")
- }
- case _ => Failure(context, "current is not a Map")
- }
- }
- }
- def cast[T : ClassTag] = new Transform[T] {
- def run(context: TransformContext, current: Any): TransformResult[T] = {
- current match {
- case tv : T => Success(tv)
- case _ => Failure(context, s"Cannot cast current to T") // TODO: Improve
- }
- }
- }
- val asString = new Transform[String] {
- def run(context: TransformContext, current: Any): TransformResult[String] = {
- current match {
- case null => Success("")
- case s : String => Success(s)
- case v => Success(v.toString)
- }
- }
- }
- val asNumber = new Transform[Number] {
- def run(context: TransformContext, current: Any): TransformResult[Number] = {
- current match {
- case null => Success(0)
- case n : Number => Success(n)
- case _ => Failure(context, "Not a number")
- }
- }
- }
- }
- case class MyInnerModel(xx: Either[Int, String])
- case class MyModel(x: Number, y: String,z: MyInnerModel)
- import Implicits._
- object Program {
- def main(args: Array[String]): Unit = {
- def intVal(v: Int): Either[Int, String] = Left(v)
- def stringVal(v: String): Either[Int, String] = Right(v)
- def string(key: String) = Transform.asString.named(key)
- def number(key: String) = Transform.asNumber.named(key)
- def either(key: String) = Transform.cast[Int].map(intVal(_))
- .or(Transform.cast[String].map(stringVal(_)))
- .or(Transform.failWith("Expected either int or string"))
- .named(key)
- def map(kvs: (String, Any)*) = Map(kvs:_*)
- val tmyInnermodel = either("xx")
- .map(MyInnerModel(_))
- val tmyModel1 = Transform
- .value(MyModel.curried)
- .apply(number("x"))
- .apply(string("y"))
- .apply(tmyInnermodel.named("z"))
- val tmyModel3 = Transform
- .value(MyModel.curried)
- .apply(number("x"))
- .apply(string("y"))
- .apply(Transform.value(MyInnerModel(Left(0))))
- val versions = Map(1 -> tmyModel1, 3 -> tmyModel3)
- def tmyModel = Transform.cast[Int].flatMap { v =>
- versions.get(v) match {
- case Some(vv) => Transform.value(vv)
- case None => Transform.failWith(s"Unrecognized version: $v")
- }
- }.named("v").flatten
- def test(map: Map[String, Any]) = {
- val result = tmyModel.transform(map)
- println(map)
- println(result)
- }
- test(map(
- "v" -> 1,
- "x" -> 1,
- "y" -> "a",
- "z" -> map("xx" -> 11)))
- test(map(
- "v" -> 1,
- "x" -> 1,
- "y" -> "a",
- "z" -> map("xx" -> "XX")))
- test(map(
- "v" -> 1,
- "x" -> 1,
- "y" -> "a",
- "z" -> map("xx" -> true)))
- test(map(
- "v" -> 1,
- "x" -> 1,
- "y" -> "a",
- "z" -> map()))
- test(map(
- "v" -> 1,
- "x" -> 1,
- "y" -> "a",
- "z" -> 1))
- test(map(
- "v" -> 1,
- "x" -> 1,
- "y" -> "a"))
- test(map(
- "v" -> 1,
- "x" -> "a",
- "y" -> "a"))
- test(map(
- "v" -> 2))
- test(map(
- "v" -> ""))
- test(map(
- "v" -> 3,
- "x" -> 1,
- "y" -> "a"))
- test(map())
- }
- }
Add Comment
Please, Sign In to add comment