2012-12-03 9 views
17

Sto provando a scrivere un annotation processor per inserire metodi e campi su una classe ... e la documentazione è così scarsa. Non sto andando lontano e non so se mi sto avvicinando correttamente.Sostituzione codice con un processore di annotazione

L'ambiente di elaborazione fornisce un oggetto Filer che dispone di metodi pratici per la creazione di nuovi file di origine e di classe. Questi funzionano bene ma poi ho cercato di capire come leggere i file sorgente esistenti e tutto ciò che fornisce è "getResource". Così nel mio implementazione processore Ho fatto questo:

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

Il mio primo dilemma è che non posso evitare di pensare che element.asType().toString().replace(".", "/") + ".java" (per ottenere il nome di tipo qualificato e convertirlo in un percorso di pacchetto e file di origine) non è un buon modo per affrontare il problema. Il resto dell'API è così sovradimensionato, ma non sembra essere un metodo pratico per recuperare il codice sorgente originale.

Il vero problema è che quindi il compilatore viene spontaneamente sconvolto dal secondo file sorgente nella directory di output ("error: duplicate class") e ora sono bloccato.

Ho già scritto il resto di questo - un macro lexer e parser e quant'altro per il calcolo di alcuni dati e l'inserimento dei valori e dei metodi del campo - ma funziona come un passo iniziale al di fuori del compilatore. Tranne il fatto che i file originali non possono avere un'estensione .java (per impedire al compilatore di vederli), questo funziona bene. Poi ho sentito che le annotazioni possono fare la generazione del codice, che presumo sarà più appropriato e conveniente, ma non riesco a trovare molte indicazioni su di esso.

+1

Vedere: http://techbitsfromsridhar.blogspot.ca/2013/02/java-compiler-plug-ins-in-java-8-use.html –

risposta

16

L'intenzione dietro il processore di annotazione è consentire a uno sviluppatore di aggiungere nuove classi, non sostituire le classi esistenti. Detto questo, c'è un bug che ti permette di aggiungere codice alle classi esistenti. Project Lombok ha leveraged questo per aggiungere getter e setter (tra le altre cose) alle tue classi java compilate.

L'approccio che ho adottato per sostituire i metodi/campi è estendere o delegare alla classe di input. Ciò consente di ignorare/deviare le chiamate alla classe di destinazione.

Quindi, se questa è la vostra classe di ingresso:

InputImpl.java:

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

si potrebbe generare il seguente a "sostituire" it:

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(); 
    } 
} 

Questo pone la domanda, come si fa riferimento a InputReplacementImpl ins tead di InputImpl. Puoi generare un po 'di codice per eseguire il wrapping o semplicemente chiamare il costruttore del codice che dovrebbe essere generato.

Non sono proprio sicuro di quale sia la tua domanda, ma spero che questo chiarisca i tuoi problemi.

+0

* Ah ah! * Penso di aver visto Lombok prima, e questo era in parte il motivo per cui non avevo motivo di pensare che fosse impossibile. Questa risposta spiega l'errore del compilatore e mi dà alcune scelte. Non desidero essere impantanato negli hack di AST quando, attualmente, le espressioni regolari semplici fanno trovare e sostituire il codice per me. Delegare la classe non è realmente fattibile nel mio caso perché la classe originale non è completa e compilabile.Per quanto vedo, questa stupida limitazione dell'API di annotazioni significa che non mi è utile, nonostante la sua complessità. Ma ora so cosa fare: non usare annotazioni! Grazie mille per il tuo aiuto. – Boann

+0

Una cosa sicura @Boann, buona fortuna per il tuo progetto. –