Code generation is fun! You can achieve what you need by using reflection, sadly, there is no MagicCode
implemented already.
You need to use the introspection to read what kind of object and create it according.
You can use the Eclipse JDT API to create classes.
Generating a compilation unit
The easiest way to programmatically generate a compilation unit is to use IPackageFragment.createCompilationUnit. You specify the name and contents of the compilation unit. The compilation unit is created inside the package and the new ICompilationUnit is returned.
From the docs, there is a example snippet.
So you basically will introspect to see what kind of object is and what is their fields and current values. Then you will use this API do create a corresponding AST. Your example might look like this.
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);
}
}
The dependencies I used:
<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>
This is what the output looks like :
Foo foo0=new Foo();
Bar bar1=new Bar();
bar1.setSomeValue(555.0);
foo0.setBar(bar1);
foo0.setPrimitiveDouble(23.0);
foo0.setValue("Hello World!");
Here is the Foo and Bar class declaration:
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;
}
}
I added this to a github repository as requested.