blob: f36ba85e5c91ab245e94b4c3f4c4ca9e9f762c2b [file] [log] [blame]
/*
* $Id$
*
* Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
*
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided that the
* following conditions are met:
* 1. Redistributions of source code must retain copyright statements and
* notices. Redistributions must also contain a copy of this document.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name "groovy" must not be used to endorse or promote products
* derived from this Software without prior written permission of The Codehaus.
* For written permission, please contact info@codehaus.org.
* 4. Products derived from this Software may not be called "groovy" nor may
* "groovy" appear in their names without prior written permission of The
* Codehaus. "groovy" is a registered trademark of The Codehaus.
* 5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
*
* THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*/
package org.codehaus.groovy.runtime;
import groovy.lang.*;
import groovy.util.CharsetToolkit;
import groovy.util.ClosureComparator;
import groovy.util.OrderBy;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class defines all the new groovy methods which appear on normal JDK
* classes inside the Groovy environment. Static methods are used with the
* first parameter the destination class.
*
* @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
* @author Jeremy Rayner
* @author Sam Pullara
* @author Rod Cope
* @author Guillaume Laforge
* @author John Wilson
* @author Hein Meling
* @author Dierk Koenig
* @version $Revision$
*/
public class DefaultGroovyMethods {
private static Logger log = Logger.getLogger(DefaultGroovyMethods.class.getName());
private static final Integer ONE = new Integer(1);
private static final char ZERO_CHAR = '\u0000';
/**
* Identity check. Since == is overridden in Groovy with the meaning of equality
* we need some fallback to check for object identity.
* @param self
* @param other
* @return true if self and other are identical, false otherwise
*/
public static boolean is(Object self, Object other){
return System.identityHashCode(self) == System.identityHashCode(other);
}
/**
* Allows the closure to be called for the object reference self
*
* @param self the object to have a closure act upon
* @param closure the closure to call on the object
* @return result of calling the closure
*/
public static Object identity(Object self, Closure closure) {
closure.setDelegate(self);
return closure.callSpecial(self);
}
/**
* Allows the subscript operator to be used to lookup dynamic property values.
* <code>bean[somePropertyNameExpression]</code>. The normal property notation
* of groovy is neater and more concise but only works with compile time known
* property names.
*
* @param self
* @return
*/
public static Object getAt(Object self, String property) {
return InvokerHelper.getProperty(self, property);
}
/**
* Allows the subscript operator to be used to set dynamically named property values.
* <code>bean[somePropertyNameExpression] = foo</code>. The normal property notation
* of groovy is neater and more concise but only works with compile time known
* property names.
*
* @param self
*/
public static void putAt(Object self, String property, Object newValue) {
InvokerHelper.setProperty(self, property, newValue);
}
/**
* Generates a detailed dump string of an object showing its class,
* hashCode and fields
*/
public static String dump(Object self) {
if (self == null) {
return "null";
}
StringBuffer buffer = new StringBuffer("<");
Class klass = self.getClass();
buffer.append(klass.getName());
buffer.append("@");
buffer.append(Integer.toHexString(self.hashCode()));
boolean groovyObject = self instanceof GroovyObject;
/*jes this may be rewritten to use the new allProperties() stuff
* but the original pulls out private variables, whereas allProperties()
* does not. What's the real use of dump() here?
*/
while (klass != null) {
Field[] fields = klass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
final Field field = fields[i];
if ((field.getModifiers() & Modifier.STATIC) == 0) {
if (groovyObject && field.getName().equals("metaClass")) {
continue;
}
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
field.setAccessible(true);
return null;
}
});
buffer.append(" ");
buffer.append(field.getName());
buffer.append("=");
try {
buffer.append(InvokerHelper.toString(field.get(self)));
} catch (Exception e) {
buffer.append(e);
}
}
}
klass = klass.getSuperclass();
}
/* here is a different implementation that uses allProperties(). I have left
* it commented out because it returns a slightly different list of properties;
* ie it does not return privates. I don't know what dump() really should be doing,
* although IMO showing private fields is a no-no
*/
/*
List props = allProperties(self);
for(Iterator itr = props.iterator(); itr.hasNext(); ) {
PropertyValue pv = (PropertyValue) itr.next();
// the original skipped this, so I will too
if(pv.getName().equals("metaClass")) continue;
if(pv.getName().equals("class")) continue;
buffer.append(" ");
buffer.append(pv.getName());
buffer.append("=");
try {
buffer.append(InvokerHelper.toString(pv.getValue()));
}
catch (Exception e) {
buffer.append(e);
}
}
*/
buffer.append(">");
return buffer.toString();
}
public static void eachPropertyName(Object self, Closure closure) {
List props = allProperties(self);
for (Iterator itr = props.iterator(); itr.hasNext();) {
PropertyValue pv = (PropertyValue) itr.next();
closure.callSpecial(pv.getName());
}
}
public static void eachProperty(Object self, Closure closure) {
List props = allProperties(self);
for (Iterator itr = props.iterator(); itr.hasNext();) {
PropertyValue pv = (PropertyValue) itr.next();
closure.callSpecial(pv);
}
}
public static List allProperties(Object self) {
List props = new ArrayList();
MetaClass metaClass = InvokerHelper.getMetaClass(self);
List mps;
if (self instanceof groovy.util.Expando) {
mps = ((groovy.util.Expando) self).getProperties();
} else {
// get the MetaProperty list from the MetaClass
mps = metaClass.getProperties();
}
for (Iterator itr = mps.iterator(); itr.hasNext();) {
MetaProperty mp = (MetaProperty) itr.next();
PropertyValue pv = new PropertyValue(self, mp);
props.add(pv);
}
return props;
}
/**
* Scoped use method
*/
public static void use(Object self, Class categoryClass, Closure closure) {
GroovyCategorySupport.use(categoryClass, closure);
}
/**
* Scoped use method with list of categories
*/
public static void use(Object self, List categoryClassList, Closure closure) {
GroovyCategorySupport.use(categoryClassList, closure);
}
/**
* Print to a console in interactive format
*/
public static void print(Object self, Object value) {
System.out.print(InvokerHelper.toString(value));
}
/**
* Print a linebreak to the standard out.
*/
public static void println(Object self) {
System.out.println();
}
/**
* Print to a console in interactive format along with a newline
*/
public static void println(Object self, Object value) {
System.out.println(InvokerHelper.toString(value));
}
/**
* Printf to a console. Only works with JDK1.5 or later.
*
* @author Russel Winder
* @version 2005.02.01.15.53
*/
public static void printf(final Object self, final String format, final Object[] values) {
if ( System.getProperty("java.version").charAt(2) == '5' ) {
//
// Cannot just do:
//
// System.out.printf(format, values) ;
//
// because this fails to compile on JDK1.4.x and earlier. So until the entire world is using
// JDK1.5 or later then we have to do things by reflection so as to hide the use of printf
// from the compiler. In JDK1.5 you might try:
//
// System.out.getClass().getMethod("printf", String.class, Object[].class).invoke(System.out, format, values) ;
//
// but of course this doesn't work on JDK1.4 as it relies on varargs. argh. So we are
// forced into:
//
try {
System.out.getClass().getMethod("printf", new Class[] {String.class, Object[].class}).invoke(System.out, new Object[] {format, values}) ;
} catch ( NoSuchMethodException nsme ) {
throw new RuntimeException ("getMethod threw a NoSuchMethodException. This is impossible.") ;
} catch ( IllegalAccessException iae ) {
throw new RuntimeException ("invoke threw a IllegalAccessException. This is impossible.") ;
} catch ( java.lang.reflect.InvocationTargetException ite ) {
throw new RuntimeException ("invoke threw a InvocationTargetException. This is impossible.") ;
}
} else {
throw new RuntimeException ("printf requires JDK1.5 or later.") ;
}
}
/**
* Returns a formatted string using the specified format string and
* arguments.
*
* <p>
* For examples, <pre>
* printf ( "Hello, %s!\n" , [ "world" ] as String[] )
* printf ( "Hello, %s!\n" , [ "Groovy" ])
* printf ( "%d + %d = %d\n" , [ 1 , 2 , 1+2 ] as Integer[] )
* printf ( "%d + %d = %d\n" , [ 3 , 3 , 3+3 ])
*
* ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as Integer[] ) }
* ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as int[] ) }
* ( 0x41..0x45 ).each { printf ( "-- %c\n" , [ it ] as char[] ) }
* ( 07..011 ).each { printf ( "-- %d\n" , [ it ] as byte[] ) }
* ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as short[] ) }
* ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as long[] ) }
* ( 7..11 ).each { printf ( "-- %5.2f\n" , [ it ] as float[] ) }
* ( 7..11 ).each { printf ( "-- %5.2g\n" , [ it ] as double[] ) }
* </pre>
* <p>
*
* @param format
* A format string
*
* @param arg
* Argument which is referenced by the format specifiers in the format
* string. The type of <code>arg</code> should be one of Object[], List,
* int[], short[], byte[], char[], boolean[], long[], float[], or double[].
*
* @return A formatted string
* @since JDK 1.5
*
* @author Pilho Kim
* @version 2005.07.25.02.31
*/
public static void printf(final Object self, final String format, Object arg) {
if (arg instanceof Object[]) {
printf(self, format, (Object[]) arg);
return;
}
else if (arg instanceof List) {
printf(self, format, ((List) arg).toArray());
return;
}
Object[] ans = null;
String elemType = arg.getClass().getName();
if (elemType.equals("[I")) {
int[] ia = (int[]) arg;
ans = new Integer[ia.length];
for (int i = 0; i < ia.length; i++) {
ans[i] = new Integer(ia[i]);
}
}
else if (elemType.equals("[C")) {
char[] ia = (char[]) arg;
ans = new Character[ia.length];
for (int i = 0; i < ia.length; i++) {
ans[i] = new Character(ia[i]);
}
}
else if (elemType.equals("[Z")) {
boolean[] ia = (boolean[]) arg;
ans = new Boolean[ia.length];
for (int i = 0; i < ia.length; i++) {
ans[i] = new Boolean(ia[i]);
}
}
else if (elemType.equals("[B")) {
byte[] ia = (byte[]) arg;
ans = new Byte[ia.length];
for (int i = 0; i < ia.length; i++) {
ans[i] = new Byte(ia[i]);
}
}
else if (elemType.equals("[S")) {
short[] ia = (short[]) arg;
ans = new Short[ia.length];
for (int i = 0; i < ia.length; i++) {
ans[i] = new Short(ia[i]);
}
}
else if (elemType.equals("[F")) {
float[] ia = (float[]) arg;
ans = new Float[ia.length];
for (int i = 0; i < ia.length; i++) {
ans[i] = new Float(ia[i]);
}
}
else if (elemType.equals("[J")) {
long[] ia = (long[]) arg;
ans = new Long[ia.length];
for (int i = 0; i < ia.length; i++) {
ans[i] = new Long(ia[i]);
}
}
else if (elemType.equals("[D")) {
double[] ia = (double[]) arg;
ans = new Double[ia.length];
for (int i = 0; i < ia.length; i++) {
ans[i] = new Double(ia[i]);
}
}
else {
throw new RuntimeException("printf(String," + arg + ")");
}
printf(self, format, (Object[]) ans);
}
/**
* @return a String that matches what would be typed into a terminal to
* create this object. e.g. [1, 'hello'].inspect() -> [1, "hello"]
*/
public static String inspect(Object self) {
return InvokerHelper.inspect(self);
}
/**
* Print to a console in interactive format
*/
public static void print(Object self, PrintWriter out) {
if (out == null) {
out = new PrintWriter(System.out);
}
out.print(InvokerHelper.toString(self));
}
/**
* Print to a console in interactive format
*
* @param out the PrintWriter used for printing
*/
public static void println(Object self, PrintWriter out) {
if (out == null) {
out = new PrintWriter(System.out);
}
InvokerHelper.invokeMethod(self, "print", out);
out.println();
}
/**
* Provide a dynamic method invocation method which can be overloaded in
* classes to implement dynamic proxies easily.
*/
public static Object invokeMethod(Object object, String method, Object arguments) {
return InvokerHelper.invokeMethod(object, method, arguments);
}
// isCase methods
//-------------------------------------------------------------------------
public static boolean isCase(Object caseValue, Object switchValue) {
return caseValue.equals(switchValue);
}
public static boolean isCase(String caseValue, Object switchValue) {
if (switchValue == null) {
return caseValue == null;
}
return caseValue.equals(switchValue.toString());
}
public static boolean isCase(Class caseValue, Object switchValue) {
return caseValue.isInstance(switchValue);
}
public static boolean isCase(Collection caseValue, Object switchValue) {
return caseValue.contains(switchValue);
}
public static boolean isCase(Pattern caseValue, Object switchValue) {
Matcher matcher = caseValue.matcher(switchValue.toString());
if (matcher.matches()) {
RegexSupport.setLastMatcher(matcher);
return true;
} else {
return false;
}
}
private static Object packArray(Object object) {
if (object instanceof Object[])
return new Object[] {object};
else
return object;
}
// Collection based methods
//-------------------------------------------------------------------------
/**
* Remove all duplicates from the Collection.
* Works on the receiver object and returns it.
* From any duplicate, the first that is returned by the Collections iterator
* is retained, all other instances are removed.
* The Collection's original sequence is retained.
* @param self
* @return self without duplicates
*/
public static Collection unique(Collection self){
if (self instanceof Set) return self;
if (self.size() == new HashSet(self).size()) return self;
Collection seen = new HashSet(self.size());
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object o = iter.next();
if (seen.contains(o)){
iter.remove();
} else {
seen.add(o);
}
}
return self;
}
/**
* Allows objects to be iterated through using a closure
*
* @param self the object over which we iterate
* @param closure the closure applied on each element found
*/
public static void each(Object self, Closure closure) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
closure.callSpecial(iter.next());
}
}
/**
* Allows object to be iterated through a closure with a counter
*
* @param self an Object
* @param closure a Closure
*/
public static void eachWithIndex(Object self, Closure closure) {
int counter = 0;
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
closure.callSpecial(new ParameterArray(new Object[]{iter.next(), new Integer(counter++)}));
}
}
/**
* Allows objects to be iterated through using a closure
*
* @param self the collection over which we iterate
* @param closure the closure applied on each element of the collection
*/
public static void each(Collection self, Closure closure) {
for (Iterator iter = self.iterator(); iter.hasNext();) {
closure.callSpecial(iter.next());
}
}
/**
* Allows a Map to be iterated through using a closure. If the
* closure takes one parameter then it will be passed the Map.Entry
* otherwise if the closure takes two parameters then it will be
* passed the key and the value.
*
* @param self the map over which we iterate
* @param closure the closure applied on each entry of the map
*/
public static void each(Map self, Closure closure) {
for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
callClosureForMapEntry(closure, entry);
}
}
/**
* Iterates over every element of a collection, and check whether a predicate is valid for all elements.
*
* @param self the object over which we iterate
* @param closure the closure predicate used for matching
* @return true if every item in the collection matches the closure
* predicate
*/
public static boolean every(Object self, Closure closure) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
if (!InvokerHelper.asBool(closure.callSpecial(iter.next()))) {
return false;
}
}
return true;
}
/**
* Iterates over every element of a collection, and check whether a predicate is valid for at least one element
*
* @param self the object over which we iterate
* @param closure the closure predicate used for matching
* @return true if any item in the collection matches the closure predicate
*/
public static boolean any(Object self, Closure closure) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
if (InvokerHelper.asBool(closure.callSpecial(iter.next()))) {
return true;
}
}
return false;
}
/**
* Iterates over every element of the collection and return each object that matches
* the given filter - calling the isCase() method used by switch statements.
* This method can be used with different kinds of filters like regular expresions, classes, ranges etc.
*
* @param self the object over which we iterate
* @param filter the filter to perform on the collection (using the isCase(object) method)
* @return a list of objects which match the filter
*/
public static List grep(Object self, Object filter) {
List answer = new ArrayList();
MetaClass metaClass = InvokerHelper.getMetaClass(filter);
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
Object object = iter.next();
if (InvokerHelper.asBool(metaClass.invokeMethod(filter, "isCase", object))) {
answer.add(object);
}
}
return answer;
}
/**
* Counts the number of occurencies of the given value inside this collection
*
* @param self the collection within which we count the number of occurencies
* @param value the value
* @return the number of occurrencies
*/
public static int count(Collection self, Object value) {
int answer = 0;
for (Iterator iter = self.iterator(); iter.hasNext();) {
if (InvokerHelper.compareEqual(iter.next(), value)) {
++answer;
}
}
return answer;
}
/**
* Convert a collection to a List.
*
* @param self a collection
* @return a List
*/
public static List toList(Collection self) {
List answer = new ArrayList(self.size());
answer.addAll(self);
return answer;
}
/**
* Iterates through this object transforming each object into a new value using the closure
* as a transformer, returning a list of transformed values.
*
* @param self the values of the object to map
* @param closure the closure used to map each element of the collection
* @return a List of the mapped values
*/
public static List collect(Object self, Closure closure) {
return (List) collect(self, new ArrayList(), closure);
}
/**
* Iterates through this object transforming each object into a new value using the closure
* as a transformer and adding it to the collection, returning the resulting collection.
*
* @param self the values of the object to map
* @param collection the Collection to which the mapped values are added
* @param closure the closure used to map each element of the collection
* @return the resultant collection
*/
public static Collection collect(Object self, Collection collection, Closure closure) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
collection.add(closure.callSpecial(iter.next()));
}
return collection;
}
/**
* Iterates through this collection transforming each entry into a new value using the closure
* as a transformer, returning a list of transformed values.
*
* @param self a collection
* @param closure the closure used for mapping
* @return a List of the mapped values
*/
public static List collect(Collection self, Closure closure) {
return (List) collect(self, new ArrayList(self.size()), closure);
}
/**
* Iterates through this collection transforming each entry into a new value using the closure
* as a transformer, returning a list of transformed values.
*
* @param self a collection
* @param collection the Collection to which the mapped values are added
* @param closure the closure used to map each element of the collection
* @return the resultant collection
*/
public static Collection collect(Collection self, Collection collection, Closure closure) {
for (Iterator iter = self.iterator(); iter.hasNext();) {
collection.add(closure.callSpecial(iter.next()));
if (closure.getDirective() == Closure.DONE) {
break;
}
}
return collection;
}
/**
* Iterates through this Map transforming each entry into a new value using the closure
* as a transformer, returning a list of transformed values.
*
* @param self a Map
* @param closure the closure used for mapping
* @return a List of the mapped values
*/
public static Collection collect(Map self, Collection collection, Closure closure) {
for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
collection.add(closure.callSpecial(iter.next()));
}
return collection;
}
/**
* Iterates through this Map transforming each entry into a new value using the closure
* as a transformer, returning a list of transformed values.
*
* @param self a Map
* @param collection the Collection to which the mapped values are added
* @param closure the closure used to map each element of the collection
* @return the resultant collection
*/
public static List collect(Map self, Closure closure) {
return (List) collect(self, new ArrayList(self.size()), closure);
}
/**
* Finds the first value matching the closure condition
*
* @param self an Object with an iterator returning its values
* @param closure a closure condition
* @return the first Object found
*/
public static Object find(Object self, Closure closure) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
Object value = iter.next();
if (InvokerHelper.asBool(closure.callSpecial(value))) {
return value;
}
}
return null;
}
/**
* Finds the first value matching the closure condition
*
* @param self a Collection
* @param closure a closure condition
* @return the first Object found
*/
public static Object find(Collection self, Closure closure) {
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object value = iter.next();
if (InvokerHelper.asBool(closure.callSpecial(value))) {
return value;
}
}
return null;
}
/**
* Finds the first value matching the closure condition
*
* @param self a Map
* @param closure a closure condition
* @return the first Object found
*/
public static Object find(Map self, Closure closure) {
for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
Object value = iter.next();
if (InvokerHelper.asBool(closure.callSpecial(value))) {
return value;
}
}
return null;
}
/**
* Finds all values matching the closure condition
*
* @param self an Object with an Iterator returning its values
* @param closure a closure condition
* @return a List of the values found
*/
public static List findAll(Object self, Closure closure) {
List answer = new ArrayList();
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
Object value = iter.next();
if (InvokerHelper.asBool(closure.callSpecial(value))) {
answer.add(value);
}
}
return answer;
}
/**
* Finds all values matching the closure condition
*
* @param self a Collection
* @param closure a closure condition
* @return a List of the values found
*/
public static List findAll(Collection self, Closure closure) {
List answer = new ArrayList(self.size());
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object value = iter.next();
if (InvokerHelper.asBool(closure.callSpecial(value))) {
answer.add(value);
}
}
return answer;
}
/**
* Finds all entries matching the closure condition. If the
* closure takes one parameter then it will be passed the Map.Entry
* otherwise if the closure takes two parameters then it will be
* passed the key and the value.
*
* @param self a Map
* @param closure a closure condition applying on the entries
* @return a new subMap
*/
public static Map findAll(Map self, Closure closure) {
Map answer = new HashMap(self.size());
for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
if (InvokerHelper.asBool(callClosureForMapEntry(closure, entry))) {
answer.put(entry.getKey(),entry.getValue());
}
}
return answer;
}
// internal helper method
protected static Object callClosureForMapEntry(Closure closure, Map.Entry entry) {
if (closure.getParameterTypes().length == 2) {
return closure.callSpecial(new ParameterArray(new Object[]{entry.getKey(), entry.getValue()}));
}
return closure.callSpecial(entry);
}
/**
* Iterates through the given collection, passing in the initial value to
* the closure along with the current iterated item then passing into the
* next iteration the value of the previous closure.
*
* @param self a Collection
* @param value a value
* @param closure a closure
* @return the last value of the last iteration
*/
public static Object inject(Collection self, Object value, Closure closure) {
Object[] params = new Object[2];
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object item = iter.next();
params[0] = value;
params[1] = item;
value = closure.callSpecial(new ParameterArray(params));
}
return value;
}
/**
* Iterates through the given array of objects, passing in the initial value to
* the closure along with the current iterated item then passing into the
* next iteration the value of the previous closure.
*
* @param self an Object[]
* @param value a value
* @param closure a closure
* @return the last value of the last iteration
*/
public static Object inject(Object[] self, Object value, Closure closure) {
Object[] params = new Object[2];
for (int i = 0; i < self.length; i++) {
params[0] = value;
params[1] = self[i];
value = closure.callSpecial(new ParameterArray(params));
}
return value;
}
/**
* Concatenates all of the items of the collection together with the given String as a separator
*
* @param self a Collection of objects
* @param separator a String separator
* @return the joined String
*/
public static String join(Collection self, String separator) {
StringBuffer buffer = new StringBuffer();
boolean first = true;
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object value = iter.next();
if (first) {
first = false;
} else {
buffer.append(separator);
}
buffer.append(InvokerHelper.toString(value));
}
return buffer.toString();
}
/**
* Concatenates all of the elements of the array together with the given String as a separator
*
* @param self an array of Object
* @param separator a String separator
* @return the joined String
*/
public static String join(Object[] self, String separator) {
StringBuffer buffer = new StringBuffer();
boolean first = true;
for (int i = 0; i < self.length; i++) {
String value = InvokerHelper.toString(self[i]);
if (first) {
first = false;
} else {
buffer.append(separator);
}
buffer.append(value);
}
return buffer.toString();
}
/**
* Selects the maximum value found in the collection
*
* @param self a Collection
* @return the maximum value
*/
public static Object max(Collection self) {
Object answer = null;
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object value = iter.next();
if (value != null) {
if (answer == null || InvokerHelper.compareGreaterThan(value, answer)) {
answer = value;
}
}
}
return answer;
}
/**
* Selects the maximum value found in the collection using the given comparator
*
* @param self a Collection
* @param comparator a Comparator
* @return the maximum value
*/
public static Object max(Collection self, Comparator comparator) {
Object answer = null;
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object value = iter.next();
if (answer == null || comparator.compare(value, answer) > 0) {
answer = value;
}
}
return answer;
}
/**
* Selects the minimum value found in the collection
*
* @param self a Collection
* @return the minimum value
*/
public static Object min(Collection self) {
Object answer = null;
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object value = iter.next();
if (value != null) {
if (answer == null || InvokerHelper.compareLessThan(value, answer)) {
answer = value;
}
}
}
return answer;
}
/**
* Selects the minimum value found in the collection using the given comparator
*
* @param self a Collection
* @param comparator a Comparator
* @return the minimum value
*/
public static Object min(Collection self, Comparator comparator) {
Object answer = null;
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object value = iter.next();
if (answer == null || comparator.compare(value, answer) < 0) {
answer = value;
}
}
return answer;
}
/**
* Selects the minimum value found in the collection using the given closure as a comparator
*
* @param self a Collection
* @param closure a closure used as a comparator
* @return the minimum value
*/
public static Object min(Collection self, Closure closure) {
Class[] params = closure.getParameterTypes();
if (params.length == 1) {
Object answer = null;
Object answer_value = null;
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object item = iter.next();
Object value = closure.callSpecial(item);
if (answer == null || InvokerHelper.compareLessThan(value, answer_value)) {
answer = item;
answer_value = value;
}
}
return answer;
} else {
return min(self, new ClosureComparator(closure));
}
}
/**
* Selects the maximum value found in the collection using the given closure as a comparator
*
* @param self a Collection
* @param closure a closure used as a comparator
* @return the maximum value
*/
public static Object max(Collection self, Closure closure) {
Class[] params = closure.getParameterTypes();
if (params.length == 1) {
Object answer = null;
Object answer_value = null;
for (Iterator iter = self.iterator(); iter.hasNext();) {
Object item = iter.next();
Object value = closure.callSpecial(item);
if (answer == null || InvokerHelper.compareLessThan(answer_value, value)) {
answer = item;
answer_value = value;
}
}
return answer;
} else {
return max(self, new ClosureComparator(closure));
}
}
/**
* Makes a String look like a Collection by adding support for the size() method
*
* @param text a String
* @return the length of the String
*/
public static int size(String text) {
return text.length();
}
/**
* Provide standard Groovy size() method for StringBuffers
*
* @param buffer a StringBuffer
* @return the length of the StringBuffer
*/
public static int size(StringBuffer buffer) {
return buffer.length();
}
/**
* Makes an Array look like a Collection by adding support for the size() method
*
* @param self an Array of Object
* @return the size of the Array
*/
public static int size(Object[] self) {
return self.length;
}
/**
* Support the subscript operator for String.
*
* @param text a String
* @param index the index of the Character to get
* @return the Character at the given index
*/
public static CharSequence getAt(CharSequence text, int index) {
index = normaliseIndex(index, text.length());
return text.subSequence(index, index + 1);
}
/**
* Support the subscript operator for String
*
* @param text a String
* @return the Character object at the given index
*/
public static String getAt(String text, int index) {
index = normaliseIndex(index, text.length());
return text.substring(index, index + 1);
}
/**
* Support the range subscript operator for CharSequence
*
* @param text a CharSequence
* @param range a Range
* @return the subsequence CharSequence
*/
public static CharSequence getAt(CharSequence text, Range range) {
int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), text.length());
int to = normaliseIndex(InvokerHelper.asInt(range.getTo()), text.length());
// if this is a backwards range, reverse the arguments to substring
if (from > to) {
int tmp = from;
from = to;
to = tmp;
}
return text.subSequence(from, to + 1);
}
/**
* Support the range subscript operator for CharSequence or StringBuffer with IntRange
*
* @param text a CharSequence
* @param range an IntRange
* @return the subsequence CharSequence
*/
public static CharSequence getAt(CharSequence text, IntRange range) {
return getAt(text, (Range) range);
}
/**
* Support the range subscript operator for String with IntRange
*
* @param text a String
* @param range an IntRange
* @return the resulting String
*/
public static String getAt(String text, IntRange range) {
return getAt(text, (Range) range);
}
/**
* Support the range subscript operator for String
*
* @param text a String
* @param range a Range
* @return a substring corresponding to the Range
*/
public static String getAt(String text, Range range) {
int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), text.length());
int to = normaliseIndex(InvokerHelper.asInt(range.getTo()), text.length());
// if this is a backwards range, reverse the arguments to substring
boolean reverse = range.isReverse();
if (from > to) {
int tmp = to;
to = from;
from = tmp;
reverse = !reverse;
}
String answer = text.substring(from, to + 1);
if (reverse) {
answer = reverse(answer);
}
return answer;
}
/**
* Creates a new string which is the reverse (backwards) of this string
*
* @param self a String
* @return a new string with all the characters reversed.
*/
public static String reverse(String self) {
int size = self.length();
StringBuffer buffer = new StringBuffer(size);
for (int i = size - 1; i >= 0; i--) {
buffer.append(self.charAt(i));
}
return buffer.toString();
}
/**
* Transforms a String representing a URL into a URL object.
*
* @param self the String representing a URL
* @return a URL
* @throws MalformedURLException is thrown if the URL is not well formed.
*/
public static URL toURL(String self) throws MalformedURLException {
return new URL(self);
}
/**
* Turns a String into a regular expression pattern.
*
* @param self a String to convert into a regular expression
* @return the regular expression pattern
*/
public static Pattern negate(String self) {
return InvokerHelper.regexPattern(self);
}
/**
* Replaces all occurrencies of a captured group by the result of a closure on that text.
*
* <p> For examples,
* <pre>
* assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { Object[] it -> it[0].toUpperCase() })
*
* Here,
* it[0] is the global string of the matched group
* it[1] is the first string in the matched group
* it[2] is the second string in the matched group
*
*
* assert "FOO-FOO-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { x, y, z -> z.toUpperCase() })
*
* Here,
* x is the global string of the matched group
* y is the first string in the matched group
* z is the second string in the matched group
* </pre>
*
* @param self a String
* @param regex the capturing regex
* @param closure the closure to apply on each captured group
* @return a String with replaced content
*/
public static String replaceAll(String self, String regex, Closure closure) {
Matcher matcher = Pattern.compile(regex).matcher(self);
if (matcher.find()) {
matcher.reset();
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
int count = matcher.groupCount();
ArrayList groups = new ArrayList();
for (int i = 0; i <= count; i++) {
groups.add(matcher.group(i));
}
String foundText = self.substring(matcher.start(0), matcher.end(0));
matcher.appendReplacement(sb, String.valueOf(closure.callSpecial(new ParameterArray(groups.toArray()))));
}
matcher.appendTail(sb);
return sb.toString();
} else {
return self;
}
}
/**
* Turns a String into a regular expression pattern
*
* @param self a GString to convert into a regular expression
* @return the regular expression pattern
*/
public static Pattern negate(GString self) {
return InvokerHelper.regexPattern(self.toString());
}
private static String getPadding(String padding, int length) {
if (padding.length() < length) {
return multiply(padding, new Integer(length / padding.length() + 1)).substring(0, length);
} else {
return padding.substring(0, length);
}
}
/**
* Pad a String with the characters appended to the left
*
* @param numberOfChars the total number of characters
* @param padding the charaters used for padding
* @return the String padded to the left
*/
public static String padLeft(String self, Number numberOfChars, String padding) {
int numChars = numberOfChars.intValue();
if (numChars <= self.length()) {
return self;
} else {
return getPadding(padding, numChars - self.length()) + self;
}
}
/**
* Pad a String with the spaces appended to the left
*
* @param numberOfChars the total number of characters
* @return the String padded to the left
*/
public static String padLeft(String self, Number numberOfChars) {
return padLeft(self, numberOfChars, " ");
}
/**
* Pad a String with the characters appended to the right
*
* @param numberOfChars the total number of characters
* @param padding the charaters used for padding
* @return the String padded to the right
*/
public static String padRight(String self, Number numberOfChars, String padding) {
int numChars = numberOfChars.intValue();
if (numChars <= self.length()) {
return self;
} else {
return self + getPadding(padding, numChars - self.length());
}
}
/**
* Pad a String with the spaces appended to the right
*
* @param numberOfChars the total number of characters
* @return the String padded to the right
*/
public static String padRight(String self, Number numberOfChars) {
return padRight(self, numberOfChars, " ");
}
/**
* Center a String and padd it with the characters appended around it
*
* @param numberOfChars the total number of characters
* @param padding the charaters used for padding
* @return the String centered with padded character around
*/
public static String center(String self, Number numberOfChars, String padding) {
int numChars = numberOfChars.intValue();
if (numChars <= self.length()) {
return self;
} else {
int charsToAdd = numChars - self.length();
String semiPad = charsToAdd % 2 == 1 ?
getPadding(padding, charsToAdd / 2 + 1) :
getPadding(padding, charsToAdd / 2);
if (charsToAdd % 2 == 0)
return semiPad + self + semiPad;
else
return semiPad.substring(0, charsToAdd / 2) + self + semiPad;
}
}
/**
* Center a String and padd it with spaces appended around it
*
* @param numberOfChars the total number of characters
* @return the String centered with padded character around
*/
public static String center(String self, Number numberOfChars) {
return center(self, numberOfChars, " ");
}
/**
* Support the subscript operator, e.g. matcher[index], for a regex Matcher.
*
* For an example using no group match, <code><pre>
* def p = /ab[d|f]/
* def m = "abcabdabeabf" =~ p
* for (i in 0..<m.count) {
* println( "m.groupCount() = " + m.groupCount())
* println( " " + i + ": " + m[i] ) // m[i] is a String
* }
* </pre></code>
*
* For an example using group matches, <code><pre>
* def p = /(?:ab([c|d|e|f]))/ `
* def m = "abcabdabeabf" =~ p
* for (i in 0..<m.count) {
* println( "m.groupCount() = " + m.groupCount())
* println( " " + i + ": " + m[i] ) // m[i] is a List
* }
* </pre></code>
*
* For another example using group matches, <code><pre>
* def m = "abcabdabeabfabxyzabx" =~ /(?:ab([d|x-z]+))/
* m.count.times {
* println( "m.groupCount() = " + m.groupCount())
* println( " " + it + ": " + m[it] ) // m[it] is a List
* }
* </pre></code>
*
* @param matcher a Matcher
* @param idx an index
* @return object a matched String if no groups matched, list of matched groups otherwise.
*/
public static Object getAt(Matcher matcher, int idx) {
try {
int count = getCount(matcher);
if (idx < -count || idx >= count) {
throw new IndexOutOfBoundsException("index is out of range " + (-count) + ".." + (count - 1) + " (index = " + idx + ")");
}
idx = normaliseIndex(idx, count);
matcher.reset();
for (int i = 0; i <= idx; i++) {
matcher.find();
}
if (hasGroup(matcher)) {
// are we using groups?
// yes, so return the specified group as list
ArrayList list = new ArrayList(matcher.groupCount());
for (int i = 0; i <= matcher.groupCount(); i++) {
list.add(matcher.group(i));
}
return list;
} else {
// not using groups, so return the nth
// occurrence of the pattern
return matcher.group();
}
}
catch (IllegalStateException ex) {
return null;
}
}
/**
* Set the position of the given Matcher to the given index.
*
* @param matcher a Matcher
* @param idx the index number
*/
public static void setIndex(Matcher matcher, int idx) {
int count = getCount(matcher);
if (idx < -count || idx >= count) {
throw new IndexOutOfBoundsException("index is out of range " + (-count) + ".." + (count - 1) + " (index = " + idx + ")");
}
if (idx == 0) {
matcher.reset();
}
else if (idx > 0) {
matcher.reset();
for (int i = 0; i < idx; i++) {
matcher.find();
}
}
else if (idx < 0) {
matcher.reset();
idx += getCount(matcher);
for (int i = 0; i < idx; i++) {
matcher.find();
}
}
}
/**
* Find the number of Strings matched to the given Matcher.
*
* @param matcher a Matcher
* @return int the number of Strings matched to the given matcher.
*/
public static int getCount(Matcher matcher) {
int counter = 0;
matcher.reset();
while (matcher.find()) {
counter++;
}
matcher.reset();
return counter;
}
/**
* Check whether a Matcher contains a group or not.
*
* @param matcher a Matcher
* @return boolean <code>true</code> if matcher contains at least one group.
*/
public static boolean hasGroup(Matcher matcher) {
return matcher.groupCount() > 0;
}
/**
* Support the range subscript operator for a List
*
* @param self a List
* @param range a Range
* @return a sublist based on range borders or a new list if range is reversed
* @see java.util.List#subList(int, int)
*/
public static List getAt(List self, IntRange range) {
RangeInfo info = subListBorders(self.size(), range);
List answer = self.subList(info.from, info.to); // sublist is always exclusive, but Ranges are not
if (info.reverse) {
answer = reverse(answer);
}
return answer;
}
// helper method for getAt and putAt
protected static RangeInfo subListBorders(int size, IntRange range){
int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), size);
int to = normaliseIndex(InvokerHelper.asInt(range.getTo()), size);
boolean reverse = range.isReverse();
if (from > to) { // support list[1..-1]
int tmp = to;
to = from;
from = tmp;
reverse = !reverse;
}
return new RangeInfo(from, to+1, reverse);
}
// helper method for getAt and putAt
protected static RangeInfo subListBorders(int size, EmptyRange range){
int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), size);
return new RangeInfo(from, from, false);
}
/**
* Allows a List to be used as the indices to be used on a List
*
* @param self a List
* @param indices a Collection of indices
* @return a new list of the values at the given indices
*/
public static List getAt(List self, Collection indices) {
List answer = new ArrayList(indices.size());
for (Iterator iter = indices.iterator(); iter.hasNext();) {
Object value = iter.next();
if (value instanceof Range) {
answer.addAll(getAt(self, (Range) value));
} else if (value instanceof List) {
answer.addAll(getAt(self, (List) value));
} else {
int idx = InvokerHelper.asInt(value);
answer.add(getAt(self, idx));
}
}
return answer;
}
/**
* Allows a List to be used as the indices to be used on a List
*
* @param self an Array of Objects
* @param indices a Collection of indices
* @return a new list of the values at the given indices
*/
public static List getAt(Object[] self, Collection indices) {
List answer = new ArrayList(indices.size());
for (Iterator iter = indices.iterator(); iter.hasNext();) {
Object value = iter.next();
if (value instanceof Range) {
answer.addAll(getAt(self, (Range) value));
} else if (value instanceof Collection) {
answer.addAll(getAt(self, (Collection) value));
} else {
int idx = InvokerHelper.asInt(value);
answer.add(getAt(self, idx));
}
}
return answer;
}
/**
* Allows a List to be used as the indices to be used on a CharSequence
*
* @param self a CharSequence
* @param indices a Collection of indices
* @return a String of the values at the given indices
*/
public static CharSequence getAt(CharSequence self, Collection indices) {
StringBuffer answer = new StringBuffer();
for (Iterator iter = indices.iterator(); iter.hasNext();) {
Object value = iter.next();
if (value instanceof Range) {
answer.append(getAt(self, (Range) value));
} else if (value instanceof Collection) {
answer.append(getAt(self, (Collection) value));
} else {
int idx = InvokerHelper.asInt(value);
answer.append(getAt(self, idx));
}
}
return answer.toString();
}
/**
* Allows a List to be used as the indices to be used on a String
*
* @param self a String
* @param indices a Collection of indices
* @return a String of the values at the given indices
*/
public static String getAt(String self, Collection indices) {
return (String) getAt((CharSequence) self, indices);
}
/**
* Allows a List to be used as the indices to be used on a Matcher
*
* @param self a Matcher
* @param indices a Collection of indices
* @return a String of the values at the given indices
*/
public static String getAt(Matcher self, Collection indices) {
StringBuffer answer = new StringBuffer();
for (Iterator iter = indices.iterator(); iter.hasNext();) {
Object value = iter.next();
if (value instanceof Range) {
answer.append(getAt(self, (Range) value));
} else if (value instanceof Collection) {
answer.append(getAt(self, (Collection) value));
} else {
int idx = InvokerHelper.asInt(value);
answer.append(getAt(self, idx));
}
}
return answer.toString();
}
/**
* Creates a sub-Map containing the given keys. This method is similar to
* List.subList() but uses keys rather than index ranges.
*
* @param map a Map
* @param keys a Collection of keys
* @return a new Map containing the given keys
*/
public static Map subMap(Map map, Collection keys) {
Map answer = new HashMap(keys.size());
for (Iterator iter = keys.iterator(); iter.hasNext();) {
Object key = iter.next();
answer.put(key, map.get(key));
}
return answer;
}
/**
* Looks up an item in a Map for the given key and returns the value - unless
* there is no entry for the given key in which case add the default value
* to the map and return that.
*
* @param map a Map
* @param key the key to lookup the value of
* @param defaultValue the value to return and add to the map for this key if
* there is no entry for the given key
* @return the value of the given key or the default value, added to the map if the
* key did not exist
*/
public static Object get(Map map, Object key, Object defaultValue) {
Object answer = map.get(key);
if (answer == null) {
answer = defaultValue;
map.put(key, answer);
}
return answer;
}
/**
* Support the range subscript operator for an Array
*
* @param array an Array of Objects
* @param range a Range
* @return a range of a list from the range's from index up to but not
* including the ranges's to value
*/
public static List getAt(Object[] array, Range range) {
List list = Arrays.asList(array);
return getAt(list, range);
}
public static List getAt(Object[] array, IntRange range) {
List list = Arrays.asList(array);
return getAt(list, range);
}
public static List getAt(Object[] array, ObjectRange range) {
List list = Arrays.asList(array);
return getAt(list, range);
}
/**
* Support the subscript operator for an Array
*
* @param array an Array of Objects
* @param idx an index
* @return the value at the given index
*/
public static Object getAt(Object[] array, int idx) {
return array[normaliseIndex(idx, array.length)];
}
/**
* Support the subscript operator for an Array
*
* @param array an Array of Objects
* @param idx an index
* @param value an Object to put at the given index
*/
public static void putAt(Object[] array, int idx, Object value) {
if (value instanceof Number) {
Class arrayComponentClass = array.getClass().getComponentType();
if (!arrayComponentClass.equals(value.getClass())) {
Object newVal = InvokerHelper.asType(value, arrayComponentClass);
array[normaliseIndex(idx, array.length)] = newVal;
return;
}
}
array[normaliseIndex(idx, array.length)] = value;
}
/**
* Allows conversion of arrays into a mutable List
*
* @param array an Array of Objects
* @return the array as a List
*/
public static List toList(Object[] array) {
int size = array.length;
List list = new ArrayList(size);
for (int i = 0; i < size; i++) {
list.add(array[i]);
}
return list;
}
/**
* Support the subscript operator for a List
*
* @param self a List
* @param idx an index
* @return the value at the given index
*/
public static Object getAt(List self, int idx) {
int size = self.size();
int i = normaliseIndex(idx, size);
if (i < size) {
return self.get(i);
} else {
return null;
}
}
/**
* A helper method to allow lists to work with subscript operators
*
* @param self a List
* @param idx an index
* @param value the value to put at the given index
*/
public static void putAt(List self, int idx, Object value) {
int size = self.size();
idx = normaliseIndex(idx, size);
if (idx < size) {
self.set(idx, value);
} else {
while (size < idx) {
self.add(size++, null);
}
self.add(idx, value);
}
}
/**
* Support the range subscript operator for StringBuffer
*
* @param self a StringBuffer
* @param range a Range
* @param value the object that's toString() will be inserted
*/
public static void putAt(StringBuffer self, IntRange range, Object value) {
RangeInfo info = subListBorders(self.length(), range);
self.replace(info.from, info.to, value.toString());
}
/**
* Support the range subscript operator for StringBuffer
*
* @param self a StringBuffer
* @param range a Range
* @param value the object that's toString() will be inserted
*/
public static void putAt(StringBuffer self, EmptyRange range, Object value) {
RangeInfo info = subListBorders(self.length(), range);
self.replace(info.from, info.to, value.toString());
}
/**
* A helper method to allow lists to work with subscript operators
*
* @param self a List
* @param range the subset of the list to set
* @param value the values to put at the given sublist or a Collection of values
*/
public static void putAt(List self, EmptyRange range, Object value) {
RangeInfo info = subListBorders(self.size(), range);
List sublist = self.subList(info.from, info.to);
sublist.clear();
if (value instanceof Collection){
Collection col = (Collection) value;
if (col.size() == 0) return;
sublist.addAll(col);
} else {
sublist.add(value);
}
}
/**
* A helper method to allow lists to work with subscript operators
*
* @param self a List
* @param range the subset of the list to set
* @param value the value to put at the given sublist or a Collection of values
*/
public static void putAt(List self, IntRange range, Object value) {
RangeInfo info = subListBorders(self.size(), range);
List sublist = self.subList(info.from, info.to);
sublist.clear();
if (value instanceof Collection){
Collection col = (Collection) value;
if (col.size() == 0) return;
sublist.addAll(col);
} else {
sublist.add(value);
}
}
/**
* A helper method to allow lists to work with subscript operators
*
* @param self a List
* @param splice the subset of the list to set
* @param values the value to put at the given sublist
* @deprecated replace with putAt(List self, Range range, List value)
*/
public static void putAt(List self, List splice, List values) {
List sublist = getSubList(self, splice);
sublist.clear();
sublist.addAll(values);
}
/**
* A helper method to allow lists to work with subscript operators
*
* @param self a List
* @param splice the subset of the list to set
* @param value the value to put at the given sublist
* @deprecated replace with putAt(List self, Range range, Object value)
*/
public static void putAt(List self, List splice, Object value) {
List sublist = getSubList(self, splice);
sublist.clear();
sublist.add(value);
}
// helper method for putAt(Splice)
// todo: remove after putAt(Splice) gets deleted
protected static List getSubList(List self, List splice) {
int left = 0;
int right = 0;
boolean emptyRange = false;
if (splice.size() == 2) {
left = InvokerHelper.asInt(splice.get(0));
right = InvokerHelper.asInt(splice.get(1));
} else if (splice instanceof IntRange) {
IntRange range = (IntRange) splice;
left = range.getFromInt();
right = range.getToInt();
} else if (splice instanceof EmptyRange) {
RangeInfo info = subListBorders(self.size(), (EmptyRange) splice);
left = info.from;
emptyRange = true;
} else {
throw new IllegalArgumentException("You must specify a list of 2 indexes to create a sub-list");
}
int size = self.size();
left = normaliseIndex(left, size);
right = normaliseIndex(right, size);
List sublist = null;
if (!emptyRange) {
sublist = self.subList(left, right + 1);
} else {
sublist = self.subList(left, left);
}
return sublist;
}
/**
* Support the subscript operator for a List
*
* @param self a Map
* @param key an Object as a key for the map
* @return the value corresponding to the given key
*/
public static Object getAt(Map self, Object key) {
return self.get(key);
}
/**
* A helper method to allow lists to work with subscript operators
*
* @param self a Map
* @param key an Object as a key for the map
* @return the value corresponding to the given key
*/
public static Object putAt(Map self, Object key, Object value) {
return self.put(key, value);
}
/**
* This converts a possibly negative index to a real index into the array.
*
* @param i
* @param size
* @return
*/
protected static int normaliseIndex(int i, int size) {
int temp = i;
if (i < 0) {
i += size;
}
if (i < 0) {
throw new ArrayIndexOutOfBoundsException("Negative array index [" + temp + "] too large for array size " + size);
}
return i;
}
/**
* Support the subscript operator for List
*
* @param coll a Collection
* @param property a String
* @return a List
*/
public static List getAt(Collection coll, String property) {
List answer = new ArrayList(coll.size());
for (Iterator iter = coll.iterator(); iter.hasNext();) {
Object item = iter.next();
Object value = InvokerHelper.getProperty(item, property);
if (value instanceof Collection) {
answer.addAll((Collection) value);
} else {
answer.add(value);
}
}
return answer;
}
/**
* A convenience method for creating an immutable map
*
* @param self a Map
* @return an immutable Map
*/
public static Map asImmutable(Map self) {
return Collections.unmodifiableMap(self);
}
/**
* A convenience method for creating an immutable sorted map
*
* @param self a SortedMap
* @return an immutable SortedMap
*/
public static SortedMap asImmutable(SortedMap self) {
return Collections.unmodifiableSortedMap(self);
}
/**
* A convenience method for creating an immutable list
*
* @param self a List
* @return an immutable List
*/
public static List asImmutable(List self) {
return Collections.unmodifiableList(self);
}
/**
* A convenience method for creating an immutable list
*
* @param self a Set
* @return an immutable Set
*/
public static Set asImmutable(Set self) {
return Collections.unmodifiableSet(self);
}
/**
* A convenience method for creating an immutable sorted set
*
* @param self a SortedSet
* @return an immutable SortedSet
*/
public static SortedSet asImmutable(SortedSet self) {
return Collections.unmodifiableSortedSet(self);
}
/**
* A convenience method for creating an immutable Collection
*
* @param self a Collection
* @return an immutable Collection
*/
public static Collection asImmutable(Collection self) {
return Collections.unmodifiableCollection(self);
}
/**
* A convenience method for creating a synchronized Map
*
* @param self a Map
* @return a synchronized Map
*/
public static Map asSynchronized(Map self) {
return Collections.synchronizedMap(self);
}
/**
* A convenience method for creating a synchronized SortedMap
*
* @param self a SortedMap
* @return a synchronized SortedMap
*/
public static SortedMap asSynchronized(SortedMap self) {
return Collections.synchronizedSortedMap(self);
}
/**
* A convenience method for creating a synchronized Collection
*
* @param self a Collection
* @return a synchronized Collection
*/
public static Collection asSynchronized(Collection self) {
return Collections.synchronizedCollection(self);
}
/**
* A convenience method for creating a synchronized List
*
* @param self a List
* @return a synchronized List
*/
public static List asSynchronized(List self) {
return Collections.synchronizedList(self);
}
/**
* A convenience method for creating a synchronized Set
*
* @param self a Set
* @return a synchronized Set
*/
public static Set asSynchronized(Set self) {
return Collections.synchronizedSet(self);
}
/**
* A convenience method for creating a synchronized SortedSet
*
* @param self a SortedSet
* @return a synchronized SortedSet
*/
public static SortedSet asSynchronized(SortedSet self) {
return Collections.synchronizedSortedSet(self);
}
/**
* Returns the converted <code>SpreadList</code> of the given <code>self</code>.
* <p>
* This is the same method to <code>toSpreadList(List self)</code>.
* <p>
* For examples, if there is defined a function like as
* <blockquote><pre>
* def fn(a, b, c, d) { return a + b + c + d }
* </pre></blockquote>, then all of the following three have the same meaning.
* <blockquote><pre>
* println fn(1, [2, 3].spread(), 4)
* println fn(1, *[2, 3], 4)
* println fn(1, 2, 3, 4)
* </pre></blockquote>
* <p>
* </pre><br>
*
* @param self a list to be converted into a spreadlist
* @return a newly created SpreadList if this list is not null and its size is positive.
*/
public static SpreadList spread(List self) {
return toSpreadList(self);
}
/**
* Returns the converted <code>SpreadList</code> of the given <code>self</code>.
* <p>
* This is the same method to <code>toSpreadList(Object[] self)</code>.
* <p>
* For examples, if there is defined a function like as
* <blockquote><pre>
* def fn(a, b, c, d) { return a + b + c + d }
* </pre></blockquote>, then all of the following three have the same meaning.
* <blockquote><pre>
* println fn(([1, 2, 3] as Object[]).spread(), 4)
* println fn(*[1, 2, 3], 4)
* println fn(1, 2, 3, 4)
* </pre></blockquote>
* <p>
* @param self an array of objects to be converted into a spreadlist
* @return a newly created SpreadList if this array is not null and its size is positive.
*/
public static SpreadList spread(Object[] self) {
return toSpreadList(self);
}
/**
* Returns the converted <code>SpreadList</code> of the given <code>self</code>.
* <p>
* For examples, if there is defined a function like as
* <blockquote><pre>
* def fn(a, b, c, d) { return a + b + c + d }
* </pre></blockquote>, then all of the following three have the same meaning.
* <blockquote><pre>
* println fn(1, [2, 3].toSpreadList(), 4)
* println fn(1, *[2, 3], 4)
* println fn(1, 2, 3, 4)
* </pre></blockquote>
* <p>
* @param self a list to be converted into a spreadlist
* @return a newly created SpreadList if this list is not null and its size is positive.
*/
public static SpreadList toSpreadList(List self) {
if (self == null)
throw new GroovyRuntimeException("Fail to convert Object[] to SpreadList, because it is null.");
else
return toSpreadList(self.toArray());
}
/**
* Returns the converted <code>SpreadList</code> of the given <code>self</code>.
* <p>
* For examples, if there is defined a function like as
* <blockquote><pre>
* def fn(a, b, c, d) { return a + b + c + d }
* </pre></blockquote>, then all of the following three have the same meaning.
* <blockquote><pre>
* println fn(([1, 2, 3] as Object[]).toSpreadList(), 4)
* println fn(*[1, 2, 3], 4)
* println fn(1, 2, 3, 4)
* </pre></blockquote>
* <p>
* @param self an array of objects to be converted into a spreadlist
* @return a newly created SpreadList if this array is not null and its size is positive.
*/
public static SpreadList toSpreadList(Object[] self) {
if (self == null)
throw new GroovyRuntimeException("Fail to convert Object[] to SpreadList, because it is null.");
else if (self.length == 0)
throw new GroovyRuntimeException("Fail to convert Object[] to SpreadList, because its length is 0.");
else
return new SpreadList(self);
}
public static SpreadMap spread(Map self) {
return toSpreadMap(self);
}
/**
* Returns the converted <code>SpreadList</code> of the given <code>self</code>.
* <p>
* For examples, if there is defined a function like as
* <blockquote><pre>
* def fn(a, b, c, d) { return a + b + c + d }
* </pre></blockquote>, then all of the following three have the same meaning.
* <blockquote><pre>
* println fn(a:1, [b:2, c:3].toSpreadMap(), d:4)
* println fn(a:1, *:[b:2, c:3], d:4)
* println fn(a:1, b:2, c:3, d:4)
* </pre></blockquote>
* <p>
* @param self a list to be converted into a spreadlist
* @return a newly created SpreadList if this list is not null and its size is positive.
*/
public static SpreadMap toSpreadMap(Map self) {
if (self == null)
throw new GroovyRuntimeException("Fail to convert Map to SpreadMap, because it is null.");
else
return new SpreadMap(self);
}
public static SpreadMap toSpreadMap(Object[] self) {
if (self == null)
throw new GroovyRuntimeException("Fail to convert Object[] to SpreadMap, because it is null.");
else if (self.length % 2 != 0)
throw new GroovyRuntimeException("Fail to convert Object[] to SpreadMap, because it's size is not even.");
else
return new SpreadMap(self);
}
/**
* Sorts the given collection into a sorted list
*
* @param self the collection to be sorted
* @return the sorted collection as a List
*/
public static List sort(Collection self) {
List answer = asList(self);
Collections.sort(answer);
return answer;
}
/**
* Avoids doing unnecessary work when sorting an already sorted set
*
* @param self
* @return the sorted set
*/
public static SortedSet sort(SortedSet self) {
return self;
}
/**
* A convenience method for sorting a List
*
* @param self a List to be sorted
* @return the sorted List
*/
public static List sort(List self) {
Collections.sort(self);
return self;
}
/**
* Removes the last item from the List. Using add() and pop()
* is similar to push and pop on a Stack.
*
* @param self a List
* @return the item removed from the List
* @throws UnsupportedOperationException if the list is empty and you try to pop() it.
*/
public static Object pop(List self) {
if (self.isEmpty()) {
throw new UnsupportedOperationException("Cannot pop() an empty List");
}
return self.remove(self.size() - 1);
}
/**
* A convenience method for sorting a List with a specific comparator
*
* @param self a List
* @param comparator a Comparator used for the comparison
* @return a sorted List
*/
public static List sort(List self, Comparator comparator) {
Collections.sort(self, comparator);
return self;
}
/**
* A convenience method for sorting a Collection with a specific comparator
*
* @param self a collection to be sorted
* @param comparator a Comparator used for the comparison
* @return a newly created sorted List
*/
public static List sort(Collection self, Comparator comparator) {
return sort(asList(self), comparator);
}
/**
* A convenience method for sorting a List using a closure as a comparator
*
* @param self a List
* @param closure a Closure used as a comparator
* @return a sorted List
*/
public static List sort(List self, Closure closure) {
// use a comparator of one item or two
Class[] params = closure.getParameterTypes();
if (params.length == 1) {
Collections.sort(self, new OrderBy(closure));
} else {
Collections.sort(self, new ClosureComparator(closure));
}
return self;
}
/**
* A convenience method for sorting a Collection using a closure as a comparator
*
* @param self a Collection to be sorted
* @param closure a Closure used as a comparator
* @return a newly created sorted List
*/
public static List sort(Collection self, Closure closure) {
return sort(asList(self), closure);
}
/**
* Converts the given collection into a List
*
* @param self a collection to be converted into a List
* @return a newly created List if this collection is not already a List
*/
public static List asList(Collection self) {
if (self instanceof List) {
return (List) self;
} else {
return new ArrayList(self);
}
}
/**
* Reverses the list
*
* @param self a List
* @return a reversed List
*/
public static List reverse(List self) {
int size = self.size();
List answer = new ArrayList(size);
ListIterator iter = self.listIterator(size);
while (iter.hasPrevious()) {
answer.add(iter.previous());
}
return answer;
}
/**
* Create a List as a union of both Collections
*
* @param left the left Collection
* @param right the right Collection
* @return a List
*/
public static List plus(Collection left, Collection right) {
List answer = new ArrayList(left.size() + right.size());
answer.addAll(left);
answer.addAll(right);
return answer;
}
/**
* Create a List as a union of a Collection and an Object
*
* @param left a Collection
* @param right an object to append
* @return a List
*/
public static List plus(Collection left, Object right) {
List answer = new ArrayList(left.size() + 1);
answer.addAll(left);
answer.add(right);
return answer;
}
/**
* Create a List composed of the same elements repeated a certain number of times.
*
* @param self a Collection
* @param factor the number of times to append
* @return a List
*/
public static List multiply(Collection self, Number factor) {
int size = factor.intValue();
List answer = new ArrayList(self.size() * size);
for (int i = 0; i < size; i++) {
answer.addAll(self);
}
return answer;
}
/**
* Create a List composed of the intersection of both collections
*
* @param left a List
* @param right a Collection
* @return a List as an intersection of both collections
*/
public static List intersect(List left, Collection right) {
if (left.size() == 0)
return new ArrayList();
boolean nlgnSort = sameType(new Collection[]{left, right});
ArrayList result = new ArrayList();
//creates the collection to look for values.
Collection pickFrom = nlgnSort ? (Collection) new TreeSet(left) : left;
for (Iterator iter = right.iterator(); iter.hasNext();) {
final Object o = iter.next();
if (pickFrom.contains(o))
result.add(o);
}
return result;
}
/**
* Create a List composed of the elements of the first list minus the elements of the collection
*
* @param self a List
* @param removeMe a Collection of elements to remove
* @return a List with the common elements removed
*/
public static List minus(List self, Collection removeMe) {
if (self.size() == 0)
return new ArrayList();
boolean nlgnSort = sameType(new Collection[]{self, removeMe});
//we can't use the same tactic as for intersection
//since AbstractCollection only does a remove on the first
//element it encounter.
if (nlgnSort && (self.get(0) instanceof Comparable)) {
//n*log(n) version
Set answer = new TreeSet(self);
answer.removeAll(removeMe);
return new ArrayList(answer);
} else {
//n*n version
List tmpAnswer = new LinkedList(self);
for (Iterator iter = tmpAnswer.iterator(); iter.hasNext();) {
Object element = iter.next();
//boolean removeElement = false;
for (Iterator iterator = removeMe.iterator(); iterator.hasNext();) {
if (element.equals(iterator.next())) {
iter.remove();
}
}
}
//remove duplicates
//can't use treeset since the base classes are different
List answer = new LinkedList();
Object[] array = tmpAnswer.toArray(new Object[tmpAnswer.size()]);
for (int i = 0; i < array.length; i++) {
if (array[i] != null) {
for (int j = i + 1; j < array.length; j++) {
if (array[i].equals(array[j])) {
array[j] = null;
}
}
answer.add(array[i]);
}
}
return new ArrayList(answer);
}
}
/**
* Flatten a list
*
* @param self a List
* @return a flattened List
*/
public static List flatten(List self) {
return new ArrayList(flatten(self, new LinkedList()));
}
/**
* Iterate over each element of the list in the reverse order.
*
* @param self a List
* @param closure a closure
*/
public static void reverseEach(List self, Closure closure) {
List reversed = reverse(self);
for (Iterator iter = reversed.iterator(); iter.hasNext();) {
closure.callSpecial(iter.next());
}
}
private static List flatten(Collection elements, List addTo) {
Iterator iter = elements.iterator();
while (iter.hasNext()) {
Object element = iter.next();
if (element instanceof Collection) {
flatten((Collection) element, addTo);
} else if (element instanceof Map) {
flatten(((Map) element).values(), addTo);
} else {
addTo.add(element);
}
}
return addTo;
}
/**
* Overloads the left shift operator to provide an easy way to append objects to a list
*
* @param self a Collection
* @param value an Object to be added to the collection.
* @return a Collection with an Object added to it.
*/
public static Collection leftShift(Collection self, Object value) {
self.add(value);
return self;
}
/**
* Overloads the left shift operator to provide an easy way to append multiple
* objects as string representations to a String
*
* @param self a String
* @param value an Obect
* @return a StringBuffer
*/
public static StringBuffer leftShift(String self, Object value) {
return new StringBuffer(self).append(value);
}
protected static StringWriter createStringWriter(String self) {
StringWriter answer = new StringWriter();
answer.write(self);
return answer;
}
protected static StringBufferWriter createStringBufferWriter(StringBuffer self) {
return new StringBufferWriter(self);
}
/**
* Overloads the left shift operator to provide an easy way to append multiple
* objects as string representations to a StringBuffer
*
* @param self a StringBuffer
* @param value a value to append
* @return a StringBuffer
*/
public static StringBuffer leftShift(StringBuffer self, Object value) {
self.append(value);
return self;
}
/**
* Overloads the left shift operator to provide an append mechanism to add things to a writer
*
* @param self a Writer
* @param value a value to append
* @return a StringWriter
*/
public static Writer leftShift(Writer self, Object value) throws IOException {
InvokerHelper.write(self, value);
return self;
}
/**
* Implementation of the left shift operator for integral types. Non integral
* Number types throw UnsupportedOperationException.
*/
public static Number leftShift(Number left, Number right) {
return NumberMath.leftShift(left, right);
}
/**
* Implementation of the right shift operator for integral types. Non integral
* Number types throw UnsupportedOperationException.
*/
public static Number rightShift(Number left, Number right) {
return NumberMath.rightShift(left, right);
}
/**
* Implementation of the right shift (unsigned) operator for integral types. Non integral
* Number types throw UnsupportedOperationException.
*/
public static Number rightShiftUnsigned(Number left, Number right) {
return NumberMath.rightShiftUnsigned(left, right);
}
/**
* A helper method so that dynamic dispatch of the writer.write(object) method
* will always use the more efficient Writable.writeTo(writer) mechanism if the
* object implements the Writable interface.
*
* @param self a Writer
* @param writable an object implementing the Writable interface
*/
public static void write(Writer self, Writable writable) throws IOException {
writable.writeTo(self);
}
/**
* Overloads the left shift operator to provide an append mechanism to add things to a stream
*
* @param self an OutputStream
* @param value a value to append
* @return a Writer
*/
public static Writer leftShift(OutputStream self, Object value) throws IOException {
OutputStreamWriter writer = new FlushingStreamWriter(self);
leftShift(writer, value);
return writer;
}
/**
* Overloads the left shift operator to provide an append mechanism to add bytes to a stream
*
* @param self an OutputStream
* @param value a value to append
* @return an OutputStream
*/
public static OutputStream leftShift(OutputStream self, byte[] value) throws IOException {
self.write(value);
self.flush();
return self;
}
private static boolean sameType(Collection[] cols) {
List all = new LinkedList();
for (int i = 0; i < cols.length; i++) {
all.addAll(cols[i]);
}
if (all.size() == 0)
return true;
Object first = all.get(0);
//trying to determine the base class of the collections
//special case for Numbers
Class baseClass;
if (first instanceof Number) {
baseClass = Number.class;
} else {
baseClass = first.getClass();
}
for (int i = 0; i < cols.length; i++) {
for (Iterator iter = cols[i].iterator(); iter.hasNext();) {
if (!baseClass.isInstance(iter.next())) {
return false;
}
}
}
return true;
}
// Primitive type array methods
//-------------------------------------------------------------------------
public static Object getAt(byte[] array, int idx) {
return primitiveArrayGet(array, idx);
}
public static Object getAt(char[] array, int idx) {
return primitiveArrayGet(array, idx);
}
public static Object getAt(short[] array, int idx) {
return primitiveArrayGet(array, idx);
}
public static Object getAt(int[] array, int idx) {
return primitiveArrayGet(array, idx);
}
public static Object getAt(long[] array, int idx) {
return primitiveArrayGet(array, idx);
}
public static Object getAt(float[] array, int idx) {
return primitiveArrayGet(array, idx);
}
public static Object getAt(double[] array, int idx) {
return primitiveArrayGet(array, idx);
}
public static Object getAt(boolean[] array, int idx) {
return primitiveArrayGet(array, idx);
}
public static Object getAt(byte[] array, Range range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(char[] array, Range range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(short[] array, Range range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(int[] array, Range range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(long[] array, Range range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(float[] array, Range range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(double[] array, Range range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(boolean[] array, Range range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(byte[] array, IntRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(char[] array, IntRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(short[] array, IntRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(int[] array, IntRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(long[] array, IntRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(float[] array, IntRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(double[] array, IntRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(boolean[] array, IntRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(byte[] array, ObjectRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(char[] array, ObjectRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(short[] array, ObjectRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(int[] array, ObjectRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(long[] array, ObjectRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(float[] array, ObjectRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(double[] array, ObjectRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(boolean[] array, ObjectRange range) {
return primitiveArrayGet(array, range);
}
public static Object getAt(byte[] array, Collection indices) {
return primitiveArrayGet(array, indices);
}
public static Object getAt(char[] array, Collection indices) {
return primitiveArrayGet(array, indices);
}
public static Object getAt(short[] array, Collection indices) {
return primitiveArrayGet(array, indices);
}
public static Object getAt(int[] array, Collection indices) {
return primitiveArrayGet(array, indices);
}
public static Object getAt(long[] array, Collection indices) {
return primitiveArrayGet(array, indices);
}
public static Object getAt(float[] array, Collection indices) {
return primitiveArrayGet(array, indices);
}
public static Object getAt(double[] array, Collection indices) {
return primitiveArrayGet(array, indices);
}
public static Object getAt(boolean[] array, Collection indices) {
return primitiveArrayGet(array, indices);
}
public static void putAt(boolean[] array, int idx, Boolean newValue) {
primitiveArrayPut(array, idx, newValue);
}
public static void putAt(byte[] array, int idx, Object newValue) {
if (!(newValue instanceof Byte)) {
Number n = (Number) newValue;
newValue = new Byte(n.byteValue());
}
primitiveArrayPut(array, idx, newValue);
}
public static void putAt(char[] array, int idx, Object newValue) {
if (newValue instanceof String) {
String s = (String) newValue;
if (s.length()!=1) throw new IllegalArgumentException("String of length 1 expected but got a bigger one");
char c = s.charAt(0);
newValue = new Character(c);
}
primitiveArrayPut(array, idx, newValue);
}
public static void putAt(short[] array, int idx, Object newValue) {
if (!(newValue instanceof Short)) {
Number n = (Number) newValue;
newValue = new Short(n.shortValue());
}
primitiveArrayPut(array, idx, newValue);
}
public static void putAt(int[] array, int idx, Object newValue) {
if (!(newValue instanceof Integer)) {
Number n = (Number) newValue;
newValue = new Integer(n.intValue());
}
primitiveArrayPut(array, idx, newValue);
}
public static void putAt(long[] array, int idx, Object newValue) {
if (!(newValue instanceof Long)) {
Number n = (Number) newValue;
newValue = new Long(n.longValue());
}
primitiveArrayPut(array, idx, newValue);
}
public static void putAt(float[] array, int idx, Object newValue) {
if (!(newValue instanceof Float)) {
Number n = (Number) newValue;
newValue = new Float(n.floatValue());
}
primitiveArrayPut(array, idx, newValue);
}
public static void putAt(double[] array, int idx, Object newValue) {
if (!(newValue instanceof Double)) {
Number n = (Number) newValue;
newValue = new Double(n.doubleValue());
}
primitiveArrayPut(array, idx, newValue);
}
public static int size(byte[] array) {
return Array.getLength(array);
}
public static int size(char[] array) {
return Array.getLength(array);
}
public static int size(short[] array) {
return Array.getLength(array);
}
public static int size(int[] array) {
return Array.getLength(array);
}
public static int size(long[] array) {
return Array.getLength(array);
}
public static int size(float[] array) {
return Array.getLength(array);
}
public static int size(double[] array) {
return Array.getLength(array);
}
public static List toList(byte[] array) {
return InvokerHelper.primitiveArrayToList(array);
}
public static List toList(char[] array) {
return InvokerHelper.primitiveArrayToList(array);
}
public static List toList(short[] array) {
return InvokerHelper.primitiveArrayToList(array);
}
public static List toList(int[] array) {
return InvokerHelper.primitiveArrayToList(array);
}
public static List toList(long[] array) {
return InvokerHelper.primitiveArrayToList(array);
}
public static List toList(float[] array) {
return InvokerHelper.primitiveArrayToList(array);
}
public static List toList(double[] array) {
return InvokerHelper.primitiveArrayToList(array);
}
private static final char[] tTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
public static Writable encodeBase64(final Byte[] data) {
return encodeBase64(InvokerHelper.convertToByteArray(data));
}
/**
* Produce a Writable object which writes the base64 encoding of the byte array
* Calling toString() on the result rerurns the encoding as a String
*
* @param data byte array to be encoded
* @return object which will write the base64 encoding of the byte array
*/
public static Writable encodeBase64(final byte[] data) {
return new Writable() {
public Writer writeTo(final Writer writer) throws IOException {
int charCount = 0;
final int dLimit = (data.length / 3) * 3;
for (int dIndex = 0; dIndex != dLimit; dIndex += 3) {
int d = ((data[dIndex] & 0XFF) << 16) | ((data[dIndex + 1] & 0XFF) << 8) | (data[dIndex + 2] & 0XFF);
writer.write(tTable[d >> 18]);
writer.write(tTable[(d >> 12) & 0X3F]);
writer.write(tTable[(d >> 6) & 0X3F]);
writer.write(tTable[d & 0X3F]);
if (++charCount == 18) {
writer.write('\n');
charCount = 0;
}
}
if (dLimit != data.length) {
int d = (data[dLimit] & 0XFF) << 16;
if (dLimit + 1 != data.length) {
d |= (data[dLimit + 1] & 0XFF) << 8;
}
writer.write(tTable[d >> 18]);
writer.write(tTable[(d >> 12) & 0X3F]);
writer.write((dLimit + 1 < data.length) ? tTable[(d >> 6) & 0X3F] : '=');
writer.write('=');
}
return writer;
}
public String toString() {
StringWriter buffer = new StringWriter();
try {
writeTo(buffer);
} catch (IOException e) {
throw new RuntimeException(e); // TODO: change this exception type
}
return buffer.toString();
}
};
}
private static final byte[] translateTable = (
//
"\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
// \t \n \r
+ "\u0042\u0042\u0041\u0041\u0042\u0042\u0041\u0042"
//
+ "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
//
+ "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
// sp ! " # $ % & '
+ "\u0041\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
// ( ) * + , - . /
+ "\u0042\u0042\u0042\u003E\u0042\u0042\u0042\u003F"
// 0 1 2 3 4 5 6 7
+ "\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B"
// 8 9 : ; < = > ?
+ "\u003C\u003D\u0042\u0042\u0042\u0040\u0042\u0042"
// @ A B C D E F G
+ "\u0042\u0000\u0001\u0002\u0003\u0004\u0005\u0006"
// H I J K L M N O
+ "\u0007\u0008\t\n\u000B\u000C\r\u000E"
// P Q R S T U V W
+ "\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016"
// X Y Z [ \ ] ^ _
+ "\u0017\u0018\u0019\u0042\u0042\u0042\u0042\u0042"
// ' a b c d e f g
+ "\u0042\u001A\u001B\u001C\u001D\u001E\u001F\u0020"
// h i j k l m n o p
+ "\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028"
// p q r s t u v w
+ "\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030"
// x y z
+ "\u0031\u0032\u0033").getBytes();
/**
* Decode the Sting from base64 into a byte array
*
* @param value the string to be decoded
* @return the decoded bytes as an array
*/
public static byte[] decodeBase64(final String value) {
int byteShift = 4;
int tmp = 0;
boolean done = false;
final StringBuffer buffer = new StringBuffer();
for (int i = 0; i != value.length(); i++) {
final char c = value.charAt(i);
final int sixBit = (c < 123) ? translateTable[c] : 66;
if (sixBit < 64) {
if (done) throw new RuntimeException("= character not at end of base64 value"); // TODO: change this exception type
tmp = (tmp << 6) | sixBit;
if (byteShift-- != 4) {
buffer.append((char) ((tmp >> (byteShift * 2)) & 0XFF));
}
} else if (sixBit == 64) {
byteShift--;
done = true;
} else if (sixBit == 66) {
// RFC 2045 says that I'm allowed to take the presence of
// these characters as evedence of data corruption
// So I will
throw new RuntimeException("bad character in base64 value"); // TODO: change this exception type
}
if (byteShift == 0) byteShift = 4;
}
try {
return buffer.toString().getBytes("ISO-8859-1");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Base 64 decode produced byte values > 255"); // TODO: change this exception type
}
}
/**
* Implements the getAt(int) method for primitve type arrays
*/
protected static Object primitiveArrayGet(Object array, int idx) {
return Array.get(array, normaliseIndex(idx, Array.getLength(array)));
}
/**
* Implements the getAt(Range) method for primitve type arrays
*/
protected static List primitiveArrayGet(Object array, Range range) {
List answer = new ArrayList();
for (Iterator iter = range.iterator(); iter.hasNext();) {
int idx = InvokerHelper.asInt(iter.next());
answer.add(primitiveArrayGet(array, idx));
}
return answer;
}
/**
* Implements the getAt(Collection) method for primitve type arrays
*/
protected static List primitiveArrayGet(Object self, Collection indices) {
List answer = new ArrayList();
for (Iterator iter = indices.iterator(); iter.hasNext();) {
Object value = iter.next();
if (value instanceof Range) {
answer.addAll(primitiveArrayGet(self, (Range) value));
} else if (value instanceof List) {
answer.addAll(primitiveArrayGet(self, (List) value));
} else {
int idx = InvokerHelper.asInt(value);
answer.add(primitiveArrayGet(self, idx));
}
}
return answer;
}
/**
* Implements the set(int idx) method for primitve type arrays
*/
protected static void primitiveArrayPut(Object array, int idx, Object newValue) {
Array.set(array, normaliseIndex(idx, Array.getLength(array)), newValue);
}
// String methods
//-------------------------------------------------------------------------
/**
* Converts the given string into a Character object
* using the first character in the string
*
* @param self a String
* @return the first Character
*/
public static Character toCharacter(String self) {
/** @todo use cache? */
return new Character(self.charAt(0));
}
/**
* Tokenize a String
*
* @param self a String
* @param token the delimiter
* @return a List of tokens
*/
public static List tokenize(String self, String token) {
return InvokerHelper.asList(new StringTokenizer(self, token));
}
/**
* Tokenize a String (with a whitespace as delimiter)
*
* @param self a String
* @return a List of tokens
*/
public static List tokenize(String self) {
return InvokerHelper.asList(new StringTokenizer(self));
}
/**
* Appends a String
*
* @param left a String
* @param value a String
* @return a String
*/
public static String plus(String left, Object value) {
//return left + value;
return left + toString(value);
}
/**
* Appends a String
*
* @param value a Number
* @param right a String
* @return a String
*/
public static String plus(Number value, String right) {
return toString(value) + right;
}
/**
* Appends a String
*
* @param left a StringBuffer
* @param value a String
* @return a String
*/
public static String plus(StringBuffer left, String value) {
return left + value;
}
/**
* Remove a part of a String
*
* @param left a String
* @param value a String part to remove
* @return a String minus the part to be removed
*/
public static String minus(String left, Object value) {
String text = toString(value);
return left.replaceFirst(text, "");
}
/**
* Provide an implementation of contains() like Collection to make Strings more polymorphic
* This method is not required on JDK 1.5 onwards
*
* @param self a String
* @param text a String to look for
* @return true if this string contains the given text
*/
public static boolean contains(String self, String text) {
int idx = self.indexOf(text);
return idx >= 0;
}
/**
* Count the number of occurencies of a substring
*
* @param self a String
* @param text a substring
* @return the number of occurrencies of the given string inside this String
*/
public static int count(String self, String text) {
int answer = 0;
for (int idx = 0; true; idx++) {
idx = self.indexOf(text, idx);
if (idx >= 0) {
++answer;
} else {
break;
}
}
return answer;
}
/**
* This method is called by the ++ operator for the class String.
* It increments the last character in the given string. If the
* character in the string is Character.MAX_VALUE a Character.MIN_VALUE
* will be appended. The empty string is incremented to a string
* consisting of the character Character.MIN_VALUE.
*
* @param self a String
* @return an incremented String
*/
public static String next(String self) {
StringBuffer buffer = new StringBuffer(self);
if (buffer.length()==0) {
buffer.append(Character.MIN_VALUE);
} else {
char last = buffer.charAt(buffer.length()-1);
if (last==Character.MAX_VALUE) {
buffer.append(Character.MIN_VALUE);
} else {
char next = last;
next++;
buffer.setCharAt(buffer.length()-1,next);
}
}
return buffer.toString();
}
/**
* This method is called by the -- operator for the class String.
* It decrements the last character in the given string. If the
* character in the string is Character.MIN_VALUE it will be deleted.
* The empty string can't be decremented.
*
* @param self a String
* @return a String with a decremented digit at the end
*/
public static String previous(String self) {
StringBuffer buffer = new StringBuffer(self);
if (buffer.length()==0) throw new IllegalArgumentException("the string is empty");
char last = buffer.charAt(buffer.length()-1);
if (last==Character.MIN_VALUE) {
buffer.deleteCharAt(buffer.length()-1);
} else {
char next = last;
next--;
buffer.setCharAt(buffer.length()-1,next);
}
return buffer.toString();
}
/**
* Executes the given string as a command line process. For more control
* over the process mechanism in JDK 1.5 you can use java.lang.ProcessBuilder.
*
* @param self a command line String
* @return the Process which has just started for this command line string
*/
public static Process execute(String self) throws IOException {
return Runtime.getRuntime().exec(self);
}
/**
* Executes the command specified by the <code>String</code> array that is the parameter.
* The first item in the array is the command the others are the parameters. For more
* control over the process mechanism in JDK 1.5 you can use
* <code>java.lang.ProcessBuilder</code>.
*
* @param commandArray an array of <code>String<code> containing the command name and
* parameters as separate items in the array.
* @return the Process which has just started for this command line string.
*/
public static Process execute ( final String[] commandArray ) throws IOException {
return Runtime.getRuntime ( ).exec ( commandArray ) ;
}
/**
* Executes the command specified by the <code>String</code> list that is the parameter.
* The first item in the array is the command the others are the parameters. All entries
* must be <code>String</code>s. For more control over the process mechanism in JDK 1.5 you
* can use <code>java.lang.ProcessBuilder</code>.
*
* @param commandList a list of <code>String<code> containing the command name and
* parameters as separate items in the list.
* @return the Process which has just started for this command line string.
*/
public static Process execute ( final List commandList ) throws IOException {
final String[] commandArray = new String[commandList.size ( )] ;
Iterator it = commandList.iterator ( ) ;
for ( int i = 0 ; it.hasNext ( ) ; ++i ) {
commandArray[i] = it.next ( ).toString ( ) ;
}
return execute ( commandArray ) ;
}
/**
* Repeat a String a certain number of times
*
* @param self a String to be repeated
* @param factor the number of times the String should be repeated
* @return a String composed of a repeatition
* @throws IllegalArgumentException if the number of repeatition is &lt; 0
*/
public static String multiply(String self, Number factor) {
int size = factor.intValue();
if (size == 0)
return "";
else if (size < 0) {
throw new IllegalArgumentException("multiply() should be called with a number of 0 or greater not: " + size);
}
StringBuffer answer = new StringBuffer(self);
for (int i = 1; i < size; i++) {
answer.append(self);
}
return answer.toString();
}
/**
* Returns the string representation of the given map with bracket boundaries.
*
* @param self a Map
* @return the string representation
*/
public static String toString(Map self) {
return toMapString(self);
}
/**
* Returns the string representation of the given map with bracket boundaries.
*
* @param self a Map
* @return the string representation
*/
public static String toMapString(Map self) {
return (self == null) ? "null" : InvokerHelper.toMapString(self);
}
/**
* Returns the string representation of the given collection with the bracket boundaries.
*
* @param self a Collection
* @return the string representation
*/
public static String toString(Collection self) {
return toListString(self);
}
/**
* Returns the string representation of the given collection with the bracket boundaries.
*
* @param self a Collection
* @return the string representation
*/
public static String toListString(Collection self) {
return (self == null) ? "null" : InvokerHelper.toListString(self);
}
/**
* Returns the string representation of the given array with the brace boundaries.
*
* @param self an Object[]
* @return the string representation
*/
public static String toString(Object[] self) {
return toArrayString(self);
}
/**
* Returns the string representation of the given array with the brace boundaries.
*
* @param self an Object[]
* @return the string representation
*/
public static String toArrayString(Object[] self) {
return (self == null) ? "null" : InvokerHelper.toArrayString(self);
}
protected static String toString(Object value) {
if (value instanceof Map)
return toMapString((Map)value);
else if (value instanceof Collection)
return toListString((Collection)value);
else if (value instanceof Object[])
return toArrayString((Object[])value);
return (value == null) ? "null" : value.toString();
}
// Number based methods
//-------------------------------------------------------------------------
/**
* Increment a Character by one
*
* @param self a Character
* @return an incremented Number
*/
public static Number next(Character self) {
return plus(self, ONE);
}
/**
* Increment a Number by one
*
* @param self a Number
* @return an incremented Number
*/
public static Number next(Number self) {
return plus(self, ONE);
}
/**
* Decrement a Character by one
*
* @param self a Character
* @return a decremented Number
*/
public static Number previous(Character self) {
return minus(self, ONE);
}
/**
* Decrement a Number by one
*
* @param self a Number
* @return a decremented Number
*/
public static Number previous(Number self) {
return minus(self, ONE);
}
/**
* Add a Character and a Number
*
* @param left a Character
* @param right a Number
* @return the addition of the Character and the Number
*/
public static Number plus(Character left, Number right) {
return plus(new Integer(left.charValue()), right);
}
/**
* Add a Number and a Character
*
* @param left a Number
* @param right a Character
* @return the addition of the Character and the Number
*/
public static Number plus(Number left, Character right) {
return plus(left, new Integer(right.charValue()));
}
/**
* Add two Characters
*
* @param left a Character
* @param right a Character
* @return the addition of both Characters
*/
public static Number plus(Character left, Character right) {
return plus(new Integer(left.charValue()), right);
}
/**
* Add two Numbers
*
* @param left a Number
* @param right another Number to add
* @return the addition of both Numbers
*/
public static Number plus(Number left, Number right) {
return NumberMath.add(left, right);
}
/**
* Compare a Character and a Number
*
* @param left a Character
* @param right a Number
* @return the result of the comparison
*/
public static int compareTo(Character left, Number right) {
return compareTo(new Integer(left.charValue()), right);
}
/**
* Compare a Number and a Character
*
* @param left a Number
* @param right a Character
* @return the result of the comparison
*/
public static int compareTo(Number left, Character right) {
return compareTo(left, new Integer(right.charValue()));
}
/**
* Compare two Characters
*
* @param left a Character
* @param right a Character
* @return the result of the comparison
*/
public static int compareTo(Character left, Character right) {
return compareTo(new Integer(left.charValue()), right);
}
/**
* Compare two Numbers
*
* @param left a Number
* @param right another Number to compare to
* @return the comparision of both numbers
*/
public static int compareTo(Number left, Number right) {
/** @todo maybe a double dispatch thing to handle new large numbers? */
return NumberMath.compareTo(left, right);
}
/**
* Subtract a Number from a Character
*
* @param left a Character
* @param right a Number
* @return the addition of the Character and the Number
*/
public static Number minus(Character left, Number right) {
return minus(new Integer(left.charValue()), right);
}
/**
* Subtract a Character from a Number
*
* @param left a Number
* @param right a Character
* @return the addition of the Character and the Number
*/
public static Number minus(Number left, Character right) {
return minus(left, new Integer(right.charValue()));
}
/**
* Subtraction two Characters
*
* @param left a Character
* @param right a Character
* @return the addition of both Characters
*/
public static Number minus(Character left, Character right) {
return minus(new Integer(left.charValue()), right);
}
/**
* Substraction of two Numbers
*
* @param left a Number
* @param right another Number to substract to the first one
* @return the substraction
*/
public static Number minus(Number left, Number right) {
return NumberMath.subtract(left, right);
}
/**
* Multiply a Character by a Number
*
* @param left a Character
* @param right a Number
* @return the multiplication of both
*/
public static Number multiply(Character left, Number right) {
return multiply(new Integer(left.charValue()), right);
}
/**
* Multiply a Number by a Character
*
* @param left a Number
* @param right a Character
* @return the multiplication of both
*/
public static Number multiply(Number left, Character right) {
return multiply(left, new Integer(right.charValue()));
}
/**
* Multiply two Characters
*
* @param left a Character
* @param right another Character
* @return the multiplication of both
*/
public static Number multiply(Character left, Character right) {
return multiply(new Integer(left.charValue()), right);
}
/**
* Multiply two Numbers
*
* @param left a Number
* @param right another Number
* @return the multiplication of both
*/
//Note: This method is NOT called if left AND right are both BigIntegers or BigDecimals because
//those classes implement a method with a better exact match.
public static Number multiply(Number left, Number right) {
return NumberMath.multiply(left, right);
}
/**
* Power of a Number to a certain exponent
*
* @param self a Number
* @param exponent a Number exponent
* @return a Number to the power of a certain exponent
*/
public static Number power(Number self, Number exponent) {
double base, exp, answer;
base = self.doubleValue();
exp = exponent.doubleValue();
answer = Math.pow(base, exp);
if ((double)((int)answer) == answer) {
return new Integer((int)answer);
}
else if ((double)((long)answer) == answer) {
return new Long((long)answer);
}
else {
return new Double(answer);
}
}
/**
* Divide a Character by a Number
*
* @param left a Character
* @param right a Number
* @return the multiplication of both
*/
public static Number div(Character left, Number right) {
return div(new Integer(left.charValue()), right);
}
/**
* Divide a Number by a Character
*
* @param left a Number
* @param right a Character
* @return the multiplication of both
*/
public static Number div(Number left, Character right) {
return div(left, new Integer(right.charValue()));
}
/**
* Divide two Characters
*
* @param left a Character
* @param right another Character
* @return the multiplication of both
*/
public static Number div(Character left, Character right) {
return div(new Integer(left.charValue()), right);
}
/**
* Divide two Numbers
*
* @param left a Number
* @param right another Number
* @return a Number resulting of the divide operation
*/
//Method name changed from 'divide' to avoid collision with BigInteger method that has
//different semantics. We want a BigDecimal result rather than a BigInteger.
public static Number div(Number left, Number right) {
return NumberMath.divide(left, right);
}
/**
* Integer Divide a Character by a Number
*
* @param left a Character
* @param right a Number
* @return the integer division of both
*/
public static Number intdiv(Character left, Number right) {
return intdiv(new Integer(left.charValue()), right);
}
/**
* Integer Divide a Number by a Character
*
* @param left a Number
* @param right a Character
* @return the integer division of both
*/
public static Number intdiv(Number left, Character right) {
return intdiv(left, new Integer(right.charValue()));
}
/**
* Integer Divide two Characters
*
* @param left a Character
* @param right another Character
* @return the integer division of both
*/
public static Number intdiv(Character left, Character right) {
return intdiv(new Integer(left.charValue()), right);
}
/**
* Integer Divide two Numbers
*
* @param left a Number
* @param right another Number
* @return a Number (an Integer) resulting of the integer division operation
*/
public static Number intdiv(Number left, Number right) {
return NumberMath.intdiv(left, right);
}
/**
* Bitwise OR together two numbers
*
* @param left a Number
* @param right another Number to bitwise OR
* @return the bitwise OR of both Numbers
*/
public static Number or(Number left, Number right) {
return NumberMath.or(left, right);
}
/**
* Bitwise AND together two Numbers
*
* @param left a Number
* @param right another Number to bitwse AND
* @return the bitwise AND of both Numbers
*/
public static Number and(Number left, Number right) {
return NumberMath.and(left, right);
}
/**
* Bitwise XOR together two Numbers
*
* @param left a Number
* @param right another Number to bitwse XOR
* @return the bitwise XOR of both Numbers
*/
public static Number xor(Number left, Number right) {
return NumberMath.xor(left, right);
}
/**
* Performs a division modulus operation
*
* @param left a Number
* @param right another Number to mod
* @return the modulus result
*/
public static Number mod(Number left, Number right) {
return NumberMath.mod(left, right);
}
/**
* Negates the number
*
* @param left a Number
* @return the negation of the number
*/
public static Number negate(Number left) {
return NumberMath.negate(left);
}
/**
* Iterates a number of times
*
* @param self a Number
* @param closure the closure to call a number of times
*/
public static void times(Number self, Closure closure) {
for (int i = 0, size = self.intValue(); i < size; i++) {
closure.callSpecial(new Integer(i));
if (closure.getDirective() == Closure.DONE) {
break;
}
}
}
/**
* Iterates from this number up to the given number
*
* @param self a Number
* @param to another Number to go up to
* @param closure the closure to call
*/
public static void upto(Number self, Number to, Closure closure) {
int self1 = self.intValue();
int to1 = to.intValue();
if (self1 <= to1) {
for (int i = self1; i <= to1; i++) {
closure.call(new Integer(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
public static void upto(long self, Number to, Closure closure) {
long to1 = to.longValue();
if (self <= to1) {
for (long i = self; i <= to1; i++) {
closure.callSpecial(new Long(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
public static void upto(Long self, Number to, Closure closure) {
long self1 = self.longValue();
long to1 = to.longValue();
if (self1 <= to1) {
for (long i = self1; i <= to1; i++) {
closure.callSpecial(new Long(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
public static void upto(float self, Number to, Closure closure) {
float to1 = to.floatValue();
if (self <= to1) {
for (float i = self; i <= to1; i++) {
closure.callSpecial(new Float(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
public static void upto(Float self, Number to, Closure closure) {
float self1 = self.floatValue();
float to1 = to.floatValue();
if (self1 <= to1) {
for (float i = self1; i <= to1; i++) {
closure.callSpecial(new Float(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
public static void upto(Double self, Number to, Closure closure) {
double self1 = self.doubleValue();
double to1 = to.doubleValue();
if (self1 <= to1) {
for (double i = self1; i <= to1; i++) {
closure.callSpecial(new Double(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
public static void upto(BigInteger self, Number to, Closure closure) {
if (to instanceof BigDecimal) {
final BigDecimal one = new BigDecimal("1.0");
BigDecimal self1 = new BigDecimal(self);
BigDecimal to1 = (BigDecimal) to;
if (self1.compareTo(to1) <= 0) {
for (BigDecimal i = self1; i.compareTo(to1) <= 0; i = i.add(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
else if (to instanceof BigInteger) {
final BigInteger one = new BigInteger("1");
BigInteger to1 = (BigInteger) to;
if (self.compareTo(to1) <= 0) {
for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
else {
final BigInteger one = new BigInteger("1");
BigInteger to1 = new BigInteger("" + to);
if (self.compareTo(to1) <= 0) {
for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
closure.call(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
}
public static void upto(BigDecimal self, Number to, Closure closure) {
final BigDecimal one = new BigDecimal("1.0");
if (to instanceof BigDecimal) {
BigDecimal to1 = (BigDecimal) to;
if (self.compareTo(to1) <= 0) {
for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
else if (to instanceof BigInteger) {
BigDecimal to1 = new BigDecimal((BigInteger) to);
if (self.compareTo(to1) <= 0) {
for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
else {
BigDecimal to1 = new BigDecimal("" + to);
if (self.compareTo(to1) <= 0) {
for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
}
}
/**
* Iterates from this number down to the given number
*
* @param self a Number
* @param to another Number to go down to
* @param closure the closure to call
*/
public static void downto(Number self, Number to, Closure closure) {
int self1 = self.intValue();
int to1 = to.intValue();
if (self1 >= to1) {
for (int i = self1; i >= to1; i--) {
closure.callSpecial(new Integer(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
public static void downto(long self, Number to, Closure closure) {
long to1 = to.longValue();
if (self >= to1) {
for (long i = self; i >= to1; i--) {
closure.callSpecial(new Long(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
public static void downto(Long self, Number to, Closure closure) {
long self1 = self.longValue();
long to1 = to.longValue();
if (self1 >= to1) {
for (long i = self1; i >= to1; i--) {
closure.callSpecial(new Long(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
public static void downto(float self, Number to, Closure closure) {
float to1 = to.floatValue();
if (self >= to1) {
for (float i = self; i >= to1; i--) {
closure.callSpecial(new Float(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
public static void downto(Float self, Number to, Closure closure) {
float self1 = self.floatValue();
float to1 = to.floatValue();
if (self1 >= to1) {
for (float i = self1; i >= to1; i--) {
closure.callSpecial(new Float(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
public static void downto(double self, Number to, Closure closure) {
double to1 = to.doubleValue();
if (self >= to1) {
for (double i = self; i >= to1; i--) {
closure.callSpecial(new Double(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
public static void downto(Double self, Number to, Closure closure) {
double self1 = self.doubleValue();
double to1 = to.doubleValue();
if (self1 >= to1) {
for (double i = self1; i >= to1; i--) {
closure.callSpecial(new Double(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
public static void downto(BigInteger self, Number to, Closure closure) {
if (to instanceof BigDecimal) {
final BigDecimal one = new BigDecimal("1.0");
BigDecimal to1 = (BigDecimal) to;
if (self.compareTo(to1) >= 0) {
for (BigDecimal i = new BigDecimal(self); i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
else if (to instanceof BigInteger) {
final BigInteger one = new BigInteger("1");
BigInteger to1 = (BigInteger) to;
if (self.compareTo(to1) >= 0) {
for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
else {
final BigInteger one = new BigInteger("1");
BigInteger to1 = new BigInteger("" + to);
if (self.compareTo(to1) >= 0) {
for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
}
public static void downto(BigDecimal self, Number to, Closure closure) {
final BigDecimal one = new BigDecimal("1.0");
if (to instanceof BigDecimal) {
BigDecimal to1 = (BigDecimal) to;
if (self.compareTo(to1) >= 0) {
for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
else if (to instanceof BigInteger) {
BigDecimal to1 = new BigDecimal((BigInteger) to);
if (self.compareTo(to1) >= 0) {
for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
}
else {
BigDecimal to1 = new BigDecimal("" + to);
if (self.compareTo(to1) >= 0) {
for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self +".downto(" + to +")");
}
}
/**
* Iterates from this number up to the given number using a step increment
*
* @param self a Number to start with
* @param to a Number to go up to
* @param stepNumber a Number representing the step increment
* @param closure the closure to call
*/
public static void step(Number self, Number to, Number stepNumber, Closure closure) {
if (self instanceof BigDecimal || to instanceof BigDecimal || stepNumber instanceof BigDecimal) {
final BigDecimal zero = new BigDecimal("0.0");
BigDecimal self1 = (self instanceof BigDecimal) ? (BigDecimal) self : new BigDecimal("" + self);
BigDecimal to1 = (to instanceof BigDecimal) ? (BigDecimal) to : new BigDecimal("" + to);
BigDecimal stepNumber1 = (stepNumber instanceof BigDecimal) ? (BigDecimal) stepNumber : new BigDecimal("" + stepNumber);
if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) {
for (BigDecimal i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) {
closure.callSpecial(i);
}
}
else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) {
for (BigDecimal i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")");
}
else if (self instanceof BigInteger || to instanceof BigInteger || stepNumber instanceof BigInteger) {
final BigInteger zero = new BigInteger("0");
BigInteger self1 = (self instanceof BigInteger) ? (BigInteger) self : new BigInteger("" + self);
BigInteger to1 = (to instanceof BigInteger) ? (BigInteger) to : new BigInteger("" + to);
BigInteger stepNumber1 = (stepNumber instanceof BigInteger) ? (BigInteger) stepNumber : new BigInteger("" + stepNumber);
if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) {
for (BigInteger i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) {
closure.callSpecial(i);
}
}
else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) {
for (BigInteger i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) {
closure.callSpecial(i);
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")");
}
else {
int self1 = self.intValue();
int to1 = to.intValue();
int stepNumber1 = stepNumber.intValue();
if (stepNumber1 > 0 && to1 > self1) {
for (int i = self1; i < to1; i += stepNumber1) {
closure.callSpecial(new Integer(i));
}
}
else if (stepNumber1 < 0 && to1 < self1) {
for (int i = self1; i > to1; i += stepNumber1) {
closure.callSpecial(new Integer(i));
}
}
else
throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")");
}
}
/**
* Get the absolute value
*
* @param number a Number
* @return the absolute value of that Number
*/
//Note: This method is NOT called if number is a BigInteger or BigDecimal because
//those classes implement a method with a better exact match.
public static int abs(Number number) {
return Math.abs(number.intValue());
}
/**
* Get the absolute value
*
* @param number a Long
* @return the absolute value of that Long
*/
public static long abs(Long number) {
return Math.abs(number.longValue());
}
/**
* Get the absolute value
*
* @param number a Float
* @return the absolute value of that Float
*/
public static float abs(Float number) {
return Math.abs(number.floatValue());
}
/**
* Get the absolute value
*
* @param number a Double
* @return the absolute value of that Double
*/
public static double abs(Double number) {
return Math.abs(number.doubleValue());
}
/**
* Get the absolute value
*
* @param number a Float
* @return the absolute value of that Float
*/
public static int round(Float number) {
return Math.round(number.floatValue());
}
/**
* Round the value
*
* @param number a Double
* @return the absolute value of that Double
*/
public static long round(Double number) {
return Math.round(number.doubleValue());
}
/**
* Parse a String into an Integer
*
* @param self a String
* @return an Integer
*/
public static Integer toInteger(String self) {
return Integer.valueOf(self);
}
/**
* Parse a String into a Long
*
* @param self a String
* @return a Long
*/
public static Long toLong(String self) {
return Long.valueOf(self);
}
/**
* Parse a String into a Float
*
* @param self a String
* @return a Float
*/
public static Float toFloat(String self) {
return Float.valueOf(self);
}
/**
* Parse a String into a Double
*
* @param self a String
* @return a Double
*/
public static Double toDouble(String self) {
return Double.valueOf(self);
}
/**
* Transform a Number into an Integer
*
* @param self a Number
* @return an Integer
*/
public static Integer toInteger(Number self) {
return new Integer(self.intValue());
}
// Date methods
//-------------------------------------------------------------------------
/**
* Increments a Date by a day
*
* @param self a Date
* @return the next days date
*/
public static Date next(Date self) {
return plus(self, 1);
}
/**
* Decrement a Date by a day
*
* @param self a Date
* @return the previous days date
*/
public static Date previous(Date self) {
return minus(self, 1);
}
/**
* Adds a number of days to this date and returns the new date
*
* @param self a Date
* @param days the number of days to increase
* @return the new date
*/
public static Date plus(Date self, int days) {
Calendar calendar = (Calendar) Calendar.getInstance().clone();
calendar.setTime(self);
calendar.add(Calendar.DAY_OF_YEAR, days);
return calendar.getTime();
}
/**
* Subtracts a number of days from this date and returns the new date
*
* @param self a Date
* @return the new date
*/
public static Date minus(Date self, int days) {
return plus(self, -days);
}
// File and stream based methods
//-------------------------------------------------------------------------
/**
* Helper method to create an object input stream from the given file.
*
* @param file a file
* @return an object input stream
* @throws FileNotFoundException
* @throws IOException
*/
public static ObjectInputStream newObjectInputStream(File file) throws FileNotFoundException, IOException {
return new ObjectInputStream(new FileInputStream(file));
}
/**
* Iterates through the given file object by object
*
* @param self a File
* @param closure a closure
* @throws IOException
* @throws ClassNotFoundException
*/
public static void eachObject(File self, Closure closure) throws IOException, ClassNotFoundException {
eachObject(newObjectInputStream(self), closure);
}
/**
* Iterates through the given object stream object by object
*
* @param self an ObjectInputStream
* @param closure a closure
* @throws IOException
* @throws ClassNotFoundException
*/
public static void eachObject(ObjectInputStream ois, Closure closure) throws IOException, ClassNotFoundException {
try {
while (true) {
try {
Object obj = ois.readObject();
// we allow null objects in the object stream
closure.callSpecial(new ParameterArray(obj));
} catch (EOFException e) {
break;
}
}
ois.close();
} catch (ClassNotFoundException e) {
try {
ois.close();
} catch (Exception e2) {
// ignore as we're already throwing
}
throw e;
} catch (IOException e) {
try {
ois.close();
} catch (Exception e2) {
// ignore as we're already throwing
}
throw e;
}
}
/**
* Iterates through the given file line by line
*
* @param self a File
* @param closure a closure
* @throws IOException
*/
public static void eachLine(File self, Closure closure) throws IOException {
eachLine(newReader(self), closure);
}
/**
* Iterates through the given reader line by line
*
* @param self a Reader
* @param closure a closure
* @throws IOException
*/
public static void eachLine(Reader self, Closure closure) throws IOException {
BufferedReader br = null;
if (self instanceof BufferedReader)
br = (BufferedReader) self;
else
br = new BufferedReader(self);
try {
while (true) {
String line = br.readLine();
if (line == null) {
break;
} else {
closure.callSpecial(line);
}
}
br.close();
} catch (IOException e) {
if (self != null) {
try {
br.close();
} catch (Exception e2) {
// ignore as we're already throwing
}
throw e;
}
}
}
/**
* Iterates through the given file line by line, splitting on the seperator
*
* @param self a File
* @param sep a String separator
* @param closure a closure
* @throws IOException
*/
public static void splitEachLine(File self, String sep, Closure closure) throws IOException {
splitEachLine(newReader(self), sep, closure);
}
/**
* Iterates through the given reader line by line, splitting on the seperator
*
* @param self a Reader
* @param sep a String separator
* @param closure a closure
* @throws IOException
*/
public static void splitEachLine(Reader self, String sep, Closure closure) throws IOException {
BufferedReader br = null;
if (self instanceof BufferedReader)
br = (BufferedReader) self;
else
br = new BufferedReader(self);
List args = new ArrayList();
try {
while (true) {
String line = br.readLine();
if (line == null) {
break;
} else {
List vals = Arrays.asList(line.split(sep));
args.clear();
args.add(vals);
closure.callSpecial(new ParameterArray(args.toArray()));
}
}
br.close();
} catch (IOException e) {
if (self != null) {
try {
br.close();
} catch (Exception e2) {
// ignore as we're already throwing
}
throw e;
}
}
}
/**
* Read a single, whole line from the given Reader
*
* @param self a Reader
* @return a line
* @throws IOException
*/
public static String readLine(Reader self) throws IOException {
BufferedReader br = null;
if (self instanceof BufferedReader) {
br = (BufferedReader) self;
} else {
br = new BufferedReader(self);
}
return br.readLine();
}
/**
* Read a single, whole line from the given InputStream
*
* @param stream an InputStream
* @return a line
* @throws IOException
*/
public static String readLine(InputStream stream) throws IOException {
return readLine(new InputStreamReader(stream));
}
/**
* Reads the file into a list of Strings for each line
*
* @param file a File
* @return a List of lines
* @throws IOException
*/
public static List readLines(File file) throws IOException {
IteratorClosureAdapter closure = new IteratorClosureAdapter(file);
eachLine(file, closure);
return closure.asList();
}
/**
* Reads the content of the File opened with the specified encoding and returns it as a String
*
* @param file the file whose content we want to read
* @param charset the charset used to read the content of the file
* @return a String containing the content of the file
* @throws IOException
*/
public static String getText(File file, String charset) throws IOException {
BufferedReader reader = newReader(file, charset);
return getText(reader);
}
/**
* Reads the content of the File and returns it as a String
*
* @param file the file whose content we want to read
* @return a String containing the content of the file
* @throws IOException
*/
public static String getText(File file) throws IOException {
BufferedReader reader = newReader(file);
return getText(reader);
}
/**
* Reads the content of this URL and returns it as a String
*
* @param url URL to read content from
* @return the text from that URL
* @throws IOException
*/
public static String getText(URL url) throws IOException {
return getText(url, CharsetToolkit.getDefaultSystemCharset().toString());
}
/**
* Reads the content of this URL and returns it as a String
*
* @param url URL to read content from
* @param charset opens the stream with a specified charset
* @return the text from that URL
* @throws IOException
*/
public static String getText(URL url, String charset) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), charset));
return getText(reader);
}
/**
* Reads the content of this InputStream and returns it as a String
*
* @param is an input stream
* @return the text from that URL
* @throws IOException
*/
public static String getText(InputStream is) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
return getText(reader);
}
/**
* Reads the content of this InputStream with a specified charset and returns it as a String
*
* @param is an input stream
* @param charset opens the stream with a specified charset
* @return the text from that URL
* @throws IOException
*/
public static String getText(InputStream is, String charset) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
return getText(reader);
}
/**
* Reads the content of the Reader and returns it as a String
*
* @param reader a Reader whose content we want to read
* @return a String containing the content of the buffered reader
* @throws IOException
*/
public static String getText(Reader reader) throws IOException {
BufferedReader bufferedReader = new BufferedReader(reader);
return getText(bufferedReader);
}
/**
* Reads the content of the BufferedReader and returns it as a String
*
* @param reader a BufferedReader whose content we want to read
* @return a String containing the content of the buffered reader
* @throws IOException
*/
public static String getText(BufferedReader reader) throws IOException {
StringBuffer answer = new StringBuffer();
// reading the content of the file within a char buffer allow to keep the correct line endings
char[] charBuffer = new char[4096];
int nbCharRead = 0;
while ((nbCharRead = reader.read(charBuffer)) != -1) {
// appends buffer
answer.append(charBuffer, 0, nbCharRead);
}
reader.close();
return answer.toString();
}
/**
* Write the text and append a new line (depending on the platform line-ending)
*
* @param writer a BufferedWriter
* @param line the line to write
* @throws IOException
*/
public static void writeLine(BufferedWriter writer, String line) throws IOException {
writer.write(line);
writer.newLine();
}
/**
* Write the text to the File.
*
* @param file a File
* @param text the text to write to the File
* @throws IOException
*/
public static void write(File file, String text) throws IOException {
BufferedWriter writer = newWriter(file);
writer.write(text);
writer.close();
}
/**
* Write the text to the File with a specified encoding.
*
* @param file a File
* @param text the text to write to the File
* @param charset the charset used
* @throws IOException
*/
public static void write(File file, String text, String charset) throws IOException {
BufferedWriter writer = newWriter(file, charset);
writer.write(text);
writer.close();
}
/**
* Append the text at the end of the File
*
* @param file a File
* @param text the text to append at the end of the File
* @throws IOException
*/
public static void append(File file, String text) throws IOException {
BufferedWriter writer = newWriter(file, true);
writer.write(text);
writer.close();
}
/**
* Append the text at the end of the File with a specified encoding
*
* @param file a File
* @param text the text to append at the end of the File
* @param charset the charset used
* @throws IOException
*/
public static void append(File file, String text, String charset) throws IOException {
BufferedWriter writer = newWriter(file, charset, true);
writer.write(text);
writer.close();
}
/**
* Reads the reader into a list of Strings for each line
*
* @param reader a Reader
* @return a List of lines
* @throws IOException
*/
public static List readLines(Reader reader) throws IOException {
IteratorClosureAdapter closure = new IteratorClosureAdapter(reader);
eachLine(reader, closure);
return closure.asList();
}
/**
* This method is used to throw useful exceptions when the eachFile* and eachDir closure methods
* are used incorrectly.
*
* @param dir The directory to check
* @throws FileNotFoundException Thrown if the given directory does not exist
* @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
*/
private static void checkDir(File dir) throws FileNotFoundException, IllegalArgumentException {
if (!dir.exists())
throw new FileNotFoundException(dir.getAbsolutePath());
if (!dir.isDirectory())
throw new IllegalArgumentException("The provided File object is not a directory: " + dir.getAbsolutePath());
}
/**
* Invokes the closure for each file in the given directory
*
* @param self a File
* @param closure a closure
* @throws FileNotFoundException Thrown if the given directory does not exist
* @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
*/
public static void eachFile(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
checkDir(self);
File[] files = self.listFiles();
for (int i = 0; i < files.length; i++) {
closure.callSpecial(files[i]);
}
}
/**
* Invokes the closure for each file in the given directory and recursively.
* It is a depth-first exploration, directories are included in the search.
*
* @param self a File
* @param closure a closure
* @throws FileNotFoundException Thrown if the given directory does not exist
* @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
*/
public static void eachFileRecurse(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
checkDir(self);
File[] files = self.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
closure.callSpecial(files[i]);
eachFileRecurse(files[i], closure);
} else {
closure.callSpecial(files[i]);
}
}
}
/**
* Invokes the closure for each directory in the given directory,
* ignoring regular files.
*
* @param self a directory
* @param closure a closure
* @throws FileNotFoundException Thrown if the given directory does not exist
* @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
*/
public static void eachDir(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
checkDir(self);
File[] files = self.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {
closure.callSpecial(files[i]);
}
}
}
/**
* Invokes the closure for each file matching the given filter in the given directory
* - calling the isCase() method used by switch statements. This method can be used
* with different kinds of filters like regular expresions, classes, ranges etc.
*
* @param self a file
* @param filter the filter to perform on the directory (using the isCase(object) method)
* @param closure
* @throws FileNotFoundException Thrown if the given directory does not exist
* @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
*/
public static void eachFileMatch(File self, Object filter, Closure closure) throws FileNotFoundException, IllegalArgumentException {
checkDir(self);
File[] files = self.listFiles();
MetaClass metaClass = InvokerHelper.getMetaClass(filter);
for (int i = 0; i < files.length; i++) {
if (InvokerHelper.asBool(metaClass.invokeMethod(filter, "isCase", files[i].getName()))) {
closure.callSpecial(files[i]);
}
}
}
/**
* Allow simple syntax for using timers.
*
* @param timer a timer object
* @param delay the delay in milliseconds before running the closure code
* @param closure
*/
public static void runAfter(Timer timer, int delay, final Closure closure) {
TimerTask timerTask = new TimerTask() {
public void run() {
closure.call();
}
};
timer.schedule(timerTask, delay);
}
/**
* Helper method to create a buffered reader for a file
*
* @param file a File
* @return a BufferedReader
* @throws IOException
*/
public static BufferedReader newReader(File file) throws IOException {
CharsetToolkit toolkit = new CharsetToolkit(file);
return toolkit.getReader();
}
/**
* Helper method to create a buffered reader for a file, with a specified charset
*
* @param file a File
* @param charset the charset with which we want to write in the File
* @return a BufferedReader
* @throws FileNotFoundException if the File was not found
* @throws UnsupportedEncodingException if the encoding specified is not supported
*/
public static BufferedReader newReader(File file, String charset)
throws FileNotFoundException, UnsupportedEncodingException {
return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
}
/**
* Provides a reader for an arbitrary input stream
*
* @param self an input stream
* @return a reader
*/
public static BufferedReader newReader(final InputStream self) {
return new BufferedReader(new InputStreamReader(self));
}
/**
* Helper method to create a new BufferedReader for a file and then
* passes it into the closure and ensures its closed again afterwords
*
* @param file
* @throws FileNotFoundException
*/
public static void withReader(File file, Closure closure) throws IOException {
withReader(newReader(file), closure);
}
/**
* Helper method to create a buffered output stream for a file
*
* @param file
* @return
* @throws FileNotFoundException
*/
public static BufferedOutputStream newOutputStream(File file) throws IOException {
return new BufferedOutputStream(new FileOutputStream(file));
}
/**
* Helper method to create a new OutputStream for a file and then
* passes it into the closure and ensures its closed again afterwords
*
* @param file a File
* @throws FileNotFoundException
*/
public static void withOutputStream(File file, Closure closure) throws IOException {
withStream(newOutputStream(file), closure);
}
/**
* Helper method to create a new InputStream for a file and then
* passes it into the closure and ensures its closed again afterwords
*
* @param file a File
* @throws FileNotFoundException
*/
public static void withInputStream(File file, Closure closure) throws IOException {
withStream(newInputStream(file), closure);
}
/**
* Helper method to create a buffered writer for a file
*
* @param file a File
* @return a BufferedWriter
* @throws FileNotFoundException
*/
public static BufferedWriter newWriter(File file) throws IOException {
return new BufferedWriter(new FileWriter(file));
}
/**
* Helper method to create a buffered writer for a file in append mode
*
* @param file a File
* @param append true if in append mode
* @return a BufferedWriter
* @throws FileNotFoundException
*/
public static BufferedWriter newWriter(File file, boolean append) throws IOException {
return new BufferedWriter(new FileWriter(file, append));
}
/**
* Helper method to create a buffered writer for a file
*
* @param file a File
* @param charset the name of the encoding used to write in this file
* @param append true if in append mode
* @return a BufferedWriter
* @throws FileNotFoundException
*/
public static BufferedWriter newWriter(File file, String charset, boolean append) throws IOException {
if (append) {
return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charset));
} else {
// first write the Byte Order Mark for Unicode encodings
FileOutputStream stream = new FileOutputStream(file);
if ("UTF-16BE".equals(charset)) {
writeUtf16Bom(stream, true);
} else if ("UTF-16LE".equals(charset)) {
writeUtf16Bom(stream, false);
}
return new BufferedWriter(new OutputStreamWriter(stream, charset));
}
}
/**
* Helper method to create a buffered writer for a file
*
* @param file a File
* @param charset the name of the encoding used to write in this file
* @return a BufferedWriter
* @throws FileNotFoundException
*/
public static BufferedWriter newWriter(File file, String charset) throws IOException {
return newWriter(file, charset, false);
}
/**
* Write a Byte Order Mark at the begining of the file
*
* @param stream the FileOuputStream to write the BOM to
* @param bigEndian true if UTF 16 Big Endian or false if Low Endian
* @throws IOException
*/
private static void writeUtf16Bom(FileOutputStream stream, boolean bigEndian) throws IOException {
if (bigEndian) {
stream.write(-2);
stream.write(-1);
} else {
stream.write(-1);
stream.write(-2);
}
}
/**
* Helper method to create a new BufferedWriter for a file and then
* passes it into the closure and ensures it is closed again afterwords
*
* @param file a File
* @param closure a closure
* @throws FileNotFoundException
*/
public static void withWriter(File file, Closure closure) throws IOException {
withWriter(newWriter(file), closure);
}
/**
* Helper method to create a new BufferedWriter for a file in a specified encoding
* and then passes it into the closure and ensures it is closed again afterwords
*
* @param file a File
* @param charset the charset used
* @param closure a closure
* @throws FileNotFoundException
*/
public static void withWriter(File file, String charset, Closure closure) throws IOException {
withWriter(newWriter(file, charset), closure);
}
/**
* Helper method to create a new BufferedWriter for a file in a specified encoding
* in append mode and then passes it into the closure and ensures it is closed again afterwords
*
* @param file a File
* @param charset the charset used
* @param closure a closure
* @throws FileNotFoundException
*/
public static void withWriterAppend(File file, String charset, Closure closure) throws IOException {
withWriter(newWriter(file, charset, true), closure);
}
/**
* Helper method to create a new PrintWriter for a file
*
* @param file a File
* @throws FileNotFoundException
*/
public static PrintWriter newPrintWriter(File file) throws IOException {
return new PrintWriter(newWriter(file));
}
/**
* Helper method to create a new PrintWriter for a file with a specified charset
*
* @param file a File
* @param charset the charset
* @return a PrintWriter
* @throws FileNotFoundException
*/
public static PrintWriter newPrintWriter(File file, String charset) throws IOException {
return new PrintWriter(newWriter(file, charset));
}
/**
* Helper method to create a new PrintWriter for a file and then
* passes it into the closure and ensures its closed again afterwords
*
* @param file a File
* @throws FileNotFoundException
*/
public static void withPrintWriter(File file, Closure closure) throws IOException {
withWriter(newPrintWriter(file), closure);
}
/**
* Allows a writer to be used, calling the closure with the writer
* and then ensuring that the writer is closed down again irrespective
* of whether exceptions occur or the
*
* @param writer the writer which is used and then closed
* @param closure the closure that the writer is passed into
* @throws IOException
*/
public static void withWriter(Writer writer, Closure closure) throws IOException {
try {
closure.callSpecial(writer);
// lets try close the writer & throw the exception if it fails
// but not try to reclose it in the finally block
Writer temp = writer;
writer = null;
temp.close();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
log.warning("Caught exception closing writer: " + e);
}
}
}
}
/**
* Allows a Reader to be used, calling the closure with the writer
* and then ensuring that the writer is closed down again irrespective
* of whether exceptions occur or the
*
* @param writer the writer which is used and then closed
* @param closure the closure that the writer is passed into
* @throws IOException
*/
public static void withReader(Reader writer, Closure closure) throws IOException {
try {
closure.callSpecial(writer);
// lets try close the writer & throw the exception if it fails
// but not try to reclose it in the finally block
Reader temp = writer;
writer = null;
temp.close();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
log.warning("Caught exception closing writer: " + e);
}
}
}
}
/**
* Allows a InputStream to be used, calling the closure with the stream
* and then ensuring that the stream is closed down again irrespective
* of whether exceptions occur or the
*
* @param stream the stream which is used and then closed
* @param closure the closure that the stream is passed into
* @throws IOException
*/
public static void withStream(InputStream stream, Closure closure) throws IOException {
try {
closure.callSpecial(stream);
// lets try close the stream & throw the exception if it fails
// but not try to reclose it in the finally block
InputStream temp = stream;
stream = null;
temp.close();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
log.warning("Caught exception closing stream: " + e);
}
}
}
}
/**
* Reads the stream into a list of Strings for each line
*
* @param stream a stream
* @return a List of lines
* @throws IOException
*/
public static List readLines(InputStream stream) throws IOException {
return readLines(new BufferedReader(new InputStreamReader(stream)));
}
/**
* Iterates through the given stream line by line
*
* @param stream a stream
* @param closure a closure
* @throws IOException
*/
public static void eachLine(InputStream stream, Closure closure) throws IOException {
eachLine(new InputStreamReader(stream), closure);
}
/**
* Iterates through the lines read from the URL's associated input stream
*
* @param url a URL to open and read
* @param closure a closure to apply on each line
* @throws IOException
*/
public static void eachLine(URL url, Closure closure) throws IOException {
eachLine(url.openConnection().getInputStream(), closure);
}
/**
* Helper method to create a new BufferedReader for a URL and then
* passes it into the closure and ensures its closed again afterwords
*
* @param url a URL
* @throws FileNotFoundException
*/
public static void withReader(URL url, Closure closure) throws IOException {
withReader(url.openConnection().getInputStream(), closure);
}
/**
* Helper method to create a new BufferedReader for a stream and then
* passes it into the closure and ensures its closed again afterwords
*
* @param in a stream
* @throws FileNotFoundException
*/
public static void withReader(InputStream in, Closure closure) throws IOException {
withReader(new InputStreamReader(in), closure);
}
/**
* Allows an output stream to be used, calling the closure with the output stream
* and then ensuring that the output stream is closed down again irrespective
* of whether exceptions occur
*
* @param stream the stream which is used and then closed
* @param closure the closure that the writer is passed into
* @throws IOException
*/
public static void withWriter(OutputStream stream, Closure closure) throws IOException {
withWriter(new OutputStreamWriter(stream), closure);
}
/**
* Allows an output stream to be used, calling the closure with the output stream
* and then ensuring that the output stream is closed down again irrespective
* of whether exceptions occur.
*
* @param stream the stream which is used and then closed
* @param charset the charset used
* @param closure the closure that the writer is passed into
* @throws IOException
*/
public static void withWriter(OutputStream stream, String charset, Closure closure) throws IOException {
withWriter(new OutputStreamWriter(stream, charset), closure);
}
/**
* Allows a OutputStream to be used, calling the closure with the stream
* and then ensuring that the stream is closed down again irrespective
* of whether exceptions occur.
*
* @param stream the stream which is used and then closed
* @param closure the closure that the stream is passed into
* @throws IOException
*/
public static void withStream(OutputStream stream, Closure closure) throws IOException {
try {
closure.callSpecial(stream);
// lets try close the stream & throw the exception if it fails
// but not try to reclose it in the finally block
OutputStream temp = stream;
stream = null;
temp.close();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
log.warning("Caught exception closing stream: " + e);
}
}
}
}
/**
* Helper method to create a buffered input stream for a file
*
* @param file a File
* @return a BufferedInputStream of the file
* @throws FileNotFoundException
*/
public static BufferedInputStream newInputStream(File file) throws FileNotFoundException {
return new BufferedInputStream(new FileInputStream(file));
}
/**
* Traverse through each byte of the specified File
*
* @param self a File
* @param closure a closure
*/
public static void eachByte(File self, Closure closure) throws IOException {
BufferedInputStream is = newInputStream(self);
eachByte(is, closure);
}
/**
* Traverse through each byte of the specified stream
*
* @param is stream to iterate over
* @param closure closure to apply to each byte
* @throws IOException
*/
public static void eachByte(InputStream is, Closure closure) throws IOException {
try {
while (true) {
int b = is.read();
if (b == -1) {
break;
} else {
closure.callSpecial(new Byte((byte) b));
}
}
is.close();
} catch (IOException e) {
if (is != null) {
try {
is.close();
} catch (Exception e2) {
// ignore as we're already throwing
}
throw e;
}
}
}
/**
* Traverse through each byte of the specified URL
*
* @param url url to iterate over
* @param closure closure to apply to each byte
* @throws IOException
*/
public static void eachByte(URL url, Closure closure) throws IOException {
InputStream is = url.openConnection().getInputStream();
eachByte(is, closure);
}
/**
* Transforms the characters from a reader with a Closure and write them to a writer
*
* @param reader
* @param writer
* @param closure
*/
public static void transformChar(Reader reader, Writer writer, Closure closure) {
int c;
try {
char[] chars = new char[1];
while ((c = reader.read()) != -1) {
chars[0] = (char) c;
writer.write((String) closure.callSpecial(new String(chars)));
}
} catch (IOException e) {
}
}
/**
* Transforms the lines from a reader with a Closure and write them to a writer
*
* @param reader
* @param writer
* @param closure
*/
public static void transformLine(Reader reader, Writer writer, Closure closure) throws IOException {
BufferedReader br = new BufferedReader(reader);
BufferedWriter bw = new BufferedWriter(writer);
String line;
while ((line = br.readLine()) != null) {
Object o = closure.callSpecial(line);
if (o != null) {
bw.write(o.toString());
bw.newLine();
}
}
}
/**
* Filter the lines from a reader and write them on the writer, according to a closure
* which returns true or false.
*
* @param reader a reader
* @param writer a writer
* @param closure the closure which returns booleans
* @throws IOException
*/
public static void filterLine(Reader reader, Writer writer, Closure closure) throws IOException {
BufferedReader br = new BufferedReader(reader);
BufferedWriter bw = new BufferedWriter(writer);
String line;
while ((line = br.readLine()) != null) {
if (InvokerHelper.asBool(closure.callSpecial(line))) {
bw.write(line);
bw.newLine();
}
}
bw.flush();
}
/**
* Filters the lines of a File and creates a Writeable in return to stream the filtered lines
*
* @param self a File
* @param closure a closure which returns a boolean indicating to filter the line or not
* @return a Writable closure
* @throws IOException if <code>self</code> is not readable
*/
public static Writable filterLine(final File self, final Closure closure) throws IOException {
return filterLine(newReader(self), closure);
}
/**
* Filter the lines from a File and write them on a writer, according to a closure
* which returns true or false
*
* @param self a File
* @param writer a writer
* @param closure a closure which returns a boolean value and takes a line as input
* @throws IOException if <code>self</code> is not readable
*/
public static void filterLine(final File self, final Writer writer, final Closure closure) throws IOException {
filterLine(newReader(self), writer, closure);
}
/**
* Filter the lines of a Reader and create a Writable in return to stream the filtered lines
*
* @param reader a reader
* @param closure a closure returning a boolean indicating to filter or not a line
* @return a Writable closure
*/
public static Writable filterLine(Reader reader, final Closure closure) {
final BufferedReader br = new BufferedReader(reader);
return new Writable() {
public Writer writeTo(Writer out) throws IOException {
BufferedWriter bw = new BufferedWriter(out);
String line;
while ((line = br.readLine()) != null) {
if (InvokerHelper.asBool(closure.callSpecial(line))) {
bw.write(line);
bw.newLine();
}
}
bw.flush();
return out;
}
public String toString() {
StringWriter buffer = new StringWriter();
try {
writeTo(buffer);
} catch (IOException e) {
throw new RuntimeException(e); // TODO: change this exception type
}
return buffer.toString();
}
};
}
/**
* Filter lines from an input stream using a closure predicate
*
* @param self an input stream
* @param predicate a closure which returns boolean and takes a line
* @return a filtered writer
*/
public static Writable filterLine(final InputStream self, final Closure predicate) {
return filterLine(newReader(self), predicate);
}
/**
* Filters lines from an input stream, writing to a writer, using a closure which
* returns boolean and takes a line.
*
* @param self an InputStream
* @param writer a writer to write output to
* @param predicate a closure which returns a boolean and takes a line as input
*/
public static void filterLine(final InputStream self, final Writer writer, final Closure predicate)
throws IOException {
filterLine(newReader(self), writer, predicate);
}
/**
* Reads the content of the file into an array of byte
*
* @param file a File
* @return a List of Bytes
*/
public static byte[] readBytes(File file) throws IOException {
byte[] bytes = new byte[(int) file.length()];
FileInputStream fileInputStream = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fileInputStream);
dis.readFully(bytes);
dis.close();
return bytes;
}
// ================================
// Socket and ServerSocket methods
/**
* Allows an InputStream and an OutputStream from a Socket to be used,
* calling the closure with the streams and then ensuring that the streams are closed down again
* irrespective of whether exceptions occur.
*
* @param socket a Socket
* @param closure a Closure
* @throws IOException
*/
public static void withStreams(Socket socket, Closure closure) throws IOException {
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();
try {
closure.callSpecial(new ParameterArray(new Object[]{input, output}));
} finally {
try {
input.close();
} catch (IOException e) {
// noop
}
try {
output.close();
} catch (IOException e) {
// noop
}
}
}
/**
* Overloads the left shift operator to provide an append mechanism
* to add things to the output stream of a socket
*
* @param self a Socket
* @param value a value to append
* @return a Writer
*/
public static Writer leftShift(Socket self, Object value) throws IOException {
return leftShift(self.getOutputStream(), value);
}
/**
* Overloads the left shift operator to provide an append mechanism
* to add bytes to the output stream of a socket
*
* @param self a Socket
* @param value a value to append
* @return an OutputStream
*/
public static OutputStream leftShift(Socket self, byte[] value) throws IOException {
return leftShift(self.getOutputStream(), value);
}
/**
* Allow to pass a Closure to the accept methods of ServerSocket
*
* @param serverSocket a ServerSocket
* @param closure a Closure
* @return a Socket
* @throws IOException
*/
public static Socket accept(ServerSocket serverSocket, final Closure closure) throws IOException {
final Socket socket = serverSocket.accept();
new Thread(new Runnable() {
public void run() {
try {
closure.callSpecial(socket);
} finally {
try {
socket.close();
} catch (IOException e) {
// noop
}
}
}
}).start();
return socket;
}
/**
* @param file a File
* @return a File which wraps the input file and which implements Writable
*/
public static File asWritable(File file) {
return new WritableFile(file);
}
/**
* @param file a File
* @param encoding the encoding to be used when reading the file's contents
* @return File which wraps the input file and which implements Writable
*/
public static File asWritable(File file, String encoding) {
return new WritableFile(file, encoding);
}
/**
* Converts the given String into a List of strings of one character
*
* @param self a String
* @return a List of characters (a 1-character String)
*/
public static List toList(String self) {
int size = self.length();
List answer = new ArrayList(size);
for (int i = 0; i < size; i++) {
answer.add(self.substring(i, i + 1));
}
return answer;
}
// Process methods
//-------------------------------------------------------------------------
/**
* An alias method so that a process appears similar to System.out, System.in, System.err;
* you can use process.in, process.out, process.err in a similar way
*
* @return an InputStream
*/
public static InputStream getIn(Process self) {
return self.getInputStream();
}
/**
* Read the text of the output stream of the Process.
*
* @param self a Process
* @return the text of the output
* @throws IOException
*/
public static String getText(Process self) throws IOException {
return getText(new BufferedReader(new InputStreamReader(self.getInputStream())));
}
/**
* An alias method so that a process appears similar to System.out, System.in, System.err;
* you can use process.in, process.out, process.err in a similar way
*
* @return an InputStream
*/
public static InputStream getErr(Process self) {
return self.getErrorStream();
}
/**
* An alias method so that a process appears similar to System.out, System.in, System.err;
* you can use process.in, process.out, process.err in a similar way
*
* @return an OutputStream
*/
public static OutputStream getOut(Process self) {
return self.getOutputStream();
}
/**
* Overloads the left shift operator to provide an append mechanism
* to pipe into a Process
*
* @param self a Process
* @param value a value to append
* @return a Writer
*/
public static Writer leftShift(Process self, Object value) throws IOException {
return leftShift(self.getOutputStream(), value);
}
/**
* Overloads the left shift operator to provide an append mechanism
* to pipe into a Process
*
* @param self a Process
* @param value a value to append
* @return an OutputStream
*/
public static OutputStream leftShift(Process self, byte[] value) throws IOException {
return leftShift(self.getOutputStream(), value);
}
/**
* Wait for the process to finish during a certain amount of time, otherwise stops the process.
*
* @param self a Process
* @param numberOfMillis the number of milliseconds to wait before stopping the process
*/
public static void waitForOrKill(Process self, long numberOfMillis) {
ProcessRunner runnable = new ProcessRunner(self);
Thread thread = new Thread(runnable);
thread.start();
runnable.waitForOrKill(numberOfMillis);
}
/**
* Process each regex group matched substring of the given string. The object
* passed to the closure is an array of strings, matched per a successful match.
*
* @param self the source string
* @param regex a Regex string
* @param closure a closure
* @author bing ran
* @author Pilho Kim
*/
public static void eachMatch(String self, String regex, Closure closure) {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(self);
while (m.find()) {
int count = m.groupCount();
ArrayList groups = new ArrayList();
for (int i = 0; i <= count; i++) {
groups.add(m.group(i));
}
closure.callSpecial(new ParameterArray(groups.toArray()));
}
}
/**
* Process each matched substring of the given group matcher. The object
* passed to the closure is an array of strings, matched per a successful match.
*
* @param self the source matcher
* @param closure a closure
* @author bing ran
* @author Pilho Kim
*/
public static void each(Matcher self, Closure closure) {
Matcher m = self;
while (m.find()) {
int count = m.groupCount();
ArrayList groups = new ArrayList();
for (int i = 0; i <= count; i++) {
groups.add(m.group(i));
}
closure.callSpecial(new ParameterArray(groups.toArray()));
}
}
/**
* Iterates over every element of the collection and return the index of the first object
* that matches the condition specified in the closure
*
* @param self the iteration object over which we iterate
* @param closure the filter to perform a match on the collection
* @return an integer that is the index of the first macthed object.
*/
public static int findIndexOf(Object self, Closure closure) {
int i = 0;
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) {
Object value = iter.next();
if (InvokerHelper.asBool(closure.callSpecial(value))) {
break;
}
}
return i;
}
/**
* A Runnable which waits for a process to complete together with a notification scheme
* allowing another thread to wait a maximum number of seconds for the process to complete
* before killing it.
*/
protected static class ProcessRunner implements Runnable {
Process process;
private boolean finished;
public ProcessRunner(Process process) {
this.process = process;
}
public void run() {
try {
process.waitFor();
} catch (InterruptedException e) {
}
synchronized (this) {
notifyAll();
finished = true;
}
}
public synchronized void waitForOrKill(long millis) {
if (!finished) {
try {
wait(millis);
} catch (InterruptedException e) {
}
if (!finished) {
process.destroy();
}
}
}
}
protected static class RangeInfo {
protected int from, to;
protected boolean reverse;
public RangeInfo(int from, int to, boolean reverse) {
this.from = from;
this.to = to;
this.reverse = reverse;
}
}
}