...
| Code Block |
|---|
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);
//System.out.println("Called invokeMethod with name: " + name + " arguments: " + list);
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) {
// push new node on stack
Object oldCurrent = current;
current = node;
// lets register the builder as the delegate
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;
}
}
|
The NodeBuilder example
To wbe be able to write such a code :
| Code Block |
|---|
def 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 :
| Code Block |
|---|
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(); } } |
The MarkupBuilder.java class as second example
| Code Block |
|---|
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; } } |