I have a simple class-level annotation written in Java:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Collection {
String name();
}
used like:
#Collection(name="mytable")
case class Foo(...)
I need to introspect classes in Scala 2.11 to obtain the value of the name parameter. How can I get this info? I'm up to here:
val sym = currentMirror.classSymbol(Class.forName(fullName))
val anno = sym.annotations.head
val annoType = anno.tree.tpe // I can get this...works
println(anno.tree.children.tail) // prints List(name = "mytable")
I'm close! I can see my name parameter and its value but this doesn't seem to be accessible like a Map or anything friendly. How can I get the value of my annotation's parameter?
The tree api implements product to get elements out, so this is kind of a hacky demonstration, but you can get out your element:
println(anno.tree.children.last.productElement(1)) // prints "mytable"
If you can handle using Jackson, then I'd re-use its annotation processing functionality instead of using scala reflection.
object Test {
#Collection(name="mytable")
case class Foo(bar: String)
def main(args: Array[String]): Unit = {
val introspector = new JacksonAnnotationIntrospector
val ac = AnnotatedClass.construct(classOf[Foo], introspector, null)
val annotation = ac.getAnnotations.get(classOf[Collection])
println(annotation.name())
}
}
If the class does not have the annotation then annotation is null.
Related
So I'm trying to list fields with specific annotation in a Scala case class and I'm not able to get it working... Let's see come code right away
The case class (it's a simplified version of it, mine extends another class and is also nested in my test class where I use it for unit testing only):
case class Foo(#Unique var str: String) {}
The custom Java annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
public #interface Unique {}
And my class (simplified again) where I'm trying to do some stuffs with fields marked as unique
class SomeClass[T] (implicit typeTag: TypeTag[T]) {
val fields: Iterable[universe.TermSymbol] = typeOf(typeTag).members.collect { case s: TermSymbol => s }.
filter(s => s.isVal || s.isVar)
val list = fields.flatMap(f => f.annotations.find(_.tpe =:= TypeOf[Unique]).((f, _))).toList
}
But the val list in the last peace of code is always empty... fields has str listed in but without the annotation.
What am I missing?
The code listing the annotations is from the following answer:
How to list all fields with a custom annotation using Scala's reflection at runtime?
Seems the reference post is Scala 2.10 is old and is not compatible with the newest Scala version.
There is an example for how to get the specify annotation by type.
def listProperties[T: TypeTag]: List[universe.Annotation] = {
typeOf[T].typeSymbol.asClass
.asClass
.primaryConstructor
.typeSignature
.paramLists.flatten.flatMap(_.annotations)
}
val annotations = listProperties[Foo].filter(_.tree.tpe =:= typeOf[Unique])
println(annotations)
and there is way to get the annotation's field value:
case class Foo(#Unique(field = "bar") val str: String) {}
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
val tb = currentMirror.mkToolBox()
val result = tb.eval(tb.untypecheck(head.tree)).asInstanceOf[Unique]
and need to call out your annotation class is implemented by using Java style, In Scala maybe you want to use StaticAnnotation for creating Annotation, like:
class Unique extends StaticAnnotation
Currently I'm using GridGain/Ignite in my project and faced with some problems:
As you may know, GridGain can hold any serializable object in Cache, like this:
val mycache = ignite.getOrCreateCache[String,MyClass]("MyName")
It means, that we can define our class and extend it with Dynamic property - that's ok.
If we set Ignite-annotation (#QuerySqlField) at specific class-field - Ignite can use sql-queries with your classes like this:
val sql = select * from MyClass
mycache.query(new SqlFieldsQuers(sql))
And now my question:
How can I set Ignite-annotations with dynamic fields in dynamic classes in Scala? I've attached my dynamic class definition and hope for some help..
class DynamicType extends Dynamic with Serializable
{
private val fields = mutable.Map.empty[String,Any].withDefault{key=>throw new NoSuchFieldError(key)}
def selectDynamic(key: String) = fields(key)
def updateDynamic(key: String)(value: Any) = fields(key) = value
def applyDynamic(key: String)(args: Any*) = fields(key)
}
As I understand your dynamic type implementation will represent just a map of fields. In this case Ignite will serialize that map as DynamicType instance field. So it's like any object with field of Map type. Map's key/value pairs can't be annotated and can't be indexed by Ignite.
How can one access the annotation of a singleton object given a string?
I am using Scala version 2.11.6 and plan to upgrade to 2.12 after sorting this issue out.
Example
Given an (java) annotation
public #interface Marked {
String name();
}
and a simplified factory interface
trait Instance { def doSomething(): Unit }
trait Factory { def create(): Instance }
I would like to check whether a dynamically loaded singleton object is annotated and with what value. The singleton object will be put in the classpath and may be defined as such:
class A extends Instance { override def doSomething() { println("A is done.") } }
#Marked(name = "My A Factory")
object AFactory extends Factory {
override def create(): Instance = new A()
}
What I tried
I can only access the module (singleton object) dynamically and in this way fail to access the annotation. Mostly due to lack of comprehensive documentation I have tried the following expressions which all somehow return an empty list.
import scala.reflect.runtime.{universe => ru}
val name = "AFactory$" // This is automatically provided.
val rm = ru.runtimeMirror(getClass.getClassLoader)
val classSym = rm.staticClass(name)
println(classSym.annotations)
println(classSym.companion.annotations)
println(classSym.baseClasses(0).annotations)
println(classSym.baseClasses(0).companion.annotations)
val moduleSym = rm.staticModule(name)
println(moduleSym.companion.annotations)
println(moduleSym.asModule.annotations)
println(moduleSym.asModule.moduleClass.annotations)
println(moduleSym.asModule.moduleClass.companion.annotations)
You need to make the JVM retain the annotation during runtime, otherwise it wont work =>
#Retention(RetentionPolicy.RUNTIME)
public #interface Marked {
}
With this change, your code worked.
Edit:
The reason is, that the default retention is "Class", which will make the compiler retain the annotaion in the classfile, but it wont be accessible during the runtime.
Consider we have the following:
class Base { def name = "Base" }
class Successor extends Base {
override def name = "Successor"
}
I have tried to do the following (took from How to call a superclass method using Java reflection):
import java.lang.invoke.{MethodHandles, MethodHandle, MethodType}
object TestApp {
def main(args: Array[String]) {
val a = new Successor;
val h1 = MethodHandles.lookup().findSpecial(classOf[Base],
"name",
MethodType.methodType(classOf[String]),
classOf[Successor]);
println(h1.invoke(a));
}
}
but I get a runtime exception:
java.lang.IllegalAccessException: no private access for invokespecial: class Successor, from TestApp$
I was told that it is possible that Java reflection may not work correctly for Scala. Is it true? Or I simply do something wrong?
Actually, you can NOT even do it in Java. Note, in the answer of "How to call a superclass method using Java reflection", it works because the Test extends the Base: public class Test extends Base {...}.
It looks like it is possible and Java reflection works for Scala as well, I just didn't read all answers for How to call a superclass method using Java reflection.
The following code works:
object TestApp {
def main(args: Array[String]) {
val a = new Successor;
val impl_lookup = classOf[MethodHandles.Lookup].getDeclaredField("IMPL_LOOKUP")
impl_lookup.setAccessible(true)
val lkp = impl_lookup.get(null).asInstanceOf[MethodHandles.Lookup];
val h1 = lkp.findSpecial(classOf[Base],
"name",
MethodType.methodType(classOf[String]),
classOf[Successor])
println(h1.invoke(a)) // prints "Base"
println(a.name) // prints "Successor"
}
}
Thanks to Jesse Glick for this solution.
(Essentially I need some kind of a synthesis of these two questions (1, 2), but I'm not smart enough to combine them myself.)
I have a set of JAXB representations in Scala like this:
abstract class Representation {
def marshalToXml(): String = {
val context = JAXBContext.newInstance(this.getClass())
val writer = new StringWriter
context.createMarshaller.marshal(this, writer)
writer.toString()
}
}
class Order extends Representation {
#BeanProperty
var name: String = _
...
}
class Invoice extends Representation { ... }
The problem I have is with my unmarshalling "constructor" methods:
def unmarshalFromJson(marshalledData: String): {{My Representation Subclass}} = {
val mapper = new ObjectMapper()
mapper.getDeserializationConfig().withAnnotationIntrospector(new JaxbAnnotationIntrospector())
mapper.readValue(marshalledData, this.getClass())
}
def unmarshalFromXml(marshalledData: String): {{My Representation Subclass}} = {
val context = JAXBContext.newInstance(this.getClass())
val representation = context.createUnmarshaller().unmarshal(
new StringReader(marshalledData)
).asInstanceOf[{{Type of My Representation Subclass}}]
representation // Return the representation
}
Specifically, I can't figure out how to attach these unmarshalling methods in a typesafe and DRY way to each of my classes, and then to call them from Scala (and hopefully sometimes by using only abstract type information). In other words, I would like to do this:
val newOrder = Order.unmarshalFromJson(someJson)
And more ambitiously:
class Resource[R <: Representation] {
getRepresentation(marshalledData: String): R =
{{R's Singleton}}.unmarshalFromXml(marshalledData)
}
In terms of my particular stumbling blocks:
I can't figure out whether I should define my unmarshalFrom*() constructors once in the Representation class, or in a singleton Representation object - if the latter, I don't see how I can automatically inherit that down through the class hierarchy of Order, Invoice etc.
I can't get this.type (as per this answer) to work as a way of self-typing unmarshalFromJson() - I get a compile error type mismatch; found: ?0 where type ?0 required: Representation.this.type on the readValue() call
I can't figure out how to use the implicit Default[A] pattern (as per this answer) to work down my Representation class hierarchy to call the singleton unmarshalling constructors using type information only
I know this is a bit of a mammoth question touching on various different (but related) issues - any help gratefully received!
Alex
The key is to not try and attach the method to the class but rather pass it in as a parameter. To indicate the type you are expecting and let the type system handle passing it in. I tried to make the unmarshal invocation something that reads a little DSL like.
val order = UnMarshalXml( xml ).toRepresentation[Order]
The following is a fully testable code snippet
abstract class Representation {
def marshalToXml(): String = {
val context = JAXBContext.newInstance(this.getClass)
val writer = new StringWriter
context.createMarshaller.marshal(this, writer)
writer.toString
}
}
#XmlRootElement
class Order extends Representation {
#BeanProperty
var name: String = _
}
case class UnMarshalXml( xml: String ) {
def toRepresentation[T <: Representation](implicit m:Manifest[T]): T = {
JAXBContext.newInstance(m.erasure).createUnmarshaller().unmarshal(
new StringReader(xml)
).asInstanceOf[T]
}
}
object test {
def main( args: Array[String] ) {
val order = new Order
order.name = "my order"
val xml = order.marshalToXml()
println("marshalled: " + xml )
val received = UnMarshalXml( xml ).toRepresentation[Order]
println("received order named: " + received.getName )
}
}
You should see the following output if you run test.main
marshalled: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><order><name>my order</name></order>
received name: my order
Here's the updated version of Neil's code which I used to support the second use case as well as the first:
case class UnmarshalXml(xml: String) {
def toRepresentation[T <: Representation](implicit m: Manifest[T]): T =
toRepresentation[T](m.erasure.asInstanceOf[Class[T]])
def toRepresentation[T <: Representation](typeT: Class[T]): T =
JAXBContext.newInstance(typeT).createUnmarshaller().unmarshal(
new StringReader(xml)
).asInstanceOf[T]
}
This supports simple examples like so:
val order = UnmarshalXml(xml).toRepresentation[Order]
But also for abstract type based usage, you can use like this:
val order = UnmarshalXml(xml).toRepresentation[T](typeOfT)
(Where you have grabbed and stored typeOfT using another implicit Manifest at the point of declaring T.)