API Java để tạo các tệp nguồn Java [đã đóng]


127

Tôi đang tìm kiếm một khung công tác để tạo các tệp nguồn Java.

Một cái gì đó giống như API sau:

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

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

Sau đó, một tệp nguồn java nên được tìm thấy trong một thư mục con của thư mục đích.

Có ai biết một khuôn khổ như vậy?


CHỈNH SỬA :

  1. Tôi thực sự cần các tập tin nguồn.
  2. Tôi cũng muốn điền vào mã của các phương thức.
  3. Tôi đang tìm kiếm một sự trừu tượng hóa ở mức độ cao, không phải là thao tác / tạo mã byte trực tiếp.
  4. Tôi cũng cần "cấu trúc của lớp" trong một cây các đối tượng.
  5. Miền vấn đề là chung: để tạo ra một lượng lớn các lớp rất khác nhau, không có "cấu trúc chung".

GIẢI PHÁP
Tôi đã đăng 2 câu trả lời dựa trên câu trả lời của bạn ... với CodeModelvới JDT Eclipse .

Tôi đã sử dụng CodeModel trong giải pháp của mình, :-)


Câu hỏi của bạn rất chung chung, miền vấn đề của bạn có thực sự chung chung này không? Bạn có thể cụ thể hơn về miền vấn đề của bạn? Ví dụ: tôi đã viết các công cụ tạo mã để tạo mã cho các vấn đề cụ thể như loại bỏ mã lớp ngoại lệ trùng lặp hoặc loại bỏ trùng lặp trong enums.
Greg Mattes

@Vlookward: Bạn có thể di chuyển các câu trả lời mà bạn đã đặt trong Câu hỏi dưới dạng 2 câu trả lời riêng biệt bên dưới. Sau đó thêm một liên kết đến mỗi từ Câu hỏi.
Ande Turner

@Banengusk: Cảm ơn bạn đã hỏi, đã giúp tôi tiết kiệm hàng giờ để tìm kiếm những phần tối nhất của internet. @skaffman: Tìm kiếm tuyệt vời - bạn đã làm cho một nhà phát triển khác thoải mái hơn với nhiệm vụ sắp tới của mình :)
Ran Biron

Câu trả lời SO này giải quyết câu hỏi cho C ++ thay vì Java, nhưng câu trả lời cũng hoạt động với Java. stackoverflow.com/a/28103779/120163
Ira Baxter

Câu trả lời:


70

Sun cung cấp một API có tên CodeModel để tạo các tệp nguồn Java bằng API. Đây không phải là điều dễ nhất để có được thông tin, nhưng nó ở đó và nó hoạt động rất tốt.

Cách dễ nhất để nắm giữ nó là một phần của JAXB 2 RI - trình tạo lược đồ XJC sử dụng CodeModel để tạo nguồn java của nó và nó là một phần của các tệp XJC. Bạn có thể sử dụng nó chỉ cho CodeModel.

Lấy nó từ http://codemodel.java.net/


2
Nó chỉ là những gì tôi cần! Đơn giản và đầy đủ chức năng. Cảm ơn, skaffman!
Daniel Fanjul


@ykaganovich Gọi tốt. Đó là [ repo.maven.apache.org/maven2/com/sun/codemodel/õ được cấp phép theo CDDL và GPL). Tôi đã xóa bình luận trước đó của tôi.
Brad Cupit

46

Giải pháp được tìm thấy với CodeModel
Cảm ơn, skaffman .

Ví dụ: với mã này:

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

Tôi có thể nhận được đầu ra này:

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

Điều này có vẻ tuyệt vời. Làm thế nào để bạn tạo một phương thức trả về một loại khác đang được tạo bằng CodeModel?
András Hummer


@ AndrásHummer sử dụng thể hiện được trả về từ cm._class(...)đối số kiểu trả về cho dc.method(...).
Hugo Baés

28

Giải pháp được tìm thấy với AST
Thanks, Giles của Eclipse .

Ví dụ: với mã này:

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

Tôi có thể nhận được đầu ra này:

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

Tôi có thể hỏi - bạn đã làm điều này như là một phần của Trình cắm Java Eclipse hay bạn đã quản lý để sử dụng mã này làm mã độc lập chưa? Tôi nhận ra đây là tuổi.
mtrc

@mtrc Nếu tôi nhớ rõ, đó là một dự án java độc lập và bình thường trong nhật thực, thêm bình thích hợp vào đường dẫn lớp - nhưng tôi không nhớ tên tệp.
Daniel Fanjul

17

Bạn có thể sử dụng Roaster ( https://github.com/forge/roaster ) để tạo mã.

Đây là một ví dụ:

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

sẽ hiển thị đầu ra sau:

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

9

Một cách khác là AST của JDT của Eclipse rất tốt nếu bạn cần viết lại mã nguồn Java tùy ý thay vì chỉ tạo mã nguồn. (và tôi tin rằng nó có thể được sử dụng độc lập với nhật thực).


1
Tuyệt quá!! Cây Cú pháp Trừu tượng là thứ tôi đang tìm kiếm ... Bây giờ tôi sẽ tìm kiếm thêm thông tin về API ... Cảm ơn!, :-)
Daniel Fanjul

API rất phức tạp, như tôi mong đợi. Nhưng nó có tất cả các chức năng tôi cần. Cảm ơn, Giles.
Daniel Fanjul

1
Như được đề cập bởi @gastaldi, trình rang xay (từ JBoss Forge) là một trình bao bọc đẹp cho JDT Eclipse. Nó che giấu sự phức tạp của JDT và cung cấp một API đẹp để phân tích, sửa đổi hoặc viết mã java. github.com/forge/roaster
Jmini

4

Các JET Eclipse dự án có thể được sử dụng để làm thế nguồn. Tôi không nghĩ API của nó giống hệt như API mà bạn đã mô tả, nhưng mỗi lần tôi nghe nói về một dự án thực hiện việc tạo nguồn Java, họ đã sử dụng JET hoặc một công cụ trong nhà.



2

Tôi đã xây dựng một cái gì đó trông rất giống DSL lý thuyết của bạn, được gọi là "sourcegen", nhưng về mặt kỹ thuật thay vì một dự án tiện dụng cho một ORM tôi đã viết. DSL trông như:

@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

Nó cũng thực hiện một số thứ gọn gàng như "Tự động tổ chức nhập" bất kỳ FQCN nào trong các loại tham số / trả về, tự động cắt xén bất kỳ tệp cũ nào không được chạm vào trong lần chạy mã này, thụt lề chính xác, v.v.

Ý tưởng là mã được tạo nên đẹp để xem xét nó, không có cảnh báo (nhập không sử dụng, v.v.), giống như phần còn lại của mã của bạn. Rất nhiều mã được tạo ra thật xấu xí để đọc ... thật kinh khủng.

Dù sao, không có nhiều tài liệu, nhưng tôi nghĩ API khá đơn giản / trực quan. Repo Maven ở đây nếu có ai quan tâm.


1

Nếu bạn thực sự cần nguồn, tôi không biết bất cứ điều gì tạo ra nguồn. Tuy nhiên, bạn có thể sử dụng ASM hoặc CGLIB để trực tiếp tạo các tệp. Class .

Bạn có thể tạo nguồn từ những thứ này, nhưng tôi chỉ sử dụng chúng để tạo mã byte.


1

Tôi đã tự làm nó cho một công cụ tạo giả. Đây là một nhiệm vụ rất đơn giản, ngay cả khi bạn cần tuân theo các nguyên tắc định dạng của Sun. Tôi cá là bạn đã hoàn thành mã đó nhanh hơn sau đó bạn tìm thấy thứ gì đó phù hợp với mục tiêu của mình trên Internet.

Về cơ bản, bạn đã tự phác thảo API. Chỉ cần điền nó với mã thực tế bây giờ!


Hehehe ... Nếu không tìm thấy khuôn khổ nào thì tôi sẽ viết nó. Tôi muốn có nhiều chức năng vì vậy tôi sẽ không nhận được nó vào một buổi sáng ...
Daniel Fanjul


1

Có dự án mới viết nó một lần . Trình tạo mã dựa trên mẫu. Bạn viết mẫu tùy chỉnh bằng Groovy và tạo tệp tùy thuộc vào phản xạ java. Đó là cách đơn giản nhất để tạo bất kỳ tập tin. Bạn có thể tạo getters / giải quyết / toString bằng cách tạo các tệp AspectJ, SQL dựa trên các chú thích JPA, chèn / cập nhật dựa trên enums, v.v.

Ví dụ mẫu:

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

Nó thực sự phụ thuộc vào những gì bạn đang cố gắng làm. Tạo mã là một chủ đề trong chính nó. Nếu không có trường hợp sử dụng cụ thể, tôi khuyên bạn nên xem thư viện tạo / vận tốc mã. Ngoài ra, nếu bạn đang thực hiện việc tạo mã ngoại tuyến, tôi khuyên bạn nên sử dụng một cái gì đó như ArgoUML để đi từ mô hình UML / mô hình đối tượng sang mã Java.


0

Ví dụ: 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//"));

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.