Modifique o novo arquivo Handleable.java para fazer as alterações a seguir. Salve o arquivo.
package ann;
public @interface Handleable {
}
É assim que as anotações são declaradas, de forma muito similar a uma declaração de interface. A diferença é que a palavra-chave interface precisa ser precedida por um sinal at (@). Essa anotação é denominada Handleable.
Informações Adicionais. Nas declarações de anotação, você também pode especificar parâmetros adicionais, por exemplo, quais tipos de elementos podem ser anotados, por exemplo, classes ou métodos. Você fazer isso adicionando @Target(value = {ElementType.TYPE}) para classes e @Target(value = {ElementType.METHOD}). Isso, a declaração de anotação torna-se anotada com meta-anotações.
Agora precisamos adicionar um código para que o processador de anotação processe a anotação Handleable.
Modifique a classe HandleableProcessor.java para adicionar o código a seguir. Salve as alterações.
package proc;
import ann.Handleable;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
@SupportedAnnotationTypes("ann.Handleable")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class HandleableProcessor extends AbstractProcessor {
/** public for ServiceLoader */
public HandleableProcessor() {
}
public boolean process(Set annotations,
RoundEnvironment roundEnv) {
for (Element e : roundEnv.getElementsAnnotatedWith(Handleable.class)) {
if (e.getKind() != ElementKind.FIELD) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.WARNING,
"Not a field", e);
continue;
}
String name = capitalize(e.getSimpleName().toString());
TypeElement clazz = (TypeElement) e.getEnclosingElement();
try {
JavaFileObject f = processingEnv.getFiler().
createSourceFile(clazz.getQualifiedName() + "Extras");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
"Creating " + f.toUri());
Writer w = f.openWriter();
try {
PrintWriter pw = new PrintWriter(w);
pw.println("package "
+ clazz.getEnclosingElement().getSimpleName() + ";");
pw.println("public abstract class "
+ clazz.getSimpleName() + "Extras {");
pw.println(" protected " + clazz.getSimpleName()
+ "Extras() {}");
TypeMirror type = e.asType();
pw.println(" /** Handle something. */");
pw.println(" protected final void handle" + name
+ "(" + type + " value) {");
pw.println(" System.out.println(value);");
pw.println(" }");
pw.println("}");
pw.flush();
} finally {
w.close();
}
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
x.toString());
}
}
return true;
}
private static String capitalize(String name) {
char[] c = name.toCharArray();
c[0] = Character.toUpperCase(c[0]);
return new String(c);
}
}
Vamos examinar mais de perto as partes principais que compõem o código para o processador de anotações (observe que, por conveniência, somente partes do código são fornecidas).
Primeiro, você especifica os tipos de anotações que o processador de anotações suporta (usando @SupportedAnnotationTypes) e a versão dos arquivos de código-fonte que são suportados (usando @SupportedSourceVersion); nesse caso, a versão é JDK 6:
@SupportedAnnotationTypes("ann.Handleable")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
A seguir, declare uma classe pública para o processador que estenda a classe AbstractProcessor do pacote javax.annotation.processing. AbstractProcessor é a superclasse padrão para processadores de anotação concretos, que contém os métodos necessários para processar anotações.
public class HandleableProcessor extends AbstractProcessor {
...
}
Você agora precisa fornecer um construtor público para a classe.
public class HandleableProcessor extends AbstractProcessor {
public HandleableProcessor() {
}
...
}
A seguir, chame o método de process() da classe AbstractProcessor principal. Por meio deste método, as anotações disponíveis para processamento são fornecidas. Além disso, este método contém informações sobre o ciclo de processamento.
public class HandleableProcessor extends AbstractProcessor {
...
public boolean process(Set annotations,
RoundEnvironment roundEnv) {
...
}
}
A lógica do processador de anotação está contida dentro do método process() da classe AbstractProcessor. Observe que, por meio de AbstractProcessor, você também acessa a interface ProcessingEnvironment, que permite que os processadores de anotação usem diversos recursos úteis, como um Filer (um handler de arquivamento que permite que os processadores de anotação criem novos arquivos) e um Messager (um meio pelo qual os processadores de anotação reportam erros).
public class HandleableProcessor extends AbstractProcessor {
...
public boolean process(Set annotations,
RoundEnvironment roundEnv) {
//For each element annotated with the Handleable annotation
for (Element e : roundEnv.getElementsAnnotatedWith(Handleable.class)) {
//Check if the type of the annotated element is not a field. If yes, return a warning.
if (e.getKind() != ElementKind.FIELD) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.WARNING,
"Not a field", e);
continue;
}
//Define the following variables: name and clazz.
String name = capitalize(e.getSimpleName().toString());
TypeElement clazz = (TypeElement) e.getEnclosingElement();
//Generate a source file with a specified class name.
try {
JavaFileObject f = processingEnv.getFiler().
createSourceFile(clazz.getQualifiedName() + "Extras");
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
"Creating " + f.toUri());
Writer w = f.openWriter();
//Add the content to the newly generated file.
try {
PrintWriter pw = new PrintWriter(w);
pw.println("package "
+ clazz.getEnclosingElement().getSimpleName() + ";");
pw.println("public abstract class "
+ clazz.getSimpleName() + "Extras {");
pw.println(" protected " + clazz.getSimpleName()
+ "Extras() {}");
TypeMirror type = e.asType();
pw.println(" /** Handle something. */");
pw.println(" protected final void handle" + name
+ "(" + type + " value) {");
pw.println(" System.out.println(value);");
pw.println(" }");
pw.println("}");
pw.flush();
} finally {
w.close();
}
} catch (IOException x) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
x.toString());
}
}
return true;
}
...
}
O último bloco neste código declara o método capitalize que é usado para colocar em maiúscula o nome do elemento anotado.
public class HandleableProcessor extends AbstractProcessor {
...
private static String capitalize(String name) {
char[] c = name.toCharArray();
c[0] = Character.toUpperCase(c[0]);
return new String(c);
}
}