TL;DR: Basically, I\'m looking for the Scala equivalent of the Java:
(MyAnnotation) Thing.getClass().getAnnotations()[0]
D
Without having to depend on scala-compiler, this is my version:
def fetchAnnotations[T <: Annotation](cls : Class[_]) : List[T]= {
import scala.reflect.runtime.universe._
val mirror = runtimeMirror(cls.getClassLoader)
val clsSymbol = mirror.staticClass(cls.getCanonicalName)
val annotations = clsSymbol.annotations
val res = ListBuffer[T]()
for(annt : Annotation <- annotations) {
val anntCls = annt.tree.tpe.typeSymbol.asClass
val classMirror = mirror.reflectClass(anntCls);
val anntType = annt.tree.tpe
val constructor = anntType.decl(termNames.CONSTRUCTOR).asMethod;
val constructorMirror = classMirror.reflectConstructor(constructor);
val instance = annt.tree match {
case Apply(c, args : List[Tree]) =>
val res = args.collect({
case i: Tree =>
i match {
case Literal(Constant(value)) =>
value
}
})
constructorMirror(res: _*).asInstanceOf[T]
}
res+=(instance)
}
res.toList
}
The previous code will only work when the arguments are primitive I suspect.
If we can afford depending on scala-compiler, then we can do something like:
def fetchAnnotations_toolBox[T <: Annotation](cls : Class[_]) : List[T]= {
import scala.reflect.runtime.universe._
val mirror = runtimeMirror(cls.getClassLoader)
val clsSymbol = mirror.staticClass(cls.getCanonicalName)
val annotations = clsSymbol.annotations
val res = ListBuffer[T]()
for(annt : Annotation <- annotations) {
import scala.tools.reflect.ToolBox
val toolbox = mirror.mkToolBox()
val instance = toolbox.eval(toolbox.untypecheck(toolbox.typecheck(annt.tree))).asInstanceOf[T]
res+=(instance)
}
res.toList
}
Not the java equivalent, but with scala 2.11.6, this works to extract values out of a annotation:
case class MyAnnotationClass(id: String) extends scala.annotation.StaticAnnotation
val myAnnotatedClass: ClassSymbol = u.runtimeMirror(Thread.currentThread().getContextClassLoader).staticClass("MyAnnotatedClass")
val annotation: Option[Annotation] = myAnnotatedClass.annotations.find(_.tree.tpe =:= u.typeOf[MyAnnotationClass])
val result = annotation.flatMap { a =>
a.tree.children.tail.collect({ case Literal(Constant(id: String)) => DoSomething(id) }).headOption
}
I understand that you might have done it already, but just in case it can help someone :)
In their current form, Scala annotations try to combine Java compatibility (which implies constant arguments only and a very limited number of language constructs allowed in annotations) and ultimate flexibility (which implies allowing anything one could imagine in annotations).
This is manifested by the ClassfileAnnotation (compatibility) vs StaticAnnotation (flexibility) distinction. According to this idea, one can choose between Java-style reflection with limited annotations being available as objects and Scala-style reflection with fully flexible annotations being available only as abstract syntax trees (note that we can't automatically convert static annotations to runtime objects, because code stored in such annotations might contain arbitrary Scala expressions, which makes it very hard to evaluate them).
Unfortunately, this idealistic picture is broken by the fact that classfile annotations don't support runtime retention: https://issues.scala-lang.org/browse/SI-32, which means that one doesn't actually get to choose - only Scala-style reflection is supported at the moment. Hopefully, some day we'll get SI-32 fixed, but I'm unaware of any ongoing effort to make it work.
A couple months ago, I wanted to implement something like scala.reflect.Annotation.eval
that would take a Scala-style annotation and evaluate it if possible. However, after a discussion during one of our reflection meetings we decided against it, because apart from an unfortunate non-generality, this API would also bring troubles to compile-time reflection (which in Scala is unified with runtime reflection).
This has been logged as an issue at https://issues.scala-lang.org/browse/SI-6423 and also discussed at https://groups.google.com/forum/#!topic/scala-internals/8v2UL-LR9yY, but with no concrete plans for improvement at the moment. Hopefully Project Palladium is going to help here, since one of its core components is a Scala interpreter, which will provide necessary generality to support Annotation.eval
.