You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2017/12/20 04:29:34 UTC

[26/47] groovy git commit: Move source files to proper packages

http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/util/ConfigObject.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/util/ConfigObject.java b/src/main/groovy/groovy/util/ConfigObject.java
new file mode 100644
index 0000000..c76bc97
--- /dev/null
+++ b/src/main/groovy/groovy/util/ConfigObject.java
@@ -0,0 +1,425 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 groovy.lang.GroovyObjectSupport;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.Writable;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.StringGroovyMethods;
+import org.codehaus.groovy.syntax.Types;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * 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 existence of "real" entries.
+ *
+ * @author Graeme Rocher
+ * @author Guillaume Laforge (rewrite in Java related to security constraints on Google App Engine)
+ * @since 1.5
+ */
+public class ConfigObject extends GroovyObjectSupport implements Writable, Map, Cloneable {
+
+    static final Collection<String> KEYWORDS = Types.getKeywords();
+
+    static final String TAB_CHARACTER = "\t";
+
+    /**
+     * The config file that was used when parsing this ConfigObject
+     */
+    private URL configFile;
+
+    private HashMap delegateMap = new LinkedHashMap();
+
+    public ConfigObject(URL file) {
+        this.configFile = file;
+    }
+
+    public ConfigObject() {
+        this(null);
+    }
+
+    public URL getConfigFile() {
+        return configFile;
+    }
+
+    public void setConfigFile(URL configFile) {
+        this.configFile = configFile;
+    }
+
+    /**
+     * 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)
+     */
+    public Writer writeTo(Writer outArg) throws IOException {
+        BufferedWriter out = new BufferedWriter(outArg);
+        try {
+            writeConfig("", this, out, 0, false);
+        } finally {
+            out.flush();
+        }
+
+        return outArg;
+    }
+
+
+    /**
+     * Overrides the default getProperty implementation to create nested ConfigObject instances on demand
+     * for non-existent keys
+     */
+    public Object getProperty(String name) {
+        if ("configFile".equals(name))
+            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
+     */
+    public Map flatten() {
+        return flatten(null);
+    }
+
+    /**
+     * Flattens this ConfigObject populating the results into the target Map
+     *
+     * @see ConfigObject#flatten()
+     */
+    public Map flatten(Map target) {
+        if (target == null)
+            target = new ConfigObject();
+        populate("", target, this);
+
+        return 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
+     */
+    public Map merge(ConfigObject other) {
+        return doMerge(this, other);
+    }
+
+
+    /**
+     * Converts this ConfigObject into a the java.util.Properties format, flattening the tree structure beforehand
+     *
+     * @return A java.util.Properties instance
+     */
+    public Properties toProperties() {
+        Properties 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
+     */
+    public Properties toProperties(String prefix) {
+        Properties props = new Properties();
+        populate(prefix + ".", props, this);
+
+        props = convertValuesToString(props);
+
+        return props;
+    }
+
+    private Map doMerge(Map config, Map other) {
+        for (Object o : other.entrySet()) {
+            Map.Entry next = (Map.Entry) o;
+            Object key = next.getKey();
+            Object value = next.getValue();
+
+            Object configEntry = config.get(key);
+
+            if (configEntry == null) {
+                config.put(key, value);
+
+                continue;
+            } else {
+                if (configEntry instanceof Map && !((Map) configEntry).isEmpty() && value instanceof Map) {
+                    // recur
+                    doMerge((Map) configEntry, (Map) value);
+                } else {
+                    config.put(key, value);
+                }
+            }
+        }
+
+        return config;
+    }
+
+    private void writeConfig(String prefix, ConfigObject map, BufferedWriter out, int tab, boolean apply) throws IOException {
+        String space = apply ? StringGroovyMethods.multiply(TAB_CHARACTER, tab) : "";
+
+        for (Object o1 : map.keySet()) {
+            String key = (String) o1;
+            Object v = map.get(key);
+
+            if (v instanceof ConfigObject) {
+                ConfigObject value = (ConfigObject) v;
+
+                if (!value.isEmpty()) {
+
+                    Object dotsInKeys = null;
+                    for (Object o : value.entrySet()) {
+                        Entry e = (Entry) o;
+                        String k = (String) e.getKey();
+                        if (k.indexOf('.') > -1) {
+                            dotsInKeys = e;
+                            break;
+                        }
+                    }
+
+                    int configSize = value.size();
+                    Object firstKey = value.keySet().iterator().next();
+                    Object firstValue = value.values().iterator().next();
+
+                    int firstSize;
+                    if (firstValue instanceof ConfigObject) {
+                        firstSize = ((ConfigObject) firstValue).size();
+                    } else {
+                        firstSize = 1;
+                    }
+
+                    if (configSize == 1 || DefaultGroovyMethods.asBoolean(dotsInKeys)) {
+                        if (firstSize == 1 && firstValue instanceof ConfigObject) {
+                            key = KEYWORDS.contains(key) ? InvokerHelper.inspect(key) : key;
+                            String writePrefix = prefix + key + "." + firstKey + ".";
+                            writeConfig(writePrefix, (ConfigObject) firstValue, out, tab, true);
+                        } else if (!DefaultGroovyMethods.asBoolean(dotsInKeys) && firstValue instanceof ConfigObject) {
+                            writeNode(key, space, tab, value, out);
+                        } else {
+                            for (Object j : value.keySet()) {
+                                Object v2 = value.get(j);
+                                Object k2 = ((String) j).indexOf('.') > -1 ? InvokerHelper.inspect(j) : j;
+                                if (v2 instanceof ConfigObject) {
+                                    key = KEYWORDS.contains(key) ? InvokerHelper.inspect(key) : key;
+                                    writeConfig(prefix + key, (ConfigObject) v2, out, tab, false);
+                                } else {
+                                    writeValue(key + "." + k2, space, prefix, v2, out);
+                                }
+                            }
+                        }
+                    } else {
+                        writeNode(key, space, tab, value, out);
+                    }
+                }
+            } else {
+                writeValue(key, space, prefix, v, out);
+            }
+        }
+    }
+
+    private static void writeValue(String key, String space, String prefix, Object value, BufferedWriter out) throws IOException {
+//        key = key.indexOf('.') > -1 ? InvokerHelper.inspect(key) : key;
+        boolean isKeyword = KEYWORDS.contains(key);
+        key = isKeyword ? InvokerHelper.inspect(key) : key;
+
+        if (!StringGroovyMethods.asBoolean(prefix) && isKeyword) prefix = "this.";
+        out.append(space).append(prefix).append(key).append('=').append(InvokerHelper.inspect(value));
+        out.newLine();
+    }
+
+    private void writeNode(String key, String space, int tab, ConfigObject value, BufferedWriter out) throws IOException {
+        key = KEYWORDS.contains(key) ? InvokerHelper.inspect(key) : key;
+        out.append(space).append(key).append(" {");
+        out.newLine();
+        writeConfig("", value, out, tab + 1, true);
+        out.append(space).append('}');
+        out.newLine();
+    }
+
+    private static Properties convertValuesToString(Map props) {
+        Properties newProps = new Properties();
+
+        for (Object o : props.entrySet()) {
+            Map.Entry next = (Map.Entry) o;
+            Object key = next.getKey();
+            Object value = next.getValue();
+
+            newProps.put(key, value != null ? value.toString() : null);
+        }
+
+        return newProps;
+    }
+
+    private void populate(String suffix, Map config, Map map) {
+        for (Object o : map.entrySet()) {
+            Map.Entry next = (Map.Entry) o;
+            Object key = next.getKey();
+            Object value = next.getValue();
+
+            if (value instanceof Map) {
+                populate(suffix + key + ".", config, (Map) value);
+            } else {
+                try {
+                    config.put(suffix + key, value);
+                } catch (NullPointerException e) {
+                    // it is idiotic story but if config map doesn't allow null values (like Hashtable)
+                    // we can't do too much
+                }
+            }
+        }
+    }
+
+    public int size() {
+        return delegateMap.size();
+    }
+
+    public boolean isEmpty() {
+        return delegateMap.isEmpty();
+    }
+
+    public boolean containsKey(Object key) {
+        return delegateMap.containsKey(key);
+    }
+
+    public boolean containsValue(Object value) {
+        return delegateMap.containsValue(value);
+    }
+
+    public Object get(Object key) {
+        return delegateMap.get(key);
+    }
+
+    public Object put(Object key, Object value) {
+        return delegateMap.put(key, value);
+    }
+
+    public Object remove(Object key) {
+        return delegateMap.remove(key);
+    }
+
+    public void putAll(Map m) {
+        delegateMap.putAll(m);
+    }
+
+    public void clear() {
+        delegateMap.clear();
+    }
+
+    public Set keySet() {
+        return delegateMap.keySet();
+    }
+
+    public Collection values() {
+        return delegateMap.values();
+    }
+
+    public Set entrySet() {
+        return delegateMap.entrySet();
+    }
+
+    /**
+     * Returns a shallow copy of this ConfigObject, keys and configuration entries are not cloned.
+     * @return a shallow copy of this ConfigObject
+     */
+    public ConfigObject clone() {
+        try {
+            ConfigObject clone = (ConfigObject) super.clone();
+            clone.configFile = configFile;
+            clone.delegateMap = (LinkedHashMap) delegateMap.clone();
+            return clone;
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Checks if a config option is set. Example usage:
+     * <pre class="groovyTestCase">
+     * def config = new ConfigSlurper().parse("foo { password='' }")
+     * assert config.foo.isSet('password')
+     * assert config.foo.isSet('username') == false
+     * </pre>
+     *
+     * The check works <b>only</v> for options <b>one</b> block below the current block.
+     * E.g. <code>config.isSet('foo.password')</code> will always return false.
+     *
+     * @param option The name of the option
+     * @return <code>true</code> if the option is set <code>false</code> otherwise
+     * @since 2.3.0
+     */
+    public Boolean isSet(String option) {
+        if (delegateMap.containsKey(option)) {
+            Object entry = delegateMap.get(option);
+            if (!(entry instanceof ConfigObject) || !((ConfigObject) entry).isEmpty()) {
+                return Boolean.TRUE;
+            }
+        }
+        return Boolean.FALSE;
+    }
+
+    public String prettyPrint() {
+        StringWriter sw = new StringWriter();
+        try {
+            writeTo(sw);
+        } catch (IOException e) {
+            throw new GroovyRuntimeException(e);
+        }
+
+        return sw.toString();
+    }
+
+    @Override
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        try {
+            InvokerHelper.write(sw, this);
+        } catch (IOException e) {
+            throw new GroovyRuntimeException(e);
+        }
+
+        return sw.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/util/ConfigSlurper.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/util/ConfigSlurper.groovy b/src/main/groovy/groovy/util/ConfigSlurper.groovy
new file mode 100644
index 0000000..20d0723
--- /dev/null
+++ b/src/main/groovy/groovy/util/ConfigSlurper.groovy
@@ -0,0 +1,309 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 org.codehaus.groovy.runtime.InvokerHelper
+
+/**
+ * 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"
+ * </code></pre>
+ *
+ * Settings can either be bound into nested maps or onto a specified JavaBean instance.
+ * In the latter case, an error will be thrown if a property cannot be bound.
+ *
+ * @author Graeme Rocher
+ * @author Andres Almiray
+ * @since 1.5
+ */
+class ConfigSlurper {
+    private static final ENVIRONMENTS_METHOD = 'environments'
+    GroovyClassLoader classLoader = new GroovyClassLoader()
+    private Map bindingVars = [:]
+
+    private final Map<String, String> conditionValues = [:]
+    private final Stack<Map<String, ConfigObject>> conditionalBlocks = new Stack<Map<String,ConfigObject>>()
+
+    ConfigSlurper() {
+        this('')
+    }
+
+    /**
+     * Constructs a new ConfigSlurper instance using the given environment
+     *
+     * @param env The Environment to use
+     */
+    ConfigSlurper(String env) {
+        conditionValues[ENVIRONMENTS_METHOD] = env
+    }
+
+    void registerConditionalBlock(String blockName, String blockValue) {
+        if (blockName) {
+            if (!blockValue) {
+                conditionValues.remove(blockName)
+            } else {
+                conditionValues[blockName] = blockValue
+            }
+        }
+    }
+
+    Map<String, String> getConditionalBlockValues() {
+        Collections.unmodifiableMap(conditionValues)
+    }
+
+    String getEnvironment() {
+        return conditionValues[ENVIRONMENTS_METHOD]
+    }
+
+    void setEnvironment(String environment) {
+        conditionValues[ENVIRONMENTS_METHOD] = environment
+    }
+
+    /**
+     * 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 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)
+     * (This method creates a new class to parse the script each time it is called.)
+     *
+     * @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) {
+        Stack<String> currentConditionalBlock = new Stack<String>()
+        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 {
+                try {
+                    result = InvokerHelper.getProperty(this, name)
+                } catch (GroovyRuntimeException e) {
+                    result = new ConfigObject()
+                    assignName.call(name, result)
+                }
+            }
+            result
+        }
+
+        ConfigObject overrides = new ConfigObject()
+        mc.invokeMethod = { String name, args ->
+            def result
+            if (args.length == 1 && args[0] instanceof Closure) {
+                if (name in conditionValues.keySet()) {
+                    try {
+                        currentConditionalBlock.push(name)
+                        conditionalBlocks.push([:])
+                        args[0].call()
+                    } finally {
+                        currentConditionalBlock.pop()
+                        for (entry in conditionalBlocks.pop().entrySet()) {
+                            def c = stack.last.config
+                            (c != config? c : overrides).merge(entry.value)
+                        }
+                    }
+                } else if (currentConditionalBlock.size() > 0) {
+                    String conditionalBlockKey = currentConditionalBlock.peek()
+                    if (name == conditionValues[conditionalBlockKey]) {
+                        def co = new ConfigObject()
+                        conditionalBlocks.peek()[conditionalBlockKey] = co
+
+                        pushStack.call(co)
+                        try {
+                            currentConditionalBlock.pop()
+                            args[0].call()
+                        } finally {
+                            currentConditionalBlock.push(conditionalBlockKey)
+                        }
+                        stack.removeLast()
+                    }
+                } 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.removeLast()
+                }
+            } 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()
+
+        config.merge(overrides)
+
+        return config
+    }
+}
+
+/**
+ * Since Groovy Script doesn't support overriding setProperty, we use 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)
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/util/DelegatingScript.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/util/DelegatingScript.java b/src/main/groovy/groovy/util/DelegatingScript.java
new file mode 100644
index 0000000..959c8e8
--- /dev/null
+++ b/src/main/groovy/groovy/util/DelegatingScript.java
@@ -0,0 +1,141 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 groovy.lang.Binding;
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaClass;
+import groovy.lang.MissingMethodException;
+import groovy.lang.MissingPropertyException;
+import groovy.lang.Script;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * {@link Script} that performs method invocations and property access like {@link groovy.lang.Closure} does.
+ *
+ * <p>
+ * {@link DelegatingScript} is a convenient basis for loading a custom-defined DSL as a {@link Script}, then execute it.
+ * The following sample code illustrates how to do it:
+ *
+ * <pre>
+ * class MyDSL {
+ *     public void foo(int x, int y, Closure z) { ... }
+ *     public void setBar(String a) { ... }
+ * }
+ *
+ * CompilerConfiguration cc = new CompilerConfiguration();
+ * cc.setScriptBaseClass(DelegatingScript.class.getName());
+ * GroovyShell sh = new GroovyShell(cl,new Binding(),cc);
+ * DelegatingScript script = (DelegatingScript)sh.parse(new File("my.dsl"))
+ * script.setDelegate(new MyDSL());
+ * script.run();
+ * </pre>
+ *
+ * <p>
+ * <tt>my.dsl</tt> can look like this:
+ *
+ * <pre>
+ * foo(1,2) {
+ *     ....
+ * }
+ * bar = ...;
+ * </pre>
+ *
+ * <p>
+ * {@link DelegatingScript} does this by delegating property access and method invocation to the <tt>delegate</tt> object.
+ *
+ * <p>
+ * More formally speaking, given the following script:
+ *
+ * <pre>
+ * a = 1;
+ * b(2);
+ * </pre>
+ *
+ * <p>
+ * Using {@link DelegatingScript} as the base class, the code will run as:
+ *
+ * <pre>
+ * delegate.a = 1;
+ * delegate.b(2);
+ * </pre>
+ *
+ * ... whereas in plain {@link Script}, this will be run as:
+ *
+ * <pre>
+ * binding.setProperty("a",1);
+ * ((Closure)binding.getProperty("b")).call(2);
+ * </pre>
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public abstract class DelegatingScript extends Script {
+    private Object delegate;
+    private MetaClass metaClass;
+
+    protected DelegatingScript() {
+        super();
+    }
+
+    protected DelegatingScript(Binding binding) {
+        super(binding);
+    }
+
+    /**
+     * Sets the delegation target.
+     */
+    public void setDelegate(Object delegate) {
+        this.delegate = delegate;
+        this.metaClass = InvokerHelper.getMetaClass(delegate.getClass());
+    }
+
+    @Override
+    public Object invokeMethod(String name, Object args) {
+        try {
+            if (delegate instanceof GroovyObject) {
+                return ((GroovyObject) delegate).invokeMethod(name, args);
+            }
+            return metaClass.invokeMethod(delegate, name, args);
+        } catch (MissingMethodException mme) {
+            return super.invokeMethod(name, args);
+        }
+    }
+
+    @Override
+    public Object getProperty(String property) {
+        try {
+            return metaClass.getProperty(delegate,property);
+        } catch (MissingPropertyException e) {
+            return super.getProperty(property);
+        }
+    }
+
+    @Override
+    public void setProperty(String property, Object newValue) {
+        try {
+            metaClass.setProperty(delegate,property,newValue);
+        } catch (MissingPropertyException e) {
+            super.setProperty(property,newValue);
+        }
+    }
+
+    public Object getDelegate() {
+        return delegate;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/util/Eval.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/util/Eval.java b/src/main/groovy/groovy/util/Eval.java
new file mode 100644
index 0000000..87f9295
--- /dev/null
+++ b/src/main/groovy/groovy/util/Eval.java
@@ -0,0 +1,124 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 groovy.lang.Binding;
+import groovy.lang.GroovyShell;
+import org.codehaus.groovy.control.CompilationFailedException;
+
+/**
+ * Allow easy integration from Groovy into Java through convenience methods.
+ * <p>
+ * This class is a simple helper on top of GroovyShell. You can use it to evaluate small
+ * Groovy scripts that don't need large Binding objects. For example, this script 
+ * executes with no errors: 
+ * <pre class="groovyTestCase">
+ * assert Eval.me(' 2 * 4 + 2') == 10
+ * assert Eval.x(2, ' x * 4 + 2') == 10
+ * </pre>
+ * 
+ * @see GroovyShell
+ * @author Dierk Koenig
+ */
+
+public class Eval {
+    /**
+     * Evaluates the specified String expression and returns the result. For example: 
+     * <pre class="groovyTestCase">
+     * assert Eval.me(' 2 * 4 + 2') == 10
+     * </pre>
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is not valid Groovy
+     */
+    public static Object me(final String expression) throws CompilationFailedException {
+        return me(null, null, expression);
+    }
+
+    /**
+     * Evaluates the specified String expression and makes the parameter available inside
+     * the script, returning the result. For example, this code binds the 'x' variable: 
+     * <pre class="groovyTestCase">
+     * assert Eval.me('x', 2, ' x * 4 + 2') == 10
+     * </pre>
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is not valid Groovy
+     */
+    public static Object me(final String symbol, final Object object, final String expression) throws CompilationFailedException {
+        Binding b = new Binding();
+        b.setVariable(symbol, object);
+        GroovyShell sh = new GroovyShell(b);
+        return sh.evaluate(expression);
+    }
+
+    /**
+     * Evaluates the specified String expression and makes the parameter available inside
+     * the script bound to a variable named 'x', returning the result. For example, this 
+     * code executes without failure: 
+     * <pre class="groovyTestCase">
+     * assert Eval.x(2, ' x * 4 + 2') == 10
+     * </pre>
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is not valid Groovy
+     */
+    public static Object x(final Object x, final String expression) throws CompilationFailedException {
+        return me("x", x, expression);
+    }
+
+    /**
+     * Evaluates the specified String expression and makes the first two parameters available inside
+     * the script bound to variables named 'x' and 'y' respectively, returning the result. For example, 
+     * this code executes without failure: 
+     * <pre class="groovyTestCase">
+     * assert Eval.xy(2, 4, ' x * y + 2') == 10
+     * </pre>
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is not valid Groovy
+     */
+    public static Object xy(final Object x, final Object y, final String expression) throws CompilationFailedException {
+        Binding b = new Binding();
+        b.setVariable("x", x);
+        b.setVariable("y", y);
+        GroovyShell sh = new GroovyShell(b);
+        return sh.evaluate(expression);
+    }
+
+    /**
+     * Evaluates the specified String expression and makes the first three parameters available inside
+     * the script bound to variables named 'x', 'y', and 'z' respectively, returning the result. For 
+     * example, this code executes without failure: 
+     * <pre class="groovyTestCase">
+     * assert Eval.xyz(2, 4, 2, ' x * y + z') == 10
+     * </pre>
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is not valid Groovy
+     */
+    public static Object xyz(final Object x, final Object y, final Object z, final String expression) throws CompilationFailedException {
+        Binding b = new Binding();
+        b.setVariable("x", x);
+        b.setVariable("y", y);
+        b.setVariable("z", z);
+        GroovyShell sh = new GroovyShell(b);
+        return sh.evaluate(expression);
+    }
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/util/Expando.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/util/Expando.java b/src/main/groovy/groovy/util/Expando.java
new file mode 100644
index 0000000..f009551
--- /dev/null
+++ b/src/main/groovy/groovy/util/Expando.java
@@ -0,0 +1,175 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.MetaExpandoProperty;
+import groovy.lang.MissingPropertyException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+
+/**
+ * Represents a dynamically expandable bean.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Hein Meling
+ * @author Pilho Kim
+ */
+public class Expando extends GroovyObjectSupport {
+
+    private Map expandoProperties;
+
+    public Expando() {
+    }
+
+    public Expando(Map expandoProperties) {
+        this.expandoProperties = expandoProperties;
+    }
+
+    /**
+     * @return the dynamically expanded properties
+     */
+    public Map getProperties() {
+        if (expandoProperties == null) {
+            expandoProperties = createMap();
+        }
+        return expandoProperties;
+    }
+
+    public List getMetaPropertyValues() {
+        // run through all our current properties and create MetaProperty objects
+        List ret = new ArrayList();
+        for (Object o : getProperties().entrySet()) {
+            Entry entry = (Entry) o;
+            ret.add(new MetaExpandoProperty(entry));
+        }
+
+        return ret;
+    }
+
+    public Object getProperty(String property) {
+        // always use the expando properties first
+        Object result = getProperties().get(property);
+        if (result != null) return result;
+        try {
+            return super.getProperty(property);
+        }
+        catch (MissingPropertyException e) {
+            // IGNORE
+        }
+        return null;
+    }
+
+    public void setProperty(String property, Object newValue) {
+        // always use the expando properties
+        getProperties().put(property, newValue);
+    }
+
+    public Object invokeMethod(String name, Object args) {
+        try {
+            return super.invokeMethod(name, args);
+        }
+        catch (GroovyRuntimeException e) {
+            // br should get a "native" property match first. getProperty includes such fall-back logic
+            Object value = this.getProperty(name);
+            if (value instanceof Closure) {
+                Closure closure = (Closure) value;
+                closure = (Closure) closure.clone();
+                closure.setDelegate(this);
+                return closure.call((Object[]) args);
+            } else {
+                throw e;
+            }
+        }
+
+    }
+
+    /**
+     * This allows toString to be overridden by a closure <i>field</i> method attached
+     * to the expando object.
+     *
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        Object method = getProperties().get("toString");
+        if (method != null && method instanceof Closure) {
+            // invoke overridden toString closure method
+            Closure closure = (Closure) method;
+            closure.setDelegate(this);
+            return closure.call().toString();
+        } else {
+            return expandoProperties.toString();
+        }
+    }
+
+    /**
+     * This allows equals to be overridden by a closure <i>field</i> method attached
+     * to the expando object.
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        Object method = getProperties().get("equals");
+        if (method != null && method instanceof Closure) {
+            // invoke overridden equals closure method
+            Closure closure = (Closure) method;
+            closure.setDelegate(this);
+            Boolean ret = (Boolean) closure.call(obj);
+            return ret.booleanValue();
+        } else {
+            return super.equals(obj);
+        }
+    }
+
+    /**
+     * This allows hashCode to be overridden by a closure <i>field</i> method attached
+     * to the expando object.
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        Object method = getProperties().get("hashCode");
+        if (method != null && method instanceof Closure) {
+            // invoke overridden hashCode closure method
+            Closure closure = (Closure) method;
+            closure.setDelegate(this);
+            Integer ret = (Integer) closure.call();
+            return ret.intValue();
+        } else {
+            return super.hashCode();
+        }
+    }
+
+    /**
+     * Factory method to create a new Map used to store the expando properties map
+     *
+     * @return a newly created Map implementation
+     */
+    protected Map createMap() {
+        return new HashMap();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/util/Factory.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/util/Factory.java b/src/main/groovy/groovy/util/Factory.java
new file mode 100644
index 0000000..c8e8fc7
--- /dev/null
+++ b/src/main/groovy/groovy/util/Factory.java
@@ -0,0 +1,90 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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 groovy.lang.Closure;
+
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:aalmiray@users.sourceforge.com">Andres Almiray</a>
+ * @author Danno Ferrin
+ */
+public interface Factory {
+    /**
+     *
+     * @return true if no child closures should be processed
+     */
+    boolean isLeaf();
+
+    /**
+     * Does this factory "Own" it's child closure.
+     *
+     * @return true  if the factory should have onContentClosure() called,
+     *         false if the builder should handle it
+     */
+    boolean isHandlesNodeChildren();
+
+    /**
+     * Called when a factory is registered to a builder
+     * @param builder the build the factory has been registered to
+     * @param registeredName the name the factory has been registered under
+     */
+    void onFactoryRegistration(FactoryBuilderSupport builder, String registeredName, String registeredGroupName);
+
+    /**
+     * @param builder the FactoryBuilder
+     * @param name the name of the node being built
+     * @param value the 'value' argument in the build node
+     * @param attributes the attributes of the build arg
+     * @return the object created for the builder
+     * @throws InstantiationException if attempting to instantiate an interface or abstract class
+     * @throws IllegalAccessException if the instance can't be created due to a security violation
+     */
+    Object newInstance( FactoryBuilderSupport builder, Object name, Object value, Map attributes )
+            throws InstantiationException, IllegalAccessException;
+
+    /**
+     * @param builder the FactoryBuilder
+     * @param node the node (returned from newINstance) to consider the attributes for
+     * @param attributes the attributes, a mutable set
+     * @return true if the factory builder should use standard bean property matching for the remaining attributes
+     */
+    boolean onHandleNodeAttributes( FactoryBuilderSupport builder, Object node, Map attributes );
+
+    /**
+     * Only called if it isLeaf is false and isHandlesNodeChildren is true
+     * @param builder the FactoryBuilder
+     * @param node the node (returned from newINstance) to consider the attributes for
+     * @param childContent the child content closure of the builder
+     * @return true if the factory builder should apply default node processing to the content child
+     */
+    boolean onNodeChildren( FactoryBuilderSupport builder, Object node, Closure childContent);
+
+    /**
+     * @param builder the FactoryBuilder
+     * @param parent the parent node (null if 'root')
+     * @param node the node just completed
+     */
+    void onNodeCompleted( FactoryBuilderSupport builder, Object parent, Object node );
+
+    void setParent( FactoryBuilderSupport builder, Object parent, Object child );
+
+    void setChild( FactoryBuilderSupport builder, Object parent, Object child );
+}