@@ -10,9 +10,9 @@ import scala.util.{Failure, Success, Try}
10
10
object Macros {
11
11
// Thanks to https://github.com/Voltir/form.rx for the idea!
12
12
13
- def getDefaultValuesFromCompanionObject (c : scala.reflect.macros.blackbox.Context )(companion : c.Expr [ Any ] ): c.Expr [Map [String , Any ]] = {
13
+ def getDefaultValuesFromCompanionObject (c : scala.reflect.macros.blackbox.Context )(companion : c.Symbol ): c.Expr [Map [String , Any ]] = {
14
14
import c .universe ._
15
- val applyMethod = companion.actualType .decls.find(_.name == TermName (" apply" )).get.asMethod
15
+ val applyMethod = companion.info .decls.find(_.name == TermName (" apply" )).get.asMethod
16
16
// can handle only default parameters from the first parameter list
17
17
// because subsequent parameter lists might depend on previous parameters
18
18
val paramName2DefaultValue = applyMethod.paramLists.head.map(_.asTerm).zipWithIndex.flatMap { case (p, i) =>
@@ -35,60 +35,103 @@ object Macros {
35
35
def generateWithoutDefault [T : c.WeakTypeTag ](c : scala.reflect.macros.blackbox.Context )
36
36
(formLayout : c.Expr [FormLayout [T ]],
37
37
validatorObject : c.Expr [Any ]): c.Expr [Form [T ]] = {
38
- generate[T ](c)(formLayout, validatorObject, c.universe.reify(None ))
38
+ generateFromExplicitValues[T ](c)(formLayout, validatorObject, c.universe.reify(None ))
39
+ .asInstanceOf [c.Expr [Form [T ]]] // unnecessary, but idea showed error otherwise
39
40
}
40
41
41
42
def generateWithDefault [T : c.WeakTypeTag ](c : scala.reflect.macros.blackbox.Context )
42
43
(formLayout : c.Expr [FormLayout [T ]],
43
44
validatorObject : c.Expr [Any ],
44
45
defaultModelValue : c.Expr [T ]): c.Expr [Form [T ]] = {
45
- generate[T ](c)(formLayout, validatorObject, c.universe.reify(Some (defaultModelValue.splice)))
46
+ import c .universe ._
47
+ generateFromExplicitValues[T ](c)(formLayout, validatorObject, reify[Option [T ]](Some (defaultModelValue.splice)))
48
+ .asInstanceOf [c.Expr [Form [T ]]] // unnecessary, but idea showed error otherwise
46
49
}
47
50
48
- def generate [DataModel : c.WeakTypeTag ](c : scala.reflect.macros.blackbox.Context )
49
- (formLayout : c.Expr [FormLayout [DataModel ]],
50
- validatorObject : c.Expr [Any ],
51
- defaultModelValue : c.Expr [Option [DataModel ]]): c.Expr [Form [DataModel ]] =
51
+ def generateOnFormBinder [DataModel : c.WeakTypeTag ](c : scala.reflect.macros.blackbox.Context ): c.Expr [Form [DataModel ]] = {
52
+ import c .universe ._
53
+
54
+ val dataModelFields = c.prefix.actualType.members
55
+ .find(m => m.isClass && m.name == TypeName (" DataModel" )).get.info.members
56
+ .collect{case v : TermSymbol => v.asTerm}
57
+
58
+ val fields = c.prefix.actualType.members
59
+ .find(m => m.isClass && m.name == TypeName (" DataModel" )).get.info.decls
60
+ .collectFirst {
61
+ case m : MethodSymbol if m.isPrimaryConstructor => m
62
+ }.get.paramLists.head
63
+
64
+ val defaultModelValue = c.prefix.actualType.members.find(m => m.name == TermName (" defaultFormValue" )).get
65
+
66
+ val dataModelType = c.prefix.actualType.members.find(m => m.isClass && m.name == TypeName (" DataModel" )).get.asType
67
+ val formLayout = c.prefix.actualType.members.find(m => m.isTerm && m.name == TermName (" formLayout" )).get.asTerm
68
+ val validatorsHolder = c.prefix.actualType.members.find(m => m.isTerm && m.name == TermName (" validatorsHolder" )).get.asTerm
69
+ val defaultFormValue = c.prefix.actualType.members.find(m => m.isTerm && m.name == TermName (" defaultFormValue" )).get.asTerm
70
+
71
+ c.Expr [Form [DataModel ]](formImpl(c)(dataModelType, formLayout, validatorsHolder, c.Expr [Option [_]](q " $defaultFormValue" )))
72
+
73
+ }
74
+
75
+ def generateFromExplicitValues [DataModel : c.WeakTypeTag ](c : scala.reflect.macros.blackbox.Context )
76
+ (formLayout : c.Expr [FormLayout [DataModel ]],
77
+ validatorsHolder : c.Expr [Any ],
78
+ defaultFormValue : c.Expr [Option [DataModel ]]): c.Expr [Form [DataModel ]] = {
79
+ import c .universe ._
80
+ val dataModelTpe = weakTypeOf[DataModel ]
81
+ val dataModelCompanion = getCompanion(c)(dataModelTpe).symbol // had problems when using abstract classes
82
+ c.Expr [Form [DataModel ]](formImpl(c)(
83
+ dataModelTpe.typeSymbol,
84
+ formLayout.tree.symbol,
85
+ validatorsHolder.tree.symbol,
86
+ defaultFormValue))
87
+ }
88
+
89
+ def formImpl (c : scala.reflect.macros.blackbox.Context )
90
+ (dataModelTypeSymbol : c.universe.Symbol ,
91
+ formLayout : c.universe.Symbol ,
92
+ validatorsHolder : c.universe.Symbol ,
93
+ defaultFormValue : c.Expr [Option [_]]): c.universe.Tree =
52
94
{
53
95
54
96
import c .universe ._
55
97
56
- val dataModelTpe = weakTypeOf[DataModel ]
57
- val dataModelCompanion = getCompanion(c)(dataModelTpe) // had problems when using abstract classes
98
+ val dataModelCompanion = dataModelTypeSymbol.companion
99
+
100
+ // val dataModelCompanion = getCompanion(c)(dataModelTypeSymbol.info) // had problems when using abstract classes
58
101
59
102
60
103
// Collect all fields for the target type
61
- // val targetFields = dataModelCompanion.actualType.decls.find(_.name == TermName("apply")).get.asMethod.paramLists.head
62
- val targetFields = dataModelTpe .decls.collectFirst {
104
+ // val targetFields = dataModelCompanion.actualType.decls.find(_.name == TermName("apply")).get.asMethod.paramLists.head
105
+ val dataModelFields = dataModelTypeSymbol.info .decls.collectFirst {
63
106
case m : MethodSymbol if m.isPrimaryConstructor => m
64
107
}.get.paramLists.head
65
108
66
- if (targetFields .exists(_.name.toString == " $global" )) {
109
+ if (dataModelFields .exists(_.name.toString == " $global" )) {
67
110
c.abort(c.enclosingPosition, s " You cannot name a field ` $$ global`. ` $$ global` is reserved methodName for the global model validator. " )
68
111
}
69
112
70
- val targetFieldNames = targetFields .map(_.name.toString)
113
+ val targetFieldNames = dataModelFields .map(_.name.toString)
71
114
72
115
val targetFieldDefaultValues : c.Expr [Map [String , Any ]] =
73
116
c.Expr [Map [String , Any ]](
74
117
q """
75
- $defaultModelValue .map((dmv: $dataModelTpe )=> Map[String, Any](.. ${targetFields .map(fn => (fn.asTerm.name.toString, q " dmv. ${fn.asTerm.name}" ))})).getOrElse( ${getDefaultValuesFromCompanionObject(c)(c. Expr [ Any ]( q " $ dataModelCompanion" ) )})
118
+ $defaultFormValue .map((dmv: $dataModelTypeSymbol )=> Map[String, Any](.. ${dataModelFields .map(fn => (fn.asTerm.name.toString, q " dmv. ${fn.asTerm.name}" ))})).getOrElse( ${getDefaultValuesFromCompanionObject(c)(dataModelCompanion)})
76
119
""" )
77
120
78
- val formFieldDescriptors = formLayout.actualType .members.map(_.asTerm).filter(_.isAccessor)
121
+ val formFieldDescriptors = formLayout.info .members.map(_.asTerm).filter(_.isAccessor)
79
122
.filter(_.asMethod.returnType.<:< (typeOf[FormFieldDescriptor [_]]))
80
123
.toList
81
124
82
125
// check that targetFields and formFieldDescriptors match
83
- val missing = targetFields .map(_.name.toString).toSet.diff(formFieldDescriptors.map(_.name.toString).toSet)
126
+ val missing = dataModelFields .map(_.name.toString).toSet.diff(formFieldDescriptors.map(_.name.toString).toSet)
84
127
if (missing.nonEmpty) {
85
128
c.abort(c.enclosingPosition, s " The form layout is not fully defined: Missing formFieldDescriptors are: \n --- ${missing.mkString(" \n --- " )}" )
86
129
}
87
130
88
131
// check types
89
132
val nonMatchingFieldTypes = formFieldDescriptors.toSet.diff(
90
133
formFieldDescriptors.filter(x =>
91
- List (targetFields .find(_.name == x.name).get.info) == x.accessed.info.typeArgs).toSet)
134
+ List (dataModelFields .find(_.name == x.name).get.info) == x.accessed.info.typeArgs).toSet)
92
135
93
136
if (nonMatchingFieldTypes.nonEmpty) {
94
137
c.abort(
@@ -98,7 +141,7 @@ object Macros {
98
141
}
99
142
100
143
// collect all targetFieldValidators
101
- val targetFieldValidators = validatorObject.actualType .members.map(_.asTerm).filter(_.isMethod).map(_.asMethod)
144
+ val targetFieldValidators = validatorsHolder.info .members.map(_.asTerm).filter(_.isMethod).map(_.asMethod)
102
145
.filter(v => targetFieldNames.contains(v.name.toString))
103
146
104
147
val targetFieldValidatorsWrongType =
@@ -111,10 +154,10 @@ object Macros {
111
154
if (! targetFieldValidators.forall { x =>
112
155
x.paramLists.size == 1 ||
113
156
x.paramLists.head.head.name == x.name ||
114
- x.paramLists.head.head.info == targetFields .find(_.name.toString == x.name.toString).get.info ||
157
+ x.paramLists.head.head.info == dataModelFields .find(_.name.toString == x.name.toString).get.info ||
115
158
x.paramLists.head.tail.forall {
116
159
case p@ q " Option[ $t] " =>
117
- val correspondingField = targetFields .find(_.name.toString == p.name.toString)
160
+ val correspondingField = dataModelFields .find(_.name.toString == p.name.toString)
118
161
correspondingField.nonEmpty && t == correspondingField.get.info
119
162
case _ => false
120
163
}
@@ -127,22 +170,22 @@ object Macros {
127
170
128
171
val targetFieldValidatorsInfo = targetFieldValidators.map(x => (x.name.toString, (x, x.paramLists.head))).toMap
129
172
130
- val globalTargetValidator = validatorObject.actualType .members.map(_.asTerm).filter(_.isMethod).map(_.asMethod)
173
+ val globalTargetValidator = validatorsHolder.info .members.map(_.asTerm).filter(_.isMethod).map(_.asMethod)
131
174
.find(_.name.toString == " $global" )
132
175
133
176
// check for correct type
134
177
if (! globalTargetValidator.forall(v => {
135
178
v.returnType.<:< (typeOf[ValidationResult ]) &&
136
179
v.paramLists.size == 1 && v.paramLists.head.size == 1 &&
137
- v.paramLists.head.head.info == dataModelTpe
180
+ v.paramLists.head.head.info.typeSymbol == dataModelTypeSymbol
138
181
})) {
139
182
c.abort(c.enclosingPosition,
140
183
s " Type of global validator must match the target type. (def $$ global(data: ${globalTargetValidator.get.accessed.info.typeArgs}): torstenrudolf.scalajs.react.formbinder.ValidationResult = ...) " )
141
184
}
142
185
143
186
val transformedGlobalTargetValidator =
144
187
globalTargetValidator.map(v => q " $v" )
145
- .getOrElse(q " (d: $dataModelTpe ) => torstenrudolf.scalajs.react.formbinder.ValidationResult.Success " )
188
+ .getOrElse(q " (d: $dataModelTypeSymbol ) => torstenrudolf.scalajs.react.formbinder.ValidationResult.Success " )
146
189
147
190
case class CompoundField (targetField : c.universe.Symbol ,
148
191
defaultValueExpr : c.universe.Tree ,
@@ -154,7 +197,7 @@ object Macros {
154
197
val formFieldBindingValDef = q " val $termName: torstenrudolf.scalajs.react.formbinder.FormFieldBinding[ ${targetField.info}] "
155
198
}
156
199
157
- val compoundFields = targetFields .zipWithIndex.map { case (f, idx) =>
200
+ val compoundFields = dataModelFields .zipWithIndex.map { case (f, idx) =>
158
201
val fieldName = f.name.toString
159
202
160
203
val validatorAndParamsOpt = targetFieldValidatorsInfo.get(fieldName)
@@ -166,7 +209,7 @@ object Macros {
166
209
val transformedTargetFieldValidator = validatorAndParamsOpt
167
210
.map(v =>
168
211
q """ (fieldValue: ${v._2.head.info}, parentForm: torstenrudolf.scalajs.react.formbinder.FormAPI[_]) => {
169
- $validatorObject . ${v._1}(
212
+ $validatorsHolder . ${v._1}(
170
213
fieldValue,
171
214
.. ${paramListFieldDescriptors.map(d => q " parentForm.fieldBinding( $d).currentValidatedValue " )})
172
215
}
@@ -185,10 +228,10 @@ object Macros {
185
228
186
229
val newTree =
187
230
q """
188
- new torstenrudolf.scalajs.react.formbinder.Form[ $dataModelTpe ] with torstenrudolf.scalajs.react.formbinder.FormAPI[ $dataModelTpe ] {
189
- override val formLayout: torstenrudolf.scalajs.react.formbinder.FormLayout[ $dataModelTpe ] = $formLayout
231
+ new torstenrudolf.scalajs.react.formbinder.Form[ $dataModelTypeSymbol ] with torstenrudolf.scalajs.react.formbinder.FormAPI[ $dataModelTypeSymbol ] {
232
+ override val formLayout: torstenrudolf.scalajs.react.formbinder.FormLayout[ $dataModelTypeSymbol ] = $formLayout
190
233
191
- override def globalValidator(data: $dataModelTpe ): torstenrudolf.scalajs.react.formbinder.ValidationResult =
234
+ override def globalValidator(data: $dataModelTypeSymbol ): torstenrudolf.scalajs.react.formbinder.ValidationResult =
192
235
( $transformedGlobalTargetValidator)(data)
193
236
194
237
override var isInitializing: Boolean = true
@@ -201,7 +244,7 @@ object Macros {
201
244
isInitializing = false
202
245
onChangeCB.runNow() // update default values
203
246
204
- override def currentValueWithoutGlobalValidation: scala.util.Try[ $dataModelTpe ] = {
247
+ override def currentValueWithoutGlobalValidation: scala.util.Try[ $dataModelTypeSymbol ] = {
205
248
val fieldValues = allFormFieldBindings.map(_.currentValidatedValue)
206
249
if (fieldValues.forall(_.nonEmpty)) {
207
250
val d = $dataModelCompanion.apply(.. ${compoundFields.map(f => q " fieldBindingsHolder. ${f.termName}.currentValidatedValue.get " )})
@@ -211,9 +254,9 @@ object Macros {
211
254
}
212
255
}
213
256
214
- override def setModelValue(newModelValue: $dataModelTpe ): japgolly.scalajs.react.Callback = {
257
+ override def setModelValue(newModelValue: $dataModelTypeSymbol ): japgolly.scalajs.react.Callback = {
215
258
${compoundFields.map(f =>
216
- q """ fieldBindingsHolder. ${f.termName}.updateValue(newModelValue. ${f.termName}) match {
259
+ q """ fieldBindingsHolder. ${f.termName}.updateValue(newModelValue. ${f.termName}) match {
217
260
case scala.util.Success(cb) => cb
218
261
case scala.util.Failure(e) => throw torstenrudolf.scalajs.react.formbinder.FormUninitialized
219
262
} """ )}.foldLeft(japgolly.scalajs.react.Callback.empty)(_ >> _)
@@ -233,9 +276,8 @@ object Macros {
233
276
}
234
277
"""
235
278
// println(show(newTree))
236
- c. Expr [ Form [ DataModel ]]( newTree)
279
+ newTree
237
280
}
238
-
239
281
}
240
282
241
283
object FormField {
0 commit comments