Advertisement
afec

Scala Macros: Annotation checking - Stack Overflow paste.

Jun 23rd, 2013
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Scala 2.65 KB | None | 0 0
  1. /**
  2.    * Finds all case classes under a given package, finds their val declarations and creates a function that returns a Map out of them which corresponds
  3.    * to their field values and field types.
  4.    *
  5.    * Example:
  6.    * package model
  7.    *
  8.    * case class User(name: String, age: Int, posts: List[Post]) {
  9.    *   val numPosts = posts.length
  10.    * }
  11.    *
  12.    * case class Post(content: String)
  13.    *
  14.    * For this package, allCaseClassFields("model") should return something along the lines of:
  15.    * Map(
  16.    *   User -> List( ("name", String), ("age", Int), ("posts", List[Post]), ("numPosts", Int) )
  17.    *   Post -> List( ("content", String) )
  18.    * )
  19.    *
  20.    */
  21.   def allCaseClassFields_impl(c: Context)(packageName: c.Expr[String]) = {
  22.     import c.universe._
  23.  
  24.     def isTransient(m: MethodSymbol) = {
  25.       m.accessed.annotations.exists(_.tpe =:= typeOf[scala.transient])
  26.     }
  27.  
  28.     // Get package name from the expression tree
  29.     val Literal(Constant(name: String)) = packageName.tree
  30.  
  31.     // Get all classes under given package name
  32.     val classes = c.mirror.staticPackage(name).typeSignature.declarations
  33.  
  34.     // Obtain types for the classes
  35.     val types = classes.collect {
  36.       case s: ClassSymbol if (s.isCaseClass) => s.toType
  37.     }
  38.    
  39.     // Apply method for List. For easy readability in later applications
  40.     val listApply = Select(reify(List).tree, newTermName("apply"))
  41.  
  42.     /*
  43.      * We get an iterable of (class -> List((value, class), ...) here.
  44.      * Basically what we do here is this:
  45.      * For all types,
  46.      *   For all members of types,
  47.      *     If the member is an accessor method (i.e. val getter), then construct a tuple (field_name, return_type)
  48.      *   The expression above returns an Iterable called members.
  49.      *   Construct a List out of members.
  50.      *   Construct a mapping type_name -> members.
  51.      * The expression above returns an iterable called result.
  52.      * Construct a Map out of result.
  53.      *
  54.      */
  55.     val result = types.map { t =>
  56.       val members = t.declarations.collect {
  57.         case m: MethodSymbol if m.isAccessor && !isTransient(m) =>
  58.           val name = c.literal(m.name.decoded)
  59.           val tpe = c.Expr(Literal(Constant(m.returnType)))
  60.           reify((name.splice, tpe.splice)).tree
  61.       }
  62.  
  63.       val membersExpr = c.Expr[List[Tuple2[String, Class[_]]]](Apply(listApply, members.toList))
  64.       val typeName = c.Expr[Any](Literal(Constant(t)))
  65.  
  66.       reify(typeName.splice -> membersExpr.splice).tree
  67.     }
  68.  
  69.     val mapApply = Select(reify(Map).tree, newTermName("apply"))
  70.  
  71.     c.Expr[Map[Class[_], List[Tuple2[String, Class[_]]]]](Apply(mapApply, result.toList))
  72.   }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement