使用具有无效键的DSL的groovy映射

时间:2016-08-04 07:38:44

标签: groovy dsl

我目前正在构建一个小型DSL,它需要在key = value对中指定一组属性,但是这些键可能包含短划线' - '或期间'。'我似乎无法让它发挥作用。

归结为我基本上尝试将Map作为委托传递给闭包,但语法一直让我着迷。

举个例子,考虑一下:

def map = [:]

map.with {
    example1 = 123
    //exam-ple2 = 123
    //'exam-ple3' = 123
    //(exam-ple4) = 123
    exam.ple5 = 123
    //'exam.ple6' = 123
}

示例1很好,键等于值并且易于阅读。例2和4根据编译器的二进制表达式而不能编译。示例3和6是常量表达式,不会编译。示例5将编译,但在运行时生成NPE。

我可以使用解决方法,例如将Map作为参数传递给闭包,这给出了我的示例3和6,但它的冗长让我烦恼。

有没有人有任何想法如何巧妙地DSL属性地图?

BTW:我从java调用DSL并不是很常规,所以解析方面的技巧必须是java:)

更新1 :在初步评论和答案之后..

因此,GroovyShell将脚本评估为DelegatingScript,其中委托是Java对象。闭包含来自.properties文件的属性,需要在不同的上下文中定义,例如

env {
  server-name=someHost1
  database.name=someHost2
  clientName=someHost3
}

委托(Java)对象将此块读为

public void env(Closure closure) {
  Map map = new HashMap();
  closure.setDelegate(map);
  closure.setResolveStrategy(Closure.DELEGATE_ONLY);
  closure.call();
  ... do something with map...

}

现在用户(即不是我)可能会从原始属性文件复制到脚本中并更改名称,因此我宁愿他们可以在不必编辑太多的情况下完成它,因为它必然会导致拼写错误。

正如我所说,我也有例子3和6也被覆盖了,但是,是的,蒂姆,我忘记了暗示它:)

现在我已将格式更改为字符串,因此DSL会写入类似

的内容
env '''
  server-name=someHost1
  database.name=someHost2
  clientName=someHost3
'''

也就是说,使用多行字符串而不是闭包,然后读取字符串并使用标准的java.util.Properties:

public void env(String envString) {
  Properties properties = new Properties;
  properties.load(new StringReader(envString))
  ....etc

}

虽然这有效但是闭合和多线串的组合是目前唯一的缺点。

1 个答案:

答案 0 :(得分:0)

在地图声明中,Groovy将example1example2等标识符解析为字符串键到地图:

[ example1: 1, example2: 2 ]

.with{}上下文中,它可能使用setProperty(property, value)机制。

但您的案例包含表达式exam.pleexam-ple。这些表达式具有优先权,因此,Groovy将首先尝试解决它们(可能分别为exam.getProperty('ple')exam.minus(ple))。

你有一些语法替代方案,但你必须向Groovy说清楚应该是什么字符串键以及其他表达式是什么:

def map = [
    'exam.ple4' : 4, // direct map declaration
    example5 : 5 // unambiguous key declaration: no quotes needed 
]

map.with {
    example1 = 1
    put 'exam.ple2', 2 // ambiguous, needs quotes
    it.'exam-ple3' = 3 // as per @TimYates suggestion
}

assert map['exam-ple3'] == 3
assert map.'exam.ple2' == 2
assert map['exam.ple4'] == 4
assert map.example5 == 5 // again, no quotes needed for key