corner imagecorner image
IDEPlatformPluginsDocs & SupportCommunityPartners

NetBeans IDE での注釈プロセッサのサポート、パート II: IDE での独自のカスタム注釈プロセッサの使用

執筆: Jesse Glick、執筆および管理: Irina Filippova

このページの内容は NetBeans IDE 6.9、7.0、および 7.1 が対象です

チュートリアルのこの節では、ユーザーが自身で書いたカスタム注釈プロセッサを IDE のプロジェクトに追加する方法を学習します。このチュートリアルでは注釈プロセッサの記述方法については扱いません。NetBeans IDE プロジェクトに追加する方法を説明します。

この節で使用されているサンプルアプリケーションは Jesse Glick 氏によって作成され、以前の IDE リリースの FAQ エントリとして公開されました。

例として使用されている注釈プロセッサは、注釈付きクラスの親クラスを生成します。生成された親クラスには、注釈付きクラスから呼び出されるメソッドも含まれています。次の手順に従って、カスタム注釈プロセッサを作成し、IDE のプロジェクトに追加します。

このチュートリアルを完了するには、次のソフトウェアとリソースが必要です。

ソフトウェアまたはリソース 必須バージョン
NetBeans IDE 6.9, 7.0, 7.1
Java Development Kit (JDK) version 6

注:

  • カスタム注釈プロセッサのサポートは、NetBeans IDE 6.9 リリースで追加されました。このチュートリアルは、それより前のバージョンの IDE では動作しません。

注釈の定義および注釈プロセッサの作成

この課題では、クラスライブラリプロジェクトを作成します。

  1. 「ファイル」>「新規プロジェクト」を選択し、「Java」カテゴリで「Java クラスライブラリ」のプロジェクトの種類を選択します。「次へ」をクリックします。
  2. 「プロジェクト名」として「AnnProcessor」と入力し、プロジェクトの場所を指定します。「完了」をクリックします。

    「完了」をクリックすると、IDE によってクラスライブラリプロジェクトが作成され、そのプロジェクトが「プロジェクト」ウィンドウに表示されます。

  3. 「プロジェクト」ウィンドウで「AnnProcessor」プロジェクトノードを右クリックし、「プロパティー」を選択します。
  4. 「ソース」カテゴリで、ソース/バイナリ形式として「JDK 6」が指定されていることを確認します。
  5. 「ライブラリ」タブを選択し、「Java プラットフォーム」が「JDK 6」に設定されていることを確認します。「了解」をクリックして、「プロジェクトプロパティー」ウィンドウを閉じます。

この課題では、2 つの Java パッケージを作成し、各パッケージ内に 1 つの Java クラスを作成します。

  1. 「AnnProcessor」プロジェクトノードの下にある「ソースパッケージ」ノードを右クリックし、「新規」>「Java パッケージ」を選択することによって、ann という名前の Java パッケージを作成します。「パッケージ名」に「ann」と入力し、「完了」をクリックします。
  2. 前の手順を繰り返して proc という名前の Java パッケージを作成します。

    2 つの Java パッケージを作成すると、プロジェクトの構造は次の図のようになるはずです。

    Java パッケージを示す「プロジェクト」ウィンドウのスクリーンショット
  3. ann」Java パッケージを右クリックし、「新規」>「Java クラス」を選択します。
  4. 「クラス名」に「Handleable」と入力します。「完了」をクリックします。
  5. 新しい Handleable.java ファイルを編集して、次の変更を加えます。ファイルを保存します。
    package ann;
    
    public @interface Handleable {
    
    }

    これは注釈を宣言するための方法であり、インタフェースの宣言と非常によく似ています。異なるのは、interface キーワードの前に at 記号 (@) が必要であることです。この注釈は Handleable と呼ばれます。

    追加情報。注釈宣言では、注釈を付けられる要素の種類 (クラスやメソッドなど) のような、追加のパラメータを指定することもできます。これを行うには、@Target(value = {ElementType.TYPE}) (クラスの場合) および @Target(value = {ElementType.METHOD}) を追加します。つまり、注釈宣言は、自身に meta-annotations で注釈が付けられます。

    ここで、Handleable 注釈を処理する注釈プロセッサのコードを追加する必要があります。

  6. proc」Java パッケージを右クリックし、「新規」>「Java クラス」を選択します。
  7. 「クラス名」に「HandleableProcessor」と入力します。「完了」をクリックします。
  8. HandleableProcessor.java クラスを変更して次のコードを追加します。変更を保存します。
    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);
        }
    }

    ここで、注釈プロセッサのコードを構成する主要な部分について少し詳しく見てみます (便宜上、コードの一部しか示されていないことに注意してください)。

    最初に、注釈プロセッサでサポートされている注釈の種類 (@SupportedAnnotationTypes を使用) と、サポートされているソースファイルのバージョン (@SupportedSourceVersion を使用。この場合のバージョンは JDK 6) を指定します。

    @SupportedAnnotationTypes("ann.Handleable")
    @SupportedSourceVersion(SourceVersion.RELEASE_6)

    次に、javax.annotation.processing パッケージの AbstractProcessor クラスを拡張する、このプロセッサのための公開クラスを宣言します。AbstractProcessor は、注釈を処理するために必要なメソッドを含む、具象注釈プロセッサの標準スーパークラスです。

    public class HandleableProcessor extends AbstractProcessor {
    ...
    }

    ここで、このクラスの public コンストラクタを指定する必要があります。

    public class HandleableProcessor extends AbstractProcessor {
        public HandleableProcessor() {
              }
    ...
    
    }

    次に、親 AbstractProcessor クラスの process() メソッドを呼び出します。このメソッドを通して、処理対象の注釈が提供されます。また、このメソッドには、処理のラウンドについての情報も含まれています。

    public class HandleableProcessor extends AbstractProcessor {
       ...
         public boolean process(Set annotations,
                RoundEnvironment roundEnv) {
         ...
         }
    
    }

    注釈プロセッサのロジックは、AbstractProcessor クラスの process() メソッド内に含まれています。AbstractProcessor を通して、ProcessingEnvironment のインタフェースにもアクセスします。これにより、注釈プロセッサは Filer (注釈プロセッサが新しいファイルを作成できるようになるファイラハンドラ) や Messager (注釈プロセッサがエラーを報告する手段) のような複数の便利な機能を使用できるようになります。

    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; } ... }

    このコードの最後のブロックでは、注釈付き要素の名前を大文字にするために使用される capitalize メソッドを宣言しています。

    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. AnnProcessor」プロジェクトを右クリックし、「構築」を選択することによってプロジェクトを構築します。

IDE での注釈プロセッサの使用

この節では、注釈プロセッサが使用される Java アプリケーションプロジェクトを作成します。

  1. 「ファイル」>「新規プロジェクト」を選択し、「Java」カテゴリで「Java アプリケーション」のプロジェクトの種類を選択します。「次へ」をクリックします。
  2. 「名前と場所」ページで、「プロジェクト名」として「Demo」と入力し、プロジェクトの場所を指定します。
  3. 「主クラスを作成」フィールドに「demo.Main」と入力します。「完了」をクリックします。
    「新規プロジェクト」ウィザードのスクリーンショット
  4. プロジェクトの「プロパティー」ウィンドウを開き、「ソース」パネルでソース/バイナリ形式として「JDK 6」が選択されていること、および「ライブラリ」パネルで「Java プラットフォーム」が「JDK 6」に設定されていることを確認します。
  5. Main.java クラスを変更して次のコードを追加します。変更を保存します。
    package demo;
    
    import ann.Handleable;
    
    public class Main extends MainExtras {
    
        @Handleable
        private String stuff;
    
        public static void main(String[] args) {
            new Main().handleStuff("hello");
        }
    }

    このコードには、次の要素が含まれています。

    • カスタム注釈プロセッサ ann.Handleable のインポート文
    • MainExtras クラスを拡張する公開クラス Main (MainExtras は、コンパイル中にこの注釈プロセッサによって生成される)
    • @Handleable 注釈が付いている stuff という名前の非公開フィールド
    • 自動的に生成された MainExtras クラスで宣言されている、handleStuff メソッドを呼び出す main メソッド

      この単純な例では、handleStuff メソッドは現在の値の出力だけを行います。ほかの処理を行うように、このメソッドを変更できます。

    Main.java コードを保存したあと、IDE によって複数のコンパイルエラーが報告されたことが表示されます。これは、まだこのプロジェクトに注釈プロセッサが追加されていないためです。

  6. 「プロジェクト」ウィンドウで「Demo」プロジェクトノードを右クリックし、「プロパティー」を選択したあと、「プロパティー」ウィンドウで「ライブラリ」カテゴリを選択します。
  7. 「コンパイル」タブで「プロジェクトを追加」をクリックし、「AnnProcessor」プロジェクトを指定します。
    プロジェクトの「プロパティー」ウィンドウの「ライブラリ」カテゴリ内の「コンパイル」タブのスクリーンショット

    「コンパイル」タブは、Java コンパイラ-classpath オプションに相当します。この注釈プロセッサは、注釈定義と注釈プロセッサの両方を含む単一の JAR ファイルであるため、これをプロジェクトのクラスパス (つまり「コンパイル」タブ) に追加する必要があります。

  8. 「プロパティー」ウィンドウで「コンパイル」カテゴリを選択し、「注釈処理を有効にする」および「エディタでの注釈処理を有効にする」チェックボックスを選択します。
  9. 「注釈プロセッサ」テキスト領域の横にある「追加」ボタンをクリックし、「注釈プロセッサ FQN」フィールドに「proc.HandleableProcessor」と入力することによって、実行する注釈プロセッサを指定します。
    「注釈プロセッサ FQN」ダイアログボックスのスクリーンショット

    「プロパティー」ウィンドウ内の「コンパイル」カテゴリは、次の図のように表示されるはずです。

    プロジェクトの「プロパティー」ウィンドウ内の「コンパイル」カテゴリのスクリーンショット
  10. 「プロパティー」ウィンドウで「了解」をクリックします。

    注: Main.java ファイルでは、まだコンパイルエラーが表示される可能性があります。これは、handleStuff メソッドを宣言している MainExtras.java ファイルを、IDE がまだ認識できていないためです。MainExtras.java ファイルは、最初に Demo プロジェクトを構築したあとで生成されます。プロジェクトで「保存時にコンパイル」が有効になっている場合は、Main.java を保存したときに IDE がプロジェクトをコンパイルしました。

  11. 「Demo」プロジェクトを右クリックし、「構築」を選択します。

    プロジェクトを構築したあとに「プロジェクト」ウィンドウでそのプロジェクトを見ると、新しい「生成されたソース」ノードが demo/MainExtras.java とともに表示されます。

    「生成されたソース」を含む「プロジェクト」ウィンドウのスクリーンショット

    生成された MainExtras.java ファイルの内容を確認すると、注釈プロセッサが handleStuff メソッドを使用して MainExtras クラスを生成したことがわかります。handleStuff メソッドは、注釈付きの Main.java ファイルから呼び出されるメソッドです。

    package demo;
    public abstract class MainExtras {
        protected MainExtras() {}
        /** Handle something. */
        protected final void handleStuff(java.lang.String value) {
            System.out.println(value);
        }
    }
  12. 「Demo」プロジェクトを右クリックし、「実行」を選択します。

    「実行」をクリックすると、「出力」ウィンドウに次が表示されるはずです。Demo プロジェクトがコンパイルされ、メッセージが出力されます。

    「生成されたソース」を含む「プロジェクト」ウィンドウのスクリーンショット

関連項目

Java アプリケーションでの注釈についての詳細は、次のリソースを参照してください。