alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Groovy example source code file (ConfigSlurper.groovy)

This example Groovy source code file (ConfigSlurper.groovy) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Groovy tags/keywords

bean, closure, configbinding, configbinding, configobject, configobject, configslurper, configslurper, env_method, env_settings, groovyclassloader, groovyruntimeexception, javabean, linkedlist, string, string

The Groovy ConfigSlurper.groovy source code

/*
 * 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
import org.codehaus.groovy.runtime.InvokerHelper

/**
* <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>
*   grails.webflow.stateless = true
*    smtp {
*        mail.host = 'smtp.myisp.com'
*        mail.auth.user = 'server'
*    }
*    resources.URL = "http://localhost:80/resources"
* </pre>
*
* <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.5
*/

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
        }
        def getPropertyClosure = { 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 {
                try {
                    result = InvokerHelper.getProperty(this, name);
                } catch (GroovyRuntimeException e) {
                    result = new ConfigObject()
                    assignName.call(name,result)
                }
            }
            result
        }
        mc.getProperty = getPropertyClosure
        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
                    if (stack.last.config.get(name) instanceof ConfigObject) {
                        co = stack.last.config.get(name)
                    } else {
                        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)
    }
}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy ConfigSlurper.groovy source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.