2008-09-23 5 views
121

Ich bin auf der Suche nach einem Framework zum Generieren von Java-Quelldateien.Eine Java-API zum Generieren von Java-Quelldateien

So etwas wie die folgenden API:

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

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

Dann wird eine Java-Quelldatei sollte in einem Unterverzeichnis des Zielverzeichnisses.

Kennt jemand solch ein Framework?


EDIT:

  1. Ich brauche wirklich die Quelldateien.
  2. Ich möchte auch den Code der Methoden ausfüllen.
  3. Ich bin auf der Suche nach einer High-Level-Abstraktion, nicht direkt Bytecode Manipulation/Generation.
  4. Ich brauche auch die "Struktur der Klasse" in einem Baum von Objekten.
  5. Die Problemdomäne ist allgemein: um eine große Menge sehr unterschiedlicher Klassen zu erzeugen, ohne eine "gemeinsame Struktur". in Ihrer Antwort basierten ... with CodeModel und with Eclipse JDT

SOLUTIONS
Ich habe 2 Antworten gepostet.

habe ich CodeModel in meiner Lösung, verwenden :-)

+0

Ihre Frage ist sehr allgemein, ist Ihre Problemdomäne wirklich diese allgemeine? Können Sie Ihre Problemdomäne genauer beschreiben? Zum Beispiel habe ich Code-Generierungs-Tools geschrieben, um Code für spezifische Probleme zu generieren, wie zum Beispiel das Entfernen von doppeltem Code von Ausnahmeklassen oder das Eliminieren von Duplizierung in Enums. –

+0

@Vlookward: Sie könnten die Antworten, die Sie in der Frage platziert haben, als 2 separate Antworten unten verschieben. Fügen Sie dann einen Link zu jedem aus der Frage hinzu. –

+0

Oh, ja. Gute Idee. –

Antwort

68

Sun bietet eine API namens CodeModel zum Generieren von Java-Quelldateien mit einer API.Es ist nicht die einfachste Sache, Informationen zu erhalten, aber es ist da und es funktioniert sehr gut.

Der einfachste Weg, um es zu bekommen ist als Teil des JAXB 2 RI - der XJC-Schema-zu-Java-Generator verwendet CodeModel, um seine Java-Quelle zu generieren, und es ist Teil der XJC-JARS. Sie können es nur für das CodeModel verwenden.

Schnappen Sie es aus http://codemodel.java.net/

+2

Es ist genau das, was ich brauche! Einfach und voll funktionsfähig. Danke, Skaffman! –

+1

@BradCupit Laut der Pom-Datei http://repo.maven.apache.org/maven2/com/sun/codemodel/codemodel/2.6/codemodel-2.6.pom ist es CDDL + GPL https://glassfish.java .net/public/CDDL + GPL_1_1.html – ykaganovich

+0

@ykaganovich Guter Anruf. Es ist [http://repo.maven.apache.org/maven2/com/sun/codemodel/codemodel-project/2.6/codemodel-project-2.6.pom] (unter CDDL und GPL lizenziert). Ich habe meinen früheren Kommentar entfernt. –

1

Wenn man wirklich die Quelle braucht, weiß ich nichts von dieser Quelle erzeugt. Sie können jedoch ASM oder CGLIB verwenden, um die .class-Dateien direkt zu erstellen.

Sie können möglicherweise Quelle von diesen generieren, aber ich habe sie nur verwendet, um Bytecode zu generieren.

+0

Ich brauche wirklich die Quelldateien. Vielen Dank! –

4

Das Projekt Eclipse JET kann zur Generierung der Quelle verwendet werden. Ich denke nicht, dass es API ist genau wie die, die Sie beschrieben, aber jedes Mal, wenn ich von einem Projekt gehört Java-Source-Generation gehört haben sie JET oder ein selbst entwickeltes Werkzeug verwendet.

1

Ich machte es selbst für ein Schein Generator-Tool. Dies ist eine sehr einfache Aufgabe, selbst wenn Sie die Sun-Formatierungsrichtlinien befolgen müssen. Ich wette, du würdest den Code schneller fertigstellen, als du etwas gefunden hast, das zu deinem Ziel im Internet passt.

Sie haben die API im Grunde selbst beschrieben. Füllen Sie es jetzt mit dem eigentlichen Code aus!

+0

Hehehe ... Wenn kein Framework gefunden wird, werde ich es schreiben. Ich möchte eine Menge Funktionalität, so werde ich es nicht an einem Morgen bekommen ... –

9

Eine weitere Alternative ist der AST von Eclipse JDT, der gut ist, wenn Sie willkürlichen Java-Quellcode umschreiben müssen, anstatt nur Quellcode zu generieren. (und ich glaube, es kann unabhängig von Eclipse verwendet werden).

+1

Großartig !! Ein abstrakter Syntaxbaum ist, was ich suche ... Jetzt werde ich mehr Informationen über die API suchen ... Danke !, :-) –

+0

Die API ist komplex, wie ich erwartet hatte. Aber es hat alle Funktionen, die ich brauche. Danke, Giles. –

+1

Wie von @ gastaldi erwähnt, ist Röstung (von JBoss Forge) ein nettes Wrapper für Eclipse JDT. Es verbirgt die Komplexität des JDT und bietet eine nette API zum Parsen, Modifizieren oder Schreiben von Java-Code. https://github.com/forge/roaster – Jmini

3

Ich kenne keine Bibliothek, aber eine generische Vorlagen-Engine könnte alles sein, was Sie brauchen. Es gibt a bunch of them, ich persönlich habe gute Erfahrungen mit FreeMarker

44

Lösung mit Codemodel
Dank gefunden, skaffman.

Zum Beispiel mit diesem Code:

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

ich diese Ausgabe bekommen:

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

Das sieht super aus. Wie generieren Sie eine Methode, die einen anderen Typ zurückgibt, der auch mit CodeModel generiert wird? –

+0

@DrH, einfache Google-Suche: http://codemodel.java.net/nonav/apidocs/com/sun/codemodel/JDefinedClass.html#method(int,%20com.sun.codemodel.JType,%20java.lang. String) –

+0

@ AndrásHummer verwenden Sie die von 'cm._class (...)' zurückgegebene Instanz als Rückgabetypargument für 'dc.method (...)'. –

27

Lösung mit Eclipse JDT AST gefunden
Danke, Giles.

Zum Beispiel mit diesem Code:

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

ich diese Ausgabe bekommen:

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

Kann ich fragen - haben Sie das als Teil eines Java Eclipse Plugins gemacht oder haben Sie es geschafft, dies als eigenständigen Code zu verwenden? Ich weiß, das ist Jahre alt. – mtrc

+0

@mtrc Wenn ich mich gut erinnere, war es ein eigenständiges und normales Java-Projekt in Eclipse, das dem Klassenpfad das richtige Jar hinzufügte - aber ich erinnere mich nicht an den Dateinamen. –

+0

OK, vielen Dank für die Hilfe! :) – mtrc

1

Es gibt auch StringTemplate ist. Es ist vom Autor von ANTLR und ist ziemlich mächtig.

0

Es hängt wirklich davon ab, was Sie versuchen zu tun. Code-Generierung ist ein Thema in sich. Ohne einen speziellen Anwendungsfall empfehle ich, die Code-Generierung/Template-Bibliothek zu Rate zu ziehen. Auch wenn Sie die Code-Generierung offline machen, würde ich vorschlagen, etwas wie ArgoUML zu verwenden, um von UML-Diagramm/Objektmodell zu Java-Code zu gelangen.

2

Ich baute etwas, das sehr ähnlich aussieht wie Ihre theoretische DSL, genannt "sourcegen", aber technisch statt eines util-Projekts für ein ORM, das ich schrieb. Der DSL wie folgt aussieht:

@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

Es tut auch einige nette Dinge wie „Auto-organisieren Importe“ jegliche FQCNs in Parameter/Rückgabetypen, automatische Beschneiden alle alten Dateien, die nicht in diese berührt wurden Codegen ausführen, Innenklassen richtig einrücken, etc.

Die Idee ist, dass generierter Code hübsch aussehen sollte, ohne Warnungen (ungenutzte Importe, etc.), genau wie der Rest Ihres Codes. So viel generierten Code ist hässlich zu lesen ... es ist schrecklich.

Wie auch immer, es gibt nicht viele Dokumente, aber ich denke, die API ist ziemlich einfach/intuitiv. Das Maven Repo ist here wenn jemand interessiert ist.

+0

Vielen Dank! –

0

Exemple: 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//")); 
1

Es gibt neues Projekt write-it-once. Template-basierter Code-Generator.Sie schreiben benutzerdefinierte Vorlage mit Groovy und generieren Datei abhängig von Java-Reflexionen. Es ist der einfachste Weg, um eine Datei zu erstellen. Sie können getters/settest/toString machen, indem Sie AspectJ-Dateien generieren, SQL basierend auf JPA-Annotationen, Einfügungen/Aktualisierungen basierend auf Enums und so weiter.

Vorlage Beispiel:

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

können Sie verwenden Roaster (https://github.com/forge/roaster) Code-Generierung zu tun. Hier

ein Beispiel:

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

wird die folgende Ausgabe angezeigt:

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

das ist sehr cool! –