/*
* Copyright 2003-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.runtime;
import groovy.io.EncodingAwareBufferedWriter;
import groovy.io.GroovyPrintWriter;
import groovy.io.GroovyPrintWriter;
import groovy.lang.*;
import groovy.sql.GroovyRowResult;
import groovy.util.*;
import org.codehaus.groovy.reflection.ClassInfo;
import org.codehaus.groovy.reflection.MixinInMetaClass;
import org.codehaus.groovy.runtime.dgmimpl.NumberNumberDiv;
import org.codehaus.groovy.runtime.dgmimpl.NumberNumberMinus;
import org.codehaus.groovy.runtime.dgmimpl.NumberNumberMultiply;
import org.codehaus.groovy.runtime.dgmimpl.NumberNumberPlus;
import org.codehaus.groovy.runtime.dgmimpl.arrays.*;
import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.typehandling.GroovyCastException;
import org.codehaus.groovy.runtime.typehandling.NumberMath;
import org.codehaus.groovy.runtime.NullObject;
import org.codehaus.groovy.tools.RootLoader;
import org.w3c.dom.NodeList;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
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 James Strachan
* @author Jeremy Rayner
* @author Sam Pullara
* @author Rod Cope
* @author Guillaume Laforge
* @author John Wilson
* @author Hein Meling
* @author Dierk Koenig
* @author Pilho Kim
* @author Marc Guillemot
* @author Russel Winder
* @author bing ran
* @author Jochen Theodorou
* @author Paul King
* @author Michael Baehr
* @author Joachim Baumann
* @author Alex Tkachman
* @author Ted Naleid
* @author Brad Long
* @author Jim Jagielski
* @author Rodolfo Velasco
*/
public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
private static final Logger LOG = Logger.getLogger(DefaultGroovyMethods.class.getName());
private static final Integer ONE = Integer.valueOf(1);
public static final Class [] additionals = {
NumberNumberPlus.class,
NumberNumberMultiply.class,
NumberNumberMinus.class,
NumberNumberDiv.class,
ObjectArrayGetAtMetaMethod.class,
ObjectArrayPutAtMetaMethod.class,
BooleanArrayGetAtMetaMethod.class,
BooleanArrayPutAtMetaMethod.class,
ByteArrayGetAtMetaMethod.class,
ByteArrayPutAtMetaMethod.class,
CharacterArrayGetAtMetaMethod.class,
CharacterArrayPutAtMetaMethod.class,
ShortArrayGetAtMetaMethod.class,
ShortArrayPutAtMetaMethod.class,
IntegerArrayGetAtMetaMethod.class,
IntegerArrayPutAtMetaMethod.class,
LongArrayGetAtMetaMethod.class,
LongArrayPutAtMetaMethod.class,
FloatArrayGetAtMetaMethod.class,
FloatArrayPutAtMetaMethod.class,
DoubleArrayGetAtMetaMethod.class,
DoubleArrayPutAtMetaMethod.class,
};
/**
* Identity check. Since == is overridden in Groovy with the meaning of equality
* we need some fallback to check for object identity. Invoke using the
* 'is' operator, like so:
* If the def same = (this is that)
*
* @param self an object
* @param other an object to compare identity with
* @return true if self and other are both references to the same
* instance, false otherwise
* @since 1.0
*/
public static boolean is(Object self, Object other) {
return self == other;
}
/**
* Allows the closure to be called for the object reference self
* synonym for 'with()'.
*
* @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
* @since 1.0
*/
public static Object identity(Object self, Closure closure) {
return DefaultGroovyMethods.with(self, closure);
}
/**
* 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
* @since 1.5.0
*/
public static Object with(Object self, Closure closure) {
final Closure clonedClosure = (Closure) closure.clone();
clonedClosure.setResolveStrategy(Closure.DELEGATE_FIRST);
clonedClosure.setDelegate(self);
return clonedClosure.call(self);
}
/**
* Allows the subscript operator to be used to lookup dynamic property values.
* bean[somePropertyNameExpression]. The normal property notation
* of groovy is neater and more concise but only works with compile-time known
* property names.
*
* @param self the object to act upon
* @param property the property name of interest
* @return the property value
* @since 1.0
*/
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.
* bean[somePropertyNameExpression] = foo. The normal property notation
* of groovy is neater and more concise but only works with property names which
* are known at compile time.
*
* @param self the object to act upon
* @param property the name of the property to set
* @param newValue the value to set
* @since 1.0
*/
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.
*
* @param self an object
* @return the dump representation
* @since 1.0
*/
public static String dump(Object self) {
if (self == null) {
return "null";
}
StringBuilder buffer = new StringBuilder("<");
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 getProperties() stuff
* but the original pulls out private variables, whereas getProperties()
* does not. What's the real use of dump() here?
*/
while (klass != null) {
for (final Field field : klass.getDeclaredFields()) {
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 getProperties(). I have left
* it commented out because it returns a slightly different list of properties;
* i.e. 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 = getProperties(self);
for(Iterator itr = props.keySet().iterator(); itr.hasNext(); ) {
String propName = itr.next().toString();
// the original skipped this, so I will too
if(pv.getName().equals("class")) continue;
if(pv.getName().equals("metaClass")) continue;
buffer.append(" ");
buffer.append(propName);
buffer.append("=");
try {
buffer.append(InvokerHelper.toString(props.get(propName)));
}
catch (Exception e) {
buffer.append(e);
}
}
*/
buffer.append(">");
return buffer.toString();
}
/**
* Retrieves the list of {@link MetaProperty} objects for 'self' and wraps it
* in a list of {@link PropertyValue} objects that additionally provide
* the value for each property of 'self'.
*
* @param self the receiver object
* @return list of {@link PropertyValue} objects
* @see groovy.util.Expando#getMetaPropertyValues()
* @since 1.0
*/
public static Listuse(CategoryClass1, CategoryClass2) { ... }
* This method saves having to wrap the the category
* classes in a list.
*
* @param self any Object
* @param array a list of category classes and a Closure
* @return the value returned from the closure
* @since 1.0
*/
public static Object use(Object self, Object[] array) {
if (array.length < 2)
throw new IllegalArgumentException(
"Expecting at least 2 arguments, a category class and a Closure");
Closure closure;
try {
closure = (Closure) array[array.length - 1];
} catch (ClassCastException e) {
throw new IllegalArgumentException("Expecting a Closure to be the last argument");
}
List
* 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[] ) }
*
*
*
* @param self any Object
* @param format A format string
* @param arg Argument which is referenced by the format specifiers in the format
* string. The type of arg should be one of Object[], List,
* int[], short[], byte[], char[], boolean[], long[], float[], or double[].
* @since 1.0
*/
public static void printf(Object self, String format, Object arg) {
if (self instanceof PrintStream)
printf((PrintStream) self, format, arg);
else
printf(System.out, format, arg);
}
private static void printf(PrintStream self, String format, Object arg) {
self.print(sprintf(self, format, arg));
}
/**
* Returns a formatted string using the specified format string and
* arguments.
*
*
* @param self any Object
* @param format A format string
* @param arg Argument which is referenced by the format specifiers in the format
* string. The type of arg should be one of Object[], List,
* int[], short[], byte[], char[], boolean[], long[], float[], or double[].
* @return the resulting printf'd string
* @since 1.5.0
*/
public static String sprintf(Object self, String format, Object arg) {
if (arg instanceof Object[]) {
return sprintf(self, format, (Object[]) arg);
}
if (arg instanceof List) {
return sprintf(self, format, ((List) arg).toArray());
}
if (!arg.getClass().isArray()) {
Object[] o = (Object[]) java.lang.reflect.Array.newInstance(arg.getClass(), 1);
o[0] = arg;
return sprintf(self, format, o);
}
Object[] ans;
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] = ia[i];
}
} else if (elemType.equals("[C")) {
char[] ca = (char[]) arg;
ans = new Character[ca.length];
for (int i = 0; i < ca.length; i++) {
ans[i] = ca[i];
}
} else if (elemType.equals("[Z")) {
boolean[] ba = (boolean[]) arg;
ans = new Boolean[ba.length];
for (int i = 0; i < ba.length; i++) {
ans[i] = ba[i];
}
} else if (elemType.equals("[B")) {
byte[] ba = (byte[]) arg;
ans = new Byte[ba.length];
for (int i = 0; i < ba.length; i++) {
ans[i] = ba[i];
}
} else if (elemType.equals("[S")) {
short[] sa = (short[]) arg;
ans = new Short[sa.length];
for (int i = 0; i < sa.length; i++) {
ans[i] = sa[i];
}
} else if (elemType.equals("[F")) {
float[] fa = (float[]) arg;
ans = new Float[fa.length];
for (int i = 0; i < fa.length; i++) {
ans[i] = fa[i];
}
} else if (elemType.equals("[J")) {
long[] la = (long[]) arg;
ans = new Long[la.length];
for (int i = 0; i < la.length; i++) {
ans[i] = la[i];
}
} else if (elemType.equals("[D")) {
double[] da = (double[]) arg;
ans = new Double[da.length];
for (int i = 0; i < da.length; i++) {
ans[i] = da[i];
}
} else {
throw new RuntimeException("sprintf(String," + arg + ")");
}
return sprintf(self, format, ans);
}
/**
* Inspects returns the String that matches what would be typed into a
* terminal to create this object.
*
* @param self any Object
* @return a String that matches what would be typed into a terminal to
* create this object. e.g. [1, 'hello'].inspect() -> [1, "hello"]
* @since 1.0
*/
public static String inspect(Object self) {
return InvokerHelper.inspect(self);
}
/**
* Print to a console in interactive format.
*
* @param self any Object
* @param out the PrintWriter used for printing
* @since 1.0
*/
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 self any Object
* @param out the PrintWriter used for printing
* @since 1.0
*/
public static void println(Object self, PrintWriter out) {
if (out == null) {
out = new PrintWriter(System.out);
}
out.println(InvokerHelper.toString(self));
}
/**
* Provide a dynamic method invocation method which can be overloaded in
* classes to implement dynamic proxies easily.
*
* @param object any Object
* @param method the name of the method to call
* @param arguments the arguments to use
* @return the result of the method call
* @since 1.0
*/
public static Object invokeMethod(Object object, String method, Object arguments) {
return InvokerHelper.invokeMethod(object, method, arguments);
}
// isCase methods
//-------------------------------------------------------------------------
/**
* Method for overloading the behavior of the 'case' method in switch statements.
* The default implementation handles arrays types but otherwise simply delegates
* to Object#equals, but this may be overridden for other types. In this example:
* switch( a ) {
* case b: //some code
* }
* "some code" is called when b.isCase( a ) returns
* true.
*
* @param caseValue the case value
* @param switchValue the switch value
* @return true if the switchValue is deemed to be equal to the caseValue
* @since 1.0
*/
public static boolean isCase(Object caseValue, Object switchValue) {
if (caseValue.getClass().isArray()) {
return isCase(DefaultTypeTransformation.asCollection(caseValue), switchValue);
}
return caseValue.equals(switchValue);
}
/**
* 'Case' implementation for a String, which uses String#equals(Object)
* in order to allow Strings to be used in switch statements.
* For example:
* switch( str ) {
* case 'one' :
* // etc...
* }
* Note that this returns true for the case where both the
* 'switch' and 'case' operand is null.
*
* @param caseValue the case value
* @param switchValue the switch value
* @return true if the switchValue's toString() equals the caseValue
* @since 1.0
*/
public static boolean isCase(String caseValue, Object switchValue) {
if (switchValue == null) {
return caseValue == null;
}
return caseValue.equals(switchValue.toString());
}
/**
* 'Case' implementation for a GString, which simply calls the equivalet method for String.
*
* @param caseValue the case value
* @param switchValue the switch value
* @return true if the switchValue's toString() equals the caseValue
* @since 1.6.0
*/
public static boolean isCase(GString caseValue, Object switchValue) {
return isCase(caseValue.toString(), switchValue);
}
/**
* Special 'Case' implementation for Class, which allows testing
* for a certain class in a switch statement.
* For example:
* switch( obj ) {
* case List :
* // obj is a list
* break;
* case Set :
* // etc
* }
*
* @param caseValue the case value
* @param switchValue the switch value
* @return true if the switchValue is deemed to be assignable from the given class
* @since 1.0
*/
public static boolean isCase(Class caseValue, Object switchValue) {
if (switchValue instanceof Class) {
Class val = (Class) switchValue;
return caseValue.isAssignableFrom(val);
}
return caseValue.isInstance(switchValue);
}
/**
* 'Case' implementation for collections which tests if the 'switch'
* operand is contained in any of the 'case' values.
* For example:
* switch( item ) {
* case firstList :
* // item is contained in this list
* // etc
* }
*
* @param caseValue the case value
* @param switchValue the switch value
* @return true if the caseValue is deemed to contain the switchValue
* @see java.util.Collection#contains(Object)
* @since 1.0
*/
public static boolean isCase(Collection caseValue, Object switchValue) {
return caseValue.contains(switchValue);
}
/**
* 'Case' implementation for the {@link Pattern} class, which allows
* testing a String against a number of regular expressions.
* For example:
* switch( str ) {
* case ~/one/ :
* // the regex 'one' matches the value of str
* }
*
* Note that this returns true for the case where both the pattern and
* the 'switch' values are null.
*
* @param caseValue the case value
* @param switchValue the switch value
* @return true if the switchValue is deemed to match the caseValue
* @since 1.0
*/
public static boolean isCase(Pattern caseValue, Object switchValue) {
if (switchValue == null) {
return caseValue == null;
}
final Matcher matcher = caseValue.matcher(switchValue.toString());
if (matcher.matches()) {
RegexSupport.setLastMatcher(matcher);
return true;
} else {
return false;
}
}
/**
* Special 'case' implementation for all numbers, which delegates to the
* compareTo() method for comparing numbers of different
* types.
*
* @param caseValue the case value
* @param switchValue the switch value
* @return true if the numbers are deemed equal
* @since 1.5.0
*/
public static boolean isCase(Number caseValue, Number switchValue) {
return NumberMath.compareTo(caseValue, switchValue) == 0;
}
/**
* Returns an iterator equivalent to this iterator all duplicated items removed
* by using the default comparator. The original iterator will become
* exhausted of elements after determining the unique values. A new iterator
* for the unique values will be returned.
*
* @param self an Iterator
* @return the modified Iterator
* @since 1.5.5
*/
public static
*
* @param self a Collection
* @param comparator a Comparator
* @return self the now modified collection without duplicates
* @since 1.0
*/
public static
* class Person {
* def fname, lname
* public String toString() {
* return fname + " " + lname
* }
* }
*
* class PersonComparator implements Comparator {
* public int compare(Object o1, Object o2) {
* Person p1 = (Person) o1
* Person p2 = (Person) o2
* if (p1.lname != p2.lname)
* return p1.lname.compareTo(p2.lname)
* else
* return p1.fname.compareTo(p2.fname)
* }
*
* public boolean equals(Object obj) {
* return this.equals(obj)
* }
* }
*
* Person a = new Person(fname:"John", lname:"Taylor")
* Person b = new Person(fname:"Clark", lname:"Taylor")
* Person c = new Person(fname:"Tom", lname:"Cruz")
* Person d = new Person(fname:"Clark", lname:"Taylor")
*
* def list = [a, b, c, d]
* List list2 = list.unique(new PersonComparator())
* assert( list2 == list && list == [a, b, c] )
*
* def result = ""
* [a:1, b:3].each { key, value -> result += "$key$value" }
* assert result == "a1b3"
* def result = ""
* [a:1, b:3].each { entry -> result += entry }
* assert result == "a=1b=3"
*
* @param self the map over which we iterate
* @param closure the closure applied on each entry of the map
* @return returns the self parameter
* @since 1.5.0
*/
public static Map each(Map self, Closure closure) {
for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
callClosureForMapEntry(closure, entry);
}
return self;
}
/**
* Allows a Map to be iterated through using a closure. If the
* closure takes two parameters then it will be passed the Map.Entry and
* the item's index (a counter starting at zero) otherwise if the closure
* takes three parameters then it will be passed the key, the value, and
* the index.
* def result = ""
* [a:1, b:3].eachWithIndex { key, value, index -> result += "$index($key$value)" }
* assert result == "0(a1)1(b3)"
* def result = ""
* [a:1, b:3].eachWithIndex { entry, index -> result += "$index($entry)" }
* assert result == "0(a=1)1(b=3)"
*
* @param self the map over which we iterate
* @param closure a Closure to operate on each item
* @return the self Object
* @since 1.5.0
*/
public static Object eachWithIndex(Map self, Closure closure) {
int counter = 0;
for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
callClosureForMapEntryAndCounter(closure, entry, counter++);
}
return self;
}
/**
* Iterate over each element of the list in the reverse order.
* def result = []
* [1,2,3].reverseEach { result << it }
* assert result == [3,2,1]
*
* @param self a List
* @param closure a closure to which each item is passed.
* @return the original list
* @since 1.5.0
*/
public static true for all items in this data structure).
* A simple example for a list:
* def list = [3,4,5]
* def greaterThanTwo = list.every { it > 2 }
*
*
* @param self the object over which we iterate
* @param closure the closure predicate used for matching
* @return true if every iteration of the object matches the closure predicate
* @since 1.0
*/
public static boolean every(Object self, Closure closure) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
if (!DefaultTypeTransformation.castToBoolean(closure.call(iter.next()))) {
return false;
}
}
return true;
}
/**
* Iterates over the entries of a map, and checks whether a predicate is
* valid for all entries.
* def map = [a:1, b:2.0, c:2L]
* assert !map.every { key, value -> value instanceof Integer }
* assert map.every { entry -> entry.value instanceof Number }
*
* @param self the map over which we iterate
* @param closure the closure predicate used for matching
* @return true if every entry of the map matches the closure predicate
* @since 1.5.0
*/
public static boolean every(Map self, Closure closure) {
for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
if (!DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) {
return false;
}
}
return true;
}
/**
* Iterates over every element of a collection, and checks whether all
* elements are true according to the Groovy Truth.
* Equivalent to self.every({element -> element})
*
* @param self the object over which we iterate
* @return true if every item in the collection matches the closure
* predicate
* @since 1.5.0
*/
public static boolean every(Object self) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
if (!DefaultTypeTransformation.castToBoolean(iter.next())) {
return false;
}
}
return true;
}
/**
* Iterates over the contents of an object or collection, and checks 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 iteration for the object matches the closure predicate
* @since 1.0
*/
public static boolean any(Object self, Closure closure) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
if (DefaultTypeTransformation.castToBoolean(closure.call(iter.next()))) {
return true;
}
}
return false;
}
/**
* Iterates over the entries of a map, and checks whether a predicate is
* valid for at least one entry
* assert [2:3, 4:5, 5:10].any { key, value -> key * 2 == value }
* assert ![2:3, 4:5, 5:10].any { entry -> entry.key == entry.value * 2 }
*
* @param self the map over which we iterate
* @param closure the closure predicate used for matching
* @return true if any entry in the map matches the closure predicate
* @since 1.5.0
*/
public static boolean any(Map self, Closure closure) {
for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) {
return true;
}
}
return false;
}
/**
* Iterates over the elements of a collection, and checks whether at least
* one element is true according to the Groovy Truth.
* Equivalent to self.any({element -> element})
*
* @param self the object over which we iterate
* @return true if any item in the collection matches the closure predicate
* @since 1.5.0
*/
public static boolean any(Object self) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
if (DefaultTypeTransformation.castToBoolean(iter.next())) {
return true;
}
}
return false;
}
/**
* Iterates over every element of the collection and returns each item that matches
* the given filter - calling the {@link #isCase(Object,Object)}
* method used by switch statements. This method can be used with different
* kinds of filters like regular expressions, classes, ranges etc.
* Example:
* def list = ['a', 'b', 'aa', 'bc' ]
* def filtered = list.grep( ~/a+/ ) //contains 'a' and 'aa'
*
*
* @param self the object over which we iterate
* @param filter the filter to perform on the collection (using the isCase(object) method)
* @return a collection of objects which match the filter
* @since 1.5.6
*/
public static Collection grep(Object self, Object filter) {
Collection answer = createSimilarOrDefaultCollection(self);
MetaClass metaClass = InvokerHelper.getMetaClass(filter);
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
Object object = iter.next();
if (DefaultTypeTransformation.castToBoolean(metaClass.invokeMethod(filter, "isCase", object))) {
answer.add(object);
}
}
return answer;
}
/**
* Counts the number of occurrences of the given value from the
* items within this Iterator.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
* The iterator will become exhausted of elements after determining the count value.
*
* @param self the Iterator from which we count the number of matching occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.5.0
*/
public static Number count(Iterator self, Object value) {
long answer = 0;
while (self.hasNext()) {
if (DefaultTypeTransformation.compareEqual(self.next(), value)) {
++answer;
}
}
// for b/c with Java return an int if we can
if (answer <= Integer.MAX_VALUE) return new Long(answer).intValue();
return answer;
}
/**
* Counts the number of occurrences of the given value inside this collection.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the collection within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.0
*/
public static Number count(Collection self, Object value) {
return count(self.iterator(), value);
}
/**
* Counts the number of occurrences of the given value inside this array.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the array within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.6.4
*/
public static Number count(Object[] self, Object value) {
return count(Arrays.asList(self), value);
}
/**
* Counts the number of occurrences of the given value inside this array.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the array within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.6.4
*/
public static Number count(int[] self, Object value) {
return count(InvokerHelper.asIterator(self), value);
}
/**
* Counts the number of occurrences of the given value inside this array.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the array within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.6.4
*/
public static Number count(long[] self, Object value) {
return count(InvokerHelper.asIterator(self), value);
}
/**
* Counts the number of occurrences of the given value inside this array.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the array within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.6.4
*/
public static Number count(short[] self, Object value) {
return count(InvokerHelper.asIterator(self), value);
}
/**
* Counts the number of occurrences of the given value inside this array.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the array within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.6.4
*/
public static Number count(char[] self, Object value) {
return count(InvokerHelper.asIterator(self), value);
}
/**
* Counts the number of occurrences of the given value inside this array.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the array within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.6.4
*/
public static Number count(boolean[] self, Object value) {
return count(InvokerHelper.asIterator(self), value);
}
/**
* Counts the number of occurrences of the given value inside this array.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the array within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.6.4
*/
public static Number count(double[] self, Object value) {
return count(InvokerHelper.asIterator(self), value);
}
/**
* Counts the number of occurrences of the given value inside this array.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the array within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.6.4
*/
public static Number count(float[] self, Object value) {
return count(InvokerHelper.asIterator(self), value);
}
/**
* Counts the number of occurrences of the given value inside this array.
* Comparison is done using Groovy's == operator (using
* compareTo(value) == 0 or equals(value) ).
*
* @param self the array within which we count the number of occurrences
* @param value the value being searched for
* @return the number of occurrences
* @since 1.6.4
*/
public static Number count(byte[] self, Object value) {
return count(InvokerHelper.asIterator(self), value);
}
/**
* Convert a collection to a List.
*
* @param self a collection
* @return a List
* @since 1.0
*/
public static def list = [1, 'a', 1.23, true ]
* def types = list.collect { it.class }
* assert types == [Integer, String, BigDecimal, Boolean]
*
* @param self the values of the object to transform
* @param closure the closure used to transform each element of the collection
* @return a List of the transformed values
* @since 1.0
*/
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 transform
* @param collection the Collection to which the transformed values are added
* @param closure the closure used to map each element of the collection
* @return the given collection after the transformed values are added
* @since 1.0
*/
public static Collection collect(Object self, Collection collection, Closure closure) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
collection.add(closure.call(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 transformed values
* @since 1.0
*/
public static List collect(Collection self, Closure closure) {
return (List) collect(self, new ArrayList(self.size()), closure);
}
/**
* Iterates through this collection transforming each value into a new value using the closure
* as a transformer, returning an initial collection plus the transformed values.
*
* @param self a collection
* @param collection an initial Collection to which the transformed values are added
* @param closure the closure used to transform each element of the collection
* @return the resulting collection of transformed values
* @since 1.0
*/
public static Collection collect(Collection self, Collection collection, Closure closure) {
for (Iterator iter = self.iterator(); iter.hasNext();) {
collection.add(closure.call(iter.next()));
if (closure.getDirective() == Closure.DONE) {
break;
}
}
return collection;
}
/**
* Recursively iterates through this collection transforming each non-Collection value
* into a new value using the closure as a transformer. Returns a potentially nested
* list of transformed values.
*
* @param self a collection
* @param closure the closure used to transform each element of the collection
* @return the resultant collection
* @since 1.5.2
*/
public static List collectAll(Collection self, Closure closure) {
return (List) collectAll(self, new ArrayList(self.size()), closure);
}
/**
* Recursively iterates through this collection transforming each non-Collection value
* into a new value using the closure as a transformer. Returns a potentially nested
* collection of transformed values.
*
* @param self a collection
* @param collection an initial Collection to which the transformed values are added
* @param closure the closure used to transform each element of the collection
* @return the resultant collection
* @since 1.5.2
*/
public static Collection collectAll(Collection self, Collection collection, Closure closure) {
for (Iterator iter = self.iterator(); iter.hasNext();) {
final Object o = iter.next();
if (o instanceof Collection) {
Collection c = (Collection) o;
collection.add(collectAll(c, createSimilarCollection(collection, c.size()), closure));
} else {
collection.add(closure.call(o));
}
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.
* assert [a:1, b:2].collect( [] as HashSet ) { key, value -> key*value } == ["a", "bb"] as Set
* assert [3:20, 2:30].collect( [] as HashSet ) { entry -> entry.key * entry.value } == [60] as Set
*
* @param self a Map
* @param collection the Collection to which the mapped values are added
* @param closure the closure used for mapping, which can take one (Map.Entry) or two (key, value) parameters
* @return a List of the mapped values
* @since 1.0
*/
public static Collection collect(Map self, Collection collection, Closure closure) {
boolean isTwoParams = (closure.getParameterTypes().length == 2);
for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
if (isTwoParams) {
Map.Entry entry = (Map.Entry) iter.next();
collection.add(closure.call(new Object[]{entry.getKey(), entry.getValue()}));
} else {
collection.add(closure.call(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.
* assert [a:1, b:2].collect { key, value -> key*value } == ["a", "bb"]
* assert [3:20, 2:30].collect { entry -> entry.key * entry.value } == [60, 60]
*
* @param self a Map
* @param closure the closure used to map each element of the collection
* @return the resultant collection
* @since 1.0
*/
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
* @since 1.0
*/
public static Object find(Object self, Closure closure) {
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
Object value = iter.next();
if (DefaultTypeTransformation.castToBoolean(closure.call(value))) {
return value;
}
}
return null;
}
/**
* Finds the first value matching the closure condition. Example:
* def list = [1,2,3]
* list.find { it > 1 } // returns 2
*
*
* @param self a Collection
* @param closure a closure condition
* @return the first Object found
* @since 1.0
*/
public static assert [a:1, b:3].find { it.value == 3 }.key == "b"
*
* @param self a Map
* @param closure a closure condition
* @return the first Object found
* @since 1.0
*/
public static def result = [['a', 'b'], [1, 2]].transpose()
* assert result == [['a', 1], ['b', 2]]
*
* @param self a List of lists
* @return a List of the transposed lists
* @see groovy.util.GroovyCollections#transpose(java.util.List)
* @since 1.5.0
*/
public static List transpose(List self) {
return GroovyCollections.transpose(self);
}
/**
* 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 should take two parameters, which will be
* the key and the value.
* self map is one of TreeMap, LinkedHashMap, Hashtable
* or Properties, the returned Map will preserve that type, otherwise a HashMap will
* be returned.
* def result = [a:1, b:2, c:4, d:5].findAll { it.value % 2 == 0 }
* assert result.every { it instanceof Map.Entry }
* assert result*.key == ["b", "c"]
* assert result*.value == [2, 4]
*
* @param self a Map
* @param closure a closure condition applying on the entries
* @return a new subMap
* @since 1.0
*/
public static
If it does match, we get the matching string back: *
* assert "10292" == "New York, NY 10292-0098".find(/\d{5}/)
*
*
* If we have capture groups in our expression, we still get back the full match *
* assert "10292-0098" == "New York, NY 10292-0098".find(/(\d{5})-?(\d{4})/)
*
*
*
* @param self a String
* @param regex the capturing regex
* @return a String containing the matched portion, or null if the regex doesn't match
* @since 1.6.1
*/
public static String find(String self, String regex) {
return find(self, Pattern.compile(regex));
}
/**
* Finds the first occurrence of a compiled regular expression Pattern within a String.
* If the pattern doesn't match, null will be returned.
*
* For example, if the pattern doesn't match the result is null: *
* assert null == "New York, NY".find(~/\d{5}/)
*
*
* If it does match, we get the matching string back: *
* assert "10292" == "New York, NY 10292-0098".find(~/\d{5}/)
*
*
* If we have capture groups in our expression, the groups are ignored and * we get back the full match: *
* assert "10292-0098" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/)
*
* If you need to work with capture groups, then use the closure version
* of this method or use Groovy's matcher operators or use eachMatch.
*
*
* @param self a String
* @param pattern the compiled regex Pattern
* @return a String containing the matched portion, or null if the regex pattern doesn't match
* @since 1.6.1
*/
public static String find(String self, Pattern pattern) {
Matcher matcher = pattern.matcher(self);
if (matcher.find()) {
return matcher.group(0);
}
return null;
}
/**
* Finds the first occurrence of a regular expression String within a String.
* If the regex doesn't match, the closure will not be called and find will return null.
*
* For example, if the regex doesn't match, the result is null: *
* assert null == "New York, NY".find(~/\d{5}/) { match -> return "-$match-"}
*
*
* If it does match and we don't have any capture groups in our regex, there is a single parameter * on the closure that the match gets passed to: *
* assert "-10292-" == "New York, NY 10292-0098".find(~/\d{5}/) { match -> return "-$match-"}
*
*
* If we have capture groups in our expression, our closure has one parameter for the match, followed by * one for each of the capture groups: *
* assert "10292" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/) { match, zip, plusFour ->
* assert match == "10292-0098"
* assert zip == "10292"
* assert plusFour == "0098"
* return zip
* }
*
* If we have capture groups in our expression, and our closure has one parameter, * the closure will be passed an array with the first element corresponding to the whole match, * followed by an element for each of the capture groups: *
* assert "10292" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/) { match, zip, plusFour ->
* assert array[0] == "10292-0098"
* assert array[1] == "10292"
* assert array[2] == "0098"
* return array[1]
* }
*
* If a capture group is optional, and doesn't match, then the corresponding value * for that capture group passed to the closure will be null as illustrated here: *
* assert "2339999" == "adsf 233-9999 adsf".find(~/(\d{3})?-?(\d{3})-(\d{4})/) { match, areaCode, exchange, stationNumber ->
* assert "233-9999" == match
* assert null == areaCode
* assert "233" == exchange
* assert "9999" == stationNumber
* return "$exchange$stationNumber"
* }
*
*
*
* @param self a String
* @param regex the capturing regex string
* @param closure the closure that will be passed the full match, plus each of the capturing groups
* @return a String containing the result of the closure, or null if the regex pattern doesn't match
* @since 1.6.1
*/
public static String find(String self, String regex, Closure closure) {
return find(self, Pattern.compile(regex), closure);
}
/**
* Finds the first occurrence of a compiled regular expression Pattern within a String.
* If the pattern doesn't match, the closure will not be called and find will return null.
*
* For example, if the pattern doesn't match, the result is null: *
* assert null == "New York, NY".find(~/\d{5}/) { match -> return "-$match-"}
*
*
* If it does match and we don't have any capture groups in our regex, there is a single parameter * on the closure that the match gets passed to: *
* assert "-10292-" == "New York, NY 10292-0098".find(~/\d{5}/) { match -> return "-$match-"}
*
*
* If we have capture groups in our expression, our closure has one parameter for the match, followed by * one for each of the capture groups: *
* assert "10292" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/) { match, zip, plusFour ->
* assert match == "10292-0098"
* assert zip == "10292"
* assert plusFour == "0098"
* return zip
* }
*
* If we have capture groups in our expression, and our closure has one parameter, * the closure will be passed an array with the first element corresponding to the whole match, * followed by an element for each of the capture groups: *
* assert "10292" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/) { match, zip, plusFour ->
* assert array[0] == "10292-0098"
* assert array[1] == "10292"
* assert array[2] == "0098"
* return array[1]
* }
*
* If a capture group is optional, and doesn't match, then the corresponding value * for that capture group passed to the closure will be null as illustrated here: *
* assert "2339999" == "adsf 233-9999 adsf".find(~/(\d{3})?-?(\d{3})-(\d{4})/) { match, areaCode, exchange, stationNumber ->
* assert "233-9999" == match
* assert null == areaCode
* assert "233" == exchange
* assert "9999" == stationNumber
* return "$exchange$stationNumber"
* }
*
*
*
* @param self a String
* @param pattern the compiled regex Pattern
* @param closure the closure that will be passed the full match, plus each of the capturing groups
* @return a String containing the result of the closure, or null if the regex pattern doesn't match
* @since 1.6.1
*/
public static String find(String self, Pattern pattern, Closure closure) {
Matcher matcher = pattern.matcher(self);
if (matcher.find()) {
if (hasGroup(matcher)) {
int count = matcher.groupCount();
List groups = new ArrayList(count);
for (int i = 0; i <= count; i++) {
groups.add(matcher.group(i));
}
return InvokerHelper.toString(closure.call(groups));
} else {
return InvokerHelper.toString(closure.call(matcher.group(0)));
}
}
return null;
}
/**
* Finds all occurrences of a regular expression string within a String. A List is returned containing all full matches or
* an empty list if there are no matches within the string.
*
* For example, if the regex doesn't match, it returns an empty list: *
* assert [] == "foo".findAll(/(\w*) Fish/)
*
* Any regular expression matches are returned in a list, and all regex capture groupings are ignored, only the full match is returned: *
* def expected = ["One Fish", "Two Fish", "Red Fish", "Blue Fish"]
* assert expected == "One Fish, Two Fish, Red Fish, Blue Fish".findAll(/(\w*) Fish/)
*
* If you need to work with capture groups, then use the closure version
* of this method or use Groovy's matcher operators or use eachMatch.
*
*
* @param self a String
* @param regex the capturing regex String
* @return a List containing all full matches of the regex within the string, an empty list will be returned if there are no matches
* @since 1.6.1
*/
public static List findAll(String self, String regex) {
return findAll(self, Pattern.compile(regex));
}
/**
* Finds all occurrences of a regular expression Pattern within a String. A List is returned containing all full matches or
* an empty list if there are no matches within the string.
*
* For example, if the pattern doesn't match, it returns an empty list: *
* assert [] == "foo".findAll(~/(\w*) Fish/)
*
* Any regular expression matches are returned in a list, and all regex capture groupings are ignored, only the full match is returned: *
* def expected = ["One Fish", "Two Fish", "Red Fish", "Blue Fish"]
* assert expected == "One Fish, Two Fish, Red Fish, Blue Fish".findAll(~/(\w*) Fish/)
*
*
* @param self a String
* @param pattern the compiled regex Pattern
* @return a List containing all full matches of the Pattern within the string, an empty list will be returned if there are no matches
* @since 1.6.1
*/
public static List findAll(String self, Pattern pattern) {
Matcher matcher = pattern.matcher(self);
List list = new ArrayList();
for (Iterator iter = iterator(matcher); iter.hasNext();) {
if (hasGroup(matcher)) {
list.add(((List) iter.next()).get(0));
} else {
list.add(iter.next());
}
}
return list;
}
/**
* Finds all occurrences of a regular expression string within a String. Any matches are passed to the specified closure. The closure
* is expected to have the full match in the first parameter. If there are any capture groups, they will be placed in subsequent parameters.
*
* If there are no matches, the closure will not be called, and an empty List will be returned.
*
* For example, if the regex doesn't match, it returns an empty list: *
* assert [] == "foo".findAll(/(\w*) Fish/) { match, firstWord -> return firstWord }
*
* Any regular expression matches are passed to the closure, if there are no capture groups, there will be one parameter for the match: *
* assert ["couldn't", "wouldn't"] == "I could not, would not, with a fox.".findAll(/.ould/) { match -> "${match}n't"}
*
* If there are capture groups, the first parameter will be the match followed by one parameter for each capture group: *
* def orig = "There's a Wocket in my Pocket"
* assert ["W > Wocket", "P > Pocket"] == orig.findAll(/(.)ocket/) { match, firstLetter -> "$firstLetter > $match" }
*
*
* @param self a String
* @param regex the capturing regex String
* @param closure will be passed the full match plus each of the capturing groups
* @return a List containing all full matches of the regex within the string, an empty list will be returned if there are no matches
* @since 1.6.1
*/
public static List findAll(String self, String regex, Closure closure) {
return findAll(self, Pattern.compile(regex), closure);
}
/**
* Finds all occurrences of a compiled regular expression Pattern within a String. Any matches are passed to the specified closure. The closure
* is expected to have the full match in the first parameter. If there are any capture groups, they will be placed in subsequent parameters.
*
* If there are no matches, the closure will not be called, and an empty List will be returned.
*
* For example, if the pattern doesn't match, it returns an empty list: *
* assert [] == "foo".findAll(~/(\w*) Fish/) { match, firstWord -> return firstWord }
*
* Any regular expression matches are passed to the closure, if there are no capture groups, there will be one parameter for the match: *
* assert ["couldn't", "wouldn't"] == "I could not, would not, with a fox.".findAll(~/.ould/) { match -> "${match}n't"}
*
* If there are capture groups, the first parameter will be the match followed by one parameter for each capture group: *
* def orig = "There's a Wocket in my Pocket"
* assert ["W > Wocket", "P > Pocket"] == orig.findAll(~/(.)ocket/) { match, firstLetter -> "$firstLetter > $match" }
*
*
* @param self a String
* @param pattern the compiled regex Pattern
* @param closure will be passed the full match plus each of the capturing groups
* @return a List containing all full matches of the regex Pattern within the string, an empty list will be returned if there are no matches
* @since 1.6.1
*/
public static List findAll(String self, Pattern pattern, Closure closure) {
Matcher matcher = pattern.matcher(self);
return collect(matcher, closure);
}
/**
* Replaces all occurrences of a captured group by the result of a closure on that text.
*
* For examples, *
* 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
*
* Note that unlike String.replaceAll(String regex, String replacement), where the replacement string * treats '$' and '\' specially (for group substitution), the result of the closure is converted to a string * and that value is used literally for the replacement.
* * @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 * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @since 1.0 * @see java.util.regex.Matcher#quoteReplacement(String) */ public static String replaceAll(final String self, final String regex, final Closure closure) { return replaceAll(self, Pattern.compile(regex), closure); } /** * Replaces all occurrences of a captured group by the result of a closure on that text. * *For examples, *
* 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
*
* Note that unlike String.replaceAll(String regex, String replacement), where the replacement string * treats '$' and '\' specially (for group substitution), the result of the closure is converted to a string * and that value is used literally for the replacement.
* * @param self a String * @param pattern the capturing regex Pattern * @param closure the closure to apply on each captured group * @return a String with replaced content * @since 1.6.8 * @see java.util.regex.Matcher#quoteReplacement(java.lang.String) */ public static String replaceAll(final String self, final Pattern pattern, final Closure closure) { final Matcher matcher = pattern.matcher(self); if (matcher.find()) { final StringBuffer sb = new StringBuffer(self.length() + 16); do { int count = matcher.groupCount(); List
* def p = /ab[d|f]/
* def m = "abcabdabeabf" =~ p
* assert 2 == m.count
* assert 2 == m.size() // synonym for m.getCount()
* assert ! m.hasGroup()
* assert 0 == m.groupCount()
* def matches = ["abd", "abf"]
* for (i in 0..<m.count) {
* assert m[i] == matches[i]
* }
*
*
* For an example using group matches,
*
* def p = /(?:ab([c|d|e|f]))/
* def m = "abcabdabeabf" =~ p
* assert 4 == m.count
* assert m.hasGroup()
* assert 1 == m.groupCount()
* def matches = [["abc", "c"], ["abd", "d"], ["abe", "e"], ["abf", "f"]]
* for (i in 0..<m.count) {
* assert m[i] == matches[i]
* }
*
*
* For another example using group matches,
*
* def m = "abcabdabeabfabxyzabx" =~ /(?:ab([d|x-z]+))/
* assert 3 == m.count
* assert m.hasGroup()
* assert 1 == m.groupCount()
* def matches = [["abd", "d"], ["abxyz", "xyz"], ["abx", "x"]]
* for (i in 0..<m.count) {
* assert m[i] == matches[i]
* }
*
*
* @param matcher a Matcher
* @param idx an index
* @return object a matched String if no groups matched, list of matched groups otherwise.
* @since 1.0
*/
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);
Iterator iter = iterator(matcher);
Object result = null;
for (int i = 0; i <= idx; i++) {
result = iter.next();
}
return result;
}
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
* @since 1.0
*/
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.
* @since 1.0
*/
public static int getCount(Matcher matcher) {
int counter = 0;
matcher.reset();
while (matcher.find()) {
counter++;
}
return counter;
}
/**
* Check whether a Matcher contains a group or not.
*
* @param matcher a Matcher
* @return boolean true if matcher contains at least one group.
* @since 1.0
*/
public static boolean hasGroup(Matcher matcher) {
return matcher.groupCount() > 0;
}
/**
* Support the range subscript operator for a List
* def list = [1, "a", 4.5, true]
* assert list[1..2] == ["a", 4.5]
*
* @param self a List
* @param range a Range indicating the items to get
* @return a sublist based on range borders or a new list if range is reversed
* @see java.util.List#subList(int,int)
* @since 1.0
*/
public static def list = [true, 1, 3.4, false]
* assert list[1,0,2] == [1, true, 3.4]
*
* @param self a List
* @param indices a Collection of indices
* @return a new list of the values at the given indices
* @since 1.0
*/
public static assert [1:10, 2:20, 4:40].subMap( [2, 4] ) == [2:20, 4:40]* * @param map a Map * @param keys a Collection of keys * @return a new Map containing the given keys * @since 1.0 */ public static
def map=[:]
* map.get("a", []) << 5
* assert map == [a:[5]]
*
* @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
* @since 1.0
*/
public static def list = [2, "a", 5.3]
* assert list[1] == "a"
*
* @param self a List
* @param idx an index
* @return the value at the given index
* @since 1.0
*/
public static def list = [2, 3]
* list[0] = 1
* assert list == [1, 3]
*
* @param self a List
* @param idx an index
* @param value the value to put at the given index
* @since 1.0
*/
public static def list = ["a", true]
* list[1..<1] = 5
* assert list == ["a", 5, true]
*
* @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
* @since 1.0
*/
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.isEmpty()) return;
sublist.addAll(col);
} else {
sublist.add(value);
}
}
private static def myList = [4, 3, 5, 1, 2, 8, 10]
* myList[3..5] = ["a", true]
* assert myList == [4, 3, 5, "a", true, 10]
*
* Items in the given
* range are relaced with items from the collection.
*
* @param self a List
* @param range the subset of the list to set
* @param col the collection of values to put at the given sublist
* @since 1.5.0
*/
public static void putAt(List self, IntRange range, Collection col) {
List sublist = resizeListWithRangeAndGetSublist(self, range);
if (col.isEmpty()) return;
sublist.addAll(col);
}
/**
* List subscript assignment operator when given a range as the index.
* Example: def myList = [4, 3, 5, 1, 2, 8, 10]
* myList[3..5] = "b"
* assert myList == [4, 3, 5, "b", 10]
*
* Items in the given
* range are relaced with the operand. The value operand is
* always treated as a single value.
*
* @param self a List
* @param range the subset of the list to set
* @param value the value to put at the given sublist
* @since 1.0
*/
public static void putAt(List self, IntRange range, Object value) {
List sublist = resizeListWithRangeAndGetSublist(self, range);
sublist.add(value);
}
/**
* A helper method to allow lists to work with subscript operators.
* def list = ["a", true, 42, 9.4]
* list[1, 4] = ["x", false]
* assert list == ["a", "x", 42, 9.4, false]
*
* @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)
* @since 1.0
*/
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.
* def list = ["a", true, 42, 9.4]
* list[1, 3] = 5
* assert list == ["a", 5, 42, 5]
*
* @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)
* @since 1.0
*/
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 = DefaultTypeTransformation.intUnbox(splice.get(0));
right = DefaultTypeTransformation.intUnbox(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 Map.
* def map = [a:10]
* assert map["a"] == 10
*
* @param self a Map
* @param key an Object as a key for the map
* @return the value corresponding to the given key
* @since 1.0
*/
public static Object getAt(Map self, Object key) {
return self.get(key);
}
/**
* Returns a new Map containing all entries from left and right,
* giving precedence to right. Any keys appearing in both Maps
* will appear in the resultant map with values from the right
* operand. If the left map is one of TreeMap, LinkedHashMap, Hashtable
* or Properties, the returned Map will preserve that type, otherwise a HashMap will
* be returned.
*
*
* Roughly equivalent to Map m = new HashMap(); m.putAll(left); m.putAll(right); return m;
* but with some additional logic to preserve the left Map type for common cases as
* described above.
*
* assert [a:10, b:20] + [a:5, c:7] == [a:5, b:20, c:7]* * @param left a Map * @param right a Map * @return a new Map containing all entries from left and right * @since 1.5.0 */ public static
SpreadMap from this map.
*
* For examples, if there is defined a function like as
*
* def fn(a, b, c, d) { return a + b + c + d }
* , then all of the following three have the same meaning.
*
* 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)
*
*
*
* @param self a list to be converted into a spreadmap
* @return a newly created Spreadmap if this list is not null and its size is positive.
* @see groovy.lang.SpreadMap#SpreadMap(java.util.Map)
* @since 1.0
*/
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);
}
/**
* Creates a spreadable map from this array.
* @param self an object array
* @return a newly created Spreadmap
* @see groovy.lang.SpreadMap#SpreadMap(java.lang.Object[])
* @since 1.0
*/
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. The collection items are
* assumed to be comparable.
*
* @param self the collection to be sorted
* @return the sorted collection as a List
* @since 1.0
*/
public static def map = [a:5, b:3, c:6, d:4].sort { a, b -> a.value <=> b.value }
* assert map == [b:3, d:4, a:5, c:6]
*
* @param self the map to be sorted
* @param closure a Closure used as a comparator
* @return the sorted map
* @since 1.6.0
*/
public static def list = ["a", false, 2]
* assert list.pop() == 2
* assert list == ["a", false]
*
* @param self a List
* @return the item removed from the List
* @throws NoSuchElementException if the list is empty and you try to pop() it.
* @since 1.0
*/
public static Map containing all entries from self and entries,
* giving precedence to entries. Any keys appearing in both Maps
* will appear in the resultant map with values from the entries
* operand. If self map is one of TreeMap, LinkedHashMap, Hashtable
* or Properties, the returned Map will preserve that type, otherwise a HashMap will
* be returned.
*
*
* @param self a Map
* @param entries a Collection of Map.Entry items to be added to the Map.
* @return a new Map containing all key, value pairs from self and entries
* @since 1.6.1
*/
public static def list = [3, 4, 2]
* list.push("x")
* assert list == [3, 4, 2, "x"]
*
* @param self a List
* @param value element to be appended to this list.
* @return true (as per the general contract of the
* Collection.add method).
* @throws NoSuchElementException if the list is empty and you try to pop() it.
* @since 1.5.5
*/
public static def list = [3, 4, 2]
* assert list.last() == 2
* assert list == [3, 4, 2]
*
* @param self a List
* @return the last item from the List
* @throws NoSuchElementException if the list is empty and you try to access the last() item.
* @since 1.5.5
*/
public static def list = [3, 4, 2]
* assert list.first() == 3
* assert list == [3, 4, 2]
*
* @param self a List
* @return the first item from the List
* @throws NoSuchElementException if the list is empty and you try to access the first() item.
* @since 1.5.5
*/
public static def list = [3, 4, 2]
* assert list.head() == 3
* assert list == [3, 4, 2]
*
* @param self a List
* @return the first item from the List
* @throws NoSuchElementException if the list is empty and you try to access the head() item.
* @since 1.5.5
*/
public static def list = [3, 4, 2]
* assert list.tail() == [4, 2]
* assert list == [3, 4, 2]
*
* @param self a List
* @return a list without its first element
* @throws NoSuchElementException if the list is empty and you try to access the tail() item.
* @since 1.5.6
*/
public static def list = ["a", 4, false]
* assert list.reverse() == [false, 4, "a"]
* assert list == ["a", 4, false]
*
* @param self a List
* @return a reversed List
* @since 1.0
*/
public static true if the intersection of two collections is empty.
*
* @param left a Collection
* @param right a Collection
* @return boolean true if the intersection of two collections
* is empty, false otherwise.
* @since 1.0
*/
public static boolean disjoint(Collection left, Collection right) {
if (left.isEmpty() || right.isEmpty())
return true;
Collection pickFrom = new TreeSet(new NumberAwareComparator());
pickFrom.addAll(right);
for (final Object o : left) {
if (pickFrom.contains(o))
return false;
}
return true;
}
// Default comparator for objects accounting for numbers of different types.
// Also handles nulls. Null is less than everything else.
private static class NumberAwareComparatornull.
*
* @param left this array
* @param right the list being compared
* @return true if the contents of both collections are equal
* @since 1.5.0
*/
public static boolean equals(Object[] left, List right) {
return coercedEquals(left, right);
}
/**
* Determines if the contents of this list are equal to the
* contents of the given array in the same order. This returns
* false if either collection is null.
* assert [1, "a"].equals( [ 1, "a" ] as Object[] )* * @param left this List * @param right this Object[] being compared to * @return true if the contents of both collections are equal * @since 1.5.0 */ public static boolean equals(List left, Object[] right) { return coercedEquals(right, left); } private static boolean coercedEquals(Object[] left, List right) { if (left == null) { return right == null; } if (right == null) { return false; } if (left.length != right.size()) { return false; } final NumberAwareComparator numberAwareComparator = new NumberAwareComparator(); for (int i = left.length - 1; i >= 0; i--) { final Object o1 = left[i]; final Object o2 = right.get(i); if (o1 == null) { if (o2 != null) return false; } else { if (o1 instanceof Number) { if (!(o2 instanceof Number && numberAwareComparator.compare(o1, o2) == 0)) { return false; } } else { if (!DefaultTypeTransformation.compareEqual(o1, o2)) return false; } } } return true; } /** * Compare the contents of two Lists. Order matters. * If numbers exist in the Lists, then they are compared as numbers, * for example 2 == 2L. If either list is
null, the result
* is false.
* assert ["a", 2].equals(["a", 2])
* assert ![2, "a"].equals("a", 2)
* assert [2.0, "a"].equals(2L, "a") // number comparison at work
*
* @param left this List
* @param right the List being compared to.
* @return boolean true if the contents of both lists are identical,
* false otherwise.
* @since 1.0
*/
public static boolean equals(List left, List right) {
if (left == null) {
return right == null;
}
if (right == null) {
return false;
}
if (left.size() != right.size()) {
return false;
}
final NumberAwareComparator numberAwareComparator = new NumberAwareComparator();
final Iterator it1 = left.iterator(), it2 = right.iterator();
while (it1.hasNext()) {
final Object o1 = it1.next();
final Object o2 = it2.next();
if (o1 == null) {
if (o2 != null) return false;
} else {
if (o1 instanceof Number) {
if (!(o2 instanceof Number && numberAwareComparator.compare(o1, o2) == 0)) {
return false;
}
} else {
if (!DefaultTypeTransformation.compareEqual(o1, o2)) return false;
}
}
}
return true;
}
/**
* Compare the contents of two Sets for equality using Groovy's coercion rules.
* WARNING: may not be included in 1.1
*
* Returns true if the two sets have the same size, and every member
* of the specified set is contained in this set (or equivalently, every member
* of this set is contained in the specified set).
* If numbers exist in the Lists, then they are compared as numbers,
* for example 2 == 2L. If either list is null, the result
* is false.
*
* @param self this List
* @param other the List being compared to
* @return true if the contents of both lists are identical
*/
/*
public static boolean coercedEquals(Set self, Set other) {
if (self == null) {
return other == null;
}
if (other == null) {
return false;
}
if (self.size() != other.size()) {
return false;
}
final NumberAwareComparator numberAwareComparator = new NumberAwareComparator();
final Iterator it1 = self.iterator();
Collection otherItems = new HashSet(other);
while (it1.hasNext()) {
final Object o1 = it1.next();
if (o1 == null && !other.contains(null)) return false;
final Iterator it2 = otherItems.iterator();
Object foundItem = null;
while (it2.hasNext() && foundItem == null) {
final Object o2 = it2.next();
if (o1 instanceof Number) {
if (o2 instanceof Number && numberAwareComparator.compare(o1, o2) == 0) {
foundItem = o2;
}
} else {
try {
if (DefaultTypeTransformation.compareEqual(o1, o2)) {
foundItem = o2;
}
} catch (ClassCastException e) {
// ignore
}
}
}
if (foundItem == null) return false;
otherItems.remove(foundItem);
}
return otherItems.size() == 0;
}
*/
/**
* Create a Set composed of the elements of the first set minus the
* elements of the given collection.
*
* TODO: remove using number comparator?
*
* @param self a set object
* @param operands the items to remove from the set
* @return the resulting set
* @since 1.5.0
*/
public static assert [1, "a", true, true, false, 5.3] - [true, 5.3] == [1, "a", false]* * @param self a List * @param removeMe a Collection of elements to remove * @return a List with the supplied elements removed * @since 1.0 */ public static
assert ["a", 5, 5, true] - 5 == ["a", true]* * @param self a List object * @param operand an element to remove from the list * @return the resulting List with the operand removed * @since 1.0 */ public static
RFC 4648.
*
* @param data Byte array to be encoded
* @param chunked whether or not the Base64 encoded data should be MIME chunked
* @return object which will write the Base64 encoding of the byte array
* @since 1.5.1
*/
public static Writable encodeBase64(Byte[] data, final boolean chunked) {
return encodeBase64(DefaultTypeTransformation.convertToByteArray(data), chunked);
}
/**
* Produce a Writable object which writes the Base64 encoding of the byte array.
* Calling toString() on the result returns the encoding as a String. For more
* information on Base64 encoding and chunking see RFC 4648.
*
* @param data Byte array to be encoded
* @return object which will write the Base64 encoding of the byte array
* @since 1.0
*/
public static Writable encodeBase64(Byte[] data) {
return encodeBase64(DefaultTypeTransformation.convertToByteArray(data), false);
}
/**
* Produce a Writable object which writes the Base64 encoding of the byte array.
* Calling toString() on the result returns the encoding as a String. For more
* information on Base64 encoding and chunking see RFC 4648.
*
* @param data byte array to be encoded
* @param chunked whether or not the Base64 encoded data should be MIME chunked
* @return object which will write the Base64 encoding of the byte array
* @since 1.5.7
*/
public static Writable encodeBase64(final byte[] data, final boolean chunked) {
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(T_TABLE[d >> 18]);
writer.write(T_TABLE[(d >> 12) & 0X3F]);
writer.write(T_TABLE[(d >> 6) & 0X3F]);
writer.write(T_TABLE[d & 0X3F]);
if (chunked && ++charCount == 19) {
writer.write(CHUNK_SEPARATOR);
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(T_TABLE[d >> 18]);
writer.write(T_TABLE[(d >> 12) & 0X3F]);
writer.write((dLimit + 1 < data.length) ? T_TABLE[(d >> 6) & 0X3F] : '=');
writer.write('=');
if (chunked && charCount != 0) {
writer.write(CHUNK_SEPARATOR);
}
}
return writer;
}
public String toString() {
StringWriter buffer = new StringWriter();
try {
writeTo(buffer);
} catch (IOException e) {
throw new StringWriterIOException(e);
}
return buffer.toString();
}
};
}
/**
* Produce a Writable object which writes the Base64 encoding of the byte array.
* Calling toString() on the result returns the encoding as a String. For more
* information on Base64 encoding and chunking see RFC 4648.
*
* @param data byte array to be encoded
* @return object which will write the Base64 encoding of the byte array
* @since 1.0
*/
public static Writable encodeBase64(final byte[] data) {
return encodeBase64(data, false);
}
private static final byte[] TRANSLATE_TABLE = (
//
"\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 String from Base64 into a byte array.
*
* @param value the string to be decoded
* @return the decoded bytes as an array
* @since 1.0
*/
public static byte[] decodeBase64(String value) {
int byteShift = 4;
int tmp = 0;
boolean done = false;
final StringBuilder buffer = new StringBuilder();
for (int i = 0; i != value.length(); i++) {
final char c = value.charAt(i);
final int sixBit = (c < 123) ? TRANSLATE_TABLE[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.
*
* @param self an array object
* @param idx the index of interest
* @return the returned value from the array
* @since 1.5.0
*/
protected static Object primitiveArrayGet(Object self, int idx) {
return Array.get(self, normaliseIndex(idx, Array.getLength(self)));
}
/**
* Implements the getAt(Range) method for primitve type arrays.
*
* @param self an array object
* @param range the range of indices of interest
* @return the returned values from the array corresponding to the range
* @since 1.5.0
*/
protected static List primitiveArrayGet(Object self, Range range) {
List answer = new ArrayList();
for (Object next : range) {
int idx = DefaultTypeTransformation.intUnbox(next);
answer.add(primitiveArrayGet(self, idx));
}
return answer;
}
/**
* Implements the getAt(Collection) method for primitve type arrays. Each
* value in the collection argument is assumed to be a valid array index.
* The value at each index is then added to a list which is returned.
*
* @param self an array object
* @param indices the indices of interest
* @return the returned values from the array
* @since 1.0
*/
protected static List primitiveArrayGet(Object self, Collection indices) {
List answer = new ArrayList();
for (Object value : indices) {
if (value instanceof Range) {
answer.addAll(primitiveArrayGet(self, (Range) value));
} else if (value instanceof List) {
answer.addAll(primitiveArrayGet(self, (List) value));
} else {
int idx = DefaultTypeTransformation.intUnbox(value);
answer.add(primitiveArrayGet(self, idx));
}
}
return answer;
}
/**
* Implements the setAt(int idx) method for primitve type arrays.
*
* @param self an object
* @param idx the index of interest
* @param newValue the new value to be put into the index of interest
* @return the added value
* @since 1.5.0
*/
protected static Object primitiveArrayPut(Object self, int idx, Object newValue) {
Array.set(self, normaliseIndex(idx, Array.getLength(self)), newValue);
return 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
* @since 1.0
*/
public static Character toCharacter(String self) {
return self.charAt(0);
}
/**
* Converts the given string into a Boolean object.
* If the trimmed string is "true", "y" or "1" (ignoring case)
* then the result is true othewrwise it is false.
*
* @param self a String
* @return The Boolean value
* @since 1.0
*/
public static Boolean toBoolean(String self) {
final String trimmed = self.trim();
if ("true".equalsIgnoreCase(trimmed) || "y".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) {
return Boolean.TRUE;
} else {
return Boolean.FALSE;
}
}
/**
* Convenience method to split a string (with whitespace as delimiter)
* Like tokenize, but returns an Array of Strings instead of a List
*
* @param self the string to split
* @return String[] result of split
* @since 1.5.0
*/
public static String[] split(String self) {
StringTokenizer st = new StringTokenizer(self);
String[] strings = new String[st.countTokens()];
for (int i = 0; i < strings.length; i++) {
strings[i] = st.nextToken();
}
return strings;
}
/**
* Convenience method to split a GString (with whitespace as delimiter).
*
* @param self the GString to split
* @return String[] result of split
* @see #split(String)
* @since 1.6.1
*/
public static String[] split(GString self) {
return split(self.toString());
}
/**
* Tokenize a String based on the given string delimiter.
*
* @param self a String
* @param token the delimiter
* @return a List of tokens
* @see java.util.StringTokenizer#StringTokenizer(java.lang.String, java.lang.String)
* @since 1.0
*/
public static List tokenize(String self, String token) {
return InvokerHelper.asList(new StringTokenizer(self, token));
}
/**
* Tokenize a String (with a whitespace as the delimiter).
*
* @param self a String
* @return a List of tokens
* @see StringTokenizer#StringTokenizer(java.lang.String)
* @since 1.0
*/
public static List tokenize(String self) {
return InvokerHelper.asList(new StringTokenizer(self));
}
/**
* Appends the String representation of the given operand to this string.
*
* @param left a String
* @param value any Object
* @return the new string with the object appended
* @since 1.0
*/
public static String plus(String left, Object value) {
return left + toString(value);
}
/**
* Appends a String to the string representation of this number.
*
* @param value a Number
* @param right a String
* @return a String
* @since 1.0
*/
public static String plus(Number value, String right) {
return toString(value) + right;
}
/**
* Appends a String to this StringBuffer.
*
* @param left a StringBuffer
* @param value a String
* @return a String
* @since 1.0
*/
public static String plus(StringBuffer left, String value) {
return left + value;
}
/**
* Remove a part of a String. This replaces the first occurrence
* of target within self with '' and returns the result. If
* target is a regex Pattern, the first occurrence of that
* pattern will be removed (using regex matching), otherwise
* the first occurrence of target.toString() will be removed.
*
* @param self a String
* @param target an object representing the part to remove
* @return a String minus the part to be removed
* @since 1.0
*/
public static String minus(String self, Object target) {
if (target instanceof Pattern) {
return ((Pattern)target).matcher(self).replaceFirst("");
}
String text = toString(target);
int index = self.indexOf(text);
if (index == -1) return self;
int end = index + text.length();
if (self.length() > end) {
return self.substring(0, index) + self.substring(end);
}
return self.substring(0, index);
}
/**
* Provide an implementation of contains() like
* {@link Collection#contains(Object)} 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
* @since 1.0
*/
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
* @since 1.0
*/
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
* @since 1.0
*/
public static String next(String self) {
StringBuilder buffer = new StringBuilder(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
* @since 1.0
*/
public static String previous(String self) {
StringBuilder buffer = new StringBuilder(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
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static Process execute(String self) throws IOException {
return Runtime.getRuntime().exec(self);
}
/**
* Executes the command specified by the String 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
* java.lang.ProcessBuilder.
*
* @param commandArray an array of String containing the command name and
* parameters as separate items in the array.
* @return the Process which has just started for this command line string.
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static Process execute(String[] commandArray) throws IOException {
return Runtime.getRuntime().exec(commandArray);
}
/**
* Executes the command specified by the self with environments envp
* under the working directory dir.
* For more control over the process mechanism in JDK 1.5 you can use java.lang.ProcessBuilder.
*
* @param self a command line String to be executed.
* @param envp an array of Strings, each element of which
* has environment variable settings in the format
* name=value, or
* null if the subprocess should inherit
* the environment of the current process.
* @param dir the working directory of the subprocess, or
* null if the subprocess should inherit
* the working directory of the current process.
* @return the Process which has just started for this command line string.
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static Process execute(String self, final String[] envp, File dir) throws IOException {
return Runtime.getRuntime().exec(self, envp, dir);
}
/**
* Executes the command specified by the String list that is the parameter.
* The first item in the array is the command the others are the parameters. All entries
* must be Strings. For more control over the process mechanism in JDK 1.5 you
* can use java.lang.ProcessBuilder.
*
* @param commandList a list of String containing the command name and
* parameters as separate items in the list.
* @return the Process which has just started for this command line string.
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static Process execute(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);
}
/**
* Executes the command specified by the self with environments envp
* under the working directory dir.
* For more control over the process mechanism in JDK 1.5 you can use java.lang.ProcessBuilder.
*
* @param self a command line String to be executed.
* @param envp a List of Strings, each member of which
* has environment variable settings in the format
* name=value, or
* null if the subprocess should inherit
* the environment of the current process.
* @param dir the working directory of the subprocess, or
* null if the subprocess should inherit
* the working directory of the current process.
* @return the Process which has just started for this command line string.
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static Process execute(String self, List envp, File dir) throws IOException {
if (envp == null) {
return execute(self, (String[]) null, dir);
}
String[] commandArray = new String[envp.size()];
Iterator it = envp.iterator();
for (int i = 0; it.hasNext(); ++i) {
commandArray[i] = it.next().toString();
}
return execute(self, commandArray, dir);
}
/**
* 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 repetition
* @throws IllegalArgumentException if the number of repetitions is < 0
* @since 1.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);
}
StringBuilder answer = new StringBuilder(self);
for (int i = 1; i < size; i++) {
answer.append(self);
}
return answer.toString();
}
/**
* Returns the string representation of the given array.
*
* @param self an array
* @return the string representation
* @since 1.6
*/
public static String toString(boolean[] self) {
return InvokerHelper.toString(self);
}
/**
* Returns the string representation of the given array.
*
* @param self an array
* @return the string representation
* @since 1.6
*/
public static String toString(byte[] self) {
return InvokerHelper.toString(self);
}
/**
* Returns the string representation of the given array.
*
* @param self an array
* @return the string representation
* @since 1.6
*/
public static String toString(char[] self) {
return InvokerHelper.toString(self);
}
/**
* Returns the string representation of the given array.
*
* @param self an array
* @return the string representation
* @since 1.6
*/
public static String toString(short[] self) {
return InvokerHelper.toString(self);
}
/**
* Returns the string representation of the given array.
*
* @param self an array
* @return the string representation
* @since 1.6
*/
public static String toString(int[] self) {
return InvokerHelper.toString(self);
}
/**
* Returns the string representation of the given array.
*
* @param self an array
* @return the string representation
* @since 1.6
*/
public static String toString(long[] self) {
return InvokerHelper.toString(self);
}
/**
* Returns the string representation of the given array.
*
* @param self an array
* @return the string representation
* @since 1.6
*/
public static String toString(float[] self) {
return InvokerHelper.toString(self);
}
/**
* Returns the string representation of the given array.
*
* @param self an array
* @return the string representation
* @since 1.6
*/
public static String toString(double[] self) {
return InvokerHelper.toString(self);
}
/**
* Returns the string representation of the given map.
*
* @param self a Map
* @return the string representation
* @see #toMapString(Map)
* @since 1.0
*/
public static String toString(AbstractMap self) {
return toMapString(self);
}
/**
* Returns the string representation of this map. The string displays the
* contents of the map, i.e. [one:1, two:2, three:3].
*
* @param self a Map
* @return the string representation
* @since 1.0
*/
public static String toMapString(Map self) {
return (self == null) ? "null" : InvokerHelper.toMapString(self);
}
/**
* Returns the string representation of the given collection. The string
* displays the contents of the collection, i.e.
* [1, 2, a].
*
* @param self a Collection
* @return the string representation
* @see #toListString(Collection)
* @since 1.0
*/
public static String toString(AbstractCollection self) {
return toListString(self);
}
/**
* Returns the string representation of the given list. The string
* displays the contents of the list, similar to a list literal, i.e.
* [1, 2, a].
*
* @param self a Collection
* @return the string representation
* @since 1.0
*/
public static String toListString(Collection self) {
return (self == null) ? "null" : InvokerHelper.toListString(self);
}
/**
* Returns the string representation of this array's contents.
*
* @param self an Object[]
* @return the string representation
* @see #toArrayString(Object[])
* @since 1.0
*/
public static String toString(Object[] self) {
return toArrayString(self);
}
/**
* Returns the string representation of the given array. The string
* displays the contents of the array, similar to an array literal, i.e.
* {1, 2, "a"}.
*
* @param self an Object[]
* @return the string representation
* @since 1.0
*/
public static String toArrayString(Object[] self) {
return (self == null) ? "null" : InvokerHelper.toArrayString(self);
}
/**
* Create a String representation of this object.
* @param value an object
* @return a string.
* @since 1.0
*/
public static String toString(Object value) {
return InvokerHelper.toString(value);
}
// Number based methods
//-------------------------------------------------------------------------
/**
* Increment a Character by one.
*
* @param self a Character
* @return an incremented Character
* @since 1.5.7
*/
public static Character next(Character self) {
return (char) (self + 1);
}
/**
* Increment a Number by one.
*
* @param self a Number
* @return an incremented Number
* @since 1.0
*/
public static Number next(Number self) {
return NumberNumberPlus.plus(self, ONE);
}
/**
* Decrement a Character by one.
*
* @param self a Character
* @return a decremented Character
* @since 1.5.7
*/
public static Character previous(Character self) {
return (char) (self - 1);
}
/**
* Decrement a Number by one.
*
* @param self a Number
* @return a decremented Number
* @since 1.0
*/
public static Number previous(Number self) {
return NumberNumberMinus.minus(self, ONE);
}
/**
* Add a Character and a Number. The ordinal value of the Character
* is used in the addition (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
* This operation will always create a new object for the result,
* while the operands remain unchanged.
*
* @see Integer#valueOf(int)
* @param left a Character
* @param right a Number
* @return the Number corresponding to the addition of left and right
* @since 1.0
*/
public static Number plus(Character left, Number right) {
return NumberNumberPlus.plus(Integer.valueOf(left), right);
}
/**
* Add a Number and a Character. The ordinal value of the Character
* is used in the addition (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @see Integer#valueOf(int)
* @param left a Number
* @param right a Character
* @return The Number corresponding to the addition of left and right
* @since 1.0
*/
public static Number plus(Number left, Character right) {
return NumberNumberPlus.plus(left, Integer.valueOf(right));
}
/**
* Add one Character to another. The ordinal values of the Characters
* are used in the addition (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
* This operation will always create a new object for the result,
* while the operands remain unchanged.
*
* @see #plus(Number, Character)
* @param left a Character
* @param right a Character
* @return the Number corresponding to the addition of left and right
* @since 1.0
*/
public static Number plus(Character left, Character right) {
return plus(Integer.valueOf(left), right);
}
/**
* Compare a Character and a Number. The ordinal value of the Character
* is used in the comparison (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right a Number
* @return the result of the comparison
* @since 1.0
*/
public static int compareTo(Character left, Number right) {
return compareTo(Integer.valueOf(left), right);
}
/**
* Compare a Number and a Character. The ordinal value of the Character
* is used in the comparison (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Number
* @param right a Character
* @return the result of the comparison
* @since 1.0
*/
public static int compareTo(Number left, Character right) {
return compareTo(left, Integer.valueOf(right));
}
/**
* Compare two Characters. The ordinal values of the Characters
* are compared (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right a Character
* @return the result of the comparison
* @since 1.0
*/
public static int compareTo(Character left, Character right) {
return compareTo(Integer.valueOf(left), right);
}
/**
* Compare two Numbers. Equality (==) for numbers dispatches to this.
*
* @param left a Number
* @param right another Number to compare to
* @return the comparision of both numbers
* @since 1.0
*/
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. The ordinal value of the Character
* is used in the subtraction (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right a Number
* @return the Number corresponding to the subtraction of right from left
* @since 1.0
*/
public static Number minus(Character left, Number right) {
return NumberNumberMinus.minus(Integer.valueOf(left), right);
}
/**
* Subtract a Character from a Number. The ordinal value of the Character
* is used in the subtraction (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Number
* @param right a Character
* @return the Number corresponding to the subtraction of right from left
* @since 1.0
*/
public static Number minus(Number left, Character right) {
return NumberNumberMinus.minus(left, Integer.valueOf(right));
}
/**
* Subtract one Character from another. The ordinal values of the Characters
* is used in the comparison (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right a Character
* @return the Number corresponding to the subtraction of right from left
* @since 1.0
*/
public static Number minus(Character left, Character right) {
return minus(Integer.valueOf(left), right);
}
/**
* Multiply a Character by a Number. The ordinal value of the Character
* is used in the multiplcation (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right a Number
* @return the Number corresponding to the multiplication of left by right
* @since 1.0
*/
public static Number multiply(Character left, Number right) {
return NumberNumberMultiply.multiply(Integer.valueOf(left), right);
}
/**
* Multiply a Number by a Character. The ordinal value of the Character
* is used in the multiplication (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Number
* @param right a Character
* @return the multiplication of left by right
* @since 1.0
*/
public static Number multiply(Number left, Character right) {
return NumberNumberMultiply.multiply(Integer.valueOf(right), left);
}
/**
* Multiply two Characters. The ordinal values of the Characters
* are used in the multiplication (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right another Character
* @return the Number corresponding to the multiplication of left by right
* @since 1.0
*/
public static Number multiply(Character left, Character right) {
return multiply(Integer.valueOf(left), right);
}
/**
* Multiply a BigDecimal and a Double.
* Note: This method was added to enforce the Groovy rule of
* BigDecimal*Double == Double. Without this method, the
* multiply(BigDecimal) method in BigDecimal would respond
* and return a BigDecimal instead. Since BigDecimal is preferred
* over Number, the Number*Number method is not chosen as in older
* versions of Groovy.
*
* @param left a BigDecimal
* @param right a Double
* @return the multiplication of left by right
* @since 1.0
*/
public static Number multiply(BigDecimal left, Double right) {
return NumberMath.multiply(left, right);
}
/**
* Multiply a BigDecimal and a BigInteger.
* Note: This method was added to enforce the Groovy rule of
* BigDecimal*long == long. Without this method, the
* multiply(BigDecimal) method in BigDecimal would respond
* and return a BigDecimal instead. Since BigDecimal is preferred
* over Number, the Number*Number method is not chosen as in older
* versions of Groovy. BigInteger is the fallback for all integer
* types in Groovy
*
* @param left a BigDecimal
* @param right a BigInteger
* @return the multiplication of left by right
* @since 1.0
*/
public static Number multiply(BigDecimal left, BigInteger right) {
return NumberMath.multiply(left, right);
}
/**
* Power of a Number to a certain exponent. Called by the '**' operator.
*
* @param self a Number
* @param exponent a Number exponent
* @return a Number to the power of a certain exponent
* @since 1.0
*/
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 (int) answer;
} else if ((double) ((long) answer) == answer) {
return (long) answer;
} else {
return answer;
}
}
/**
* Divide a Character by a Number. The ordinal value of the Character
* is used in the division (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right a Number
* @return the Number corresponding to the division of left by right
* @since 1.0
*/
public static Number div(Character left, Number right) {
return NumberNumberDiv.div(Integer.valueOf(left), right);
}
/**
* Divide a Number by a Character. The ordinal value of the Character
* is used in the division (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Number
* @param right a Character
* @return the Number corresponding to the division of left by right
* @since 1.0
*/
public static Number div(Number left, Character right) {
return NumberNumberDiv.div(left, Integer.valueOf(right));
}
/**
* Divide one Character by another. The ordinal values of the Characters
* are used in the division (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right another Character
* @return the Number corresponding to the division of left by right
* @since 1.0
*/
public static Number div(Character left, Character right) {
return div(Integer.valueOf(left), right);
}
/**
* Integer Divide a Character by a Number. The ordinal value of the Character
* is used in the division (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right a Number
* @return a Number (an Integer) resulting from the integer division operation
* @since 1.0
*/
public static Number intdiv(Character left, Number right) {
return intdiv(Integer.valueOf(left), right);
}
/**
* Integer Divide a Number by a Character. The ordinal value of the Character
* is used in the division (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Number
* @param right a Character
* @return a Number (an Integer) resulting from the integer division operation
* @since 1.0
*/
public static Number intdiv(Number left, Character right) {
return intdiv(left, Integer.valueOf(right));
}
/**
* Integer Divide two Characters. The ordinal values of the Characters
* are used in the division (the ordinal value is the unicode
* value which for simple character sets is the ASCII value).
*
* @param left a Character
* @param right another Character
* @return a Number (an Integer) resulting from the integer division operation
* @since 1.0
*/
public static Number intdiv(Character left, Character right) {
return intdiv(Integer.valueOf(left), right);
}
/**
* Integer Divide two Numbers.
*
* @param left a Number
* @param right another Number
* @return a Number (an Integer) resulting from the integer division operation
* @since 1.0
*/
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
* @since 1.0
*/
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 bitwise AND
* @return the bitwise AND of both Numbers
* @since 1.0
*/
public static Number and(Number left, Number right) {
return NumberMath.and(left, right);
}
/**
* Bitwise AND together two BitSets.
*
* @param left a BitSet
* @param right another BitSet to bitwise AND
* @return the bitwise AND of both BitSets
* @since 1.5.0
*/
public static BitSet and(BitSet left, BitSet right) {
BitSet result = (BitSet) left.clone();
result.and(right);
return result;
}
/**
* Bitwise XOR together two BitSets. Called when the '^' operator is used
* between two bit sets.
*
* @param left a BitSet
* @param right another BitSet to bitwise AND
* @return the bitwise XOR of both BitSets
* @since 1.5.0
*/
public static BitSet xor(BitSet left, BitSet right) {
BitSet result = (BitSet) left.clone();
result.xor(right);
return result;
}
/**
* Bitwise NEGATE a BitSet.
*
* @param self a BitSet
* @return the bitwise NEGATE of the BitSet
* @since 1.5.0
*/
public static BitSet bitwiseNegate(BitSet self) {
BitSet result = (BitSet) self.clone();
result.flip(0, result.size() - 1);
return result;
}
/**
* Bitwise OR together two BitSets. Called when the '|' operator is used
* between two bit sets.
*
* @param left a BitSet
* @param right another BitSet to bitwise AND
* @return the bitwise OR of both BitSets
* @since 1.5.0
*/
public static BitSet or(BitSet left, BitSet right) {
BitSet result = (BitSet) left.clone();
result.or(right);
return result;
}
/**
* Bitwise XOR together two Numbers. Called when the '|' operator is used.
*
* @param left a Number
* @param right another Number to bitwse XOR
* @return the bitwise XOR of both Numbers
* @since 1.0
*/
public static Number xor(Number left, Number right) {
return NumberMath.xor(left, right);
}
/**
* Performs a division modulus operation. Called by the '%' operator.
*
* @param left a Number
* @param right another Number to mod
* @return the modulus result
* @since 1.0
*/
public static Number mod(Number left, Number right) {
return NumberMath.mod(left, right);
}
/**
* Negates the number. Equivalent to the '-' operator when it preceeds
* a single operand, i.e. -10
*
* @param left a Number
* @return the negation of the number
* @since 1.5.0
*/
public static Number unaryMinus(Number left) {
return NumberMath.unaryMinus(left);
}
/**
* Executes the closure this many times, starting from zero. The current
* index is passed to the closure each time.
* Example:
* 10.times {
* println it
* }
* Prints the numbers 0 through 9.
*
* @param self a Number
* @param closure the closure to call a number of times
* @since 1.0
*/
public static void times(Number self, Closure closure) {
for (int i = 0, size = self.intValue(); i < size; i++) {
closure.call(i);
if (closure.getDirective() == Closure.DONE) {
break;
}
}
}
/**
* Iterates from this number up to the given number, inclusive,
* incrementing by one each time.
*
* @param self a Number
* @param to another Number to go up to
* @param closure the closure to call
* @since 1.0
*/
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(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
}
/**
* Iterates from this number up to the given number, inclusive,
* incrementing by one each time.
*
* @param self a long
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
}
/**
* Iterates from this number up to the given number, inclusive,
* incrementing by one each time.
*
* @param self a Long
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
}
/**
* Iterates from this number up to the given number, inclusive,
* incrementing by one each time.
*
* @param self a float
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
}
/**
* Iterates from this number up to the given number, inclusive,
* incrementing by one each time.
*
* @param self a Float
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
}
/**
* Iterates from this number up to the given number, inclusive,
* incrementing by one each time.
*
* @param self a double
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
public static void upto(double self, Number to, Closure closure) {
double to1 = to.doubleValue();
if (self <= to1) {
for (double i = self; i <= to1; i++) {
closure.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
}
/**
* Iterates from this number up to the given number, inclusive,
* incrementing by one each time.
*
* @param self a Double
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
public static void upto(Double self, Number to, Closure closure) {
double to1 = to.doubleValue();
if (self <= to1) {
for (double i = self; i <= to1; i++) {
closure.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
}
/**
* Iterates from this number up to the given number, inclusive,
* incrementing by one each time. Example:
* 0.upto( 10 ) {
* println it
* }
* Prints numbers 0 to 10
*
* @param self a BigInteger
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
public static void upto(BigInteger self, Number to, Closure closure) {
if (to instanceof BigDecimal) {
final BigDecimal one = BigDecimal.valueOf(10, 1);
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
} else if (to instanceof BigInteger) {
final BigInteger one = BigInteger.valueOf(1);
BigInteger to1 = (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 + ")");
} else {
final BigInteger one = BigInteger.valueOf(1);
BigInteger to1 = new BigInteger(to.toString());
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 + ")");
}
}
/**
* Iterates from this number up to the given number, inclusive,
* incrementing by one each time.
* 0.1.upto( 10 ) {
* println it
* }
* Prints numbers 0.1, 1.1, 2.1... to 9.1
*
* @param self a BigDecimal
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
public static void upto(BigDecimal self, Number to, Closure closure) {
final BigDecimal one = BigDecimal.valueOf(10, 1); // That's what you get for "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.call(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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
} else {
BigDecimal to1 = new BigDecimal(to.toString());
if (self.compareTo(to1) <= 0) {
for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
closure.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
}
}
/**
* Iterates from this number down to the given number, inclusive,
* decrementing by one each time.
*
* @param self a Number
* @param to another Number to go down to
* @param closure the closure to call
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
}
/**
* Iterates from this number down to the given number, inclusive,
* decrementing by one each time.
*
* @param self a long
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
}
/**
* Iterates from this number down to the given number, inclusive,
* decrementing by one each time.
*
* @param self a Long
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
}
/**
* Iterates from this number down to the given number, inclusive,
* decrementing by one each time.
*
* @param self a float
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
}
/**
* Iterates from this number down to the given number, inclusive,
* decrementing by one each time.
*
* @param self a Float
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
}
/**
* Iterates from this number down to the given number, inclusive,
* decrementing by one each time.
*
* @param self a double
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
}
/**
* Iterates from this number down to the given number, inclusive,
* decrementing by one each time.
*
* @param self a Double
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
}
/**
* Iterates from this number down to the given number, inclusive,
* decrementing by one each time.
*
* @param self a BigInteger
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
public static void downto(BigInteger self, Number to, Closure closure) {
if (to instanceof BigDecimal) {
final BigDecimal one = BigDecimal.valueOf(10, 1); // That's what you get for "1.0".
final BigDecimal to1 = (BigDecimal) to;
final BigDecimal selfD = new BigDecimal(self);
if (selfD.compareTo(to1) >= 0) {
for (BigDecimal i = selfD; i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.call(i.toBigInteger());
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
} else if (to instanceof BigInteger) {
final BigInteger one = BigInteger.valueOf(1);
final BigInteger to1 = (BigInteger) to;
if (self.compareTo(to1) >= 0) {
for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
} else {
final BigInteger one = BigInteger.valueOf(1);
final BigInteger to1 = new BigInteger(to.toString());
if (self.compareTo(to1) >= 0) {
for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
}
}
/**
* Iterates from this number down to the given number, inclusive,
* decrementing by one each time. Each number is passed to the closure.
* Example:
* 10.5.downto(0) {
* println it
* }
* Prints numbers 10.5, 9.5 ... to 0.5.
*
* @param self a BigDecimal
* @param to the end number
* @param closure the code to execute for each number
* @since 1.0
*/
public static void downto(BigDecimal self, Number to, Closure closure) {
final BigDecimal one = BigDecimal.valueOf(10, 1); // Quick way to get "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.call(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.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
} else {
BigDecimal to1 = new BigDecimal(to.toString());
if (self.compareTo(to1) >= 0) {
for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
closure.call(i);
}
} else
throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
}
}
/**
* Iterates from this number up to the given number using a step increment.
* Each intermediate number is passed to the given closure. Example:
* 0.step( 10, 2 ) {
* println it
* }
* Prints even numbers 0 through 8.
*
* @param self a Number to start with
* @param to a Number to go up to, exclusive
* @param stepNumber a Number representing the step increment
* @param closure the closure to call
* @since 1.0
*/
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 = BigDecimal.valueOf(0, 1); // Same as "0.0".
BigDecimal self1 = (self instanceof BigDecimal) ? (BigDecimal) self : new BigDecimal(self.toString());
BigDecimal to1 = (to instanceof BigDecimal) ? (BigDecimal) to : new BigDecimal(to.toString());
BigDecimal stepNumber1 = (stepNumber instanceof BigDecimal) ? (BigDecimal) stepNumber : new BigDecimal(stepNumber.toString());
if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) {
for (BigDecimal i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) {
closure.call(i);
}
} else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) {
for (BigDecimal i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) {
closure.call(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 = BigInteger.valueOf(0);
BigInteger self1 = (self instanceof BigInteger) ? (BigInteger) self : new BigInteger(self.toString());
BigInteger to1 = (to instanceof BigInteger) ? (BigInteger) to : new BigInteger(to.toString());
BigInteger stepNumber1 = (stepNumber instanceof BigInteger) ? (BigInteger) stepNumber : new BigInteger(stepNumber.toString());
if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) {
for (BigInteger i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) {
closure.call(i);
}
} else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) {
for (BigInteger i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) {
closure.call(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.call(i);
}
} else if (stepNumber1 < 0 && to1 < self1) {
for (int i = self1; i > to1; i += stepNumber1) {
closure.call(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
* @since 1.0
*/
//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
* @since 1.0
*/
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
* @since 1.0
*/
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
* @since 1.0
*/
public static double abs(Double number) {
return Math.abs(number);
}
/**
* Round the value
*
* @param number a Float
* @return the rounded value of that Float
* @since 1.0
*/
public static int round(Float number) {
return Math.round(number.floatValue());
}
/**
* Round the value
*
* @param number a Float
* @param precision the number of decimal places to keep
* @return the Float rounded to the number of decimal places specified by precision
* @since 1.6.x
*/
public static float round(Float number, int precision) {
return (float)(Math.floor(number.doubleValue()*Math.pow(10,precision)+0.5)/Math.pow(10,precision));
}
/**
* Truncate the value
*
* @param number a Float
* @param precision the number of decimal places to keep
* @return the Float truncated to the number of decimal places specified by precision
* @since 1.6.x
*/
public static float trunc(Float number, int precision) {
return (float)(Math.floor(number.doubleValue()*Math.pow(10,precision))/Math.pow(10,precision));
}
/**
* Truncate the value
*
* @param number a Double
* @return the Double truncated to 0 decimal places (i.e. a synonym for floor)
* @since 1.6.x
*/
public static float trunc(Float number) {
return (float)Math.floor(number.doubleValue());
}
/**
* Round the value
*
* @param number a Double
* @return the rounded value of that Double
* @since 1.0
*/
public static long round(Double number) {
return Math.round(number);
}
/**
* Round the value
*
* @param number a Double
* @param precision the number of decimal places to keep
* @return the Double rounded to the number of decimal places specified by precision
* @since 1.6.4
*/
public static double round(Double number, int precision) {
return Math.floor(number *Math.pow(10,precision)+0.5)/Math.pow(10,precision);
}
/**
* Truncate the value
*
* @param number a Double
* @return the Double truncated to 0 decimal places (i.e. a synonym for floor)
* @since 1.6.4
*/
public static double trunc(Double number) {
return Math.floor(number);
}
/**
* Truncate the value
*
* @param number a Double
* @param precision the number of decimal places to keep
* @return the Double truncated to the number of decimal places specified by precision
* @since 1.6.4
*/
public static double trunc(Double number, int precision) {
return Math.floor(number *Math.pow(10,precision))/Math.pow(10,precision);
}
/**
* Parse a String into an Integer
*
* @param self a String
* @return an Integer
* @since 1.0
*/
public static Integer toInteger(String self) {
return Integer.valueOf(self.trim());
}
/**
* Parse a String into a Long
*
* @param self a String
* @return a Long
* @since 1.0
*/
public static Long toLong(String self) {
return Long.valueOf(self.trim());
}
/**
* Parse a String into a Short
*
* @param self a String
* @return a Short
* @since 1.5.7
*/
public static Short toShort(String self) {
return Short.valueOf(self.trim());
}
/**
* Parse a String into a Float
*
* @param self a String
* @return a Float
* @since 1.0
*/
public static Float toFloat(String self) {
return Float.valueOf(self.trim());
}
/**
* Parse a String into a Double
*
* @param self a String
* @return a Double
* @since 1.0
*/
public static Double toDouble(String self) {
return Double.valueOf(self.trim());
}
/**
* Parse a String into a BigInteger
*
* @param self a String
* @return a BigInteger
* @since 1.0
*/
public static BigInteger toBigInteger(String self) {
return new BigInteger(self.trim());
}
/**
* Parse a String into a BigDecimal
*
* @param self a String
* @return a BigDecimal
* @since 1.0
*/
public static BigDecimal toBigDecimal(String self) {
return new BigDecimal(self.trim());
}
/**
* Determine if a String can be parsed into an Integer.
*
* @param self a String
* @return true if the string can be parsed
* @since 1.5.0
*/
public static boolean isInteger(String self) {
try {
Integer.valueOf(self.trim());
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
/**
* Determine if a String can be parsed into a Long.
*
* @param self a String
* @return true if the string can be parsed
* @since 1.5.0
*/
public static boolean isLong(String self) {
try {
Long.valueOf(self.trim());
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
/**
* Determine if a String can be parsed into a Float.
*
* @param self a String
* @return true if the string can be parsed
* @since 1.5.0
*/
public static boolean isFloat(String self) {
try {
Float.valueOf(self.trim());
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
/**
* Determine if a String can be parsed into a Double.
*
* @param self a String
* @return true if the string can be parsed
* @since 1.5.0
*/
public static boolean isDouble(String self) {
try {
Double.valueOf(self.trim());
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
/**
* Determine if a String can be parsed into a BigInteger.
*
* @param self a String
* @return true if the string can be parsed
* @since 1.5.0
*/
public static boolean isBigInteger(String self) {
try {
new BigInteger(self.trim());
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
/**
* Determine if a String can be parsed into a BigDecimal.
*
* @param self a String
* @return true if the string can be parsed
* @since 1.5.0
*/
public static boolean isBigDecimal(String self) {
try {
new BigDecimal(self.trim());
return true;
} catch (NumberFormatException nfe) {
return false;
}
}
/**
* Determine if a String can be parsed into a Number.
* Synonym for 'isBigDecimal()'.
*
* @param self a String
* @return true if the string can be parsed
* @see #isBigDecimal(String)
* @since 1.5.0
*/
public static boolean isNumber(String self) {
return isBigDecimal(self);
}
/**
* Determine if a Character is uppercase.
* Synonym for 'Character.isUpperCase(this)'.
*
* @param self a Character
* @return true if the character is uppercase
* @see java.lang.Character#isUpperCase(char)
* @since 1.5.7
*/
public static boolean isUpperCase(Character self) {
return Character.isUpperCase(self);
}
/**
* Determine if a Character is lowercase.
* Synonym for 'Character.isLowerCase(this)'.
*
* @param self a Character
* @return true if the character is lowercase
* @see java.lang.Character#isLowerCase(char)
* @since 1.5.7
*/
public static boolean isLowerCase(Character self) {
return Character.isLowerCase(self);
}
/**
* Determines if a character is a letter.
* Synonym for 'Character.isLetter(this)'.
*
* @param self a Character
* @return true if the character is a letter
* @see java.lang.Character#isLetter(char)
* @since 1.5.7
*/
public static boolean isLetter(Character self) {
return Character.isLetter(self);
}
/**
* Determines if a character is a digit.
* Synonym for 'Character.isDigit(this)'.
*
* @param self a Character
* @return true if the character is a digit
* @see java.lang.Character#isDigit(char)
* @since 1.5.7
*/
public static boolean isDigit(Character self) {
return Character.isDigit(self);
}
/**
* Determines if a character is a letter or digit.
* Synonym for 'Character.isLetterOrDigit(this)'.
*
* @param self a Character
* @return true if the character is a letter or digit
* @see java.lang.Character#isLetterOrDigit(char)
* @since 1.5.7
*/
public static boolean isLetterOrDigit(Character self) {
return Character.isLetterOrDigit(self);
}
/**
* Determines if a character is a whitespace character.
* Synonym for 'Character.isWhitespace(this)'.
*
* @param self a Character
* @return true if the character is a whitespace character
* @see java.lang.Character#isWhitespace(char)
* @since 1.5.7
*/
public static boolean isWhitespace(Character self) {
return Character.isWhitespace(self);
}
/**
* Converts the character to uppercase.
* Synonym for 'Character.toUpperCase(this)'.
*
* @param self a Character to convert
* @return the uppercase equivalent of the character, if any;
* otherwise, the character itself.
* @see java.lang.Character#isUpperCase(char)
* @see java.lang.String#toUpperCase()
* @since 1.5.7
*/
public static char toUpperCase(Character self) {
return Character.toUpperCase(self);
}
/**
* Converts the character to lowercase.
* Synonym for 'Character.toLowerCase(this)'.
*
* @param self a Character to convert
* @return the lowercase equivalent of the character, if any;
* otherwise, the character itself.
* @see java.lang.Character#isLowerCase(char)
* @see java.lang.String#toLowerCase()
* @since 1.5.7
*/
public static char toLowerCase(Character self) {
return Character.toLowerCase(self);
}
/**
* Transform a Number into an Integer
*
* @param self a Number
* @return an Integer
* @since 1.0
*/
public static Integer toInteger(Number self) {
return self.intValue();
}
/**
* Transform a Number into a Long
*
* @param self a Number
* @return an Long
* @since 1.0
*/
public static Long toLong(Number self) {
return self.longValue();
}
/**
* Transform a Number into a Float
*
* @param self a Number
* @return an Float
* @since 1.0
*/
public static Float toFloat(Number self) {
return self.floatValue();
}
/**
* Transform a Number into a Double
*
* @param self a Number
* @return an Double
* @since 1.0
*/
public static Double toDouble(Number self) {
// Conversions in which all decimal digits are known to be good.
if ((self instanceof Double)
|| (self instanceof Long)
|| (self instanceof Integer)
|| (self instanceof Short)
|| (self instanceof Byte))
{
return self.doubleValue();
}
// Chances are this is a Float or a Big.
// With Float we're extending binary precision and that gets ugly in decimal.
// If we used Float.doubleValue() on 0.1f we get 0.10000000149011612.
// Note that this is different than casting '(double) 0.1f' which will do the
// binary extension just like in Java.
// With Bigs and other unkowns, this is likely to be the same.
return Double.valueOf(self.toString());
}
/**
* Transform a Number into a BigDecimal
*
* @param self a Number
* @return an BigDecimal
* @since 1.0
*/
public static BigDecimal toBigDecimal(Number self) {
// Quick method for scalars.
if ((self instanceof Long)
|| (self instanceof Integer)
|| (self instanceof Short)
|| (self instanceof Byte))
{
return BigDecimal.valueOf(self.longValue());
}
return new BigDecimal(self.toString());
}
/**
* Transform this number to a the given type, using the 'as' operator. The
* following types are supported in addition to the default
* {@link #asType(Object,Class)}:
*
* - BigDecimal
* - BigInteger
* - Double
* - Float
*
* @param self this number
* @param c the desired type of the transformed result
* @return an instance of the given type
* @since 1.0
*/
public static Object asType(Number self, Class c) {
if (c == BigDecimal.class) {
return toBigDecimal(self);
} else if (c == BigInteger.class) {
return toBigInteger(self);
} else if (c == Double.class) {
return toDouble(self);
} else if (c == Float.class) {
return toFloat(self);
}
return asType((Object) self, c);
}
/**
* Transform this Number into a BigInteger.
*
* @param self a Number
* @return an BigInteger
* @since 1.0
*/
public static BigInteger toBigInteger(Number self) {
if (self instanceof BigInteger) {
return (BigInteger) self;
} else if (self instanceof BigDecimal) {
return ((BigDecimal) self).toBigInteger();
} else if (self instanceof Double) {
return new BigDecimal((Double)self).toBigInteger();
} else if (self instanceof Float) {
return new BigDecimal((Float)self).toBigInteger();
} else {
return new BigInteger(Long.toString(self.longValue()));
}
}
// Date methods
//-------------------------------------------------------------------------
/**
* Increment a Date by one day.
*
* @param self a Date
* @return the next days date
* @since 1.0
*/
public static Date next(Date self) {
return plus(self, 1);
}
/**
* Increment a java.sql.Date by one day.
*
* @param self a java.sql.Date
* @return the next days date
* @since 1.0
*/
public static java.sql.Date next(java.sql.Date self) {
return new java.sql.Date(next((Date) self).getTime());
}
/**
* Decrement a Date by one day.
*
* @param self a Date
* @return the previous days date
* @since 1.0
*/
public static Date previous(Date self) {
return minus(self, 1);
}
/**
* Decrement a java.sql.Date by one day.
*
* @param self a java.sql.Date
* @return the previous days date
* @since 1.0
*/
public static java.sql.Date previous(java.sql.Date self) {
return new java.sql.Date(previous((Date) self).getTime());
}
/**
* Add 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
* @since 1.0
*/
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();
}
/**
* Add a number of days to this date and returns the new date.
*
* @param self a java.sql.Date
* @param days the number of days to increase
* @return the new date
* @since 1.0
*/
public static java.sql.Date plus(java.sql.Date self, int days) {
return new java.sql.Date(plus((Date) self, days).getTime());
}
/**
* Subtract a number of days from this date and returns the new date.
*
* @param self a Date
* @param days the number of days to subtract
* @return the new date
* @since 1.0
*/
public static Date minus(Date self, int days) {
return plus(self, -days);
}
/**
* Subtract a number of days from this date and returns the new date.
*
* @param self a java.sql.Date
* @param days the number of days to subtract
* @return the new date
* @since 1.0
*/
public static java.sql.Date minus(java.sql.Date self, int days) {
return new java.sql.Date(minus((Date) self, days).getTime());
}
/**
* Subtract another date from this one and return the number of days of the difference.
*
* Date self = Date then + (Date self - Date then)
*
* IOW, if self is before then the result is a negative value.
*
* @param self a Calendar
* @param then another Calendar
* @return number of days
* @since 1.6.0
*/
public static int minus(Calendar self, Calendar then) {
Calendar a = self;
Calendar b = then;
boolean swap = a.before(b);
if (swap) {
Calendar t = a;
a = b;
b = t;
}
int days = 0;
b = (Calendar) b.clone();
while (a.get(Calendar.YEAR) > b.get(Calendar.YEAR)) {
days += 1 + (b.getActualMaximum(Calendar.DAY_OF_YEAR) - b.get(Calendar.DAY_OF_YEAR));
b.set(Calendar.DAY_OF_YEAR, 1);
b.add(Calendar.YEAR, 1);
}
days += a.get(Calendar.DAY_OF_YEAR) - b.get(Calendar.DAY_OF_YEAR);
if (swap) days = -days;
return days;
}
/**
* Subtract another Date from this one and return the number of days of the difference.
*
* Date self = Date then + (Date self - Date then)
*
* IOW, if self is before then the result is a negative value.
*
* @param self a Date
* @param then another Date
* @return number of days
* @since 1.6.0
*/
public static int minus(Date self, Date then) {
Calendar a = (Calendar) Calendar.getInstance().clone();
a.setTime(self);
Calendar b = (Calendar) Calendar.getInstance().clone();
b.setTime(then);
return minus(a, b);
}
/**
* Create a String representation of this date according to the given
* format pattern.
*
* For example, if the system timezone is GMT,
* new Date(0).format('MM/dd/yy') would return the string
* "01/01/70". See documentation for {@link SimpleDateFormat}
* for format pattern use.
*
* Note that a new DateFormat instance is created for every
* invocation of this method (for thread safety).
*
* @see SimpleDateFormat
* @param self a Date
* @param format the format pattern to use according to {@link SimpleDateFormat}
* @return a string representation of this date.
* @since 1.5.7
*/
public static String format( Date self, String format ) {
return new SimpleDateFormat( format ).format( self );
}
/**
* Return a string representation of the 'day' portion of this date
* according to the locale-specific {@link DateFormat#SHORT} default format.
* For an "en_UK" system locale, this would be dd/MM/yy.
*
* Note that a new DateFormat instance is created for every
* invocation of this method (for thread safety).
*
* @see DateFormat#getDateInstance(int)
* @see DateFormat#SHORT
* @param self a Date
* @return a string representation of this date
* @since 1.5.7
*/
public static String getDateString( Date self ) {
return DateFormat.getDateInstance(DateFormat.SHORT).format( self );
}
/**
* Return a string representation of the time portion of this date
* according to the locale-specific {@link DateFormat#MEDIUM} default format.
* For an "en_UK" system locale, this would be HH:MM:ss.
*
* Note that a new DateFormat instance is created for every
* invocation of this method (for thread safety).
*
* @see DateFormat#getTimeInstance(int)
* @see DateFormat#MEDIUM
* @param self a Date
* @return a string representing the time portion of this date
* @since 1.5.7
*/
public static String getTimeString( Date self ) {
return DateFormat.getTimeInstance(DateFormat.MEDIUM).format( self );
}
/**
* Return a string representation of the date and time time portion of
* this Date instance, according to the locale-specific format used by
* {@link DateFormat}. This method uses the {@link DateFormat#SHORT}
* preset for the day portion and {@link DateFormat#MEDIUM} for the time
* portion of the output string.
*
* Note that a new DateFormat instance is created for every
* invocation of this method (for thread safety).
*
* @see DateFormat#getDateTimeInstance(int, int)
* @param self a Date
* @return a string representation of this date and time
* @since 1.5.7
*/
public static String getDateTimeString( Date self ) {
return DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.MEDIUM).format( self );
}
/**
* Common code for {@link #clearTime(Calendar)} and {@link #clearTime(Date)}
* and {@link #clearTime(java.sql.Date)}
*/
private static void clearTimeCommon(final Calendar self) {
self.set(Calendar.HOUR_OF_DAY, 0);
self.clear(Calendar.MINUTE);
self.clear(Calendar.SECOND);
self.clear(Calendar.MILLISECOND);
}
/**
* Clears the time portion of this Date instance; Util where it makes sense to
* compare month/day/year only portions of a Date
*
* @param self a Date
*
* @since 1.6.7
*/
public static void clearTime(final Date self){
Calendar calendar = Calendar.getInstance();
calendar.setTime(self);
clearTimeCommon(calendar);
self.setTime(calendar.getTime().getTime());
}
/**
* Clears the time portion of this java.sql.Date instance; Util where it makes sense to
* compare month/day/year only portions of a Date
*
* @param self a java.sql.Date
*
* @since 1.6.7
*/
public static void clearTime(final java.sql.Date self){
Calendar calendar = Calendar.getInstance();
calendar.setTime(self);
clearTimeCommon(calendar);
self.setTime(calendar.getTime().getTime());
}
/**
* Clears the time portion of this Calendar instance; Util where it makes sense to
*
* compare month/day/year only portions of a Calendar
* @param self a Calendar
*
* @since 1.6.7
*/
public static void clearTime(final Calendar self){
clearTimeCommon(self);
}
// Boolean based methods
//-------------------------------------------------------------------------
/**
* Logical conjunction of two boolean operators.
*
* @param left left operator
* @param right right operator
* @return result of logical conjunction
* @since 1.0
*/
public static Boolean and(Boolean left, Boolean right) {
return left && right;
}
/**
* Logical disjunction of two boolean operators
*
* @param left left operator
* @param right right operator
* @return result of logical disjunction
* @since 1.0
*/
public static Boolean or(Boolean left, Boolean right) {
return left || right;
}
/**
* Exclusive disjunction of two boolean operators
*
* @param left left operator
* @param right right operator
* @return result of exclusive disjunction
* @since 1.0
*/
public static Boolean xor(Boolean left, Boolean right) {
return left ^ right;
}
// public static Boolean negate(Boolean left) {
// return Boolean.valueOf(!left.booleanValue());
// }
// File and stream based methods
//-------------------------------------------------------------------------
/**
* Create an object output stream for this file.
*
* @param file a file
* @return an object output stream
* @throws IOException if an IOException occurs.
*/
public static ObjectOutputStream newObjectOutputStream(File file) throws IOException {
return new ObjectOutputStream(new FileOutputStream(file));
}
/**
* Create an object output stream for this output stream.
*
* @param outputStream an output stream
* @return an object output stream
* @throws IOException if an IOException occurs.
*/
public static ObjectOutputStream newObjectOutputStream(OutputStream outputStream) throws IOException {
return new ObjectOutputStream(outputStream);
}
/**
* Create a new ObjectOutputStream for this file and then pass it to the
* closure. This method ensures the stream is closed after the closure
* returns.
*
* @param file a File
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(OutputStream,Closure)
*/
public static Object withObjectOutputStream(File file, Closure closure) throws IOException {
return withStream(newObjectOutputStream(file), closure);
}
/**
* Create a new ObjectOutputStream for this output stream and then pass it to the
* closure. This method ensures the stream is closed after the closure
* returns.
*
* @param outputStream am output stream
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(OutputStream,Closure)
*/
public static Object withObjectOutputStream(OutputStream outputStream, Closure closure) throws IOException {
return withStream(newObjectOutputStream(outputStream), closure);
}
/**
* Create an object input stream for this file.
*
* @param file a file
* @return an object input stream
* @throws IOException if an IOException occurs.
* @since 1.5.0
*/
public static ObjectInputStream newObjectInputStream(File file) throws IOException {
return new ObjectInputStream(new FileInputStream(file));
}
/**
* Create an object input stream for this input stream.
*
* @param inputStream an input stream
* @return an object input stream
* @throws IOException if an IOException occurs.
*/
public static ObjectInputStream newObjectInputStream(InputStream inputStream) throws IOException {
return new ObjectInputStream(inputStream);
}
/**
* Create an object input stream for this input stream using the given class loader.
*
* @param inputStream an input stream
* @param classLoader the class loader to use when loading the class
* @return an object input stream
* @throws IOException if an IOException occurs.
*/
public static ObjectInputStream newObjectInputStream(InputStream inputStream, final ClassLoader classLoader) throws IOException {
return new ObjectInputStream(inputStream) {
protected Class> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
return Class.forName(desc.getName(), true, classLoader);
}
};
}
/**
* Create an object input stream for this file using the given class loader.
*
* @param file a file
* @param classLoader the class loader to use when loading the class
* @return an object input stream
* @throws IOException if an IOException occurs.
* @since 1.5.0
*/
public static ObjectInputStream newObjectInputStream(File file, final ClassLoader classLoader) throws IOException {
return newObjectInputStream(new FileInputStream(file), classLoader);
}
/**
* Iterates through the given file object by object.
*
* @param self a File
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws ClassNotFoundException if the class is not found.
* @see #eachObject(ObjectInputStream,Closure)
* @since 1.0
*/
public static void eachObject(File self, Closure closure) throws IOException, ClassNotFoundException {
eachObject(newObjectInputStream(self), closure);
}
/**
* Iterates through the given object stream object by object. The
* ObjectInputStream is closed afterwards.
*
* @param ois an ObjectInputStream, closed after the operation
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws ClassNotFoundException if the class is not found.
* @since 1.0
*/
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.call(obj);
} catch (EOFException e) {
break;
}
}
InputStream temp = ois;
ois = null;
temp.close();
} finally {
if (ois != null) {
try {
ois.close();
}
catch (Exception e) {
// ignore this exception since there
// has to be another already
LOG.warning("Caught exception closing ObjectInputStream: " + e);
}
}
}
}
/**
* Create a new ObjectInputStream for this file and pass it to the closure.
* This method ensures the stream is closed after the closure returns.
*
* @param file a File
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(InputStream,Closure)
* @since 1.5.2
*/
public static Object withObjectInputStream(File file, Closure closure) throws IOException {
return withStream(newObjectInputStream(file), closure);
}
/**
* Create a new ObjectInputStream for this file associated with the given class loader and pass it to the closure.
* This method ensures the stream is closed after the closure returns.
*
* @param file a File
* @param classLoader the class loader to use when loading the class
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(InputStream,Closure)
* @since 1.5.2
*/
public static Object withObjectInputStream(File file, ClassLoader classLoader, Closure closure) throws IOException {
return withStream(newObjectInputStream(file, classLoader), closure);
}
/**
* Create a new ObjectInputStream for this file and pass it to the closure.
* This method ensures the stream is closed after the closure returns.
*
* @param inputStream an input stream
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(InputStream,Closure)
*/
public static Object withObjectInputStream(InputStream inputStream, Closure closure) throws IOException {
return withStream(newObjectInputStream(inputStream), closure);
}
/**
* Create a new ObjectInputStream for this file and pass it to the closure.
* This method ensures the stream is closed after the closure returns.
*
* @param inputStream an input stream
* @param classLoader the class loader to use when loading the class
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(InputStream,Closure)
*/
public static Object withObjectInputStream(InputStream inputStream, ClassLoader classLoader, Closure closure) throws IOException {
return withStream(newObjectInputStream(inputStream, classLoader), closure);
}
/**
* Iterates through this String line by line. Each line is passed
* to the given 1 or 2 arg closure. If a 2 arg closure is found
* the line count is passed as the second argument.
*
* @param self a String
* @param closure a closure
* @return the last value returned by the closure
* @throws java.io.IOException if an error occurs
* @see #eachLine(String, int, groovy.lang.Closure)
* @since 1.5.5
*/
public static Object eachLine(String self, Closure closure) throws IOException {
return eachLine(self, 0, closure);
}
/**
* Iterates through this String line by line. Each line is passed
* to the given 1 or 2 arg closure. If a 2 arg closure is found
* the line count is passed as the second argument.
*
* @param self a String
* @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
* @param closure a closure
* @return the last value returned by the closure
* @throws java.io.IOException if an error occurs
* @since 1.5.7
*/
public static Object eachLine(String self, int firstLine, Closure closure) throws IOException {
int count = firstLine;
String line = null;
for (Object o : readLines(self)) {
line = (String) o;
callClosureForLine(closure, line, count);
count++;
}
return line;
}
/**
* Iterates through this file line by line. Each line is passed to the
* given 1 or 2 arg closure. The file reader is closed before this method
* returns.
*
* @param self a File
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #eachLine(java.io.File, int, groovy.lang.Closure)
* @since 1.5.5
*/
public static Object eachLine(File self, Closure closure) throws IOException {
return eachLine(self, 1, closure);
}
/**
* Iterates through this file line by line. Each line is passed to the
* given 1 or 2 arg closure. The file is read using a reader which
* is closed before this method returns.
*
* @param self a File
* @param charset opens the file with a specified charset
* @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1)
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #eachLine(java.io.File, String, int, groovy.lang.Closure)
* @since 1.6.8
*/
public static Object eachLine(File self, String charset, Closure closure) throws IOException {
return eachLine(self, charset, 1, closure);
}
/**
* Iterates through this file line by line. Each line is passed
* to the given 1 or 2 arg closure. The file reader is closed
* before this method returns.
*
* @param self a File
* @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #eachLine(java.io.Reader, int, groovy.lang.Closure)
* @since 1.5.7
*/
public static Object eachLine(File self, int firstLine, Closure closure) throws IOException {
return eachLine(newReader(self), firstLine, closure);
}
/**
* Iterates through this file line by line. Each line is passed
* to the given 1 or 2 arg closure. The file is read using a reader
* which is closed before this method returns.
*
* @param self a File
* @param charset opens the file with a specified charset
* @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
* @param closure a closure (arg 1 is line, optional arg 2 is line number)
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #eachLine(java.io.Reader, int, groovy.lang.Closure)
* @since 1.6.8
*/
public static Object eachLine(File self, String charset, int firstLine, Closure closure) throws IOException {
return eachLine(newReader(self, charset), firstLine, closure);
}
/**
* Iterates through this stream reading with the provided charset, passing each line to the
* given 1 or 2 arg closure. The stream is closed before this method returns.
*
* @param stream a stream
* @param charset opens the stream with a specified charset
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #eachLine(java.io.InputStream, String, int, groovy.lang.Closure)
* @since 1.5.5
*/
public static Object eachLine(InputStream stream, String charset, Closure closure) throws IOException {
return eachLine(stream, charset, 1, closure);
}
/**
* Iterates through this stream reading with the provided charset, passing each line to
* the given 1 or 2 arg closure. The stream is closed after this method returns.
*
* @param stream a stream
* @param charset opens the stream with a specified charset
* @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
* @param closure a closure
* @return the last value returned by the closure
* @throws IOException if an IOException occurs.
* @see #eachLine(java.io.Reader, int, groovy.lang.Closure)
* @since 1.5.7
*/
public static Object eachLine(InputStream stream, String charset, int firstLine, Closure closure) throws IOException {
return eachLine(new InputStreamReader(stream, charset), firstLine, closure);
}
/**
* Iterates through this stream, passing each line to the given 1 or 2 arg closure.
* The stream is closed before this method returns.
*
* @param stream a stream
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #eachLine(java.io.InputStream, int, groovy.lang.Closure)
* @since 1.5.6
*/
public static Object eachLine(InputStream stream, Closure closure) throws IOException {
return eachLine(stream, 1, closure);
}
/**
* Iterates through this stream, passing each line to the given 1 or 2 arg closure.
* The stream is closed before this method returns.
*
* @param stream a stream
* @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #eachLine(java.io.Reader, int, groovy.lang.Closure)
* @since 1.5.7
*/
public static Object eachLine(InputStream stream, int firstLine, Closure closure) throws IOException {
return eachLine(new InputStreamReader(stream), firstLine, closure);
}
/**
* Iterates through the lines read from the URL's associated input stream passing each
* line to the given 1 or 2 arg closure. The stream is closed before this method returns.
*
* @param url a URL to open and read
* @param closure a closure to apply on each line
* @return the last value returned by the closure
* @throws IOException if an IOException occurs.
* @see #eachLine(java.net.URL, int, groovy.lang.Closure)
* @since 1.5.6
*/
public static Object eachLine(URL url, Closure closure) throws IOException {
return eachLine(url, 1, closure);
}
/**
* Iterates through the lines read from the URL's associated input stream passing each
* line to the given 1 or 2 arg closure. The stream is closed before this method returns.
*
* @param url a URL to open and read
* @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
* @param closure a closure to apply on each line
* @return the last value returned by the closure
* @throws IOException if an IOException occurs.
* @see #eachLine(java.io.InputStream, int, groovy.lang.Closure)
* @since 1.5.7
*/
public static Object eachLine(URL url, int firstLine, Closure closure) throws IOException {
return eachLine(url.openConnection().getInputStream(), firstLine, closure);
}
/**
* Iterates through the lines read from the URL's associated input stream passing each
* line to the given 1 or 2 arg closure. The stream is closed before this method returns.
*
* @param url a URL to open and read
* @param charset opens the stream with a specified charset
* @param closure a closure to apply on each line
* @return the last value returned by the closure
* @throws IOException if an IOException occurs.
* @see #eachLine(java.net.URL, String, int, groovy.lang.Closure)
* @since 1.5.6
*/
public static Object eachLine(URL url, String charset, Closure closure) throws IOException {
return eachLine(url, charset, 1, closure);
}
/**
* Iterates through the lines read from the URL's associated input stream passing each
* line to the given 1 or 2 arg closure. The stream is closed before this method returns.
*
* @param url a URL to open and read
* @param charset opens the stream with a specified charset
* @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
* @param closure a closure to apply on each line
* @return the last value returned by the closure
* @throws IOException if an IOException occurs.
* @see #eachLine(java.io.Reader, int, groovy.lang.Closure)
* @since 1.5.7
*/
public static Object eachLine(URL url, String charset, int firstLine, Closure closure) throws IOException {
return eachLine(newReader(url, charset), firstLine, closure);
}
/**
* Iterates through the given reader line by line. Each line is passed to the
* given 1 or 2 arg closure. If the closure has two arguments, the line count is passed
* as the second argument. The Reader is closed before this method returns.
*
* @param self a Reader, closed after the method returns
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #eachLine(java.io.Reader, int, groovy.lang.Closure)
* @since 1.5.6
*/
public static Object eachLine(Reader self, Closure closure) throws IOException {
return eachLine(self, 1, closure);
}
/**
* Iterates through the given reader line by line. Each line is passed to the
* given 1 or 2 arg closure. If the closure has two arguments, the line count is passed
* as the second argument. The Reader is closed before this method returns.
*
* @param self a Reader, closed after the method returns
* @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0)
* @param closure a closure which will be passed each line (or for 2 argument closures the line and count)
* @return the last value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.7
*/
public static Object eachLine(Reader self, int firstLine, Closure closure) throws IOException {
BufferedReader br;
int count = firstLine;
Object result = 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 {
result = callClosureForLine(closure, line, count);
count++;
}
}
Reader temp = self;
self = null;
temp.close();
return result;
} finally {
closeWithWarning(self);
closeWithWarning(br);
}
}
/**
* Iterates through this file line by line, splitting each line using
* the given regex separator. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression.
* Finally the resources used for processing the file are closed.
*
* @param self a File
* @param regex the delimiting regular expression
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid
* @return the last value returned by the closure
* @see #splitEachLine(Reader,String,Closure)
* @since 1.5.5
*/
public static Object splitEachLine(File self, String regex, Closure closure) throws IOException {
return splitEachLine(newReader(self), regex, closure);
}
/**
* Iterates through this file line by line, splitting each line using
* the given separator Pattern. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression Pattern.
* Finally the resources used for processing the file are closed.
*
* @param self a File
* @param pattern the regular expression Pattern for the delimiter
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure)
* @since 1.6.8
*/
public static Object splitEachLine(File self, Pattern pattern, Closure closure) throws IOException {
return splitEachLine(newReader(self), pattern, closure);
}
/**
* Iterates through this file line by line, splitting each line using
* the given regex separator. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression.
* Finally the resources used for processing the file are closed.
*
* @param self a File
* @param regex the delimiting regular expression
* @param charset opens the file with a specified charset
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid
* @return the last value returned by the closure
* @see #splitEachLine(Reader,String,Closure)
* @since 1.6.8
*/
public static Object splitEachLine(File self, String regex, String charset, Closure closure) throws IOException {
return splitEachLine(newReader(self, charset), regex, closure);
}
/**
* Iterates through this file line by line, splitting each line using
* the given regex separator Pattern. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression.
* Finally the resources used for processing the file are closed.
*
* @param self a File
* @param pattern the regular expression Pattern for the delimiter
* @param charset opens the file with a specified charset
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure)
* @since 1.6.8
*/
public static Object splitEachLine(File self, Pattern pattern, String charset, Closure closure) throws IOException {
return splitEachLine(newReader(self, charset), pattern, closure);
}
/**
* Iterates through the input stream associated with this URL line by line, splitting each line using
* the given regex separator. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression.
* Finally the resources used for processing the URL are closed.
*
* @param self a URL to open and read
* @param regex the delimiting regular expression
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid
* @return the last value returned by the closure
* @see #splitEachLine(Reader,String,Closure)
* @since 1.6.8
*/
public static Object splitEachLine(URL self, String regex, Closure closure) throws IOException {
return splitEachLine(newReader(self), regex, closure);
}
/**
* Iterates through the input stream associated with this URL line by line, splitting each line using
* the given regex separator Pattern. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression.
* Finally the resources used for processing the URL are closed.
*
* @param self a URL to open and read
* @param pattern the regular expression Pattern for the delimiter
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure)
* @since 1.6.8
*/
public static Object splitEachLine(URL self, Pattern pattern, Closure closure) throws IOException {
return splitEachLine(newReader(self), pattern, closure);
}
/**
* Iterates through the input stream associated with this URL line by line, splitting each line using
* the given regex separator. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression.
* Finally the resources used for processing the URL are closed.
*
* @param self a URL to open and read
* @param regex the delimiting regular expression
* @param charset opens the file with a specified charset
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid
* @return the last value returned by the closure
* @see #splitEachLine(Reader,String,Closure)
* @since 1.6.8
*/
public static Object splitEachLine(URL self, String regex, String charset, Closure closure) throws IOException {
return splitEachLine(newReader(self, charset), regex, closure);
}
/**
* Iterates through the input stream associated with this URL line by line, splitting each line using
* the given regex separator Pattern. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression.
* Finally the resources used for processing the URL are closed.
*
* @param self a URL to open and read
* @param pattern the regular expression Pattern for the delimiter
* @param charset opens the file with a specified charset
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure)
* @since 1.6.8
*/
public static Object splitEachLine(URL self, Pattern pattern, String charset, Closure closure) throws IOException {
return splitEachLine(newReader(self, charset), pattern, closure);
}
/**
* Iterates through the given reader line by line, splitting each line using
* the given regex separator. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression. The Reader is closed afterwards.
*
* Here is an example:
*
* def s = 'The 3 quick\nbrown 4 fox'
* def result = ''
* new StringReader(s).splitEachLine(/\d/){ parts ->
* result += "${parts[0]}_${parts[1]}|"
* }
* assert result == 'The _ quick|brown _ fox|'
*
*
* @param self a Reader, closed after the method returns
* @param regex the delimiting regular expression
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid
* @return the last value returned by the closure
* @see java.lang.String#split(String)
* @since 1.5.5
*/
public static Object splitEachLine(Reader self, String regex, Closure closure) throws IOException {
return splitEachLine(self, Pattern.compile(regex), closure);
}
/**
* Iterates through the given reader line by line, splitting each line using
* the given regex separator Pattern. For each line, the given closure is called with
* a single parameter being the list of strings computed by splitting the line
* around matches of the given regular expression. The Reader is closed afterwards.
*
* Here is an example:
*
* def s = 'The 3 quick\nbrown 4 fox'
* def result = ''
* new StringReader(s).splitEachLine(~/\d/){ parts ->
* result += "${parts[0]}_${parts[1]}|"
* }
* assert result == 'The _ quick|brown _ fox|'
*
*
* @param self a Reader, closed after the method returns
* @param pattern the regular expression Pattern for the delimiter
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid
* @return the last value returned by the closure
* @see java.lang.String#split(java.lang.String)
* @since 1.6.8
*/
public static Object splitEachLine(Reader self, Pattern pattern, Closure closure) throws IOException {
BufferedReader br;
Object result = 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 {
List vals = Arrays.asList(pattern.split(line));
result = closure.call(vals);
}
}
Reader temp = self;
self = null;
temp.close();
return result;
} finally {
closeWithWarning(self);
closeWithWarning(br);
}
}
/**
* Iterates through the given InputStream line by line using the specified
* encoding, splitting each line using the given separator. The list of tokens
* for each line is then passed to the given closure. Finally, the stream
* is closed.
*
* @param stream an InputStream
* @param regex the delimiting regular expression
* @param charset opens the stream with a specified charset
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid
* @return the last value returned by the closure
* @see #splitEachLine(Reader,String,Closure)
* @since 1.5.5
*/
public static Object splitEachLine(InputStream stream, String regex, String charset, Closure closure) throws IOException {
return splitEachLine(new BufferedReader(new InputStreamReader(stream, charset)), regex, closure);
}
/**
* Iterates through the given InputStream line by line using the specified
* encoding, splitting each line using the given separator Pattern. The list of tokens
* for each line is then passed to the given closure. Finally, the stream
* is closed.
*
* @param stream an InputStream
* @param pattern the regular expression Pattern for the delimiter
* @param charset opens the stream with a specified charset
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure)
* @since 1.6.8
*/
public static Object splitEachLine(InputStream stream, Pattern pattern, String charset, Closure closure) throws IOException {
return splitEachLine(new BufferedReader(new InputStreamReader(stream, charset)), pattern, closure);
}
/**
* Iterates through the given InputStream line by line, splitting each line using
* the given separator. The list of tokens for each line is then passed to
* the given closure. The stream is closed before the method returns.
*
* @param stream an InputStream
* @param regex the delimiting regular expression
* @param closure a closure
* @throws IOException if an IOException occurs.
* @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid
* @return the last value returned by the closure
* @see #splitEachLine(Reader,String,Closure)
* @since 1.5.6
*/
public static Object splitEachLine(InputStream stream, String regex, Closure closure) throws IOException {
return splitEachLine(new BufferedReader(new InputStreamReader(stream)), regex, closure);
}
/**
* Iterates through the given InputStream line by line, splitting each line using
* the given separator Pattern. The list of tokens for each line is then passed to
* the given closure. The stream is closed before the method returns.
*
* @param stream an InputStream
* @param pattern the regular expression Pattern for the delimiter
* @param closure a closure
* @throws IOException if an IOException occurs.
* @return the last value returned by the closure
* @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure)
* @since 1.6.8
*/
public static Object splitEachLine(InputStream stream, Pattern pattern, Closure closure) throws IOException {
return splitEachLine(new BufferedReader(new InputStreamReader(stream)), pattern, closure);
}
/**
* Iterates through the given String line by line, splitting each line using
* the given separator. The list of tokens for each line is then passed to
* the given closure.
*
* @param self a String
* @param regex the delimiting regular expression
* @param closure a closure
* @return the last value returned by the closure
* @throws java.io.IOException if an error occurs
* @see String#split(String)
* @since 1.5.5
*/
public static Object splitEachLine(String self, String regex, Closure closure) throws IOException {
return splitEachLine(self, Pattern.compile(regex), closure);
}
/**
* Iterates through the given String line by line, splitting each line using
* the given separator Pattern. The list of tokens for each line is then passed to
* the given closure.
*
* @param self a String
* @param pattern the regular expression Pattern for the delimiter
* @param closure a closure
* @return the last value returned by the closure
* @throws java.io.IOException if an error occurs
* @see java.util.regex.Pattern#split(java.lang.CharSequence)
* @since 1.6.8
*/
public static Object splitEachLine(String self, Pattern pattern, Closure closure) throws IOException {
final List list = readLines(self);
Object result = null;
for (String line : list) {
List vals = Arrays.asList(pattern.split(line));
result = closure.call(vals);
}
return result;
}
/**
* Read a single, whole line from the given Reader.
*
* @param self a Reader
* @return a line
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static String readLine(Reader self) throws IOException {
if (self instanceof BufferedReader) {
BufferedReader br = (BufferedReader) self;
return br.readLine();
}
if (self.markSupported()) {
return readLineFromReaderWithMark(self);
}
return readLineFromReaderWithoutMark(self);
}
private static int charBufferSize = 4096; // half the default stream buffer size
private static int expectedLineLength = 160; // double the default line length
private static int EOF = -1; // End Of File
/*
* This method tries to read subsequent buffers from the reader using a mark
*/
private static String readLineFromReaderWithMark(final Reader input)
throws IOException {
char[] cbuf = new char[charBufferSize];
try {
input.mark(charBufferSize);
} catch (IOException e) {
// this should never happen
LOG.warning("Caught exception setting mark on supporting reader: " + e);
// fallback
return readLineFromReaderWithoutMark(input);
}
// could be changed into do..while, but then
// we might create an additional StringBuffer
// instance at the end of the stream
int count = input.read(cbuf);
if (count == EOF) // we are at the end of the input data
return null;
StringBuffer line = new StringBuffer(expectedLineLength);
// now work on the buffer(s)
int ls = lineSeparatorIndex(cbuf, count);
while (ls == -1) {
line.append(cbuf, 0, count);
count = input.read(cbuf);
if (count == EOF) {
// we are at the end of the input data
return line.toString();
}
ls = lineSeparatorIndex(cbuf, count);
}
line.append(cbuf, 0, ls);
// correct ls if we have \r\n
int skipLS = 1;
if (ls + 1 < count) {
// we are not at the end of the buffer
if (cbuf[ls] == '\r' && cbuf[ls + 1] == '\n') {
skipLS++;
}
} else {
if (cbuf[ls] == '\r' && input.read() == '\n') {
skipLS++;
}
}
//reset() and skip over last linesep
input.reset();
input.skip(line.length() + skipLS);
return line.toString();
}
/*
* This method reads without a buffer.
* It returns too many empty lines if \r\n combinations
* are used. Nothing can be done because we can't push
* back the character we have just read.
*/
private static String readLineFromReaderWithoutMark(Reader input)
throws IOException {
int c = input.read();
if (c == -1)
return null;
StringBuffer line = new StringBuffer(expectedLineLength);
while (c != EOF && c != '\n' && c != '\r') {
char ch = (char) c;
line.append(ch);
c = input.read();
}
return line.toString();
}
/*
* searches for \n or \r
* Returns -1 if not found.
*/
private static int lineSeparatorIndex(char[] array, int length) {
for (int k = 0; k < length; k++) {
if (isLineSeparator(array[k])) {
return k;
}
}
return -1;
}
/*
* true if either \n or \r
*/
private static boolean isLineSeparator(char c) {
return c == '\n' || c == '\r';
}
/**
* Just throws a DeprecationException. DO NOT USE. It used to read a single, whole line from the given InputStream.
*
* @param stream an InputStream
* @return a line
* @throws IOException if an IOException occurs.
* @deprecated use Reader#readLine instead please
* @since 1.0
*/
public static String readLine(InputStream stream) throws IOException {
throw new DeprecationException(
"readLine() on InputStream is no longer supported. " +
"Either use a Reader or encapsulate the InputStream" +
" with a BufferedReader and an InputStreamReader."
);
}
static String lineSeparator = null;
/**
* Return a String with lines (separated by LF, CR/LF, or CR)
* terminated by the platform specific line separator.
*
* @param self a String object
* @return the denormalized string
* @since 1.6.0
*/
public static String denormalize(final String self) {
// Don't do this in static initializer because we may never be needed.
// TODO: Put this lineSeparator property somewhere everyone can use it.
if (lineSeparator == null) {
final StringWriter sw = new StringWriter(2);
try {
// We use BufferedWriter rather than System.getProperty because
// it has the security manager rigamarole to deal with the possible exception.
final BufferedWriter bw = new BufferedWriter(sw);
bw.newLine();
bw.flush();
lineSeparator = sw.toString();
} catch (IOException ioe) {
// This shouldn't happen, but this is the same default used by
// BufferedWriter on a security exception.
lineSeparator = "\n";
}
}
final int len = self.length();
if (len < 1) {
return self;
}
final StringBuilder sb = new StringBuilder((110 * len) / 100);
int i = 0;
while (i < len) {
final char ch = self.charAt(i++);
switch (ch) {
case '\r':
sb.append(lineSeparator);
// Eat the following LF if any.
if ((i < len) && (self.charAt(i) == '\n')) {
++i;
}
break;
case '\n':
sb.append(lineSeparator);
break;
default:
sb.append(ch);
break;
}
}
return sb.toString();
}
/**
* Return a String with linefeeds and carriage returns normalized to linefeeds.
*
* @param self a String object
* @return the normalized string
* @since 1.6.0
*/
public static String normalize(final String self) {
int nx = self.indexOf('\r');
if (nx < 0) {
return self;
}
final int len = self.length();
final StringBuilder sb = new StringBuilder(len);
int i = 0;
do {
sb.append(self, i, nx);
sb.append('\n');
if ((i = nx + 1) >= len) break;
if (self.charAt(i) == '\n') {
// skip the LF in CR LF
if (++i >= len) break;
}
nx = self.indexOf('\r', i);
} while (nx > 0);
sb.append(self, i, len);
return sb.toString();
}
/**
* Return the lines of a String as a List of Strings.
*
* @param self a String object
* @return a list of lines
* @throws java.io.IOException if an error occurs
* @since 1.5.5
*/
public static List readLines(String self) throws IOException {
return readLines(new StringReader(self));
}
/**
* Reads the file into a list of Strings, with one item for each line.
*
* @param file a File
* @return a List of lines
* @throws IOException if an IOException occurs.
* @see #readLines(Reader)
* @since 1.0
*/
public static List readLines(File file) throws IOException {
return readLines(newReader(file));
}
/**
* Reads the file into a list of Strings, with one item for each line.
*
* @param file a File
* @param charset opens the file with a specified charset
* @return a List of lines
* @throws IOException if an IOException occurs.
* @see #readLines(Reader)
* @since 1.6.8
*/
public static List readLines(File file, String charset) throws IOException {
return readLines(newReader(file, charset));
}
/**
* Reads the stream into a list, with one element for each line.
*
* @param stream a stream
* @return a List of lines
* @throws IOException if an IOException occurs.
* @see #readLines(Reader)
* @since 1.0
*/
public static List readLines(InputStream stream) throws IOException {
return readLines(newReader(stream));
}
/**
* Reads the stream into a list, with one element for each line.
*
* @param stream a stream
* @param charset opens the stream with a specified charset
* @return a List of lines
* @throws IOException if an IOException occurs.
* @see #readLines(Reader)
* @since 1.6.8
*/
public static List readLines(InputStream stream, String charset) throws IOException {
return readLines(newReader(stream, charset));
}
/**
* Reads the URL contents into a list, with one element for each line.
*
* @param self a URL
* @return a List of lines
* @throws IOException if an IOException occurs.
* @see #readLines(Reader)
* @since 1.6.8
*/
public static List readLines(URL self) throws IOException {
return readLines(newReader(self));
}
/**
* Reads the URL contents into a list, with one element for each line.
*
* @param self a URL
* @param charset opens the URL with a specified charset
* @return a List of lines
* @throws IOException if an IOException occurs.
* @see #readLines(Reader)
* @since 1.6.8
*/
public static List readLines(URL self, String charset) throws IOException {
return readLines(newReader(self, charset));
}
/**
* Reads the reader into a list of Strings, with one entry for each line.
* The reader is closed before this method returns.
*
* @param reader a Reader
* @return a List of lines
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static List readLines(Reader reader) throws IOException {
IteratorClosureAdapter closure = new IteratorClosureAdapter(reader);
eachLine(reader, closure);
return closure.asList();
}
/**
* Read the content of the File using the specified encoding and return 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 if an IOException occurs.
* @since 1.0
*/
public static String getText(File file, String charset) throws IOException {
return getText(newReader(file, charset));
}
/**
* Read 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 if an IOException occurs.
* @since 1.0
*/
public static String getText(File file) throws IOException {
return getText(newReader(file));
}
/**
* Read 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 if an IOException occurs.
* @since 1.0
*/
public static String getText(URL url) throws IOException {
return getText(url, CharsetToolkit.getDefaultSystemCharset().toString());
}
/**
* Read the data from this URL and return it as a String. The connection
* stream is closed before this method returns.
*
* @param url URL to read content from
* @param charset opens the stream with a specified charset
* @return the text from that URL
* @throws IOException if an IOException occurs.
* @see URLConnection#getInputStream()
* @since 1.0
*/
public static String getText(URL url, String charset) throws IOException {
BufferedReader reader = newReader(url, charset);
return getText(reader);
}
/**
* Read the content of this InputStream and return it as a String.
* The stream is closed before this method returns.
*
* @param is an input stream
* @return the text from that URL
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static String getText(InputStream is) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
return getText(reader);
}
/**
* Read the content of this InputStream using specified charset and return
* it as a String. The stream is closed before this method returns.
*
* @param is an input stream
* @param charset opens the stream with a specified charset
* @return the text from that URL
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static String getText(InputStream is, String charset) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
return getText(reader);
}
/**
* Read the content of the Reader and return it as a String. The reader
* is closed before this method returns.
*
* @param reader a Reader whose content we want to read
* @return a String containing the content of the buffered reader
* @throws IOException if an IOException occurs.
* @see #getText(BufferedReader)
* @since 1.0
*/
public static String getText(Reader reader) throws IOException {
BufferedReader bufferedReader = new BufferedReader(reader);
return getText(bufferedReader);
}
/**
* Read the content of the BufferedReader and return it as a String.
* The BufferedReader is closed afterwards.
*
* @param reader a BufferedReader whose content we want to read
* @return a String containing the content of the buffered reader
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static String getText(BufferedReader reader) throws IOException {
StringBuilder answer = new StringBuilder();
// reading the content of the file within a char buffer
// allow to keep the correct line endings
char[] charBuffer = new char[8192];
int nbCharRead /* = 0*/;
try {
while ((nbCharRead = reader.read(charBuffer)) != -1) {
// appends buffer
answer.append(charBuffer, 0, nbCharRead);
}
Reader temp = reader;
reader = null;
temp.close();
} finally {
closeWithWarning(reader);
}
return answer.toString();
}
/**
* Write the text and append a newline (using the platform's line-ending).
*
* @param writer a BufferedWriter
* @param line the line to write
* @throws IOException if an IOException occurs.
* @since 1.0
*/
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 if an IOException occurs.
* @since 1.0
*/
public static void write(File file, String text) throws IOException {
BufferedWriter writer = null;
try {
writer = newWriter(file);
writer.write(text);
writer.flush();
Writer temp = writer;
writer = null;
temp.close();
} finally {
closeWithWarning(writer);
}
}
/**
* Synonym for write(text) allowing file.text = 'foo'.
*
* @param file a File
* @param text the text to write to the File
* @throws IOException if an IOException occurs.
* @see #write(File, String)
* @since 1.5.1
*/
public static void setText(File file, String text) throws IOException {
write(file, text);
}
/**
* Write the text to the File.
*
* @param file a File
* @param text the text to write to the File
* @return the original file
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static File leftShift(File file, Object text) throws IOException {
append(file, text);
return file;
}
/**
* Write bytes to a File.
*
* @param file a File
* @param bytes the byte array to append to the end of the File
* @return the original file
* @throws IOException if an IOException occurs.
*/
public static File leftShift(File file, byte[] bytes) throws IOException {
append(file, bytes);
return file;
}
/**
* Append binary data to the file. See {@link #append(File, InputStream)}
* @param file a File
* @param data an InputStream of data to write to the file
* @return the file
* @throws IOException if an IOException occurs.
*/
public static File leftShift(File file, InputStream data) throws IOException {
append(file, data);
return file;
}
/**
* Write the text to the File, using the specified encoding.
*
* @param file a File
* @param text the text to write to the File
* @param charset the charset used
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static void write(File file, String text, String charset) throws IOException {
BufferedWriter writer = null;
try {
writer = newWriter(file, charset);
writer.write(text);
writer.flush();
Writer temp = writer;
writer = null;
temp.close();
} finally {
closeWithWarning(writer);
}
}
/**
* 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 if an IOException occurs.
* @since 1.0
*/
public static void append(File file, Object text) throws IOException {
BufferedWriter writer = null;
try {
writer = newWriter(file, true);
InvokerHelper.write(writer, text);
writer.flush();
Writer temp = writer;
writer = null;
temp.close();
} finally {
closeWithWarning(writer);
}
}
/**
* Append bytes to the end of a File.
*
* @param file a File
* @param bytes the byte array to append to the end of the File
* @throws IOException if an IOException occurs.
* @since 1.5.1
*/
public static void append(File file, byte[] bytes) throws IOException {
BufferedOutputStream stream = null;
try {
stream = new BufferedOutputStream( new FileOutputStream(file,true) );
stream.write(bytes, 0, bytes.length);
stream.flush();
OutputStream temp = stream;
stream = null;
temp.close();
} finally {
closeWithWarning(stream);
}
}
/**
* Append binary data to the file. It will not be
* interpreted as text.
* @param self a File
* @param stream stream to read data from.
* @throws IOException if an IOException occurs.
*/
public static void append(File self, InputStream stream ) throws IOException {
OutputStream out = new FileOutputStream( self, true );
try {
leftShift( out, stream );
}
finally {
closeWithWarning( out );
}
}
/**
* Append the text at the end of the File, using 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 if an IOException occurs.
* @since 1.0
*/
public static void append(File file, Object text, String charset) throws IOException {
BufferedWriter writer = null;
try {
writer = newWriter(file, charset, true);
InvokerHelper.write(writer, text);
writer.flush();
Writer temp = writer;
writer = null;
temp.close();
} finally {
closeWithWarning(writer);
}
}
/**
* 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 if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @since 1.0
*/
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());
}
/**
* Common code for {@link #eachFile(File,Closure)} and {@link #eachDir(File,Closure)}
*
* @param self a file object
* @param closure the closure to invoke
* @param onlyDir if normal file should be skipped
* @throws FileNotFoundException if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @since 1.5.0
*/
private static void eachFile(final File self, final Closure closure, final boolean onlyDir)
throws FileNotFoundException, IllegalArgumentException {
checkDir(self);
final File[] files = self.listFiles();
// null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836
if (files == null) return;
for (File file : files) {
if (!onlyDir || file.isDirectory()) {
closure.call(file);
}
}
}
/**
* Invokes the closure for each file in the given directory
*
* @param self a File
* @param closure a closure
* @throws FileNotFoundException if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @see File#listFiles()
* @since 1.5.0
*/
public static void eachFile(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException {
eachFile(self, closure, false);
}
/**
* Invokes the closure for each directory in this directory,
* ignoring regular files.
*
* @param self a directory
* @param closure a closure
* @throws FileNotFoundException if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @since 1.0
*/
public static void eachDir(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
eachFile(self, closure, true);
}
/**
* Common code for {@link #eachFileRecurse(File,Closure)} and {@link #eachDirRecurse(File,Closure)}
*
* @param self a file object
* @param closure the closure to invoke on each file
* @param onlyDir if normal file should be skipped
* @throws FileNotFoundException if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @since 1.5.0
*/
private static void eachFileRecurse(final File self, final Closure closure, final boolean onlyDir)
throws FileNotFoundException, IllegalArgumentException {
checkDir(self);
final File[] files = self.listFiles();
// null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836
if (files == null) return;
for (File file : files) {
if (file.isDirectory()) {
closure.call(file);
eachFileRecurse(file, closure, onlyDir);
} else if (!onlyDir) {
closure.call(file);
}
}
}
/**
* Invokes the closure for each descendant file in this directory.
* Sub-directories are recursively searched in a depth-first fashion.
*
* @param self a File
* @param closure a closure
* @throws FileNotFoundException if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @since 1.0
*/
public static void eachFileRecurse(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
eachFileRecurse(self, closure, false);
}
/**
* Invokes the closure for each descendant directory of this directory.
* Sub-directories are recursively searched in a depth-first fashion.
* Only directories are passed to the closure; regular files are ignored.
*
* @param self a directory
* @param closure a closure
* @throws FileNotFoundException if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @see #eachFileRecurse(File,Closure,boolean)
* @since 1.5.0
*/
public static void eachDirRecurse(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException {
eachFileRecurse(self, closure, true);
}
/**
* Common code for {@link #eachFileMatch(File,Object,Closure)} and {@link #eachDirMatch(File,Object,Closure)}
*
* @param self a file
* @param filter the filter to perform on the file/directory (using the isCase(object) method)
* @param closure the closure to invoke
* @param onlyDir if normal file should be skipped
* @throws FileNotFoundException if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @since 1.5.0
*/
private static void eachFileMatch(final File self, final Object filter, final Closure closure, final boolean onlyDir)
throws FileNotFoundException, IllegalArgumentException {
checkDir(self);
final File[] files = self.listFiles();
// null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836
if (files == null) return;
final MetaClass metaClass = InvokerHelper.getMetaClass(filter);
for (final File currentFile : files) {
if ((!onlyDir || currentFile.isDirectory())
&& DefaultTypeTransformation.castToBoolean(metaClass.invokeMethod(filter, "isCase", currentFile.getName()))) {
closure.call(currentFile);
}
}
}
/**
* Invokes the closure for each file whose name (file.name) matches the given filter in the given directory
* - calling the isCase() method to determine if a match occurs. This method can be used
* with different kinds of filters like regular expressions, classes, ranges etc.
*
* @param self a file
* @param filter the filter to perform on the directory (using the isCase(object) method)
* @param closure the closure to invoke
* @throws FileNotFoundException if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @since 1.5.0
*/
public static void eachFileMatch(final File self, final Object filter, final Closure closure)
throws FileNotFoundException, IllegalArgumentException {
eachFileMatch(self, filter, closure, false);
}
/**
* Invokes the closure for each directory whose name (dir.name) matches the given filter in the given directory
* - calling the isCase() method to determine if a match occurs. This method can be used
* with different kinds of filters like regular expressions, classes, ranges etc.
*
* @param self a file
* @param filter the filter to perform on the directory (using the isCase(object) method)
* @param closure the closure to invoke
* @throws FileNotFoundException if the given directory does not exist
* @throws IllegalArgumentException if the provided File object does not represent a directory
* @since 1.5.0
*/
public static void eachDirMatch(final File self, final Object filter, final Closure closure) throws FileNotFoundException, IllegalArgumentException {
eachFileMatch(self, filter, closure, true);
}
/**
* Deletes a directory with all contained files and subdirectories.
* The method returns
*
* - true, when deletion was successful
* - true, when it is called for a non existing directory
* - false, when it is called for a file which isn't a directory
* - false, when directory couldn't be deleted
*
*
*
* @param self a File
* @return true if the file doesn't exist or deletion was successful
* @since 1.6.0
*/
public static boolean deleteDir(final File self) {
if (!self.exists())
return true;
if (!self.isDirectory())
return false;
File[] files = self.listFiles();
if (files == null)
// couldn't access files
return false;
// delete contained files
boolean result = true;
for (File file : files) {
if (file.isDirectory()) {
if (!deleteDir(file))
result = false;
} else {
if (!file.delete())
result = false;
}
}
// now delete directory itself
if(!self.delete())
result = false;
return result;
}
/**
* Allows a simple syntax for using timers. This timer will execute the
* given closure after the given delay.
*
* @param timer a timer object
* @param delay the delay in milliseconds before running the closure code
* @param closure the closure to invoke
* @return The timer task which has been scheduled.
* @since 1.5.0
*/
public static TimerTask runAfter(Timer timer, int delay, final Closure closure) {
TimerTask timerTask = new TimerTask() {
public void run() {
closure.call();
}
};
timer.schedule(timerTask, delay);
return timerTask;
}
/**
* Create a buffered reader for this file.
*
* @param file a File
* @return a BufferedReader
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static BufferedReader newReader(File file) throws IOException {
CharsetToolkit toolkit = new CharsetToolkit(file);
return toolkit.getReader();
}
/**
* Create a buffered reader for this file, using the specified
* charset as the encoding.
*
* @param file a File
* @param charset the charset for this File
* @return a BufferedReader
* @throws FileNotFoundException if the File was not found
* @throws UnsupportedEncodingException if the encoding specified is not supported
* @since 1.0
*/
public static BufferedReader newReader(File file, String charset)
throws FileNotFoundException, UnsupportedEncodingException {
return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
}
/**
* Creates a reader for this input stream.
*
* @param self an input stream
* @return a reader
* @since 1.0
*/
public static BufferedReader newReader(InputStream self) {
return new BufferedReader(new InputStreamReader(self));
}
/**
* Creates a reader for this input stream, using the specified
* charset as the encoding.
*
* @param self an input stream
* @param charset the charset for this input stream
* @return a reader
* @throws UnsupportedEncodingException if the encoding specified is not supported
* @since 1.6.0
*/
public static BufferedReader newReader(InputStream self, String charset) throws UnsupportedEncodingException {
return new BufferedReader(new InputStreamReader(self, charset));
}
/**
* Create a new BufferedReader for this file and then
* passes it into the closure, ensuring the reader is closed after the
* closure returns.
*
* @param file a file object
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withReader(File file, Closure closure) throws IOException {
return withReader(newReader(file), closure);
}
/**
* Create a new BufferedReader for this file using the specified charset and then
* passes it into the closure, ensuring the reader is closed after the
* closure returns.
*
* @param file a file object
* @param charset the charset for this input stream
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.6.0
*/
public static Object withReader(File file, String charset, Closure closure) throws IOException {
return withReader(newReader(file, charset), closure);
}
/**
* Create a buffered output stream for this file.
*
* @param file a file object
* @return the created OutputStream
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static BufferedOutputStream newOutputStream(File file) throws IOException {
return new BufferedOutputStream(new FileOutputStream(file));
}
/**
* Creates a new data output stream for this file.
*
* @param file a file object
* @return the created DataOutputStream
* @throws IOException if an IOException occurs.
* @since 1.5.0
*/
public static DataOutputStream newDataOutputStream(File file) throws IOException {
return new DataOutputStream(new FileOutputStream(file));
}
/**
* Creates a new OutputStream for this file and passes it into the closure.
* This method ensures the stream is closed after the closure returns.
*
* @param file a File
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(OutputStream,Closure)
* @since 1.5.2
*/
public static Object withOutputStream(File file, Closure closure) throws IOException {
return withStream(newOutputStream(file), closure);
}
/**
* Create a new InputStream for this file and passes it into the closure.
* This method ensures the stream is closed after the closure returns.
*
* @param file a File
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(InputStream,Closure)
* @since 1.5.2
*/
public static Object withInputStream(File file, Closure closure) throws IOException {
return withStream(newInputStream(file), closure);
}
/**
* Creates a new InputStream for this URL and passes it into the closure.
* This method ensures the stream is closed after the closure returns.
*
* @param url a URL
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(InputStream,Closure)
* @since 1.5.2
*/
public static Object withInputStream(URL url, Closure closure) throws IOException {
return withStream(newInputStream(url), closure);
}
/**
* Create a new DataOutputStream for this file and passes it into the closure.
* This method ensures the stream is closed after the closure returns.
*
* @param file a File
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(OutputStream,Closure)
* @since 1.5.2
*/
public static Object withDataOutputStream(File file, Closure closure) throws IOException {
return withStream(newDataOutputStream(file), closure);
}
/**
* Create a new DataInputStream for this file and passes it into the closure.
* This method ensures the stream is closed after the closure returns.
*
* @param file a File
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withStream(InputStream,Closure)
* @since 1.5.2
*/
public static Object withDataInputStream(File file, Closure closure) throws IOException {
return withStream(newDataInputStream(file), closure);
}
/**
* Create a buffered writer for this file.
*
* @param file a File
* @return a BufferedWriter
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static BufferedWriter newWriter(File file) throws IOException {
return new BufferedWriter(new FileWriter(file));
}
/**
* Creates a buffered writer for this file, optionally appending to the
* existing file content.
*
* @param file a File
* @param append true if data should be appended to the file
* @return a BufferedWriter
* @throws IOException if an IOException occurs.
* @since 1.0
*/
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. If the given
* charset is "UTF-16BE" or "UTF-16LE", the requisite byte order mark is
* written to the stream before the writer is returned.
*
* @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 IOException if an IOException occurs.
* @since 1.0
*/
public static BufferedWriter newWriter(File file, String charset, boolean append) throws IOException {
if (append) {
return new EncodingAwareBufferedWriter(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 EncodingAwareBufferedWriter(new OutputStreamWriter(stream, charset));
}
}
/**
* Creates a buffered writer for this file, writing data using the given
* encoding.
*
* @param file a File
* @param charset the name of the encoding used to write in this file
* @return a BufferedWriter
* @throws IOException if an IOException occurs.
* @since 1.0
*/
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 if an IOException occurs.
* @since 1.0
*/
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);
}
}
/**
* Creates a new BufferedWriter for this file, passes it to the closure, and
* ensures the stream is flushed and closed after the closure returns.
*
* @param file a File
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withWriter(File file, Closure closure) throws IOException {
return withWriter(newWriter(file), closure);
}
/**
* Creates a new BufferedWriter for this file, passes it to the closure, and
* ensures the stream is flushed and closed after the closure returns.
* The writer will use the given charset encoding.
*
* @param file a File
* @param charset the charset used
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withWriter(File file, String charset, Closure closure) throws IOException {
return withWriter(newWriter(file, charset), closure);
}
/**
* Create a new BufferedWriter which will append to this
* file. The writer is passed to the closure and will be closed before
* this method returns.
*
* @param file a File
* @param charset the charset used
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withWriterAppend(File file, String charset, Closure closure) throws IOException {
return withWriter(newWriter(file, charset, true), closure);
}
/**
* Create a new BufferedWriter for this file in append mode. The writer
* is passed to the closure and is closed after the closure returns.
*
* @param file a File
* @param closure a closure
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withWriterAppend(File file, Closure closure) throws IOException {
return withWriter(newWriter(file, true), closure);
}
/**
* Create a new PrintWriter for this file.
*
* @param file a File
* @return the created PrintWriter
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static PrintWriter newPrintWriter(File file) throws IOException {
return new GroovyPrintWriter(newWriter(file));
}
/**
* Create a new PrintWriter for this file, using specified
* charset.
*
* @param file a File
* @param charset the charset
* @return a PrintWriter
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static PrintWriter newPrintWriter(File file, String charset) throws IOException {
return new GroovyPrintWriter(newWriter(file, charset));
}
/**
* Create a new PrintWriter for this file, using specified
* charset.
*
* @param writer a writer
* @return a PrintWriter
* @since 1.6
*/
public static PrintWriter newPrintWriter(Writer writer) {
return new GroovyPrintWriter(writer);
}
/**
* Create a new PrintWriter for this file which is then
* passed it into the given closure. This method ensures its the writer
* is closed after the closure returns.
*
* @param file a File
* @param closure the closure to invoke with the PrintWriter
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withPrintWriter(File file, Closure closure) throws IOException {
return withWriter(newPrintWriter(file), closure);
}
/**
* Create a new PrintWriter with a specified charset for
* this file. The writer is passed to the closure, and will be closed
* before this method returns.
*
* @param file a File
* @param charset the charset
* @param closure the closure to invoke with the PrintWriter
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withPrintWriter(File file, String charset, Closure closure) throws IOException {
return withWriter(newPrintWriter(file, charset), closure);
}
/**
* Create a new PrintWriter with a specified charset for
* this file. The writer is passed to the closure, and will be closed
* before this method returns.
*
* @param writer a writer
* @param closure the closure to invoke with the PrintWriter
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.6
*/
public static Object withPrintWriter(Writer writer, Closure closure) throws IOException {
return withWriter(newPrintWriter(writer), closure);
}
/**
* Allows this writer to be used within the closure, ensuring that it
* is flushed and closed before this method returns.
*
* @param writer the writer which is used and then closed
* @param closure the closure that the writer is passed into
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withWriter(Writer writer, Closure closure) throws IOException {
try {
Object result = closure.call(writer);
try {
writer.flush();
} catch (IOException e) {
// try to continue even in case of error
}
Writer temp = writer;
writer = null;
temp.close();
return result;
} finally {
closeWithWarning(writer);
}
}
/**
* Allows this reader to be used within the closure, ensuring that it
* is closed before this method returns.
*
* @param reader the reader which is used and then closed
* @param closure the closure that the writer is passed into
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withReader(Reader reader, Closure closure) throws IOException {
try {
Object result = closure.call(reader);
Reader temp = reader;
reader = null;
temp.close();
return result;
} finally {
closeWithWarning(reader);
}
}
/**
* Allows this input stream to be used within the closure, ensuring that it
* is flushed and closed before this method returns.
*
* @param stream the stream which is used and then closed
* @param closure the closure that the stream is passed into
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withStream(InputStream stream, Closure closure) throws IOException {
try {
Object result = closure.call(stream);
InputStream temp = stream;
stream = null;
temp.close();
return result;
} finally {
closeWithWarning(stream);
}
}
/**
* Helper method to create a new BufferedReader for a URL and then
* passes it to the closure. The reader is closed after the closure returns.
*
* @param url a URL
* @param closure the closure to invoke with the reader
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withReader(URL url, Closure closure) throws IOException {
return withReader(url.openConnection().getInputStream(), closure);
}
/**
* Helper method to create a new Reader for a URL and then
* passes it to the closure. The reader is closed after the closure returns.
*
* @param url a URL
* @param charset the charset used
* @param closure the closure to invoke with the reader
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.6
*/
public static Object withReader(URL url, String charset, Closure closure) throws IOException {
return withReader(url.openConnection().getInputStream(), charset, closure);
}
/**
* Helper method to create a new Reader for a stream and then
* passes it into the closure. The reader (and this stream) is closed after
* the closure returns.
*
* @see java.io.InputStreamReader
* @param in a stream
* @param closure the closure to invoke with the InputStream
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withReader(InputStream in, Closure closure) throws IOException {
return withReader(new InputStreamReader(in), closure);
}
/**
* Helper method to create a new Reader for a stream and then
* passes it into the closure. The reader (and this stream) is closed after
* the closure returns.
*
* @see java.io.InputStreamReader
* @param in a stream
* @param charset the charset used to decode the stream
* @param closure the closure to invoke with the reader
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.6
*/
public static Object withReader(InputStream in, String charset, Closure closure) throws IOException {
return withReader(new InputStreamReader(in, charset), closure);
}
/**
* Creates a writer from this stream, passing it to the given closure.
* This method ensures the stream is closed after the closure returns.
*
* @param stream the stream which is used and then closed
* @param closure the closure that the writer is passed into
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withWriter(Writer,Closure)
* @since 1.5.2
*/
public static Object withWriter(OutputStream stream, Closure closure) throws IOException {
return withWriter(new OutputStreamWriter(stream), closure);
}
/**
* Creates a writer from this stream, passing it to the given closure.
* This method ensures the stream is closed after the closure returns.
*
* @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
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @see #withWriter(Writer,Closure)
* @since 1.5.2
*/
public static Object withWriter(OutputStream stream, String charset, Closure closure) throws IOException {
return withWriter(new OutputStreamWriter(stream, charset), closure);
}
/**
* Passes this OutputStream to the closure, ensuring that the stream
* is closed after the closure returns, regardless of errors.
*
* @param os the stream which is used and then closed
* @param closure the closure that the stream is passed into
* @return the value returned by the closure
* @throws IOException if an IOException occurs.
* @since 1.5.2
*/
public static Object withStream(OutputStream os, Closure closure) throws IOException {
try {
Object result = closure.call(os);
os.flush();
OutputStream temp = os;
os = null;
temp.close();
return result;
} finally {
closeWithWarning(os);
}
}
/**
* Creates a buffered input stream for this file.
*
* @param file a File
* @return a BufferedInputStream of the file
* @throws FileNotFoundException if the file is not found.
* @since 1.0
*/
public static BufferedInputStream newInputStream(File file) throws FileNotFoundException {
return new BufferedInputStream(new FileInputStream(file));
}
/**
* Creates a buffered input stream for this URL.
*
* @param url a URL
* @return a BufferedInputStream for the URL
* @throws MalformedURLException is thrown if the URL is not well formed
* @throws IOException if an I/O error occurs while creating the input stream
* @since 1.5.2
*/
public static BufferedInputStream newInputStream(URL url) throws MalformedURLException, IOException {
return new BufferedInputStream(url.openConnection().getInputStream());
}
/**
* Creates a buffered reader for this URL.
*
* @param url a URL
* @return a BufferedReader for the URL
* @throws MalformedURLException is thrown if the URL is not well formed
* @throws IOException if an I/O error occurs while creating the input stream
* @since 1.5.5
*/
public static BufferedReader newReader(URL url) throws MalformedURLException, IOException {
return newReader(url.openConnection().getInputStream());
}
/**
* Creates a buffered reader for this URL using the given encoding.
*
* @param url a URL
* @param charset opens the stream with a specified charset
* @return a BufferedReader for the URL
* @throws MalformedURLException is thrown if the URL is not well formed
* @throws IOException if an I/O error occurs while creating the input stream
* @since 1.5.5
*/
public static BufferedReader newReader(URL url, String charset) throws MalformedURLException, IOException {
return new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), charset));
}
/**
* Create a data input stream for this file
*
* @param file a File
* @return a DataInputStream of the file
* @throws FileNotFoundException if the file is not found.
* @since 1.5.0
*/
public static DataInputStream newDataInputStream(File file) throws FileNotFoundException {
return new DataInputStream(new FileInputStream(file));
}
/**
* Traverse through each byte of this File
*
* @param self a File
* @param closure a closure
* @throws IOException if an IOException occurs.
* @see #eachByte(InputStream,Closure)
* @since 1.0
*/
public static void eachByte(File self, Closure closure) throws IOException {
BufferedInputStream is = newInputStream(self);
eachByte(is, closure);
}
/**
* Traverse through each byte of this Byte array. Alias for each.
*
* @param self a Byte array
* @param closure a closure
* @see #each(Object,Closure)
* @since 1.5.5
*/
public static void eachByte(Byte[] self, Closure closure) {
each(self, closure);
}
/**
* Traverse through each byte of this byte array. Alias for each.
*
* @param self a byte array
* @param closure a closure
* @see #each(Object,Closure)
* @since 1.5.5
*/
public static void eachByte(byte[] self, Closure closure) {
each(self, closure);
}
/**
* Traverse through each byte of the specified stream. The
* stream is closed after the closure returns.
*
* @param is stream to iterate over, closed after the method call
* @param closure closure to apply to each byte
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static void eachByte(InputStream is, Closure closure) throws IOException {
try {
while (true) {
int b = is.read();
if (b == -1) {
break;
} else {
closure.call((byte) b);
}
}
InputStream temp = is;
is = null;
temp.close();
} finally {
closeWithWarning(is);
}
}
/**
* Reads the InputStream from this URL, passing each byte to the given
* closure. The URL stream will be closed before this method returns.
*
* @param url url to iterate over
* @param closure closure to apply to each byte
* @throws IOException if an IOException occurs.
* @see #eachByte(InputStream,Closure)
* @since 1.0
*/
public static void eachByte(URL url, Closure closure) throws IOException {
InputStream is = url.openConnection().getInputStream();
eachByte(is, closure);
}
/**
* Transforms each character from this reader by passing it to the given
* closure. The Closure should return each transformed character, which
* will be passed to the Writer. The reader and writer will be both be
* closed before this method returns.
*
* @param self a Reader object
* @param writer a Writer to receive the transformed characters
* @param closure a closure that performs the required transformation
* @throws IOException if an IOException occurs.
* @since 1.5.0
*/
public static void transformChar(Reader self, Writer writer, Closure closure) throws IOException {
int c;
try {
char[] chars = new char[1];
while ((c = self.read()) != -1) {
chars[0] = (char) c;
writer.write((String) closure.call(new String(chars)));
}
writer.flush();
Writer temp2 = writer;
writer = null;
temp2.close();
Reader temp1 = self;
self = null;
temp1.close();
} finally {
closeWithWarning(self);
closeWithWarning(writer);
}
}
/**
* Transforms the lines from a reader with a Closure and
* write them to a writer. Both Reader and Writer are
* closed after the operation.
*
* @param reader Lines of text to be transformed. Reader is closed afterwards.
* @param writer Where transformed lines are written. Writer is closed afterwards.
* @param closure Single parameter closure that is called to transform each line of
* text from the reader, before writing it to the writer.
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static void transformLine(Reader reader, Writer writer, Closure closure) throws IOException {
BufferedReader br = new BufferedReader(reader);
BufferedWriter bw = new BufferedWriter(writer);
String line;
try {
while ((line = br.readLine()) != null) {
Object o = closure.call(line);
if (o != null) {
bw.write(o.toString());
bw.newLine();
}
}
bw.flush();
Writer temp2 = writer;
writer = null;
temp2.close();
Reader temp1 = reader;
reader = null;
temp1.close();
} finally {
closeWithWarning(br);
closeWithWarning(reader);
closeWithWarning(bw);
closeWithWarning(writer);
}
}
/**
* Filter the lines from a reader and write them on the writer,
* according to a closure which returns true if the line should be included.
* Both Reader and Writer are closed after the operation.
*
* @param reader a reader, closed after the call
* @param writer a writer, closed after the call
* @param closure the closure which returns booleans
* @throws IOException if an IOException occurs.
* @since 1.0
*/
public static void filterLine(Reader reader, Writer writer, Closure closure) throws IOException {
BufferedReader br = new BufferedReader(reader);
BufferedWriter bw = new BufferedWriter(writer);
String line;
try {
while ((line = br.readLine()) != null) {
if (DefaultTypeTransformation.castToBoolean(closure.call(line))) {
bw.write(line);
bw.newLine();
}
}
bw.flush();
Writer temp2 = writer;
writer = null;
temp2.close();
Reader temp1 = reader;
reader = null;
temp1.close();
} finally {
closeWithWarning(br);
closeWithWarning(reader);
closeWithWarning(bw);
closeWithWarning(writer);
}
}
/**
* 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 self is not readable
* @see #filterLine(Reader,Closure)
* @since 1.0
*/
public static Writable filterLine(File self, Closure closure) throws IOException {
return filterLine(newReader(self), closure);
}
/**
* Filters the lines of a File and creates a Writeable in return to
* stream the filtered lines.
*
* @param self a File
* @param charset opens the file with a specified charset
* @param closure a closure which returns a boolean indicating to filter
* the line or not
* @return a Writable closure
* @throws IOException if an IOException occurs
* @see #filterLine(Reader,Closure)
* @since 1.6.8
*/
public static Writable filterLine(File self, String charset, Closure closure) throws IOException {
return filterLine(newReader(self, charset), closure);
}
/**
* Filter the lines from this File, and write them to the given writer based
* on the given closure predicate.
*
* @param self a File
* @param writer a writer destination to write filtered lines to
* @param closure a closure which takes each line as a parameter and returns
* true if the line should be written to this writer.
* @throws IOException if self is not readable
* @see #filterLine(Reader,Writer,Closure)
* @since 1.0
*/
public static void filterLine(File self, Writer writer, Closure closure) throws IOException {
filterLine(newReader(self), writer, closure);
}
/**
* Filter the lines from this File, and write them to the given writer based
* on the given closure predicate.
*
* @param self a File
* @param writer a writer destination to write filtered lines to
* @param charset opens the file with a specified charset
* @param closure a closure which takes each line as a parameter and returns
* true if the line should be written to this writer.
* @throws IOException if an IO error occurs
* @see #filterLine(Reader,Writer,Closure)
* @since 1.6.8
*/
public static void filterLine(File self, Writer writer, String charset, Closure closure) throws IOException {
filterLine(newReader(self, charset), writer, closure);
}
/**
* Filter the lines from this Reader, and return a Writable which can be
* used to stream the filtered lines to a destination. The closure should
* return true if the line should be passed to the writer.
*
* @param reader this reader
* @param closure a closure used for filtering
* @return a Writable which will use the closure to filter each line
* from the reader when the Writable#writeTo(Writer) is called.
* @since 1.0
*/
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 (DefaultTypeTransformation.castToBoolean(closure.call(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 StringWriterIOException(e);
}
return buffer.toString();
}
};
}
/**
* Returns a GroovyRowResult given a ResultSet.
*
* @param rs a ResultSet
* @return the resulting GroovyRowResult
* @throws SQLException if a database error occurs
* @since 1.6.0
*/
public static GroovyRowResult toRowResult(ResultSet rs) throws SQLException {
ResultSetMetaData metadata = rs.getMetaData();
LinkedHashMap lhm = new LinkedHashMap(metadata.getColumnCount(), 1);
for (int i = 1; i <= metadata.getColumnCount(); i++) {
lhm.put(metadata.getColumnLabel(i), rs.getObject(i));
}
return new GroovyRowResult(lhm);
}
/**
* Filter lines from an input stream using a closure predicate. The closure
* will be passed each line as a String, and it should return
*