web-dev-qa-db-fra.com

Une API Java pour générer des fichiers source Java

Je cherche un framework pour générer des fichiers source Java.

Quelque chose comme l'API suivante:

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

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

Ensuite, un fichier source Java doit être trouvé dans un sous-répertoire du répertoire cible.

Est-ce que quelqu'un connaît un tel cadre?


MODIFIER:

  1. J'ai vraiment besoin des fichiers source.
  2. Je voudrais aussi remplir le code des méthodes.
  3. Je recherche une abstraction de haut niveau, pas une manipulation/génération directe de bytecode.
  4. J'ai aussi besoin de la "structure de la classe" dans un arbre d'objets.
  5. Le domaine du problème est général: générer un grand nombre de classes très différentes, sans "structure commune".

SOLUTIONS
J'ai posté 2 réponses basées sur vos réponses ... avec CodeModel et avec Eclipse JDT .

J'ai utilisé CodeModel dans ma solution, :-)

123
Daniel Fanjul

Sun fournit une API appelée CodeModel pour générer des fichiers source Java à l'aide d'une API. Ce n'est pas la chose la plus facile pour obtenir des informations, mais c'est là et ça fonctionne extrêmement bien.

Le moyen le plus simple de l'obtenir consiste à utiliser l'interface JAXB 2 RI: le générateur de schéma en Java XJC utilise CodeModel pour générer son source Java, qui fait partie des fichiers jar XJC. Vous pouvez l'utiliser uniquement pour le CodeModel.

Prenez-le à partir de http://codemodel.Java.net/

69
skaffman

Solution trouvée avec CodeModel
Merci, skaffman .

Par exemple, avec ce 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);

Je peux obtenir cette sortie:

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

Solution trouvée avec les JDT d'Eclipse AST
Merci, Giles .

Par exemple, avec ce 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);

Je peux obtenir cette sortie:

package foo;
import Java.util.Set;
class Foo<X> {
  void MISSING(){
    x();
  }
}
27
Daniel Fanjul

Vous pouvez utiliser Roaster ( https://github.com/forge/roaster ) pour générer du code.

Voici un exemple: 

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

affichera la sortie suivante:

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

Une autre alternative est AST d'Eclipse JDT, ce qui est utile si vous devez réécrire du code source Java arbitraire plutôt que de simplement générer du code source . (Et je crois qu'il peut être utilisé indépendamment d'Eclipse).

9
Squirrel

Le projet Eclipse JET peut être utilisé pour générer des sources. Je ne pense pas que son API ressemble exactement à celle que vous avez décrite, mais chaque fois que j'ai entendu parler d'un projet générant de la source source Java, ils utilisaient JET ou un outil développé localement.

4
Mike Deck

Je ne connais pas de bibliothèque, mais un moteur de modèle générique pourrait suffire. Il y en a plusieurs , j'ai personnellement eu une bonne expérience avec FreeMarker

3
ykaganovich

J'ai construit quelque chose qui ressemble beaucoup à votre DSL théorique, appelé "sourcegen", mais techniquement au lieu d'un projet util pour un ORM que j'ai écrit. Le DSL ressemble à:

@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

Il fait aussi des choses intéressantes comme "Organiser automatiquement les importations" des FQCN dans les types de paramètres/retour, élaguer automatiquement tous les anciens fichiers qui n'ont pas été touchés dans cette exécution de codegen, indenter correctement les classes internes, etc.

L'idée est que le code généré doit être joli à regarder, sans avertissements (importations inutilisées, etc.), tout comme le reste de votre code. Tellement de code généré est moche à lire ... c'est horrible.

Quoi qu’il en soit, il n’ya pas beaucoup de documentation, mais je pense que l’API est assez simple/intuitive. Le repo Maven est ici si quelqu'un est intéressé.

2
Stephen Haberman

Si vous avez VRAIMENT besoin de la source, je ne sais rien qui puisse en générer. Vous pouvez cependant utiliser ASM ou CGLIB pour créer directement les fichiers .class. 

Vous pourrez peut-être générer des sources à partir de celles-ci, mais je ne les ai utilisées que pour générer du bytecode.

1
Steve g

Il y a un nouveau projet write-it-once . Générateur de code basé sur un modèle. Vous écrivez un modèle personnalisé avec Groovy et générez un fichier en fonction des réflexions Java. C'est le moyen le plus simple de générer un fichier. Vous pouvez créer des getters/settest/toString en générant des fichiers AspectJ, SQL basé sur des annotations JPA, des insertions/mises à jour basées sur des énumérations, etc.

Exemple de modèle:

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;
    }
}
1
Atmega

Il y a aussi StringTemplate . Il est de l'auteur d'ANTLR et est assez puissant.

1
Bala

Je le faisais moi-même pour un outil générateur de maquette. C'est une tâche très simple, même si vous devez suivre les instructions de mise en forme Sun. Je parie que vous finiriez le code qui le fait plus rapidement que vous avez trouvé quelque chose qui correspond à votre objectif sur Internet.

Vous avez essentiellement décrit l'API vous-même. Il suffit de le remplir avec le code actuel maintenant!

1
Vladimir Dyuzhev

Cela dépend vraiment de ce que vous essayez de faire. La génération de code est un sujet en soi. Sans cas d'utilisation spécifique, je suggère de regarder la bibliothèque de génération/code de vélocité. De plus, si vous effectuez la génération de code hors ligne, je suggérerais d'utiliser quelque chose comme ArgoUML pour passer du modèle diagramme/objet UML au code Java.

0
Berlin Brown

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//"));
0
user3207181

Voici un projet JSON-to-POJO qui a l'air intéressant:

http://www.jsonschema2pojo.org/

0
mtyson