从String对象在Groovy中创建动态闭包

时间:2013-06-15 06:18:26

标签: grails groovy

我想用Grails(GORM)中的Criteria API创建一个查询。 查询必须是这样的:

MyEntity.createCriteria().list{
   assoc{
      parent{
         eq("code", val)
      }
   }
}

我需要的是从String对象动态构建嵌套闭包。上例中的字符串将为"assoc.parent.code"。我逐点拆分String(通过执行String.split("\\.")),但我不知道如何构造嵌套的闭包:

   assoc{
      parent{
         eq("code", val)
      }
   }

动态地基于上面分割字符串的数组。

2 个答案:

答案 0 :(得分:2)

createAlias怎么样?你可以尝试这样的事情:

def path = "assoc.parent.code"

def split = path.split(/\./)

MyEntity.createCriteria().list {
  // this will get you 'createAlias( assoc.parent, alias1 )'
  createAlias split.take( split.size() - 1 ), "alias1"

  // this will get you 'eq(alias1.code, userInput)'
  eq "alias1.${split[-1]}", userInput
}

此代码段不是通用的,但您明白了。


<强>更新

不常规,但您可以构建一个包含带闭包的代码的字符串,并使用GroovyShell对其进行评估:

assoc = 'assoc.parent.child.name'
split = assoc.split( /\./ )
path  = split[-2..0] // will get us 'child.parent.assoc';
                     // we will build it from inside-out

def firstClosure = "{ eq '${split[-1]}', 'john doe' }"
def lastClosure = firstClosure

for (entity in path) {
  def criteriaClosure =  "{ ${entity} ${lastClosure} }"
  lastClosure = criteriaClosure
}

assert lastClosure == "{ assoc { parent { child { eq 'name', 'john doe' } } } }"
def builtClosure = new GroovyShell().evaluate("return " + lastClosure)
assert builtClosure instanceof Closure

答案 1 :(得分:1)

更通用的方法是metaClass String,如下所示,并将其用于任何类型的分隔符
. | , - ~等等。

String.metaClass.convertToClosureWithValue = {op, val ->
    split = delegate.split(op) as List
    if(split.size() == 1) {return "Cannot split string '$delegate' on '$op'"} 

    items = []
    split.each{
        if(it == split.last()){
            items << "{ eq '$it', $val }"
            split.indexOf(it).times{items.push("}")}
        } else {
            items << "{$it"
        }
    }

    println items.join()
    new GroovyShell().evaluate("return " + items.join())
}

assert "assoc.parent.child.name".convertToClosureWithValue(/\./, "John Doe") instanceof Closure
assert "assoc-parent-child-name".convertToClosureWithValue(/\-/, "Billy Bob") instanceof Closure
assert "assoc|parent|child|grandChild|name".convertToClosureWithValue(/\|/, "Max Payne") instanceof Closure
assert "assoc~parent~child~grandChild~name".convertToClosureWithValue('\\~', "Private Ryan") instanceof Closure
assert "assocparentchildname".convertToClosureWithValue(/\|/, "Captain Miller") == "Cannot split string 'assocparentchildname' on '\\|'"

//Print lines from items.join()
{assoc{parent{child{ eq 'name', John Doe }}}}
{assoc{parent{child{ eq 'name', Billy Bob }}}}
{assoc{parent{child{grandChild{ eq 'name', Max Payne }}}}}
{assoc{parent{child{grandChild{ eq 'name', Private Ryan }}}}}