In Example 25.4, the method printTaskInfoAnnotation() at (1) in the TaskInfoAnnotationProcessor class can be used to specifically discover TaskInfo annotations on program elements. Its parameter is an array of AnnotatedElement—that is, it contains program elements that are presumably marked with annotations. A stream of AnnotatedElement to process each annotated element is created at (2) from the array of AnnotatedElement that is passed as an argument to the method. This stream is filtered at (3) to discard any annotated element that is not marked with the TaskInfo annotation. A header is printed at (4) to indicate which annotated element is being processed for TaskInfo annotation. At (5) the method getDeclaredAnnotationsByType() returns an array of TaskInfo, containing any TaskInfo annotations applied to the current annotated element. This array of TaskInfo is flattened to create a stream of TaskInfo annotations. The element values of a TaskInfo annotation are extracted at (6) by calling the methods specified in the annotation type elements declared in the TaskInfo annotation type.

The method printTaskInfoAnnotation() is called at (8) and (9) to process TaskInfo annotations for the class NuclearPlant and its methods, respectively. The program output shows the result of running this annotation processor on the NuclearPlant class.

In Example 25.5, the class AnnotationPrinter implements a more general annotation processor that prints all annotations applied on a class and its members. The numbers below refer to the numbered lines in the code in Example 25.5.

(1) The printAllAnnotations() method is passed the Class object of the class whose annotations should be printed.

(2) through (5): The method printAnnotatedElements() is successively called to print any annotations on the class, its constructors, its methods, and its fields.

(6) The method printAnnotatedElements() accepts elements that implement the AnnotatedElement interface. Recall that this is the case for the Class<T>, Constructor<T>, Method, and Field classes (Figure 25.2).

(7) A stream of AnnotatedElement is created.

(8) The name of the AnnotatedElement is printed.

(9) The method getDeclaredAnnotations() returns an array with the annotations applied to an AnnotatedElement, which is flattened into a stream of Annotation.

(10) The text representation of an Annotation is printed.

The program output shows the results of running the annotation processor on the NuclearPlant class. It is worth running this annotation processor on classes that we have seen in this chapter, keeping in mind that any annotation applied in the code must have RetentionPolicy.RUNTIME in order for it to be processed at runtime. For instance, the @Override annotation on the toString() method of the NuclearPlant class is not in the program output, as it has RetentionPolicy.SOURCE.

Example 25.5 Annotation Processor

Click here to view code image

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.stream.Stream;
public class AnnotationPrinter {
  public static void printAllAnnotations(Class<?> classobj) {              // (1)
    printAnnotatedElements(classobj);                                      // (2)
    printAnnotatedElements(classobj.getDeclaredConstructors());            // (3)
    printAnnotatedElements(classobj.getDeclaredMethods());                 // (4)
    printAnnotatedElements(classobj.getDeclaredFields());                  // (5)
  }
  public static void printAnnotatedElements(AnnotatedElement… elements) {// (6)
    Stream.of(elements)                                                    // (7)
      .peek(ae -> System.out.printf(“Annotations for \’%s\’:%n”, ae))      // (8)
      .flatMap(ae -> Stream.of(ae.getDeclaredAnnotations()))               // (9)
      .forEach(a -> System.out.println(”  ” + a));                         // (10)
  }
}

Click here to view code image

public class AnnotationClient {
  public static void main(String[] args) {
    AnnotationPrinter.printAllAnnotations(NuclearPlant.class);
  }
}

Output from the program (edited to fit on the page):

Click here to view code image

Annotations for ‘class NuclearPlant’:
  @Pending()
  @TaskInfo(priority=HIGH, taskDesc=”Class for running a nuclear reactor.”,
            assignedTo={“Tom”, “Dick”, “Harriet”})
Annotations for ‘public NuclearPlant()’:
  @Pending()
Annotations for ‘public void NuclearPlant.shutDownNuclearReactor()’:
  @Pending()
  @TaskInfo(priority=NORMAL, taskDesc=”Procedure for nuclear reactor shutdown”,
            assignedTo={“Tom”, “Harriet”})
Annotations for ‘public void NuclearPlant.notInUse()’:
  @java.lang.Deprecated(forRemoval=false, since=”10″)
Annotations for ‘public void NuclearPlant.changeNuclearRods()’:
  @TaskInfo(priority=LOW, taskDesc=”Exchange nuclear rods”,
            assignedTo={“Tom”, “Dick”})
Annotations for ‘public void NuclearPlant.adjustNuclearFuel()’:
  @TaskInfo(priority=LOW, taskDesc=”Adjust nuclear fuel”, assignedTo={“Harriet”})
Annotations for ‘public void NuclearPlant.startNuclearReactor()’:
  @TaskInfo(priority=NORMAL, taskDesc=”Start nuclear reactor”,
            assignedTo={“Dick”})
Annotations for ‘public java.lang.String NuclearPlant.toString()’:
  @Pending()
Annotations for ‘public boolean NuclearPlant.outOfProduction’:
  @java.lang.Deprecated(forRemoval=true, since=”8″)

Leave a Reply

Your email address will not be published. Required fields are marked *