Un'API Java per generare file sorgente Java [chiuso]


127

Sto cercando un framework per generare file sorgente Java.

Qualcosa come la seguente API:

X clazz = Something.createClass("package name", "class name");
clazz.addSuperInterface("interface name");
clazz.addMethod("method name", returnType, argumentTypes, ...);

File targetDir = ...;
clazz.generate(targetDir);

Quindi, un file di origine java dovrebbe essere trovato in una sottodirectory della directory di destinazione.

Qualcuno conosce un tale quadro?


MODIFICA :

  1. Ho davvero bisogno dei file di origine.
  2. Vorrei anche compilare il codice dei metodi.
  3. Sto cercando un'astrazione di alto livello, non la manipolazione / generazione di bytecode.
  4. Ho anche bisogno della "struttura della classe" in un albero di oggetti.
  5. Il dominio problematico è generale: generare una grande quantità di classi molto diverse, senza una "struttura comune".

SOLUZIONI
Ho pubblicato 2 risposte in base alle tue risposte ... con CodeModel e con Eclipse JDT .

Ho usato CodeModel nella mia soluzione, :-)


La tua domanda è molto generale, il tuo dominio problematico è davvero così generale? Puoi essere più specifico sul tuo dominio problematico? Ad esempio, ho scritto strumenti di generazione del codice per generare codice per problemi specifici come l'eliminazione del codice della classe di eccezione duplicata o l'eliminazione della duplicazione negli enum.
Greg Mattes,

@Viewward: puoi spostare le risposte che hai inserito nella Domanda come 2 risposte separate di seguito. Quindi aggiungere un collegamento a ciascuno dalla domanda.
Ande Turner,

@Banengusk: grazie per avermelo chiesto, mi ha risparmiato ore di ricerche nelle parti più buie di Internet. @skaffman: Grande scoperta - hai reso un altro sviluppatore più a suo agio con il suo prossimo compito :)
Ran Biron,

Questa risposta SO risponde alla domanda per C ++ piuttosto che per Java, ma la risposta funziona anche per Java. stackoverflow.com/a/28103779/120163
Ira Baxter il

Risposte:


70

Sun fornisce un'API denominata CodeModel per la generazione di file sorgente Java mediante un'API. Non è la cosa più semplice per ottenere informazioni, ma è lì e funziona estremamente bene.

Il modo più semplice per ottenerlo è come parte del RI JAXB 2: il generatore da schema a java XJC utilizza CodeModel per generare la sua sorgente java ed è parte dei barattoli XJC. Puoi usarlo solo per CodeModel.

Prendi da http://codemodel.java.net/


2
È proprio quello di cui ho bisogno! Semplice e perfettamente funzionante. Grazie Skaffman!
Daniel Fanjul,


@ykaganovich Buona chiamata. È [ repo.maven.apache.org/maven2/com/sun/codemodel/… concesso in licenza in base a CDDL e GPL). Ho rimosso il mio commento precedente.
Brad Cupit,

46

Soluzione trovata con CodeModel
Grazie, skaffman .

Ad esempio, con questo codice:

JCodeModel cm = new JCodeModel();
JDefinedClass dc = cm._class("foo.Bar");
JMethod m = dc.method(0, int.class, "foo");
m.body()._return(JExpr.lit(5));

File file = new File("./target/classes");
file.mkdirs();
cm.build(file);

Posso ottenere questo output:

package foo;
public class Bar {
    int foo() {
        return  5;
    }
}

Sembra fantastico. Come si genera un metodo che restituisce un altro tipo che viene generato anche con CodeModel?
András Hummer,


@ AndrásHummer usa l'istanza restituita da cm._class(...)come argomento di tipo return per dc.method(...).
Hugo Baés,

28

Soluzione trovata con AST di Eclipse JDT
Grazie, Giles .

Ad esempio, con questo codice:

AST ast = AST.newAST(AST.JLS3);
CompilationUnit cu = ast.newCompilationUnit();

PackageDeclaration p1 = ast.newPackageDeclaration();
p1.setName(ast.newSimpleName("foo"));
cu.setPackage(p1);

ImportDeclaration id = ast.newImportDeclaration();
id.setName(ast.newName(new String[] { "java", "util", "Set" }));
cu.imports().add(id);

TypeDeclaration td = ast.newTypeDeclaration();
td.setName(ast.newSimpleName("Foo"));
TypeParameter tp = ast.newTypeParameter();
tp.setName(ast.newSimpleName("X"));
td.typeParameters().add(tp);
cu.types().add(td);

MethodDeclaration md = ast.newMethodDeclaration();
td.bodyDeclarations().add(md);

Block block = ast.newBlock();
md.setBody(block);

MethodInvocation mi = ast.newMethodInvocation();
mi.setName(ast.newSimpleName("x"));

ExpressionStatement e = ast.newExpressionStatement(mi);
block.statements().add(e);

System.out.println(cu);

Posso ottenere questo output:

package foo;
import java.util.Set;
class Foo<X> {
  void MISSING(){
    x();
  }
}

Posso chiedere: l'hai fatto come parte di un plug-in Java Eclipse o sei riuscito a usarlo come codice autonomo? Mi rendo conto che ha anni.
mtrc,

@mtrc Se ricordo bene, era un progetto java autonomo e normale in eclipse, che aggiungeva il barattolo corretto al classpath - ma non ricordo il nome del file.
Daniel Fanjul,

17

È possibile utilizzare Roaster ( https://github.com/forge/roaster ) per generare codice.

Ecco un esempio:

JavaClassSource source = Roaster.create(JavaClassSource.class);
source.setName("MyClass").setPublic();
source.addMethod().setName("testMethod").setPrivate().setBody("return null;")
           .setReturnType(String.class).addAnnotation(MyAnnotation.class);
System.out.println(source);

visualizzerà il seguente output:

public class MyClass {
   private String testMethod() {
       return null;
   }
}

9

Un'altra alternativa è l'AST di JDT di Eclipse, che è utile se è necessario riscrivere il codice sorgente Java arbitrario piuttosto che generare semplicemente il codice sorgente. (e credo che possa essere usato indipendentemente dall'eclissi).


1
Grande!! Un albero di sintassi astratto è quello che sto cercando ... Ora cercherò più informazioni sull'API ... Grazie !, :-)
Daniel Fanjul,

L'API è complessa, come mi aspettavo. Ma ha tutte le funzionalità di cui ho bisogno. Grazie Giles.
Daniel Fanjul,

1
Come menzionato da @gastaldi, il torrefattore (di JBoss Forge) è un ottimo wrapper per Eclipse JDT. Nasconde la complessità di JDT e fornisce una bella API per analizzare, modificare o scrivere codice java. github.com/forge/roaster
Jmini

4

Il progetto JET Eclipse può essere utilizzato per la generazione di sorgenti. Non penso che l'API sia esattamente come quella che hai descritto, ma ogni volta che ho sentito parlare di un progetto che sta generando sorgenti Java hanno usato JET o uno strumento nazionale.


3

Non conosci una libreria, ma un motore di template generico potrebbe essere tutto ciò di cui hai bisogno. Ce ne sono un sacco , personalmente ho avuto una buona esperienza con FreeMarker


2

Ho creato qualcosa che assomiglia molto al tuo DSL teorico, chiamato "sourcegen", ma tecnicamente invece di un progetto util per un ORM che ho scritto. Il DSL si presenta come:

@Test
public void testTwoMethods() {
    GClass gc = new GClass("foo.bar.Foo");

    GMethod hello = gc.getMethod("hello");
    hello.arguments("String foo");
    hello.setBody("return 'Hi' + foo;");

    GMethod goodbye = gc.getMethod("goodbye");
    goodbye.arguments("String foo");
    goodbye.setBody("return 'Bye' + foo;");

    Assert.assertEquals(
    Join.lines(new Object[] {
        "package foo.bar;",
        "",
        "public class Foo {",
        "",
        "    public void hello(String foo) {",
        "        return \"Hi\" + foo;",
        "    }",
        "",
        "    public void goodbye(String foo) {",
        "        return \"Bye\" + foo;",
        "    }",
        "",
        "}",
        "" }),
    gc.toCode());
}

https://github.com/stephenh/joist/blob/master/util/src/test/java/joist/sourcegen/GClassTest.java

Fa anche alcune cose ordinate come "Organizza automaticamente le importazioni" qualsiasi FQCN in parametri / tipi restituiti, eliminando automaticamente tutti i vecchi file che non sono stati toccati in questa esecuzione di codegen, rientrando correttamente nelle classi interne, ecc.

L'idea è che il codice generato dovrebbe essere carino da guardare, senza avvisi (importazioni inutilizzate, ecc.), Proprio come il resto del codice. Tanto codice generato è brutto da leggere ... è orribile.

Ad ogni modo, non ci sono molti documenti, ma penso che l'API sia piuttosto semplice / intuitiva. Il repository Maven è qui se qualcuno è interessato.


1

Se hai davvero bisogno della fonte, non so nulla che generi fonte. È tuttavia possibile utilizzare ASM o CGLIB per creare direttamente i file .class.

Potresti essere in grado di generare sorgente da questi, ma li ho usati solo per generare bytecode.


1

Lo stavo facendo da solo per uno strumento generatore di finto. È un compito molto semplice, anche se è necessario seguire le linee guida sulla formattazione di Sun. Scommetto che finiresti il ​​codice che lo fa più velocemente, poi hai trovato qualcosa che si adatta al tuo obiettivo su Internet.

Fondamentalmente hai delineato l'API da solo. Basta riempirlo con il codice attuale ora!


Hehehe ... Se non viene trovato alcun framework, lo scrivo. Vorrei molta funzionalità, quindi non la riceverò entro una mattina ...
Daniel Fanjul,


1

C'è un nuovo progetto scrivilo una volta . Generatore di codice basato su template. Scrivi template personalizzati usando Groovy e generi file a seconda delle riflessioni Java. È il modo più semplice per generare qualsiasi file. Puoi creare getter / settest / toString generando file AspectJ, SQL basato su annotazioni JPA, inserimenti / aggiornamenti basati su enum e così via.

Esempio di modello:

package ${cls.package.name};

public class ${cls.shortName}Builder {

    public static ${cls.name}Builder builder() {
        return new ${cls.name}Builder();
    }
<% for(field in cls.fields) {%>
    private ${field.type.name} ${field.name};
<% } %>
<% for(field in cls.fields) {%>
    public ${cls.name}Builder ${field.name}(${field.type.name} ${field.name}) {
        this.${field.name} = ${field.name};
        return this;
    }
<% } %>
    public ${cls.name} build() {
        final ${cls.name} data = new ${cls.name}();
<% for(field in cls.fields) {%>
        data.${field.setter.name}(this.${field.name});
<% } %>
        return data;
    }
}

0

Dipende davvero da cosa stai cercando di fare. La generazione del codice è un argomento in sé. Senza un caso d'uso specifico, suggerisco di esaminare la libreria di generazione / modello di codice di velocità. Inoltre, se stai eseguendo la generazione del codice offline, ti suggerirei di usare qualcosa come ArgoUML per passare dal diagramma UML / modello di oggetti al codice Java.


0

Esempio: 1 /

private JFieldVar generatedField;

2 /

String className = "class name";
        /* package name */
        JPackage jp = jCodeModel._package("package name ");
         /*  class name  */
        JDefinedClass jclass = jp._class(className);
        /* add comment */
        JDocComment jDocComment = jclass.javadoc();
        jDocComment.add("By AUTOMAT D.I.T tools : " + new Date() +" => " + className);
        // génération des getter & setter & attribues

            // create attribue 
             this.generatedField = jclass.field(JMod.PRIVATE, Integer.class) 
                     , "attribue name ");
             // getter
             JMethod getter = jclass.method(JMod.PUBLIC, Integer.class) 
                     , "attribue name ");
             getter.body()._return(this.generatedField);
             // setter
             JMethod setter = jclass.method(JMod.PUBLIC, Integer.class) 
                     ,"attribue name ");
             // create setter paramétre 
             JVar setParam = setter.param(getTypeDetailsForCodeModel(Integer.class,"param name");
             // affectation  ( this.param = setParam ) 
             setter.body().assign(JExpr._this().ref(this.generatedField), setParam);

        jCodeModel.build(new File("path c://javaSrc//"));

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.