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 00:56:02 UTC
[05/49] groovy git commit: Move source files to proper packages
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/util/BuilderSupport.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/util/BuilderSupport.java b/src/main/groovy/util/BuilderSupport.java
deleted file mode 100644
index f634f1f..0000000
--- a/src/main/groovy/util/BuilderSupport.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * 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.MissingMethodException;
-import org.codehaus.groovy.runtime.InvokerHelper;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * An abstract base class for creating arbitrary nested trees of objects
- * or events
- *
- * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
- */
-public abstract class BuilderSupport extends GroovyObjectSupport {
-
- private Object current;
- private Closure nameMappingClosure;
- private final BuilderSupport proxyBuilder;
-
- public BuilderSupport() {
- this.proxyBuilder = this;
- }
-
- public BuilderSupport(BuilderSupport proxyBuilder) {
- this(null, proxyBuilder);
- }
-
- public BuilderSupport(Closure nameMappingClosure, BuilderSupport proxyBuilder) {
- this.nameMappingClosure = nameMappingClosure;
- this.proxyBuilder = proxyBuilder;
- }
-
- /**
- * Convenience method when no arguments are required
- *
- * @param methodName the name of the method to invoke
- * @return the result of the call
- */
- public Object invokeMethod(String methodName) {
- return invokeMethod(methodName, null);
- }
-
- public Object invokeMethod(String methodName, Object args) {
- Object name = getName(methodName);
- return doInvokeMethod(methodName, name, args);
- }
-
- protected Object doInvokeMethod(String methodName, Object name, Object args) {
- Object node = null;
- Closure closure = null;
- List list = InvokerHelper.asList(args);
-
- //System.out.println("Called invokeMethod with name: " + name + " arguments: " + list);
-
- switch (list.size()) {
- case 0:
- node = proxyBuilder.createNode(name);
- break;
- case 1: {
- Object object = list.get(0);
- if (object instanceof Map) {
- node = proxyBuilder.createNode(name, (Map) object);
- } else if (object instanceof Closure) {
- closure = (Closure) object;
- node = proxyBuilder.createNode(name);
- } else {
- node = proxyBuilder.createNode(name, object);
- }
- }
- break;
- case 2: {
- Object object1 = list.get(0);
- Object object2 = list.get(1);
- if (object1 instanceof Map) {
- if (object2 instanceof Closure) {
- closure = (Closure) object2;
- node = proxyBuilder.createNode(name, (Map) object1);
- } else {
- node = proxyBuilder.createNode(name, (Map) object1, object2);
- }
- } else {
- if (object2 instanceof Closure) {
- closure = (Closure) object2;
- node = proxyBuilder.createNode(name, object1);
- } else if (object2 instanceof Map) {
- node = proxyBuilder.createNode(name, (Map) object2, object1);
- } else {
- throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
- }
- }
- }
- break;
- case 3: {
- Object arg0 = list.get(0);
- Object arg1 = list.get(1);
- Object arg2 = list.get(2);
- if (arg0 instanceof Map && arg2 instanceof Closure) {
- closure = (Closure) arg2;
- node = proxyBuilder.createNode(name, (Map) arg0, arg1);
- } else if (arg1 instanceof Map && arg2 instanceof Closure) {
- closure = (Closure) arg2;
- node = proxyBuilder.createNode(name, (Map) arg1, arg0);
- } else {
- throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
- }
- }
- break;
- default: {
- throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
- }
-
- }
-
- if (current != null) {
- proxyBuilder.setParent(current, node);
- }
-
- if (closure != null) {
- // push new node on stack
- Object oldCurrent = getCurrent();
- setCurrent(node);
- // let's register the builder as the delegate
- setClosureDelegate(closure, node);
- try {
- closure.call();
- } catch (Exception e) {
- throw new GroovyRuntimeException(e);
- }
- setCurrent(oldCurrent);
- }
-
- proxyBuilder.nodeCompleted(current, node);
- return proxyBuilder.postNodeCompletion(current, node);
- }
-
- /**
- * A strategy method to allow derived builders to use
- * builder-trees and switch in different kinds of builders.
- * This method should call the setDelegate() method on the closure
- * which by default passes in this but if node is-a builder
- * we could pass that in instead (or do something wacky too)
- *
- * @param closure the closure on which to call setDelegate()
- * @param node the node value that we've just created, which could be
- * a builder
- */
- protected void setClosureDelegate(Closure closure, Object node) {
- closure.setDelegate(this);
- }
-
- protected abstract void setParent(Object parent, Object child);
-
- protected abstract Object createNode(Object name);
-
- protected abstract Object createNode(Object name, Object value);
-
- protected abstract Object createNode(Object name, Map attributes);
-
- protected abstract Object createNode(Object name, Map attributes, Object value);
-
- /**
- * A hook to allow names to be converted into some other object
- * such as a QName in XML or ObjectName in JMX.
- *
- * @param methodName the name of the desired method
- * @return the object representing the name
- */
- protected Object getName(String methodName) {
- if (nameMappingClosure != null) {
- return nameMappingClosure.call(methodName);
- }
- return methodName;
- }
-
-
- /**
- * A hook to allow nodes to be processed once they have had all of their
- * children applied.
- *
- * @param node the current node being processed
- * @param parent the parent of the node being processed
- */
- protected void nodeCompleted(Object parent, Object node) {
- }
-
- /**
- * A hook to allow nodes to be processed once they have had all of their
- * children applied and allows the actual node object that represents
- * the Markup element to be changed
- *
- * @param node the current node being processed
- * @param parent the parent of the node being processed
- * @return the node, possibly new, that represents the markup element
- */
- protected Object postNodeCompletion(Object parent, Object node) {
- return node;
- }
-
- protected Object getCurrent() {
- return current;
- }
-
- protected void setCurrent(Object current) {
- this.current = current;
- }
-}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/util/CharsetToolkit.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/util/CharsetToolkit.java b/src/main/groovy/util/CharsetToolkit.java
deleted file mode 100644
index e127459..0000000
--- a/src/main/groovy/util/CharsetToolkit.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * 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 java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.LineNumberReader;
-import java.nio.charset.Charset;
-import java.util.Collection;
-
-/**
- * Utility class to guess the encoding of a given text file.
- * <p>
- * Unicode files encoded in UTF-16 (low or big endian) or UTF-8 files
- * with a Byte Order Marker are correctly discovered. For UTF-8 files with no BOM, if the buffer
- * is wide enough, the charset should also be discovered.
- * <p>
- * A byte buffer of 4KB is used to be able to guess the encoding.
- * <p>
- * Usage:
- * <pre>
- * CharsetToolkit toolkit = new CharsetToolkit(file);
- *
- * // guess the encoding
- * Charset guessedCharset = toolkit.getCharset();
- *
- * // create a reader with the correct charset
- * BufferedReader reader = toolkit.getReader();
- *
- * // read the file content
- * String line;
- * while ((line = br.readLine())!= null)
- * {
- * System.out.println(line);
- * }
- * </pre>
- *
- * @author Guillaume Laforge
- */
-public class CharsetToolkit {
- private final byte[] buffer;
- private Charset defaultCharset;
- private Charset charset;
- private boolean enforce8Bit = true;
- private final File file;
- private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
-
- /**
- * Constructor of the <code>CharsetToolkit</code> utility class.
- *
- * @param file of which we want to know the encoding.
- */
- public CharsetToolkit(File file) throws IOException {
- this.file = file;
- this.defaultCharset = getDefaultSystemCharset();
- this.charset = null;
- InputStream input = new FileInputStream(file);
- try {
- byte[] bytes = new byte[4096];
- int bytesRead = input.read(bytes);
- if (bytesRead == -1) {
- this.buffer = EMPTY_BYTE_ARRAY;
- }
- else if (bytesRead < 4096) {
- byte[] bytesToGuess = new byte[bytesRead];
- System.arraycopy(bytes, 0, bytesToGuess, 0, bytesRead);
- this.buffer = bytesToGuess;
- }
- else {
- this.buffer = bytes;
- }
- } finally {
- try {input.close();} catch (IOException e){
- // IGNORE
- }
- }
- }
-
- /**
- * Defines the default <code>Charset</code> used in case the buffer represents
- * an 8-bit <code>Charset</code>.
- *
- * @param defaultCharset the default <code>Charset</code> to be returned
- * if an 8-bit <code>Charset</code> is encountered.
- */
- public void setDefaultCharset(Charset defaultCharset) {
- if (defaultCharset != null)
- this.defaultCharset = defaultCharset;
- else
- this.defaultCharset = getDefaultSystemCharset();
- }
-
- public Charset getCharset() {
- if (this.charset == null)
- this.charset = guessEncoding();
- return charset;
- }
-
- /**
- * If US-ASCII is recognized, enforce to return the default encoding, rather than US-ASCII.
- * It might be a file without any special character in the range 128-255, but that may be or become
- * a file encoded with the default <code>charset</code> rather than US-ASCII.
- *
- * @param enforce a boolean specifying the use or not of US-ASCII.
- */
- public void setEnforce8Bit(boolean enforce) {
- this.enforce8Bit = enforce;
- }
-
- /**
- * Gets the enforce8Bit flag, in case we do not want to ever get a US-ASCII encoding.
- *
- * @return a boolean representing the flag of use of US-ASCII.
- */
- public boolean getEnforce8Bit() {
- return this.enforce8Bit;
- }
-
- /**
- * Retrieves the default Charset
- */
- public Charset getDefaultCharset() {
- return defaultCharset;
- }
-
- /**
- * Guess the encoding of the provided buffer.
- * If Byte Order Markers are encountered at the beginning of the buffer, we immediately
- * return the charset implied by this BOM. Otherwise, the file would not be a human
- * readable text file.
- * <p>
- * If there is no BOM, this method tries to discern whether the file is UTF-8 or not.
- * If it is not UTF-8, we assume the encoding is the default system encoding
- * (of course, it might be any 8-bit charset, but usually, an 8-bit charset is the default one).
- * <p>
- * It is possible to discern UTF-8 thanks to the pattern of characters with a multi-byte sequence.
- * <pre>
- * UCS-4 range (hex.) UTF-8 octet sequence (binary)
- * 0000 0000-0000 007F 0xxxxxxx
- * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
- * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
- * 0001 0000-001F FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
- * 0020 0000-03FF FFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- * 0400 0000-7FFF FFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
- * </pre>
- * With UTF-8, 0xFE and 0xFF never appear.
- *
- * @return the Charset recognized.
- */
- private Charset guessEncoding() {
- // if the file has a Byte Order Marker, we can assume the file is in UTF-xx
- // otherwise, the file would not be human readable
- if (hasUTF8Bom())
- return Charset.forName("UTF-8");
- if (hasUTF16LEBom())
- return Charset.forName("UTF-16LE");
- if (hasUTF16BEBom())
- return Charset.forName("UTF-16BE");
-
- // if a byte has its most significant bit set, the file is in UTF-8 or in the default encoding
- // otherwise, the file is in US-ASCII
- boolean highOrderBit = false;
-
- // if the file is in UTF-8, high order bytes must have a certain value, in order to be valid
- // if it's not the case, we can assume the encoding is the default encoding of the system
- boolean validU8Char = true;
-
- // TODO the buffer is not read up to the end, but up to length - 6
-
- int length = buffer.length;
- int i = 0;
- while (i < length - 6) {
- byte b0 = buffer[i];
- byte b1 = buffer[i + 1];
- byte b2 = buffer[i + 2];
- byte b3 = buffer[i + 3];
- byte b4 = buffer[i + 4];
- byte b5 = buffer[i + 5];
- if (b0 < 0) {
- // a high order bit was encountered, thus the encoding is not US-ASCII
- // it may be either an 8-bit encoding or UTF-8
- highOrderBit = true;
- // a two-bytes sequence was encountered
- if (isTwoBytesSequence(b0)) {
- // there must be one continuation byte of the form 10xxxxxx,
- // otherwise the following character is is not a valid UTF-8 construct
- if (!isContinuationChar(b1))
- validU8Char = false;
- else
- i++;
- }
- // a three-bytes sequence was encountered
- else if (isThreeBytesSequence(b0)) {
- // there must be two continuation bytes of the form 10xxxxxx,
- // otherwise the following character is is not a valid UTF-8 construct
- if (!(isContinuationChar(b1) && isContinuationChar(b2)))
- validU8Char = false;
- else
- i += 2;
- }
- // a four-bytes sequence was encountered
- else if (isFourBytesSequence(b0)) {
- // there must be three continuation bytes of the form 10xxxxxx,
- // otherwise the following character is is not a valid UTF-8 construct
- if (!(isContinuationChar(b1) && isContinuationChar(b2) && isContinuationChar(b3)))
- validU8Char = false;
- else
- i += 3;
- }
- // a five-bytes sequence was encountered
- else if (isFiveBytesSequence(b0)) {
- // there must be four continuation bytes of the form 10xxxxxx,
- // otherwise the following character is is not a valid UTF-8 construct
- if (!(isContinuationChar(b1)
- && isContinuationChar(b2)
- && isContinuationChar(b3)
- && isContinuationChar(b4)))
- validU8Char = false;
- else
- i += 4;
- }
- // a six-bytes sequence was encountered
- else if (isSixBytesSequence(b0)) {
- // there must be five continuation bytes of the form 10xxxxxx,
- // otherwise the following character is is not a valid UTF-8 construct
- if (!(isContinuationChar(b1)
- && isContinuationChar(b2)
- && isContinuationChar(b3)
- && isContinuationChar(b4)
- && isContinuationChar(b5)))
- validU8Char = false;
- else
- i += 5;
- }
- else
- validU8Char = false;
- }
- if (!validU8Char)
- break;
- i++;
- }
- // if no byte with an high order bit set, the encoding is US-ASCII
- // (it might have been UTF-7, but this encoding is usually internally used only by mail systems)
- if (!highOrderBit) {
- // returns the default charset rather than US-ASCII if the enforce8Bit flag is set.
- if (this.enforce8Bit)
- return this.defaultCharset;
- else
- return Charset.forName("US-ASCII");
- }
- // if no invalid UTF-8 were encountered, we can assume the encoding is UTF-8,
- // otherwise the file would not be human readable
- if (validU8Char)
- return Charset.forName("UTF-8");
- // finally, if it's not UTF-8 nor US-ASCII, let's assume the encoding is the default encoding
- return this.defaultCharset;
- }
-
- /**
- * If the byte has the form 10xxxxx, then it's a continuation byte of a multiple byte character;
- *
- * @param b a byte.
- * @return true if it's a continuation char.
- */
- private static boolean isContinuationChar(byte b) {
- return -128 <= b && b <= -65;
- }
-
- /**
- * If the byte has the form 110xxxx, then it's the first byte of a two-bytes sequence character.
- *
- * @param b a byte.
- * @return true if it's the first byte of a two-bytes sequence.
- */
- private static boolean isTwoBytesSequence(byte b) {
- return -64 <= b && b <= -33;
- }
-
- /**
- * If the byte has the form 1110xxx, then it's the first byte of a three-bytes sequence character.
- *
- * @param b a byte.
- * @return true if it's the first byte of a three-bytes sequence.
- */
- private static boolean isThreeBytesSequence(byte b) {
- return -32 <= b && b <= -17;
- }
-
- /**
- * If the byte has the form 11110xx, then it's the first byte of a four-bytes sequence character.
- *
- * @param b a byte.
- * @return true if it's the first byte of a four-bytes sequence.
- */
- private static boolean isFourBytesSequence(byte b) {
- return -16 <= b && b <= -9;
- }
-
- /**
- * If the byte has the form 11110xx, then it's the first byte of a five-bytes sequence character.
- *
- * @param b a byte.
- * @return true if it's the first byte of a five-bytes sequence.
- */
- private static boolean isFiveBytesSequence(byte b) {
- return -8 <= b && b <= -5;
- }
-
- /**
- * If the byte has the form 1110xxx, then it's the first byte of a six-bytes sequence character.
- *
- * @param b a byte.
- * @return true if it's the first byte of a six-bytes sequence.
- */
- private static boolean isSixBytesSequence(byte b) {
- return -4 <= b && b <= -3;
- }
-
- /**
- * Retrieve the default charset of the system.
- *
- * @return the default <code>Charset</code>.
- */
- public static Charset getDefaultSystemCharset() {
- return Charset.forName(System.getProperty("file.encoding"));
- }
-
- /**
- * Has a Byte Order Marker for UTF-8 (Used by Microsoft's Notepad and other editors).
- *
- * @return true if the buffer has a BOM for UTF8.
- */
- public boolean hasUTF8Bom() {
- if (buffer.length >= 3)
- return (buffer[0] == -17 && buffer[1] == -69 && buffer[2] == -65);
- else
- return false;
- }
-
- /**
- * Has a Byte Order Marker for UTF-16 Low Endian
- * (ucs-2le, ucs-4le, and ucs-16le).
- *
- * @return true if the buffer has a BOM for UTF-16 Low Endian.
- */
- public boolean hasUTF16LEBom() {
- if (buffer.length >= 2)
- return (buffer[0] == -1 && buffer[1] == -2);
- else
- return false;
- }
-
- /**
- * Has a Byte Order Marker for UTF-16 Big Endian
- * (utf-16 and ucs-2).
- *
- * @return true if the buffer has a BOM for UTF-16 Big Endian.
- */
- public boolean hasUTF16BEBom() {
- if (buffer.length >= 2)
- return (buffer[0] == -2 && buffer[1] == -1);
- else
- return false;
- }
-
- /**
- * Gets a <code>BufferedReader</code> (indeed a <code>LineNumberReader</code>) from the <code>File</code>
- * specified in the constructor of <code>CharsetToolkit</code> using the charset discovered or the default
- * charset if an 8-bit <code>Charset</code> is encountered.
- *
- * @return a <code>BufferedReader</code>
- * @throws FileNotFoundException if the file is not found.
- */
- public BufferedReader getReader() throws FileNotFoundException {
- LineNumberReader reader = new LineNumberReader(new InputStreamReader(new FileInputStream(file), getCharset()));
- if (hasUTF8Bom() || hasUTF16LEBom() || hasUTF16BEBom()) {
- try {
- reader.read();
- }
- catch (IOException e) {
- // should never happen, as a file with no content
- // but with a BOM has at least one char
- }
- }
- return reader;
- }
-
- /**
- * Retrieves all the available <code>Charset</code>s on the platform,
- * among which the default <code>charset</code>.
- *
- * @return an array of <code>Charset</code>s.
- */
- public static Charset[] getAvailableCharsets() {
- Collection collection = Charset.availableCharsets().values();
- return (Charset[]) collection.toArray(new Charset[collection.size()]);
- }
-}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/util/CliBuilder.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/util/CliBuilder.groovy b/src/main/groovy/util/CliBuilder.groovy
deleted file mode 100644
index bc7d44a..0000000
--- a/src/main/groovy/util/CliBuilder.groovy
+++ /dev/null
@@ -1,798 +0,0 @@
-/*
- * 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.cli.CliBuilderException
-import groovy.cli.Option
-import groovy.cli.TypedOption
-import groovy.cli.Unparsed
-import groovy.transform.Undefined
-import org.apache.commons.cli.CommandLine
-import org.apache.commons.cli.CommandLineParser
-import org.apache.commons.cli.DefaultParser
-import org.apache.commons.cli.GnuParser
-import org.apache.commons.cli.HelpFormatter
-import org.apache.commons.cli.Option as CliOption
-import org.apache.commons.cli.Options
-import org.apache.commons.cli.ParseException
-import org.codehaus.groovy.runtime.InvokerHelper
-import org.codehaus.groovy.runtime.MetaClassHelper
-import org.codehaus.groovy.runtime.StringGroovyMethods
-
-import java.lang.annotation.Annotation
-import java.lang.reflect.Array
-import java.lang.reflect.Field
-import java.lang.reflect.Method
-
-/**
- * Provides a builder to assist the processing of command line arguments.
- * Two styles are supported: dynamic api style (declarative method calls provide a mini DSL for describing options)
- * and annotation style (annotations on an interface or class describe options).
- * <p>
- * <b>Dynamic api style</b>
- * <p>
- * Typical usage (emulate partial arg processing of unix command: ls -alt *.groovy):
- * <pre>
- * def cli = new CliBuilder(usage:'ls')
- * cli.a('display all files')
- * cli.l('use a long listing format')
- * cli.t('sort by modification time')
- * def options = cli.parse(args)
- * assert options // would be null (false) on failure
- * assert options.arguments() == ['*.groovy']
- * assert options.a && options.l && options.t
- * </pre>
- * The usage message for this example (obtained using <code>cli.usage()</code>) is shown below:
- * <pre>
- * usage: ls
- * -a display all files
- * -l use a long listing format
- * -t sort by modification time
- * </pre>
- * An underlying parser that supports what is called argument 'bursting' is used
- * by default. Bursting would convert '-alt' into '-a -l -t' provided no long
- * option exists with value 'alt' and provided that none of 'a', 'l' or 't'
- * takes an argument (in fact the last one is allowed to take an argument).
- * The bursting behavior can be turned off by using an
- * alternate underlying parser. The simplest way to achieve this is by using
- * the deprecated GnuParser from Commons CLI with the parser property on the CliBuilder,
- * i.e. include <code>parser: new GnuParser()</code> in the constructor call.
- * <p>
- * Another example (partial emulation of arg processing for 'ant' command line):
- * <pre>
- * def cli = new CliBuilder(usage:'ant [options] [targets]',
- * header:'Options:')
- * cli.help('print this message')
- * cli.logfile(args:1, argName:'file', 'use given file for log')
- * cli.D(args:2, valueSeparator:'=', argName:'property=value',
- * 'use value for given property')
- * def options = cli.parse(args)
- * ...
- * </pre>
- * Usage message would be:
- * <pre>
- * usage: ant [options] [targets]
- * Options:
- * -D <property=value> use value for given property
- * -help print this message
- * -logfile <file> use given file for log
- * </pre>
- * And if called with the following arguments '-logfile foo -Dbar=baz target'
- * then the following assertions would be true:
- * <pre>
- * assert options // would be null (false) on failure
- * assert options.arguments() == ['target']
- * assert options.Ds == ['bar', 'baz']
- * assert options.logfile == 'foo'
- * </pre>
- * Note the use of some special notation. By adding 's' onto an option
- * that may appear multiple times and has an argument or as in this case
- * uses a valueSeparator to separate multiple argument values
- * causes the list of associated argument values to be returned.
- * <p>
- * Another example showing long options (partial emulation of arg processing for 'curl' command line):
- * <pre>
- * def cli = new CliBuilder(usage:'curl [options] <url>')
- * cli._(longOpt:'basic', 'Use HTTP Basic Authentication')
- * cli.d(longOpt:'data', args:1, argName:'data', 'HTTP POST data')
- * cli.G(longOpt:'get', 'Send the -d data with a HTTP GET')
- * cli.q('If used as the first parameter disables .curlrc')
- * cli._(longOpt:'url', args:1, argName:'URL', 'Set URL to work with')
- * </pre>
- * Which has the following usage message:
- * <pre>
- * usage: curl [options] <url>
- * --basic Use HTTP Basic Authentication
- * -d,--data <data> HTTP POST data
- * -G,--get Send the -d data with a HTTP GET
- * -q If used as the first parameter disables .curlrc
- * --url <URL> Set URL to work with
- * </pre>
- * This example shows a common convention. When mixing short and long names, the
- * short names are often one character in size. One character options with
- * arguments don't require a space between the option and the argument, e.g.
- * <code>-Ddebug=true</code>. The example also shows
- * the use of '_' when no short option is applicable.
- * <p>
- * Also note that '_' was used multiple times. This is supported but if
- * any other shortOpt or any longOpt is repeated, then the behavior is undefined.
- * <p>
- * Short option names may not contain a hyphen. If a long option name contains a hyphen, e.g. '--max-wait' then you can either
- * use the long hand method call <code>options.hasOption('max-wait')</code> or surround
- * the option name in quotes, e.g. <code>options.'max-wait'</code>.
- * <p>
- * Although CliBuilder on the whole hides away the underlying library used
- * for processing the arguments, it does provide some hooks which let you
- * make use of the underlying library directly should the need arise. For
- * example, the last two lines of the 'curl' example above could be replaced
- * with the following:
- * <pre>
- * import org.apache.commons.cli.*
- * ... as before ...
- * cli << new Option('q', false, 'If used as the first parameter disables .curlrc')
- * cli << Option.builder().longOpt('url').hasArg().argName('URL').
- * desc('Set URL to work with').build()
- * ...
- * </pre>
- *
- * CliBuilder also supports Argument File processing. If an argument starts with
- * an '@' character followed by a filename, then the contents of the file with name
- * filename are placed into the command line. The feature can be turned off by
- * setting expandArgumentFiles to false. If turned on, you can still pass a real
- * parameter with an initial '@' character by escaping it with an additional '@'
- * symbol, e.g. '@@foo' will become '@foo' and not be subject to expansion. As an
- * example, if the file temp.args contains the content:
- * <pre>
- * -arg1
- * paramA
- * paramB paramC
- * </pre>
- * Then calling the command line with:
- * <pre>
- * someCommand @temp.args -arg2 paramD
- * </pre>
- * Is the same as calling this:
- * <pre>
- * someCommand -arg1 paramA paramB paramC -arg2 paramD
- * </pre>
- * This feature is particularly useful on operating systems which place limitations
- * on the size of the command line (e.g. Windows). The feature is similar to
- * the 'Command Line Argument File' processing supported by javadoc and javac.
- * Consult the corresponding documentation for those tools if you wish to see further examples.
- * <p>
- * <b>Supported Option Properties</b>:
- * <pre>
- * argName: String
- * longOpt: String
- * args: int or String
- * optionalArg: boolean
- * required: boolean
- * type: Class
- * valueSeparator: char
- * convert: Closure
- * defaultValue: String
- * </pre>
- * See {@link org.apache.commons.cli.Option} for the meaning of most of these properties
- * and {@link CliBuilderTest} for further examples.
- * <p>
- * <b>Annotation style with an interface</b>
- * <p>
- * With this style an interface is defined containing an annotated method for each option.
- * It might look like this (following roughly the earlier 'ls' example):
- * <pre>
- * import groovy.cli.Option
- * import groovy.cli.Unparsed
- *
- * interface OptionInterface {
- * @{@link groovy.cli.Option}(shortName='a', description='display all files') boolean all()
- * @{@link groovy.cli.Option}(shortName='l', description='use a long listing format') boolean longFormat()
- * @{@link groovy.cli.Option}(shortName='t', description='sort by modification time') boolean time()
- * @{@link groovy.cli.Unparsed} List remaining()
- * }
- * </pre>
- * Then this description is supplied to CliBuilder during parsing, e.g.:
- * <pre>
- * def args = '-alt *.groovy'.split() // normally from commandline itself
- * def cli = new CliBuilder(usage:'ls')
- * def options = cli.parseFromSpec(OptionInterface, args)
- * assert options.remaining() == ['*.groovy']
- * assert options.all() && options.longFormat() && options.time()
- * </pre>
- * <p>
- * <b>Annotation style with a class</b>
- * <p>
- * With this style a user-supplied instance is used. Annotations on that instance's class
- * members (properties and setter methods) indicate how to set options and provide the option details
- * using annotation attributes.
- * It might look like this (again using the earlier 'ls' example):
- * <pre>
- * import groovy.cli.Option
- * import groovy.cli.Unparsed
- *
- * class OptionClass {
- * @{@link groovy.cli.Option}(shortName='a', description='display all files') boolean all
- * @{@link groovy.cli.Option}(shortName='l', description='use a long listing format') boolean longFormat
- * @{@link groovy.cli.Option}(shortName='t', description='sort by modification time') boolean time
- * @{@link groovy.cli.Unparsed} List remaining
- * }
- * </pre>
- * Then this description is supplied to CliBuilder during parsing, e.g.:
- * <pre>
- * def args = '-alt *.groovy'.split() // normally from commandline itself
- * def cli = new CliBuilder(usage:'ls')
- * def options = new OptionClass()
- * cli.parseFromInstance(options, args)
- * assert options.remaining == ['*.groovy']
- * assert options.all && options.longFormat && options.time
- * </pre>
- */
-class CliBuilder {
-
- /**
- * Usage summary displayed as the first line when <code>cli.usage()</code> is called.
- */
- String usage = 'groovy'
-
- /**
- * Normally set internally but allows you full customisation of the underlying processing engine.
- */
- CommandLineParser parser = null
-
- /**
- * To change from the default PosixParser to the GnuParser, set this to false. Ignored if the parser is explicitly set.
- * @deprecated use the parser option instead with an instance of your preferred parser
- */
- @Deprecated
- Boolean posix = null
-
- /**
- * Whether arguments of the form '{@code @}<i>filename</i>' will be expanded into the arguments contained within the file named <i>filename</i> (default true).
- */
- boolean expandArgumentFiles = true
-
- /**
- * Normally set internally but can be overridden if you want to customise how the usage message is displayed.
- */
- HelpFormatter formatter = new HelpFormatter()
-
- /**
- * Defaults to stdout but you can provide your own PrintWriter if desired.
- */
- PrintWriter writer = new PrintWriter(System.out)
-
- /**
- * Optional additional message for usage; displayed after the usage summary but before the options are displayed.
- */
- String header = ''
-
- /**
- * Optional additional message for usage; displayed after the options are displayed.
- */
- String footer = ''
-
- /**
- * Indicates that option processing should continue for all arguments even
- * if arguments not recognized as options are encountered (default true).
- */
- boolean stopAtNonOption = true
-
- /**
- * Allows customisation of the usage message width.
- */
- int width = HelpFormatter.DEFAULT_WIDTH
-
- /**
- * Not normally accessed directly but full access to underlying options if needed.
- */
- Options options = new Options()
-
- Map<String, TypedOption> savedTypeOptions = new HashMap<String, TypedOption>()
-
- public <T> TypedOption<T> option(Map args, Class<T> type, String description) {
- def name = args.opt ?: '_'
- args.type = type
- args.remove('opt')
- "$name"(args, description)
- }
-
- /**
- * Internal method: Detect option specification method calls.
- */
- def invokeMethod(String name, Object args) {
- if (args instanceof Object[]) {
- if (args.size() == 1 && (args[0] instanceof String || args[0] instanceof GString)) {
- def option = option(name, [:], args[0])
- options.addOption(option)
-
- return create(option, null, null, null)
- }
- if (args.size() == 1 && args[0] instanceof CliOption && name == 'leftShift') {
- CliOption option = args[0]
- options.addOption(option)
- return create(option, null, null, null)
- }
- if (args.size() == 2 && args[0] instanceof Map) {
- def convert = args[0].remove('convert')
- def type = args[0].remove('type')
- def defaultValue = args[0].remove('defaultValue')
- if (type && !(type instanceof Class)) {
- throw new CliBuilderException("'type' must be a Class")
- }
- if ((convert || type) && !args[0].containsKey('args') &&
- type?.simpleName?.toLowerCase() != 'boolean') {
- args[0].args = 1
- }
- def option = option(name, args[0], args[1])
- options.addOption(option)
- return create(option, type, defaultValue, convert)
- }
- }
- return InvokerHelper.getMetaClass(this).invokeMethod(this, name, args)
- }
-
- /**
- * Make options accessible from command line args with parser.
- * Returns null on bad command lines after displaying usage message.
- */
- OptionAccessor parse(args) {
- if (expandArgumentFiles) args = expandArgumentFiles(args)
- if (!parser) {
- parser = posix != null && posix == false ? new GnuParser() : new DefaultParser()
- }
- try {
- def accessor = new OptionAccessor(
- parser.parse(options, args as String[], stopAtNonOption))
- accessor.savedTypeOptions = savedTypeOptions
- return accessor
- } catch (ParseException pe) {
- writer.println("error: " + pe.message)
- usage()
- return null
- }
- }
-
- /**
- * Print the usage message with writer (default: System.out) and formatter (default: HelpFormatter)
- */
- void usage() {
- formatter.printHelp(writer, width, usage, header, options, HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, footer)
- writer.flush()
- }
-
- /**
- * Given an interface containing members with annotations, derive
- * the options specification.
- *
- * @param optionsClass
- * @param args
- * @return an instance containing the processed options
- */
- public <T> T parseFromSpec(Class<T> optionsClass, String[] args) {
- addOptionsFromAnnotations(optionsClass, false)
- def cli = parse(args)
- def cliOptions = [:]
- setOptionsFromAnnotations(cli, optionsClass, cliOptions, false)
- cliOptions as T
- }
-
- /**
- * Given an instance containing members with annotations, derive
- * the options specification.
- *
- * @param optionInstance
- * @param args
- * @return the options instance populated with the processed options
- */
- public <T> T parseFromInstance(T optionInstance, args) {
- addOptionsFromAnnotations(optionInstance.getClass(), true)
- def cli = parse(args)
- setOptionsFromAnnotations(cli, optionInstance.getClass(), optionInstance, true)
- optionInstance
- }
-
- void addOptionsFromAnnotations(Class optionClass, boolean namesAreSetters) {
- optionClass.methods.findAll{ it.getAnnotation(Option) }.each { Method m ->
- Annotation annotation = m.getAnnotation(Option)
- def typedOption = processAddAnnotation(annotation, m, namesAreSetters)
- options.addOption(typedOption.cliOption)
- }
-
- def optionFields = optionClass.declaredFields.findAll { it.getAnnotation(Option) }
- if (optionClass.isInterface() && !optionFields.isEmpty()) {
- throw new CliBuilderException("@Option only allowed on methods in interface " + optionClass.simpleName)
- }
- optionFields.each { Field f ->
- Annotation annotation = f.getAnnotation(Option)
- String setterName = "set" + MetaClassHelper.capitalize(f.getName());
- Method m = optionClass.getMethod(setterName, f.getType())
- def typedOption = processAddAnnotation(annotation, m, true)
- options.addOption(typedOption.cliOption)
- }
- }
-
- private TypedOption processAddAnnotation(Option annotation, Method m, boolean namesAreSetters) {
- String shortName = annotation.shortName()
- String description = annotation.description()
- String defaultValue = annotation.defaultValue()
- char valueSeparator = 0
- if (annotation.valueSeparator()) valueSeparator = annotation.valueSeparator() as char
- boolean optionalArg = annotation.optionalArg()
- Integer numberOfArguments = annotation.numberOfArguments()
- String numberOfArgumentsString = annotation.numberOfArgumentsString()
- Class convert = annotation.convert()
- if (convert == Undefined.CLASS) {
- convert = null
- }
- Map names = calculateNames(annotation.longName(), shortName, m, namesAreSetters)
- def builder = names.short ? CliOption.builder(names.short) : CliOption.builder()
- if (names.long) {
- builder.longOpt(names.long)
- }
- if (numberOfArguments != 1) {
- if (numberOfArgumentsString) {
- throw new CliBuilderException("You can't specify both 'numberOfArguments' and 'numberOfArgumentsString'")
- }
- }
- def details = [:]
- Class type = namesAreSetters ? (m.parameterTypes.size() > 0 ? m.parameterTypes[0] : null) : m.returnType
- if (optionalArg && (!type || !type.isArray())) {
- throw new CliBuilderException("Attempted to set optional argument for non array type")
- }
- def isFlag = type.simpleName.toLowerCase() == 'boolean'
- if (numberOfArgumentsString) {
- details.args = numberOfArgumentsString
- details = adjustDetails(details)
- if (details.optionalArg) optionalArg = true
- } else {
- details.args = isFlag ? 0 : numberOfArguments
- }
- if (details?.args == 0 && !(isFlag || type.name == 'java.lang.Object')) {
- throw new CliBuilderException("Flag '${names.long ?: names.short}' must be Boolean or Object")
- }
- if (description) builder.desc(description)
- if (valueSeparator) builder.valueSeparator(valueSeparator)
- if (type) {
- if (isFlag && details.args == 1) {
- // special flag: treat like normal not boolean expecting explicit 'true' or 'false' param
- isFlag = false
- }
- if (!isFlag) {
- builder.hasArg(true)
- if (details.containsKey('args')) builder.numberOfArgs(details.args)
- }
- if (type.isArray()) {
- builder.optionalArg(optionalArg)
- }
- }
- def typedOption = create(builder.build(), convert ? null : type, defaultValue, convert)
- typedOption
- }
-
- private TypedOption create(CliOption o, Class theType, defaultValue, convert) {
- Map<String, Object> result = new TypedOption<Object>()
- o.with {
- if (opt != null) result.put("opt", opt)
- result.put("longOpt", longOpt)
- result.put("cliOption", o)
- if (defaultValue) {
- result.put("defaultValue", defaultValue)
- }
- if (convert) {
- if (theType) {
- throw new CliBuilderException("You can't specify 'type' when using 'convert'")
- }
- result.put("convert", convert)
- result.put("type", convert instanceof Class ? convert : convert.getClass())
- } else {
- result.put("type", theType)
- }
- }
- savedTypeOptions[o.longOpt ?: o.opt] = result
- result
- }
-
- def setOptionsFromAnnotations(def cli, Class optionClass, Object t, boolean namesAreSetters) {
- optionClass.methods.findAll{ it.getAnnotation(Option) }.each { Method m ->
- Annotation annotation = m.getAnnotation(Option)
- Map names = calculateNames(annotation.longName(), annotation.shortName(), m, namesAreSetters)
- processSetAnnotation(m, t, names.long ?: names.short, cli, namesAreSetters)
- }
- optionClass.declaredFields.findAll { it.getAnnotation(Option) }.each { Field f ->
- Annotation annotation = f.getAnnotation(Option)
- String setterName = "set" + MetaClassHelper.capitalize(f.getName());
- Method m = optionClass.getMethod(setterName, f.getType())
- Map names = calculateNames(annotation.longName(), annotation.shortName(), m, true)
- processSetAnnotation(m, t, names.long ?: names.short, cli, true)
- }
- def remaining = cli.arguments()
- optionClass.methods.findAll{ it.getAnnotation(Unparsed) }.each { Method m ->
- processSetRemaining(m, remaining, t, cli, namesAreSetters)
- }
- optionClass.declaredFields.findAll{ it.getAnnotation(Unparsed) }.each { Field f ->
- String setterName = "set" + MetaClassHelper.capitalize(f.getName());
- Method m = optionClass.getMethod(setterName, f.getType())
- processSetRemaining(m, remaining, t, cli, namesAreSetters)
- }
- }
-
- private void processSetRemaining(Method m, remaining, Object t, cli, boolean namesAreSetters) {
- def resultType = namesAreSetters ? m.parameterTypes[0] : m.returnType
- def isTyped = resultType?.isArray()
- def result
- def type = null
- if (isTyped) {
- type = resultType.componentType
- result = remaining.collect{ cli.getValue(type, it, null) }
- } else {
- result = remaining.toList()
- }
- if (namesAreSetters) {
- m.invoke(t, isTyped ? [result.toArray(Array.newInstance(type, result.size()))] as Object[] : result)
- } else {
- Map names = calculateNames("", "", m, namesAreSetters)
- t.put(names.long, { -> result })
- }
- }
-
- private void processSetAnnotation(Method m, Object t, String name, cli, boolean namesAreSetters) {
- def conv = savedTypeOptions[name]?.convert
- if (conv && conv instanceof Class) {
- savedTypeOptions[name].convert = conv.newInstance(t, t)
- }
- boolean hasArg = savedTypeOptions[name]?.cliOption?.numberOfArgs == 1
- boolean noArg = savedTypeOptions[name]?.cliOption?.numberOfArgs == 0
- if (namesAreSetters) {
- def isBoolArg = m.parameterTypes.size() > 0 && m.parameterTypes[0].simpleName.toLowerCase() == 'boolean'
- boolean isFlag = (isBoolArg && !hasArg) || noArg
- if (cli.hasOption(name) || isFlag || cli.defaultValue(name)) {
- m.invoke(t, [isFlag ? cli.hasOption(name) :
- cli.hasOption(name) ? optionValue(cli, name) : cli.defaultValue(name)] as Object[])
- }
- } else {
- def isBoolRetType = m.returnType.simpleName.toLowerCase() == 'boolean'
- boolean isFlag = (isBoolRetType && !hasArg) || noArg
- t.put(m.getName(), cli.hasOption(name) ?
- { -> isFlag ? true : optionValue(cli, name) } :
- { -> isFlag ? false : cli.defaultValue(name) })
- }
- }
-
- private optionValue(cli, String name) {
- if (savedTypeOptions.containsKey(name)) {
- return cli.getOptionValue(savedTypeOptions[name])
- }
- cli[name]
- }
-
- private Map calculateNames(String longName, String shortName, Method m, boolean namesAreSetters) {
- boolean useShort = longName == '_'
- if (longName == '_') longName = ""
- def result = longName
- if (!longName) {
- result = m.getName()
- if (namesAreSetters && result.startsWith("set")) {
- result = MetaClassHelper.convertPropertyName(result.substring(3))
- }
- }
- [long: useShort ? "" : result, short: (useShort && !shortName) ? result : shortName]
- }
-
- // implementation details -------------------------------------
-
- /**
- * Internal method: How to create an option from the specification.
- */
- CliOption option(shortname, Map details, info) {
- CliOption option
- if (shortname == '_') {
- option = CliOption.builder().desc(info).longOpt(details.longOpt).build()
- details.remove('longOpt')
- } else {
- option = new CliOption(shortname, info)
- }
- adjustDetails(details).each { key, value ->
- option[key] = value
- }
- return option
- }
-
- static Map adjustDetails(Map m) {
- m.collectMany { k, v ->
- if (k == 'args' && v == '+') {
- [[args: org.apache.commons.cli.Option.UNLIMITED_VALUES]]
- } else if (k == 'args' && v == '*') {
- [[args: org.apache.commons.cli.Option.UNLIMITED_VALUES,
- optionalArg: true]]
- } else if (k == 'args' && v instanceof String) {
- [[args: Integer.parseInt(v)]]
- } else {
- [[(k): v]]
- }
- }.sum()
- }
-
- static expandArgumentFiles(args) throws IOException {
- def result = []
- for (arg in args) {
- if (arg && arg != '@' && arg[0] == '@') {
- arg = arg.substring(1)
- if (arg[0] != '@') {
- expandArgumentFile(arg, result)
- continue
- }
- }
- result << arg
- }
- return result
- }
-
- private static expandArgumentFile(name, args) throws IOException {
- def charAsInt = { String s -> s.toCharacter() as int }
- new File(name).withReader { r ->
- new StreamTokenizer(r).with {
- resetSyntax()
- wordChars(charAsInt(' '), 255)
- whitespaceChars(0, charAsInt(' '))
- commentChar(charAsInt('#'))
- quoteChar(charAsInt('"'))
- quoteChar(charAsInt('\''))
- while (nextToken() != StreamTokenizer.TT_EOF) {
- args << sval
- }
- }
- }
- }
-
-}
-
-class OptionAccessor {
- CommandLine commandLine
- Map<String, TypedOption> savedTypeOptions
-
- OptionAccessor(CommandLine commandLine) {
- this.commandLine = commandLine
- }
-
- boolean hasOption(TypedOption typedOption) {
- commandLine.hasOption(typedOption.longOpt ?: typedOption.opt)
- }
-
- public <T> T defaultValue(String name) {
- Class<T> type = savedTypeOptions[name]?.type
- String value = savedTypeOptions[name]?.defaultValue() ? savedTypeOptions[name].defaultValue() : null
- return (T) value ? getTypedValue(type, name, value) : null
- }
-
- public <T> T getOptionValue(TypedOption<T> typedOption) {
- getOptionValue(typedOption, null)
- }
-
- public <T> T getOptionValue(TypedOption<T> typedOption, T defaultValue) {
- String optionName = (String) typedOption.longOpt ?: typedOption.opt
- if (commandLine.hasOption(optionName)) {
- if (typedOption.containsKey('type') && typedOption.type.isArray()) {
- def compType = typedOption.type.componentType
- return (T) getTypedValuesFromName(optionName, compType)
- }
- return getTypedValueFromName(optionName)
- }
- return defaultValue
- }
-
- private <T> T[] getTypedValuesFromName(String optionName, Class<T> compType) {
- CliOption option = commandLine.options.find{ it.longOpt == optionName }
- T[] result = null
- if (option) {
- int count = 0
- def optionValues = commandLine.getOptionValues(optionName)
- for (String optionValue : optionValues) {
- if (result == null) {
- result = (T[]) Array.newInstance(compType, optionValues.length)
- }
- result[count++] = (T) getTypedValue(compType, optionName, optionValue)
- }
- }
- if (result == null) {
- result = (T[]) Array.newInstance(compType, 0)
- }
- return result
- }
-
- public <T> T getAt(TypedOption<T> typedOption) {
- getAt(typedOption, null)
- }
-
- public <T> T getAt(TypedOption<T> typedOption, T defaultValue) {
- String optionName = (String) typedOption.longOpt ?: typedOption.opt
- if (savedTypeOptions.containsKey(optionName)) {
- return getTypedValueFromName(optionName)
- }
- return defaultValue
- }
-
- private <T> T getTypedValueFromName(String optionName) {
- Class type = savedTypeOptions[optionName].type
- String optionValue = commandLine.getOptionValue(optionName)
- return (T) getTypedValue(type, optionName, optionValue)
- }
-
- private <T> T getTypedValue(Class<T> type, String optionName, String optionValue) {
- if (savedTypeOptions[optionName]?.cliOption?.numberOfArgs == 0) {
- return (T) commandLine.hasOption(optionName)
- }
- def convert = savedTypeOptions[optionName]?.convert
- return getValue(type, optionValue, convert)
- }
-
- private <T> T getValue(Class<T> type, String optionValue, Closure convert) {
- if (!type) {
- return (T) optionValue
- }
- if (Closure.isAssignableFrom(type) && convert) {
- return (T) convert(optionValue)
- }
- if (type?.simpleName?.toLowerCase() == 'boolean') {
- return (T) Boolean.parseBoolean(optionValue)
- }
- StringGroovyMethods.asType(optionValue, (Class<T>) type)
- }
-
- def invokeMethod(String name, Object args) {
- return InvokerHelper.getMetaClass(commandLine).invokeMethod(commandLine, name, args)
- }
-
- def getProperty(String name) {
- if (!savedTypeOptions.containsKey(name)) {
- def alt = savedTypeOptions.find{ it.value.opt == name }
- if (alt) name = alt.key
- }
- def methodname = 'getOptionValue'
- Class type = savedTypeOptions[name]?.type
- def foundArray = type?.isArray()
- if (name.size() > 1 && name.endsWith('s')) {
- def singularName = name[0..-2]
- if (commandLine.hasOption(singularName) || foundArray) {
- name = singularName
- methodname += 's'
- type = savedTypeOptions[name]?.type
- }
- }
- if (type?.isArray()) {
- methodname = 'getOptionValues'
- }
- if (name.size() == 1) name = name as char
- def result = InvokerHelper.getMetaClass(commandLine).invokeMethod(commandLine, methodname, name)
- if (result != null) {
- if (result instanceof String[]) {
- result = result.collect{ type ? getTypedValue(type.isArray() ? type.componentType : type, name, it) : it }
- } else {
- if (type) result = getTypedValue(type, name, result)
- }
- } else if (type?.simpleName != 'boolean' && savedTypeOptions[name]?.defaultValue) {
- result = getTypedValue(type, name, savedTypeOptions[name].defaultValue)
- } else {
- result = commandLine.hasOption(name)
- }
- return result
- }
-
- List<String> arguments() {
- commandLine.args.toList()
- }
-}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/util/ClosureComparator.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/util/ClosureComparator.java b/src/main/groovy/util/ClosureComparator.java
deleted file mode 100644
index dc70ea6..0000000
--- a/src/main/groovy/util/ClosureComparator.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
-
-import java.io.Serializable;
-import java.util.Comparator;
-
-/**
- * A Comparator which uses a closure to compare 2 values being equal
- *
- * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
- */
-public class ClosureComparator<T> implements Comparator<T>, Serializable {
-
- private static final long serialVersionUID = -4593521535656429522L;
- Closure closure;
-
- public ClosureComparator(Closure closure) {
- this.closure = closure;
- }
-
- public int compare(T object1, T object2) {
- Object value = closure.call(object1, object2);
- return DefaultTypeTransformation.intUnbox(value);
- }
-}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/util/ConfigObject.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/util/ConfigObject.java b/src/main/groovy/util/ConfigObject.java
deleted file mode 100644
index c76bc97..0000000
--- a/src/main/groovy/util/ConfigObject.java
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
- * 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/10110145/src/main/groovy/util/ConfigSlurper.groovy
----------------------------------------------------------------------
diff --git a/src/main/groovy/util/ConfigSlurper.groovy b/src/main/groovy/util/ConfigSlurper.groovy
deleted file mode 100644
index 20d0723..0000000
--- a/src/main/groovy/util/ConfigSlurper.groovy
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * 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)
- }
-}