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

sjsonnet.BaseRenderer.scala Maven / Gradle / Ivy

package sjsonnet
import upickle.core.{ObjVisitor, ArrVisitor}
import scala.annotation.switch

/**
 * Vendored version of `ujson.BaseRenderer` from ujson 1.2.3.
 *
 * uJson has replaced this with a pair of byte/char specialized renderers for
 * performance. For now, we just want to upgrade uJson to the latest version
 * to avoid classpath conflicts, so just vendor this code for now. In the future
 * we may remove it to interface with uJson's specialized renderers directly, to
 * benefit from their improved performance.
 */
class BaseRenderer[T <: java.io.Writer]
                  (out: T,
                   indent: Int = -1,
                   escapeUnicode: Boolean = false) extends ujson.JsVisitor[T, T]{
  var depth: Int = 0
  val colonSnippet = if (indent == -1) ":" else ": "

  var commaBuffered = false

  def flushBuffer() = {
    if (commaBuffered) {
      commaBuffered = false
      out.append(',')
      renderIndent()
    }
  }
  def visitArray(length: Int, index: Int) = new ArrVisitor[T, T] {
    flushBuffer()
    out.append('[')

    depth += 1
    renderIndent()
    def subVisitor = BaseRenderer.this
    def visitValue(v: T, index: Int): Unit = {
      flushBuffer()
      commaBuffered = true
    }
    def visitEnd(index: Int) = {
      commaBuffered = false
      depth -= 1
      renderIndent()
      out.append(']')
      out
    }
  }

  def visitObject(length: Int, index: Int) = new ObjVisitor[T, T] {
    flushBuffer()
    out.append('{')
    depth += 1
    renderIndent()
    def subVisitor = BaseRenderer.this
    def visitKey(index: Int) = BaseRenderer.this
    def visitKeyValue(s: Any): Unit = out.append(colonSnippet)
    def visitValue(v: T, index: Int): Unit = {
      commaBuffered = true
    }
    def visitEnd(index: Int) = {
      commaBuffered = false
      depth -= 1
      renderIndent()
      out.append('}')
      out
    }
  }

  def visitNull(index: Int) = {
    flushBuffer()
    out.append("null")
    out
  }

  def visitFalse(index: Int) = {
    flushBuffer()
    out.append("false")
    out
  }

  def visitTrue(index: Int) = {
    flushBuffer()
    out.append("true")
    out
  }

  def visitFloat64StringParts(s: CharSequence, decIndex: Int, expIndex: Int, index: Int) = {
    flushBuffer()
    out.append(s)
    out
  }

  override def visitFloat64(d: Double, index: Int) = {
    d match{
      case Double.PositiveInfinity => visitString("Infinity", -1)
      case Double.NegativeInfinity => visitString("-Infinity", -1)
      case d if java.lang.Double.isNaN(d) => visitString("NaN", -1)
      case d =>
        val i = d.toInt
        if (d == i) visitFloat64StringParts(i.toString, -1, -1, index)
        else super.visitFloat64(d, index)
        flushBuffer()
    }

    out
  }

  def visitString(s: CharSequence, index: Int) = {
    flushBuffer()
    if (s == null) out.append("null")
    else BaseRenderer.escape(out, s, escapeUnicode)

    out
  }

  final def renderIndent() = {
    if (indent == -1) ()
    else {
      out.append('\n')
      var i = indent * depth
      while(i > 0) {
        out.append(' ')
        i -= 1
      }
    }
  }
}
object BaseRenderer {
  final def escape(sb: java.io.Writer, s: CharSequence, unicode: Boolean): Unit = {
    sb.append('"')
    var i = 0
    val len = s.length
    while (i < len) {
      (s.charAt(i): @switch) match {
        case '"' => sb.append("\\\"")
        case '\\' => sb.append("\\\\")
        case '\b' => sb.append("\\b")
        case '\f' => sb.append("\\f")
        case '\n' => sb.append("\\n")
        case '\r' => sb.append("\\r")
        case '\t' => sb.append("\\t")
        case c =>
          if (c < ' ' || (c > '~' && unicode)) {
            sb.append("\\u").append(toHex((c >> 12) & 15)).append(toHex((c >> 8) & 15))
              .append(toHex((c >> 4) & 15)).append(toHex(c & 15))
          } else sb.append(c)
      }
      i += 1
    }
    sb.append('"')
  }

  private def toHex(nibble: Int): Char = (nibble + (if (nibble >= 10) 87 else 48)).toChar
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy