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

com.carrotgarden.maven.scalor.util.Classer.scala Maven / Gradle / Ivy

package com.carrotgarden.maven.scalor.util

import java.util.concurrent.Callable
import scala.reflect.ClassTag

import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import java.io.StringWriter
import java.io.PrintWriter

/**
 * Classes support.
 */
object Classer {

  /**
   * Convert from primitive type into wrapper type.
   */
  val primitiveMap = Map[ Class[ _ ], Class[ _ ] ](
    java.lang.Boolean.TYPE -> classOf[ java.lang.Boolean ],
    java.lang.Byte.TYPE -> classOf[ java.lang.Byte ],
    java.lang.Short.TYPE -> classOf[ java.lang.Short ],
    java.lang.Character.TYPE -> classOf[ java.lang.Character ],
    java.lang.Integer.TYPE -> classOf[ java.lang.Integer ],
    java.lang.Long.TYPE -> classOf[ java.lang.Long ],
    java.lang.Float.TYPE -> classOf[ java.lang.Float ],
    java.lang.Double.TYPE -> classOf[ java.lang.Double ]
  )

  /**
   * Convert from primitive type into wrapper type.
   */
  def primitiveWrap( klaz : Class[ _ ] ) = {
    primitiveMap.getOrElse( klaz, klaz )
  }

  /**
   * Obtain true companion object for a type.
   * True companion extends the type.
   */
  def trueCompanion[ T ]( klaz : Class[ T ] )(
    implicit
    MF : Manifest[ T ], loader : ClassLoader
  ) : T =
    trueCompanion( klaz.getName )

  /**
   * Obtain true companion object for a type.
   * True companion extends the type.
   */
  def trueCompanion[ T ]( name : String )(
    implicit
    MF : Manifest[ T ], loader : ClassLoader
  ) : T =
    loader.loadClass( name + "$" ).getField( "MODULE$" ).get( MF.runtimeClass ).asInstanceOf[ T ]

  trait Companion[ T ] {
    type Module
    def apply() : Module
  }

  object Companion {
    implicit def companion[ T ]( implicit module : Companion[ T ] ) = module.apply()
  }

  /**
   * Obtain companion object for a type.
   * Fake companion does not extend the type.
   */
  def fakeCompanion[ T ]( klaz : Class[ T ] ) : AnyRef = fakeCompanion( klaz.getName )

  /**
   * Obtain companion object for a type.
   * Fake companion does not extend the type.
   */
  def fakeCompanion[ T ]( name : String ) : AnyRef =
    this.getClass.getClassLoader.loadClass( name + "$" ).getField( "MODULE$" ).get( null )

  /**
   * Execute block with context class loader switch.
   */
  def withContextLoader[ T ]( target : ClassLoader )( block : => T ) : T = {
    val thread = Thread.currentThread
    val source = thread.getContextClassLoader
    try {
      thread.setContextClassLoader( target )
      block
    } finally {
      thread.setContextClassLoader( source )
    }
  }

  import java.lang.reflect.Field

  // https://gist.github.com/carymrobbins/7b8ed52cd6ea186dbdf8

  def prettyPrint(
    instance :        Any,
    indentSize :      Int = 2,
    maxElementWidth : Int = 40,
    depth :           Int = 0
  ) : String = {

    val indent = " " * depth * indentSize
    val fieldIndent = indent + ( " " * indentSize )
    val thisDepth = prettyPrint( _ : Any, indentSize, maxElementWidth, depth )
    val nextDepth = prettyPrint( _ : Any, indentSize, maxElementWidth, depth + 1 )

    val replaceMap = Seq(
      "\n" -> "\\n",
      "\r" -> "\\r",
      "\t" -> "\\t",
      "\"" -> "\\\""
    )

    instance match {

      // Strings.
      case s : String =>
        '"' + replaceMap.foldLeft( s ) { case ( acc, ( c, r ) ) => acc.replace( c, r ) } + '"'

      // Lists
      case xs : Seq[ _ ] if xs.isEmpty => xs.toString()

      // Lists.
      case xs : Seq[ _ ] =>
        // If the Seq is not too long, pretty print on one line.
        val resultOneLine = xs.map( nextDepth ).toString()
        if ( resultOneLine.length <= maxElementWidth ) return resultOneLine
        // Otherwise, build it with newlines and proper field indents.
        val result = xs.map( x => s"\n$fieldIndent${nextDepth( x )}" ).toString()
        result.substring( 0, result.length - 1 ) + "\n" + indent + ")"

      // Case classes
      case kase : Product =>
        val prefix = kase.productPrefix
        // We'll use reflection to get the constructor arg names and values.
        val cls = kase.getClass
        val fields = cls.getDeclaredFields.filterNot( _.isSynthetic ).map( _.getName )
        val values = kase.productIterator.toSeq
        // If we weren't able to match up fields/values, fall back to toString.
        if ( fields.length != values.length ) return kase.toString
        fields.zip( values ).toList match {
          // If there are no fields, just use the normal String representation.
          case Nil                 => kase.toString
          // If there is just one field, let's just print it as a wrapper.
          case ( _, value ) :: Nil => s"$prefix(${thisDepth( value )})"
          // If there is more than one field, build up the field names and values.
          case kvps =>
            val prettyFields = kvps.map { case ( k, v ) => s"$fieldIndent$k = ${nextDepth( v )}" }
            // If the result is not too long, pretty print on one line.
            val resultOneLine = s"$prefix(${prettyFields.mkString( ", " )})"
            if ( resultOneLine.length <= maxElementWidth ) return resultOneLine
            // Otherwise, build it with newlines and proper field indents.
            s"$prefix(\n${prettyFields.mkString( ",\n" )}\n$indent)"
        }

      // Any other type.
      case _ => instance.toString
    }

  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy