Un’API Java per generare file sorgente Java

Sto cercando un framework per generare file sorgenti 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 simile 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 una manipolazione / generazione diretta di bytecode.
    4. Ho anche bisogno della “struttura della class” in un albero di oggetti.
    5. Il dominio del problema è generale: generare una grande quantità di classi molto diverse, senza una “struttura comune”.

    SOLUZIONI
    Ho pubblicato 2 risposte basate sulle tue risposte … con CodeModel e con Eclipse JDT .

    Ho usato CodeModel nella mia soluzione, 🙂

    Sun fornisce un’API chiamata CodeModel per la generazione di file di origine Java utilizzando un’API. Non è la cosa più facile da ottenere informazioni, ma è lì e funziona molto bene.

    Il modo più semplice per procurarselo è come parte del JAXB 2 RI: il generatore schema-java XJC usa CodeModel per generare il suo java source, ed è parte dei vasi XJC. Puoi usarlo solo per CodeModel.

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

    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/classs"); file.mkdirs(); cm.build(file); 

    Posso ottenere questo risultato:

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

    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 risultato:

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

    È ansible 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); 

    mostrerà il seguente output:

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

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

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

    Non so di una libreria, ma un motore di template generico potrebbe essere tutto ciò che serve. Ce ne sono un sacco , personalmente ho avuto una buona esperienza con FreeMarker

    Ho creato qualcosa che somiglia molto al tuo DSL teorico, chiamato “sourcegen”, ma tecnicamente invece di un progetto util per un ORM che ho scritto. La DSL ha il seguente aspetto:

     @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 carine come “Auto-organizza importa” qualsiasi FQCNs in parametri / tipi di ritorno, auto-potando i vecchi file che non sono stati toccati in questa esecuzione di codegen, indentando correttamente le classi interne, ecc.

    L’idea è che il codice generato dovrebbe essere bello da guardare, senza avvisi (importazioni inutilizzate, ecc.), Proprio come il resto del codice. Così tanto codice generato è brutto da leggere … è orribile.

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

    Se hai VERAMENTE bisogno della fonte, non so nulla che generi fonte. È comunque ansible utilizzare ASM o CGLIB per creare direttamente i file .class.

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

    Lo stavo facendo io stesso per uno strumento generatore fittizio. È un compito molto semplice, anche se è necessario seguire le linee guida di formattazione di Sun. Scommetto che finiresti il ​​codice per farlo più velocemente, quindi hai trovato qualcosa che si adatta al tuo objective su Internet.

    Hai praticamente delineato l’API tu stesso. Basta riempirlo con il codice attuale ora!

    C’è anche StringTemplate . È dell’autore di ANTLR ed è piuttosto potente.

    C’è un nuovo progetto write-it-once . Generatore di codice basato su template. Scrivi un modello personalizzato usando Groovy e genera file in base alle riflessioni java. È il modo più semplice per generare qualsiasi file. Puoi creare getter / settest / toString generando i file AspectJ, SQL basato su annotazioni JPA, inserti / aggiornamenti basati sull’enumerazione 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; } } 

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

    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 DIT 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//")); 

    Ecco un progetto JSON-to-POJO che sembra interessante:

    http://www.jsonschema2pojo.org/