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, que tipos de elementos podem ser anotados, ou seja, classes ou métodos. Faça isso adicionando @Target(value = {ElementType.TYPE}) para classes e @Target(value = {ElementType.METHOD). Portanto, a declaração de anotação se torna também anotada com meta-annotations.
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 extends TypeElement> 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. Através 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 extends TypeElement> 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 manipulador 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 extends TypeElement> 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. Se for, retorne um erro.
if (e.getKind() != ElementKind.FIELD) {
processingEnv.getMessager().printMessage(
Diagnostic.Kind.WARNING,
"Não é um campo", 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();
Adicione as linhas seguintes ao arquivo : 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);
}
}