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: вы можете переместить ответы, которые вы разместили в вопросе, как два отдельных ответа ниже. Затем добавьте ссылку на каждый из Вопросов.
Анде Тернер,
@Banengusk: Спасибо за вопрос, сэкономили мне часы на поиск в самых темных уголках Интернета. @skaffman: Отличная находка - вы сделали другого разработчика более непринужденным с его предстоящей задачей :)
Ран Бирон
В этом SO-ответе рассматривается вопрос для C ++, а не для Java, но ответ работает и для Java. stackoverflow.com/a/28103779/120163
Ира Бакстер,

Ответы:

70

Sun предоставляет API под названием CodeModel для генерации исходных файлов Java с помощью API. Получить информацию не так-то просто, но она есть и работает очень хорошо.

Самый простой способ получить его - это часть JAXB 2 RI - генератор схемы XJC в java использует CodeModel для генерации своего источника java, и он является частью jar-файлов XJC. Вы можете использовать его только для CodeModel.

Возьмите его с http://codemodel.java.net/

skaffman
источник
2
Это как раз то, что мне нужно! Просто и полностью функционально. Спасибо, скаффман!
Даниэль Фанжул,
1
@BradCupit Согласно pom-файлу repo.maven.apache.org/maven2/com/sun/codemodel/codemodel/2.6/… , это CDDL + GPL glassfish.java.net/public/CDDL+GPL_1_1.html
ykaganovich
@ykaganovich Хороший звонок. Это [ repo.maven.apache.org/maven2/com/sun/codemodel/… под лицензией CDDL и GPL). Я удалил свой предыдущий комментарий.
Брэд Купит
46

Решение найдено с помощью CodeModel
Спасибо, skaffman .

Например, с этим кодом:

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/…
Дэниел
@ AndrásHummer использует экземпляр, возвращенный из, cm._class(...)как аргумент возвращаемого типа для dc.method(...).
Hugo Baés
28

Решение найдено с помощью Eclipse JDT AST
Спасибо, Джайлс .

Например, с этим кодом:

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-проект в eclipse, добавляющий надлежащую банку в путь к классам, но я не помню имя файла.
Daniel Fanjul
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;
   }
}
Gastaldi
источник
9

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

белочка
источник
1
Большой!! Я ищу абстрактное синтаксическое дерево ... Теперь я буду искать дополнительную информацию об API ... Спасибо !, :-)
Дэниел Фанжул
Как я и ожидал, API сложный. Но в нем есть вся необходимая мне функциональность. Спасибо, Джайлз.
Даниэль Фанжул,
1
Как упомянул @gastaldi, roaster (из JBoss Forge) - это хорошая оболочка для Eclipse JDT. Он скрывает сложность JDT и предоставляет удобный API для анализа, изменения или написания кода Java. github.com/forge/roaster
Jmini
4

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

Майк Дек
источник
3

Не знаю библиотеки, но общий шаблонизатор может быть всем, что вам нужно. Их много , у меня лично был хороший опыт работы с FreeMarker

ykaganovich
источник
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

Я сам делал это для инструмента генератора макетов. Это очень простая задача, даже если вам нужно следовать рекомендациям Sun по форматированию. Бьюсь об заклад, вы закончите код, который сделает это быстрее, чем вы найдете что-то, что соответствует вашей цели в Интернете.

Вы в основном сами обрисовали 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;
    }
}
оборота Atmega
источник
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//"));
user3207181
источник