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

com.rojoma.json.v3.io.JsonEvent.scala Maven / Gradle / Ivy

The newest version!
package com.rojoma.json.v3
package io

sealed abstract class JsonEvent {
  val position: Position

  def unpositioned: JsonEvent // This is abstract so evt.unpositioned can retain its type
  def positionedAt(newPos: Position): JsonEvent
}

case class StartOfObjectEvent()(val position: Position) extends JsonEvent {
  def unpositioned: JsonEvent = positionedAt(Position.Invalid)
  def positionedAt(newPos: Position) = StartOfObjectEvent()(newPos)
}

case class EndOfObjectEvent()(val position: Position) extends JsonEvent {
  def unpositioned: JsonEvent = positionedAt(Position.Invalid)
  def positionedAt(newPos: Position) = EndOfObjectEvent()(newPos)
}

case class StartOfArrayEvent()(val position: Position) extends JsonEvent {
  def unpositioned: JsonEvent = positionedAt(Position.Invalid)
  def positionedAt(newPos: Position) = StartOfArrayEvent()(newPos)
}

case class EndOfArrayEvent()(val position: Position) extends JsonEvent {
  def unpositioned: JsonEvent = positionedAt(Position.Invalid)
  def positionedAt(newPos: Position) = EndOfArrayEvent()(newPos)
}

case class FieldEvent(name: String)(val position: Position) extends JsonEvent {
  def unpositioned: JsonEvent = positionedAt(Position.Invalid)
  def positionedAt(newPos: Position) = FieldEvent(name)(newPos)
}

case class IdentifierEvent(text: String)(val position: Position) extends JsonEvent {
  def unpositioned: JsonEvent = positionedAt(Position.Invalid)
  def positionedAt(newPos: Position) = IdentifierEvent(text)(newPos)
  def isValid = ReaderUtils.isValidIdentifier(text)
}

case class NumberEvent(number: String)(val position: Position) extends JsonEvent {
  def unpositioned: JsonEvent = positionedAt(Position.Invalid)
  def positionedAt(newPos: Position) = NumberEvent(number)(newPos)
  def isValid = ReaderUtils.isValidNumber(number)
}

case class StringEvent(string: String)(val position: Position) extends JsonEvent {
  def unpositioned: JsonEvent = positionedAt(Position.Invalid)
  def positionedAt(newPos: Position) = StringEvent(string)(newPos)
}

object JsonEvent {
  import ast._
  import codec._

  private val StartOfObjectTag = JString("start of object")
  private val EndOfObjectTag = JString("end of object")
  private val StartOfArrayTag = JString("start of array")
  private val EndOfArrayTag = JString("end of array")
  private val FieldTag = JString("field")
  private val IdentifierTag = JString("identifier")
  private val NumberTag = JString("number")
  private val StringTag = JString("string")

  val sansPositionCodec: JsonEncode[JsonEvent] with JsonDecode[JsonEvent] = new JsonEncode[JsonEvent] with JsonDecode[JsonEvent] {
    private val Type = "type"
    private val Value = "value"

    def encode(e: JsonEvent) = e match {
      case StartOfObjectEvent() => JObject(Map(Type -> StartOfObjectTag))
      case EndOfObjectEvent() => JObject(Map(Type -> EndOfObjectTag))
      case StartOfArrayEvent() => JObject(Map(Type -> StartOfArrayTag))
      case EndOfArrayEvent() => JObject(Map(Type -> EndOfArrayTag))
      case FieldEvent(name) => JObject(Map(Type -> FieldTag, Value -> JString(name)))
      case IdentifierEvent(name) => JObject(Map(Type -> IdentifierTag, Value -> JString(name)))
      case NumberEvent(num) => JObject(Map(Type -> NumberTag, Value -> JString(num)))
      case StringEvent(str) => JObject(Map(Type -> StringTag, Value -> JString(str)))
    }

    private def extractValue[T](o: JObject, wrap: String => T)(validate: T => Boolean = Function.const(true) _) =
      o.get(Value) match {
        case Some(js@JString(s)) =>
          val res = wrap(s)
          if(validate(res)) Right(res)
          else Left(DecodeError.InvalidValue(js, Path(Value)))
        case Some(other) => Left(DecodeError.InvalidType(expected = JString, got = other.jsonType, path = Path(Value)))
        case None => Left(DecodeError.MissingField(Value))
      }

    def decode(x: JValue) = x match {
      case o: JObject =>
        o.get(Type) match {
          case Some(StartOfObjectTag) => Right(StartOfObjectEvent()(Position.Invalid))
          case Some(EndOfObjectTag) => Right(EndOfObjectEvent()(Position.Invalid))
          case Some(StartOfArrayTag) => Right(StartOfArrayEvent()(Position.Invalid))
          case Some(EndOfArrayTag) => Right(EndOfArrayEvent()(Position.Invalid))
          case Some(FieldTag) => extractValue(o, FieldEvent(_)(Position.Invalid))()
          case Some(IdentifierTag) => extractValue(o, IdentifierEvent(_)(Position.Invalid))(_.isValid)
          case Some(NumberTag) => extractValue(o, NumberEvent(_)(Position.Invalid))(_.isValid)
          case Some(StringTag) => extractValue(o, StringEvent(_)(Position.Invalid))()
          case Some(other : JString) => Left(DecodeError.InvalidValue(other, Path(Type)))
          case Some(other) => Left(DecodeError.InvalidType(expected = JString, got = other.jsonType, path = Path(Type)))
          case None => Left(DecodeError.MissingField(Type))
        }
      case other =>
        Left(DecodeError.InvalidType(expected = JObject, got = other.jsonType))
    }
  }

  implicit val jCodec: JsonEncode[JsonEvent] with JsonDecode[JsonEvent] = new JsonEncode[JsonEvent] with JsonDecode[JsonEvent] {
    private val Pos = "position"

    def encode(e: JsonEvent) = {
      val JObject(fields) = sansPositionCodec.encode(e)
      JObject(fields + (Pos -> JsonEncode.toJValue(e.position)))
    }

    def decode(x: JValue) = x match {
      case obj: JObject =>
        sansPositionCodec.decode(obj).right.flatMap { token =>
          obj.get(Pos) match {
            case Some(jsonPos) =>
              JsonDecode.fromJValue[Position](jsonPos) match {
                case Right(pos) => Right(token.positionedAt(pos))
                case Left(err) => Left(err.prefix(Pos))
              }
            case None => // no position is OK
              Right(token)
          }
        }
      case other =>
        Left(DecodeError.InvalidType(expected = JObject, got = other.jsonType))
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy