I\'ve the following java Annotation defined
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.FIELD})
@Retenti
You just need to do the following :
case class TestAnno(arg1 : String, @(Prefer @field)("abc") agr2 : String)
More info here http://www.scala-lang.org/api/current/#scala.annotation.meta.package
I did some searching around and have come with with two solutions. Comments, Suggesstions, improvements are welcome, I've marked this answer as wiki. The first one is based on ScalaBeans and ParaNamer.
def valNamesWithAnnotations[T <: AnyRef](obj : T)(implicit m : Manifest[T]) : List[(String, List[java.lang.annotation.Annotation])] = {
val descriptor = descriptorOf(obj.getClass)
val c = descriptor.beanType.erasure
val constructor: Option[Constructor[_]] = {
if (c.getConstructors().isEmpty) None
else Some(c.getConstructors()(0).asInstanceOf[Constructor[_]])
}
val paranamer = new BytecodeReadingParanamer
val ctorParameterNames = constructor.map(paranamer.lookupParameterNames(_)).getOrElse(scala.Array[String]()).toList
val ctorParamAnnos = constructor.getOrElse(sys.error("Cannot find constructor entry for class " + c.getName)).getParameterAnnotations
val builder = List.newBuilder[(String, List[java.lang.annotation.Annotation])]
val paramIter = ctorParameterNames.iterator
val annoIter = ctorParamAnnos.iterator
while(paramIter.hasNext && annoIter.hasNext ) {
builder += ((paramIter.next, annoIter.next.toList))
}
builder.result
}
The second approach uses ScalaSignatures and is based on this SO answer:
def valNamesWithAnnotations[C: ClassManifest] : List[(String, List[java.lang.annotation.Annotation])] = {
val cls = classManifest[C].erasure
val ctors = cls.getConstructors
assert(ctors.size == 1, "Class " + cls.getName + " should have only one constructor")
val sig = ScalaSigParser.parse(cls).getOrElse(sys.error("No ScalaSig for class " + cls.getName + ", make sure it is a top-level case class"))
val classSymbol = sig.parseEntry(0).asInstanceOf[ClassSymbol]
assert(classSymbol.isCase, "Class " + cls.getName + " is not a case class")
val tableSize = sig.table.size
val ctorIndex = (1 until tableSize).find { i =>
sig.parseEntry(i) match {
case m @ MethodSymbol(SymbolInfo("<init>", owner, _, _, _, _), _) => owner match {
case sym: SymbolInfoSymbol if sym.index == 0 => true
case _ => false
}
case _ => false
}
}.getOrElse(sys.error("Cannot find constructor entry in ScalaSig for class " + cls.getName))
val paramsListBuilder = List.newBuilder[String]
for (i <- (ctorIndex + 1) until tableSize) {
sig.parseEntry(i) match {
case MethodSymbol(SymbolInfo(name, owner, _, _, _, _), _) => owner match {
case sym: SymbolInfoSymbol if sym.index == ctorIndex => paramsListBuilder += name
case _ =>
}
case _ =>
}
}
val paramAnnoArr = ctors(0).getParameterAnnotations
val builder = List.newBuilder[(String, List[java.lang.annotation.Annotation])]
val paramIter = paramsListBuilder.result.iterator
val annoIter = paramAnnoArr.iterator
while(paramIter.hasNext && annoIter.hasNext ) {
builder += ((paramIter.next, annoIter.next.toList))
}
builder.result
}
Quentin's solution worked, but IMHO it's too much boilerplate for the user.
You can read annotations on the constructor arguments with the standard reflection API. I needed this for a macro implementation.
scala> :paste
// Entering paste mode (ctrl-D to finish)
import scala.annotation.StaticAnnotation
final class min(i: Long) extends StaticAnnotation
case class Foo(@min(1) c: String)
import scala.reflect.runtime.universe._
symbolOf[Foo].asClass.primaryConstructor.typeSignature.paramLists.head.head.annotations
// Exiting paste mode, now interpreting.
import scala.annotation.StaticAnnotation
defined class min
defined class Foo
import scala.reflect.runtime.universe._
res0: List[reflect.runtime.universe.Annotation] = List(min(1L))