Feeds
|
Make a builder
|
|
To create a new builder like a the MarkupBuilder or AntBuilder, you have to implement in java (in groovy later too) a subclass of the groovy.util.BuilderSupport class. The main methods to be implemnted are the following :
- protected abstract void setParent(Object parent, Object child);
- protected abstract Object createNode(Object name); // anode without parameter and closure
- protected abstract Object createNode(Object name, Object value); //a node without parameters, but with closure
- protected abstract Object createNode(Object name, Map attributes); // aNode without closure but with parameters
- protected abstract Object createNode(Object name, Map attributes, Object value); //a node with closure and parameters
- protected Object getName(String methodName)
package groovy.util;
import groovy.lang.Closure;
import groovy.lang.GroovyObjectSupport;
import java.util.List;
import java.util.Map;
import org.codehaus.groovy.runtime.InvokerHelper;
public abstract class BuilderSupport extends GroovyObjectSupport {
private Object current;
private Closure nameMappingClosure;
private BuilderSupport proxyBuilder;
public BuilderSupport() {
this.proxyBuilder = this;
}
public BuilderSupport(BuilderSupport proxyBuilder) {
this(null, proxyBuilder);
}
public BuilderSupport(Closure nameMappingClosure, BuilderSupport proxyBuilder) {
this.nameMappingClosure = nameMappingClosure;
this.proxyBuilder = proxyBuilder;
}
public Object invokeMethod(String methodName, Object args) {
Object name = getName(methodName);
return doInvokeMethod(methodName, name, args);
}
protected Object doInvokeMethod(String methodName, Object name, Object args) {
Object node = null;
Closure closure = null;
List list = InvokerHelper.asList(args);
switch (list.size()) {
case 0:
break;
case 1:
{
Object object = list.get(0);
if (object instanceof Map) {
node = proxyBuilder.createNode(name, (Map) object);
} else if (object instanceof Closure) {
closure = (Closure) object;
node = proxyBuilder.createNode(name);
} else {
node = proxyBuilder.createNode(name, object);
}
}
break;
case 2:
{
Object object1 = list.get(0);
Object object2 = list.get(1);
if (object1 instanceof Map) {
if (object2 instanceof Closure) {
closure = (Closure) object2;
node = proxyBuilder.createNode(name, (Map) object1);
} else {
node = proxyBuilder.createNode(name, (Map) object1, object2);
}
} else {
if (object2 instanceof Closure) {
closure = (Closure) object2;
node = proxyBuilder.createNode(name, object1);
}
}
}
break;
case 3:
{
Object attributes = list.get(0);
Object value = list.get(1);
closure = (Closure) list.get(2);
node = proxyBuilder.createNode(name, (Map) attributes, value);
}
break;
}
if (node == null) {
node = proxyBuilder.createNode(name);
}
if (current != null) {
proxyBuilder.setParent(current, node);
}
if (closure != null) {
Object oldCurrent = current;
current = node;
setClosureDelegate(closure, node);
closure.call();
current = oldCurrent;
}
proxyBuilder.nodeCompleted(current, node);
return node;
}
protected void setClosureDelegate(Closure closure, Object node) {
closure.setDelegate(this);
}
protected abstract void setParent(Object parent, Object child);
protected abstract Object createNode(Object name);
protected abstract Object createNode(Object name, Object value);
protected abstract Object createNode(Object name, Map attributes);
protected abstract Object createNode(Object name, Map attributes, Object value);
protected Object getName(String methodName) {
if (nameMappingClosure != null) {
return nameMappingClosure.call(methodName);
}
return methodName;
}
protected void nodeCompleted(Object parent, Object node) {
}
protected Object getCurrent() {
return current;
}
protected void setCurrent(Object current) {
this.current = current;
}
}
To wbe able to write such a code :
someBuilder = new NodeBuilder()
someBuilder.people(kind:'folks', groovy:true) {
person(x:123, name:'James', cheese:'edam') {
project(name:'groovy')
project(name:'geronimo')
}
person(x:234, name:'bob', cheese:'cheddar') {
project(name:'groovy')
project(name:'drools')
}
}
we need :
package groovy.util;
import java.util.ArrayList;
import java.util.Map;
/**
* A helper class for creating nested trees of Node objects for
* handling arbitrary data
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
* @version $Revision: 1.3 $
*/
public class NodeBuilder extends BuilderSupport {
public static NodeBuilder newInstance() {
return new NodeBuilder();
}
protected void setParent(Object parent, Object child) {
}
protected Object createNode(Object name) {
return new Node(getCurrentNode(), name, new ArrayList());
}
protected Object createNode(Object name, Object value) {
return new Node(getCurrentNode(), name, value);
}
protected Object createNode(Object name, Map attributes) {
return new Node(getCurrentNode(), name, attributes, new ArrayList());
}
protected Object createNode(Object name, Map attributes, Object value) {
return new Node(getCurrentNode(), name, attributes, value);
}
protected Node getCurrentNode() {
return (Node) getCurrent();
}
}
package groovy.xml;
import groovy.util.BuilderSupport;
import groovy.util.IndentPrinter;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.Map;
/**
* A helper class for creating XML or HTML markup
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
* @author Stefan Matthias Aust
* @version $Revision: 1.8 $
*/
public class MarkupBuilder extends BuilderSupport {
private IndentPrinter out;
private boolean nospace;
private int state;
private boolean nodeIsEmpty = true;
public MarkupBuilder() {
this(new IndentPrinter());
}
public MarkupBuilder(PrintWriter writer) {
this(new IndentPrinter(writer));
}
public MarkupBuilder(Writer writer) {
this(new IndentPrinter(new PrintWriter(writer)));
}
public MarkupBuilder(IndentPrinter out) {
this.out = out;
}
protected void setParent(Object parent, Object child) {
}
/*
public Object getProperty(String property) {
if (property.equals("_")) {
nospace = true;
return null;
} else {
Object node = createNode(property);
nodeCompleted(getCurrent(), node);
return node;
}
}
*/
protected Object createNode(Object name) {
toState(1, name);
return name;
}
protected Object createNode(Object name, Object value) {
toState(2, name);
out.print(">");
out.print(value.toString());
return name;
}
protected Object createNode(Object name, Map attributes, Object value) {
toState(1, name);
for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
out.print(" ");
print(transformName(entry.getKey().toString()));
out.print("='");
print(transformValue(entry.getValue().toString()));
out.print("'");
}
if (value != null)
{
nodeIsEmpty = false;
out.print(">" + value + "</" + name + ">");
}
return name;
}
protected Object createNode(Object name, Map attributes) {
return createNode(name, attributes, null);
}
protected void nodeCompleted(Object parent, Object node) {
toState(3, node);
out.flush();
}
protected void print(Object node) {
out.print(node == null ? "null" : node.toString());
}
protected Object getName(String methodName) {
return super.getName(transformName(methodName));
}
protected String transformName(String name) {
if (name.startsWith("_")) name = name.substring(1);
return name.replace('_', '-');
}
protected String transformValue(String value) {
return value.replaceAll("\\'", """);
}
private void toState(int next, Object name) {
switch (state) {
case 0:
switch (next) {
case 1:
case 2:
out.print("<");
print(name);
break;
case 3:
throw new Error();
}
break;
case 1:
switch (next) {
case 1:
case 2:
out.print(">");
if (nospace) {
nospace = false;
} else {
out.println();
out.incrementIndent();
out.printIndent();
}
out.print("<");
print(name);
break;
case 3:
if (nodeIsEmpty) {
out.print(" />");
}
break;
}
break;
case 2:
switch (next) {
case 1:
case 2:
throw new Error();
case 3:
out.print("</");
print(name);
out.print(">");
break;
}
break;
case 3:
switch (next) {
case 1:
case 2:
if (nospace) {
nospace = false;
} else {
out.println();
out.printIndent();
}
out.print("<");
print(name);
break;
case 3:
if (nospace) {
nospace = false;
} else {
out.println();
out.decrementIndent();
out.printIndent();
}
out.print("</");
print(name);
out.print(">");
break;
}
break;
}
state = next;
}
}
|
|