Suporte aos Processadores de Anotação no NetBeans IDE, Parte II: Utilizando Processadores Próprios de Anotação Personalizada no IDE

This tutorial needs a review. You can open a JIRA issue, or edit it in GitHub following these contribution guidelines.

_Contribuição de Jesse Glick, redigido e mantido por Irina Filippova _

  • Utilizando Processadores Próprios de Anotação Personalizada no IDE

netbeans stamp 71 72 73
Figure 1. O conteúdo desta página se aplica ao NetBeans IDE 7.0, 7.1, 7.2 e 7.3

Nesta seção do tutorial, você aprenderá a adicionar um processador de anotação personalizado de redação própria a um projeto no IDE. Este tutorial não ensina como criar um processador de anotação. Ele explica como adicioná-lo a um projeto NetBeans IDE.

A aplicação de amostra usada nesta seção foi criada por Jesse Glick e publicada como uma entrada de FAQ das releases anteriores do IDE.

O processador de anotação usado como exemplo gera uma classe principal da classe anotada. A classe principal gerada também contém um método chamado usando a classe anotada. Siga as instruções abaixo sobre como criar e adicionar um processador personalizado de anotação em um projeto do IDE.

Para concluir este tutorial, você precisa dos seguintes recursos e softwares.

Software ou Recurso Versão Necessária

NetBeans IDE

7.0, 7.1, 7.2, 7.3

JDK (Java Development Kit)

versão 6 ou 7

lombok.jar

v1.12.4 ou mais recente

Definindo uma Anotação e Criando um Processador de Anotação

Neste exercício, você criará um projeto de biblioteca de classes.

  1. Escolha Arquivo > Novo Projeto e selecione o tipo de projeto de Bibliotecas de Classes Java na categoria Java. Clique em Próximo.

  2. Digite * AnnProcessor * como Nome do Projeto e especifique um local para o projeto. Clique em Finalizar.

Quando você clica em Finalizar, o IDE cria o projeto de biblioteca de classes e lista o projeto na janela Projetos.

  1. Clique com o botão direito do mouse no nó do projeto AnnProcessor na janela Projetos e escolha Propriedades.

  2. Na categoria Códigos-fonte, confirme se JDK 6 ou JDK 7 está especificado como o formato de código-fonte/binário.

  3. Selecione a guia Bibliotecas e confirme se a plataforma Java está definida como JDK 1.6 ou JDK 1.7. Clique em OK para fechar a janela Propriedades do Projeto.

Neste exercício, você criará dois pacotes Java e uma classe Java em cada um destes pacotes.

  1. Clique com o botão direito do mouse no nó Pacotes de Código-fonte sob o nó do projeto AnnProcessor e escolha Novo > Pacote Java.

  2. Digite * ann * como Nome do Pacote e clique em Finalizar para criar o novo pacote Java.

  3. Repita as duas etapas anteriores para criar um pacote Java chamado * proc *.

Após ter criado os dois pacotes Java, a estrutura do projeto deverá ser similar à seguinte imagem.

packages
Figure 2. A estrutura do projeto para o processador de anotação.
  1. Clique com o botão direito do mouse no pacote Java ann e selecione Nova > Classe Java.

  2. Digite * Handleable * para o nome da classe. Clique em Finalizar.

  3. 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 .

  1. Clique com o botão direito do mouse no pacote * proc * e selecione Nova > Classe Java.

  2. Digite * HandleableProcessor * para o Nome da Classe. Clique em Finalizar.

  3. Modifique a classe HandleableProcessor.java para adicionar o código a seguir. Salve as alterações.

Observação. O valor de @SupportedSourceVersion (em negrito) dependerá da versão do JDK que você está usando e será (SourceVersion.RELEASE_7) ou (SourceVersion.RELEASE_6) .

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_7*)
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. 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<? 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 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<? 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. 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);
}
*}
  1. Compile o projeto clicando com o botão direito do mouse no projeto AnnProcessor e escolhendo Compilar.

Usando o processador de anotação no IDE

Nesta seção, você criará um projeto da Aplicação Java no qual o processador de anotações será usado.

  1. Escolha Arquivo > Novo Projeto e selecione o tipo de projeto da Aplicação Java na categoria Java. Clique em Próximo.

  2. Na página Nome e Localização, digite * Demo * como Nome do Projeto e especifique o local do projeto.

  3. Digite * demo.Main * no campo Criar Classe Principal. Clique em Finalizar.

demo project wizard
Figure 3. Criando projeto de Demonstração no assistente de Novo Projeto.
  1. Abra a janela Propriedades do Projeto e confirme se JDK 6 ou JDK 7 está selecionado como o formato de código-fonte/binário no painel Códigos-fonte e se a plataforma Java está definida como JDK 1.6 ou JDK 1.7 no painel Bibliotecas.

  2. Modifique a classe Main.java para adicionar o código a seguir. Salve as alterações.

package demo;

*import ann.Handleable;*

public class Main *extends MainExtras* {

    *@Handleable
    private String stuff;*

    *public static void main(String[] args) {
        new Main().handleStuff("hello");
    }*
}

O código contém os seguintes elementos:

  • instrução de importação para o processador personalizado de anotação ann.Handleable

  • a classe pública Main que estende a classe MainExtras ( MainExtras deve ser gerada por seu processador de anotação durante a compilação)

  • um campo privado denominado stuff que é anotado com a anotação @Handleable

  • o método main que chama o método handleStuff , declarado na classe MainExtras automaticamente gerada

Em nosso exemplo simples, o método handleStuff somente imprime o valor atual. Você pode modificar este método para executar outras tarefas.

Após salvar o código Main.java , você verá que o IDE relata diversos erros de compilação. Isso acontece porque o processador de anotação ainda não foi adicionado no projeto.

  1. Clique com o botão direito do mouse no nó do projeto Demo , na janela Projetos, escolha Propriedades e, em seguida, selecione a categoria Bibliotecas na janela Propriedades do Projeto.

  2. Na guia Compilar, clique em Adicionar Projeto e localize o projeto AnnProcessor .

demo properties compile
Figure 4. Guia Compilar na categoria Bibliotecas da janela Propriedades do projeto

A guia Compilar corresponde a opção -classpath do compilador Java. Como o processador de anotação é um arquivo JAR único que contém a definição da anotação e o processador de anotação, ele deve ser adicionado na classpath do projeto, que é a guia Compilar.

  1. Selecione a categoria Compilação na janela Propriedades do Projeto e marque as caixas de seleção Ativar Processamento de Anotações e Ativar Processamento de Anotações no Editor.

  2. Especifique o processador de anotações a ser executado clicando no botão Adicionar ao lado da área de texto Processadores de Anotações e digitando * proc.HandleableProcessor * no campo FQN do Processador de Anotações.

demo processor fqn
Figure 5. Caixa de diálogo FQN do Processador de Anotação

A categoria Compilação na janela Propriedades do Projeto deve ser semelhante à imagem a seguir.

demo properties compiling
Figure 6. Categoria compilação na janela Propriedades do projeto
  1. Clique em OK na janela Propriedades.

Observação. No arquivo Main.java talvez você ainda veja os erros de compilação. Isso é porque o IDE ainda não pode localizar o arquivo MainExtras.java que declara o método handleStuff . O arquivo MainExtras.java será gerado após você desenvolver o projeto Demo pela primeira vez. Se Compilar ao Salvar estiver ativado para seu projeto, o IDE compilou o projeto quando você salvou o Main.java .

  1. Clique com o botão direito do mouse no projeto Demo e escolha Construir.

Após construir o projeto, se você examinar os projetos na janela Projetos, poderá agora ver um novo nó Códigos-fonte Gerados com o arquivo demo/MainExtras.java .

demo generated sources
Figure 7. Janela Projetos com Origens Geradas

Caso você revise o conteúdo do arquivo MainExtras.java gerado, verá que o processador de anotações gerou a classe MainExtras com o método handleStuff . O método handleStuff é aquele chamado a partir do arquivo Main.java anotado.

package demo;
public abstract class MainExtras {
    protected MainExtras() {}
    /** Handle something. */
    protected final void handleStuff(java.lang.String value) {
        System.out.println(value);
    }
}
  1. Clique com o botão direito do mouse no projeto Demonstração e escolha Executar.

Quando você clicar em Executar, deverá ver o seguinte na janela Saída. O projeto Demonstração é compilado e imprime a mensagem.

demo run
Figure 8. Janela Projetos com Origens Geradas

Consulte Também

Consulte os seguintes recursos para obter mais informações sobre anotações em aplicações Java: