web-dev-qa-db-ja.com

注釈プロセッサによるコード置換

クラスにメソッドとフィールドを挿入するために アノテーションプロセッサ を書き込もうとしています...そしてドキュメントはとてもまばらです。遠くまで行かず、正しく近づいているかどうかわかりません。

処理環境は、新しいソースファイルとクラスファイルを作成するための便利なメソッドを持つ Filer オブジェクトを提供します。これらは正常に機能しますが、既存のソースファイルを読み取る方法を理解しようとしましたが、提供されるのは「getResource」だけです。したがって、プロセッサの実装では、これを実行しました。

_@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    try {
        for (TypeElement te : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(te)) {
                FileObject in_file = processingEnv.getFiler().getResource(
                    StandardLocation.SOURCE_PATH, "",
                    element.asType().toString().replace(".", "/") + ".Java");

                FileObject out_file = processingEnv.getFiler().getResource(
                    StandardLocation.SOURCE_OUTPUT, "",
                    element.asType().toString().replace(".", "/") + ".Java");

                //if (out_file.getLastModified() >= in_file.getLastModified()) continue;

                CharSequence data = in_file.getCharContent(false);

                data = transform(data); // run the macro processor

                JavaFileObject out_file2 = processingEnv.getFiler().createSourceFile(
                    element.asType().toString(), element);
                Writer w = out_file2.openWriter();
                w.append(data);
                w.close();
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
    }
    return true;
}
_

私の最初の悩みは、element.asType().toString().replace(".", "/") + ".Java"(修飾された型名を取得してパッケージとソースファイルのパスに変換する)が問題に取り組む良い方法ではないと感じずにはいられないことです。 APIの残りの部分は非常に過剰に設計されていますが、元のソースコードを取得するための便利な方法はないようです。

本当の問題は、コンパイラが出力ディレクトリの2番目のソースファイルに自発的に動揺し(「エラー:クラスの重複」)、スタックすることです。

これの残りの部分(マクロレクサーとパーサー、および一部のデータを計算してフィールド値とメソッドを挿入するためのもの)はすでに作成しましたが、コンパイラーの外部の最初のステップとして機能します。元のファイルに.Java拡張子を付けることができないという事実を除いて(コンパイラがそれらを認識しないようにするため)、これはうまく機能します。それから、アノテーションはコード生成を行うことができると聞きました。これはより適切で便利だと思いますが、それに関するガイダンスはあまり見つかりません。

20
Boann

アノテーションプロセッサの背後にある意図は、開発者が既存のクラスを置き換えるのではなく、新しいクラスを追加できるようにすることです。そうは言っても、既存のクラスにコードを追加できるバグがあります。 Project Lombok has leveraged これは、コンパイルされたJavaクラスに(とりわけ)getterとsetterを追加します。

メソッド/フィールドを「置換」するために私が採用したアプローチは、入力クラスから拡張するか、入力クラスに委任することです。これにより、ターゲットクラスへの呼び出しをオーバーライド/転送できます。

したがって、これが入力クラスの場合:

InputImpl.Java:

public class InputImpl implmements Input{
    public void foo(){
        System.out.println("foo");
    }
    public void bar(){
        System.out.println("bar");
    }
}

以下を生成して「置き換える」ことができます。

InputReplacementImpl.Java:

public class InputReplacementImpl implmements Input{

    private Input delegate;

    //setup delegate....

    public void foo(){
        System.out.println("foo replacement");
    }
    public void bar(){
        delegate.bar();
    }
}

これは疑問を投げかけます、どのようにInputReplacementImplの代わりにInputImplを参照しますか。さらにコードを生成してラッピングを実行するか、生成されると予想されるコードのコンストラクターを呼び出すことができます。

あなたの質問が何であるかはよくわかりませんが、これがあなたの問題に光を当てることを願っています。

19
John Ericksen

少し遅れています:)が、1つの解決策は、ビルドプロセスの一部としてByteBuddyのトランスフォーマーを使用することです。たとえば https://github.com/raphw/byte-buddy/tree/master/byte-buddy-maven-plugin を参照してください。

0