A Java API to Generate Java Source Files

A Java API to generate Java source files

Sun provides an API called CodeModel for generating Java source files using an API. It's not the easiest thing to get information on, but it's there and it works extremely well.

The easiest way to get hold of it is as part of the JAXB 2 RI - the XJC schema-to-java generator uses CodeModel to generate its java source, and it's part of the XJC jars. You can use it just for the CodeModel.

Grab it from http://codemodel.java.net/

Is there any API to generate package structure from Java source files

Here's what I came up for this problem.It opens the java file, reads the package name, generates the structure and copies the file to that structure. Suggestions for improvement are welcome. :)

public final class FileListing {

private Map packageMap;

public void createPackageStructure(String sourceDir) throws FileNotFoundException
{
FileListing fileListing = new FileListing();
File startingDirectory= new File(sourceDir);

fileListing.packageMap = new HashMap();
List<File> files = fileListing.getFileListing(startingDirectory, fileListing.getPackageMap());

fileListing.moveFiles(fileListing.packageMap);

}

public List<File> getFileListing(File aStartingDir, Map packageMap) throws FileNotFoundException
{
validateDirectory(aStartingDir);
List<File> result = getFileListingNoSort(aStartingDir,packageMap);
Collections.sort(result);
return result;
}

private List<File> getFileListingNoSort(File aStartingDir, Map packageMap) throws FileNotFoundException
{
List<File> result = new ArrayList<File>();
File[] filesAndDirs = aStartingDir.listFiles();
List<File> filesDirs = Arrays.asList(filesAndDirs);

for(File file : filesDirs)
{
result.add(file);
if(file.isFile())
{
packageMap.put(file, readPackageName(file.getAbsolutePath()).replace(".", "/").replace(";", "/"));
}
else
{
//must be a directory
//recursive call!
List<File> deeperList = getFileListingNoSort(file,packageMap);
result.addAll(deeperList);
}
}
return result;
}

public String readPackageName(String filePath)
{
String packageName=null;
String line;
String temp[] = new String[2];
BufferedReader br=null;
try{
File javaFile = new File(filePath);
br = new BufferedReader(new FileReader(javaFile));
while((line=br.readLine())!=null)
{
if(line.indexOf("package")!=-1)
{
temp = line.split(" ");
break;
}
}
br.close();

}catch(FileNotFoundException fnfe)
{
fnfe.printStackTrace();
}catch(IOException ioe)
{
ioe.printStackTrace();
}
return temp[1];
}

public void moveFiles(Map packageMap)
{
Set keySet = packageMap.keySet();
Iterator it = keySet.iterator();
File sourceFile, destFile, destDirs;
InputStream in = null;
OutputStream out = null;
byte[] buf = new byte[1024];
int len;

try{
while(it.hasNext())
{
sourceFile = (File)it.next();
destDirs = new File("src/"+(String)packageMap.get(sourceFile));
destFile = new File("src/"+ (String)packageMap.get(sourceFile)+"/"+sourceFile.getName());
destDirs.mkdirs();
in = new FileInputStream(sourceFile);
out = new FileOutputStream(destFile);

while((len = in.read(buf)) > 0){
out.write(buf, 0, len);
}
}
}catch(FileNotFoundException fnfe)
{
fnfe.printStackTrace();
}catch(IOException ioe)
{
ioe.printStackTrace();
}
}

static private void validateDirectory (File aDirectory) throws FileNotFoundException
{
if (aDirectory == null) {
throw new IllegalArgumentException("Directory should not be null.");
}
if (!aDirectory.exists()) {
throw new FileNotFoundException("Directory does not exist: " + aDirectory);
}
if (!aDirectory.isDirectory()) {
throw new IllegalArgumentException("Is not a directory: " + aDirectory);
}
if (!aDirectory.canRead()) {
throw new IllegalArgumentException("Directory cannot be read: " + aDirectory);
}
}

public Map getPackageMap()
{
return this.packageMap;
}
}

Is there a Java library which generates Java source code to create objects?

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.

JAXB API to generate Java source files directly to OutputStream

I haven't leveraged this code in the way you described, but this fragment might point you in the right direction:

import com.sun.codemodel.*;
import com.sun.tools.xjc.*;
import com.sun.tools.xjc.api.*;

SchemaCompiler sc = XJC.createSchemaCompiler();
sc.setEntityResolver(new YourEntityResolver());
sc.setErrorListener(new YourErrorListener());
sc.parseSchema(SYSTEM_ID, element);
S2JJAXBModel model = sc.bind();

Java/Scala library to generate Java source code

CodeModel and Eclipse JDT worked for this fellow, and he ultimately chose CodeModel.



Related Topics



Leave a reply



Submit