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

org.bitlap.tools.internal.javaCompatibleMacro.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2023 bitlap
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package org.bitlap.tools.internal

import scala.reflect.macros.whitebox

/** @author
 *    梦境迷离
 *  @since 2021/11/23
 *  @version 1.0
 */
object javaCompatibleMacro {

  class JavaCompatibleProcessor(override val c: whitebox.Context) extends AbstractMacroProcessor(c) {

    import c.universe._

    /** We generate this method with currying, and we have to deal with the first layer of currying alone.
     */
    private def getNoArgsContrWithCurrying(
      annotteeClassParams: List[List[Tree]],
      annotteeClassDefinitions: Seq[Tree]
    ): Tree = {
      annotteeClassDefinitions.foreach {
        case defDef: DefDef if defDef.name.decodedName.toString == "this" && defDef.vparamss.isEmpty =>
          c.abort(
            defDef.pos,
            "Non-parameter constructor method has already defined, please remove it or not use'@javaCompatible'"
          )
        case _ =>
      }

      val acsVals = annotteeClassParams.map(valDefAccessors)
      acsVals.foreach(valDefs =>
        valDefs.foreach(valDef =>
          if (valDef.mods.hasFlag(Flag.PRIVATE)) {
            c.abort(c.enclosingPosition, "`@javaCompatible` can be applied only to non-private fields")

          }
        )
      )
      val defaultParameters = acsVals.map(params => params.map(_.zeroValue))
      if (annotteeClassParams.isEmpty || annotteeClassParams.size == 1) {
        q"""
          def this() = {
            this(..${defaultParameters.flatten})
          }
         """
      } else {
        q"""
          def this() = {
            this(..${defaultParameters.head})(...${defaultParameters.tail})
          }
         """
      }
    }

    private def replaceAnnotation(valDefTree: Tree): Tree = {
      val safeValDef = valDefAccessors(Seq(valDefTree)).head
      val mods = safeValDef.mods.mapAnnotations { f =>
        if (!f.toString().contains("BeanProperty")) f ++ List(q"new _root_.scala.beans.BeanProperty") else f
      }
      ValDef(mods, safeValDef.name, safeValDef.tpt, safeValDef.rhs)
    }

    private def getClassWithBeanProperty(classDecl: ClassDef): Tree = {
      val q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends ..$bases { ..$body }" = classDecl
      val newFieldss = paramss.asInstanceOf[List[List[Tree]]].map(_.map(replaceAnnotation))
      q"$mods class $tpname[..$tparams] $ctorMods(...$newFieldss) extends ..$bases { ..$body }"
    }

    override def createCustomExpr(classDecl: ClassDef, compDeclOpt: Option[ModuleDef] = None): Any = {
      val tmpClassDefTree = appendClassBody(
        classDecl,
        classInfo => List(getNoArgsContrWithCurrying(classInfo.classParamss, classInfo.body))
      )
      val rest = getClassWithBeanProperty(tmpClassDefTree)
      c.Expr(q"""
          ${compDeclOpt.fold(EmptyTree)(x => x)}
          $rest
         """)
    }

    override def checkAnnottees(annottees: Seq[c.universe.Expr[Any]]): Unit = {
      super.checkAnnottees(annottees)
      val annotateeClass: ClassDef = checkClassDef(annottees)
      if (!isCaseClass(annotateeClass)) {
        c.abort(c.enclosingPosition, ErrorMessage.ONLY_CASE_CLASS)
      }
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy