是否有生成 Java 源代码来创建对象的 Java 库?

Ber*_*ach 5 java generated-code

我正在寻找一个 API,它生成我的源代码,它会生成与我传递给库的对象相同的对象。

public static void main(String[] args) {
  int[] arr = new int[3];
  arr[0] = 3;
  arr[1] = 4;
  arr[2] = 5;

  // looking for this one
  MagicCode.generateCode(arr);
}
Run Code Online (Sandbox Code Playgroud)

应该生成

  int[] arr = new int[3];
  arr[0] = 3;
  arr[1] = 4;
  arr[2] = 5;
Run Code Online (Sandbox Code Playgroud)

或者

  int[] arr = new int[] { 3, 4, 5 };
Run Code Online (Sandbox Code Playgroud)

所以我想传递一个对象并获取Java 源代码,它会创建一个与我的初始对象相等的对象。

这不仅适用于原始类型,也适用于我自己的对象:

public static void main(String[] args) {
  Condition condition = new Condition();
  condition.setNumber(2);

  // looking for this one
  MagicCode.generateCode(condition);
}
Run Code Online (Sandbox Code Playgroud)

应该生成

  Condition c1 = new Condition();
  c1.setNumber(2);
Run Code Online (Sandbox Code Playgroud)

作为一个字符串,然后可以将其粘贴到 Java 源文件中。

编辑

我不想绕过编译器。

我即将重写一个没有经过很好测试的组件。所以大约有 1000 个测试用例需要编写。功能基本上是输入/输出。我有 1000 个输入字符串,并希望在重写组件后测试它的行为完全相同。

因此,我希望实现每个对象,#toString()以便它创建 Java 源来创建自己。然后我可以传递我的 1000 个字符串并让当前实现编写测试用例以确保组件的行为。

And*_*dré 4

代码生成很有趣!您可以通过使用反射来实现您所需要的,遗憾的是,还没有MagicCode实现。

你需要使用内省来读取什么样的对象并据此创建它。

您可以使用Eclipse JDT API创建类。

生成编译单元

以编程方式生成编译单元的最简单方法是使用 IPackageFragment.createCompilationUnit。您指定编译单元的名称和内容。编译单元在包内创建,并返回新的 ICompilationUnit。

文档中,有一个示例片段。

因此,您基本上会内省一下对象是什么类型以及它们的字段和当前值是什么。然后您将使用此 API 创建相应的 AST。您的示例可能如下所示。

public class CodeGenerator {

    public static void main(String[] args) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Foo foobar = new Foo();

        Bar bar = new Bar();
        bar.setSomeValue(555d);
        foobar.setBar(bar);
        foobar.setPrimitiveDouble(23);
        foobar.setValue("Hello World!");

        CodeGenerator codeGenerator = new CodeGenerator();

        String generatedCode = codeGenerator.generateCode(foobar);

        System.out.println(generatedCode);

    }

    int counter = 0;

    private String createVariableName(String clazzName) {
        return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, clazzName + getCurrentCounter());
    }

    public String generateCode(AST ast, List statements, Object object) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        String clazzName = object.getClass().getSimpleName();
        String variableName = createVariableName(clazzName);

        VariableDeclarationFragment newVariable = ast.newVariableDeclarationFragment();
        newVariable.setName(ast.newSimpleName(variableName)); // Or clazzName.toCamelCase()

        ClassInstanceCreation newInstance = ast.newClassInstanceCreation();
        newInstance.setType(ast.newSimpleType(ast.newSimpleName(clazzName)));
        newVariable.setInitializer(newInstance);

        VariableDeclarationStatement newObjectStatement = ast.newVariableDeclarationStatement(newVariable);
        newObjectStatement.setType(ast.newSimpleType(ast.newSimpleName(clazzName)));

        statements.add(newObjectStatement);

        BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
        for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
            String propertyName = propertyDesc.getName();

            if (!shouldIgnore(propertyName)) {

                MethodInvocation setterInvocation = ast.newMethodInvocation();

                SimpleName setterName = ast.newSimpleName(propertyDesc.getWriteMethod().getName());
                setterInvocation.setName(setterName);

                Object invoked = propertyDesc.getReadMethod().invoke(object);

                if (invoked == null) {
                    continue;
                }

                if (Primitives.isWrapperType(invoked.getClass())) {
                    if (Number.class.isAssignableFrom(invoked.getClass())) {
                        setterInvocation.arguments().add(ast.newNumberLiteral(invoked.toString()));
                    }

                    // TODO: Booleans
                } else {

                    if (invoked instanceof String) {
                        StringLiteral newStringLiteral = ast.newStringLiteral();
                        newStringLiteral.setLiteralValue(invoked.toString());
                        setterInvocation.arguments().add(newStringLiteral);
                    } else {

                        String newObjectVariable = generateCode(ast, statements, invoked);
                        SimpleName newSimpleName = ast.newSimpleName(newObjectVariable);
                        setterInvocation.arguments().add(newSimpleName);
                    }

                }

                SimpleName newSimpleName = ast.newSimpleName(variableName);
                setterInvocation.setExpression(newSimpleName);

                ExpressionStatement setterStatement = ast.newExpressionStatement(setterInvocation);

                statements.add(setterStatement);

            }

        }

        return variableName;
    }

    public String generateCode(Object object) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        resetCounter();

        AST ast = AST.newAST(AST.JLS3);
        Block block = ast.newBlock();

        generateCode(ast, block.statements(), object);

        return block.toString();

    }

    private int getCurrentCounter() {
        return counter++;
    }

    private void resetCounter() {
        counter = 0;
    }

    private boolean shouldIgnore(String propertyName) {
        return "class".equals(propertyName);
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用的依赖项:

    <dependency>
        <groupId>org.eclipse.tycho</groupId>
        <artifactId>org.eclipse.jdt.core</artifactId>
        <version>3.9.1.v20130905-0837</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.core</groupId>
        <artifactId>runtime</artifactId>
        <version>3.9.100-v20131218-1515</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.birt.runtime</groupId>
        <artifactId>org.eclipse.core.resources</artifactId>
        <version>3.8.101.v20130717-0806</version>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>18.0</version>
    </dependency>
Run Code Online (Sandbox Code Playgroud)

输出如下所示:

  Foo foo0=new Foo();
  Bar bar1=new Bar();
  bar1.setSomeValue(555.0);
  foo0.setBar(bar1);
  foo0.setPrimitiveDouble(23.0);
  foo0.setValue("Hello World!");
Run Code Online (Sandbox Code Playgroud)

这是 Foo 和 Bar 类声明:

public class Bar {

private double someValue;

public double getSomeValue() {
    return someValue;
}

public void setSomeValue(double someValue) {
    this.someValue = someValue;
}

}

public class Foo {

private String value;
private double primitiveDouble;
private Bar bar;

public Bar getBar() {
    return bar;
}

public double getPrimitiveDouble() {
    return primitiveDouble;
}

public String getValue() {
    return value;
}

public void setBar(Bar bar) {
    this.bar = bar;
}

public void setPrimitiveDouble(double primitiveDouble) {
    this.primitiveDouble = primitiveDouble;
}

public void setValue(String value) {
        this.value = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

我按照要求将其添加到 github存储库中。