Une API Java pour générer des fichiers source Java [fermé]

127

Je recherche 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.

Quelqu'un connaît-il un tel cadre?


MODIFIER :

  1. J'ai vraiment besoin des fichiers source.
  2. Je voudrais également 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 également 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 publié 2 réponses basées sur vos réponses ... avec CodeModel et avec Eclipse JDT .

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

Daniel Fanjul
la source
Votre question est très générale, votre domaine de problème est-il vraiment aussi général? Pouvez-vous être plus précis sur votre domaine de problème? Par exemple, j'ai écrit des outils de génération de code pour générer du code pour des problèmes spécifiques tels que l'élimination du code de classe d'exception en double ou l'élimination de la duplication dans les énumérations.
Greg Mattes
@Vlookward: Vous pouvez déplacer les réponses que vous avez placées dans la question sous forme de 2 réponses séparées ci-dessous. Ensuite, ajoutez un lien vers chacun à partir de la question.
Ande Turner
@Banengusk: Merci d'avoir posé la question, cela m'a sauvé des heures de recherche dans les parties les plus sombres d'Internet. @skaffman: Bonne trouvaille - vous avez rendu un autre développeur plus à l'aise avec sa tâche à venir :)
Ran Biron
Cette réponse SO répond à la question pour C ++ plutôt que pour Java, mais la réponse fonctionne également pour Java. stackoverflow.com/a/28103779/120163
Ira Baxter

Réponses:

70

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 cela fonctionne extrêmement bien.

Le moyen le plus simple de s'en procurer est dans le cadre du JAXB 2 RI - le générateur XJC schema-to-java utilise CodeModel pour générer sa source java, et il fait partie des jars XJC. Vous pouvez l'utiliser uniquement pour le CodeModel.

Récupérez-le sur http://codemodel.java.net/

skaffman
la source
2
C'est exactement ce dont j'ai besoin! Simple et entièrement fonctionnel. Merci, skaffman!
Daniel Fanjul
1
@BradCupit Selon le fichier pom repo.maven.apache.org/maven2/com/sun/codemodel/codemodel/2.6/… , il s'agit de CDDL + GPL glassfish.java.net/public/CDDL+GPL_1_1.html
ykaganovich
@ykaganovich Bon appel. C'est [ repo.maven.apache.org/maven2/com/sun/codemodel / ... sous licence CDDL et GPL). J'ai supprimé mon commentaire précédent.
Brad Cupit
46

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;
    }
}
Daniel Fanjul
la source
Cela a l'air génial. Comment générer une méthode qui renvoie un autre type qui est également généré avec CodeModel?
András Hummer
@DrH, recherche google simple: codemodel.java.net/nonav/apidocs/com/sun/codemodel/…
Daniel Fanjul
@ AndrásHummer utilise l'instance renvoyée par cm._class(...)comme argument de type de retour pour dc.method(...).
Hugo Baés
28

Solution trouvée avec AST Eclipse JDT
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();
  }
}
Daniel Fanjul
la source
Puis-je demander - avez-vous fait cela dans le cadre d'un plugin Java Eclipse ou avez-vous réussi à l'utiliser comme code autonome? Je me rends compte que cela a des années.
mtrc
@mtrc Si je me souviens bien, c'était un projet Java autonome et normal dans eclipse, ajoutant le fichier jar approprié au chemin de classe - mais je ne me souviens pas du nom du fichier.
Daniel Fanjul
17

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;
   }
}
Gastaldi
la source
9

Une autre alternative est l'AST d'Eclipse JDT, qui convient si vous avez besoin de 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 de l'éclipse).

Écureuil
la source
1
Génial!! Un arbre de syntaxe abstraite est ce que je recherche ... Maintenant, je vais chercher plus d'informations sur l'API ... Merci !, :-)
Daniel Fanjul
L'API est complexe, comme je m'y attendais. Mais il a toutes les fonctionnalités dont j'ai besoin. Merci, Giles.
Daniel Fanjul le
1
Comme mentionné par @gastaldi, roaster (de JBoss Forge) est un bon wrapper pour Eclipse JDT. Il cache la complexité du JDT et fournit une belle API pour analyser, modifier ou écrire du code java. github.com/forge/roaster
Jmini
4

Le projet Eclipse JET peut être utilisé pour générer des sources. Je ne pense pas que son API soit exactement comme celle que vous avez décrite, mais chaque fois que j'ai entendu parler d'un projet faisant de la génération de sources Java, ils ont utilisé JET ou un outil local.

Mike Deck
la source
3

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

Ykaganovich
la source
2

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 également des choses intéressantes comme «organiser automatiquement les importations» de tous les FQCN dans les paramètres / types de 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 avertissement (importations inutilisées, etc.), tout comme le reste de votre code. Tant de code généré est moche à lire ... c'est horrible.

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

Stephen Haberman
la source
1

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

Vous pourrez peut-être générer la source à partir de ceux-ci, mais je ne les ai utilisés que pour générer du bytecode.

Steve g
la source
1

Je le faisais moi-même pour un outil générateur simulé. C'est une tâche très simple, même si vous devez suivre les directives de formatage de Sun. Je parie que vous finirez 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. Remplissez-le simplement avec le code réel maintenant!

Vladimir Dyuzhev
la source
Hehehe ... Si aucun cadre n'est trouvé, je vais l'écrire. J'aimerais beaucoup de fonctionnalités, donc je ne les aurai pas dans un matin ...
Daniel Fanjul
1

Il y a un nouveau projet en écriture unique . Générateur de code basé sur un modèle. Vous écrivez un modèle personnalisé à l'aide de Groovy et générez un fichier en fonction des réflexions Java. C'est le moyen le plus simple de générer n'importe quel 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;
    }
}
Atmega
la source
0

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 génération de code de vitesse / bibliothèque de modèles. 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 diagramme UML / modèle objet au code Java.

Brun de Berlin
la source
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//"));
user3207181
la source