@@ -33,10 +33,13 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
33
33
34
34
import c .universe ._
35
35
36
- protected val packageName = q " _root_.org.bitlap.common "
37
- private val builderFunctionPrefix = " _TransformableFunction$"
38
- private val annoBuilderPrefix = " _AnonObjectTransformable$"
39
- private val fromTermName = TermName (" from" )
36
+ import scala .collection .immutable
37
+
38
+ protected val packageName = q " _root_.org.bitlap.common "
39
+ private val builderFunctionPrefix = " _TransformableFunction$"
40
+ private val builderDefaultValuePrefix$ = " _TransformableDefaultValue$"
41
+ private val annoBuilderPrefix = " _AnonObjectTransformable$"
42
+ private val fromTermName = TermName (" from" )
40
43
41
44
def mapTypeImpl [From , To , FromField , ToField ](
42
45
selectFromField : Expr [From => FromField ],
@@ -51,6 +54,19 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
51
54
exprPrintTree[Transformable [From , To ]](force = false , tree)
52
55
}
53
56
57
+ def setDefaultValueImpl [From , To , ToField ](
58
+ selectToField : Expr [To => ToField ],
59
+ defaultValue : Expr [ToField ]
60
+ ): Expr [Transformable [From , To ]] = {
61
+ val Function (_, Select (_, toName)) = selectToField.tree
62
+ val builderId = getBuilderId(annoBuilderPrefix)
63
+ MacroCache .classFieldDefaultValueMapping
64
+ .getOrElseUpdate(builderId, mutable.Map .empty)
65
+ .update(toName.decodedName.toString, defaultValue)
66
+ val tree = q " new ${c.prefix.actualType}"
67
+ exprPrintTree[Transformable [From , To ]](force = false , tree)
68
+ }
69
+
54
70
def mapNameImpl [From , To , FromField , ToField ](
55
71
selectFromField : Expr [From => FromField ],
56
72
selectToField : Expr [To => ToField ]
@@ -98,14 +114,27 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
98
114
}
99
115
100
116
private def getPreTree : Iterable [Tree ] = {
101
- val customTrees = MacroCache .classFieldTypeMapping.getOrElse(getBuilderId(annoBuilderPrefix), mutable.Map .empty)
102
- val (_, preTrees) = customTrees.collect { case (key, expr : Expr [Tree ] @ unchecked) =>
117
+ val customFunctionTrees = buildPreTrees(
118
+ MacroCache .classFieldTypeMapping.getOrElse(getBuilderId(annoBuilderPrefix), mutable.Map .empty)
119
+ )
120
+ val customDefaultValueTrees = buildPreTrees(
121
+ MacroCache .classFieldDefaultValueMapping.getOrElse(getBuilderId(annoBuilderPrefix), mutable.Map .empty)
122
+ )
123
+
124
+ customFunctionTrees ++ customDefaultValueTrees
125
+ }
126
+
127
+ private def buildPreTrees (mapping : mutable.Map [String , Any ]): Iterable [Tree ] = {
128
+ val (_, preTrees) = mapping.collect { case (key, expr : Expr [Tree ] @ unchecked) =>
129
+ val wrapName = (prefix : String ) => TermName (prefix + key)
103
130
expr.tree match {
104
- case buildFunction : Function =>
105
- val functionName = TermName (builderFunctionPrefix + key)
106
- key -> q " lazy val $functionName: ${buildFunction.tpe} = $buildFunction"
131
+ case function : Function =>
132
+ key -> q " lazy val ${wrapName(builderFunctionPrefix)}: ${function.tpe} = $function"
133
+ case tree : Tree =>
134
+ key -> q " lazy val ${wrapName(builderDefaultValuePrefix$)} = $tree"
107
135
}
108
136
}.unzip
137
+
109
138
preTrees
110
139
}
111
140
@@ -114,32 +143,47 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
114
143
val fromClassName = resolveClassTypeName[From ]
115
144
val toClassInfo = getCaseClassFieldInfo[To ]()
116
145
val fromClassInfo = getCaseClassFieldInfo[From ]()
117
- if (fromClassInfo.size < toClassInfo.size) {
118
- c.abort(
119
- c.enclosingPosition,
120
- s " From type: ` $fromClassName` has fewer fields than To type: ` $toClassName` and cannot be transformed "
121
- )
122
- }
123
-
146
+ val customDefaultValueMapping =
147
+ MacroCache .classFieldDefaultValueMapping.getOrElse(getBuilderId(annoBuilderPrefix), mutable.Map .empty)
124
148
val customFieldNameMapping =
125
149
MacroCache .classFieldNameMapping.getOrElse(getBuilderId(annoBuilderPrefix), mutable.Map .empty)
126
150
val customFieldTypeMapping =
127
151
MacroCache .classFieldTypeMapping.getOrElse(getBuilderId(annoBuilderPrefix), mutable.Map .empty)
128
- c.info(c.enclosingPosition, s " Field Name Mapping: $customFieldNameMapping" , force = true )
129
- c.info(c.enclosingPosition, s " Field Type Mapping: $customFieldTypeMapping" , force = true )
152
+
153
+ c.info(c.enclosingPosition, s " Field default value mapping: $customDefaultValueMapping" , force = true )
154
+ c.info(c.enclosingPosition, s " Field name mapping: $customFieldNameMapping" , force = true )
155
+ c.info(c.enclosingPosition, s " Field type mapping: $customFieldTypeMapping" , force = true )
156
+
157
+ val missingFields = toClassInfo.map(_.fieldName).filterNot(fromClassInfo.map(_.fieldName).contains)
158
+ val missingExcludeMappingName = missingFields.filterNot(customFieldNameMapping.contains)
159
+ if (missingExcludeMappingName.nonEmpty) {
160
+ val noDefaultValueFields = missingExcludeMappingName.filterNot(customDefaultValueMapping.keySet.contains)
161
+ if (noDefaultValueFields.nonEmpty) {
162
+ c.abort(
163
+ c.enclosingPosition,
164
+ s " From type: ` $fromClassName` has fewer fields than To type: ` $toClassName` and cannot be transformed! " +
165
+ s " \n Missing field mapping: ` $fromClassName`.? => ` $toClassName`.` ${missingExcludeMappingName.mkString(" ," )}`. " +
166
+ s " \n Please consider using `setName` or `setDefaultValue` method for ` $toClassName`. ${missingExcludeMappingName
167
+ .mkString(" ," )}! "
168
+ )
169
+ }
170
+ }
171
+
130
172
val fields = toClassInfo.map { field =>
131
173
val fromFieldName = customFieldNameMapping.get(field.fieldName)
132
174
val realToFieldName = fromFieldName.fold(field.fieldName)(x => x)
175
+ // scalafmt: { maxColumn = 400 }
133
176
fromFieldName match {
134
177
case Some (fromName) if customFieldTypeMapping.contains(fromName) =>
135
- q """ ${TermName (builderFunctionPrefix + fromName)}.apply( ${q " $fromTermName. ${TermName (realToFieldName)}" }) """
178
+ q """ ${TermName (field.fieldName)} = ${ TermName ( builderFunctionPrefix + fromName)}.apply( ${q " $fromTermName. ${TermName (realToFieldName)}" }) """
136
179
case None if customFieldTypeMapping.contains(field.fieldName) =>
137
- q """ ${TermName (builderFunctionPrefix + field.fieldName)}.apply( ${q " $fromTermName. ${TermName (realToFieldName)}" }) """
180
+ q """ ${TermName (field.fieldName)} = ${ TermName ( builderFunctionPrefix + field.fieldName)}.apply( ${q " $fromTermName. ${TermName (realToFieldName)}" }) """
138
181
case _ =>
139
182
checkFieldGetFieldTerm[From ](
140
183
realToFieldName,
141
184
fromClassInfo.find(_.fieldName == realToFieldName),
142
- field
185
+ field,
186
+ customDefaultValueMapping
143
187
)
144
188
}
145
189
}
@@ -153,24 +197,30 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
153
197
private def checkFieldGetFieldTerm [From : WeakTypeTag ](
154
198
realFromFieldName : String ,
155
199
fromFieldOpt : Option [FieldInformation ],
156
- toField : FieldInformation
200
+ toField : FieldInformation ,
201
+ customDefaultValueMapping : mutable.Map [String , Any ]
157
202
): Tree = {
158
203
val fromFieldTerm = q " $fromTermName. ${TermName (realFromFieldName)}"
159
204
val fromClassName = resolveClassTypeName[From ]
160
205
161
- if (fromFieldOpt.isEmpty) {
206
+ if (fromFieldOpt.isEmpty && ! customDefaultValueMapping.keySet.contains(toField.fieldName) ) {
162
207
c.abort(
163
208
c.enclosingPosition,
164
- s " value ` $realFromFieldName` is not a member of ` $fromClassName`, Please consider using `setName` method! "
209
+ s " The value ` $realFromFieldName` is not a member of ` $fromClassName`! " +
210
+ s " \n Please consider using `setDefaultValue` method! "
165
211
)
166
212
return fromFieldTerm
167
213
}
168
214
169
- val fromField = fromFieldOpt.get
170
- if (! (fromField.fieldType weak_<:< toField.fieldType)) {
171
- tryForWrapType(fromFieldTerm, fromField, toField)
172
- } else {
173
- fromFieldTerm
215
+ fromFieldOpt match {
216
+ case Some (fromField) if ! (fromField.fieldType weak_<:< toField.fieldType) =>
217
+ tryForWrapType(fromFieldTerm, fromField, toField)
218
+ case Some (fromField) if fromField.fieldType weak_<:< toField.fieldType =>
219
+ q " ${TermName (toField.fieldName)} = $fromFieldTerm"
220
+ case _ =>
221
+ val value = q """ ${TermName (builderDefaultValuePrefix$ + toField.fieldName)}"""
222
+ q " ${TermName (toField.fieldName)} = $value"
223
+
174
224
}
175
225
}
176
226
@@ -186,16 +236,18 @@ class TransformerMacro(override val c: whitebox.Context) extends AbstractMacroPr
186
236
(collectionsFlags1.isVector && collectionsFlags2.isVector) ||
187
237
(collectionsFlags1.isOption && collectionsFlags2.isOption))
188
238
&& genericType1.nonEmpty && genericType2.nonEmpty =>
239
+ // scalafmt: { maxColumn = 400 }
189
240
q """
190
- $packageName.Transformer[ $fromFieldType, $toFieldType].transform( $fromFieldTerm)
241
+ ${ TermName (toField.fieldName)} = $ packageName.Transformer[ $fromFieldType, $toFieldType].transform( $fromFieldTerm)
191
242
"""
192
243
case (information1, information2) =>
193
244
c.warning(
194
245
c.enclosingPosition,
195
246
s " No implicit `Transformer` is defined for ${information1.fieldType} => ${information2.fieldType}, which may cause compilation errors!!! " +
196
- s " Please consider using `setType` method, or define an `Transformer[ ${information1.fieldType}, ${information2.fieldType}]` implicit ! "
247
+ s " \n Please consider using `setType` method, or define an `Transformer[ ${information1.fieldType}, ${information2.fieldType}]` implicit ! "
197
248
)
198
- q """ $packageName.Transformer[ ${information1.fieldType}, ${information2.fieldType}].transform( $fromFieldTerm) """
249
+ // scalafmt: { maxColumn = 400 }
250
+ q """ ${TermName (toField.fieldName)} = $packageName.Transformer[ ${information1.fieldType}, ${information2.fieldType}].transform( $fromFieldTerm) """
199
251
}
200
252
201
253
}
0 commit comments