org.codehaus.groovy.runtime.DefaultGroovyMethods Maven / Gradle / Ivy
Show all versions of groovy-all Show documentation
/*
* 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.lang.*;
import groovy.sql.GroovyResultSet;
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.tools.RootLoader;
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.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
*/
public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
private static final Logger LOG = Logger.getLogger(DefaultGroovyMethods.class.getName());
private static final Integer ONE = 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: 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 List getMetaPropertyValues(Object self) {
MetaClass metaClass = InvokerHelper.getMetaClass(self);
List mps = metaClass.getProperties();
List props = new ArrayList(mps.size());
for (MetaProperty mp : mps) {
props.add(new PropertyValue(self, mp));
}
return props;
}
/**
* Convenience method that calls {@link #getMetaPropertyValues(Object)}(self)
* and provides the data in form of simple key/value pairs, i.e.&nsbp;without
* type() information.
*
* @param self the receiver object
* @return meta properties as Map of key/value pairs
* @since 1.0
*/
public static Map getProperties(Object self) {
List metaProps = getMetaPropertyValues(self);
Map props = new LinkedHashMap(metaProps.size());
for (PropertyValue mp : metaProps) {
try {
props.put(mp.getName(), mp.getValue());
} catch (Exception e) {
LOG.throwing(self.getClass().getName(), "getProperty(" + mp.getName() + ")", e);
}
}
return props;
}
/**
* Scoped use method
*
* @param self any Object
* @param categoryClass a category class to use
* @param closure the closure to invoke with the category in place
* @return the value returned from the closure
* @since 1.0
*/
public static Object use(Object self, Class categoryClass, Closure closure) {
return GroovyCategorySupport.use(categoryClass, closure);
}
/**
* Extend object with category methods.
* All methods for given class and all super classes will be added to the object.
*
* @param self any Class
* @param categoryClasses a category classes to use
* @since 1.6.0
*/
public static void mixin(MetaClass self, List categoryClasses) {
MixinInMetaClass.mixinClassesToMetaClass(self, categoryClasses);
}
/**
* Extend class globally with category methods.
* All methods for given class and all super classes will be added to the class.
*
* @param self any Class
* @param categoryClasses a category classes to use
* @since 1.6.0
*/
public static void mixin(Class self, List categoryClasses) {
mixin(getMetaClass(self), categoryClasses);
}
/**
* Extend class globally with category methods.
*
* @param self any Class
* @param categoryClass a category class to use
* @since 1.6.0
*/
public static void mixin(Class self, Class categoryClass) {
mixin(getMetaClass(self), Collections.singletonList(categoryClass));
}
/**
* Extend class globally with category methods.
*
* @param self any Class
* @param categoryClass a category class to use
* @since 1.6.0
*/
public static void mixin(Class self, Class[] categoryClass) {
mixin(getMetaClass(self), Arrays.asList(categoryClass));
}
/**
* Extend class globally with category methods.
*
* @param self any Class
* @param categoryClass a category class to use
* @since 1.6.0
*/
public static void mixin(MetaClass self, Class categoryClass) {
mixin(self, Collections.singletonList(categoryClass));
}
/**
* Extend class globally with category methods.
*
* @param self any Class
* @param categoryClass a category class to use
* @since 1.6.0
*/
public static void mixin(MetaClass self, Class[] categoryClass) {
mixin(self, Arrays.asList(categoryClass));
}
/**
* Scoped use method with list of categories.
*
* @param self any Object
* @param categoryClassList a list of category classes
* @param closure the closure to invoke with the categories in place
* @return the value returned from the closure
* @since 1.0
*/
public static Object use(Object self, List categoryClassList, Closure closure) {
return GroovyCategorySupport.use(categoryClassList, closure);
}
/**
* Allows the usage of addShutdownHook without getting the runtime first.
*
* @param self the object the method is called on (ignored)
* @param closure the shutdown hook action
* @since 1.5.0
*/
public static void addShutdownHook(Object self, Closure closure) {
Runtime.getRuntime().addShutdownHook(new Thread(closure));
}
/**
* Allows you to use a list of categories, specifying the list as varargs.
* use(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 list = new ArrayList(array.length - 1);
for (int i = 0; i < array.length - 1; ++i) {
Class categoryClass;
try {
categoryClass = (Class) array[i];
} catch (ClassCastException e) {
throw new IllegalArgumentException("Expecting a Category Class for argument " + i);
}
list.add(categoryClass);
}
return GroovyCategorySupport.use(list, closure);
}
/**
* Print a value formatted Groovy style to self if it
* is a Writer, otherwise to the standard output stream.
*
* @param self any Object
* @param value the value to print
* @since 1.0
*/
public static void print(Object self, Object value) {
// we won't get here if we are a PrintWriter
if (self instanceof Writer) {
try {
((Writer) self).write(InvokerHelper.toString(value));
} catch (IOException e) {
// TODO: Should we have some unified function like PrintWriter.checkError()?
}
} else {
System.out.print(InvokerHelper.toString(value));
}
}
/**
* Print a value formatted Groovy style to the print writer.
*
* @param self a PrintWriter
* @param value the value to print
* @since 1.0
*/
public static void print(PrintWriter self, Object value) {
self.print(InvokerHelper.toString(value));
}
/**
* Print a value formatted Groovy style to the print stream.
*
* @param self a PrintStream
* @param value the value to print
* @since 1.6
*/
public static void print(PrintStream self, Object value) {
self.print(InvokerHelper.toString(value));
}
/**
* Print a value to the standard output stream.
* This method delegates to the owner to execute the method.
*
* @param self a generated closure
* @param value the value to print
* @since 1.0
*/
public static void print(Closure self, Object value) {
Object owner = getClosureOwner(self);
InvokerHelper.invokeMethod(owner, "print", new Object[]{value});
}
/**
* Print a linebreak to the standard output stream.
*
* @param self any Object
* @since 1.0
*/
public static void println(Object self) {
// we won't get here if we are a PrintWriter
if (self instanceof Writer) {
PrintWriter pw = new GroovyPrintWriter((Writer) self);
pw.println();
} else {
System.out.println();
}
}
/**
* Print a linebreak to the standard output stream.
* This method delegates to the owner to execute the method.
*
* @param self a closure
* @since 1.0
*/
public static void println(Closure self) {
Object owner = getClosureOwner(self);
InvokerHelper.invokeMethod(owner, "println", new Object[0]);
}
private static Object getClosureOwner(Closure cls) {
Object owner = cls.getOwner();
while (owner instanceof GeneratedClosure) {
owner = ((Closure) owner).getOwner();
}
return owner;
}
/**
* Print a value formatted Groovy style (followed by a newline) to self
* if it is a Writer, otherwise to the standard output stream.
*
* @param self any Object
* @param value the value to print
* @since 1.0
*/
public static void println(Object self, Object value) {
// we won't get here if we are a PrintWriter
if (self instanceof Writer) {
final PrintWriter pw = new GroovyPrintWriter((Writer) self);
pw.println(value);
} else {
System.out.println(InvokerHelper.toString(value));
}
}
/**
* Print a value formatted Groovy style (followed by a newline) to the print writer.
*
* @param self a PrintWriter
* @param value the value to print
* @since 1.0
*/
public static void println(PrintWriter self, Object value) {
self.println(InvokerHelper.toString(value));
}
/**
* Print a value formatted Groovy style (followed by a newline) to the print stream.
*
* @param self any Object
* @param value the value to print
* @since 1.6
*/
public static void println(PrintStream self, Object value) {
self.println(InvokerHelper.toString(value));
}
/**
* Print a value (followed by a newline) to the standard output stream.
* This method delegates to the owner to execute the method.
*
* @param self a closure
* @param value the value to print
* @since 1.0
*/
public static void println(Closure self, Object value) {
Object owner = getClosureOwner(self);
InvokerHelper.invokeMethod(owner, "println", new Object[]{value});
}
/**
* Printf to a console (Only works with JDK1.5 or later).
*
* @param self any Object
* @param format a format string
* @param values values referenced by the format specifiers in the format string.
* @since 1.0
*/
public static void printf(Object self, String format, Object[] values) {
if (self instanceof PrintStream)
((PrintStream)self).printf(format, values);
else
System.out.printf(format, values);
}
/**
* Sprintf to a string (Only works with JDK1.5 or later).
*
* @param self any Object
* @param format a format string
* @param values values referenced by the format specifiers in the format string.
* @return the resulting formatted string
* @since 1.5.0
*/
public static String sprintf(Object self, String format, Object[] values) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PrintStream out = new PrintStream(outputStream);
out.printf(format, values);
return outputStream.toString();
}
/**
* Prints a formatted string using the specified format string and
* arguments.
*
* For examples,
* 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 Iterator unique(Iterator self) {
return toList(unique(toList(self))).listIterator();
}
/**
* Modifies this collection to remove all duplicated items, using the
* default comparator.
*
* @param self a collection
* @return the now modified collection
* @since 1.0
*/
public static Collection unique(Collection self) {
if (self instanceof Set)
return self;
List answer = new ArrayList();
NumberAwareComparator numberAwareComparator = new NumberAwareComparator();
for (T t : self) {
boolean duplicated = false;
for (T t2 : answer) {
if (numberAwareComparator.compare(t, t2) == 0) {
duplicated = true;
break;
}
}
if (!duplicated)
answer.add(t);
}
self.clear();
self.addAll(answer);
return self;
}
/**
* Provides a method that compares two comparables using Groovy's
* default number aware comparator.
*
* @param self a Comparable
* @param other another Comparable
* @return a -ve number, 0 or a +ve number according to Groovy's compareTo contract
* @since 1.6.0
*/
public static int numberAwareCompareTo(Comparable self, Comparable other) {
NumberAwareComparator numberAwareComparator = new NumberAwareComparator();
return numberAwareComparator.compare(self, other);
}
/**
* Returns an iterator equivalent to this iterator but with all duplicated items
* removed by using a Closure to determine duplicate (equal) items.
* The original iterator will be fully processed after the call.
*
* If the closure takes a
* single parameter, the argument passed will be each element, and the
* closure should return a value used for comparison (either using
* {@link Comparable#compareTo(Object)} or {@link Object#equals(Object)}).
* If the closure takes two parameters, two items from the Iterator
* will be passed as arguments, and the closure should return an
* int value (with 0 indicating the items are not unique).
*
* @param self an Iterator
* @param closure a Closure used to determine unique items
* @return the modified Iterator
* @since 1.5.5
*/
public static Iterator unique(Iterator self, Closure closure) {
return toList(unique(toList(self), closure)).listIterator();
}
/**
* A convenience method for making a collection unique using a Closure
* to determine duplicate (equal) items.
*
* If the closure takes a single parameter, the
* argument passed will be each element, and the closure
* should return a value used for comparison (either using
* {@link Comparable#compareTo(Object)} or {@link Object#equals(Object)}).
* If the closure takes two parameters, two items from the collection
* will be passed as arguments, and the closure should return an
* int value (with 0 indicating the items are not unique).
*
* @param self a Collection
* @param closure a Closure used to determine unique items
* @return self without any duplicates
* @since 1.0
*/
public static Collection unique(Collection self, Closure closure) {
// use a comparator of one item or two
int params = closure.getMaximumNumberOfParameters();
if (params == 1) {
unique(self, new OrderBy(closure));
} else {
unique(self, new ClosureComparator(closure));
}
return self;
}
/**
* Returns an iterator equivalent to this iterator with all duplicated
* items removed by using the supplied comparator.
*
* @param self an Iterator
* @param comparator a Comparator
* @return the modified Iterator
* @since 1.5.5
*/
public static Iterator unique(Iterator self, Comparator comparator) {
return toList(unique(toList(self), comparator)).listIterator();
}
/**
* Remove all duplicates from a given Collection.
* Works on the receiver object and returns it.
* The order of members in the Collection are compared by the given Comparator.
* For each duplicate, the first member which is returned
* by the given Collection's iterator is retained, but all other ones are removed.
* The given Collection's original order is preserved.
*
*
* 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] )
*
*
*
* @param self a Collection
* @param comparator a Comparator
* @return self the now modified collection without duplicates
* @since 1.0
*/
public static Collection unique(Collection self, Comparator comparator) {
List answer = new ArrayList();
for (T t : self) {
boolean duplicated = false;
for (T t2 : answer) {
if (comparator.compare(t, t2) == 0) {
duplicated = true;
break;
}
}
if (!duplicated)
answer.add(t);
}
self.clear();
self.addAll(answer);
return self;
}
/**
* Iterates through an aggregate type or data structure,
* passing each item to the given closure. Custom types may utilize this
* method by simply providing an "iterator()" method. The items returned
* from the resulting iterator will be passed to the closure.
*
* @param self the object over which we iterate
* @param closure the closure applied on each element found
* @return the self Object
* @since 1.0
*/
public static T each(T self, Closure closure) {
each(InvokerHelper.asIterator(self), closure);
return self;
}
/**
* Iterates through an aggregate type or data structure,
* passing each item and the item's index (a counter starting at
* zero) to the given closure.
*
* @param self an Object
* @param closure a Closure to operate on each item
* @return the self Object
* @since 1.0
*/
public static Object eachWithIndex(Object self, Closure closure) {
int counter = 0;
for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
closure.call(new Object[]{iter.next(), counter++});
}
return self;
}
private static Iterator each(Iterator iter, Closure closure) {
while (iter.hasNext()) {
closure.call(iter.next());
}
return iter;
}
/**
* Allows a Map to be iterated through using a closure. If the
* closure takes one parameter then it will be passed the Map.Entry
* otherwise if the closure takes two parameters then it will be
* passed the key and the value.
*
* @param self the map over which we iterate
* @param closure the closure applied on each entry of the map
* @return returns the self parameter
* @since 1.5.0
*/
public static Map each(Map self, Closure closure) {
for (Map.Entry entry : self.entrySet()) {
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.
*
* @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 Map eachWithIndex(Map self, Closure closure) {
int counter = 0;
for (Map.Entry entry : self.entrySet()) {
callClosureForMapEntryAndCounter(closure, entry, counter++);
}
return self;
}
/**
* Iterate over each element of the list in the reverse order.
*
* @param self a List
* @param closure a closure to which each item is passed.
* @return the original list
* @since 1.5.0
*/
public static List reverseEach(List self, Closure closure) {
each(new ReverseListIterator(self), closure);
return self;
}
/**
* Iterate over each element of the array in the reverse order.
*
* @param self an Object array
* @param closure a closure to which each item is passed
* @return the original array
* @since 1.5.2
*/
public static T[] reverseEach(T[] self, Closure closure) {
each(new ReverseListIterator(Arrays.asList(self)), closure);
return self;
}
/**
* Reverse the items in an Object array.
*
* @param self an Object array
* @return an array containing the reversed items
* @since 1.5.5
*/
@SuppressWarnings("unchecked")
public static T[] reverse(T[] self) {
return (T[]) toList(new ReverseListIterator(Arrays.asList(self))).toArray();
}
/**
* Used to determine if the given predicate closure is valid (i.e.&nsbp;returns
* 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.
*
* @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 (Map.Entry entry : self.entrySet()) {
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
*
* @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 (Map.Entry entry : self.entrySet()) {
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 List toList(Collection self) {
List answer = new ArrayList(self.size());
answer.addAll(self);
return answer;
}
/**
* Convert an iterator to a List. The iterator will become
* exhausted of elements after making this conversion.
*
* @param self an iterator
* @return a List
* @since 1.5.0
*/
public static List toList(Iterator self) {
List answer = new ArrayList();
while (self.hasNext()) {
answer.add(self.next());
}
return answer;
}
/**
* Convert an enumeration to a List.
*
* @param self an enumeration
* @return a List
* @since 1.5.0
*/
public static List toList(Enumeration self) {
List answer = new ArrayList();
while (self.hasMoreElements()) {
answer.add(self.nextElement());
}
return answer;
}
/**
* Iterates through this object transforming each value into a new value using the
* closure as a transformer, returning a list of transformed values.
* Example:
* def list = [1, 'a', 1.23, true ]
* def types = list.collect { it.class }
*
*
* @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.
*
* @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.
*
* @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 T find(Collection self, Closure closure) {
for (T value : self) {
if (DefaultTypeTransformation.castToBoolean(closure.call(value))) {
return value;
}
}
return null;
}
/**
* Finds the first entry matching the closure condition. If the closure takes
* two parameters, the entry key and value are passed. If the closure takes
* one parameter, the Map.Entry object is passed.
*
* @param self a Map
* @param closure a closure condition
* @return the first Object found
* @since 1.0
*/
public static Map.Entry find(Map self, Closure closure) {
for (Map.Entry entry : self.entrySet()) {
if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) {
return entry;
}
}
return null;
}
/**
* Finds all values matching the closure condition.
*
* @param self a Collection
* @param closure a closure condition
* @return a Collection of matching values
* @since 1.5.6
*/
public static Collection findAll(Collection self, Closure closure) {
Collection answer = createSimilarCollection(self);
Iterator iter = self.iterator();
return findAll(closure, answer, iter);
}
/**
* Finds all items matching the closure condition.
*
* @param self an Object with an Iterator returning its values
* @param closure a closure condition
* @return a List of the values found
* @since 1.6.0
*/
public static Collection findAll(Object self, Closure closure) {
List answer = new ArrayList();
Iterator iter = InvokerHelper.asIterator(self);
return findAll(closure, answer, iter);
}
private static Collection findAll(Closure closure, Collection answer, Iterator iter) {
while (iter.hasNext()) {
T value = iter.next();
if (DefaultTypeTransformation.castToBoolean(closure.call(value))) {
answer.add(value);
}
}
return answer;
}
/**
* Splits all items into two lists based on the closure condition.
* The first list contains all items matching the closure expression.
* The second list all those that don't.
*
* @param self an Object with an Iterator returning its values
* @param closure a closure condition
* @return a List containing whose first item is the accepted values and whose second item is the rejected values
* @since 1.6.0
*/
public static Collection split(Object self, Closure closure) {
List accept = new ArrayList();
List reject = new ArrayList();
return split(closure, accept, reject, InvokerHelper.asIterator(self));
}
/**
* Splits all items into two collections based on the closure condition.
* The first list contains all items which match the closure expression.
* The second list all those that don't.
*
* @param self a Collection of values
* @param closure a closure condition
* @return a List containing whose first item is the accepted values and whose second item is the rejected values
* @since 1.6.0
*/
public static Collection> split(Collection self, Closure closure) {
Collection accept = createSimilarCollection(self);
Collection reject = createSimilarCollection(self);
Iterator iter = self.iterator();
return split(closure, accept, reject, iter);
}
private static Collection> split(Closure closure, Collection accept, Collection reject, Iterator iter) {
List> answer = new ArrayList>();
while (iter.hasNext()) {
T value = iter.next();
if (DefaultTypeTransformation.castToBoolean(closure.call(value))) {
accept.add(value);
} else {
reject.add(value);
}
}
answer.add(accept);
answer.add(reject);
return answer;
}
/**
* Adds GroovyCollections#combinations(Collection) as a method on collections.
*
* @param self a Collection of lists
* @return a List of the combinations found
* @see groovy.util.GroovyCollections#combinations(java.util.Collection)
* @since 1.5.0
*/
public static List combinations(Collection self) {
return GroovyCollections.combinations(self);
}
/**
* Adds GroovyCollections#transpose(List) as a method on lists.
*
* @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.
*
* If the 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 closure a closure condition applying on the entries
* @return a new subMap
* @since 1.0
*/
public static Map findAll(Map self, Closure closure) {
Map answer = createSimilarMap(self);
for (Map.Entry entry : self.entrySet()) {
if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) {
answer.put(entry.getKey(), entry.getValue());
}
}
return answer;
}
/**
* Sorts all collection members into groups determined by the
* supplied mapping closure. The closure should return the key that this
* item should be grouped by. The returned LinkedHashMap will have an entry for each
* distinct key returned from the closure, with each value being a list of
* items for that group.
*
* @param self a collection to group (no map)
* @param closure a closure mapping entries on keys
* @return a new Map grouped by keys
* @since 1.0
*/
public static Map
* If the closure has two parameters
* it is used like a traditional Comparator. I.e. it should compare
* its two parameters for order, returning a negative integer,
* zero, or a positive integer when the first parameter is less than,
* equal to, or greater than the second respectively. Otherwise,
* the Closure is assumed to take a single parameter and return a
* Comparable (typically an Integer) which is then used for
* further comparison.
*
* @param self an Iterator
* @param closure a Closure used to determine the correct ordering
* @return the minimum value
* @see #min(java.util.Collection, groovy.lang.Closure)
* @since 1.5.5
*/
public static T min(Iterator self, Closure closure) {
return min(toList(self), closure);
}
/**
* Selects the minimum value found from the Object array
* using the closure to determine the correct ordering.
*
* If the closure has two parameters
* it is used like a traditional Comparator. I.e. it should compare
* its two parameters for order, returning a negative integer,
* zero, or a positive integer when the first parameter is less than,
* equal to, or greater than the second respectively. Otherwise,
* the Closure is assumed to take a single parameter and return a
* Comparable (typically an Integer) which is then used for
* further comparison.
*
* @param self an Object array
* @param closure a Closure used to determine the correct ordering
* @return the minimum value
* @see #min(java.util.Collection, groovy.lang.Closure)
* @since 1.5.5
*/
public static T min(T[] self, Closure closure) {
return min(toList(self), closure);
}
/**
* Adds max() method to Collection objects.
*
* @param self a Collection
* @return the maximum value
* @see groovy.util.GroovyCollections#max(java.util.Collection)
* @since 1.0
*/
public static T max(Collection self) {
return GroovyCollections.max(self);
}
/**
* Adds max() method to Iterator objects. The iterator will become
* exhausted of elements after determining the maximum value.
*
* @param self an Iterator
* @return the maximum value
* @see groovy.util.GroovyCollections#max(java.util.Collection)
* @since 1.5.5
*/
public static T max(Iterator self) {
return max(toList(self));
}
/**
* Adds max() method to Object arrays.
*
* @param self an Object array
* @return the maximum value
* @see #max(java.util.Collection)
* @since 1.5.5
*/
public static T max(T[] self) {
return max(toList(self));
}
/**
* Selects the maximum value found in the collection
* using the closure to determine the correct ordering.
*
* If the closure has two parameters
* it is used like a traditional Comparator. I.e. it should compare
* its two parameters for order, returning a negative integer,
* zero, or a positive integer when the first parameter is less than,
* equal to, or greater than the second respectively. Otherwise,
* the Closure is assumed to take a single parameter and return a
* Comparable (typically an Integer) which is then used for
* further comparison.
*
* @param self a Collection
* @param closure a Closure used to determine the correct ordering
* @return the maximum value
* @since 1.0
*/
public static T max(Collection self, Closure closure) {
int params = closure.getMaximumNumberOfParameters();
if (params != 1) {
return max(self, new ClosureComparator(closure));
}
T answer = null;
Object answerValue = null;
for (T item : self) {
Object value = closure.call(item);
if (answer == null || ScriptBytecodeAdapter.compareLessThan(answerValue, value)) {
answer = item;
answerValue = value;
}
}
return answer;
}
/**
* Selects the maximum value found from the Iterator
* using the closure to determine the correct ordering.
* The iterator will become exhausted of elements after this operation.
*
* If the closure has two parameters
* it is used like a traditional Comparator. I.e. it should compare
* its two parameters for order, returning a negative integer,
* zero, or a positive integer when the first parameter is less than,
* equal to, or greater than the second respectively. Otherwise,
* the Closure is assumed to take a single parameter and return a
* Comparable (typically an Integer) which is then used for
* further comparison.
*
* @param self an Iterator
* @param closure a Closure used to determine the correct ordering
* @return the maximum value
* @see #max(java.util.Collection, groovy.lang.Closure)
* @since 1.5.5
*/
public static T max(Iterator self, Closure closure) {
return max(toList(self), closure);
}
/**
* Selects the maximum value found from the Object array
* using the closure to determine the correct ordering.
*
* If the closure has two parameters
* it is used like a traditional Comparator. I.e. it should compare
* its two parameters for order, returning a negative integer,
* zero, or a positive integer when the first parameter is less than,
* equal to, or greater than the second respectively. Otherwise,
* the Closure is assumed to take a single parameter and return a
* Comparable (typically an Integer) which is then used for
* further comparison.
*
* @param self an Object array
* @param closure a Closure used to determine the correct ordering
* @return the maximum value
* @see #max(java.util.Collection, groovy.lang.Closure)
* @since 1.5.5
*/
public static T max(T[] self, Closure closure) {
return max(toList(self), closure);
}
/**
* Selects the maximum value found in the collection using the given comparator.
*
* @param self a Collection
* @param comparator a Comparator
* @return the maximum value
* @since 1.0
*/
public static T max(Collection self, Comparator comparator) {
T answer = null;
for (T value : self) {
if (answer == null || comparator.compare(value, answer) > 0) {
answer = value;
}
}
return answer;
}
/**
* Selects the maximum value found from the Iterator using the given comparator.
*
* @param self an Iterator
* @param comparator a Comparator
* @return the maximum value
* @since 1.5.5
*/
public static T max(Iterator self, Comparator comparator) {
return max(toList(self), comparator);
}
/**
* Selects the maximum value found from the Object array using the given comparator.
*
* @param self an Object array
* @param comparator a Comparator
* @return the maximum value
* @since 1.5.5
*/
public static T max(T[] self, Comparator comparator) {
return max(toList(self), comparator);
}
/**
* Provide the standard Groovy size()
method for Iterator
.
* The iterator will become exhausted of elements after determining the size value.
*
* @param self an Iterator
* @return the length of the Iterator
* @since 1.5.5
*/
public static int size(Iterator self) {
int count = 0;
while (self.hasNext()) {
self.next();
count++;
}
return count;
}
/**
* Provide the standard Groovy size()
method for String
.
*
* @param text a String
* @return the length of the String
* @since 1.0
*/
public static int size(String text) {
return text.length();
}
/**
* Provide the standard Groovy size()
method for StringBuffer
.
*
* @param buffer a StringBuffer
* @return the length of the StringBuffer
* @since 1.0
*/
public static int size(StringBuffer buffer) {
return buffer.length();
}
/**
* Provide the standard Groovy size()
method for File
.
*
* @param self a file object
* @return the file's size (length)
* @since 1.5.0
*/
public static long size(File self) {
return self.length();
}
/**
* Provide the standard Groovy size()
method for Matcher
.
*
* @param self a matcher object
* @return the matcher's size (count)
* @since 1.5.0
*/
public static long size(Matcher self) {
return getCount(self);
}
/**
* Provide the standard Groovy size()
method for an array.
*
* @param self an Array of objects
* @return the size (length) of the Array
* @since 1.0
*/
public static int size(Object[] self) {
return self.length;
}
/**
* Support the subscript operator for CharSequence.
*
* @param text a CharSequence
* @param index the index of the Character to get
* @return the Character at the given index
* @since 1.0
*/
public static CharSequence getAt(CharSequence text, int index) {
index = normaliseIndex(index, text.length());
return text.subSequence(index, index + 1);
}
/**
* Support the subscript operator for String.
*
* @param text a String
* @param index the index of the Character to get
* @return the Character at the given index
* @since 1.0
*/
public static String getAt(String text, int index) {
index = normaliseIndex(index, text.length());
return text.substring(index, index + 1);
}
/**
* Support the range subscript operator for CharSequence
*
* @param text a CharSequence
* @param range a Range
* @return the subsequence CharSequence
* @since 1.0
*/
public static CharSequence getAt(CharSequence text, Range range) {
int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), text.length());
int to = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getTo()), text.length());
boolean reverse = range.isReverse();
// If this is a backwards range, reverse the arguments to substring.
if (from > to) {
int tmp = from;
from = to;
to = tmp;
reverse = !reverse;
}
CharSequence sequence = text.subSequence(from, to + 1);
return reverse ? reverse((String) sequence) : sequence;
}
/**
* Support the range subscript operator for CharSequence or StringBuffer with IntRange
*
* @param text a CharSequence
* @param range an IntRange
* @return the subsequence CharSequence
* @since 1.0
*/
public static CharSequence getAt(CharSequence text, IntRange range) {
return getAt(text, (Range) range);
}
/**
* Support the range subscript operator for CharSequence or StringBuffer with EmptyRange
*
* @param text a CharSequence
* @param range an EmptyRange
* @return the subsequence CharSequence
* @since 1.5.0
*/
public static CharSequence getAt(CharSequence text, EmptyRange range) {
return "";
}
/**
* Support the range subscript operator for String with IntRange
*
* @param text a String
* @param range an IntRange
* @return the resulting String
* @since 1.0
*/
public static String getAt(String text, IntRange range) {
return getAt(text, (Range) range);
}
/**
* Support the range subscript operator for String with EmptyRange
*
* @param text a String
* @param range an EmptyRange
* @return the resulting String
* @since 1.5.0
*/
public static String getAt(String text, EmptyRange range) {
return "";
}
/**
* Support the range subscript operator for String
*
* @param text a String
* @param range a Range
* @return a substring corresponding to the Range
* @since 1.0
*/
public static String getAt(String text, Range range) {
int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), text.length());
int to = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getTo()), text.length());
// If this is a backwards range, reverse the arguments to substring.
boolean reverse = range.isReverse();
if (from > to) {
int tmp = to;
to = from;
from = tmp;
reverse = !reverse;
}
String answer = text.substring(from, to + 1);
if (reverse) {
answer = reverse(answer);
}
return answer;
}
/**
* Creates a new string which is the reverse (backwards) of this string
*
* @param self a String
* @return a new string with all the characters reversed.
* @since 1.0
* @see StringBuilder#reverse()
*/
public static String reverse(String self) {
return new StringBuilder(self).reverse().toString();
}
/**
* Transforms a String representing a URL into a URL object.
*
* @param self the String representing a URL
* @return a URL
* @throws MalformedURLException is thrown if the URL is not well formed.
* @since 1.0
*/
public static URL toURL(String self) throws MalformedURLException {
return new URL(self);
}
/**
* Transforms a String representing a URI into a URI object.
*
* @param self the String representing a URI
* @return a URI
* @throws URISyntaxException is thrown if the URI is not well formed.
* @since 1.0
*/
public static URI toURI(String self) throws URISyntaxException {
return new URI(self);
}
/**
* Turns a String into a regular expression Pattern
*
* @param self a String to convert into a regular expression
* @return the regular expression pattern
* @since 1.5.0
*/
public static Pattern bitwiseNegate(String self) {
return Pattern.compile(self);
}
/**
* Replaces the first substring of a String that matches the given
* compiled regular expression with the given replacement.
*
* Note that backslashes (\) and dollar signs ($) in the
* replacement string may cause the results to be different than if it were
* being treated as a literal replacement string; see
* {@link java.util.regex.Matcher#replaceFirst}.
* Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special
* meaning of these characters, if desired.
*
* @param self the string that is to be matched
* @param pattern the regex Pattern to which the string of interest is to be matched
* @param replacement the string to be substituted for the first match
* @return The resulting String
* @see java.lang.String#replaceFirst(String, String)
*
* @since 1.6.1
*/
public static String replaceFirst(String self, Pattern pattern, String replacement) {
return pattern.matcher(self).replaceFirst(replacement);
}
/**
* Replaces all substrings of a String that match the given
* compiled regular expression with the given replacement.
*
* Note that backslashes (\) and dollar signs ($) in the
* replacement string may cause the results to be different than if it were
* being treated as a literal replacement string; see
* {@link java.util.regex.Matcher#replaceAll}.
* Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special
* meaning of these characters, if desired.
*
* @param self the string that is to be matched
* @param pattern the regex Pattern to which the string of interest is to be matched
* @param replacement the string to be substituted for the first match
* @return The resulting String
* @see java.lang.String#replaceAll(String, String)
*
* @since 1.6.1
*/
public static String replaceAll(String self, Pattern pattern, String replacement) {
return pattern.matcher(self).replaceAll(replacement);
}
/**
* Tells whether or not self matches the given
* compiled regular expression Pattern.
*
* @param self the string that is to be matched
* @param pattern the regex Pattern to which the string of interest is to be matched
* @return The resulting String
* @see java.lang.String#matches(String)
*
* @since 1.6.1
*/
public static boolean matches(String self, Pattern pattern) {
return pattern.matcher(self).matches();
}
/**
* Finds the first occurrence of a regular expression String within a String.
* If the regex doesn't match, null will be returned.
*
* For example, if the regex 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, 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
* @since 1.0
* @see java.util.regex.Matcher#quoteReplacement(String)
*/
public static String replaceAll(final String self, final String regex, final Closure closure) {
final Matcher matcher = Pattern.compile(regex).matcher(self);
if (matcher.find()) {
final StringBuffer sb = new StringBuffer(self.length() + 16);
do {
int count = matcher.groupCount();
List groups = new ArrayList();
for (int i = 0; i <= count; i++) {
groups.add(matcher.group(i));
}
final String replacement = InvokerHelper.toString(closure.call(groups.toArray()));
matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
} while (matcher.find());
matcher.appendTail(sb);
return sb.toString();
} else {
return self;
}
}
private static String getPadding(String padding, int length) {
if (padding.length() < length) {
return multiply(padding, length / padding.length() + 1).substring(0, length);
} else {
return padding.substring(0, length);
}
}
/**
* Pad a String with the characters appended to the left
*
* @param self a String object
* @param numberOfChars the total number of characters
* @param padding the charaters used for padding
* @return the String padded to the left
* @since 1.0
*/
public static String padLeft(String self, Number numberOfChars, String padding) {
int numChars = numberOfChars.intValue();
if (numChars <= self.length()) {
return self;
} else {
return getPadding(padding, numChars - self.length()) + self;
}
}
/**
* Pad a String with the spaces appended to the left
*
* @param self a String object
* @param numberOfChars the total number of characters
* @return the String padded to the left
* @since 1.0
*/
public static String padLeft(String self, Number numberOfChars) {
return padLeft(self, numberOfChars, " ");
}
/**
* Pad a String with the characters appended to the right
*
* @param self a String object
* @param numberOfChars the total number of characters
* @param padding the charaters used for padding
* @return the String padded to the right
* @since 1.0
*/
public static String padRight(String self, Number numberOfChars, String padding) {
int numChars = numberOfChars.intValue();
if (numChars <= self.length()) {
return self;
} else {
return self + getPadding(padding, numChars - self.length());
}
}
/**
* Pad a String with the spaces appended to the right
*
* @param self a String object
* @param numberOfChars the total number of characters
* @return the String padded to the right
* @since 1.0
*/
public static String padRight(String self, Number numberOfChars) {
return padRight(self, numberOfChars, " ");
}
/**
* Center a String and pad it with the characters appended around it
*
* @param self a String object
* @param numberOfChars the total number of characters
* @param padding the charaters used for padding
* @return the String centered with padded character around
* @since 1.0
*/
public static String center(String self, Number numberOfChars, String padding) {
int numChars = numberOfChars.intValue();
if (numChars <= self.length()) {
return self;
} else {
int charsToAdd = numChars - self.length();
String semiPad = charsToAdd % 2 == 1 ?
getPadding(padding, charsToAdd / 2 + 1) :
getPadding(padding, charsToAdd / 2);
if (charsToAdd % 2 == 0)
return semiPad + self + semiPad;
else
return semiPad.substring(0, charsToAdd / 2) + self + semiPad;
}
}
/**
* Center a String and pad it with spaces appended around it
*
* @param self a String object
* @param numberOfChars the total number of characters
* @return the String centered with padded character around
* @since 1.0
*/
public static String center(String self, Number numberOfChars) {
return center(self, numberOfChars, " ");
}
/**
* Support the subscript operator, e.g. matcher[index], for a regex Matcher.
*
* For an example using no group match,
*
* 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
*
* @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 List getAt(List self, IntRange range) {
RangeInfo info = subListBorders(self.size(), range);
List answer = self.subList(info.from, info.to); // sublist is always exclusive, but Ranges are not
if (info.reverse) {
answer = reverse(answer);
}
return answer;
}
/**
* Select a List of items from a List using a Collection to
* identify the indices to be selected.
*
* @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 List getAt(List self, Collection indices) {
List answer = new ArrayList(indices.size());
for (Object value : indices) {
if (value instanceof Range) {
answer.addAll(getAt(self, (Range) value));
} else if (value instanceof List) {
answer.addAll(getAt(self, (List) value));
} else {
int idx = DefaultTypeTransformation.intUnbox(value);
answer.add(getAt(self, idx));
}
}
return answer;
}
/**
* Select a List of items from an Object array using a Collection to
* identify the indices to be selected.
*
* @param self an Array of Objects
* @param indices a Collection of indices
* @return a new list of the values at the given indices
* @since 1.0
*/
public static List getAt(T[] self, Collection indices) {
List answer = new ArrayList(indices.size());
for (Object value : indices) {
if (value instanceof Range) {
answer.addAll(getAt(self, (Range) value));
} else if (value instanceof Collection) {
answer.addAll(getAt(self, (Collection) value));
} else {
int idx = DefaultTypeTransformation.intUnbox(value);
answer.add(getAtImpl(self, idx));
}
}
return answer;
}
/**
* Select a List of characters from a CharSequence using a Collection
* to identify the indices to be selected.
*
* @param self a CharSequence
* @param indices a Collection of indices
* @return a CharSequence consisting of the characters at the given indices
* @since 1.0
*/
public static CharSequence getAt(CharSequence self, Collection indices) {
StringBuilder answer = new StringBuilder();
for (Object value : indices) {
if (value instanceof Range) {
answer.append(getAt(self, (Range) value));
} else if (value instanceof Collection) {
answer.append(getAt(self, (Collection) value));
} else {
int idx = DefaultTypeTransformation.intUnbox(value);
answer.append(getAt(self, idx));
}
}
return answer.toString();
}
/**
* Select a List of characters from a String using a Collection
* to identify the indices to be selected.
*
* @param self a String
* @param indices a Collection of indices
* @return a String consisting of the characters at the given indices
* @since 1.0
*/
public static String getAt(String self, Collection indices) {
return (String) getAt((CharSequence) self, indices);
}
/**
* Select a List of values from a Matcher using a Collection
* to identify the indices to be selected.
*
* @param self a Matcher
* @param indices a Collection of indices
* @return a String of the values at the given indices
* @since 1.6.0
*/
public static List getAt(Matcher self, Collection indices) {
List result = new ArrayList();
for (Object value : indices) {
if (value instanceof Range) {
result.addAll(getAt(self, (Range) value));
} else {
int idx = DefaultTypeTransformation.intUnbox(value);
result.add(getAt(self, idx));
}
}
return result;
}
/**
* Creates a sub-Map containing the given keys. This method is similar to
* List.subList() but uses keys rather than index ranges.
*
* @param map a Map
* @param keys a Collection of keys
* @return a new Map containing the given keys
* @since 1.0
*/
public static Map subMap(Map map, Collection keys) {
Map answer = new LinkedHashMap(keys.size());
for (K key : keys) {
answer.put(key, map.get(key));
}
return answer;
}
/**
* Looks up an item in a Map for the given key and returns the value - unless
* there is no entry for the given key in which case add the default value
* to the map and return that.
*
* @param map a Map
* @param key the key to lookup the value of
* @param defaultValue the value to return and add to the map for this key if
* there is no entry for the given key
* @return the value of the given key or the default value, added to the map if the
* key did not exist
* @since 1.0
*/
public static V get(Map map, K key, V defaultValue) {
if (!map.containsKey(key)) {
map.put(key, defaultValue);
}
return map.get(key);
}
/**
* Support the range subscript operator for an Array
*
* @param array an Array of Objects
* @param range a Range
* @return a range of a list from the range's from index up to but not
* including the range's to value
* @since 1.0
*/
public static List getAt(T[] array, Range range) {
List list = Arrays.asList(array);
return getAt(list, range);
}
/**
*
* @param array an Array of Objects
* @param range an IntRange
* @return a range of a list from the range's from index up to but not
* including the range's to value
* @since 1.0
*/
public static List getAt(T[] array, IntRange range) {
List list = Arrays.asList(array);
return getAt(list, range);
}
/**
*
* @param array an Array of Objects
* @param range an EmptyRange
* @return an empty Range
* @since 1.5.0
*/
public static List getAt(T[] array, EmptyRange range) {
return new ArrayList();
}
/**
*
* @param array an Array of Objects
* @param range an ObjectRange
* @return a range of a list from the range's from index up to but not
* including the range's to value
* @since 1.0
*/
public static List getAt(T[] array, ObjectRange range) {
List list = Arrays.asList(array);
return getAt(list, range);
}
private static T getAtImpl(T[] array, int idx) {
return array[normaliseIndex(idx, array.length)];
}
/**
* Allows conversion of arrays into a mutable List.
*
* @param array an Array of Objects
* @return the array as a List
* @since 1.0
*/
public static List toList(T[] array) {
return new ArrayList(Arrays.asList(array));
}
/**
* Support the subscript operator for a List.
*
* @param self a List
* @param idx an index
* @return the value at the given index
* @since 1.0
*/
public static T getAt(List self, int idx) {
int size = self.size();
int i = normaliseIndex(idx, size);
if (i < size) {
return self.get(i);
} else {
return null;
}
}
/**
* Support the subscript operator for a Date.
*
* @param self a Date
* @param field a Calendar field, e.g. MONTH
* @return the value for the given field, e.g. FEBRUARY
* @see java.util.Calendar
* @since 1.5.5
*/
public static int getAt(Date self, int field) {
Calendar cal = Calendar.getInstance();
cal.setTime(self);
return cal.get(field);
}
/**
* A helper method to allow lists to work with subscript operators.
*
* @param self a List
* @param idx an index
* @param value the value to put at the given index
* @since 1.0
*/
public static void putAt(List self, int idx, T value) {
int size = self.size();
idx = normaliseIndex(idx, size);
if (idx < size) {
self.set(idx, value);
} else {
while (size < idx) {
self.add(size++, null);
}
self.add(idx, value);
}
}
/**
* Support the range subscript operator for StringBuffer. Index values are
* treated as characters within the buffer.
*
* @param self a StringBuffer
* @param range a Range
* @param value the object that's toString() will be inserted
* @since 1.0
*/
public static void putAt(StringBuffer self, IntRange range, Object value) {
RangeInfo info = subListBorders(self.length(), range);
self.replace(info.from, info.to, value.toString());
}
/**
* Support the range subscript operator for StringBuffer.
*
* @param self a StringBuffer
* @param range a Range
* @param value the object that's toString() will be inserted
* @since 1.0
*/
public static void putAt(StringBuffer self, EmptyRange range, Object value) {
RangeInfo info = subListBorders(self.length(), range);
self.replace(info.from, info.to, value.toString());
}
/**
* A helper method to allow lists to work with subscript operators.
*
* @param self a List
* @param range the subset of the list to set
* @param value the values to put at the given sublist or a Collection of values
* @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);
}
}
public static void putAt(List self, EmptyRange range, Collection value) {
putAt(self, range, (Object)value);
}
private static List resizeListWithRangeAndGetSublist(List self, IntRange range) {
RangeInfo info = subListBorders(self.size(), range);
int size = self.size();
if (info.to >= size) {
while (size < info.to) {
self.add(size++, null);
}
}
List sublist = self.subList(info.from, info.to);
sublist.clear();
return sublist;
}
/**
* List subscript assignment operator when given a range as the index and
* the assignment operand is a collection.
* Example: myList[3..5] = anotherList
. 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: myList[3..5] = newItem
. 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.
*
* @param self a List
* @param splice the subset of the list to set
* @param values the value to put at the given sublist
* @since 1.0
*/
public static void putAt(List self, List splice, List values) {
if (splice.isEmpty()) {
if ( ! values.isEmpty() )
throw new IllegalArgumentException("Trying to replace 0 elements with "+values.size()+" elements");
return;
}
Object first = splice.iterator().next();
if (first instanceof Integer) {
if (values.size() != splice.size())
throw new IllegalArgumentException("Trying to replace "+splice.size()+" elements with "+values.size()+" elements");
Iterator valuesIter = values.iterator();
for (Object index : splice) {
putAt(self, (Integer) index, valuesIter.next());
}
} else {
throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of "+first.getClass().getName());
}
}
/**
* A helper method to allow lists to work with subscript operators.
*
* @param self a List
* @param splice the subset of the list to set
* @param value the value to put at the given sublist
* @since 1.0
*/
public static void putAt(List self, List splice, Object value) {
if (splice.isEmpty()) {
return;
}
Object first = splice.iterator().next();
if (first instanceof Integer) {
for (Object index : splice) {
self.set((Integer) index, value);
}
} else {
throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of "+first.getClass().getName());
}
}
// 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.
*
* @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 V getAt(Map self, K 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.
*
*
* @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 Map plus(Map left, Map right) {
Map map = cloneSimilarMap(left);
map.putAll(right);
return map;
}
/**
* A helper method to allow lists to work with subscript operators
*
* @param self a Map
* @param key an Object as a key for the map
* @param value the value to put into the map
* @return the value corresponding to the given key
* @since 1.0
*/
public static V putAt(Map self, K key, V value) {
self.put(key, value);
return value;
}
/**
* Support the subscript operator for List
*
* @param coll a Collection
* @param property a String
* @return a List
* @since 1.0
*/
public static List getAt(Collection coll, String property) {
List answer = new ArrayList(coll.size());
for (Object item : coll) {
if (item == null) continue;
Object value;
try {
value = InvokerHelper.getProperty(item, property);
} catch (MissingPropertyExceptionNoStack mpe) {
String causeString = new MissingPropertyException(mpe.getProperty(), mpe.getType()).toString();
throw new MissingPropertyException("Exception evaluating property '" + property +
"' for " + coll.getClass().getName() + ", Reason: " + causeString);
}
answer.add(value);
}
return answer;
}
/**
* A convenience method for creating an immutable map.
*
* @param self a Map
* @return an immutable Map
* @see java.util.Collections#unmodifiableMap(java.util.Map)
* @since 1.0
*/
public static Map asImmutable(Map self) {
return Collections.unmodifiableMap(self);
}
/**
* A convenience method for creating an immutable sorted map.
*
* @param self a SortedMap
* @return an immutable SortedMap
* @see java.util.Collections#unmodifiableSortedMap(java.util.SortedMap)
* @since 1.0
*/
public static SortedMap asImmutable(SortedMap self) {
return Collections.unmodifiableSortedMap(self);
}
/**
* A convenience method for creating an immutable list
*
* @param self a List
* @return an immutable List
* @see java.util.Collections#unmodifiableList(java.util.List)
* @since 1.0
*/
public static List asImmutable(List self) {
return Collections.unmodifiableList(self);
}
/**
* A convenience method for creating an immutable list.
*
* @param self a Set
* @return an immutable Set
* @see java.util.Collections#unmodifiableSet(java.util.Set)
* @since 1.0
*/
public static Set asImmutable(Set self) {
return Collections.unmodifiableSet(self);
}
/**
* A convenience method for creating an immutable sorted set.
*
* @param self a SortedSet
* @return an immutable SortedSet
* @see java.util.Collections#unmodifiableSortedSet(java.util.SortedSet)
* @since 1.0
*/
public static SortedSet asImmutable(SortedSet self) {
return Collections.unmodifiableSortedSet(self);
}
/**
* A convenience method for creating an immutable Collection.
*
* @param self a Collection
* @return an immutable Collection
* @see java.util.Collections#unmodifiableCollection(java.util.Collection)
* @since 1.5.0
*/
public static Collection asImmutable(Collection self) {
return Collections.unmodifiableCollection(self);
}
/**
* A convenience method for creating a synchronized Map.
*
* @param self a Map
* @return a synchronized Map
* @see java.util.Collections#synchronizedMap(java.util.Map)
* @since 1.0
*/
public static Map asSynchronized(Map self) {
return Collections.synchronizedMap(self);
}
/**
* A convenience method for creating a synchronized SortedMap.
*
* @param self a SortedMap
* @return a synchronized SortedMap
* @see java.util.Collections#synchronizedSortedMap(java.util.SortedMap)
* @since 1.0
*/
public static SortedMap asSynchronized(SortedMap self) {
return Collections.synchronizedSortedMap(self);
}
/**
* A convenience method for creating a synchronized Collection.
*
* @param self a Collection
* @return a synchronized Collection
* @see java.util.Collections#synchronizedCollection(java.util.Collection)
* @since 1.0
*/
public static Collection asSynchronized(Collection self) {
return Collections.synchronizedCollection(self);
}
/**
* A convenience method for creating a synchronized List.
*
* @param self a List
* @return a synchronized List
* @see java.util.Collections#synchronizedList(java.util.List)
* @since 1.0
*/
public static List asSynchronized(List self) {
return Collections.synchronizedList(self);
}
/**
* A convenience method for creating a synchronized Set.
*
* @param self a Set
* @return a synchronized Set
* @see java.util.Collections#synchronizedSet(java.util.Set)
* @since 1.0
*/
public static Set asSynchronized(Set self) {
return Collections.synchronizedSet(self);
}
/**
* A convenience method for creating a synchronized SortedSet.
*
* @param self a SortedSet
* @return a synchronized SortedSet
* @see java.util.Collections#synchronizedSortedSet(java.util.SortedSet)
* @since 1.0
*/
public static SortedSet asSynchronized(SortedSet self) {
return Collections.synchronizedSortedSet(self);
}
/**
* Synonym for {@link #toSpreadMap(Map)}.
* @param self a map
* @return a newly created Spreadmap
* @since 1.0
*/
public static SpreadMap spread(Map self) {
return toSpreadMap(self);
}
/**
* Returns a new 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 List sort(Collection self) {
List answer = asList(self);
Collections.sort(answer, new NumberAwareComparator());
return answer;
}
/**
* Sorts the given map into a sorted map using
* the closure as a comparator.
*
* @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 Map sort(Map self, Closure closure) {
Map result = new LinkedHashMap();
List> entries = asList(self.entrySet());
sort(entries, closure);
for (Map.Entry entry : entries) {
result.put(entry.getKey(), entry.getValue());
}
return result;
}
/**
* Sorts the given Object array into sorted order. The array items are
* assumed to be comparable.
*
* @param self the array to be sorted
* @return the sorted array
* @since 1.5.5
*/
public static T[] sort(T[] self) {
Arrays.sort(self, new NumberAwareComparator());
return self;
}
/**
* Sorts the given iterator items into a sorted iterator. The items are
* assumed to be comparable. The original iterator will become
* exhausted of elements after completing this method call. A new iterator
* is produced that traverses the items in sorted order.
*
* @param self the Iterator to be sorted
* @return the sorted items as an Iterator
* @since 1.5.5
*/
public static Iterator sort(Iterator self) {
return sort(toList(self)).listIterator();
}
/**
* Sorts the given iterator items into a sorted iterator using
* the comparator.
*
* @param self the Iterator to be sorted
* @param comparator a Comparator used for comparing items
* @return the sorted items as an Iterator
* @since 1.5.5
*/
public static Iterator sort(Iterator self, Comparator comparator) {
return sort(toList(self), comparator).listIterator();
}
/**
* Sorts the Collection using the given comparator. The elements are
* sorted into a new list, and the existing collection is unchanged.
*
* @param self a collection to be sorted
* @param comparator a Comparator used for the comparison
* @return a newly created sorted List
* @since 1.0
*/
public static List sort(Collection self, Comparator comparator) {
List list = asList(self);
Collections.sort(list, comparator);
return list;
}
/**
* Sorts the given Object array into sorted order using the given comparator.
*
* @param self the array to be sorted
* @param comparator a Comparator used for the comparison
* @return the sorted array
* @since 1.5.5
*/
public static T[] sort(T[] self, Comparator comparator) {
Arrays.sort(self, comparator);
return self;
}
/**
* Sorts the given iterator items into a sorted iterator using
* the Closure to determine the correct ordering. The original
* iterator will be fully processed after the method call.
*
* If the closure has two parameters
* it is used like a traditional Comparator. I.e. it should compare
* its two parameters for order, returning a negative integer,
* zero, or a positive integer when the first parameter is less than,
* equal to, or greater than the second respectively. Otherwise,
* the Closure is assumed to take a single parameter and return a
* Comparable (typically an Integer) which is then used for
* further comparison.
*
* @param self the Iterator to be sorted
* @param closure a Closure used to determine the correct ordering
* @return the sorted items as an Iterator
* @since 1.5.5
*/
public static Iterator sort(Iterator self, Closure closure) {
return sort(toList(self), closure).listIterator();
}
/**
* Sorts the given Object array into a newly created array using
* the Closure to determine the correct ordering.
*
* If the closure has two parameters
* it is used like a traditional Comparator. I.e. it should compare
* its two parameters for order, returning a negative integer,
* zero, or a positive integer when the first parameter is less than,
* equal to, or greater than the second respectively. Otherwise,
* the Closure is assumed to take a single parameter and return a
* Comparable (typically an Integer) which is then used for
* further comparison.
*
* @param self the array to be sorted
* @param closure a Closure used to determine the correct ordering
* @return the sorted array
* @since 1.5.5
*/
@SuppressWarnings("unchecked")
public static T[] sort(T[] self, Closure closure) {
return (T[]) sort(toList(self), closure).toArray();
}
/**
* Sorts this Collection using
* the closure to determine the correct ordering.
*
* If the closure has two parameters
* it is used like a traditional Comparator. I.e. it should compare
* its two parameters for order, returning a negative integer,
* zero, or a positive integer when the first parameter is less than,
* equal to, or greater than the second respectively. Otherwise,
* the Closure is assumed to take a single parameter and return a
* Comparable (typically an Integer) which is then used for
* further comparison.
*
* @param self a Collection to be sorted
* @param closure a Closure used to determine the correct ordering
* @return a newly created sorted List
* @since 1.0
*/
public static List sort(Collection