| /* |
| * Copyright 2003-2007 the original author or authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package groovy.util |
| |
| import java.beans.Introspector |
| import java.beans.BeanInfo |
| |
| /** |
| * <p> |
| * ConfigSlurper is a utility class for reading configuration files defined in the form of Groovy |
| * scripts. Configuration settings can be defined using dot notation or scoped using closures |
| * |
| * <pre><code> |
| * grails.webflow.stateless = true |
| * smtp { |
| * mail.host = 'smtp.myisp.com' |
| * mail.auth.user = 'server' |
| * } |
| * resources.URL = "http://localhost:80/resources" |
| * </pre></code> |
| * |
| * <p>Settings can either be bound into nested maps or onto a specified JavaBean instance. In the case |
| * of the latter an error will be thrown if a property cannot be bound. |
| * |
| * @author Graeme Rocher |
| * @since 1.1 |
| * |
| * <p/> |
| * Created: Jun 19, 2007 |
| * Time: 3:53:48 PM |
| */ |
| |
| class ConfigSlurper { |
| |
| private static final ENV_METHOD = "environments" |
| static final ENV_SETTINGS = '__env_settings__' |
| //private BeanInfo bean |
| //private instance |
| GroovyClassLoader classLoader = new GroovyClassLoader() |
| String environment |
| private envMode = false |
| private Map bindingVars |
| |
| ConfigSlurper() { } |
| |
| /** |
| * Constructs a new ConfigSlurper instance using the given environment |
| * @param env The Environment to use |
| */ |
| ConfigSlurper(String env) { |
| this.environment = env |
| } |
| |
| /** |
| * Sets any additional variables that should be placed into the binding when evaluating Config scripts |
| */ |
| void setBinding(Map vars) { |
| this.bindingVars = vars |
| } |
| |
| /** |
| * Parses a ConfigObject instances from an instance of java.util.Properties |
| * @param The java.util.Properties instance |
| */ |
| ConfigObject parse(Properties properties) { |
| ConfigObject config = new ConfigObject() |
| for(key in properties.keySet()) { |
| def tokens = key.split(/\./) |
| |
| def current = config |
| def currentToken |
| def last |
| def lastToken |
| def foundBase = false |
| for(token in tokens) { |
| if (foundBase) { |
| // handle not properly nested tokens by ignoring |
| // hierarchy below this point |
| lastToken += "." + token |
| current = last |
| } else { |
| last = current |
| lastToken = token |
| current = current."${token}" |
| if(!(current instanceof ConfigObject)) foundBase = true |
| } |
| } |
| |
| if(current instanceof ConfigObject) { |
| if(last[lastToken]) { |
| def flattened = last.flatten() |
| last.clear() |
| flattened.each { k2, v2 -> last[k2] = v2 } |
| last[lastToken] = properties.get(key) |
| } |
| else { |
| last[lastToken] = properties.get(key) |
| } |
| } |
| current = config |
| } |
| return config |
| } |
| /** |
| * Parse the given script as a string and return the configuration object |
| * |
| * @see ConfigSlurper#parse(groovy.lang.Script) |
| */ |
| ConfigObject parse(String script) { |
| return parse(classLoader.parseClass(script)) |
| } |
| |
| /** |
| * Create a new instance of the given script class and parse a configuration object from it |
| * |
| * @see ConfigSlurper#parse(groovy.lang.Script) |
| */ |
| ConfigObject parse(Class scriptClass) { |
| return parse(scriptClass.newInstance()) |
| } |
| |
| /** |
| * Parse the given script into a configuration object (a Map) |
| * @param script The script to parse |
| * @return A Map of maps that can be navigating with dot de-referencing syntax to obtain configuration entries |
| */ |
| ConfigObject parse(Script script) { |
| return parse(script, null) |
| } |
| |
| /** |
| * Parses a Script represented by the given URL into a ConfigObject |
| * |
| * @param scriptLocation The location of the script to parse |
| * @return The ConfigObject instance |
| */ |
| ConfigObject parse(URL scriptLocation) { |
| return parse(classLoader.parseClass(scriptLocation.text).newInstance(), scriptLocation) |
| } |
| |
| /** |
| * Parses the passed groovy.lang.Script instance using the second argument to allow the ConfigObject |
| * to retain an reference to the original location other Groovy script |
| * |
| * @param script The groovy.lang.Script instance |
| * @param location The original location of the Script as a URL |
| * @return The ConfigObject instance |
| */ |
| ConfigObject parse(Script script, URL location) { |
| def config = location ? new ConfigObject(location) : new ConfigObject() |
| GroovySystem.metaClassRegistry.removeMetaClass(script.class) |
| def mc = script.class.metaClass |
| def prefix = "" |
| LinkedList stack = new LinkedList() |
| stack << [config:config,scope:[:]] |
| def pushStack = { co -> |
| stack << [config:co,scope:stack.last.scope.clone()] |
| } |
| def assignName = { name, co -> |
| def current = stack.last |
| current.config[name] = co |
| current.scope[name] = co |
| } |
| mc.getProperty = { String name -> |
| def current = stack.last |
| def result |
| if(current.config.get(name)) { |
| result = current.config.get(name) |
| } else if(current.scope[name]) { |
| result = current.scope[name] |
| } else { |
| result = new ConfigObject() |
| assignName.call(name,result) |
| } |
| result |
| } |
| mc.invokeMethod = { String name, args -> |
| def result |
| if(args.length == 1 && args[0] instanceof Closure) { |
| if(name == ENV_METHOD) { |
| try { |
| envMode = true |
| args[0].call() |
| } finally { |
| envMode = false |
| } |
| } else if (envMode) { |
| if(name == environment) { |
| def co = new ConfigObject() |
| config[ENV_SETTINGS] = co |
| |
| pushStack.call(co) |
| try { |
| envMode = false |
| args[0].call() |
| } finally { |
| envMode = true |
| } |
| stack.pop() |
| } |
| } else { |
| def co = new ConfigObject() |
| assignName.call(name, co) |
| pushStack.call(co) |
| args[0].call() |
| stack.pop() |
| } |
| } else if (args.length == 2 && args[1] instanceof Closure) { |
| try { |
| prefix = name +'.' |
| assignName.call(name, args[0]) |
| args[1].call() |
| } finally { prefix = "" } |
| } else { |
| MetaMethod mm = mc.getMetaMethod(name, args) |
| if(mm) { |
| result = mm.invoke(delegate, args) |
| } else { |
| throw new MissingMethodException(name, getClass(), args) |
| } |
| } |
| result |
| } |
| script.metaClass = mc |
| |
| def setProperty = { String name, value -> |
| assignName.call(prefix+name, value) |
| } |
| def binding = new ConfigBinding(setProperty) |
| if(this.bindingVars) { |
| binding.getVariables().putAll(this.bindingVars) |
| } |
| script.binding = binding |
| |
| |
| script.run() |
| |
| def envSettings = config.remove(ENV_SETTINGS) |
| if(envSettings) { |
| config.merge(envSettings) |
| } |
| |
| return config |
| } |
| |
| } |
| /** |
| * Since Groovy Script don't support overriding setProperty, we have to using a trick with the Binding to provide this |
| * functionality |
| */ |
| class ConfigBinding extends Binding { |
| def callable |
| ConfigBinding(Closure c) { |
| this.callable = c |
| } |
| |
| void setVariable(String name, Object value) { |
| callable(name, value) |
| } |
| } |
| ----- |
| [Groovy script] |
| [Package definition] |
| [Modifiers] |
| [Import statement] |
| [Modifiers] |
| [Import statement] |
| [Modifiers] |
| [Class definition : ConfigSlurper] |
| [Modifiers] |
| [Extends clause] |
| [Implements clause] |
| [Type definition body] |
| [Variable definitions] |
| [Modifiers] |
| [Field : ENV_METHOD] |
| [Variable definitions] |
| [Modifiers] |
| [Field : ENV_SETTINGS] |
| [Variable definitions] |
| [Modifiers] |
| [Field : classLoader] |
| [Variable definitions] |
| [Modifiers] |
| [Field : environment] |
| [Variable definitions] |
| [Modifiers] |
| [Field : envMode] |
| [Variable definitions] |
| [Modifiers] |
| [Field : bindingVars] |
| [Constructor : ConfigSlurper] |
| [Modifiers] |
| [Parameter list] |
| [Constructor : ConfigSlurper] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : env] |
| [Modifiers] |
| [Method : setBinding] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : vars] |
| [Modifiers] |
| [Method : parse] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : properties] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Parameter : key] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Parameter : token] |
| [Modifiers] |
| [Parameter list] |
| [Variable definitions] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : k2] |
| [Modifiers] |
| [Parameter : v2] |
| [Modifiers] |
| [Method : parse] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : script] |
| [Modifiers] |
| [Method : parse] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : scriptClass] |
| [Modifiers] |
| [Method : parse] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : script] |
| [Modifiers] |
| [Method : parse] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : scriptLocation] |
| [Modifiers] |
| [Method : parse] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : script] |
| [Modifiers] |
| [Parameter : location] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : co] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : name] |
| [Modifiers] |
| [Parameter : co] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : name] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : name] |
| [Modifiers] |
| [Parameter : args] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : name] |
| [Modifiers] |
| [Parameter : value] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Variable definitions] |
| [Modifiers] |
| [Class definition : ConfigBinding] |
| [Modifiers] |
| [Extends clause] |
| [Implements clause] |
| [Type definition body] |
| [Variable definitions] |
| [Modifiers] |
| [Field : callable] |
| [Constructor : ConfigBinding] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : c] |
| [Modifiers] |
| [Method : setVariable] |
| [Modifiers] |
| [Parameter list] |
| [Parameter : name] |
| [Modifiers] |
| [Parameter : value] |
| [Modifiers] |