constructorMacro.scala 5.5 KB
Newer Older
1
/*
梦境迷离's avatar
梦境迷离 已提交
2
 * Copyright (c) 2021 org.bitlap
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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.
 */

梦境迷离's avatar
梦境迷离 已提交
22
package org.bitlap.tools.macros
梦境迷离's avatar
梦境迷离 已提交
23 24 25 26 27 28 29 30 31

import scala.reflect.macros.whitebox

/**
 *
 * @author 梦境迷离
 * @since 2021/7/7
 * @version 1.0
 */
梦境迷离's avatar
梦境迷离 已提交
32
object constructorMacro {
梦境迷离's avatar
梦境迷离 已提交
33

梦境迷离's avatar
梦境迷离 已提交
34
  class ConstructorProcessor(override val c: whitebox.Context) extends AbstractMacroProcessor(c) {
梦境迷离's avatar
梦境迷离 已提交
35

梦境迷离's avatar
梦境迷离 已提交
36
    import c.universe._
梦境迷离's avatar
梦境迷离 已提交
37

梦境迷离's avatar
梦境迷离 已提交
38 39
    private val extractArgumentsDetail: Tuple2[Boolean, Seq[String]] = {
      extractArgumentsTuple2 {
梦境迷离's avatar
梦境迷离 已提交
40 41 42 43 44
        case q"new constructor(verbose=$verbose)" => (evalTree(verbose.asInstanceOf[Tree]), Nil)
        case q"new constructor(excludeFields=$excludeFields)" => (false, evalTree(excludeFields.asInstanceOf[Tree]))
        case q"new constructor(verbose=$verbose, excludeFields=$excludeFields)" => (evalTree(verbose.asInstanceOf[Tree]), evalTree(excludeFields.asInstanceOf[Tree]))
        case q"new constructor()" => (false, Nil)
        case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN)
梦境迷离's avatar
梦境迷离 已提交
45
      }
梦境迷离's avatar
梦境迷离 已提交
46
    }
梦境迷离's avatar
梦境迷离 已提交
47

48 49 50 51 52
    private def getMutableValDefAndExcludeFields(annotteeClassDefinitions: Seq[Tree]): Seq[c.universe.ValDef] = {
      getClassMemberValDefs(annotteeClassDefinitions).filter(v => v.mods.hasFlag(Flag.MUTABLE) &&
        !extractArgumentsDetail._2.contains(v.name.decodedName.toString))
    }

梦境迷离's avatar
梦境迷离 已提交
53 54 55
    /**
     * Extract the internal fields of members belonging to the class, but not in primary constructor and only `var`.
     */
56 57 58
    private def getMemberVarDefTermNameWithType(annotteeClassDefinitions: Seq[Tree]): Seq[Tree] = {
      getMutableValDefAndExcludeFields(annotteeClassDefinitions).map { v =>
        if (v.tpt.isEmpty) { // val i = 1, tpt is `<type ?>`
梦境迷离's avatar
梦境迷离 已提交
59
          // TODO getClass RETURN a java type, maybe we can try use class reflect to get the fields type name.
60 61 62 63
          q"${v.name}: ${TypeName(toScalaType(evalTree(v.rhs).getClass.getTypeName))}"
        } else {
          q"${v.name}: ${v.tpt}"
        }
梦境迷离's avatar
梦境迷离 已提交
64
      }
梦境迷离's avatar
梦境迷离 已提交
65
    }
梦境迷离's avatar
梦境迷离 已提交
66

梦境迷离's avatar
梦境迷离 已提交
67 68 69 70
    /**
     * We generate this method with currying, and we have to deal with the first layer of currying alone.
     */
    private def getThisMethodWithCurrying(annotteeClassParams: List[List[Tree]], annotteeClassDefinitions: Seq[Tree]): Tree = {
71
      val classInternalFieldsWithType = getMemberVarDefTermNameWithType(annotteeClassDefinitions)
梦境迷离's avatar
梦境迷离 已提交
72

73
      if (classInternalFieldsWithType.isEmpty) {
梦境迷离's avatar
梦境迷离 已提交
74
        c.abort(c.enclosingPosition, s"${ErrorMessage.ONLY_CLASS} and the internal fields (declare as 'var') should not be Empty.")
梦境迷离's avatar
梦境迷离 已提交
75 76
      }
      // Extract the internal fields of members belonging to the class, but not in primary constructor.
77 78
      val annotteeClassFieldNames = getMutableValDefAndExcludeFields(annotteeClassDefinitions).map(_.name)
      val allFieldsTermName = getClassConstructorValDefsNotFlatten(annotteeClassParams).map(_.map(_.name.toTermName))
梦境迷离's avatar
梦境迷离 已提交
79
      // Extract the field of the primary constructor.
80
      val classParamsNameWithType = getConstructorParamsNameWithType(annotteeClassParams.flatten)
梦境迷离's avatar
梦境迷离 已提交
81 82
      val applyMethod = if (annotteeClassParams.isEmpty || annotteeClassParams.size == 1) {
        q"""
83
          def this(..${classParamsNameWithType ++ classInternalFieldsWithType}) = {
梦境迷离's avatar
梦境迷离 已提交
84 85 86
            this(..${allFieldsTermName.flatten})
            ..${annotteeClassFieldNames.map(f => q"this.$f = $f")}
          }
梦境迷离's avatar
梦境迷离 已提交
87
         """
梦境迷离's avatar
梦境迷离 已提交
88 89
      } else {
        // NOTE: currying constructor overload must be placed in the first bracket block.
90
        val allClassCtorParamsNameWithType = annotteeClassParams.map(cc => getConstructorParamsNameWithType(cc))
梦境迷离's avatar
梦境迷离 已提交
91
        q"""
92
          def this(..${allClassCtorParamsNameWithType.head ++ classInternalFieldsWithType})(...${allClassCtorParamsNameWithType.tail}) = {
梦境迷离's avatar
梦境迷离 已提交
93
            this(..${allFieldsTermName.head})(...${allFieldsTermName.tail})
梦境迷离's avatar
梦境迷离 已提交
94 95 96 97
            ..${annotteeClassFieldNames.map(f => q"this.$f = $f")}
          }
         """
      }
梦境迷离's avatar
梦境迷离 已提交
98 99
      applyMethod
    }
梦境迷离's avatar
梦境迷离 已提交
100

101 102 103 104 105 106 107 108 109
    override def createCustomExpr(classDecl: ClassDef, compDeclOpt: Option[ModuleDef] = None): Any = {
      val resTree = appendClassBody(
        classDecl,
        classInfo => List(getThisMethodWithCurrying(classInfo.classParamss, classInfo.body)))
      c.Expr(
        q"""
          ${compDeclOpt.fold(EmptyTree)(x => x)}
          $resTree
         """)
梦境迷离's avatar
梦境迷离 已提交
110
    }
梦境迷离's avatar
梦境迷离 已提交
111

梦境迷离's avatar
梦境迷离 已提交
112 113
    override def checkAnnottees(annottees: Seq[c.universe.Expr[Any]]): Unit = {
      super.checkAnnottees(annottees)
114 115 116
      val annotateeClass: ClassDef = checkGetClassDef(annottees)
      if (isCaseClass(annotateeClass)) {
        c.abort(c.enclosingPosition, ErrorMessage.ONLY_CLASS)
梦境迷离's avatar
梦境迷离 已提交
117
      }
梦境迷离's avatar
梦境迷离 已提交
118
    }
梦境迷离's avatar
梦境迷离 已提交
119
    override val verbose: Boolean = extractArgumentsDetail._1
梦境迷离's avatar
梦境迷离 已提交
120
  }
梦境迷离's avatar
梦境迷离 已提交
121

梦境迷离's avatar
梦境迷离 已提交
122
}