如何使用FactoryBuilderSupport在Groovy上创建基于嵌套Builder的DSL

问题描述

我想创建一个嵌套的生成器。像这样:

file1: select file
file2: select file
file3: select file
....

我不想拥有

Meta('JSON') {
   // JSON content using JSON Builder in this case
}
content('HTML') {
   // HTML content using MarkupTemplateEngine in this case
}

JsonBuilder builder = new JsonBuilder()
builder.author {
}

如何设置初始构建器和嵌套构建器?


我将DSL通用元素定义为:

def builder = new groovy.xml.MarkupBuilder()
builder.html {
}

我定义为以下元素:

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 {

}

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())
    }
}

我还没想过如何嵌套另一个构建器和参数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,illegalaccessexception { return new Content(attributes) } } @SelfType(DSLFactoryBuilder) @CompileStatic trait ContentRegistration { registerMeta() { registerFactory("content",new ContentFactory()) } } 'JSON'

我正在遵循Groovy for Domain-Specific Languages

中给出的FactoryBuilderSupport示例

解决方法

也许我没有完全理解这个问题-太长了,以至于您提供的代码无法运行(或不完整)

我假设您想实现这一目标:

def html = meta('HTML'){
    person(name:'foo')
}

def json = meta('JSON'){
    person(name:'bar')
}

如果是的话,这是实现它的代码

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')
}

结果:

<person name='foo' />
{
    "person": {
        "name": "bar"
    }
}