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 );
+}