corner imagecorner image
IDEPlatformPluginsDocs & SupportCommunityPartners

Suporte aos processadores de anotação no NetBeans IDE, Parte II: Usando processadores próprios personalizados de anotação no IDE

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

O conteúdo desta página se aplica ao NetBeans IDE 6.9, 7.0 e 7.1

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

O aplicativo de amostra usado nesta seção foi criado por Jesse Glick e publicado como uma entrada de FAQ para as versões anteriores do IDE.

O processador de anotação usando como exemplo gera uma classe principal para a classe anotada. A classe principal gerada também contém um método que é chamado da 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 6.9, 7.0, 7.1
Java Development Kit (JDK) versão 6

Observações:

  • O suporte a processadores de anotações personalizadas foi adicionado na versão 6.9 do IDE NetBeans. Este tutorial não funcionará com versões anteriores do IDE.

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 projeto de Bibliotecas de classes Java na categoria Java. Clique em Próximo.
  2. Digite AnnProcessor como nome do projeto e especifique uma localização para o projeto. Clique em Terminar.

    Ao clicar em Terminar, o IDE cria o projeto de biblioteca de classes e lista o projeto na janela Projetos.

  3. Clique com o botão direito do mouse no nó do projeto AnnProcessor na janela Projetos e escolha Propriedades.
  4. Na categoria Códigos-fonte, confirme se JDK 6 está especificado como o formato de código-fonte/binário.
  5. Selecione a aba Bibliotecas e confirme se a plataforma Java está definida como JDK 6. 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. Crie um pacote Java denominado ann clicando no nó Pacotes de código-fonte sob o nó do projeto AnnProcessor e escolha Novo > Pacote Java. Digite ann como o nome do pacote e clique em Terminar.
  2. Repita a etapa anterior e crie um pacote Java denominado proc.

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

    captura de tela da janela Projetos mostrando os pacotes Java
  3. Clique com o botão direito do mouse no pacote Java ann e selecione Nova > Classe Java.
  4. Digite Handleable para o nome da classe. Clique em Terminar.
  5. 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.

  6. Clique com o botão direito do mouse no pacote proc e selecione Nova > Classe Java.
  7. Digite HandleableProcessor para o nome da classe. Clique em Terminar.
  8. 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. 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 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 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);
    }
    }
  9. 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 de aplicativo Java no qual o processador de anotações será usado.

  1. Escolha Arquivo > Novo projeto e selecione o tipo de projeto aplicativo Java na categoria Java. Clique em Próximo.
  2. Na página Nome e localização, digite Demo como o nome do projeto e especifique o local do projeto.
  3. Digite demo.Main no campo Criar classe principal. Clique em Terminar.
    captura de tela do assistente Novo projeto
  4. Abra a janela Propriedades do projeto e confirme se o JDK 6 está selecionado como o formato de código-fonte/binário no painel Código-fonte e que a plataforma Java esteja definida como JDK 6 no painel Bibliotecas.
  5. 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:

    • declaração de importação para o processador personalizado de anotação ann.Handleable
    • a classe pública Main que estende a classe MainExtras (MainExtras deveria 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.

  6. Clique com o botão direito do mouse no nó do projeto Demo na janela Projetos, escolha Propriedades e, a seguir, selecione a categoria Bibliotecas na janela Propriedades.
  7. Na aba Compilar, clique em Adicionar projeto e localize o projeto AnnProcessor.
    captura de tela da aba Compilar na categoria Bibliotecas da janela Propriedades do projeto

    A aba 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 aba Compilar.

  8. Selecione a categoria Compilar na janela Propriedades e selecione as caixas de seleção Ativar o processamento de anotações e Ativar o processamento de anotações no editor.
  9. Especifique o processador de anotações a ser executado clicando no botão Adicionar próximo à área de texto Processadores de anotações e digite proc.HandleableProcessor no campo Processador de anotações FQN.
    captura de tela da caixa de diálogo Processador de anotações FQN

    A categoria Compilar na janela Propriedades deve se parecer com a imagem a seguir.

    captura de tela da categoria Compilar na janela Propriedades
  10. Clique em OK na janela Propriedades.

    Observação. No arquivo Main.java, você ainda poderá ver 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.

  11. Clique com o botão direito do mouse no projeto Demo e escolha Compilar.

    Após compilar 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.

    captura de tela da janela Projetos com Códigos-fonte gerados

    Caso você reveja 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);
        }
    }
  12. Clique com o botão direito do mouse no projeto Demo e escolha Executar.

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

    captura de tela da janela Projetos com códigos-fonte gerados

Veja também

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