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

nt.better-tostring_3.5.0.0.3.17.source-code.BetterToStringImpl.scala Maven / Gradle / Ivy

/*
 * Copyright 2020 Polyvariant
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.polyvariant

// Source-compatible core between 2.x and 3.x implementations

trait CompilerApi {
  type Tree
  type Clazz
  type Param
  type ParamName
  type Method
  type EnclosingObject

  def className(clazz: Clazz): String
  def isPackageOrPackageObject(enclosingObject: EnclosingObject): Boolean
  def enclosingObjectName(enclosingObject: EnclosingObject): String
  def params(clazz: Clazz): List[Param]
  def literalConstant(value: String): Tree

  def paramName(param: Param): ParamName
  def selectInThis(clazz: Clazz, name: ParamName): Tree
  def concat(l: Tree, r: Tree): Tree

  def createToString(clazz: Clazz, body: Tree): Method
  def addMethod(clazz: Clazz, method: Method): Clazz
  def methodNames(clazz: Clazz): List[String]
  // better name: "is case class or object"
  def isCaseClass(clazz: Clazz): Boolean
  def isObject(clazz: Clazz): Boolean
}

trait BetterToStringImpl[+C <: CompilerApi] {
  val compilerApi: C

  def transformClass(
    clazz: compilerApi.Clazz,
    isNested: Boolean,
    enclosingObject: Option[compilerApi.EnclosingObject]
  ): compilerApi.Clazz

}

object BetterToStringImpl {

  def instance(
    api: CompilerApi
  ): BetterToStringImpl[api.type] =
    new BetterToStringImpl[api.type] {
      val compilerApi: api.type = api

      import api._

      def transformClass(
        clazz: Clazz,
        isNested: Boolean,
        enclosingObject: Option[EnclosingObject]
      ): Clazz = {
        // technically, the method found by this can be even something like "def toString(s: String): Unit", but we're ignoring that
        val hasToString: Boolean = methodNames(clazz).contains("toString")

        val shouldModify = isCaseClass(clazz) && !isNested && !hasToString

        if (shouldModify) overrideToString(clazz, enclosingObject)
        else clazz
      }

      private def overrideToString(clazz: Clazz, enclosingObject: Option[EnclosingObject]): Clazz =
        addMethod(clazz, createToString(clazz, toStringImpl(clazz, enclosingObject)))

      private def toStringImpl(clazz: Clazz, enclosingObject: Option[EnclosingObject]): Tree = {
        val className = api.className(clazz)
        val parentPrefix = enclosingObject.filterNot(api.isPackageOrPackageObject).fold("")(api.enclosingObjectName(_) ++ ".")

        val namePart = literalConstant(parentPrefix ++ className)

        val paramListParts: List[Tree] = params(clazz).zipWithIndex.flatMap { case (v, index) =>
          val commaPrefix = if (index > 0) ", " else ""

          val name = paramName(v)

          List(
            literalConstant(commaPrefix ++ name.toString ++ " = "),
            selectInThis(clazz, name)
          )
        }

        val paramParts =
          if (api.isObject(clazz)) Nil
          else
            List(
              List(literalConstant("(")),
              paramListParts,
              List(literalConstant(")"))
            ).flatten

        val parts =
          namePart :: paramParts

        parts.reduceLeft(concat(_, _))
      }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy