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

io.idml.jackson.JsonAstGenerator.scala Maven / Gradle / Ivy

package io.idml.jackson

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.databind.{JsonSerializer, ObjectMapper, SerializerProvider}
import io.idml.IdmlMapping
import io.idml.ast._
import io.idml.datanodes.{IBool, IDouble, IInt, IString}
import io.idml.functions.{ArrayFunction, IdmlFunction0, IdmlValueFunction, SetSizeFunction}
import org.json4s.jackson.Json4sScalaModule

/**
  * Provides functions for turning parsed IDML documents into JSON
  */
object JsonAstGenerator {
  val OM = new ObjectMapper()
  OM.registerModule(IdmlAstSerializers.ptolemyAstModule)
  OM.registerModule(Json4sScalaModule)

  /**
    * Turn a parsed IdmlMapping into a JSON string
    * @param p
    * @return
    */
  def generateJson(p: IdmlMapping): String = {
    OM.writeValueAsString(p)
  }

  /**
    * Implicit extension to IdmlMapping to provide .toJson
    * Usage:
    *  import io.idml.jackson.JsonAstGenerator._
    *  ...
    *  ptolemy.fromString("foo = bar").toJson
    * @param m mapping to wrap
    */
  implicit class SerializableIdmlMapping(m: IdmlMapping) {
    def toJson: String = generateJson(m)
  }
}

/**
  * Implements serializes that are capable of turning a parsed IDML document into a JSON representation
  */
object IdmlAstSerializers {
  val ptolemyAstModule: SimpleModule = new SimpleModule("IdmlAst")

  class IdmlMappingSerializer extends JsonSerializer[IdmlMapping] {
    override def serialize(m: IdmlMapping, jgen: JsonGenerator, sp: SerializerProvider): Unit = {
      m.nodes.blocks.filter(_._1 == "main").foreach { kv =>
        val (name, block) = kv
        jgen.writeObject(block)
      }
    }
    override def handledType(): Class[IdmlMapping] = classOf[IdmlMapping]
  }
  ptolemyAstModule.addSerializer(new IdmlAstSerializers.IdmlMappingSerializer)

  class IdmlBlockSerializer extends JsonSerializer[Block] {
    override def serialize(b: Block, jgen: JsonGenerator, sp: SerializerProvider): Unit = {
      jgen.writeStartArray()
      b.rules.foreach { r =>
        jgen.writeStartObject()
        r match {
          case re: Reassignment =>
            jgen.writeObjectField("operation", "reassignment")
            jgen.writeObjectField("destination", re.dest.mkString("."))
            jgen.writeObject(re.exps)
          case as: Assignment =>
            jgen.writeObjectField("operation", "assignment")
            jgen.writeObjectField("destination", as.dest.mkString("."))
            jgen.writeObject(as.exps)
        }
        jgen.writeEndObject()
      }
      jgen.writeEndArray()
    }
    override def handledType(): Class[Block] = classOf[Block]
  }
  ptolemyAstModule.addSerializer(new IdmlAstSerializers.IdmlBlockSerializer)

  class IdmlPipelineSerializer extends JsonSerializer[Pipeline] {
    private val TYPEFUNCTIONS =
      Set("int", "string", "geo", "bool", "date", "url")

    private def findFunction(p: Pipeline): Option[String] = {
      p.exps
        .find(_.isInstanceOf[IdmlFunction])
        .map(_.asInstanceOf[IdmlFunction].name)
    }

    override def serialize(p: Pipeline, jgen: JsonGenerator, sp: SerializerProvider): Unit = {
      p.exps.foreach {
        case vf: IdmlValueFunction if TYPEFUNCTIONS.contains(vf.name) =>
          jgen.writeObjectField("type", vf.name)
        case vf: IdmlFunction0 if TYPEFUNCTIONS.contains(vf.name) =>
          jgen.writeObjectField("type", vf.name)
        case vf: IdmlValueFunction =>
          ptolemyValueFunction(jgen, sp, vf)
        case ssf: SetSizeFunction => // the size function doesn't work like anything else, so it's got a special case
          jgen.writeObjectField(ssf.name, ssf.arg)
        case af: ArrayFunction => // the array function takes another function so it needs to be a special case too
          arrayFunction(jgen, af)
        case enl: ExecNavLiteral =>
          execNavLiteral(jgen, enl)
        case _ =>
          throw new Exception("Unsupported pipeline type for this serializer")
      }
    }

    private def ptolemyValueFunction(jgen: JsonGenerator, sp: SerializerProvider, vf: IdmlValueFunction): Unit = {
      vf.args.length match {
        case l: Any if l == 0 =>
          jgen.writeObjectField(vf.name, true)
        case l: Any if l == 1 =>
          jgen.writeFieldName(vf.name)
          serialize(vf.args.head, jgen, sp)
        case l: Any if l > 1 =>
          jgen.writeArrayFieldStart(vf.name)
          vf.args.foreach(serialize(_, jgen, sp))
          jgen.writeEndArray()
      }
    }

    private def arrayFunction(jgen: JsonGenerator, af: ArrayFunction): Unit = {
      af.expr match {
        case p: Pipeline =>
          jgen.writeObjectField("type", "array(%s)".format(findFunction(p).getOrElse("unknown")))
      }
    }

    private def execNavLiteral(jgen: JsonGenerator, enl: ExecNavLiteral): Unit = {
      enl.literal.value match {
        case i: IInt =>
          jgen.writeObject(i.value)
        case s: IString =>
          jgen.writeObject(s.value)
        case b: IBool =>
          jgen.writeObject(b.value)
        case d: IDouble =>
          jgen.writeObject(d.value)
        case _ =>
          throw new scala.Exception("Unsupported literal used inside JSON AST")
      }
    }

    override def handledType(): Class[Pipeline] = classOf[Pipeline]
  }

  ptolemyAstModule.addSerializer(new IdmlAstSerializers.IdmlPipelineSerializer)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy