Reputation: 1901
I want to create a nested builder. Something like:
meta('JSON') {
// JSON content using JSON Builder in this case
}
content('HTML') {
// HTML content using MarkupTemplateEngine in this case
}
I don't want to have
JsonBuilder builder = new JsonBuilder()
builder.author {
}
or
def builder = new groovy.xml.MarkupBuilder()
builder.html {
}
How can initial builder and the nested builder be setup?
I have defined the DSL common elements as:
import groovy.transform.CompileStatic
import org.eclipse.collections.impl.map.mutable.UnifiedMap
@CompileStatic
abstract class DSLElement extends UnifiedMap {
DSLElement(Map attributes) {
putAll(attributes)
}
getAt(Object key) {
get(key)
}
putAt(Object key, Object value) {
put(key, value)
}
}
@CompileStatic
class BaseFactoryBuilder extends FactoryBuilderSupport {
BaseFactoryBuilder(boolean init = true) {
super(init)
}
}
@CompileStatic
class DSLFactoryBuilder
extends BaseFactoryBuilder
implements MetaRegistration,
ContentRegistration,
TemplateRegistration {
}
Individual elements I have defined as:
import groovy.transform.CompileStatic
import groovy.transform.InheritConstructors
import groovy.transform.SelfType
@CompileStatic
@InheritConstructors
class Meta extends DSLElement {}
@CompileStatic
class MetaFactory extends AbstractFactory {
boolean isLeaf() {
return false
}
Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes)
throws InstantiationException, IllegalAccessException {
return new Meta(attributes)
}
}
@SelfType(DSLFactoryBuilder)
@CompileStatic
trait MetaRegistration {
registerMeta() {
registerFactory("meta", new MetaFactory())
}
}
and
import groovy.transform.CompileStatic
import groovy.transform.InheritConstructors
import groovy.transform.SelfType
@CompileStatic
@InheritConstructors
class Content extends DSLElement {
}
@CompileStatic
class ContentFactory extends AbstractFactory {
boolean isLeaf() {
return false
}
Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes)
throws InstantiationException, IllegalAccessException {
return new Content(attributes)
}
}
@SelfType(DSLFactoryBuilder)
@CompileStatic
trait ContentRegistration {
registerMeta() {
registerFactory("content", new ContentFactory())
}
}
I have not figured you how to do the nesting another builder and the parameter 'JSON'
or 'HTML'
.
I am following the FactoryBuilderSupport example given in: Groovy for Domain-Specific Languages
Upvotes: 0
Views: 277
Reputation: 28599
maybe i did not fully understand the question - it's too long to read and code you provided not runnable (or not full)
i assume you want to achieve this:
def html = meta('HTML'){
person(name:'foo')
}
def json = meta('JSON'){
person(name:'bar')
}
if so, here is a code to implement it
String meta(String ctxName, Closure builderBody){
def builder = null
def writer = null
if(ctxName=='HTML'){
writer=new StringWriter()
builder=new groovy.xml.MarkupBuilder(writer)
}else if(ctxName=='JSON'){
builder=new groovy.json.JsonBuilder()
}
else throw new RuntimeException("unsupported meta=`$ctxName`")
def result = builder.with(builderBody)
if(ctxName=='HTML'){
return writer.toString()
}else if(ctxName=='JSON'){
return builder.toPrettyString()
}
}
println meta('HTML'){
person(name:'foo')
}
println meta('JSON'){
person(name:'bar')
}
result:
<person name='foo' />
{
"person": {
"name": "bar"
}
}
Upvotes: 1