The Annotation Scene Library provides classes to represent the annotations on a
Java program and read and write those annotations in various formats.
Structure
- An {@link annotations.el.AScene} can hold annotations for all the classes
and packages that a client is considering.
- A {@link annotations.el.AElement} represents one particular element of a
Java program within an
AScene
.
- Package {@link annotations.io} provides routines to read and write
{@link annotations.el.AScene}s in various formats.
- An {@link annotations.Annotation} represents an annotation that might be a
field of another annotation.
- An {@link annotations.AnnotationDef} represents an annotation definition,
consisting of a definition name and field names and types
({@link annotations.field.AnnotationFieldType}s).
- A {@link annotations.TLAnnotation} represents a top-level annotation, which
has a retention policy and can be attached to an
{@link annotations.el.AElement}.
Example
Here is an example program that uses the library to perform some simple
annotation processing. While the example may seem arbitrary, it does
serve to demonstrate what the library can do.
package annotations.tests;
import java.io.*;
import java.util.*;
import annotations.*;
import annotations.el.*;
import annotations.field.*;
import annotations.io.*;
// Invoke as: java Example <input.jann> <ClassToProcess> <output.jann>
public class Example {
public static void main(/*@NonNull*/ String[/*@NonNull*/ /*@ReadOnly*/] args) {
/*@NonNull*/ AScene<SimpleAnnotation> scene;
System.out.println("Reading in " + args[0]);
try {
scene = new AScene<SimpleAnnotation>(SimpleAnnotationFactory.saf);
IndexFileParser.parse(new FileReader(args[0]), scene);
} catch (IOException e) {
e.printStackTrace(System.err);
return;
} catch (ParseException e) {
e.printStackTrace(System.err);
return;
}
System.out.println("Processing class " + args[1]);
// Get a handle on the class
AClass<SimpleAnnotation> clazz1 = scene.classes.get(args[1]);
if (clazz1 == null) {
System.out
.println("That class is never mentioned in the annotation file!");
return;
}
/*@NonNull*/ AClass<SimpleAnnotation> clazz =
(/*@NonNull*/ AClass<SimpleAnnotation>) clazz1;
for (/*@NonNull*/ Map.Entry</*@NonNull*/ String, /*@NonNull*/ AMethod<SimpleAnnotation>> me : clazz.methods
.entrySet()) {
/*@NonNull*/ AMethod<SimpleAnnotation> method = me.getValue();
TLAnnotation<SimpleAnnotation> rro =
method.receiver.tlAnnotationsHere.lookup("ReadOnly");
if (rro == null)
System.out.println("Method " + me.getKey()
+ " might modify the receiver");
else
System.out.println("Method " + me.getKey()
+ " must not modify the receiver");
/*@NonNull*/ ATypeElement<SimpleAnnotation> param1 =
method.parameters.vivify(0);
TLAnnotation<SimpleAnnotation> p1nn =
param1.tlAnnotationsHere.lookup("NonNull");
if (p1nn == null) {
System.out.println("Annotating first parameter of "
+ me.getKey() + " nonnull");
/*@NonNull*/ AnnotationDef nonnullDef =
new AnnotationDef(
"NonNull",
Collections
.</*@NonNull*/ String, /*@NonNull*/ AnnotationFieldType> emptyMap());
/*@NonNull*/ SimpleAnnotation p1nn2 =
new SimpleAnnotation(
nonnullDef,
Collections
.</*@NonNull*/ String, /*@NonNull*/ Object> emptyMap());
param1.tlAnnotationsHere
.add(new TLAnnotation<SimpleAnnotation>(
new TLAnnotationDef(nonnullDef,
RetentionPolicy.RUNTIME), p1nn2));
}
}
System.out.println("Writing out " + args[2]);
try {
IndexFileWriter.write(scene, new FileWriter(args[2]));
} catch (IOException e) {
e.printStackTrace(System.err);
return;
} catch (DefException e) {
e.printStackTrace(System.err);
return;
}
System.out.println("Success.");
}
}
Running java annotations.tests.Example input.jann foo.Bar output.jann
with this file input.jann
:
package:
annotation visible @ReadOnly:
annotation visible @NonNull:
package foo:
class Bar:
method getSomething(Ljava/lang/StringBuffer;):
receiver: @ReadOnly
method setSomething(Ljava/lang/String;):
parameter #0: @NonNull
method <init>(Ljava/lang/String;):
produces this output on the terminal:
Reading in input.jann
Processing class foo.Bar
Method getSomething must not modify the receiver
Annotating first parameter of getSomething nonnull
Method setSomething might modify the receiver
Method <init> might modify the receiver
Annotating first parameter of <init> nonnull
Writing out output.jann
Success.
and this file output.jann
:
package :
annotation visible @ReadOnly:
package :
annotation visible @NonNull:
package foo:
class Bar:
method getSomething(Ljava/lang/StringBuffer;):
parameter #0: @NonNull
receiver: @ReadOnly
method setSomething(Ljava/lang/String;):
parameter #0: @NonNull
receiver:
method <init>(Ljava/lang/String;):
parameter #0: @NonNull
receiver: