playframework/playframework — pull request #2819 by mchv (2014-05-11 01:11:51+00:00)
Title: Json macros - Use constructor parameters instead of introspecting apply and unapply methods 

This pull request tries to simplify a bit the implementation of JSON inception.Basically, fields of the class are retrieved directly from the constructor instead of finding them by introspecting `apply` and `unapply` methods.

As the proposed change modified the indentation, the diff is difficult to read, so I will detail for clarity the modifications.

To retrieve class fields (`params`), the current implementation does the following:

```scala
companionType.declaration(stringToTermName("unapply")) match {
case NoSymbol => c.abort(c.enclosingPosition, "No unapply function found")
case s =>

companionType.declaration(stringToTermName("apply")) match {
case NoSymbol => c.abort(c.enclosingPosition, "No apply function found")
case s =>
// searches apply method corresponding to unapply
val applies = s.asMethod.alternatives
val apply = applies.collectFirst {
case (apply: MethodSymbol) if (apply.paramss.headOption.map(_.map(_.asTerm.typeSignature)) == unapplyReturnTypes) => apply
}
apply match {
case Some(apply) =>
val params = apply.paramss.head

... CONTINUE TO WORK ...

case None => c.abort(c.enclosingPosition, "No apply function found matching unapply return types")
}

}
}
```

The proposed implementation retrieves the fields by:
* Get all `declarations` of the class
* Find the primary constructor in the list
* Get the constructor parameters

```scala
val params = weakTypeOf[A].declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor ⇒ m
}.get.paramss.head
```

The 3 modified methods (`readsImpl`, `writesImpl`, `formatsImpl`) follow the same pattern.
In addition to complexity reduction, it enables case classes without unapply `methods` (case classes [with more than 22 parameters](https://github.com/scala/scala/pull/2305) since 2.11) to be used, even if [play functional library](https://github.com/playframework/playframework/blob/master/framework/src/play-functional/src/main/scala/play/api/libs/functional/Products.scala) needs to be enhanced to support more than 22 parameters to be useful.