API Java untuk menghasilkan file sumber Java [ditutup]

127

Saya mencari kerangka kerja untuk menghasilkan file sumber Java.

Sesuatu seperti API berikut:

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

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

Kemudian, file sumber java harus ditemukan di sub-direktori dari direktori target.

Apakah ada yang tahu kerangka kerja seperti itu?


EDIT :

  1. Saya sangat membutuhkan file sumber.
  2. Saya juga ingin mengisi kode metode.
  3. Saya mencari abstraksi tingkat tinggi, bukan manipulasi bytecode langsung / generasi.
  4. Saya juga membutuhkan "struktur kelas" di pohon benda.
  5. Domain masalahnya adalah umum: untuk menghasilkan sejumlah besar kelas yang sangat berbeda, tanpa "struktur umum".

SOLUSI
Saya telah mengirim 2 jawaban berdasarkan jawaban Anda ... dengan CodeModel dan dengan Eclipse JDT .

Saya telah menggunakan CodeModel dalam solusi saya, :-)

Daniel Fanjul
sumber
Pertanyaan Anda sangat umum, apakah domain masalah Anda benar-benar umum? Bisakah Anda lebih spesifik tentang domain masalah Anda? Sebagai contoh, saya telah menulis alat penghasil kode untuk menghasilkan kode untuk masalah khusus seperti menghilangkan kode kelas pengecualian duplikat, atau menghilangkan duplikasi di enum.
Greg Mattes
@ Lihat: Anda dapat memindahkan jawaban yang telah Anda tempatkan di Pertanyaan sebagai 2 jawaban terpisah di bawah ini. Kemudian tambahkan tautan ke masing-masing dari Pertanyaan.
Ande Turner
@ Banengusk: Terima kasih telah bertanya, menyelamatkan saya berjam-jam mencari bagian paling gelap dari internet. @skaffman: Great ditemukan - Anda membuat pengembang lain lebih nyaman dengan tugasnya yang akan datang :)
Ran Biron
Jawaban SO ini membahas pertanyaan untuk C ++ daripada Java, tetapi jawabannya juga berfungsi untuk Java. stackoverflow.com/a/28103779/120163
Ira Baxter

Jawaban:

70

Sun menyediakan API yang disebut CodeModel untuk menghasilkan file sumber Java menggunakan API. Ini bukan hal termudah untuk mendapatkan informasi, tetapi ada di sana dan berfungsi dengan sangat baik.

Cara termudah untuk mendapatkannya adalah sebagai bagian dari JAXB 2 RI - generator skema-ke-Jawa XJC menggunakan CodeModel untuk menghasilkan sumber java-nya, dan itu adalah bagian dari toples XJC. Anda dapat menggunakannya hanya untuk CodeModel.

Raih dari http://codemodel.java.net/

skaffman
sumber
2
Itu yang saya butuhkan! Sederhana dan berfungsi penuh. Terima kasih, skaffman!
Daniel Fanjul
@ykaganovich Panggilan bagus. Ini [ repo.maven.apache.org/maven2/com/sun/codemodel/... dilisensikan dengan CDDL dan GPL). Saya menghapus komentar saya sebelumnya.
Brad Cupit
46

Solusi ditemukan dengan CodeModel
Terima kasih, skaffman .

Misalnya, dengan kode ini:

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

Saya bisa mendapatkan output ini:

package foo;
public class Bar {
    int foo() {
        return  5;
    }
}
Daniel Fanjul
sumber
Ini terlihat luar biasa. Bagaimana Anda menghasilkan metode yang mengembalikan tipe lain yang sedang dibuat dengan CodeModel juga?
András Hummer
@DrH, pencarian google sederhana: codemodel.java.net/nonav/apidocs/com/sun/codemodel/…
Daniel Fanjul
@ AndrásHummer menggunakan instance yang dikembalikan dari cm._class(...)sebagai argumen tipe pengembalian dc.method(...).
Hugo Baés
28

Solusi ditemukan dengan AST Eclipse JDT
Terima kasih, Giles .

Misalnya, dengan kode ini:

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

Saya bisa mendapatkan output ini:

package foo;
import java.util.Set;
class Foo<X> {
  void MISSING(){
    x();
  }
}
Daniel Fanjul
sumber
Dapatkah saya bertanya - apakah Anda melakukan ini sebagai bagian dari Plugin Java Eclipse atau apakah Anda berhasil menggunakan ini sebagai kode mandiri? Saya menyadari ini berumur bertahun-tahun.
mtrc
@ mtrc Jika saya ingat dengan baik, itu adalah proyek java mandiri dan normal dalam gerhana, menambahkan toples yang tepat ke classpath - tapi saya tidak ingat nama filenya.
Daniel Fanjul
17

Anda dapat menggunakan Roaster ( https://github.com/forge/roaster ) untuk melakukan pembuatan kode.

Berikut ini sebuah contoh:

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

akan menampilkan output berikut:

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

Alternatif lain adalah AST Eclipse JDT yang baik jika Anda perlu menulis ulang kode sumber Java sewenang-wenang daripada hanya menghasilkan kode sumber. (dan saya percaya ini dapat digunakan secara independen dari gerhana).

Tupai
sumber
1
Bagus!! Pohon Sintaks Abstrak adalah apa yang saya cari ... Sekarang saya akan mencari lebih banyak info tentang API ... Terima kasih !, :-)
Daniel Fanjul
API itu kompleks, seperti yang saya harapkan. Tetapi ia memiliki semua fungsi yang saya butuhkan. Terima kasih, Giles.
Daniel Fanjul
1
Seperti yang disebutkan oleh @gastaldi, roaster (dari JBoss Forge) adalah pembungkus yang bagus untuk Eclipse JDT. Ini menyembunyikan kompleksitas JDT dan menyediakan API yang bagus untuk mem-parsing, memodifikasi atau menulis kode java. github.com/forge/roaster
Jmini
4

Proyek Eclipse JET dapat digunakan untuk melakukan pembuatan sumber. Saya tidak berpikir itu API persis seperti yang Anda gambarkan, tetapi setiap kali saya mendengar tentang proyek yang melakukan pembuatan sumber Java, mereka telah menggunakan JET atau alat buatan lokal.

Mike Deck
sumber
3

Tidak tahu perpustakaan, tetapi mesin templat generik mungkin yang Anda butuhkan. Ada banyak dari mereka , saya secara pribadi memiliki pengalaman yang baik dengan FreeMarker

ykaganovich
sumber
2

Saya membuat sesuatu yang sangat mirip dengan DSL teoretis Anda, yang disebut "sourcegen", tetapi secara teknis alih-alih proyek util untuk ORM yang saya tulis. DSL terlihat seperti:

@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

Ini juga melakukan beberapa hal yang rapi seperti "Impor pengaturan otomatis" FQCNs apa pun dalam tipe parameter / pengembalian, pemangkasan otomatis semua file lama yang tidak tersentuh dalam proses codegen ini, indentasi dengan benar kelas dalam, dll.

Idenya adalah bahwa kode yang dihasilkan harus cantik untuk melihatnya, tanpa peringatan (impor yang tidak digunakan, dll.), Sama seperti sisa kode Anda. Begitu banyak kode yang dihasilkan jelek untuk dibaca ... itu mengerikan.

Bagaimanapun, tidak ada banyak dokumen, tapi saya pikir APInya cukup sederhana / intuitif. Repo Maven ada di sini jika ada yang tertarik.

Stephen Haberman
sumber
1

Jika Anda BENAR-BENAR membutuhkan sumber, saya tidak tahu apa pun yang menghasilkan sumber. Namun Anda dapat menggunakan ASM atau CGLIB untuk secara langsung membuat file .class.

Anda mungkin dapat menghasilkan sumber dari ini, tetapi saya hanya menggunakannya untuk menghasilkan bytecode.

Steve g
sumber
1

Saya melakukannya sendiri untuk alat generator tiruan. Ini adalah tugas yang sangat sederhana, bahkan jika Anda harus mengikuti pedoman pemformatan Sun. Saya yakin Anda akan menyelesaikan kode yang melakukannya lebih cepat daripada Anda menemukan sesuatu yang sesuai dengan tujuan Anda di Internet.

Anda pada dasarnya telah menguraikan API sendiri. Cukup isi dengan kode aktual sekarang!

Vladimir Dyuzhev
sumber
Hehehe ... Jika tidak ada kerangka yang ditemukan maka saya akan menulisnya. Saya ingin banyak fungsi jadi saya tidak akan mendapatkannya di pagi hari ...
Daniel Fanjul
1

Ada proyek baru write-it-once . Pembuat kode berbasis template. Anda menulis template khusus menggunakan Groovy , dan menghasilkan file tergantung pada refleksi java. Ini cara paling sederhana untuk menghasilkan file apa pun. Anda dapat membuat getter / settest / toString dengan menghasilkan file AspectJ, SQL berdasarkan anotasi JPA, sisipan / pembaruan berdasarkan enum dan sebagainya.

Contoh template:

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;
    }
}
Atmega
sumber
0

Itu benar-benar tergantung pada apa yang Anda coba lakukan. Pembuatan kode adalah topik dalam dirinya sendiri. Tanpa case-use khusus, saya sarankan melihat perpustakaan pembuatan kode / templat kecepatan. Juga, jika Anda melakukan pembuatan kode offline, saya akan menyarankan menggunakan sesuatu seperti ArgoUML untuk beralih dari diagram UML / model objek ke kode Java.

Berlin Brown
sumber
0

Contoh: 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//"));
pengguna3207181
sumber