Java API do generowania plików źródłowych Java [zamknięte]

127

Szukam frameworka do generowania plików źródłowych Java.

Coś w rodzaju następującego interfejsu API:

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

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

Następnie plik źródłowy Java powinien znajdować się w podkatalogu katalogu docelowego.

Czy ktoś zna takie ramy?


EDYCJA :

  1. Naprawdę potrzebuję plików źródłowych.
  2. Chciałbym również wypełnić kod metod.
  3. Szukam abstrakcji wysokiego poziomu, a nie bezpośredniej manipulacji / generowania kodu bajtowego.
  4. Potrzebuję również „struktury klasy” w drzewie obiektów.
  5. Dziedzina problemowa jest ogólna: generowanie dużej liczby bardzo różnych klas bez „wspólnej struktury”.

ROZWIĄZANIA
Opublikowałem 2 odpowiedzi oparte na twoich odpowiedziach ... z CodeModel i Eclipse JDT .

W swoim rozwiązaniu wykorzystałem CodeModel , :-)

Daniel Fanjul
źródło
Twoje pytanie jest bardzo ogólne. Czy domena, w której występuje problem, jest rzeczywiście tak ogólna? Czy możesz bardziej szczegółowo opisać swoją problematyczną domenę? Na przykład napisałem narzędzia do generowania kodu w celu generowania kodu dla określonych problemów, takich jak eliminowanie zduplikowanych kodów klas wyjątków lub eliminowanie duplikacji w wyliczeniach.
Greg Mattes,
@Vlookward: Możesz przenieść odpowiedzi, które umieściłeś w pytaniu jako 2 oddzielne odpowiedzi poniżej. Następnie dodaj link do każdego z pytania.
Ande Turner,
@Banengusk: Dzięki za pytanie, zaoszczędziłem godziny na przeszukiwaniu najciemniejszych części internetu. @skaffman: Świetne odkrycie - ułatwiłeś innemu deweloperowi wykonanie jego nadchodzącego zadania :)
Ran Biron
Ta odpowiedź SO dotyczy raczej C ++ niż Javy, ale odpowiedź działa również dla Javy. stackoverflow.com/a/28103779/120163
Ira Baxter

Odpowiedzi:

70

Firma Sun udostępnia interfejs API o nazwie CodeModel do generowania plików źródłowych Java za pomocą interfejsu API. Nie jest to najłatwiejsza rzecz do uzyskania informacji, ale jest tam i działa bardzo dobrze.

Najłatwiej jest go zdobyć w ramach JAXB 2 RI - generator schematu XJC-to-java używa CodeModel do generowania źródła Java i jest on częścią jarów XJC. Możesz go użyć tylko dla CodeModel.

Pobierz go z http://codemodel.java.net/

skaffman
źródło
2
Właśnie tego potrzebuję! Prosty iw pełni funkcjonalny. Dzięki, skaffman!
Daniel Fanjul,
@ykaganovich Dobre połączenie. Jest [ repo.maven.apache.org/maven2/com/sun/codemodel/ ... na licencji CDDL i GPL). Usunąłem swój wcześniejszy komentarz.
Brad Cupit
46

Rozwiązanie znalezione z CodeModel
Thanks, skaffman .

Na przykład z tym kodem:

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

Mogę uzyskać ten wynik:

package foo;
public class Bar {
    int foo() {
        return  5;
    }
}
Daniel Fanjul
źródło
To wygląda niesamowicie. Jak wygenerować metodę, która zwraca inny typ, który jest również generowany za pomocą CodeModel?
András Hummer
@DrH, proste wyszukiwanie w Google: codemodel.java.net/nonav/apidocs/com/sun/codemodel/…
Daniel Fanjul
@ AndrásHummer używa wystąpienia zwróconego z cm._class(...)jako argumentu typu zwracanego dla dc.method(...).
Hugo Baés
28

Rozwiązanie znalezione za pomocą programu AST Eclipse JDT
Thanks, Giles .

Na przykład z tym kodem:

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

Mogę uzyskać ten wynik:

package foo;
import java.util.Set;
class Foo<X> {
  void MISSING(){
    x();
  }
}
Daniel Fanjul
źródło
Mogę zapytać - czy zrobiłeś to jako część wtyczki Java Eclipse, czy też udało ci się użyć tego jako samodzielnego kodu? Zdaję sobie sprawę, że to ma lata.
mtrc
@mtrc Jeśli dobrze pamiętam, był to samodzielny i normalny projekt java w eclipse, dodający odpowiedni jar do ścieżki klas - ale nie pamiętam nazwy pliku.
Daniel Fanjul
17

Możesz użyć Roaster ( https://github.com/forge/roaster ) do generowania kodu.

Oto przykład:

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

wyświetli następujące dane wyjściowe:

public class MyClass {
   private String testMethod() {
       return null;
   }
}
gastaldi
źródło
9

Inną alternatywą jest AST Eclipse JDT, który jest dobry, jeśli chcesz przepisać dowolny kod źródłowy Java, a nie tylko generować kod źródłowy. (i uważam, że można go używać niezależnie od zaćmienia).

Wiewiórka
źródło
1
Świetny!! Poszukuję abstrakcyjnego drzewa składni ... Teraz poszukam więcej informacji o API ... Dzięki !, :-)
Daniel Fanjul
API jest złożone, tak jak się spodziewałem. Ale ma wszystkie potrzebne mi funkcje. Dzięki, Giles.
Daniel Fanjul,
1
Jak wspomniał @gastaldi, roaster (z JBoss Forge) jest ładnym opakowaniem dla Eclipse JDT. Ukrywa złożoność JDT i zapewnia przyjemne API do analizowania, modyfikowania lub pisania kodu java. github.com/forge/roaster
Jmini
4

Projekt Eclipse JET może służyć do generowania źródeł. Nie sądzę, żeby to API było dokładnie takie, jak to, które opisałeś, ale za każdym razem, gdy słyszałem o projekcie generującym źródła w Javie, używali JET lub własnego narzędzia.

Mike Deck
źródło
3

Nie znasz biblioteki, ale może być wszystko, czego potrzebujesz, jeśli potrzebujesz ogólnego silnika szablonów. Jest ich sporo , osobiście miałem dobre doświadczenia z FreeMarkerem

ykaganovich
źródło
2

Zbudowałem coś, co wygląda bardzo podobnie do twojego teoretycznego DSL, zwanego "sourcegen", ale technicznie zamiast projektu użytkowego dla ORM, który napisałem. DSL wygląda następująco:

@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

Wykonuje również kilka fajnych rzeczy, takich jak „Automatyczne organizowanie importu” wszelkich FQCN w parametrach / typach zwracanych, automatyczne przycinanie wszelkich starych plików, które nie zostały dotknięte w tym przebiegu kodegenu, prawidłowe wcięcie klas wewnętrznych itp.

Chodzi o to, że wygenerowany kod powinien wyglądać ładnie, bez ostrzeżeń (nieużywane importy itp.), Tak jak reszta kodu. Tak dużo wygenerowanego kodu jest brzydkie do odczytania ... to straszne.

W każdym razie nie ma wielu dokumentów, ale myślę, że API jest dość proste / intuicyjne. Repozytorium Maven jest tutaj, jeśli ktoś jest zainteresowany.

Stephen Haberman
źródło
1

Jeśli NAPRAWDĘ potrzebujesz źródła, nie znam niczego, co je generuje. Możesz jednak użyć ASM lub CGLIB do bezpośredniego tworzenia plików .class.

Możesz być w stanie wygenerować z nich źródło, ale użyłem ich tylko do wygenerowania kodu bajtowego.

Steve g
źródło
1

Robiłem to sam dla narzędzia do symulowanego generatora. To bardzo proste zadanie, nawet jeśli musisz przestrzegać wytycznych dotyczących formatowania firmy Sun. Założę się, że skończyłbyś kod, który robi to szybciej, niż znalazłeś w Internecie coś, co pasuje do twojego celu.

W zasadzie sam opisałeś API. Po prostu wypełnij go rzeczywistym kodem teraz!

Vladimir Dyuzhev
źródło
Hehehe ... Jeśli nie znaleziono żadnego frameworka, napiszę go. Chciałbym dużo funkcjonalności, więc nie dostanę ich rano ...
Daniel Fanjul
1

Pojawił się nowy projekt jednorazowego zapisu . Generator kodu oparty na szablonach. Piszesz własny szablon za pomocą Groovy i generujesz plik w zależności od odbić Java. To najprostszy sposób na wygenerowanie dowolnego pliku. Możesz tworzyć getters / setest / toString, generując pliki AspectJ, SQL na podstawie adnotacji JPA, wstawki / aktualizacje na podstawie wyliczeń i tak dalej.

Przykład szablonu:

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
źródło
0

To naprawdę zależy od tego, co próbujesz zrobić. Generowanie kodu jest tematem samym w sobie. Bez konkretnego przypadku użycia proponuję przyjrzeć się bibliotece szablonów / generowania kodu prędkości. Ponadto, jeśli generujesz kod offline, sugerowałbym użycie czegoś takiego jak ArgoUML, aby przejść od diagramu UML / modelu obiektowego do kodu Java.

Berlin Brown
źródło
0

Przykład: 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//"));
user3207181
źródło