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

basis.form.JsonVariant.scala Maven / Gradle / Ivy

//      ____              ___
//     / __ | ___  ____  /__/___      A library of building blocks
//    / __  / __ |/ ___|/  / ___|
//   / /_/ / /_/ /\__ \/  /\__ \      (c) 2012-2015 Chris Sachs
//  |_____/\_____\____/__/\____/      http://basis.reify.it

package basis.form

import basis._
import basis.collections._
import basis.text._

trait JsonVariant extends Variant { variant =>
  override type AnyForm    <: JsonValue
  override type ObjectForm <: JsonObject with AnyForm
  override type SeqForm    <: JsonSeq with AnyForm
  override type SetForm    <: JsonSet with AnyForm
  override type TextForm   <: JsonText with AnyForm
  override type DataForm   <: JsonData with AnyForm
  override type NumberForm <: JsonNumber with AnyForm
  override type DateForm   <: JsonDate with AnyForm
  override type BoolForm   <: JsonBool with AnyForm
  override type NullForm   <: JsonNull with AnyForm
  override type NoForm     <: JsonNo with AnyForm

  override val AnyForm: JsonValueFactory
  override val ObjectForm: JsonObjectFactory
  override val SeqForm: JsonSeqFactory
  override val SetForm: JsonSetFactory

  def JsonObjectValue(form: ObjectForm): AnyForm = decodeJsonObject(form)
  def JsonArrayValue(form: SeqForm): AnyForm     = form
  def JsonStringValue(form: TextForm): AnyForm   = DateForm.parse(form.toUString.toString).bindOrElse(form)

  def JsonObjectBuilder: Builder[(String, AnyForm)] with State[ObjectForm] = ObjectForm.Builder
  def JsonArrayBuilder: Builder[AnyForm] with State[SeqForm]               = SeqForm.Builder
  def JsonStringBuilder: StringBuilder with State[TextForm]                = TextForm.Builder

  def JsonString(value: String): TextForm   = TextForm(value)
  def JsonNumber(value: Int): NumberForm    = NumberForm(value)
  def JsonNumber(value: Long): NumberForm   = NumberForm(value)
  def JsonNumber(value: Float): NumberForm  = NumberForm(value)
  def JsonNumber(value: Double): NumberForm = NumberForm(value)
  def JsonNumber(value: String): NumberForm = NumberForm(value)
  def JsonTrue: BoolForm                    = TrueForm
  def JsonFalse: BoolForm                   = FalseForm
  def JsonNull: NullForm                    = NullForm
  def JsonUndefined: NoForm                 = NoForm

  def JsonNew(identifier: String, arguments: SeqForm): AnyForm = NoForm

  implicit def JsonStringContext(stringContext: StringContext): JsonStringContext[variant.type] =
    macro JsonVariantMacros.JsonStringContext[variant.type]

  private[this] def decodeJsonObject(form: ObjectForm): AnyForm = {
    if (form.size == 1) {
      val field = form.iterator.head
      val value = field._2
      field._1 match {
        case "$base64" if value.isTextForm =>
          try DataForm.fromBase64(value.asTextForm.toUString.toString)
          catch { case _: IllegalArgumentException => form }
        case "$date" if value.isNumberForm =>
          DateForm(value.asNumberForm.toLong)
        case _ => form
      }
    }
    else form
  }


  trait JsonValue extends BaseValue { this: AnyForm =>
    def writeJson(builder: StringBuilder): Unit
    def toJson: String = {
      val builder = String.Builder
      writeJson(builder)
      builder.state
    }
  }

  trait JsonValueFactory extends BaseValueFactory {
    def parseJson(json: String): AnyForm = {
      val factory = new JsonVariantFactory[variant.type](variant)
      val parser = new JsonStringParser(json)
      parser.skipWhitespace()
      if (parser.isEOF) NoForm
      else {
        val value = parser.parseValue(factory)
        parser.skipWhitespace()
        parser.parseEOF()
        value
      }
    }

    def parseJsonPrefix(json: String): (AnyForm, String) = {
      val factory = new JsonVariantFactory[variant.type](variant)
      val parser = new JsonStringParser(json)
      parser.skipWhitespace()
      val value =
        try parser.parseValue(factory)
        catch { case _: JsonException => NoForm }
      parser.skipWhitespace()
      (value, parser.remaining)
    }
  }


  trait JsonObject extends JsonValue with BaseObject { this: ObjectForm =>
    override def writeJson(builder: StringBuilder): Unit = {
      builder.append('{')
      val fields = iterator
      var field = null: (String, AnyForm)
      while (!fields.isEmpty && { field = fields.head; !field._2.isDefined })
        fields.step() // filter leading undefined fields
      if (!fields.isEmpty) {
        TextForm(field._1).writeJson(builder)
        builder.append(':')
        field._2.writeJson(builder)
        fields.step()
        while (!fields.isEmpty) {
          field = fields.head
          if (field._2.isDefined) { // filter undefined fields
            builder.append(',')
            TextForm(field._1).writeJson(builder)
            builder.append(':')
            field._2.writeJson(builder)
          }
          fields.step()
        }
      }
      builder.append('}')
    }
  }

  trait JsonObjectFactory extends BaseObjectFactory {
    def parseJson(json: String): ObjectForm = {
      val factory = new JsonVariantFactory[variant.type](variant)
      val parser = new JsonStringParser(json)
      parser.skipWhitespace()
      val value = parser.parseObject(factory)(Builder)
      parser.skipWhitespace()
      parser.parseEOF()
      value
    }
  }


  trait JsonSeq extends JsonValue with BaseSeq { this: SeqForm =>
    override def writeJson(builder: StringBuilder): Unit = {
      builder.append('[')
      val values = iterator
      var value = null.asInstanceOf[AnyForm]
      while (!values.isEmpty && { value = values.head; !value.isDefined })
        values.step() // filter leading undefined values
      if (!values.isEmpty) {
        value.writeJson(builder)
        values.step()
        while (!values.isEmpty) {
          value = values.head
          if (value.isDefined) {
            builder.append(',')
            value.writeJson(builder)
          }
          values.step()
        }
      }
      builder.append(']')
    }
  }

  trait JsonSeqFactory extends BaseSeqFactory {
    def parseJson(json: String): SeqForm = {
      val factory = new JsonVariantFactory[variant.type](variant)
      val parser = new JsonStringParser(json)
      parser.skipWhitespace()
      val value = parser.parseArray(factory)(Builder)
      parser.skipWhitespace()
      parser.parseEOF()
      value
    }
  }


  trait JsonSet extends JsonValue with BaseSet { this: SetForm =>
    override def writeJson(builder: StringBuilder): Unit = {
      builder.append('[')
      val values = iterator
      var value = null.asInstanceOf[AnyForm]
      while (!values.isEmpty && { value = values.head; !value.isDefined })
        values.step() // filter leading undefined values
      if (!values.isEmpty) {
        value.writeJson(builder)
        values.step()
        while (!values.isEmpty) {
          value = values.head
          if (value.isDefined) {
            builder.append(',')
            value.writeJson(builder)
          }
          values.step()
        }
      }
      builder.append(']')
    }
  }

  trait JsonSetFactory extends BaseSetFactory {
    def parseJson(json: String): SetForm = {
      val factory = new JsonVariantFactory[variant.type](variant)
      val parser = new JsonStringParser(json)
      parser.skipWhitespace()
      val value = parser.parseArray(factory)(Builder)
      parser.skipWhitespace()
      parser.parseEOF()
      value
    }
  }


  trait JsonText extends JsonValue with BaseText { this: TextForm =>
    override def writeJson(builder: StringBuilder): Unit = {
      def hexToChar(h: Int): Int = if (h < 10) '0' + h else 'A' + (h - 10)
      builder.append('"')
      val cs = iterator
      var c = 0
      while (!cs.isEmpty) {
        val b = c
        c = cs.head
        c match {
          case '"'  => builder.append('\\'); builder.append('"')
          case '\\' => builder.append('\\'); builder.append('\\')
          case '\b' => builder.append('\\'); builder.append('b')
          case '\f' => builder.append('\\'); builder.append('f')
          case '\n' => builder.append('\\'); builder.append('n')
          case '\r' => builder.append('\\'); builder.append('r')
          case '\t' => builder.append('\\'); builder.append('t')
          case '/'  if b == '<' => builder.append('\\'); builder.append('/')
          case c    if (c >= '\u0000' && c <= '\u001F') ||
                       (c >= '\u007F' && c <= '\u009F') =>
                       builder.append('\\'); builder.append('u')
                       builder.append(hexToChar(c >>> 12 & 0xF))
                       builder.append(hexToChar(c >>>  8 & 0xF))
                       builder.append(hexToChar(c >>>  4 & 0xF))
                       builder.append(hexToChar(c        & 0xF))
          case c    => builder.append(c)
        }
        cs.step()
      }
      builder.append('"')
    }
  }


  trait JsonData extends JsonValue with BaseData { this: DataForm =>
    override def writeJson(builder: StringBuilder): Unit = {
      builder.append('{')
      builder.append("\"$base64\"")
      builder.append(':')
      builder.append('"')
      this.writeBase64(builder)
      builder.append('"')
      builder.append('}')
    }
  }


  trait JsonNumber extends JsonValue with BaseNumber { this: NumberForm =>
    override def writeJson(builder: StringBuilder): Unit = builder.append(toJson)
    override def toJson: String                          = if (!isNaN && !isInfinite) toDecimalString else "null"
  }


  trait JsonDate extends JsonValue with BaseDate { this: DateForm =>
    override def writeJson(builder: StringBuilder): Unit = {
      builder.append('"')
      writeISO8601(builder)
      builder.append('"')
    }
  }


  trait JsonBool extends JsonValue with BaseBool { this: BoolForm =>
    override def writeJson(builder: StringBuilder): Unit = builder.append(toJson)
    override def toJson: String                          = if (toBoolean) "true" else "false"
  }


  trait JsonNull extends JsonValue with BaseNull { this: NullForm =>
    override def writeJson(builder: StringBuilder): Unit = builder.append(toJson)
    override def toJson: String                          = "null"
  }


  trait JsonNo extends JsonValue with BaseNo { this: NoForm =>
    override def writeJson(builder: StringBuilder): Unit = builder.append(toJson)
    override def toJson: String                          = "undefined"
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy