All Downloads are FREE. Search and download functionalities are using the official Maven repository.

groovy.util.ConfigObject.groovy Maven / Gradle / Ivy

There is a newer version: 3.0.22
Show newest version
/* Copyright 2006-2007 Graeme Rocher
 *
 * 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


/**
* A ConfigObject at a simple level is a Map that creates configuration entries (other ConfigObjects) when referencing them.
* This means that navigating to foo.bar.stuff will not return null but nested ConfigObjects which are of course empty maps
* The Groovy truth can be used to check for the existance of "real" entries.
*
* @author Graeme Rocher
* @since 1.1
*/
class ConfigObject extends LinkedHashMap implements Writable {

    // would be better to have these availabe as constants from Groovy, but couldn't find
    static final KEYWORDS = ['class', 'extends', 'implements', 'package','return','def',
                             'try','finally','this','new','catch','switch','case','default','while','if',
                             'else','elseif','private','protected','final','for','in','byte','short','break',
                             'instanceof','synchronized','const','float','null','throws','do','super','with',
                             'threadsafe','transient','native','interface','any','double','volatile','as',
                             'assert','goto','enum','int','boolean','char','false','true','static','abstract',
                             'continue','import','void','long']

    static final TAB_CHARACTER = '\t'

	/**
	 * The config file that was used when parsing this ConfigObject
	 */
    URL configFile

    ConfigObject(URL file) {
        this.configFile = file
    }

    ConfigObject() {}

    /**
	 * Writes this config object into a String serialized representation which can later be parsed back using the parse()
	 * method
	 *
     * @see groovy.lang.Writable#writeTo(java.io.Writer)
     * @see #parse(URL)
     */ 
	Writer writeTo(Writer outArg) {
        def out
        try {
            out = new BufferedWriter(outArg)
            writeConfig("",this, out, 0, false)
        } finally {
            out.flush()
        }

		return outArg
	}
                  

    /**
     * Overrides the default getProperty implementation to create nested ConfigObject instances on demand
     * for non-existant keys
     */
    def getProperty(String name) {  
		if(name == 'configFile') return this.configFile
        if(!containsKey (name)) {
           ConfigObject prop = new ConfigObject(this.configFile)
           put(name, prop)
           return prop
        }
        return get(name)
    }
    
    /**
     * A ConfigObject is a tree structure consisting of nested maps. This flattens the maps into
     * a single level structure like a properties file
     */
    Map flatten() {
        return flatten(null)
    }
    /**
     * Flattens this ConfigObject populating the results into the target Map
     *
     * @see ConfigObject#flatten()
     */
    Map flatten(Map target) {
        if(target == null)target = new ConfigObject()
        populate("", target, this)
        target
    }

    /**
     * Merges the given map with this ConfigObject overriding any matching configuration entries in this ConfigObject
     *
     * @param other The ConfigObject to merge with
     * @return The result of the merge
     */
    Map merge(ConfigObject other) {
        return merge(this,other)
    }


    /**
     * Converts this ConfigObject into a the java.util.Properties format, flattening the tree structure beforehand
     * @return A java.util.Properties instance
     */
    Properties toProperties() {
        def props = new Properties()
        flatten(props)
        props = convertValuesToString(props)
        return props
    }

    /**
     * Converts this ConfigObject ino the java.util.Properties format, flatten the tree and prefixing all entries with the given prefix
     * @param prefix The prefix to append before property entries
     * @return A java.util.Properties instance
     */
    Properties toProperties(String prefix) {
        def props = new Properties()
        populate("${prefix}.", props, this)
        props = convertValuesToString(props)
        return props
    }

    private merge(Map config, Map other) {
        for(entry in other) {

            def configEntry = config[entry.key]
            if(configEntry == null) {
                config[entry.key] = entry.value
                continue
            }
            else {
                if(configEntry instanceof Map && configEntry.size() > 0 && entry.value instanceof Map) {
                    // recurse
                    merge(configEntry, entry.value)
               }
               else {
                    config[entry.key] = entry.value
               }
            }
        }
        return config

    }

    private writeConfig(String prefix,ConfigObject map, out, Integer tab, boolean apply) {
        def space = apply ? TAB_CHARACTER*tab : ''
        for(key in map.keySet()) {
            def value = map.get(key)


			if(value instanceof ConfigObject) {
                if(!value.isEmpty()) {
                  def dotsInKeys = value.find { entry -> entry.key.indexOf('.') > -1 }
                  def configSize = value.size()
                  def firstKey = value.keySet().iterator().next()
                  def firstValue = value.values().iterator().next()
                  def firstSize
                  if(firstValue instanceof ConfigObject){
                      firstSize = firstValue.size()
                  }
                  else { firstSize = 1 }
                  if(configSize == 1|| dotsInKeys )  {

                      if(firstSize == 1 && firstValue instanceof ConfigObject) {
                          key = KEYWORDS.contains(key) ? key.inspect() : key
                          def writePrefix = "${prefix}${key}.${firstKey}."
                          writeConfig(writePrefix, firstValue, out, tab, true)
                      }
                      else if(!dotsInKeys && firstValue instanceof ConfigObject) {
                          writeNode(key, space, tab,value, out)
                      }  else {
                          for(j in value.keySet()) {
                              def v2 = value.get(j)
                              def k2 = j.indexOf('.') > -1 ? j.inspect() : j
                              if(v2 instanceof ConfigObject) {
                                  key = KEYWORDS.contains(key) ? key.inspect() : key
                                  writeConfig("${prefix}${key}", v2, out, tab, false)
                              }
                              else {
                                  writeValue("${key}.${k2}", space, prefix, v2, out)
                              }
                          }
                      }

                  }
                  else {
                      writeNode(key, space,tab, value, out)
                  }                  
                }
			}
			else {

                writeValue(key, space, prefix, value, out)
			}
		}
	}

    private writeValue(key, space, prefix, value, out) {
        key = key.indexOf('.') > -1 ? key.inspect() : key
        boolean isKeyword = KEYWORDS.contains(key)
        key = isKeyword ? key.inspect() : key

        if(!prefix && isKeyword) prefix = "this."
        out << "${space}${prefix}$key=${value.inspect()}"
        out.newLine()
    }

    private writeNode(key, space, tab, value, out) {
        key = KEYWORDS.contains(key) ? key.inspect() : key
        out << "${space}$key {"
        out.newLine()
        writeConfig("",value, out, tab+1, true)
        def last = "${space}}"
        out << last
        out.newLine()
    }

    private convertValuesToString(props) {
        def newProps = [:]
        for(e in props) {
            newProps[e.key] = e.value?.toString()
        }
        return newProps
    }

    private populate(suffix, config, map) {
        for(key in map.keySet()) {
            def value = map.get(key)
            if(value instanceof Map) {
                populate(suffix+"${key}.", config, value)
            }
            else {
                try {
                  config[suffix+key] = value
                }
                catch (java.lang.NullPointerException e) {
                    // it is idiotic story but if config map doesn't allow null values (like Hashtable)
                    // we can't do too much
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy