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

梦境迷离's avatar
梦境迷离 已提交
22
package org.bitlap.common.internal
梦境迷离's avatar
梦境迷离 已提交
23 24 25 26 27

import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import scala.reflect.macros.blackbox

梦境迷离's avatar
梦境迷离 已提交
28
/** This is a generic implementation of macro handling, and subclasses need to inherit it to reduce redundant code.
梦境迷离's avatar
梦境迷离 已提交
29
 *
梦境迷离's avatar
梦境迷离 已提交
30 31 32 33
 *  @author
 *    梦境迷离
 *  @since 2021/7/24
 *  @version 1.0
梦境迷离's avatar
梦境迷离 已提交
34 35 36 37 38
 */
abstract class AbstractMacroProcessor(val c: blackbox.Context) {

  import c.universe._

39 40
  final case class FieldZipInformation(fieldNames: List[String], fieldIndexTypeMapping: List[(Int, Type)])

梦境迷离's avatar
梦境迷离 已提交
41 42 43 44 45 46
  final case class CollectionFlags(
    isSeq: Boolean = false,
    isList: Boolean = false,
    isOption: Boolean = false,
    isVector: Boolean = false,
    isSet: Boolean = false
梦境迷离's avatar
梦境迷离 已提交
47 48 49 50 51
  ) {
    def isCollection: Boolean = isSeq || isList || isOption || isVector || isSet

    def isStrictCollection: Boolean = isSeq || isList || isVector || isSet
  }
梦境迷离's avatar
梦境迷离 已提交
52

53 54 55 56 57
  final case class FieldTreeInformation(
    index: Int,
    fieldTerm: Tree,
    fieldType: Type,
    zeroValue: Tree,
梦境迷离's avatar
梦境迷离 已提交
58 59
    collectionsFlags: CollectionFlags,
    genericType: List[Type] = Nil
60 61 62 63 64
  )

  final case class FieldInformation(
    fieldName: String,
    fieldType: Type,
梦境迷离's avatar
梦境迷离 已提交
65
    collectionFlags: CollectionFlags,
梦境迷离's avatar
梦境迷离 已提交
66 67 68
    genericType: List[Type] = Nil,
    hasDefaultValue: Boolean,
    zeroValue: Tree
69 70 71
  )

  def tryGetOrElse(tree: Tree, default: Tree): Tree =
72 73
    q"_root_.scala.util.Try($tree).getOrElse($default)"

74
  def tryOptionGetOrElse(optionTree: Tree, default: Tree): Tree =
75 76
    q"_root_.scala.util.Try($optionTree.getOrElse($default)).getOrElse($default)"

77 78
  def tryOption(optionTree: Tree): Tree =
    q"_root_.scala.util.Try($optionTree).getOrElse(_root_.scala.None)"
79

梦境迷离's avatar
梦境迷离 已提交
80
  /** Get the list of case class constructor parameters and return the column index, column name, and parameter type
81
   *  that zip as a `List[FieldTreeInformation]`.
梦境迷离's avatar
梦境迷离 已提交
82
   *
梦境迷离's avatar
梦境迷离 已提交
83 84 85 86 87
   *  @param columnsFunc
   *    The function to get CSV row data temporary identifier, also known as a line.
   *  @tparam T
   *    Type of the case class.
   *  @return
梦境迷离's avatar
梦境迷离 已提交
88
   */
89
  def checkGetFieldTreeInformationList[T: WeakTypeTag](columnsFunc: TermName): List[FieldTreeInformation] = {
梦境迷离's avatar
梦境迷离 已提交
90
    val idxColumn    = (i: Int) => q"$columnsFunc()($i)"
梦境迷离's avatar
梦境迷离 已提交
91
    val params       = getCaseClassFieldInfoList[T]()
梦境迷离's avatar
梦境迷离 已提交
92
    val paramsSize   = params.size
93
    val types        = params.map(_.fieldType)
梦境迷离's avatar
梦境迷离 已提交
94 95 96 97 98
    val indexColumns = (0 until paramsSize).toList.map(i => i -> idxColumn(i))
    if (indexColumns.size != types.size) {
      c.abort(c.enclosingPosition, "The column num of CSV file is different from that in case class constructor!")
    }

99
    indexColumns zip types map { kv =>
梦境迷离's avatar
梦境迷离 已提交
100 101 102 103
      val collectionFlag          = isWrapType(kv._2)
      val typed                   = c.typecheck(tq"${kv._2}", c.TYPEmode).tpe
      var genericType: List[Type] = Nil
      if (collectionFlag.isCollection) {
梦境迷离's avatar
梦境迷离 已提交
104
        genericType = typed.dealias.typeArgs ::: genericType
105 106 107 108 109
      }
      FieldTreeInformation(
        kv._1._1,
        kv._1._2,
        kv._2,
梦境迷离's avatar
梦境迷离 已提交
110 111
        getZeroValue(kv._2),
        collectionFlag,
112 113 114
        genericType
      )
    }
梦境迷离's avatar
梦境迷离 已提交
115 116
  }

梦境迷离's avatar
梦境迷离 已提交
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
  def getFieldDefaultValueMap[T: WeakTypeTag](init: MethodSymbol): Map[String, Tree] = {
    val classSym = weakTypeOf[T].typeSymbol
    init.paramLists.head
      .map(_.asTerm)
      .zipWithIndex
      .flatMap { case (p, i) =>
        if (!p.isParamWithDefault) None
        else {
          val getterName = TermName("apply$default$" + (i + 1))
          Some(p.name.decodedName.toString -> q"${classSym.name.toTermName}.$getterName") // moduleSym is none
        }
      }
      .toMap
  }

梦境迷离's avatar
梦境迷离 已提交
132
  /** Get only the symbol of the case class constructor parameters.
梦境迷离's avatar
梦境迷离 已提交
133
   *
梦境迷离's avatar
梦境迷离 已提交
134 135 136
   *  @tparam T
   *    Type of the case class.
   *  @return
梦境迷离's avatar
梦境迷离 已提交
137
   */
梦境迷离's avatar
梦境迷离 已提交
138 139 140 141
  def getCaseClassFieldInfoList[T: WeakTypeTag](): List[FieldInformation] = {
    val init              = c.weakTypeOf[T].resultType.member(TermName("<init>")).asMethod
    val defaultValuesTerm = getFieldDefaultValueMap[T](init)
    val parameters        = init.typeSignature.paramLists
梦境迷离's avatar
梦境迷离 已提交
142 143 144
    if (parameters.size > 1) {
      c.abort(c.enclosingPosition, "The constructor of case class has currying!")
    }
145
    parameters.flatten.map { p =>
梦境迷离's avatar
梦境迷离 已提交
146 147 148 149
      val typed                   = c.typecheck(tq"$p", c.TYPEmode).tpe
      var genericType: List[Type] = Nil
      val collectionFlags         = isWrapType(typed)
      if (collectionFlags.isCollection) {
梦境迷离's avatar
梦境迷离 已提交
150
        genericType = typed.dealias.typeArgs ::: genericType
151 152 153 154
      }
      FieldInformation(
        p.name.decodedName.toString,
        typed,
梦境迷离's avatar
梦境迷离 已提交
155 156 157 158
        collectionFlags,
        genericType,
        defaultValuesTerm.contains(p.name.decodedName.toString),
        getZeroValue(typed)
159 160
      )
    }
梦境迷离's avatar
梦境迷离 已提交
161 162
  }

梦境迷离's avatar
梦境迷离 已提交
163
  /** Print the expanded code of macro.
梦境迷离's avatar
梦境迷离 已提交
164
   *
梦境迷离's avatar
梦境迷离 已提交
165 166 167 168
   *  @param force
   *  @param resTree
   *  @tparam T
   *  @return
梦境迷离's avatar
梦境迷离 已提交
169
   */
170
  def exprPrintTree[T: WeakTypeTag](force: Boolean, resTree: Tree): Expr[T] = {
梦境迷离's avatar
梦境迷离 已提交
171 172 173 174
    c.info(
      c.enclosingPosition,
      s"\n###### Time: ${ZonedDateTime.now().format(DateTimeFormatter.ISO_ZONED_DATE_TIME)} Expanded macro start ######\n" + resTree
        .toString() + "\n###### Expanded macro end ######\n",
梦境迷离's avatar
梦境迷离 已提交
175
      force = force
梦境迷离's avatar
梦境迷离 已提交
176 177 178 179
    )
    c.Expr[T](resTree)
  }

梦境迷离's avatar
梦境迷离 已提交
180
  /** Get the `TypeName` of the class.
梦境迷离's avatar
梦境迷离 已提交
181
   *
梦境迷离's avatar
梦境迷离 已提交
182 183 184
   *  @tparam T
   *    Type of the case class.
   *  @return
梦境迷离's avatar
梦境迷离 已提交
185
   */
186 187
  def resolveClassTypeName[T: WeakTypeTag]: TypeName =
    c.weakTypeOf[T].typeSymbol.name.toTypeName
梦境迷离's avatar
梦境迷离 已提交
188

梦境迷离's avatar
梦境迷离 已提交
189 190 191 192 193 194 195 196 197
  /** Get the `TypeName` of the class generic type.
   *
   *  @tparam T
   *    Type of the case class.
   *  @return
   */
  def getGenericTypes[T: WeakTypeTag]: List[Type] =
    c.weakTypeOf[T].dealias.typeArgs

梦境迷离's avatar
梦境迷离 已提交
198
  /** Get the list of case class constructor parameters and return the column index and parameter type that zip as a
199
   *  `FieldZipInformation`.
梦境迷离's avatar
梦境迷离 已提交
200
   *
梦境迷离's avatar
梦境迷离 已提交
201 202 203
   *  @tparam T
   *    Type of the case class.
   *  @return
梦境迷离's avatar
梦境迷离 已提交
204
   */
205
  def checkGetFieldZipInformation[T: WeakTypeTag]: FieldZipInformation = {
梦境迷离's avatar
梦境迷离 已提交
206
    val params     = getCaseClassFieldInfoList[T]()
梦境迷离's avatar
梦境迷离 已提交
207
    val paramsSize = params.size
208 209 210 211 212
    val names      = params.map(_.fieldName)
    FieldZipInformation(
      names,
      params.zip(0 until paramsSize).map(f => f._2 -> f._1.fieldType)
    )
梦境迷离's avatar
梦境迷离 已提交
213 214
  }

梦境迷离's avatar
梦境迷离 已提交
215
  /** Get the builderId of the current class which generated by *Builder,apply macro.
梦境迷离's avatar
梦境迷离 已提交
216
   *
梦境迷离's avatar
梦境迷离 已提交
217 218
   *  @param annoBuilderPrefix
   *  @return
梦境迷离's avatar
梦境迷离 已提交
219
   */
220
  def getBuilderId(annoBuilderPrefix: String): Int =
梦境迷离's avatar
梦境迷离 已提交
221
    c.prefix.actualType.toString.replace(annoBuilderPrefix, "").toInt
222

梦境迷离's avatar
梦境迷离 已提交
223
  def getZeroValue(typ: Type): Tree =
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
    typ match {
      case t if t =:= typeOf[Int] =>
        q"0"
      case t if t =:= typeOf[String] =>
        val empty = ""
        q"$empty"
      case t if t =:= typeOf[Float] =>
        q"0.asInstanceOf[Float]"
      case t if t =:= typeOf[Double] =>
        q"0D"
      case t if t =:= typeOf[Char] =>
        q"'?'"
      case t if t =:= typeOf[Byte] =>
        q"0"
      case t if t =:= typeOf[Short] =>
        q"0"
      case t if t =:= typeOf[Boolean] =>
        q"false"
      case t if t =:= typeOf[Long] =>
        q"0L"
      case t if t weak_<:< typeOf[List[_]]   => q"_root_.scala.Nil"
      case t if t weak_<:< typeOf[Option[_]] => q"_root_.scala.None"
梦境迷离's avatar
梦境迷离 已提交
246 247 248
      case t if t weak_<:< typeOf[Set[_]]    => q"_root_.scala.Predef.Set.empty"
      case t if t weak_<:< typeOf[Vector[_]] => q"_root_.scala.Vector.empty"
      case t if t weak_<:< typeOf[Seq[_]]    => q"_root_.scala.Nil"
249 250 251 252
      case _ =>
        q"null"
    }

梦境迷离's avatar
梦境迷离 已提交
253
  private def isWrapType(typed: Type): CollectionFlags = {
254 255 256
    var isList: Boolean   = false
    var isSeq: Boolean    = false
    var isOption: Boolean = false
梦境迷离's avatar
梦境迷离 已提交
257 258
    var isVector: Boolean = false
    var isSet: Boolean    = false
259 260 261 262 263
    typed match {
      case t if t weak_<:< weakTypeOf[List[_]] =>
        isList = true
      case t if t weak_<:< weakTypeOf[Option[_]] =>
        isOption = true
梦境迷离's avatar
梦境迷离 已提交
264 265 266 267
      case t if t weak_<:< weakTypeOf[Vector[_]] =>
        isVector = true
      case t if t weak_<:< weakTypeOf[Set[_]] =>
        isSet = true
梦境迷离's avatar
梦境迷离 已提交
268 269
      case t if !isList && (t weak_<:< weakTypeOf[Seq[_]]) =>
        isSeq = true
270 271
      case _ =>
    }
梦境迷离's avatar
梦境迷离 已提交
272
    CollectionFlags(isSeq, isList, isOption, isVector, isSet)
273 274
  }

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