如何在FactoryBuilderSupport中定义propertyMissing()而不会丢失脚本委托

时间:2014-07-23 12:29:41

标签: groovy dsl

我正在尝试定义用于构建特定于域的查询的DSL。 DSL是使用FactoryBuilderSupport实现的,所以它看起来像下面(缩写):

class QueryBuilder extends FactoryBuilderSupport {
   {
     registerFactory('query', new QueryFactory())
     registerFactory('match', new QueryMatchFactory())
     registerFactory('obj', new ObjFactory())
   }
   def propertyMissing(String name) {
     //give user meaningful message about the bad 'name' here
   }
}

如果我只是将它作为一个groovy脚本运行(在IntelliJ或其他任何东西中),这可以正常工作。脚本如下所示:

def bldr = new QueryBuilder()

def queryBuilder = bldr.query() {
  match {
    obj {
      //More stuff in here
    }
  }
}

现在我真正想要的是从Web界面获取脚本并运行它。所以我的第一个镜头是创建一个委托脚本。无论出于何种原因,我不得不说delegate.query()而不仅仅是查询或忽略我的委托。这与我读到的文档相矛盾。

class DslEvaluator {

 def evaluateDsl (dsl) {
    QueryBuilder queryBuilder = new QueryBuilder()
    def compilerConfiguration = new CompilerConfiguration()
    compilerConfiguration.scriptBaseClass = DelegatingScript.class.name

    def shell = new GroovyShell(this.class.classLoader, new Binding(), compilerConfiguration)

    Script script = shell.parse("""
    delegate.query() {
        ${dsl}
    }
    """)
    script.setDelegate queryBuilder
    script.run()
 }
}

只要我没有定义propertyMissing方法,这就可以正常工作。如果我确定它,它会因某种原因被调用,这就是我看到的错误:

at com.xxx.xxx.site.dsl.QueryBuilder.propertyMissing(QueryBuilder.groovy:26)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
 at        sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
at groovy.lang.MetaClassImpl.invokeMissingProperty(MetaClassImpl.java:778)
at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:1731)
at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:3458)
at groovy.util.DelegatingScript.getProperty(DelegatingScript.java:102)
at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:231)
at Script1.run(Script1.groovy:2)
at Script1$run.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)

为了完整性,这里是测试用例。

@Test
 void testEvaluateDsl() {
    DslEvaluator dslEvaluator = new DslEvaluator()
    dslEvaluator.evaluateDsl("""
 match {
    obj {
        //more stuff in here
    }
 }
 """)
}

那么如何处理添加propertyMissing()方法而不会失去在运行时作为脚本构建和执行它的能力呢?

1 个答案:

答案 0 :(得分:0)

我想我现在可以回答我自己的问题了。

在仔细观察之后,我注意到了一些事情。

  1. FactoryBuilderSupport扩展了Binding。因此,要将额外的变量绑定到脚本,我只需要在构建器实例上调用setVariable()。我可以通过任何AbstractFactory方法中的getVariable()方法访问绑定变量,因为它们都提供了构建器的实例。

  2. 我试图弄清楚如何在构建器上设置解析策略,所以我不需要在我的dsl中使用delegate.query。这是错误的做法。 FactoryBuilderSupport在其构建方法中获取脚本,并为我处理所有这些。一旦这样做,它还解决了无法提供propertyMissing()方法的问题。构建器还将在提供的脚本上调用run。

  3. 在一天结束时,这就是我现在所拥有的。

    def evaluateDsl (dsl) {
        QueryBuilder queryBuilder = new QueryBuilder()
        def shell = new GroovyShell(this.class.classLoader, queryBuilder)
    
        Script script = shell.parse("""
        query() {
            ${dsl}
        }
        """)
        Query query = queryBuilder.build(script)
        query.toQueryString()
    }