2012-08-13 30 views
0

我有一个应用程序,其中一些配置存储在数据库中,启动时它们被注入配置对象。 它们的存储方式是object path字符串,如:"some.config.value"Groovy绑定:rebind值

所以解析后,就变成这个样图: [some: [config: [value: [:]]]]

我实现了这个使用Binding,这里是代码:

Binding bind = new Binding() 
bind.tmp = bean // Bean is an object where it is being injected 

String[] traverse = stringPath.split(/\./) 
def shift = bind.tmp 
traverse.eachWithIndex { String it, int i -> 
    if (it) { 
     if (!(shift instanceof Map)) { 
      shift = ["$it": [:]] // <-- Doesn't work 
     } else { 
      if (!shift?.containsKey(it)) { 
       // val - is a value to assign to last key in path 
       shift[it] = (i + 1 == traverse.size()) ? val : [:] 
      } else if (shift?.containsKey(it) && (i + 1 == traverse.size())) { 
       shift[it] = val 
      } 
     } 

     shift = shift[it] 
    } 
} 

这种方法的问题是,如果我已经定义键绑定,我似乎无法重新定义它(在shift = ["$it": [:]])。

例如:如果我有config.valconfig.val.moreConfig - 则不会分配moreConfig

经过一段时间,我只是决定通过在所有路径中自动声明value键来指定值,但我仍然好奇是否有方法来重新定义绑定中的值?

或者在groovy中通过Binding声明的值变为可变吗?

更新: bean是一个哈希映射与硬编码配置。它可能只是一张空白的地图。 基本上我在做的是在那里注入更多有价值的地图。

如果我有值的链条:

  • "config.some.var" = 10
  • "config.some.var2" = 20
  • "config.some.var.more" = 30

会导致:

[config: [ 
    some: [ 
     var: 10, 
     var2: 20 
    ] 
]] 

预期的最新数据将被丢弃,因为它的值已经为10。我想要的是用最新的值覆盖第一个值,同时保持所有其他值处于相同的深度级别。

上面的代码产生如下:

def bean = [someOtherConfig: []] 
convert bean, 'config.var', 10 
convert bean, 'config.var2', 20 
convert bean, 'config.var.more', 30 
assert bean == [someOtherConfig: [], config: [var: 10, var2: 20]] 
+0

你能提供一个运行的例子,但不工作,因为你想?我不能运行上面的示例,因为我不知道'bean'应该是什么...... – 2012-08-13 11:22:45

回答

1

我无法从你的问题看你想要做什么,但是这给你,你说你会从输入期望的输出你描述:

def build = { name -> 
    name.split(/\./).reverse().inject([:]) { m, n -> 
    [ (n): m ] 
    } 
} 

def map = build('some.config.value') 

assert map == ['some':['config':['value':[:]]]] 

你也可以做一些有趣的像这样的事情通过与闭幕代表玩:

def map = new ConfigBuilder().build { 
    config.some.var = 10 
    config.some.var2 = 20 
    config.some.var.more = 30 
} 

assert map == [config:[some:[var:10, var2:20]]] 

// Implementation 

class ConfigBuilder { 
    private Map map, curr 
    private boolean ignore = false 

    ConfigBuilder(Map initial=[:]) { 
    this.map = initial 
    this.curr = this.map 
    } 

    def build(Closure c) { 
    c.delegate = this 
    c.resolveStrategy = Closure.DELEGATE_FIRST 
    c() 
    map 
    } 

    def propertyMissing(String name) { 
    if(ignore) return this 
    if(curr[ name ] == null) { 
     curr[ name ] = [:] 
     curr = curr[ name ] 
    } 
    else if(curr[ name ] instanceof Map) { 
     curr = curr[ name ] 
    } 
    else { 
     ignore = true 
    } 
    this 
    } 

    void setProperty(String name, value) { 
    if(!ignore) { 
     curr[ name ] = value 
    } 
    // Reset and go again 
    ignore = false 
    curr = map 
    value 
    } 

} 
+0

感谢您的回答。我已经更新了我的问题,但同时会尝试调整您的示例。 – 2012-08-13 12:23:15

+0

@liaant你看过[使用'ConfigSlurper'](http://groovy.codehaus.org/gapi/groovy/util/ConfigSlurper.html)吗?它不采用默认值,并会在重新分配时抛出异常,但也可能有所帮助。 – 2012-08-13 12:53:30

+0

@liaant发布了一个更新到我的答案...没有经过巨大的测试,但可能是有趣的... – 2012-08-13 13:04:20

0

我不知道是否有可能作出关键性的结合值可变的,但你应该能够得到一个单一的几乎相同的效果您只绑定一次的可变值。