API Java для створення вихідних файлів Java [закрито]


127

Я шукаю основу для створення вихідних файлів Java.

Щось на зразок наступного API:

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

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

Потім у вихідному файлі цільового каталогу слід знайти вихідний файл Java.

Хтось знає такі рамки?


Редагувати :

  1. Мені дуже потрібні вихідні файли.
  2. Я також хотів би заповнити код методів.
  3. Я шукаю абстракцію високого рівня, а не пряму маніпуляцію байткодом / генерацію.
  4. Мені також потрібна "структура класу" на дереві предметів.
  5. Проблемна область загальна: генерувати велику кількість дуже різних класів, без "загальної структури".

РІШЕННЯ
Я розмістив 2 відповіді на основі ваших відповідей ... з CodeModel та з Eclipse JDT .

Я використовував CodeModel у своєму рішенні, :-)


Ваше запитання дуже загальне, чи справді ваша загальна проблемна область? Чи можете ви бути більш конкретними щодо вашого проблемного домену? Наприклад, я написав засоби генерації коду для генерації коду для конкретних проблем, таких як усунення дублікату коду класу виключень або усунення дублювання в перерахунках.
Грег Маттес

@Vlookward: Ви можете перемістити відповіді, які ви розмістили у Питання, як дві окремі відповіді нижче. Потім додайте посилання на кожне із Запитання.
Ande Turner

@Banengusk: Дякую за запитання, врятував мене години пошуку найтемніших частин Інтернету. @skaffman: Чудова знахідка - ви зробили іншого розробника більш легким у своєму майбутньому завданні :)
Ран Бірон

Ця відповідь ТА стосується питання для C ++, а не Java, але відповідь працює і для Java. stackoverflow.com/a/28103779/120163
Іра Бакстер

Відповіді:


70

Sun надає API під назвою CodeModel для генерації вихідних файлів Java за допомогою API. Інформацію отримати не простіше, але вона є, і вона працює надзвичайно добре.

Найпростіший спосіб отримати це як частина JAXB 2 RI - генератор схеми XJC схеми до ява використовує CodeModel для створення свого джерела java, і він є частиною банок XJC. Ви можете використовувати його просто для CodeModel.

Візьміть це з http://codemodel.java.net/


2
Це саме те, що мені потрібно! Простий і повністю функціональний. Спасибі, скафмане!
Даніель Фанджул


@ykaganovich Добрий дзвінок. Це [ repo.maven.apache.org/maven2/com/sun/codemodel/… ліцензується відповідно до CDDL та GPL). Раніше я видалив свій коментар.
Бред Купіт

46

Знайдено рішення із CodeModel
Спасибі, скафман .

Наприклад, з цим кодом:

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

Я можу отримати такий вихід:

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

Це виглядає приголомшливо. Як ви створюєте метод, який повертає інший тип, який також генерується разом із CodeModel?
Андрас Хаммер

@DrH, простий пошук у Google: codemodel.java.net/nonav/apidocs/com/sun/codemodel/…
Daniel Fanjul

@ AndrásHummer використовує екземпляр, повернений з cm._class(...)аргументу типу return для dc.method(...).
Хуго Баес

28

Знайдено рішення за допомогою AST Eclipse JDT
Спасибі, Джайлз .

Наприклад, з цим кодом:

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

Я можу отримати такий вихід:

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

Чи можу я запитати - ви робили це як частина додатка Java Eclipse або вам вдалося використовувати це як окремий код? Я усвідомлюю, що це роки.
mtrc

@mtrc Якщо я добре пам’ятаю, це був окремий і нормальний проект Java в затемненні, додавши належну банку до classpath - але я не пам’ятаю ім’я файлу.
Даніель Фанджул

17

Ви можете використовувати Roaster ( https://github.com/forge/roaster ) для створення коду.

Ось приклад:

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

відобразиться наступний вихід:

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

9

Інша альтернатива - AST Eclipse JDT, що добре, якщо вам потрібно переписати довільний вихідний код Java, а не просто генерувати вихідний код. (і я вважаю, що його можна використовувати незалежно від затемнення).


1
Чудово !! Абстрактне синтаксичне дерево - це те, що я шукаю ... Тепер я шукатиму більше інформації про API ... Спасибі !, :-)
Daniel Fanjul

API я складний, як я і очікував. Але він має весь функціонал, який мені потрібен. Спасибі, Джайлз.
Даніель Фанджул

1
Як згадував @gastaldi, жаровня (від JBoss Forge) - хороша обгортка для JDT Eclipse. Він приховує складність JDT і надає приємний API для розбору, зміни або запису Java-коду. github.com/forge/roaster
Jmini

4

Проект Eclipse JET можна використовувати для створення джерела. Я не думаю, що це API точно такий, як описаний вами, але кожен раз, коли я чув про проект, який робить генерацію джерела Java, вони використовували JET або інструмент для домашнього вирощування.


3

Не знаєте про бібліотеку, але загальний двигун шаблонів може бути усім, що вам потрібно. Їх є купа , я особисто мав хороший досвід роботи з FreeMarker


2

Я створив щось, що дуже схоже на ваш теоретичний DSL, який називається "sourcegen", але технічно замість корисного проекту для ORM я написав. DSL виглядає так:

@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

Це також робить деякі акуратні речі, такі як "Автоорганізувати імпорт" будь-яких FQCN в параметрах / типах повернення, автоматичне обрізання будь-яких старих файлів, які не торкалися в цьому запуску кодегенту, правильно відступаючи внутрішні класи тощо.

Ідея полягає в тому, що згенерований код повинен бути гарним, щоб його переглядати, без попереджень (невикористаний імпорт тощо), як і решта вашого коду. Стільки генерованого коду некрасиво читати ... це жахливо.

Як би там не було багато документів, але я думаю, що API досить простий / інтуїтивний. Ревен Maven є тут, якщо хтось зацікавлений.


1

Якщо вам дійсно потрібне джерело, я не знаю нічого, що створює джерело. Однак ви можете використовувати ASM або CGLIB для прямого створення файлів .class.

Можливо, ви зможете генерувати з них джерело, але я використовував їх лише для створення байтового коду.


1

Я робив це сам за інструмент генератора макетів. Це дуже просте завдання, навіть якщо вам потрібно керуватися вказівками щодо форматування НД. Б'юсь об заклад, що ви закінчите код, який робить це швидше, тоді ви знайшли щось, що відповідає вашій меті в Інтернеті.

Ви в основному окреслили API самостійно. Просто заповніть його фактичним кодом зараз!


Хе-хе ... Якщо не знайдено жодної рамки, я збираюся її написати. Мені б хотілося багато функціональності, тому я не отримаю його вранці ...
Даніель Фанджул,


1

Є новий проект, який пишемо один раз . Генератор коду на основі шаблонів. Ви пишете спеціальний шаблон за допомогою Groovy та генеруєте файл залежно від відображень Java. Це найпростіший спосіб генерувати будь-який файл. Ви можете зробити getters / settest / toString, генеруючи файли AspectJ, SQL на основі анотацій JPA, вставок / оновлень на основі перерахунків тощо.

Приклад шаблону:

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

Це дійсно залежить від того, що ви намагаєтеся зробити. Генерація коду - це тема всередині себе. Без конкретного випадку використання я пропоную переглянути швидкість генерації коду / бібліотеку шаблонів. Крім того, якщо ви робите генерацію коду в автономному режимі, я б запропонував використовувати щось на зразок ArgoUML для переходу від UML-діаграми / моделі об'єкта до коду Java.


0

Приклад: 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//"));

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.