SourceGen is a source code generation framework that is intended to be feature complete while still providing a simple and straightforward API. Currently, all Java constructs, including comments and JavaDoc comments (for classes, methods and fields) are supported except for inner classes and annotations. Further releases will have annotations and/or inner classes, if anyone asks for them. The contact link is in the upper right corner.
SourceGen provides a Java API for constructing Java source code. Of all major projects, the Abstract Syntax Tree in Eclipse is the closest. However, use of Eclipse's Abstract Syntax Tree to construct a file requires either: (a) every last Java construct on every line must be manually converted into a part of the source file, or (b) source lines have to be parsed into an AST representation and then reconstituted on output. With SourceGen, methods contain an ArrayList of lines, which are regular Java Strings. Of course, the ability to freely put whatever is required in a method means that the syntax cannot be checked until the compiler is run on the generated source code. If syntax checking is absolutely necessary before compile time, consider using the Abstract Syntax Tree approach.
In general, SourceGen is designed to require the minimum amount of
Java code necessary for generating Java source code as output.
All SourceGen objects have an output
method that takes
a StringBuffer
and a FormattingRules
Object
for maximum flexibility. A BufferedWriter
or other other
helper Object can then be used to write the source code to a file, the
console, a pipe, a socket, or any other output destination.
The FormattingRules class handles details like tab or space indents, Unix or Windows style newlines, and line length. All generated code will obey these rules. The FormattingRules class is general enough for most purposes. If greater flexibility in source code formatting is desired, Jalopy is recommended. It can run on all generated source after generation is complete. Since all method lines are added explicitly, brace style and indents within a method are left up to the user. SourceGen will not modify these. It is recommended (but not required) that tabs be used for the method lines. These will be converted into the appropriate number of spaces if space indents are requested.
See the JavaDoc.
The following example code is provided as a starting point to see how SourceGen works, and what the code that uses it looks like.
package com.supermathie.sourcegen.test; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.Modifier; import java.util.ArrayList; import com.supermathie.sourcegen.ClassSource; import com.supermathie.sourcegen.FormattingRules; import com.supermathie.sourcegen.JavaSourceFile; import com.supermathie.sourcegen.MethodSource; /** * This class illustrates the basic usage of SourceGen. */ public class SourceGenExample { public static void main(String[] args) throws IOException { // The JavaSourceFile object represents a Java source file, which // could be an interface or a class. In this case, we will be // creating a new class. JavaSourceFile sourceFile = new JavaSourceFile("com.example.foo"); ArrayList imports = new ArrayList(); imports.add("com.example.bar.*"); sourceFile.setImports(imports); // Create the public class for the Java source file. Each Java // source file must contain exactly one public class. It is not // allowed to change a class from public to non-public or // vice-versa. An attempt to change the modifiers in this way // results in an IllegalArgumentException. boolean publicClass = true; ClassSource classSource = new ClassSource(publicClass, "Foo"); sourceFile.setPublicClass(classSource); // If we wanted to create an interface, we would change the class // modifiers like so: // classSource.setModifiers(Modifier.INTERFACE); // This class will implement the IFoo interface. classSource.setImplements("IFoo"); // Foo will extend Bar. classSource.setSuperClass("Bar"); // Create a private field "baz" of type "Baz" and add a getter // and setter method. classSource.addProperty("Baz", "baz"); // Create a toString method that is not a constructor and returns a // String. MethodSource methodSource = new MethodSource(false, "toString", "String"); classSource.addMethod(methodSource); // Just for fun, we'll make it public and synchronized. methodSource.setModifiers(Modifier.PUBLIC | Modifier.SYNCHRONIZED); ArrayList codeLines = new ArrayList(); codeLines.add("StringBuffer sb = new StringBuffer();"); codeLines.add("sb.append(\"Foo: \\\"\");"); codeLines.add("sb.append(baz.toString());"); codeLines.add("sb.append(\"\\\"\");"); codeLines.add(""); codeLines.add("return sb.toString()"); methodSource.setCodeLines(codeLines); // Use 4 spaces for the indent. FormattingRules rules = new FormattingRules(true, 4); // Create the file on disk. StringBuffer sb = new StringBuffer(); sourceFile.output(rules, sb); BufferedWriter bw = new BufferedWriter(new FileWriter("c:\\temp\\Foo.java")); bw.write(sb.toString()); bw.close(); } }
The source code above produces the following file.
package com.example.foo; import com.example.bar.*; public class Foo extends Bar implements IFoo { private Baz baz; public Baz getBaz() { return baz; } public void setBaz(Baz baz) { this.baz = baz; } public synchronized String toString() { StringBuffer sb = new StringBuffer(); sb.append("Foo: \""); sb.append(baz.toString()); sb.append("\""); return sb.toString() } }