Una API Java para generar archivos fuente Java [cerrado]

127

Estoy buscando un marco para generar archivos fuente Java.

Algo así como la siguiente API:

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

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

Luego, se debe encontrar un archivo fuente de Java en un subdirectorio del directorio de destino.

¿Alguien sabe tal marco?


EDITAR :

  1. Realmente necesito los archivos fuente.
  2. También me gustaría completar el código de los métodos.
  3. Estoy buscando una abstracción de alto nivel, no manipulación / generación directa de bytecode.
  4. También necesito la "estructura de la clase" en un árbol de objetos.
  5. El dominio del problema es general: generar una gran cantidad de clases muy diferentes, sin una "estructura común".

SOLUCIONES
He publicado 2 respuestas basadas en sus respuestas ... con CodeModel y con Eclipse JDT .

He usado CodeModel en mi solución :-)

Daniel Fanjul
fuente
Su pregunta es muy general, ¿su problema es realmente este dominio general? ¿Puedes ser más específico sobre tu dominio problemático? Por ejemplo, he escrito herramientas de generación de código para generar código para problemas específicos, como eliminar el código de clase de excepción duplicado o eliminar la duplicación en las enumeraciones.
Greg Mattes el
@Vlookward: puede mover las respuestas que ha colocado en la pregunta como 2 respuestas separadas a continuación. Luego agregue un enlace a cada uno desde la Pregunta.
Ande Turner
@Banengusk: Gracias por preguntar, me ahorró horas de búsqueda en las partes más oscuras de Internet. @skaffman: Gran descubrimiento: hizo que otro desarrollador se sintiera más cómodo con su próxima tarea :)
Ran Biron
Esta respuesta SO aborda la pregunta para C ++ en lugar de Java, pero la respuesta también funciona para Java. stackoverflow.com/a/28103779/120163
Ira Baxter

Respuestas:

70

Sun proporciona una API llamada CodeModel para generar archivos fuente Java utilizando una API. No es lo más fácil obtener información, pero está ahí y funciona extremadamente bien.

La forma más fácil de obtenerlo es como parte de JAXB 2 RI: el generador de esquema XJC a Java usa CodeModel para generar su fuente Java, y es parte de los frascos XJC. Puede usarlo solo para CodeModel.

Agarrarlo desde http://codemodel.java.net/

skaffman
fuente
2
¡Es justo lo que necesito! Simple y completamente funcional. Gracias skaffman!
Daniel Fanjul
@ykaganovich Buena llamada. Es [ repo.maven.apache.org/maven2/com/sun/codemodel/… con licencia bajo CDDL y GPL). Eliminé mi comentario anterior.
Brad Cupit
46

Solución encontrada con CodeModel
Gracias, skaffman .

Por ejemplo, con este código:

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

Puedo obtener esta salida:

package foo;
public class Bar {
    int foo() {
        return  5;
    }
}
Daniel Fanjul
fuente
Esto se ve genial. ¿Cómo se genera un método que devuelve otro tipo que también se genera con CodeModel?
András Hummer
@DrH, simple búsqueda en google: codemodel.java.net/nonav/apidocs/com/sun/codemodel/…
Daniel Fanjul
@ AndrásHummer usa la instancia devuelta cm._class(...)como argumento de tipo de retorno para dc.method(...).
Hugo Baés
28

Solución encontrada con AST de Eclipse JDT
Gracias, Giles .

Por ejemplo, con este código:

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

Puedo obtener esta salida:

package foo;
import java.util.Set;
class Foo<X> {
  void MISSING(){
    x();
  }
}
Daniel Fanjul
fuente
¿Puedo preguntar: hizo esto como parte de un Java Eclipse Plugin o logró usar esto como código independiente? Me doy cuenta de que esto tiene años.
mtrc
@mtrc Si recuerdo bien, fue un proyecto java independiente y normal en eclipse, agregando el jar apropiado al classpath, pero no recuerdo el nombre del archivo.
Daniel Fanjul
17

Puede usar Roaster ( https://github.com/forge/roaster ) para generar código.

Aquí hay un ejemplo:

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

mostrará la siguiente salida:

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

Otra alternativa es el AST de Eclipse JDT, que es bueno si necesita reescribir código fuente arbitrario de Java en lugar de simplemente generar código fuente. (y creo que se puede usar independientemente del eclipse).

Ardilla
fuente
1
¡¡Excelente!! Un árbol de sintaxis abstracta es lo que estoy buscando ... Ahora buscaré más información sobre la API ... ¡Gracias! :-)
Daniel Fanjul
La API es compleja, como esperaba. Pero tiene toda la funcionalidad que necesito. Gracias Giles.
Daniel Fanjul
1
Como mencionó @gastaldi, el tostador (de JBoss Forge) es un buen contenedor para Eclipse JDT. Oculta la complejidad del JDT y proporciona una buena API para analizar, modificar o escribir código Java. github.com/forge/roaster
Jmini
4

El proyecto Eclipse JET se puede utilizar para generar fuentes. No creo que su API sea exactamente como la que usted describió, pero cada vez que escuché de un proyecto que generaba fuentes de Java, utilizaron JET o una herramienta local.

Mike Deck
fuente
3

No conozco una biblioteca, pero un motor de plantillas genérico podría ser todo lo que necesita. Hay un montón de ellos , personalmente he tenido una buena experiencia con FreeMarker

ykaganovich
fuente
2

Construí algo que se parece mucho a su DSL teórico, llamado "sourcegen", pero técnicamente en lugar de un proyecto de utilidad para un ORM que escribí. El DSL se ve así:

@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

También hace algunas cosas interesantes como "Organizar automáticamente las importaciones" de cualquier FQCN en parámetros / tipos de retorno, podar automáticamente los archivos antiguos que no se tocaron en esta ejecución de codegen, sangrar correctamente las clases internas, etc.

La idea es que el código generado debería ser bonito para mirarlo, sin advertencias (importaciones no utilizadas, etc.), al igual que el resto de su código. Tanto código generado es feo de leer ... es horrible.

De todos modos, no hay muchos documentos, pero creo que la API es bastante simple / intuitiva. El repositorio de Maven está aquí si alguien está interesado.

Stephen Haberman
fuente
1

Si REALMENTE necesita la fuente, no sé de nada que genere la fuente. Sin embargo, puede usar ASM o CGLIB para crear directamente los archivos .class.

Es posible que pueda generar una fuente a partir de estos, pero solo los he usado para generar bytecode.

Steve g
fuente
1

Lo estaba haciendo yo mismo para una herramienta de generador simulada. Es una tarea muy simple, incluso si necesita seguir las pautas de formato de Sun. Apuesto a que terminaría el código que lo hace más rápido que cuando encontró algo que se ajusta a su objetivo en Internet.

Básicamente, usted describió la API usted mismo. ¡Solo complételo con el código real ahora!

Vladimir Dyuzhev
fuente
Jejeje ... Si no se encuentra ningún marco, lo escribiré. Quisiera mucha funcionalidad para no tenerla en una mañana ...
Daniel Fanjul
1

Hay un nuevo proyecto para escribirlo una vez . Generador de código basado en plantillas. Usted escribe una plantilla personalizada usando Groovy y genera un archivo en función de los reflejos de Java. Es la forma más sencilla de generar cualquier archivo. Puede hacer getters / settest / toString generando archivos AspectJ, SQL basado en anotaciones JPA, inserciones / actualizaciones basadas en enumeraciones, etc.

Ejemplo de plantilla:

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
fuente
0

Realmente depende de lo que intentes hacer. La generación de código es un tema en sí mismo. Sin un caso de uso específico, sugiero buscar en la biblioteca de generación / plantilla de código de velocidad. Además, si está haciendo la generación de código fuera de línea, sugeriría usar algo como ArgoUML para pasar del diagrama UML / modelo de objetos al código Java.

Berlin Brown
fuente
0

Ejemplo: 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//"));
usuario3207181
fuente