-
1. Re: reading java 5.0 annotations
chiba Feb 1, 2005 8:30 AM (in response to bheal)The current release 3.0 does not provide high-level API.
So you must use the lower-level API, but using it is not difficult.
For example,import javassist.*; import javassist.bytecode.*; CtMethod m = ... ; MethodInfo minfo = m.getMethodInfo(); AnnotationsAttribute attr = (AnnotationsAttribute) minfo.getAttribute(AnnotationsAttribute.invisibleTag); if (atr != null) { Annotation an = attr.getAnnotation("Author"); String s = ((StringMemberValue)a.getMemberValue("name")).getValue(); System.out.println("@Author(name=" + s + ")"); }
This prints the @Author annotation of the method m. -
2. Re: reading java 5.0 annotations
bheal Feb 1, 2005 12:15 PM (in response to bheal)Many thanks for prompt reply. However when I do that, I find that 'attr' is always null! Here's part of a test of annotating methods.
When I annotate a class, I can get the annotation for the class, but then find memberNames() always returns null, so cannot get members from within the annotion.
I have been using the latest .jar from the site, but I also got the CVS source and rebuilt that, but the effect is the same.
Sorry to appear so slow.
Benedict
--------------------------------------------------------------------
@Retention(RetentionPolicy.RUNTIME)//
public @interface Author {
String name() default "me";
int age() default 999;
}
@Author//
class Thing {
@Author public void methodWithAuthor() {
}
public void methodWithoutAuthor() {
}
}
/**
* * Output is:
*
* <pre>
*
*
*
* .classname:ProblemTest$Thing
* .raw demoClass=javassist.CtClassType@10d448[ class ProblemTest$Thing fields=ProblemTest$Thing.this$0:LProblemTest;, constructors=javassist.CtConstructor@1ca318a[ ProblemTest$Thing (LProblemTest;)V], methods=javassist.CtMethod@4c3cba33[public methodWithAuthor ()V], javassist.CtMethod@128f593[public methodWithoutAuthor ()V], ]
* CtMethod: javassist.CtMethod@4c3cba33[public methodWithAuthor ()V]
* attr:null
* CtMethod: javassist.CtMethod@128f593[public methodWithoutAuthor ()V]
* attr:null
*
*
*
* </pre>
*/
public void testJavassistMethodAnnotations() throws Exception {
ClassPool pool = ClassPool.getDefault();
String demoClassName = "ProblemTest$Thing";
CtClass demoClass = pool.get(demoClassName);
echo("raw demoClass=" + demoClass);
CtMethod[] thingMethods = demoClass.getDeclaredMethods();
for (CtMethod ctMethod : thingMethods) {
echo("CtMethod: " + ctMethod);
MethodInfo mInfo = ctMethod.getMethodInfo();
AnnotationsAttribute attr = (AnnotationsAttribute) mInfo
.getAttribute(AnnotationsAttribute.invisibleTag);
System.out.println("attr:" + attr);
if (attr != null) {
// we never get here!
Annotation author = attr.getAnnotation("Author");
echo("Author annotation:" + author);
String s = ((StringMemberValue) author.getMemberValue("name")).getValue();
System.out.println("@Author(name=" + s + ")");
}
}
} -
3. Re: reading java 5.0 annotations
chiba Feb 5, 2005 11:20 AM (in response to bheal)Can you replace AnnotationsAttribute.invisibleTag
with AnnotationsAttribute.visibleTag in the above
test code and try again? -
4. Re: reading java 5.0 annotations
bheal Feb 5, 2005 12:38 PM (in response to bheal)Thanks for suggestion. I did that: I can now get as far as seeing the @Author attribute, but cannot
retrieve the name() element. In fact, getMemberNames() always is null.
Benedict
Here's a self-contained test class that shows the problem .
Thanks
Benedictimport java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.List; import java.util.Set; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.AttributeInfo; import javassist.bytecode.ClassFile; import javassist.bytecode.MethodInfo; import javassist.bytecode.SourceFileAttribute; import javassist.bytecode.annotation.Annotation; import javassist.bytecode.annotation.StringMemberValue; import junit.framework.TestCase; import junit.textui.TestRunner; /** * This test demonstrates that when I try to access annotations using Javassist, * I can get the annotation per se, but the membernames are always null, so I * can't access the data within the annotation. * <p> * Output is: * * <pre> . ---------------------testJavassistMethodAnnotations raw demoClass=javassist.CtClassType@2e7263[ class ProblemTest$Thing fields=ProblemTest$Thing.this$0:LProblemTest;, constructors=javassist.CtConstructor@1ffb8dc[ ProblemTest$Thing (LProblemTest;)V], methods=javassist.CtMethod@4c3cba33[public methodWithAuthor ()V], javassist.CtMethod@128f593[public methodWithoutAuthor ()V], ] -----CtMethod: javassist.CtMethod@4c3cba33[public methodWithAuthor ()V] attr:@ProblemTest$Author Author annotation:@ProblemTest$Author author.getMemberNames is null F. ---------------------testClassAnnotations demoCtClass: javassist.CtClassType@2e7263[changed class ProblemTest$Thing fields=ProblemTest$Thing.this$0:LProblemTest;, constructors=javassist.CtConstructor@1ffb8dc[ ProblemTest$Thing (LProblemTest;)V], methods=javassist.CtMethod@4c3cba33[public methodWithAuthor ()V], javassist.CtMethod@128f593[public methodWithoutAuthor ()V], ] classfile: javassist.bytecode.ClassFile@1027b4d interfaces: [] attributes: [javassist.bytecode.SourceFileAttribute@1ed2ae8, javassist.bytecode.InnerClassesAttribute@19c26f5, @ProblemTest$Author] --- attribute:javassist.bytecode.SourceFileAttribute@1ed2ae8 an AttributeInfo is of class: class javassist.bytecode.SourceFileAttribute - javassist.bytecode.SourceFileAttribute@1ed2ae8 --- attribute:javassist.bytecode.InnerClassesAttribute@19c26f5 an AttributeInfo is of class: class javassist.bytecode.InnerClassesAttribute - javassist.bytecode.InnerClassesAttribute@19c26f5 --- attribute:@ProblemTest$Author annotations: [@ProblemTest$Author] ++annotation: @ProblemTest$Author of class class javassist.bytecode.annotation.Annotation interfaces: [] typename:ProblemTest$Author memberNames: null We have found the Author annotation, but cannot look at the members! memberValue-name: null stringMemberValue: null F Time: 0.141 There were 2 failures: 1) testJavassistMethodAnnotations(ProblemTest)junit.framework.AssertionFailedError: stringmembervalue for name() is null at ProblemTest.testJavassistMethodAnnotations(ProblemTest.java:166) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at ProblemTest.main(ProblemTest.java:274) 2) testClassAnnotations(ProblemTest)junit.framework.AssertionFailedError: On Author annotation, memberNames is null! at ProblemTest.dumpAnnotation(ProblemTest.java:261) at ProblemTest.testClassAnnotations(ProblemTest.java:217) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at ProblemTest.main(ProblemTest.java:274) FAILURES!!! Tests run: 2, Failures: 2, Errors: 0 * </pre> * * @author bheal . */ public class ProblemTest extends TestCase { @Retention(RetentionPolicy.RUNTIME)// public @interface Author { String name() default "me"; int age() default 999; } @Author// class Thing { @Author public void methodWithAuthor() { } public void methodWithoutAuthor() { } } public void setUp() { echo("\n\n---------------------" + getName()); } /** * * Output is: * * <pre> * * * * * * * * * * * * * .classname:ProblemTest$Thing * .raw demoClass=javassist.CtClassType@10d448[ class ProblemTest$Thing fields=ProblemTest$Thing.this$0:LProblemTest;, constructors=javassist.CtConstructor@1ca318a[ ProblemTest$Thing (LProblemTest;)V], methods=javassist.CtMethod@4c3cba33[public methodWithAuthor ()V], javassist.CtMethod@128f593[public methodWithoutAuthor ()V], ] * CtMethod: javassist.CtMethod@4c3cba33[public methodWithAuthor ()V] * attr:null * CtMethod: javassist.CtMethod@128f593[public methodWithoutAuthor ()V] * attr:null * * * * * * * * * * * * * </pre> */ public void testJavassistMethodAnnotations() throws Exception { ClassPool pool = ClassPool.getDefault(); String demoClassName = "ProblemTest$Thing"; CtClass demoClass = pool.get(demoClassName); echo("raw demoClass=" + demoClass); CtMethod[] thingMethods = demoClass.getDeclaredMethods(); for (CtMethod ctMethod : thingMethods) { echo("\n-----CtMethod: " + ctMethod); MethodInfo mInfo = ctMethod.getMethodInfo(); // AnnotationsAttribute attr = (AnnotationsAttribute) mInfo // .getAttribute(AnnotationsAttribute.invisibleTag); // on suggestion from chiba, changed to 'visibleTag' AnnotationsAttribute attr = (AnnotationsAttribute) mInfo .getAttribute(AnnotationsAttribute.visibleTag); System.out.println("attr:" + attr); if (attr != null) { // we never get here for invisible tag, but do for visible tag! Annotation authorAnnotation = attr.getAnnotation("ProblemTest$Author"); echo("Author annotation:" + authorAnnotation); assertNotNull("no @Author annotation", authorAnnotation); // we manage to get here, but cannot find the name() element if ( authorAnnotation.getMemberNames()==null) { // it always is null! echo("author.getMemberNames is null"); } StringMemberValue stringMemberValue = ((StringMemberValue) authorAnnotation .getMemberValue("name")); assertNotNull("stringmembervalue for name() is null", stringMemberValue); String nameValue = stringMemberValue.getValue(); System.out.println("@Author(name=" + nameValue + ")"); } // assertNotNull("attr is null, so cannot get Author", attr); } } // remove working reflection tests, to avoid possible name clashes // on // 'Annotation' // public void testAllAnnotations() throws Exception { // Annotation[] allAnnotations = Thing.class.getAnnotations(); // echo("all annotations: " + Arrays.asList(allAnnotations)); // assertNotNull("no annotations at all", allAnnotations); // } // // public void testFlagAnnotation() throws Exception { // Flag flag = Thing.class.getAnnotation(Flag.class); // assertNotNull("null flag", flag); // assertEquals("wrong value", flag.f1(), 999); // } public void testClassAnnotations() throws Exception { ClassPool pool = ClassPool.getDefault(); // pool.insertClassPath("."); // echo("pool=" + pool); String demoClassName = "ProblemTest$Thing"; CtClass demoCtClass = pool.get(demoClassName); echo("demoCtClass: " + demoCtClass); ClassFile classfile = demoCtClass.getClassFile(); echo("classfile: " + classfile); echo("interfaces: " + Arrays.asList(classfile.getInterfaces())); echo("attributes: " + classfile.getAttributes()); for (AttributeInfo attributeInfo : (List<AttributeInfo>) classfile.getAttributes()) { echo("\n---"); echo("attribute:" + attributeInfo); if (attributeInfo instanceof AnnotationsAttribute) { AnnotationsAttribute annotationsAttribute = (AnnotationsAttribute) attributeInfo; // note these are javassist annotations, not java ones. Annotation[] annotations = annotationsAttribute.getAnnotations(); echo("annotations: " + Arrays.asList(annotations)); for (Annotation annotation : annotations) { dumpAnnotation(annotation); } } else { echo("an AttributeInfo is of class: %s - %s %n", attributeInfo.getClass(), attributeInfo); } } } /* * MethodInfo minfo = m.getMethodInfo(); AnnotationsAttribute attr = * (AnnotationsAttribute) * minfo.getAttribute(AnnotationsAttribute.invisibleTag); if (atr != null) { * Annotation an = attr.getAnnotation("Author"); String s = * ((StringMemberValue)a.getMemberValue("name")).getValue(); * System.out.println("@Author(name=" + s + ")"); } */ void dumpAnnotation(Annotation a) { echo("\n++annotation: " + a + " of class " + a.getClass()); echo("interfaces: " + Arrays.asList(a.getClass().getInterfaces())); echo("typename:" + a.getTypeName()); Set<String> memberNames = (Set<String>) a.getMemberNames(); echo("memberNames: " + memberNames); if (memberNames != null) { for (String memberName : memberNames) { echo("member " + memberName); } } if (a.getTypeName().equals("ProblemTest$Author")) { echo("We have found the Author annotation, but cannot look at the members!"); echo("memberValue-name: " + a.getMemberValue("name")); StringMemberValue stringMemberValue = ((StringMemberValue) a.getMemberValue("name")); echo("stringMemberValue: " + stringMemberValue); if (stringMemberValue != null) { String nameValue = stringMemberValue.getValue(); echo("@Author(name=" + nameValue + ")"); } assertNotNull("On Author annotation, memberNames is null!", memberNames); } } void echo(String format, Object... objects) { System.out.format(format, objects); } void echo(String line) { echo("%s%n", line); } public static void main(String[] args) { TestRunner.run(ProblemTest.class); } }
-
5. Re: reading java 5.0 annotations
chiba Feb 6, 2005 8:15 AM (in response to bheal)Maybe that is because the default value is not included in each
class file. (I'm not sure)public @interface Author { String name() default "me"; int age() default 999; }
Won't you try to read the annotations of the Author interface?
Regards,
Chiba -
6. Re: reading java 5.0 annotations
bheal Feb 6, 2005 9:26 AM (in response to bheal)Thankyou. As soon as I stop using default values, all is well!
So this presumably means that if I want all the effective annotations, I have to walk the superclasses to get inherited annotations, and access the annotation declaration to get the defaults.
Perhaps the API could include some utility methods that would do this for one. There would seem to be two dimensions of generality: inherited annotations from superclasses, and 'inherited' data values from default values.
Perhaps something like:
getDeclaredAnnotations // just the actual annotations with actual data
// no inherited ones, no default values
getEffectiveAnnotations // fills in defaults, but no inherited ones
getAllAnnotations // includes inherited annotations, no default values
getAllEffectiveAnnotations // includes inherited annotations ,
and fills in default values
What do you think?
Benedict -
7. Re: reading java 5.0 annotations
bheal Feb 6, 2005 2:09 PM (in response to bheal)>Maybe that is because the default value is not included in each
>class file. (I'm not sure)
I've just discovered in
http://java.sun.com/docs/books/vmspec/2nd-edition/ClassFileFormat.pdf
stuff about an AnnotationDefault attribute which sounds what I need.
It says it contains default values for annottions, but if it did, each class would have the defaults bound in at compile time, whereas I had understood that recompiling the annotation led to each class getting the latest defaults.
Does it really contain the default values? If so, is there any way to get at it?
Many thanks,
Benedict -
8. Re: reading java 5.0 annotations
chiba Feb 27, 2005 10:04 AM (in response to bheal)Yes, the default value is available from AnnotationDefault
attributes. Thank you for your information.
I added the support of AnnotationDefault attributes to Javassist.
It is available from CVS HEAD or Branch_Javassist_3_0.
Chiba