Java API เพื่อสร้างซอร์สไฟล์ 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. ฉันกำลังมองหาสิ่งที่เป็นนามธรรมระดับสูงไม่ใช่การจัดการ / สร้างรหัส bytecode โดยตรง
  4. ฉันยังต้องการ "โครงสร้างของคลาส" ในโครงสร้างของวัตถุ
  5. โดเมนปัญหาเป็นเรื่องทั่วไป: เพื่อสร้างคลาสที่แตกต่างกันจำนวนมากโดยไม่มี "โครงสร้างทั่วไป"

SOLUTIONS
ผมได้โพสต์ 2 คำตอบอยู่ในคำตอบของคุณ ... กับ CodeModelและกับคราส JDT

ฉันใช้CodeModelในโซลูชันของฉันแล้ว :-)


คำถามของคุณเป็นคำถามทั่วไปโดเมนปัญหาของคุณเป็นเรื่องทั่วไปหรือไม่ คุณสามารถระบุเจาะจงมากขึ้นเกี่ยวกับโดเมนปัญหาของคุณได้ไหม ตัวอย่างเช่นฉันได้เขียนเครื่องมือสร้างโค้ดเพื่อสร้างโค้ดสำหรับปัญหาเฉพาะเช่นการกำจัดรหัสคลาสข้อยกเว้นที่ซ้ำกันหรือกำจัดการทำซ้ำใน enums
Greg Mattes

@Vlookward: คุณสามารถย้ายคำตอบที่คุณวางไว้ในคำถามเป็น 2 คำตอบแยกกันด้านล่าง จากนั้นเพิ่มลิงก์จากคำถาม
Ande Turner

@Banengusk: ขอบคุณสำหรับการถามช่วยฉันประหยัดเวลาในการค้นหาส่วนที่มืดที่สุดของอินเทอร์เน็ต @skaffman: การค้นพบที่ยอดเยี่ยม - คุณทำให้นักพัฒนาคนอื่นสบายใจขึ้นกับงานที่กำลังจะมาถึงของเขา :)
Ran Biron

คำตอบ SO นี้ตอบคำถามสำหรับ C ++ แทนที่จะเป็น Java แต่คำตอบก็ใช้ได้กับ Java เช่นกัน stackoverflow.com/a/28103779/120163
Ira Baxter

คำตอบ:


70

Sun จัดเตรียม API ที่เรียกว่า CodeModel สำหรับสร้างซอร์สไฟล์ Java โดยใช้ API ไม่ใช่สิ่งที่ง่ายที่สุดในการรับข้อมูล แต่อยู่ที่นั่นและทำงานได้ดีมาก

วิธีที่ง่ายที่สุดในการยึดมันคือส่วนหนึ่งของ JAXB 2 RI - ตัวสร้างสคีมา - ทู - จาวา XJC ใช้ CodeModel เพื่อสร้างแหล่งจาวาและเป็นส่วนหนึ่งของขวด XJC คุณสามารถใช้งานได้เฉพาะกับ CodeModel

ไปที่http://codemodel.java.net/


2
มันเป็นเพียงสิ่งที่ฉันต้องการ! เรียบง่ายและใช้งานได้เต็มที่ ขอบคุณ Skaffman!
Daniel Fanjul


@ykaganovich ดีโทร. เป็น [ repo.maven.apache.org/maven2/com/sun/codemodel/…ได้รับอนุญาตภายใต้ CDDL และ GPL) ฉันลบความคิดเห็นก่อนหน้านี้ออก
Brad Cupit

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 ได้อย่างไร
András Hummer

@DrH ค้นหาด้วย Google ง่ายๆ: codemodel.java.net/nonav/apidocs/com/sun/codemodel/…
Daniel Fanjul

@ AndrásHummerใช้อินสแตนซ์ที่ส่งคืนจากcm._class(...)เป็นอาร์กิวเมนต์ชนิดส่งคืนสำหรับdc.method(...).
Hugo Baés

28

วิธีการแก้ปัญหาที่พบกับคราส 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 โดยเพิ่ม jar ที่เหมาะสมลงใน classpath - แต่ฉันจำชื่อไฟล์ไม่ได้
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;
   }
}

9

อีกทางเลือกหนึ่งคือ AST ของ Eclipse JDT ซึ่งดีถ้าคุณต้องการเขียนซอร์สโค้ด Java ใหม่โดยพลการแทนที่จะสร้างซอร์สโค้ด (และฉันเชื่อว่ามันสามารถใช้ได้อย่างอิสระจากคราส)


1
ยิ่งใหญ่ !! ต้นไม้ไวยากรณ์นามธรรมคือสิ่งที่ฉันกำลังมองหา ... ตอนนี้ฉันจะค้นหาข้อมูลเพิ่มเติมเกี่ยวกับ API ... ขอบคุณ! :-)
Daniel Fanjul

API ซับซ้อนอย่างที่ฉันคาดไว้ แต่มีฟังก์ชันทั้งหมดที่ฉันต้องการ ขอบคุณไจลส์
Daniel Fanjul

1
ตามที่ @gastaldi กล่าวไว้เครื่องคั่ว (จาก JBoss Forge) เป็นเครื่องห่อหุ้มที่ดีสำหรับ Eclipse JDT มันซ่อนความซับซ้อนของ JDT และจัดเตรียม API ที่ดีเพื่อแยกวิเคราะห์แก้ไขหรือเขียนโค้ดจาวา github.com/forge/roaster
Jmini

4

JET คราสโครงการสามารถนำมาใช้ในการทำรุ่นแหล่งที่มา ฉันไม่คิดว่า API จะเหมือนกับที่คุณอธิบายไว้ แต่ทุกครั้งที่ฉันได้ยินโครงการที่สร้างซอร์ส Java พวกเขาใช้ JET หรือเครื่องมือพื้นบ้าน


3

ไม่รู้จักไลบรารี แต่เครื่องมือเทมเพลตทั่วไปอาจเป็นสิ่งที่คุณต้องการ มีหลายอย่างฉันเองมีประสบการณ์ที่ดีกับFreeMarker


2

ฉันสร้างสิ่งที่ดูเหมือน DSL เชิงทฤษฎีของคุณมากที่เรียกว่า "sourcegen" แต่ในทางเทคนิคแทนที่จะเป็นโครงการ util สำหรับ 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 repo อยู่ที่นี่หากใครสนใจ


1

ถ้าคุณต้องการแหล่งที่มาจริงๆฉันไม่รู้อะไรเลยที่สร้างแหล่งที่มา อย่างไรก็ตามคุณสามารถใช้ASMหรือCGLIBเพื่อสร้างไฟล์. class ได้โดยตรง

คุณอาจสร้างซอร์สจากสิ่งเหล่านี้ได้ แต่ฉันใช้เพื่อสร้าง bytecode เท่านั้น


1

ฉันกำลังทำมันด้วยตัวเองเพื่อสร้างเครื่องมือจำลอง เป็นงานที่ง่ายมากแม้ว่าคุณจะต้องปฏิบัติตามแนวทางการจัดรูปแบบของ Sun ก็ตาม ฉันพนันได้เลยว่าคุณจะกรอกรหัสให้เสร็จเร็วขึ้นจากนั้นคุณจะพบสิ่งที่เหมาะกับเป้าหมายของคุณบนอินเทอร์เน็ต

คุณได้สรุป API ด้วยตัวคุณเอง เพียงกรอกรหัสจริงตอนนี้!


อิอิอิ ... ถ้าไม่พบ framework ผมจะเขียนให้ครับ ฉันต้องการฟังก์ชั่นมากมายดังนั้นฉันจะไม่ได้รับมันในตอนเช้า ...
Daniel Fanjul


1

มีโครงการใหม่คือการเขียนมันครั้งเดียว ตัวสร้างรหัสตามเทมเพลต คุณเขียนเทมเพลตแบบกำหนดเองโดยใช้Groovyและสร้างไฟล์ขึ้นอยู่กับการสะท้อนของ java เป็นวิธีที่ง่ายที่สุดในการสร้างไฟล์ใด ๆ คุณสามารถสร้าง getters / Setest / toString ได้โดยการสร้างไฟล์ AspectJ, SQL ตามคำอธิบายประกอบ JPA, การแทรก / อัพเดตตาม enums และอื่น ๆ

ตัวอย่างเทมเพลต:

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 / Object model เป็นโค้ด 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//"));

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.