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

org.scalameter.persistence.json.scalaMeterModule.scala Maven / Gradle / Ivy

The newest version!
package org.scalameter
package persistence.json



import com.fasterxml.jackson.core.{JsonToken, JsonParser, JsonGenerator}
import com.fasterxml.jackson.databind._
import com.fasterxml.jackson.databind.deser.Deserializers
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.jsontype.{TypeSerializer, TypeDeserializer}
import com.fasterxml.jackson.databind.Module.SetupContext
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.ser.Serializers
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import com.fasterxml.jackson.databind.`type`.MapLikeType
import org.scalameter.picklers.Pickler
import scala.collection.immutable



object MeasurementSerializer
extends StdSerializer[Measurement[_]](classOf[Measurement[_]]) {
  def serialize(
    value: Measurement[_], jgen: JsonGenerator, provider: SerializerProvider
  ): Unit = {
    val pickler = value.pickler.asInstanceOf[Pickler[Any]]

    jgen.writeStartObject()

    jgen.writeStringField("@pickler", value.pickler.getClass.getName)

    jgen.writeBinaryField("value", pickler.pickle(value.value))

    jgen.writeFieldName("params")
    provider.findValueSerializer(classOf[Parameters])
      .serialize(value.params, jgen, provider)

    jgen.writeObjectFieldStart("data")
    jgen.writeArrayFieldStart("complete")
    value.data.complete.foreach(v => jgen.writeBinary(pickler.pickle(v)))
    jgen.writeEndArray()
    jgen.writeBooleanField("success", value.data.success)
    jgen.writeEndObject()

    jgen.writeStringField("units", value.units)

    jgen.writeEndObject()
  }
}


object MeasurementDeserializer extends StdDeserializer[Measurement[_]](
  classOf[Measurement[_]]) {
  def deserialize(p: JsonParser, ctx: DeserializationContext): Measurement[_] = {
    def getField(name: String): Option[JsonToken] = {
      Option(p.nextToken())
        .filter(_ == JsonToken.FIELD_NAME && p.getCurrentName == name)
    }

    val clazz = classOf[Measurement[_]]
    val measurement = for {
      s <- Option(p.getCurrentToken) if s == JsonToken.START_OBJECT

      _ <- getField("@pickler")
      pickler <- Option {
        p.nextToken()
        Pickler.makeInstance[Any](Class.forName(p.getValueAsString))
      }

      _ <- getField("value")
      value <- Option {
        p.nextToken()
        pickler.unpickle(p.getBinaryValue)
      }

      _ <- getField("params")
      params <- Option {
        p.nextToken()
        val paramsType = ctx.getTypeFactory.constructType(classOf[Parameters])
        ctx.findContextualValueDeserializer(paramsType, null)
          .deserialize(p, ctx).asInstanceOf[Parameters]
      }

      _ <- getField("data")
      ds <- Option(p.nextToken()) if ds == JsonToken.START_OBJECT
      _ <- getField("complete")
      complete <- Option {
        val builder = Seq.newBuilder[Any]
        if (p.nextToken() != JsonToken.START_ARRAY) throw ctx.mappingException(clazz)
        p.nextToken()
        while (p.getCurrentToken != JsonToken.END_ARRAY && p.getCurrentToken != null) {
          builder += pickler.unpickle(p.getBinaryValue)
          p.nextToken()
        }
        if (p.getCurrentToken != JsonToken.END_ARRAY) throw ctx.mappingException(clazz)
        builder.result()
      }
      _ <- getField("success")
      success <- Option {
        p.nextToken()
        p.getBooleanValue
      }
      de <- Option(p.nextToken()) if de == JsonToken.END_OBJECT

      _ <- getField("units")
      units <- Option {
        p.nextToken()
        p.getValueAsString
      }

      e <- Option(p.nextToken()) if e == JsonToken.END_OBJECT
    } yield {
      Measurement(value, params, MeasurementData(complete, success), units)(pickler)
    }

    measurement.getOrElse(throw ctx.mappingException(clazz))
  }
}

/** Serializer for maps with keys whose are subtype of
 *  [[org.scalameter.PicklerBasedKey]].
 *
 *  It serializes map values as Base64 encoded byte arrays.
 *
 * @tparam MapKey subtype of [[org.scalameter.PicklerBasedKey]]
 * @param clazz handled type
 */
class PicklerBasedMapSerializer[MapKey <: PicklerBasedKey[_]](
  clazz: Class[immutable.Map[MapKey, Any]]
) extends StdSerializer[immutable.Map[MapKey, Any]](clazz) {
  def serialize(
    value: immutable.Map[MapKey, Any], jgen: JsonGenerator,
    provider: SerializerProvider
  ): Unit = {
    jgen.writeStartObject()
    value.foreach { case (k, v) =>
      if (!k.isTransient) {
        jgen.writeFieldName(k.repr)
        jgen.writeBinary(k.pickler.asInstanceOf[Pickler[Any]].pickle(v))
      }
    }
    jgen.writeEndObject()
  }
}

/** Deserializer for maps with keys whose are subtype of
 *  [[org.scalameter.PicklerBasedKey]].
 *
 * @tparam MapKey subtype of a [[org.scalameter.PicklerBasedKey]]
 * @param clazz handled type
 * @param keyCreator function used to create a concrete [[MapKey]]
 *                   from `fullName` and [[org.scalameter.picklers.Pickler]]
 */
class PicklerBasedMapDeserializer[MapKey <: PicklerBasedKey[_]](
  clazz: Class[immutable.Map[MapKey, Any]], keyCreator: (String, Pickler[_]) => MapKey)
  extends StdDeserializer[immutable.Map[MapKey, Any]](clazz) {
  def deserialize(
    p: JsonParser, ctx: DeserializationContext
  ): immutable.Map[MapKey, Any] = {
    if (p.getCurrentToken != JsonToken.START_OBJECT) throw ctx.mappingException(clazz)

    val builder = immutable.Map.newBuilder[MapKey, Any]
    while (p.nextToken() == JsonToken.FIELD_NAME) {

      val key = PicklerBasedKey.fromString(p.getCurrentName, keyCreator)
      val pickler = key.pickler
      val value = p.nextToken match {
        case JsonToken.VALUE_STRING => pickler.unpickle(p.getBinaryValue)
        case _ => throw ctx.mappingException(clazz)
      }
      builder += (key -> value)
    }

    builder.result()
  }
}

/** Serializer resolver used for precise selection of a PicklerBasedMapSerializer.
 */
object PicklerBasedMapSerializerResolver extends Serializers.Base {
  private val allowedTypes: Map[Class[_], JsonSerializer[_]] = Map(
    classOf[Key[_]] -> new PicklerBasedMapSerializer[Key[_]](
      classOf[immutable.Map[Key[_], Any]]).asInstanceOf[JsonSerializer[_]],
    classOf[Parameter[_]] -> new PicklerBasedMapSerializer[Parameter[_]](
      classOf[immutable.Map[Parameter[_], Any]]).asInstanceOf[JsonSerializer[_]]
  )

  override def findMapLikeSerializer(
    config: SerializationConfig,
    tpe: MapLikeType,
    beanDesc: BeanDescription,
    keySerializer: JsonSerializer[AnyRef],
    elementTypeSerializer: TypeSerializer,
    elementValueSerializer: JsonSerializer[AnyRef]
  ): JsonSerializer[_] = {
    val keyClazz = tpe.getKeyType.getRawClass
    if (classOf[immutable.Map[_, _]].isAssignableFrom(tpe.getRawClass) &&
      allowedTypes.contains(keyClazz)) allowedTypes(keyClazz)
    else null
  }
}

/** Deserializer resolver used for precise selection of a PicklerBasedMapDeserializer.
 */
object PicklerBasedMapDeserializerResolver extends Deserializers.Base {
  private val allowedTypes: Map[Class[_], JsonDeserializer[_]] = Map(
    classOf[Key[_]] -> new PicklerBasedMapDeserializer[Key[_]](
      // we ignore pickler for Key since it's reconstructed using key registry
      classOf[immutable.Map[Key[_], Any]],
      (name, _) => Key.parseKey(name)
    ).asInstanceOf[JsonDeserializer[_]],
    classOf[Parameter[_]] -> new PicklerBasedMapDeserializer[Parameter[_]](
      classOf[immutable.Map[Parameter[_], Any]],
      (name, pickler) => Parameter(name)(pickler)
    ).asInstanceOf[JsonDeserializer[_]]
  )

  override def findMapLikeDeserializer(
    tpe: MapLikeType,
    config: DeserializationConfig,
    beanDesc: BeanDescription,
    keyDeserializer: KeyDeserializer,
    elementTypeDeserializer: TypeDeserializer,
    elementDeserializer: JsonDeserializer[_]
  ): JsonDeserializer[_] = {
    val keyClazz = tpe.getKeyType.getRawClass
    if (classOf[immutable.Map[_, _]].isAssignableFrom(tpe.getRawClass) &&
      allowedTypes.contains(keyClazz)) allowedTypes(keyClazz)
    else null
  }
}

object ScalaMeterModule extends SimpleModule {
  addSerializer(classOf[Measurement[_]], MeasurementSerializer)
  addDeserializer(classOf[Measurement[_]], MeasurementDeserializer)

  override def setupModule(context: SetupContext): Unit = {
    context.addSerializers(PicklerBasedMapSerializerResolver)
    context.addDeserializers(PicklerBasedMapDeserializerResolver)
    super.setupModule(context)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy