|
This document covers the essential Sugar4j language constructs. It documents the Sugar4j calls necessary to generate common Java code.
The code examples below assume that they are contained in method of a Sugar4j subclass, for example, within the doCreate() method.
Import a single class:
importt("java.io.Reader");
importt(Reader.class);
|
import java.io.Reader;
import java.io.Reader;
|
Import on demand:
importAll("java.io");
importAll(Reader.class.getPackage());
|
import java.io.*;
import java.io.*;
|
Static import:
importStatic(JavaLangAnnotation.ElementTypeANNOTATION_TYPE);
|
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
|
Note that if using class objects as identifiers, the output results in full qualified class names.
The advantage is that your Sugar class contains less strings and you do not ever have to care about import lists.
If you prefer simple names over full qualified names, you may want to enable the auto import facility.
In your Sugar4j subclass's constructor, call setAutoImport(true) to enable the auto import facility:
public class HelloWorldSugar extends Sugar4j<HelloWorldSugar> {
public HelloWorldSugar() {
super("tutorial.HelloWorld");
setAutoImport(true);
}
...
}
|
With auto import enabled, the import list is maintained automagically. Every type is added to the import list as soon you refer to it
and further references to the type are made using simple names.
To create a class, interface, enum or annotation type declaration, two methods exists for each of them.
One which takes no parameter and the other one which expects the name of the type to create.
Use the parameterless method if you are creating the top level type; the type's name is then obtained by a call to getClassSimpleName() . Use the other one to create an inner type.
public class HelloWorldSugar extends Sugar4j<HelloWorldSugar> {
public HelloWorldSugar() {
super("tutorial.HelloWorld");
}
public void doCreate() {
beginCompilationUnit();
publicc().classs().begin();
{
privatee().staticc().classs("Inner").begin();
{
}
end();
}
end();
}
}
|
A class may extend another class and/or implement many interfaces:
publicc().classs().extendss("MyBaseClass").implementss(Runnable, JavaIo.Serializable).begin();
{
}
end();
|
An interface:
publicc().interfacee().begin();
{
method(intt, "getCount").eos();
}
end();
|
public interface HelloWorld {
int getCount();
}
|
An enum in it's simplest form:
publicc().enummm("item1", "item2", "item3");
publicc().enummmm("Color", "red", "green", "blue");
|
public enum HelloWorld {
item1, item2, item3
}
public enum Color {
red, green, blue
}
|
Enum items with parametrized constructors:
publicc().enumm().begin();
{
enumItem("item1", 0xff0000).next();
enumItem("item2", 0xff00).next();
enumItem("item3", 0xff).eos();
privatee().var(intt, "_code").eos();
privatee().constructor(intt, "code").begin();
{
assign("_code", "code").eos();
}
end();
publicc().method(intt, "getCode").begin();
{
returnn("_code").eos();
}
end();
}
end();
|
public enum HelloWorld {
item1(16711680),
item2(65280),
item3(255);
private int _code;
private HelloWorld(int code) {
_code = code;
}
public int getCode() {
return _code;
}
}
|
Inner enum items:
publicc().enumm().begin();
{
enumItem("item1").begin();
{
publicc().method(intt, "getCode").begin();
{
returnn(45).eos();
}
end();
}
end().next();
enumItem("item2").begin();
{
publicc().method(intt, "getCode").begin();
{
returnn(46).eos();
}
end();
}
end().eos();
abstractt().publicc().method(intt, "getCode").eos();
}
end();
|
public enum HelloWorld {
item1 {
public int getCode() {
return 45;
}
},
item2 {
public int getCode() {
return 46;
}
};
abstract public int getCode();
}
|
An annotation with attributes:
annotate(
JavaLangAnnotation.Target,
arrayInit(JavaLangAnnotation.ElementTypeFIELD,
JavaLangAnnotation.ElementTypeMETHOD));
annotate(JavaLangAnnotation.Retention,
JavaLangAnnotation.RetentionPolicySOURCE);
annotation("MyAnnotation").begin();
{
attribute(String, "value");
attribute(booleann, "enabled", truee);
}
end();
|
@java.lang.annotation.Target(
{ java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD })
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
@interface MyAnnotation {
String value();
boolean enabled() default true;
}
|
Using static imports and package content interfaces, the code above can be much more shortened. The result stays the same.
import static sugar4j.lang.JavaLangAnnotation.ElementTypeFIELD;
import static sugar4j.lang.JavaLangAnnotation.ElementTypeMETHOD;
import static sugar4j.lang.JavaLangAnnotation.Retention;
import static sugar4j.lang.JavaLangAnnotation.RetentionPolicySOURCE;
import static sugar4j.lang.JavaLangAnnotation.Target;
...
annotate(Target, arrayInit(ElementTypeFIELD, ElementTypeMETHOD));
annotate(Retention, RetentionPolicySOURCE);
annotation("MyAnnotation").begin();
{
attribute(String, "value");
attribute(booleann, "enabled", truee);
}
end();
|
Variables are declared using the var method. It is used for:
- a member declaration
- a local variable declaration
- a variable declaration in the initialization part of a for loop
- an annotated or final method parameter declaration
Member declaration:
publicc().classs().begin();
{
privatee().var(String, "_name").eos();
}
end();
|
public class HelloWorld {
private String _name;
}
|
Assign an initial value:
var(intt, "size", 0).eos();
var(String, "s", nulll).eos();
|
int size = 0;
String size = null;
|
Declare many variables of the same type. All must have an initial value:
var(intt, "a", 0, "b", 1, "c", 2).eos();
|
int a = 0, b = 1, c = 2;
|
If you forgot an initial value, a runtime exception is thrown:
var(intt, "a", 0, "b", 1, "c").eos();
|
ParametersAreNotPairsException: params must be [Type name] pairs like "String s"
|
If the initial value is an expression, you may use the assign() method:
var(StringBuilder, "s").assign().neww(StringBuilder).eos();
|
StringBuilder s = new StringBuilder();
|
assign is flexible:
assign("_name", "name").eos();
assign("_name").calll("getName").eos();
var(StringBuilder, "s").assign().neww(StringBuilder).eos();
|
_name = name;
_name = getName();
StringBuilder s = new StringBuilder();
|
A full blown for loop. (Note that there also exists an enhanced for loop in Sugar4j).
forr(
expr().var(intt, "i", 0, "n", expr().call("list", "size")),
expr().lt("i", "n"),
expr().incr("i")).begin();
{
}
end();
|
for (
int i = 0, n = list.size();
i < n;
i++) {
}
|
final method parameter declaration. This is rather seldom:
method(intt, "getCount", expr().finall().var(String, "s")).begin();
{
returnn().call("s", "length").eos();
}
end();
|
int getCount(final String s) {
return s.length();
}
|
Methods are declared using the method method.
Method with no parameters:
privatee().method(voidd, "fooo").begin();
{
}
end();
|
private void fooo() {
}
|
Method with parameters:
publicc().staticc().method(intt, "fooo", String, "s", intt, "count").begin();
{
returnn().plus(expr().call("s", "length"), "count").eos();
}
end();
|
public static int fooo(String s, int count) {
return s.length() + count;
}
|
Method with varargs parameter:
publicc().method(String, "printf", varargs(Object), "params").begin();
{
throww().neww(UnsupportedOperationException, quote("this is just a demo")).eos();
}
end();
|
public String printf(Object... params) {
throw new UnsupportedOperationException("this is just a demo");
}
|
Method with throws declaration:
method(voidd, "run").throwss(ClassNotFoundException).begin();
{
}
end();
|
void run() throws ClassNotFoundException {
}
|
Constructors are methods with a null return type and the same name as the class. Sugar4j provides the constructor convenient method.
Instead of writing:
method(null, getClassSimpleName()).begin();
{
}
end();
|
You can write:
constructor().begin();
{
}
end();
|
HelloWorld() {
}
|
For an inner class:
classs("Inner").begin();
{
constructorr("Inner").begin();
{
}
end();
}
end();
|
class Inner {
Inner() {
}
}
|
Methods are called using the call method, fields are accessed using the get decorator method.
Call a method on an object:
assign("size").call("list", "size").eos();
assign("item").call("list", "get", 0).eos();
|
size = list.size();
item = list.get(0);
|
Calling a method on this is the same as passing null for the object.
Sugar4j provides the calll() convenient method.
Instead of writing:
assign("item").call(null, "toString").eos();
|
You can write:
assign("item").calll("toString").eos();
|
item = toString();
|
If you prefer the this notation:
assign("item").call(thiss, "toString").eos();
|
item = this.toString();
|
Get the class member of the String class. get concatenates all parameters together and delimits them with the dot.
var(Class, "c", get(String, classs)).eos();
var(String, "cell", get("table", "currentRow", "currentCell")).eos();
var(String, "name", get(thiss, "_name")).eos();
|
Class c = String.class;
String cell = table.currentRow.currentCell;
String name = this._name;
|
For loops exists in three different flavors.
The full blown for loop:
forr(
expr().var(intt, "i", 0, "n", expr().call("list", "size")),
expr().lt("i", "n"),
expr().incr("i")).begin();
{
}
end();
|
for (
int i = 0, n = list.size();
i < n;
i++) {
}
|
Iterate from a start index to an end index. This is the relaxed version of the above.
Within such a for loop, the index and size variables ii and nn are available.
forrr(0, expr().call("list", "size")).begin();
{
var(String, "item").assign().call("list", "get", ii).eos();
}
end();
|
for (int i = 0, n = list.size(); i < n; i++) {
String item = list.get(i);
}
|
The enhanced for loop:
forrr(String, "item", "list").begin();
{
}
end();
|
for (String item : list) {
}
|
The while loop:
whilee(truee).begin();
{
}
end();
|
while (true) {
}
|
The do loop:
doo().begin();
{
}
end().whilee(truee).eos();
|
do {
} while (true);
|
The if statement with a simple boolean identifier:
iff("ok").begin();
{
}
end();
|
if (ok) {
}
|
The if statement with an expression and an else clause:
iff(expr().gt("size", 0)).begin();
{
}
end().elsee().begin();
{
}
end();
|
if (size > 0) {
} else {
}
|
If with else if and else:
var(intt, "result").eos();
iff(expr().call(quote("red"), equals, "s")).begin();
{
assign("result", 1).eos();
}
end().elsee().iff(expr().call(quote("green"), equals, "s")).begin();
{
assign("result", 2).eos();
}
end().elsee().begin();
{
assign("result", -1).eos();
}
end();
|
int result;
if ("red".equals(s)) {
result = 1;
} else if ("green".equals(s)) {
result = 2;
} else {
result = -1;
}
|
Conditional if:
assign("s").ifff(expr().gt("size", 0), quote("full"), quote("empty")).eos();
|
s = size > 0 ? "full" : "empty";
|
Switch:
switchh("result").begin();
{
casee(1);
{
assign("s", quote("red")).eos();
breakk();
}
casee(2);
{
assign("s", quote("green")).eos();
breakk();
}
defaultt();
{
assign("s", quote("unknown")).eos();
breakk();
}
}
end();
|
switch (result) {
case 1:
s = "red";
break;
case 2:
s = "green";
break;
default:
s = "unknown";
break;
}
|
Every Sugar4j method that deals with arrays are static and are prefixed with array .
An array method acts as a decorator around a given type or array variable.
Sugar4j does not support so called 'extra dimensions' out of the box:
String[] arr;
String arr[];
|
Declare an array variable:
var(arrayOf(String), "arr", nulll).eos();
|
String[] arr = null;
|
Declare an array variable with an initializer:
var(arrayOf(intt), "arr", arrayInit(0, 1, 2)).eos();
|
int[] arr = {0, 1, 2};
|
Create a new array with a given size:
assign("arr", arrayNew(intt, 2)).eos();
|
arr = new int[2];
|
Create a new array with an initializer:
assign("arr", arrayNeww(intt, 0, 1, 2)).eos();
|
arr = new int[] {0, 1, 2};
|
Access an array element at a given index:
var(intt, "first", arrayAt("arr", 0)).eos();
assign(arrayAt("arr", 0), 99).eos();
|
int first = arr[0];
arr[0] = 99;
|
Determine the length of the array:
var(intt, "len", arrayLength("arr")).eos();
|
int len = arr.length;
|
Multi dimensional arrays:
var(arrayOf(arrayOf(intt)), "arr", arrayNew(intt, 2, 3)).eos();
var(arrayOf(arrayOf(intt)), "arr", arrayInit(arrayInit(0, 1), arrayInit(2, 3))).eos();
|
int[][] arr = new int[2][3];
int[][] arr = { {0, 1}, {2, 3} };
|
Anonymous inner classes are created by calling neww and adding a begin / end block.
Anonymous inner class:
var(Runnable, "r").assign().neww(Runnable).begin();
{
publicc().method(voidd, "run").begin();
{
}
end();
}
end().eos();
|
Runnable r = new Runnable() {
public void run() {
}
};
|
try/catch/finally:
tryy().begin();
{
}
end().catchh(Exception, "e").begin();
{
throww().neww(RuntimeException, "e").eos();
}
end().finallyy().begin();
{
}
end();
|
try {
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
}
|
Sugar4j uses Java 5.0 Class#cast(Object) to cast an expression.
Dynamic and static cast:
var(String, "s").assign().cast(String, "obj").eos();
assign("s").cast(String).calll("getName").eos();
|
String s = String.class.cast(obj);
s = (String)getName();
|
To declare a varargs method parameter, use the varargs method. It's a decorator around a given type.
varargs method parameter:
publicc().method(String, "printf", varargs(Object), "params").begin();
{
}
end();
|
public String printf(Object... params) {
}
|
To parenthesize an expression, call the static decorator method expr (not the expr instance method).
Example:
var(intt, "i", expr(12345)).eos();
|
int i = (12345);
|
Not instanceof test:
iff(expr().not(expr(expr().instanceoff("obj", String)))).begin();
{
throww().neww(RuntimeException, quote("obj is not a string")).eos();
}
end();
|
if (!(obj instanceof String)) {
throw new RuntimeException("obj is not a string");
}
|
Not that this rather heavy expression can easily be extracted to a method like this:
protected MySugar ifNotInstanceOf(Object var, Object type) {
return iff(expr().not(expr(expr().instanceoff(var, type))));
}
ifNotInstanceOf("obj", String).begin();
{
throww().neww(RuntimeException, quote("obj is not a string")).eos();
}
end();
|
Java elements are annotated using the annotate method. Note that annotation is used to declare
an annotation type (see chapter above), whereas annotate is used to apply an annotation to a Java element.
Marker annotation:
annotate("MyMarkerAnnotation");
method(voidd, "fooo").begin();
{
}
end();
|
@MyMarkerAnnotation
void fooo() {
}
|
Single member annotation:
annotate("MySingleMemberAnnotation", quote("hello"));
method(voidd, "fooo").begin();
{
}
end();
|
@MySingleMemberAnnotation("hello")
void fooo() {
}
|
Normal annotation:
annotate("MyNormalAnnotation", "value", quote("hello"), "enabled", falsee);
method(voidd, "fooo").begin();
{
}
end();
|
@MyNormalAnnotation(value = "hello", enabled = false)
void fooo() {
}
|
Special support is available for the @Override and @SuppressWarnings annotations.
To improve readability, Sugar4j breaks another Java rule here: Override and SuppressWarnings begin with an upper case letter.
classs("B").extendss("A").begin();
{
Override();
method(voidd, "doIt").begin();
{
superr("doIt").eos();
}
end();
}
end();
|
class B extends A {
@Override
void doIt() {
super.doIt();
}
}
|
SuppressWarnings("unused", "deprecation");
method(voidd, "doIt", "List", "list").begin();
{
}
end();
|
@SuppressWarnings( { "unused", "deprecation" })
void doIt(List list) {
}
|
Annotate a method parameter declaration. This is rather seldom:
method(intt, "getCount", expr().SuppressWarnings("unchecked").var("List", "list")).begin();
{
}
end();
|
int getCount(@SuppressWarnings("unchecked") List list) {
}
|
Type arguments are added to types using the decorator method typeArg .
Declare a list of strings:
var(typeArg("List", String), "list").assign().neww(typeArg("ArrayList", String)).eos();
|
List<String> list = new ArrayList<String>();
|
Declare a map:
var(typeArg("Map", String, Integer), "map").assign().neww(typeArg("HashMap", String, Integer)).eos();
|
Map<String, Integer> map = new HashMap<String, Integer>();
|
Extend/implement a parametrized type:
publicc().classs(typeArg(getClassSimpleName(), "T")).extendss(typeArg("AbstractList", "T")).begin();
{
}
end();
|
public class HelloWorld<T> extends AbstractList<T> {
}
|
Wildcards:
var(typeArg(Class, Question), "c", get(String, classs)).eos();
var(typeArg(Class, expr().question().extendss(String)), "c", get(String, classs)).eos();
var(typeArg(Class, expr().question().superrr(String)), "c", get(String, classs)).eos();
|
Class<?> c = String.class;
Class<? extends String> c = String.class;
Class<? super String> c = String.class;
|
Method type argument:
publicc().staticc().typeArgg("T").method("T", "toList", "T", "obj").begin();
{
returnn(nulll).eos();
}
end();
|
public static <T> T toList(T obj) {
return null;
}
|
Note that such a type definition can easily be extracted to a variable:
String MY_MAP = typeArg("Map", String, Integer);
String MY_MAP_IMPL = typeArg("HashMap", String, Integer);
publicc().classs().begin();
{
privatee().var(MY_MAP, "_map").assign().neww(MY_MAP_IMPL).eos();
publicc().method(MY_MAP, "getMap").begin();
{
returnn("_map").eos();
}
end();
}
end();
|
public class HelloWorld {
private Map<String, Integer> _map = new HashMap<String, Integer>();
public Map<String, Integer> getMap() {
return _map;
}
}
|
All methods that deal with javadoc begin with the prefix doc . Javadoc starts with docBegin and ends with docEnd .
An example:
docBegin();
doc("Returns whether the given string is empty.");
docParam("s", "The string");
docReturn(booleann);
docSee(String, "valueOf", charr);
docSee(String, "length");
docThrows(RuntimeException, "if the function fails");
docEnd();
publicc().staticc().method(booleann, "isEmpty", String, "s").begin();
{
returnn().eq(expr().call("s", "length"), 0).eos();
}
end();
|
/**
* Returns whether the given string is empty.
* @param s The string
* @return boolean
* @see String#valueOf(char)
* @see String#length()
* @throws RuntimeException if the function fails
*/
public static boolean isEmpty(String s) {
return s.length() == 0;
}
|
As you can see in the code above, the expr method occurs occasionally.
The expr method creates a new internal string buffer, a new branch which is added as a whole to the ouput.
Instead of passing a simple identifier to a sugar method,
you pass the result of a expr to the method.
An example:
call("list", "set", 0, quote("hello")).eos();
call("list", "set", 0, expr().calll("getName")).eos();
call("list", "set", 0, calll("getName")).eos();
call("list", "set", 0, "getName()").eos();
|
list.set(0, "hello");
list.set(0, getName());
getName()list.set(0, );
list.set(0, getName());
|
The methods assign and returnn have special versions that reduces the need of expr calls.
Instead of writing:
var(String, "s", expr().calll("getName")).eos();
|
You can write:
var(String, "s").assign().calll("getName").eos();
|
String s = getName();
|
Instead of writing:
returnn(expr().calll("getName")).eos();
|
You can write:
returnn().calll("getName").eos();
|
return getName();
|
|
|