Writing and processing custom annotations - Part 3
Processing the @Option Annotation
Let's learn how to write a custom annotation processor that will generate a Java class to process our @Option annotation. Here are the steps:
- Write a class that extends AbstractProcessor. The AbstractProcessor class is in javax.annotation.processing package.
import
javax.annotation.processing.*; |
- Next we have to specify the annotations that our annotation processor class, OptionProcessor, is interested in processing. There are two ways of doing this: either override the getSupportedAnnotationTypes() method from AbstractProcessor or use the @SupportedAnnotationTypes. We will use the latter method to specify the fully qualified path name of our @Option annotation like so (assume that @Option is in options package):
@SupportedAnnotationTypes({"options.Option"}) |
The SupportedAnnotationTypes annotation is a multi-valued option, so we need a '{}' to specify all the possible annotations that we are going to support. In this particular example, options.Option is the only one.
- Now override the process() method. This is the workhorse of the processor; the method has the following signature:
public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) |
Here is an explanation of the parameters and return values of process:
- elements - contains the set of annotations that we are interested in processing. The annotations in the set should correspond with the list of annotations that we specify in the @SupportedAnnotationTypes annotation
- env - this is the processing environment
- boolean - a boolean value is to indicate whether we have claimed ownership of the set of annotations passed by the processing environment in elements. "Claiming ownership" means that the set of annotations in elements are ours, and we have processed it. Return true if you claim ownership of them; false otherwise.
Here is what we are going to do in process(); we will use a for loop to go through all the annotations in elements. For every annotation, we will use getElementsAnnotateWith() (in RoundEnvironment) to give us all the annotated statement.
public
boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) { |
- Let's look at how processAnnotation() method works. For every statement that is annotated, represented by element parameter that is passed in from process(), we retrieve the annotation for that statement. Next we store the option switch against the member name in the value hashmap.
private void processAnnotation(Element element |
- Once we have collected all the statements that are annotated with Option, we can now proceed to generate the annotation processor class. The generateOptionProcessor() method does just this. It accepts as its parameter an object call Filer. Filer allows you to write out the annotation processor class to a file; this class is then also compiled during the processing.
private
void generateOptionProcessor(Filer filer |
The entire source file for OptionProcessor can be found here. The generated options processor class looks something like this:
/*
Generated on Tue Apr 11 17:03:41 SGT 2006 */ |
You can find the full source for OptionProcessor here. In our next and final installment of this series, we will learn how to use our annotation processor.
Lee Chuk-Munn has been programming in the Java language since 1996, when he first joined Sun Microsystems in Hong Kong. He currently works as a senior developer consultant and technology evangelist for Technology Outreach at Sun in Singapore. Chuk's focus is in Java APIs, Java EE, Java SE, and Java ME. Chuk graduated in 1987 from the Royal Melbourne Institute of Technology in Melbourne, Australia, where his favorite subject was compiler theory.