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

io.exoquery.pprint.PPrinter.kt Maven / Gradle / Ivy

There is a newer version: 2.0.2
Show newest version
package io.exoquery.pprint

import io.exoquery.fansi.Attrs
import java.util.SortedMap
import kotlin.reflect.full.*

object EncodeHelperImpl: EncodeHelper() {
  override fun makeHexString(c: Char): String = "\\u%04x".format(c.code)
}

open class PPrinter(override val config: PPrinterConfig = PPrinterConfig()): PPrinterBase(config) {

  open override fun treeify(x: Any?, escapeUnicode: Boolean, showFieldNames: Boolean): Tree {
    fun treeifySame(x: Any?) = treeify(x, escapeUnicode, showFieldNames)

    fun  applyArray(name: String, seq: Sequence) =
      Tree.Apply(name, seq.map {x -> treeifySame(x)}.iterator())

    return when {

      x == null -> Tree.Literal("null")
      x is Boolean -> Tree.Literal(x.toString())
      x is Char -> EncodeHelperImpl.encodeChar(x, escapeUnicode)
      x is Byte -> Tree.Literal(x.toString())
      x is Short -> Tree.Literal(x.toString())
      x is Int -> Tree.Literal(x.toString())
      x is Long -> Tree.Literal(x.toString() + "L")
      x is Float -> Tree.Literal(x.toString() + "F")
      x is Double -> Tree.Literal(x.toString())
      x is String -> EncodeHelperImpl.encodeString(x, escapeUnicode)

      // No Symbol in Kotlin
      //x is Symbol -> Tree.Literal("'" + x.name)

      x is Map<*, *> -> {
        val name =
          if (showGenericForCollections) {
            val superTypes = x::class.allSuperclasses.toSet()
            when {
              (SortedMap::class in superTypes) -> "SortedMap"
              (LinkedHashMap::class in superTypes) -> "LinkedHashMap"
              else -> "Map"
            }
          } else {
            x::class.simpleName ?: "Map"
          }
        Tree.Apply(
          name,
          x.asSequence().flatMap { (k, v) ->
            listOf(Tree.Infix(treeify(k, escapeUnicode, showFieldNames), "->", treeify(v, escapeUnicode, showFieldNames)))
          }.iterator()
        )
      }

      // Note: Maybe want to have a configuration to always make it just "Sequence"
      x is Sequence<*> -> {
        val name = if (showGenericForCollections) "Sequence" else x::class.simpleName ?: "Map"
        Tree.Apply(name, x.asSequence().map { x -> treeify(x, escapeUnicode, showFieldNames) }.iterator())
      }

      // Note: Maybe want to have a configuration to always make it just "Iterable"
      x is Iterable<*> -> {
        val name =
          if (showGenericForCollections) {
            val superTypes = x::class.allSuperclasses.toSet()
            when {
              (List::class in superTypes) -> "List"
              (Set::class in superTypes) -> "Set"
              else -> x::class.simpleName ?: "Iterable"
            }
          } else {
            x::class.simpleName ?: "Iterable"
          }
        Tree.Apply(name, x.asSequence().map { x -> treeify(x, escapeUnicode, showFieldNames) }.iterator())
      }

      // No None in kotlin
      //case None -> Tree.Literal("None")

      x is Iterator<*> -> {
        if (!x.hasNext())
          Tree.Literal("empty iterator")
        else
          Tree.Literal("non-empty iterator")
      }

      x is BooleanArray -> applyArray("BooleanArray", x.asSequence())
      x is ByteArray -> applyArray("ByteArray", x.asSequence())
      x is CharArray -> applyArray("CharArray", x.asSequence())
      x is DoubleArray -> applyArray("DoubleArray", x.asSequence())
      x is FloatArray -> applyArray("FloatArray", x.asSequence())
      x is IntArray -> applyArray("IntArray", x.asSequence())
      x is LongArray -> applyArray("LongArray", x.asSequence())
      x is ShortArray -> applyArray("ShortArray", x.asSequence())

      x is Array<*> -> applyArray("Array", x.asSequence())

      x is Sequence<*> -> Tree.Apply("Sequence", x.map {x -> treeifySame(x)}.iterator())

      x is Pair<*, *> -> {
        // We could also do Tree.Infix(treeifySame(x.first), "to", treeifySame(x.second)), so it would be "a to b" not sure if that is better or worse
        Tree.Apply("Pair", sequenceOf(treeifySame(x.first), treeifySame(x.second)).iterator())
      }

      x is Triple<*, *, *> ->
        Tree.Apply("Triple", sequenceOf(treeifySame(x.first), treeifySame(x.second), treeifySame(x.third)).iterator())

      (x::class.isData) -> {
        val cls = x::class
        val className = cls.simpleName ?: cls.toString()
        val productArity = cls.constructors.firstOrNull()?.parameters?.size ?: 0

        // If there are no constructors it's usually a `data object`
        if (productArity == 0) Tree.Lazy { ctx -> sequenceOf(x.toString()).iterator() }
        // Don't do "a to b" when product has 2 elements since there's a specific Pair/Triple in Kotlin
        //else if(productArity == 2 && Util.isOperator(x.productPrefix)){
        //  Tree.Infix(
        //    treeify(x.productElement(0), escapeUnicode, showFieldNames),
        //    x.productPrefix,
        //    treeify(x.productElement(1), escapeUnicode, showFieldNames)
        //  )
        // }
        //else (className.startsWith(tuplePrefix), className.lift(tuplePrefix.length)) match{
        //  // leave out tuple1, so it gets printed as Tuple1(foo) instead of (foo)
        //  // Don't check the whole suffix, because of specialization there may be
        //  // funny characters after the digit
        //  case (true, Some('2' | '3' | '4' | '5' | '6' | '7' | '8' | '9')) ->
        //    Tree.Apply("", x.productIterator.map(x -> treeify(x, escapeUnicode, showFieldNames)))
        //  case _ ->
        //    Tree.Apply(x.productPrefix, ProductSupport.treeifyProductElements(x, this, escapeUnicode, showFieldNames))
        //}
        else {
          Tree.Apply(className, ProductSupport.treeifyProductElements(x, cls, this, escapeUnicode, showFieldNames))
        }
      }

      else -> Tree.Lazy { ctx ->
        val v = x.toString()
          //when {
          //  x.toString() == null -> "null" // It appears that in Kotlin x.toString() can never be null and we checked x for nullity at the top of this function
          //  else -> x.toString()
          //}
        sequenceOf(v).iterator()
      }
    }
  }

  companion object {
    val Color = PPrinter(PPrinterConfig())
    val BlackWhite = PPrinter(
      PPrinterConfig().copy(
        colorLiteral = Attrs.Empty,
        colorApplyPrefix = Attrs.Empty
      )
    )
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy