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

msgpack4z.ArgonautMsgpack.scala Maven / Gradle / Ivy

The newest version!
package msgpack4z

import argonaut._
import argonaut.Json.JsonArray
import scalaz.{-\/, \/, \/-}

object ArgonautMsgpack {

  def jsonCodec(options: ArgonautUnpackOptions): MsgpackCodec[Json] =
    new CodecArgonautJson(options)

  def jsonArrayCodec(options: ArgonautUnpackOptions): MsgpackCodec[JsonArray] =
    new CodecArgonautJsonArray(options)

  def jsonObjectCodec(options: ArgonautUnpackOptions): MsgpackCodec[JsonObject] =
    new CodecArgonautJsonObject(options)

  def allCodec(options: ArgonautUnpackOptions): (MsgpackCodec[Json], MsgpackCodec[JsonArray], MsgpackCodec[JsonObject]) = (
    jsonCodec(options),
    jsonArrayCodec(options),
    jsonObjectCodec(options)
  )

  def jsonObject2msgpack(packer: MsgPacker, obj: JsonObject): Unit = {
    val fields = obj.toList
    packer.packMapHeader(fields.size)
    fields.foreach { field =>
      packer.packString(field._1)
      json2msgpack(packer, field._2)
    }
    packer.mapEnd()
  }

  def jsonArray2msgpack(packer: MsgPacker, array: JsonArray): Unit = {
    packer.packArrayHeader(array.size)
    array.foreach { x =>
      json2msgpack(packer, x)
    }
    packer.arrayEnd()
  }

  def json2msgpack(packer: MsgPacker, json: Json): Unit = {
    json.fold(
      jsonNull = {
        packer.packNil()
      },
      jsonBool = value => {
        packer.packBoolean(value)
      },
      jsonNumber = {
        case JsonDecimal(value) =>
          packer.packDouble(java.lang.Double.parseDouble(value))
        case JsonBigDecimal(value) =>
          packer.packDouble(value.toDouble)
        case JsonLong(value) =>
          packer.packLong(value)
        case JsonDouble(value) =>
          packer.packDouble(value)
      },
      jsonString = string => {
        packer.packString(string)
      },
      jsonArray = array => {
        jsonArray2msgpack(packer, array)
      },
      jsonObject = obj => {
        jsonObject2msgpack(packer, obj)
      }
    )
  }

  def msgpack2json(unpacker: MsgUnpacker, unpackOptions: ArgonautUnpackOptions): UnpackResult[Json] = {
    val result = Result.empty[Json]
    if (msgpack2json0(unpacker, result, unpackOptions)) {
      \/-(result.value)
    } else {
      -\/(result.error)
    }
  }

  def msgpack2jsonObject(unpacker: MsgUnpacker, unpackOptions: ArgonautUnpackOptions): UnpackResult[JsonObject] = {
    val result = Result.empty[JsonObject]
    if (msgpack2jsObj0(unpacker, result, unpackOptions)) {
      \/-(result.value)
    } else {
      -\/(result.error)
    }
  }

  def msgpack2jsonArray(unpacker: MsgUnpacker, unpackOptions: ArgonautUnpackOptions): UnpackResult[JsonArray] = {
    val result = Result.empty[JsonArray]
    if (msgpack2jsArray0(unpacker, result, unpackOptions)) {
      \/-(result.value)
    } else {
      -\/(result.error)
    }
  }

  private[this] final case class Result[A](
    var value: A, var error: UnpackError
  )
  private[this] object Result {
    def fromEither[A](e: UnpackError \/ A, result: Result[A]): Boolean = e match{
      case \/-(r) =>
        result.value = r
        true
      case -\/(l) =>
        result.error = l
        false
    }

    def empty[A >: Null]: Result[A] = Result[A](null, null)
  }

  private[this] def msgpack2jsObj0(unpacker: MsgUnpacker, result: Result[JsonObject], unpackOptions: ArgonautUnpackOptions): Boolean = {
    val size = unpacker.unpackMapHeader()
    var obj = JsonObject.empty
    var i = 0
    val mapElem = Result.empty[Json]
    var success = true

    def process(key: String): Unit = {
     if (msgpack2json0(unpacker, mapElem, unpackOptions)) {
       obj += (key, mapElem.value)
       i += 1
     } else {
       result.error = mapElem.error
       success = false
     }
    }

    while (i < size && success) {
      val tpe = unpacker.nextType()
      if(tpe == MsgType.STRING) {
        process(unpacker.unpackString())
      }else{
        unpackOptions.nonStringKey(tpe, unpacker) match {
          case Some(key) =>
            process(key)
          case None =>
            success = false
            result.error = Other("not string key")
        }
      }
    }
    unpacker.mapEnd()
    if (success) {
      result.value = obj
    }
    success
  }

  private[this] def msgpack2jsArray0(unpacker: MsgUnpacker, result: Result[JsonArray], unpackOptions: ArgonautUnpackOptions): Boolean = {
    val size = unpacker.unpackArrayHeader()
    val array = new Array[Json](size)
    var i = 0
    val arrayElem = Result[Json](null, null)
    var success = true
    while (i < size && success) {
      if (msgpack2json0(unpacker, arrayElem, unpackOptions)) {
        array(i) = arrayElem.value
        i += 1
      } else {
        result.error = arrayElem.error
        success = false
      }
    }
    unpacker.arrayEnd()
    if (success) {
      result.value = array.toList
    }
    success
  }

  private[this] val BigIntegerLongMax = java.math.BigInteger.valueOf(Long.MaxValue)
  private[this] val BigIntegerLongMin = java.math.BigInteger.valueOf(Long.MinValue)

  private def isValidLong(value: java.math.BigInteger): Boolean =
    (BigIntegerLongMin.compareTo(value) <= 0) && (value.compareTo(BigIntegerLongMax) <= 0)

  private[msgpack4z] def msgpack2json0(unpacker: MsgUnpacker, result: Result[Json], unpackOptions: ArgonautUnpackOptions): Boolean = {
    unpacker.nextType match {
      case MsgType.NIL =>
        unpacker.unpackNil()
        result.value = Json.jNull
        true
      case MsgType.BOOLEAN =>
        if (unpacker.unpackBoolean()) {
          result.value = Json.jTrue
        } else {
          result.value = Json.jFalse
        }
        true
      case MsgType.INTEGER =>
        val value = unpacker.unpackBigInteger()
        if(isValidLong(value)){
          result.value = Json.jNumber(JsonLong(value.longValue()))
        }else{
          result.value = Json.jNumber(JsonBigDecimal(BigDecimal(value)))
        }
        true
      case MsgType.FLOAT =>
        val f = unpacker.unpackDouble()
        if(f.isPosInfinity){
          Result.fromEither(unpackOptions.positiveInf, result)
        }else if(f.isNegInfinity){
          Result.fromEither(unpackOptions.negativeInf, result)
        }else if(java.lang.Double.isNaN(f)) {
          Result.fromEither(unpackOptions.nan, result)
        }else{
          result.value = Json.jNumber(JsonDouble(f))
        }
        true
      case MsgType.STRING =>
        result.value = Json.jString(unpacker.unpackString())
        true
      case MsgType.ARRAY =>
        val result0 = Result.empty[JsonArray]
        val r = msgpack2jsArray0(unpacker, result0, unpackOptions)
        result.error = result0.error
        result.value = Json.jArray(result0.value)
        r
      case MsgType.MAP =>
        val result0 = Result.empty[JsonObject]
        val r = msgpack2jsObj0(unpacker, result0, unpackOptions)
        result.error = result0.error
        result.value = Json.jObject(result0.value)
        r
      case MsgType.BINARY =>
        Result.fromEither(unpackOptions.binary(unpacker), result)
      case MsgType.EXTENSION =>
        Result.fromEither(unpackOptions.extension(unpacker), result)
    }
  }
}


private final class CodecArgonautJsonArray(unpackOptions: ArgonautUnpackOptions) extends MsgpackCodecConstant[JsonArray](
  ArgonautMsgpack.jsonArray2msgpack,
  unpacker => ArgonautMsgpack.msgpack2jsonArray(unpacker, unpackOptions)
)

private final class CodecArgonautJson(unpackOptions: ArgonautUnpackOptions) extends MsgpackCodecConstant[Json](
  ArgonautMsgpack.json2msgpack,
  unpacker => ArgonautMsgpack.msgpack2json(unpacker, unpackOptions)
)

private final class CodecArgonautJsonObject(unpackOptions: ArgonautUnpackOptions) extends MsgpackCodecConstant[JsonObject](
  ArgonautMsgpack.jsonObject2msgpack,
  unpacker => ArgonautMsgpack.msgpack2jsonObject(unpacker, unpackOptions)
)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy