Eine Java-API zum Generieren von Java-Quelldateien [geschlossen]


127

Ich suche nach einem Framework zum Generieren von Java-Quelldateien.

So etwas wie die folgende 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 sollte eine Java-Quelldatei in einem Unterverzeichnis des Zielverzeichnisses gefunden werden.

Kennt jemand einen solchen Rahmen?


EDIT :

  1. Ich brauche wirklich die Quelldateien.
  2. Ich möchte auch den Code der Methoden ausfüllen.
  3. Ich suche eine Abstraktion auf hoher Ebene, keine direkte Manipulation / Generierung von Bytecode.
  4. Ich brauche auch die "Struktur der Klasse" in einem Baum von Objekten.
  5. Die Problemdomäne ist allgemein: eine große Anzahl sehr unterschiedlicher Klassen ohne eine "gemeinsame Struktur" zu generieren.

LÖSUNGEN
Ich habe 2 Antworten basierend auf Ihren Antworten veröffentlicht ... mit CodeModel und mit Eclipse JDT .

Ich habe CodeModel in meiner Lösung verwendet, :-)


Ihre Frage ist sehr allgemein, ist Ihre Problemdomäne wirklich so allgemein? Können Sie Ihre Problemdomäne genauer beschreiben? Ich habe beispielsweise Tools zur Codegenerierung geschrieben, um Code für bestimmte Probleme zu generieren, z. B. das Entfernen von doppeltem Code für Ausnahmeklassen oder das Entfernen von doppelten Aufzählungen in Aufzählungen.
Greg Mattes

@Vlookward: Sie können die Antworten, die Sie in die Frage gestellt haben, als 2 separate Antworten unten verschieben. Fügen Sie dann aus der Frage jeweils einen Link hinzu.
Ande Turner

@ Banengusk: Danke, dass Sie gefragt haben, hat mir Stunden beim Durchsuchen der dunkelsten Teile des Internets erspart. @skaffman: Großartige Entdeckung - Sie haben einen anderen Entwickler mit seiner bevorstehenden Aufgabe wohler gemacht :)
Ran Biron

Diese SO-Antwort behandelt die Frage eher für C ++ als für Java, aber die Antwort funktioniert auch für Java. stackoverflow.com/a/28103779/120163
Ira Baxter

Antworten:


70

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

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

Hol es dir von http://codemodel.java.net/


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


@ykaganovich Guter Anruf. Es ist [ repo.maven.apache.org/maven2/com/sun/codemodel/… lizenziert unter CDDL und GPL]. Ich habe meinen früheren Kommentar entfernt.
Brad Cupit

46

Lösung mit CodeModel gefunden
Danke, 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 kann diese Ausgabe erhalten:

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

Das sieht toll aus. Wie generieren Sie eine Methode, die einen anderen Typ zurückgibt, der ebenfalls mit CodeModel generiert wird?
András Hummer


@ AndrásHummer verwendet die von zurückgegebene Instanz cm._class(...)als Rückgabetypargument für dc.method(...).
Hugo Baés

28

Lösung gefunden mit Eclipse JDTs AST
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 kann diese Ausgabe erhalten:

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

Kann ich fragen - haben Sie dies als Teil eines Java Eclipse-Plugins getan oder haben Sie es geschafft, dies als eigenständigen Code zu verwenden? Mir ist klar, dass dies Jahre alt ist.
mtrc

@mtrc Wenn ich mich gut erinnere, war es ein eigenständiges und normales Java-Projekt in Eclipse, bei dem dem Klassenpfad das richtige Jar hinzugefügt wurde - aber ich erinnere mich nicht an den Dateinamen.
Daniel Fanjul

17

Sie können Roaster ( https://github.com/forge/roaster ) verwenden, um Code zu generieren.

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

zeigt die folgende Ausgabe an:

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

9

Eine andere Alternative ist Eclipse JDTs AST. Dies ist gut, wenn Sie beliebigen Java-Quellcode neu schreiben müssen, anstatt nur Quellcode zu generieren. (und ich glaube, es kann unabhängig von Eclipse verwendet werden).


1
Toll!! Ein abstrakter Syntaxbaum ist das, wonach ich suche ... Jetzt werde ich mehr Informationen über die API suchen ... Danke!, :-)
Daniel Fanjul

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

1
Wie von @gastaldi erwähnt, ist Roaster (von JBoss Forge) ein guter Wrapper für Eclipse JDT. Es verbirgt die Komplexität des JDT und bietet eine nette API zum Parsen, Ändern oder Schreiben von Java-Code. github.com/forge/roaster
Jmini

4

Das Eclipse JET- Projekt kann zur Generierung von Quellen verwendet werden. Ich glaube nicht, dass die API genau der von Ihnen beschriebenen entspricht, aber jedes Mal, wenn ich von einem Projekt gehört habe, das Java-Quellen generiert, haben sie JET oder ein selbst entwickeltes Tool verwendet.



2

Ich habe etwas gebaut, das Ihrem theoretischen DSL sehr ähnlich sieht, "sourcegen" genannt, aber technisch gesehen anstelle eines util-Projekts für ein ORM, das ich geschrieben habe. Das DSL sieht aus wie:

@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 werden auch einige nette Dinge erledigt, wie "Importe automatisch organisieren", alle FQCNs in Parametern / Rückgabetypen, alte Dateien, die in diesem Codegen-Lauf nicht berührt wurden, automatisch bereinigen, innere Klassen korrekt einrücken usw.

Die Idee ist, dass generierter Code hübsch aussehen sollte, ohne Warnungen (nicht verwendete Importe usw.), genau wie der Rest Ihres Codes. So viel generierter 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 hier, wenn jemand interessiert ist.


1

Wenn Sie die Quelle WIRKLICH brauchen, weiß ich nichts, was die Quelle erzeugt. Sie können jedoch ASM oder CGLIB verwenden , um die .class-Dateien direkt zu erstellen.

Möglicherweise können Sie aus diesen Quellen generieren, aber ich habe sie nur zum Generieren von Bytecode verwendet.


1

Ich habe es selbst für ein Mock-Generator-Tool gemacht. Dies ist eine sehr einfache Aufgabe, selbst wenn Sie die Formatierungsrichtlinien von Sun befolgen müssen. Ich wette, Sie würden den Code, der es schneller macht, fertigstellen, als Sie im Internet etwas gefunden haben, das zu Ihrem Ziel passt.

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


Hehehe ... Wenn kein Rahmen gefunden wird, werde ich ihn schreiben. Ich hätte gerne viele Funktionen, damit ich sie an einem Morgen nicht bekomme ...
Daniel Fanjul


1

Es gibt ein neues Projekt zum einmaligen Schreiben . Vorlagenbasierter Codegenerator. Sie schreiben eine benutzerdefinierte Vorlage mit Groovy und generieren eine Datei in Abhängigkeit von Java-Reflexionen. Dies ist der einfachste Weg, eine Datei zu generieren. Sie können Getter / Settest / ToString erstellen, indem Sie AspectJ-Dateien, SQL basierend auf JPA-Anmerkungen, Einfügungen / Aktualisierungen basierend auf Aufzählungen usw. generieren.

Vorlagenbeispiel:

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

Es hängt wirklich davon ab, was Sie versuchen zu tun. Codegenerierung ist ein Thema für sich. Ohne einen bestimmten Anwendungsfall schlage ich vor, die Geschwindigkeitscode-Generierung / Vorlagenbibliothek zu betrachten. Wenn Sie die Codegenerierung offline durchführen, würde ich empfehlen, ArgoUML zu verwenden, um vom UML-Diagramm / Objektmodell zum Java-Code zu wechseln.


0

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.