/* * Copyright 2003-2011 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.FileType; import groovy.io.FileVisitResult; import groovy.io.GroovyPrintWriter; import groovy.lang.*; import groovy.util.*; import org.codehaus.groovy.classgen.Verifier; import org.codehaus.groovy.reflection.ClassInfo; import org.codehaus.groovy.reflection.MixinInMetaClass; import org.codehaus.groovy.reflection.ReflectionCache; 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.ClosureMetaClass; 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.util.*; import java.util.concurrent.BlockingQueue; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This class defines new groovy methods which appear on normal JDK * classes inside the Groovy environment. Static methods are used with the * first parameter being the destination class, * i.e. <code>public static String reverse(String self)</code> * provides a <code>reverse()</code> method for <code>String</code>. * <p> * NOTE: While this class contains many 'public' static methods, it is * primarily regarded as an internal class (its internal package name * suggests this also). We value backwards compatibility of these * methods when used within Groovy but value less backwards compatibility * at the Java method call level. I.e. future versions of Groovy may * remove or move a method call in this file but would normally * aim to keep the method available from within Groovy. * * @author <a href="mailto:[email protected]">James Strachan</a> * @author Jeremy Rayner * @author Sam Pullara * @author Rod Cope * @author Guillaume Laforge * @author John Wilson * @author Hein Meling * @author Dierk Koenig * @author Pilho Kim * @author Marc Guillemot * @author Russel Winder * @author bing ran * @author Jochen Theodorou * @author Paul King * @author Michael Baehr * @author Joachim Baumann * @author Alex Tkachman * @author Ted Naleid * @author Brad Long * @author Jim Jagielski * @author Rodolfo Velasco * @author jeremi Joslin * @author Hamlet D'Arcy * @author Cedric Champeau * @author Tim Yates * @author Dinko Srkoc */ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { private static final Logger LOG = Logger.getLogger(DefaultGroovyMethods.class.getName()); private static final Integer ONE = 1; private static final BigInteger BI_INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE); private static final BigInteger BI_INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE); private static final BigInteger BI_LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE); private static final BigInteger BI_LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE); 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' method, like so: <code>def same = this.is(that)</code> * * @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 <T> T identity(Object self, Closure<T> closure) { return DefaultGroovyMethods.with(self, closure); } /** * Allows the closure to be called for the object reference self. <br/><br/> * Any method invoked inside the closure will first be invoked on the * self reference. For instance, the following method calls to the append() * method are invoked on the StringBuilder instance: * <pre> * def b = new StringBuilder().with { * append('foo') * append('bar') * return it * } * assert b.toString() == 'foobar' * </pre> * This is commonly used to simplify object creation, such as this example: * <pre> * def p = new Person().with { * firstName = 'John' * lastName = 'Doe' * return it * } * </pre> * * @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 <T> T with(Object self, Closure<T> closure) { @SuppressWarnings("unchecked") final Closure<T> clonedClosure = (Closure<T>) 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. * <code>bean[somePropertyNameExpression]</code>. The normal property notation * of groovy is neater and more concise but only works with compile-time known * property names. * * @param self 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. * <code>bean[somePropertyNameExpression] = foo</code>. 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 groovy.lang.MetaProperty} objects for 'self' and wraps it * in a list of {@link groovy.lang.PropertyValue} objects that additionally provide * the value for each property of 'self'. * * @param self the receiver object * @return list of {@link groovy.lang.PropertyValue} objects * @see groovy.util.Expando#getMetaPropertyValues() * @since 1.0 */ public static List<PropertyValue> getMetaPropertyValues(Object self) { MetaClass metaClass = InvokerHelper.getMetaClass(self); List<MetaProperty> mps = metaClass.getProperties(); List<PropertyValue> props = new ArrayList<PropertyValue>(mps.size()); for (MetaProperty mp : mps) { props.add(new PropertyValue(self, mp)); } return props; } /** * Convenience method that calls {@link #getMetaPropertyValues(java.lang.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<PropertyValue> metaProps = getMetaPropertyValues(self); Map<String, Object> props = new LinkedHashMap<String, Object>(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 <T> T use(Object self, Class categoryClass, Closure<T> 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<Class> 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<Class> 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 <T> T use(Object self, List<Class> categoryClassList, Closure<T> 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. * <code>use(CategoryClass1, CategoryClass2) { ... }</code> * 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<Class> list = new ArrayList<Class>(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.0 */ 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.0 */ 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. * <p/> * For examples, <pre> * printf ( "Hello, %s!\n" , [ "world" ] as String[] ) * printf ( "Hello, %s!\n" , [ "Groovy" ]) * printf ( "%d + %d = %d\n" , [ 1 , 2 , 1+2 ] as Integer[] ) * printf ( "%d + %d = %d\n" , [ 3 , 3 , 3+3 ]) * <p/> * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as Integer[] ) } * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as int[] ) } * ( 0x41..0x45 ).each { printf ( "-- %c\n" , [ it ] as char[] ) } * ( 07..011 ).each { printf ( "-- %d\n" , [ it ] as byte[] ) } * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as short[] ) } * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as long[] ) } * ( 7..11 ).each { printf ( "-- %5.2f\n" , [ it ] as float[] ) } * ( 7..11 ).each { printf ( "-- %5.2g\n" , [ it ] as double[] ) } * </pre> * <p/> * * @param 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 <code>arg</code> 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 if (self instanceof Writer) printf((Writer) 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)); } private static void printf(Writer self, String format, Object arg) { try { self.write(sprintf(self, format, arg)); } catch (IOException e) { printf(System.out, format, arg); } } /** * Returns a formatted string using the specified format string and * arguments. * <p/> * * @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 <code>arg</code> 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: * <pre> switch( a ) { * case b: //some code * }</pre> * "some code" is called when <code>b.isCase( a )</code> returns * <code>true</code>. * * @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: * <pre>switch( str ) { * case 'one' : * // etc... * }</pre> * Note that this returns <code>true</code> for the case where both the * 'switch' and 'case' operand is <code>null</code>. * * @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 CharSequence, which simply calls the equivalent 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.8.2 */ public static boolean isCase(CharSequence caseValue, Object switchValue) { return isCase(caseValue.toString(), switchValue); } /** * 'Case' implementation for a GString, which simply calls the equivalent 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: * <pre>switch( obj ) { * case List : * // obj is a list * break; * case Set : * // etc * }</pre> * * @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: * <pre class="groovyTestCase">switch( 3 ) { * case [1,3,5]: * assert true * break * default: * assert false * }</pre> * * @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(java.lang.Object) * @since 1.0 */ public static boolean isCase(Collection caseValue, Object switchValue) { return caseValue.contains(switchValue); } /** * 'Case' implementation for maps which tests the groovy truth * value obtained using the 'switch' operand as key. * For example: * <pre class="groovyTestCase">switch( 'foo' ) { * case [foo:true, bar:false]: * assert true * break * default: * assert false * }</pre> * * @param caseValue the case value * @param switchValue the switch value * @return the groovy truth value from caseValue corresponding to the switchValue key * @since 1.7.6 */ public static boolean isCase(Map caseValue, Object switchValue) { return DefaultTypeTransformation.castToBoolean(caseValue.get(switchValue)); } /** * 'Case' implementation for the {@link java.util.regex.Pattern} class, which allows * testing a String against a number of regular expressions. * For example: * <pre>switch( str ) { * case ~/one/ : * // the regex 'one' matches the value of str * } * </pre> * Note that this returns true for the case where both the pattern and * the 'switch' values are <code>null</code>. * * @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 * <code>compareTo()</code> 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 <T> Iterator<T> unique(Iterator<T> self) { return toList(unique(toList(self))).listIterator(); } /** * Modifies this collection to remove all duplicated items, using the * default comparator. * <pre class="groovyTestCase">assert [1,3] == [1,3,3].unique()</pre> * * @param self a collection * @return the now modified collection * @see #unique(Collection, boolean) * @since 1.0 */ public static <T> Collection<T> unique(Collection<T> self) { return unique(self, true); } /** * Remove all duplicates from a given Collection using the default comparator. * If mutate is true, it works by modifying the original object (and also returning it). * If mutate is false, a new collection is returned leaving the original unchanged. * <pre class="groovyTestCase"> * assert [1,3] == [1,3,3].unique() * </pre> * <pre class="groovyTestCase"> * def orig = [1, 3, 2, 3] * def uniq = orig.unique(false) * assert orig == [1, 3, 2, 3] * assert uniq == [1, 3, 2] * </pre> * * @param self a collection * @param mutate false will cause a new list containing unique items from the collection to be created, true will mutate collections in place * @return the now modified collection * @since 1.8.1 */ public static <T> Collection<T> unique(Collection<T> self, boolean mutate) { List<T> answer = new ArrayList<T>(); for (T t : self) { boolean duplicated = false; for (T t2 : answer) { if (coercedEquals(t, t2)) { duplicated = true; break; } } if (!duplicated) answer.add(t); } if (mutate) { self.clear(); self.addAll(answer); } return mutate ? self : answer ; } /** * 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<Comparable> numberAwareComparator = new NumberAwareComparator<Comparable>(); 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. * </p> * 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 java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.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 <T> Iterator<T> unique(Iterator<T> 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. * </p> * 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 java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.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). * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].unique { it % 2 }</pre> * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].unique { a, b -> a <=> b }</pre> * * @param self a Collection * @param closure a 1 or 2 arg Closure used to determine unique items * @return self without any duplicates * @see #unique(Collection, boolean, Closure) * @since 1.0 */ public static <T> Collection<T> unique(Collection<T> self, Closure closure) { return unique(self, true, closure); } /** * A convenience method for making a collection unique using a Closure to determine duplicate (equal) items. * If mutate is true, it works on the receiver object and returns it. If mutate is false, a new collection is returned. * </p> * 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 java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.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). * <pre class="groovyTestCase"> * def orig = [1, 3, 4, 5] * def uniq = orig.unique(false) { it % 2 } * assert orig == [1, 3, 4, 5] * assert uniq == [1, 4] * </pre> * <pre class="groovyTestCase"> * def orig = [2, 3, 3, 4] * def uniq = orig.unique(false) { a, b -> a <=> b } * assert orig == [2, 3, 3, 4] * assert uniq == [2, 3, 4] * </pre> * * @param self a Collection * @param mutate false will always cause a new list to be created, true will mutate lists in place * @param closure a 1 or 2 arg Closure used to determine unique items * @return self without any duplicates * @since 1.8.1 */ public static <T> Collection<T> unique(Collection<T> self, boolean mutate, Closure closure) { // use a comparator of one item or two int params = closure.getMaximumNumberOfParameters(); if (params == 1) { OrderBy<T> by = new OrderBy<T>(closure); by.setEqualityCheck(true); self = unique(self, mutate, by); } else { self = unique(self, mutate, new ClosureComparator<T>(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 <T> Iterator<T> unique(Iterator<T> self, Comparator<T> comparator) { return toList(unique(toList(self), comparator)).listIterator(); } /** * Remove all duplicates from a given Collection. * Works on the original object (and also 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. * <p/> * <code><pre class="groovyTestCase"> * class Person { * def fname, lname * String toString() { * return fname + " " + lname * } * } * * class PersonComparator implements Comparator { * 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) * } * * 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] ) * </pre></code> * * @param self a Collection * @param comparator a Comparator * @return self the now modified collection without duplicates * @see #unique(java.util.Collection, boolean, java.util.Comparator) * @since 1.0 */ public static <T> Collection<T> unique(Collection<T> self, Comparator<T> comparator) { return unique(self, true, comparator) ; } /** * Remove all duplicates from a given Collection. * If mutate is true, it works on the original object (and also returns it). If mutate is false, a new collection is returned. * 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. * <p/> * <code><pre class="groovyTestCase"> * class Person { * def fname, lname * String toString() { * return fname + " " + lname * } * } * * class PersonComparator implements Comparator { * 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) * } * * 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(false, new PersonComparator()) * assert( list2 != list && list2 == [a, b, c] ) * </pre></code> * * * @param self a Collection * @param mutate false will always cause a new collection to be created, true will mutate collections in place * @param comparator a Comparator * @return self the collection without duplicates * @since 1.8.1 */ public static <T> Collection<T> unique(Collection<T> self, boolean mutate, Comparator<T> comparator) { List<T> answer = new ArrayList<T>(); 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); } if (mutate) { self.clear(); self.addAll(answer); } return mutate ? self : answer; } /** * 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> 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 <T> T eachWithIndex(T self, Closure closure) { final Object[] args = new Object[2]; int counter = 0; for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { args[0] = iter.next(); args[1] = counter++; closure.call(args); } return self; } private static <T> Iterator<T> each(Iterator<T> iter, Closure closure) { final Object[] args = new Object[1]; final Class[] types = new Class[1]; MetaClass mc = closure.getMetaClass(); MetaMethod cachedMethod = null; Class cachedType = null; while (iter.hasNext()) { Object arg = iter.next(); args[0] = arg; Class type = null; if (arg!=null) type = arg.getClass(); if (cachedMethod==null || type!=cachedType) { types[0] = type; cachedMethod = mc.pickMethod("call", types); cachedType = type; } if (cachedMethod!=null) { cachedMethod.doMethodInvoke(closure, args); } else { mc.invokeMethod(closure, "call", args); } } 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. * <pre class="groovyTestCase">def result = "" * [a:1, b:3].each { key, value -> result += "$key$value" } * assert result == "a1b3"</pre> * <pre class="groovyTestCase">def result = "" * [a:1, b:3].each { entry -> result += entry } * assert result == "a=1b=3"</pre> * * In general, the order in which the map contents are processed * cannot be guaranteed. In practise, specialized forms of Map, * e.g. a TreeMap will have its contents processed according to * the natural ordering of the map. * * @param self the map over which we iterate * @param closure the 1 or 2 arg closure applied on each entry of the map * @return returns the self parameter * @since 1.5.0 */ public static <K, V> Map<K, V> each(Map<K, V> self, Closure closure) { for (Map.Entry entry : self.entrySet()) { callClosureForMapEntry(closure, entry); } return self; } /** * Allows a Map to be iterated through in reverse order using a closure. * * In general, the order in which the map contents are processed * cannot be guaranteed. In practise, specialized forms of Map, * e.g. a TreeMap will have its contents processed according to the * reverse of the natural ordering of the map. * * @param self the map over which we iterate * @param closure the 1 or 2 arg closure applied on each entry of the map * @return returns the self parameter * @see #each(Map, Closure) * @since 1.7.2 */ public static <K, V> Map<K, V> reverseEach(Map<K, V> self, Closure closure) { final Iterator<Map.Entry<K, V>> entries = reverse(self.entrySet().iterator()); while (entries.hasNext()) { callClosureForMapEntry(closure, entries.next()); } 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. * <pre class="groovyTestCase">def result = "" * [a:1, b:3].eachWithIndex { key, value, index -> result += "$index($key$value)" } * assert result == "0(a1)1(b3)"</pre> * <pre class="groovyTestCase">def result = "" * [a:1, b:3].eachWithIndex { entry, index -> result += "$index($entry)" } * assert result == "0(a=1)1(b=3)"</pre> * * @param self the map over which we iterate * @param closure a 2 or 3 arg Closure to operate on each item * @return the self Object * @since 1.5.0 */ public static <K, V> Map<K, V> eachWithIndex(Map<K, V> 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. * <pre class="groovyTestCase">def result = [] * [1,2,3].reverseEach { result << it } * assert result == [3,2,1]</pre> * * @param self a List * @param closure a closure to which each item is passed. * @return the original list * @since 1.5.0 */ public static <T> List<T> reverseEach(List<T> self, Closure closure) { each(new ReverseListIterator<T>(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> T[] reverseEach(T[] self, Closure closure) { each(new ReverseListIterator<T>(Arrays.asList(self)), closure); return self; } /** * Used to determine if the given predicate closure is valid (i.e.&nsbp;returns * <code>true</code> for all items in this data structure). * A simple example for a list: * <pre>def list = [3,4,5] * def greaterThanTwo = list.every { it > 2 } * </pre> * * @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. 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. * <pre class="groovyTestCase">def map = [a:1, b:2.0, c:2L] * assert !map.every { key, value -> value instanceof Integer } * assert map.every { entry -> entry.value instanceof Number }</pre> * * @param self the map over which we iterate * @param closure the 1 or 2 arg Closure predicate used for matching * @return true if every entry of the map matches the closure predicate * @since 1.5.0 */ public static <K, V> boolean every(Map<K, V> 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 <code>true</code> according to the Groovy Truth. * Equivalent to <code>self.every({element -> element})</code> * * @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. 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. * <pre class="groovyTestCase"> * assert [2:3, 4:5, 5:10].any { key, value -> key * 2 == value } * assert ![2:3, 4:5, 5:10].any { entry -> entry.key == entry.value * 2 } * </pre> * * @param self the map over which we iterate * @param closure the 1 or 2 arg closure predicate used for matching * @return true if any entry in the map matches the closure predicate * @since 1.5.0 */ public static <K, V> boolean any(Map<K, V> self, Closure<?> closure) { for (Map.Entry<K, V> 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 the collection of items which this Object represents and returns each item that matches * the given filter - calling the <code>{@link #isCase(java.lang.Object, java.lang.Object)}</code> * method used by switch statements. This method can be used with different * kinds of filters like regular expressions, classes, ranges etc. * Example: * <pre class="groovyTestCase"> * def list = ['a', 'b', 'aa', 'bc', 3, 4.5] * assert list.grep( ~/a+/ ) == ['a', 'aa'] * assert list.grep( ~/../ ) == ['aa', 'bc'] * assert list.grep( Number ) == [ 3, 4.5 ] * assert list.grep{ it.toString().size() == 1 } == [ 'a', 'b', 3 ] * </pre> * * @param self the object over which we iterate * @param filter the filter to perform on the object (using the {@link #isCase(java.lang.Object, java.lang.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; } /** * Iterates over the collection of items which this Object represents and returns each item that matches * using the IDENTITY Closure as a filter - effectively returning all elements which satisfy Groovy truth. * <p/> * Example: * <pre class="groovyTestCase"> * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null] * assert items.grep() == [1, 2, true, 'foo', [4, 5]] * </pre> * * @param self the object over which we iterate * @return a collection of objects which match the filter * @since 1.8.1 * @see Closure#IDENTITY */ public static Collection grep(Object self) { return grep(self, Closure.IDENTITY); } /** * Counts the number of occurrences of the given value from the * items within this Iterator. * Comparison is done using Groovy's == operator (using * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * 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 (int) answer; return answer; } /** * Counts the number of occurrences which satisfy the given closure from the * items within this Iterator. * The iterator will become exhausted of elements after determining the count value. * <p> * Example usage: * <pre class="groovyTestCase">assert [2,4,2,1,3,5,2,4,3].toSet().iterator().count{ it % 2 == 0 } == 2</pre> * * @param self the Iterator from which we count the number of matching occurrences * @param closure a closure condition * @return the number of occurrences * @since 1.8.0 */ public static Number count(Iterator self, Closure closure) { long answer = 0; while (self.hasNext()) { if (DefaultTypeTransformation.castToBoolean(closure.call(self.next()))) { ++answer; } } // for b/c with Java return an int if we can if (answer <= Integer.MAX_VALUE) return (int) answer; return answer; } /** * Counts the number of occurrences of the given value inside this collection. * Comparison is done using Groovy's == operator (using * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * <p> * Example usage: * <pre class="groovyTestCase">assert [2,4,2,1,3,5,2,4,3].count(4) == 2</pre> * * @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 which satisfy the given closure from inside this collection. * <p> * Example usage: * <pre class="groovyTestCase">assert [2,4,2,1,3,5,2,4,3].count{ it % 2 == 0 } == 5</pre> * * @param self the collection within which we count the number of occurrences * @param closure a closure condition * @return the number of occurrences * @since 1.8.0 */ public static Number count(Collection self, Closure closure) { return count(self.iterator(), closure); } /** * Counts the number of occurrences which satisfy the given closure from inside this map. * If the closure takes one parameter then it will be passed the Map.Entry. * Otherwise, the closure should take two parameters and will be passed the key and value. * <p> * Example usage: * <pre class="groovyTestCase">assert [a:1, b:1, c:2, d:2].count{ k,v -> k == 'a' || v == 2 } == 3</pre> * * @param self the map within which we count the number of occurrences * @param closure a 1 or 2 arg Closure condition applying on the entries * @return the number of occurrences * @since 1.8.0 */ public static Number count(Map self, Closure<?> closure) { long answer = 0; for (Object entry : self.entrySet()) { if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, (Map.Entry) entry))) { ++answer; } } // for b/c with Java return an int if we can if (answer <= Integer.MAX_VALUE) return (int) answer; return answer; } /** * Counts the number of occurrences of the given value inside this array. * Comparison is done using Groovy's == operator (using * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * * @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 which satisfy the given closure from inside this array. * * @param self the array within which we count the number of occurrences * @param closure a closure condition * @return the number of occurrences * @since 1.8.0 */ public static Number count(Object[] self, Closure closure) { return count(Arrays.asList(self), closure); } /** * Counts the number of occurrences of the given value inside this array. * Comparison is done using Groovy's == operator (using * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * * @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 * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * * @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 * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * * @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 * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * * @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 * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * * @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 * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * * @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 * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * * @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 * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ). * * @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. Always returns a new List * even if the Collection is already a List. * <p> * Example usage: * <pre class="groovyTestCase">def x = [1,2,3] as HashSet * assert x.class == HashSet * assert x.toList() instanceof List</pre> * * @param self a collection * @return a List * @since 1.0 */ public static <T> List<T> toList(Collection<T> self) { List<T> answer = new ArrayList<T>(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 <T> List<T> toList(Iterator<T> self) { List<T> answer = new ArrayList<T>(); 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 <T> List<T> toList(Enumeration<T> self) { List<T> answer = new ArrayList<T>(); 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: * <pre class="groovyTestCase">def list = [1, 'a', 1.23, true ] * def types = list.collect { it.class } * assert types == [Integer, String, BigDecimal, Boolean]</pre> * * @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 <T> List<T> collect(Object self, Closure<T> closure) { return (List<T>) collect(self, new ArrayList<T>(), 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 <T> Collection<T> collect(Object self, Collection<T> collection, Closure<? extends T> 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. * <pre class="groovyTestCase">assert [2,4,6] == [1,2,3].collect { it * 2 }</pre> * * @param self a collection * @param closure the closure used for mapping * @return a List of the transformed values * @since 1.0 */ public static <T> List<T> collect(Collection<?> self, Closure<T> closure) { return (List<T>) collect(self, new ArrayList<T>(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. * <pre class="groovyTestCase">assert [1,2,3] as HashSet == [2,4,5,6].collect(new HashSet()) { (int)(it / 2) }</pre> * * @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 <T> Collection<T> collect(Collection<?> self, Collection<T> collection, Closure<? extends T> closure) { for (Iterator iter = self.iterator(); iter.hasNext();) { collection.add(closure.call(iter.next())); if (closure.getDirective() == Closure.DONE) { break; } } return collection; } /** * Alias for collectNested * * @deprecated Use collectNested instead * @see #collectNested(Collection, Closure) * @since 1.5.2 */ public static List collectAll(Collection self, Closure closure) { return collectNested(self, 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 * list of transformed values. * <pre class="groovyTestCase">assert [2,[4,6],[8],[]] == [1,[2,3],[4],[]].collectNested { it * 2 }</pre> * * @param self a collection * @param closure the closure used to transform each element of the collection * @return the resultant collection * @since 1.8.1 */ public static List collectNested(Collection self, Closure closure) { return (List) collectNested(self, new ArrayList(self.size()), closure); } /** * Alias for collectNested * * @deprecated Use collectNested instead * @see #collectNested(Collection, Collection, Closure) * @since 1.5.2 */ public static Collection collectAll(Collection self, Collection collection, Closure closure) { return collectNested(self, collection, 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. * <pre class="groovyTestCase">def x = [1,[2,3],[4],[]].collectNested(new Vector()) { it * 2 } * assert x == [2,[4,6],[8],[]] * assert x instanceof Vector</pre> * * @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.8.1 */ public static Collection collectNested(Collection self, Collection collection, Closure closure) { for (Object item : self) { if (item instanceof Collection) { Collection c = (Collection) item; collection.add(collectNested(c, createSimilarCollection(collection, c.size()), closure)); } else { collection.add(closure.call(item)); } if (closure.getDirective() == Closure.DONE) { break; } } return collection; } /** * Projects each item from a source collection to a collection and concatenates (flattens) the resulting collections into a single one. * * <pre class="groovyTestCase"> * def nums = 1..10 * def squaresAndCubesOfEvens = nums.collectMany{ it % 2 ? [] : [it**2, it**3] } * assert squaresAndCubesOfEvens == [4, 8, 16, 64, 36, 216, 64, 512, 100, 1000] * * def animals = ['CAT', 'DOG', 'ELEPHANT'] as Set * def smallAnimals = animals.collectMany{ it.size() > 3 ? [] : [it.toLowerCase()] } * assert smallAnimals == ['cat', 'dog'] as Set * </pre> * * @param self a collection * @param closure a projecting Closure returning a collection of items * @return the projected collections concatenated (flattened) together * @see #sum(java.util.Collection, groovy.lang.Closure) */ public static <T> Collection<T> collectMany(Collection self, Closure<Collection<T>> closure) { Collection<T> result = createSimilarCollection(self); for (Object next : self) { result.addAll(closure.call(next)); } return result; } /** * Projects each item from a source array to a collection and concatenates (flattens) the resulting collections into a single one. * * <pre class="groovyTestCase"> * def nums = [1, 2, 3, 4, 5, 6] as Object[] * def squaresAndCubesOfEvens = nums.collectMany{ it % 2 ? [] : [it**2, it**3] } * assert squaresAndCubesOfEvens == [4, 8, 16, 64, 36, 216] * </pre> * * @param self an object array * @param closure a projecting Closure returning a collection of items * @return the projected collections concatenated (flattened) together * @see #sum(Object[], groovy.lang.Closure) */ public static <T> Collection<T> collectMany(Object[] self, Closure<Collection<T>> closure) { return collectMany(toList(self), closure); } /** * Projects each item from a source iterator to a collection and concatenates (flattens) the resulting collections into a single one. * * <pre class="groovyTestCase"> * def numsIter = [1, 2, 3, 4, 5, 6].iterator() * def squaresAndCubesOfEvens = numsIter.collectMany{ it % 2 ? [] : [it**2, it**3] } * assert squaresAndCubesOfEvens == [4, 8, 16, 64, 36, 216] * </pre> * * @param self an iterator * @param closure a projecting Closure returning a collection of items * @return the projected collections concatenated (flattened) together * @see #sum(Iterator, groovy.lang.Closure) */ public static <T> Collection<T> collectMany(Iterator<Object> self, Closure<Collection<T>> closure) { return collectMany(toList(self), closure); } /** * Iterates through this Map transforming each entry into a new value using the closure * as a transformer, returning a list of transformed values. * <pre class="groovyTestCase">assert [a:1, b:2].collect( [] as HashSet ) { key, value -> key*value } == ["a", "bb"] as Set * assert [3:20, 2:30].collect( [] as HashSet ) { entry -> entry.key * entry.value } == [60] as Set</pre> * * @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 <T> Collection<T> collect(Map<?, ?> self, Collection<T> collection, Closure<? extends T> closure) { for (Map.Entry<?, ?> entry : self.entrySet()) { collection.add(callClosureForMapEntry(closure, entry)); } 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. * <pre class="groovyTestCase">assert [a:1, b:2].collect { key, value -> key*value } == ["a", "bb"] * assert [3:20, 2:30].collect { entry -> entry.key * entry.value } == [60, 60]</pre> * * @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 <T> List<T> collect(Map self, Closure<T> closure) { return (List<T>) collect(self, new ArrayList<T>(self.size()), closure); } /** * Iterates through this Map transforming each entry using the closure * as a transformer, returning a map of the transformed entries. * <pre class="groovyTestCase"> * assert [a:1, b:2].collectEntries( [:] ) { k, v -> [v, k] } == [1:'a', 2:'b'] * assert [a:1, b:2].collectEntries( [30:'C'] ) { key, value -> * [(value*10): key.toUpperCase()] } == [10:'A', 20:'B', 30:'C'] * </pre> * * @param self a Map * @param result the Map into which the mapped entries are put * @param closure the closure used for mapping, which can take one (Map.Entry) or two (key, value) parameters and * should return a Map.Entry, a Map or a two-element list containing the resulting key and value * @return a Map of the transformed entries * @see #collect(Map, Collection, Closure) * @since 1.7.9 */ public static <K, V> Map<K, V> collectEntries(Map<?, ?> self, Map<K, V> result, Closure<?> closure) { for (Map.Entry<?, ?> entry : self.entrySet()) { addEntry(result, callClosureForMapEntry(closure, entry)); } return result; } /** * Iterates through this Map transforming each entry using the closure * as a transformer, returning a map of the transformed entries. * <pre class="groovyTestCase"> * assert [a:1, b:2].collectEntries { key, value -> [value, key] } == [1:'a', 2:'b'] * assert [a:1, b:2].collectEntries { key, value -> * [(value*10): key.toUpperCase()] } == [10:'A', 20:'B'] * </pre> * * @param self a Map * @param closure the closure used for mapping, which can take one (Map.Entry) or two (key, value) parameters and * should return a Map.Entry, a Map or a two-element list containing the resulting key and value * @return a Map of the transformed entries * @see #collect(Map, Collection, Closure) * @since 1.7.9 */ public static Map<?, ?> collectEntries(Map<?, ?> self, Closure<?> closure) { return collectEntries(self, createSimilarMap(self), closure); } /** * Iterates through this Collection transforming each item using the closure * as a transformer into a map entry, returning a map of the transformed entries. * <pre class="groovyTestCase"> * def letters = "abc" * // collect letters with index * assert (0..2).collectEntries( [:] ) { index -> [index, letters[index]] } == [0:'a', 1:'b', 2:'c'] * assert (0..2).collectEntries( [4:'d'] ) { index -> * [(index+1): letters[index]] } == [1:'a', 2:'b', 3:'c', 4:'d'] * </pre> * * @param self a Collection * @param result the Map into which the collected entries are put * @param closure the closure used for mapping, which has an item from self as the parameter and * should return a Map.Entry, a Map or a two-element list containing the resulting key and value * @return a Map of the transformed entries * @see #collect(Map, Collection, Closure) * @since 1.7.9 */ public static <K, V> Map<K, V> collectEntries(Collection<?> self, Map<K, V> result, Closure<?> closure) { for (Object next : self) { addEntry(result, closure.call(next)); } return result; } /** * Iterates through this array transforming each item using the closure * as a transformer into a map entry, returning a map of the transformed entries. * <pre class="groovyTestCase"> * def letters = "abc" * def nums = [0, 1, 2] as Integer[] * // collect letters with index * assert nums.collectEntries( [:] ) { index -> [index, letters[index]] } == [0:'a', 1:'b', 2:'c'] * assert nums.collectEntries( [4:'d'] ) { index -> * [(index+1): letters[index]] } == [1:'a', 2:'b', 3:'c', 4:'d'] * </pre> * * @param self a Collection * @param result the Map into which the collected entries are put * @param closure the closure used for mapping, which has an item from self as the parameter and * should return a Map.Entry, a Map or a two-element list containing the resulting key and value * @return a Map of the transformed entries * @see #collect(Map, Collection, Closure) * @since 1.7.9 */ public static <K, V> Map<K, V> collectEntries(Object[] self, Map<K, V> result, Closure<?> closure) { return collectEntries(toList(self), result, closure); } /** * Iterates through this Collection transforming each item using the closure * as a transformer into a map entry, returning a map of the transformed entries. * <pre class="groovyTestCase"> * def letters = "abc" * // collect letters with index using list style * assert (0..2).collectEntries { index -> [index, letters[index]] } == [0:'a', 1:'b', 2:'c'] * // collect letters with index using map style * assert (0..2).collectEntries { index -> [(index): letters[index]] } == [0:'a', 1:'b', 2:'c'] * </pre> * * @param self a Collection * @param closure the closure used for mapping, which has an item from self as the parameter and * should return a Map.Entry, a Map or a two-element list containing the resulting key and value * @return a Map of the transformed entries * @see #collectEntries(Collection, Map, Closure) * @since 1.7.9 */ public static <K, V> Map<K, V> collectEntries(Collection<?> self, Closure<?> closure) { return collectEntries(self, new LinkedHashMap<K, V>(), closure); } /** * Iterates through this array transforming each item using the closure * as a transformer into a map entry, returning a map of the transformed entries. * <pre class="groovyTestCase"> * def letters = "abc" * def nums = [0, 1, 2] as Integer[] * // collect letters with index using list style * assert nums.collectEntries { index -> [index, letters[index]] } == [0:'a', 1:'b', 2:'c'] * // collect letters with index using map style * assert nums.collectEntries { index -> [(index): letters[index]] } == [0:'a', 1:'b', 2:'c'] * </pre> * * @param self a Collection * @param closure the closure used for mapping, which has an item from self as the parameter and * should return a Map.Entry, a Map or a two-element list containing the resulting key and value * @return a Map of the transformed entries * @see #collectEntries(Collection, Map, Closure) * @since 1.7.9 */ public static <K, V> Map<K, V> collectEntries(Object[] self, Closure<?> closure) { return collectEntries(toList(self), new LinkedHashMap<K, V>(), closure); } private static <K, V> void addEntry(Map<K, V> result, Object newEntry) { if (newEntry instanceof Map) { leftShift(result, (Map)newEntry); } else if (newEntry instanceof List && ((List)newEntry).size() == 2) { List list = (List) newEntry; leftShift(result, new MapEntry(list.get(0), list.get(1))); } else { // TODO: enforce stricter behavior? // given Map.Entry is an interface, we get a proxy which gives us lots // of flexibility but sometimes the error messages might be unexpected leftShift(result, asType(newEntry, Map.Entry.class)); } } /** * 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 or null if none was 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 item matching the IDENTITY Closure (i.e. matching Groovy truth). * <p/> * Example: * <pre class="groovyTestCase"> * def items = [null, 0, 0.0, false, '', [], 42, 43] * assert items.find() == 42 * </pre> * * @param self an Object with an Iterator returning its values * @return the first Object found or null if none was found * @since 1.8.1 * @see Closure#IDENTITY */ public static Object find(Object self) { return find(self, Closure.IDENTITY); } /** * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns the defaultResult. * * @param self an Object with an iterator returning its values * @param defaultResult an Object that should be returned if all closure results are null * @param closure a closure that returns a non-null value when processing should stop * @return the first non-null result of the closure, otherwise the default value * @since 1.7.5 */ public static Object findResult(Object self, Object defaultResult, Closure closure) { Object result = findResult(self, closure); if (result == null) return defaultResult; return result; } /** * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns null. * * @param self an Object with an iterator returning its values * @param closure a closure that returns a non-null value when processing should stop * @return the first non-null result of the closure * @since 1.7.5 */ public static Object findResult(Object self, Closure closure) { for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { Object value = iter.next(); Object result = closure.call(value); if (result != null) { return result; } } return null; } /** * Finds the first value matching the closure condition. Example: * <pre class="groovyTestCase">def list = [1,2,3] * assert 2 == list.find { it > 1 } * </pre> * * @param self a Collection * @param closure a closure condition * @return the first Object found * @since 1.0 */ public static <T> T find(Collection<T> self, Closure closure) { for (T value : self) { if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { return value; } } return null; } /** * Finds the first item matching the IDENTITY Closure (i.e. matching Groovy truth). * <p/> * Example: * <pre class="groovyTestCase"> * def items = [null, 0, 0.0, false, '', [], 42, 43] * assert items.find() == 42 * </pre> * * @param self a Collection * @return the first Object found or null if none was found * @since 1.8.1 * @see Closure#IDENTITY */ public static <T> T find(Collection<T> self) { return find(self, Closure.IDENTITY); } /** * Iterates through the collection calling the given closure for each item but stopping once the first non-null * result is found and returning that result. If all are null, the defaultResult is returned. * <p/> * Examples: * <pre class="groovyTestCase"> * def list = [1,2,3] * assert "Found 2" == list.findResult("default") { it > 1 ? "Found $it" : null } * assert "default" == list.findResult("default") { it > 3 ? "Found $it" : null } * </pre> * * @param self a Collection * @param defaultResult an Object that should be returned if all closure results are null * @param closure a closure that returns a non-null value when processing should stop and a value should be returned * @return the first non-null result from calling the closure, or the defaultValue * @since 1.7.5 */ public static <T, U extends T, V extends T> T findResult(Collection<?> self, U defaultResult, Closure<V> closure) { T result = findResult(self, closure); if (result == null) return defaultResult; return result; } /** * Iterates through the collection calling the given closure for each item but stopping once the first non-null * result is found and returning that result. If all results are null, null is returned. * <p/> * Example: * <pre class="groovyTestCase"> * def list = [1,2,3] * assert "Found 2" == list.findResult { it > 1 ? "Found $it" : null } * </pre> * * @param self a Collection * @param closure a closure that returns a non-null value when processing should stop and a value should be returned * @return the first non-null result from calling the closure, or null * @since 1.7.5 */ public static <T> T findResult(Collection<?> self, Closure<T> closure) { for (Object value : self) { T result = closure.call(value); if (result != null) { return result; } } return null; } /** * Iterates through the collection transforming items using the supplied closure * and collecting any non-null results. * <p/> * Example: * <pre class="groovyTestCase"> * def list = [1,2,3] * def result = list.findResults { it > 1 ? "Found $it" : null } * assert result == ["Found 2", "Found 3"] * </pre> * * @param self a Collection * @param filteringTransform a Closure that should return either a non-null transformed value or null for items which should be discarded * @return the list of non-null transformed values * @since 1.8.1 */ public static <T> Collection<T> findResults(Collection<?> self, Closure<T> filteringTransform) { List<T> result = new ArrayList<T>(); for (Object value : self) { T transformed = filteringTransform.call(value); if (transformed != null) { result.add(transformed); } } return result; } /** * Iterates through the map transforming items using the supplied closure * and collecting any non-null results. * 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. * <p/> * Example: * <pre class="groovyTestCase"> * def map = [a:1, b:2, hi:2, cat:3, dog:2] * def result = map.findResults { k, v -> k.size() == v ? "Found $k:$v" : null } * assert result == ["Found a:1", "Found hi:2", "Found cat:3"] * </pre> * * @param self a Map * @param filteringTransform a 1 or 2 arg Closure that should return either a non-null transformed value or null for items which should be discarded * @return the list of non-null transformed values * @since 1.8.1 */ public static <T> Collection<T> findResults(Map<?, ?> self, Closure<T> filteringTransform) { List<T> result = new ArrayList<T>(); for (Map.Entry<?, ?> entry : self.entrySet()) { T transformed = callClosureForMapEntry(filteringTransform, entry); if (transformed != null) { result.add(transformed); } } return result; } /** * 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. * <pre class="groovyTestCase">assert [a:1, b:3].find { it.value == 3 }.key == "b"</pre> * * @param self a Map * @param closure a 1 or 2 arg Closure condition * @return the first Object found * @since 1.0 */ public static <K, V> Map.Entry<K, V> find(Map<K, V> self, Closure<?> closure) { for (Map.Entry<K, V> entry : self.entrySet()) { if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) { return entry; } } return null; } /** * Returns the first non-null closure result found by passing each map entry to the closure, otherwise the defaultResult is returned. * 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. * <pre class="groovyTestCase"> * assert "Found b:3" == [a:1, b:3].findResult("default") { if (it.value == 3) return "Found ${it.key}:${it.value}" } * assert "default" == [a:1, b:3].findResult("default") { if (it.value == 9) return "Found ${it.key}:${it.value}" } * assert "Found a:1" == [a:1, b:3].findResult("default") { k, v -> if (k.size() + v == 2) return "Found $k:$v" } * </pre> * * @param self a Map * @param defaultResult an Object that should be returned if all closure results are null * @param closure a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned * @return the first non-null result collected by calling the closure, or the defaultResult if no such result was found * @since 1.7.5 */ public static <T, U extends T, V extends T> T findResult(Map<?, ?> self, U defaultResult, Closure<V> closure) { T result = findResult(self, closure); if (result == null) return defaultResult; return result; } /** * Returns the first non-null closure result found by passing each map entry to the closure, otherwise null is returned. * 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. * <pre class="groovyTestCase"> * assert "Found b:3" == [a:1, b:3].findResult { if (it.value == 3) return "Found ${it.key}:${it.value}" } * assert null == [a:1, b:3].findResult { if (it.value == 9) return "Found ${it.key}:${it.value}" } * assert "Found a:1" == [a:1, b:3].findResult { k, v -> if (k.size() + v == 2) return "Found $k:$v" } * </pre> * * @param self a Map * @param closure a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned * @return the first non-null result collected by calling the closure, or null if no such result was found * @since 1.7.5 */ public static <T> T findResult(Map<?, ?> self, Closure<T> closure) { for (Map.Entry<?, ?> entry : self.entrySet()) { T result = callClosureForMapEntry(closure, entry); if (result != null) { return result; } } return null; } /** * Finds all values matching the closure condition. * <pre class="groovyTestCase">assert [2,4] == [1,2,3,4].findAll { it % 2 == 0 }</pre> * * @param self a Collection * @param closure a closure condition * @return a Collection of matching values * @since 1.5.6 */ public static <T> Collection<T> findAll(Collection<T> self, Closure closure) { Collection<T> answer = createSimilarCollection(self); Iterator<T> iter = self.iterator(); return findAll(closure, answer, iter); } /** * Finds the items matching the IDENTITY Closure (i.e. matching Groovy truth). * <p/> * Example: * <pre class="groovyTestCase"> * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null] * assert items.findAll() == [1, 2, true, 'foo', [4, 5]] * </pre> * * @param self a Collection * @return a List of the values found * @since 1.8.1 * @see Closure#IDENTITY */ public static <T> Collection<T> findAll(Collection<T> self) { return findAll(self, Closure.IDENTITY); } /** * 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); } /** * Finds all items matching the IDENTITY Closure (i.e. matching Groovy truth). * <p/> * Example: * <pre class="groovyTestCase"> * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null] * assert items.findAll() == [1, 2, true, 'foo', [4, 5]] * </pre> * * @param self an Object with an Iterator returning its values * @return a List of the values found * @since 1.8.1 * @see Closure#IDENTITY */ public static Collection findAll(Object self) { return findAll(self, Closure.IDENTITY); } private static <T> Collection<T> findAll(Closure closure, Collection<T> answer, Iterator<? extends T> iter) { while (iter.hasNext()) { T value = iter.next(); if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { answer.add(value); } } return answer; } /** * Returns <tt>true</tt> if this collection contains all of the elements * in the specified array. * * @param self a Collection to be checked for containment * @param items array to be checked for containment in this collection * @return <tt>true</tt> if this collection contains all of the elements * in the specified array * @see Collection#containsAll(Collection) * @since 1.7.2 */ public static boolean containsAll(Collection self, Object[] items) { return self.containsAll(Arrays.asList(items)); } /** * Modifies this collection by removing its elements that are contained * within the specified object array. * * See also <code>findAll</code> and <code>grep</code> when wanting to produce a new list * containing items which don't match some criteria while leaving the original collection unchanged. * * @param self a Collection to be modified * @param items array containing elements to be removed from this collection * @return <tt>true</tt> if this collection changed as a result of the call * @see Collection#removeAll(Collection) * @since 1.7.2 */ public static boolean removeAll(Collection self, Object[] items) { return self.removeAll(Arrays.asList(items)); } /** * Modifies this collection so that it retains only its elements that are contained * in the specified array. In other words, removes from this collection all of * its elements that are not contained in the specified array. * * See also <code>grep</code> and <code>findAll</code> when wanting to produce a new list * containing items which match some specified items but leaving the original collection unchanged. * * @param self a Collection to be modified * @param items array containing elements to be retained from this collection * @return <tt>true</tt> if this collection changed as a result of the call * @see Collection#retainAll(Collection) * @since 1.7.2 */ public static boolean retainAll(Collection self, Object[] items) { return self.retainAll(Arrays.asList(items)); } /** * Modifies this collection so that it retains only its elements * that are matched according to the specified closure condition. In other words, * removes from this collection all of its elements that don't match. * * See also <code>findAll</code> and <code>grep</code> when wanting to produce a new list * containing items which match some criteria but leaving the original collection unchanged. * * @param self a Collection to be modified * @param condition a closure condition * @return <tt>true</tt> if this collection changed as a result of the call * @see Iterator#remove() * @since 1.7.2 */ public static boolean retainAll(Collection self, Closure condition) { Iterator iter = InvokerHelper.asIterator(self); boolean result = false; while (iter.hasNext()) { Object value = iter.next(); if (!DefaultTypeTransformation.castToBoolean(condition.call(value))) { iter.remove(); result = true; } } return result; } /** * Modifies this collection by removing the elements that are matched according * to the specified closure condition. * * See also <code>findAll</code> and <code>grep</code> when wanting to produce a new list * containing items which don't match some criteria while leaving the original collection unchanged. * * @param self a Collection to be modified * @param condition a closure condition * @return <tt>true</tt> if this collection changed as a result of the call * @see Iterator#remove() * @since 1.7.2 */ public static boolean removeAll(Collection self, Closure condition) { Iterator iter = InvokerHelper.asIterator(self); boolean result = false; while (iter.hasNext()) { Object value = iter.next(); if (DefaultTypeTransformation.castToBoolean(condition.call(value))) { iter.remove(); result = true; } } return result; } /** * Modifies the collection by adding all of the elements in the specified array to the collection. * The behavior of this operation is undefined if * the specified array is modified while the operation is in progress. * * See also <code>plus</code> or the '+' operator if wanting to produce a new collection * containing additional items but while leaving the original collection unchanged. * * @param self a Collection to be modified * @param items array containing elements to be added to this collection * @return <tt>true</tt> if this collection changed as a result of the call * @see Collection#addAll(Collection) * @since 1.7.2 */ public static <T> boolean addAll(Collection<T> self, T[] items) { return self.addAll(Arrays.asList(items)); } /** * Modifies this list by inserting all of the elements in the specified array into the * list at the specified position. Shifts the * element currently at that position (if any) and any subsequent * elements to the right (increases their indices). The new elements * will appear in this list in the order that they occur in the array. * The behavior of this operation is undefined if the specified array * is modified while the operation is in progress. * * See also <code>plus</code> for similar functionality with copy semantics, i.e. which produces a new * list after adding the additional items at the specified position but leaves the original list unchanged. * * @param self a list to be modified * @param items array containing elements to be added to this collection * @param index index at which to insert the first element from the * specified array * @return <tt>true</tt> if this collection changed as a result of the call * @see List#addAll(int, Collection) * @since 1.7.2 */ public static <T> boolean addAll(List<T> self, int index, T[] items) { return self.addAll(index, Arrays.asList(items)); } /** * 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 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. * <p> * Example usage: * <pre class="groovyTestCase">assert [[2,4],[1,3]] == [1,2,3,4].split { it % 2 == 0 }</pre> * * @param self a Collection of values * @param closure a closure condition * @return a List whose first item is the accepted values and whose second item is the rejected values * @since 1.6.0 */ public static <T> Collection<Collection<T>> split(Collection<T> self, Closure closure) { Collection<T> accept = createSimilarCollection(self); Collection<T> reject = createSimilarCollection(self); Iterator<T> iter = self.iterator(); return split(closure, accept, reject, iter); } private static <T> Collection<Collection<T>> split(Closure closure, Collection<T> accept, Collection<T> reject, Iterator<T> iter) { List<Collection<T>> answer = new ArrayList<Collection<T>>(); 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. * <p> * Example usage: * <pre class="groovyTestCase">assert [['a', 'b'],[1, 2, 3]].combinations() == [['a', 1], ['b', 1], ['a', 2], ['b', 2], ['a', 3], ['b', 3]]</pre> * * @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); } /** * Finds all non-null subsequences of a list. * <p> * Example usage: * <pre class="groovyTestCase">def result = [1, 2, 3].subsequences() * assert result == [[1, 2, 3], [1, 3], [2, 3], [1, 2], [1], [2], [3]] as Set</pre> * * @param self the List of items * @return the subsequences from the list * @since 1.7.0 */ public static <T> Set<List<T>> subsequences(List<T> self) { return GroovyCollections.subsequences(self); } /** * Finds all permutations of a collection. * <p> * Example usage: * <pre class="groovyTestCase">def result = [1, 2, 3].permutations() * assert result == [[3, 2, 1], [3, 1, 2], [1, 3, 2], [2, 3, 1], [2, 1, 3], [1, 2, 3]] as Set</pre> * * @param self the Collection of items * @return the permutations from the list * @since 1.7.0 */ public static <T> Set<List<T>> permutations(List<T> self) { Set<List<T>> ans = new HashSet<List<T>>(); PermutationGenerator<T> generator = new PermutationGenerator<T>(self); while (generator.hasNext()) { ans.add(generator.next()); } return ans; } /** * Iterates over all permutations of a collection, running a closure for each iteration. * <p> * Example usage: * <pre class="groovyTestCase">def permutations = [] * [1, 2, 3].eachPermutation{ permutations << it } * assert permutations == [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]</pre> * * @param self the Collection of items * @param closure the closure to call for each permutation * @return the permutations from the list * @since 1.7.0 */ public static <T> Iterator<List<T>> eachPermutation(Collection<T> self, Closure closure) { Iterator<List<T>> generator = new PermutationGenerator<T>(self); while (generator.hasNext()) { closure.call(generator.next()); } return generator; } /** * Adds GroovyCollections#transpose(List) as a method on lists. <br/> * A TransposeFunction takes a collection of columns and returns a collection of * rows. The first row consists of the first element from each column. Successive * rows are constructed similarly. * <p> * Example usage: * <pre class="groovyTestCase">def result = [['a', 'b'], [1, 2]].transpose() * assert result == [['a', 1], ['b', 2]]</pre> * <pre class="groovyTestCase">def result = [['a', 'b'], [1, 2], [3, 4]].transpose() * assert result == [['a', 1, 3], ['b', 2, 4]]</pre> * * @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. * <p> * If the <code>self</code> map is one of TreeMap, LinkedHashMap, Hashtable * or Properties, the returned Map will preserve that type, otherwise a HashMap will * be returned. * <p> * Example usage: * <pre class="groovyTestCase">def result = [a:1, b:2, c:4, d:5].findAll { it.value % 2 == 0 } * assert result.every { it instanceof Map.Entry } * assert result*.key == ["b", "c"] * assert result*.value == [2, 4]</pre> * * @param self a Map * @param closure a 1 or 2 arg Closure condition applying on the entries * @return a new subMap * @since 1.0 */ public static <K, V> Map<K, V> findAll(Map<K, V> self, Closure closure) { Map<K, V> answer = createSimilarMap(self); for (Map.Entry<K, V> 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. * <p> * Example usage: * <pre class="groovyTestCase">assert [0:[2,4,6], 1:[1,3,5]] == [1,2,3,4,5,6].groupBy { it % 2 }</pre> * * @param self a collection to group * @param closure a closure mapping entries on keys * @return a new Map grouped by keys * @since 1.0 */ public static <K, T> Map<K, List<T>> groupBy(Collection<T> self, Closure<K> closure) { Map<K, List<T>> answer = new LinkedHashMap<K, List<T>>(); for (T element : self) { K value = closure.call(element); groupAnswer(answer, element, value); } return answer; } /** * Sorts all collection members into (sub)groups determined by the supplied * mapping closures. Each closure should return the key that this item * should be grouped by. The returned LinkedHashMap will have an entry for each * distinct 'key path' returned from the closures, with each value being a list * of items for that 'group path'. <p> * * Example usage: * <pre class="groovyTestCase">def result = [1,2,3,4,5,6].groupBy({ it % 2 }, { it < 4 }) * assert result == [1:[(true):[1, 3], (false):[5]], 0:[(true):[2], (false):[4, 6]]]</pre> * * Another example: * <pre>def sql = groovy.sql.Sql.newInstance(/* ... */) * def data = sql.rows("SELECT * FROM a_table").groupBy({ it.column1 }, { it.column2 }, { it.column3 }) * if (data.val1.val2.val3) { * // there exists a record where: * // a_table.column1 == val1 * // a_table.column2 == val2, and * // a_table.column3 == val3 * } else { * // there is no such record * }</pre> * If an empty array of closures is supplied the IDENTITY Closure will be used. * * @param self a collection to group * @param closures an array of closures, each mapping entries on keys * @return a new Map grouped by keys on each criterion * @since 1.8.1 * @see Closure#IDENTITY */ public static Map groupBy(Collection self, Object... closures) { final Closure head = closures.length == 0 ? Closure.IDENTITY : (Closure) closures[0]; @SuppressWarnings("unchecked") Map<Object, List> first = groupBy(self, head); if (closures.length < 2) return first; final Object[] tail = new Object[closures.length - 1]; System.arraycopy(closures, 1, tail, 0, closures.length - 1); // Arrays.copyOfRange only since JDK 1.6 // inject([:]) { a,e -> a << [(e.key): e.value.groupBy(tail)] } Map<Object, Map> acc = new LinkedHashMap<Object, Map>(); for (Map.Entry<Object, List> item : first.entrySet()) { acc.put(item.getKey(), groupBy(item.getValue(), tail)); } return acc; } /** * Sorts all collection members into (sub)groups determined by the supplied * mapping closures. Each closure should return the key that this item * should be grouped by. The returned LinkedHashMap will have an entry for each * distinct 'key path' returned from the closures, with each value being a list * of items for that 'group path'. <p> * * Example usage: * <pre class="groovyTestCase">def result = [1,2,3,4,5,6].groupBy([{ it % 2 }, { it < 4 }]) * assert result == [1:[(true):[1, 3], (false):[5]], 0:[(true):[2], (false):[4, 6]]]</pre> * * Another example: * <pre>def sql = groovy.sql.Sql.newInstance(/* ... */) * def data = sql.rows("SELECT * FROM a_table").groupBy([{ it.column1 }, { it.column2 }, { it.column3 }]) * if (data.val1.val2.val3) { * // there exists a record where: * // a_table.column1 == val1 * // a_table.column2 == val2, and * // a_table.column3 == val3 * } else { * // there is no such record * }</pre> * If an empty list of closures is supplied the IDENTITY Closure will be used. * * @param self a collection to group * @param closures a list of closures, each mapping entries on keys * @return a new Map grouped by keys on each criterion * @since 1.8.1 * @see Closure#IDENTITY */ public static Map groupBy(Collection self, List<Closure> closures) { return groupBy(self, closures.toArray()); } /** * Sorts all collection members into groups determined by the supplied mapping * closure and counts the group size. The closure should return the key that each * item should be grouped by. The returned Map will have an entry for each * distinct key returned from the closure, with each value being the frequency of * items occurring for that group. * <p> * Example usage: * <pre class="groovyTestCase">assert [0:2, 1:3] == [1,2,3,4,5].countBy { it % 2 }</pre> * * @param self a collection to group and count * @param closure a closure mapping items to the frequency keys * @return a new Map grouped by keys with frequency counts * @since 1.8.0 */ public static <K> Map<K, Integer> countBy(Collection self, Closure<K> closure) { return countBy(self.iterator(), closure); } /** * Sorts all array members into groups determined by the supplied mapping * closure and counts the group size. The closure should return the key that each * item should be grouped by. The returned Map will have an entry for each * distinct key returned from the closure, with each value being the frequency of * items occurring for that group. * <p> * Example usage: * <pre class="groovyTestCase">assert ([1,2,2,2,3] as Object[]).countBy{ it % 2 } == [1:2, 0:3]</pre> * * @param self an object array to group and count * @param closure a closure mapping items to the frequency keys * @return a new Map grouped by keys with frequency counts * @see #countBy(Collection, Closure) * @since 1.8.0 */ public static <K> Map<K, Integer> countBy(Object[] self, Closure<K> closure) { return countBy(Arrays.asList(self), closure); } /** * Sorts all iterator items into groups determined by the supplied mapping * closure and counts the group size. The closure should return the key that each * item should be grouped by. The returned Map will have an entry for each * distinct key returned from the closure, with each value being the frequency of * items occurring for that group. * <p> * Example usage: * <pre class="groovyTestCase">assert [1,2,2,2,3].toSet().iterator().countBy{ it % 2 } == [1:2, 0:1]</pre> * * @param self an iterator to group and count * @param closure a closure mapping items to the frequency keys * @return a new Map grouped by keys with frequency counts * @see #countBy(Collection, Closure) * @since 1.8.0 */ public static <K> Map<K, Integer> countBy(Iterator self, Closure<K> closure) { Map<K, Integer> answer = new LinkedHashMap<K, Integer>(); while (self.hasNext()) { K value = closure.call(self.next()); countAnswer(answer, value); } return answer; } /** * Groups all map entries into groups determined by the * supplied mapping closure. The closure will be passed a Map.Entry or * key and value (depending on the number of parameters the closure accepts) * and should return the key that each item should be grouped under. The * resulting map will have an entry for each 'group' key returned by the * closure, with values being the list of map entries that belong to each * group. (If instead of a list of map entries, you want an actual map * use {code}groupBy{code}.) * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupEntriesBy { it.value % 2 } * assert result[0]*.key == ["b", "d", "f"] * assert result[1]*.value == [1, 3, 5]</pre> * * @param self a map to group * @param closure a 1 or 2 arg Closure mapping entries on keys * @return a new Map grouped by keys * @since 1.5.2 */ public static <G, K, V> Map<G, List<Map.Entry<K, V>>> groupEntriesBy(Map<K, V> self, Closure<G> closure) { final Map<G, List<Map.Entry<K, V>>> answer = new LinkedHashMap<G, List<Map.Entry<K, V>>>(); for (Map.Entry<K, V> entry : self.entrySet()) { G value = callClosureForMapEntry(closure, entry); groupAnswer(answer, entry, value); } return answer; } /** * Groups the members of a map into sub maps determined by the * supplied mapping closure. The closure will be passed a Map.Entry or * key and value (depending on the number of parameters the closure accepts) * and should return the key that each item should be grouped under. The * resulting map will have an entry for each 'group' key returned by the * closure, with values being the map members from the original map that * belong to each group. (If instead of a map, you want a list of map entries * use {code}groupEntriesBy{code}.) * <p> * If the <code>self</code> map is one of TreeMap, Hashtable or Properties, * the returned Map will preserve that type, otherwise a LinkedHashMap will * be returned. * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy { it.value % 2 } * assert result == [0:[b:2, d:4, f:6], 1:[a:1, c:3, e:5]]</pre> * * @param self a map to group * @param closure a closure mapping entries on keys * @return a new Map grouped by keys * @since 1.0 */ public static <G, K, V> Map<G, Map<K, V>> groupBy(Map<K, V> self, Closure<G> closure) { final Map<G, List<Map.Entry<K, V>>> initial = groupEntriesBy(self, closure); final Map<G, Map<K, V>> answer = new LinkedHashMap<G, Map<K, V>>(); for (Map.Entry<G, List<Map.Entry<K, V>>> outer : initial.entrySet()) { G key = outer.getKey(); List<Map.Entry<K, V>> entries = outer.getValue(); Map<K, V> target = createSimilarMap(self); putAll(target, entries); answer.put(key, target); } return answer; } /** * Groups the members of a map into sub maps determined by the supplied * mapping closures. Each closure will be passed a Map.Entry or key and * value (depending on the number of parameters the closure accepts) and * should return the key that each item should be grouped under. The * resulting map will have an entry for each 'group path' returned by all * closures, with values being the map members from the original map that * belong to each such 'group path'. <p> * * If the <code>self</code> map is one of TreeMap, Hashtable, or Properties, * the returned Map will preserve that type, otherwise a LinkedHashMap will * be returned. * * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy({ it.value % 2 }, { it.key.next() }) * assert result == [1:[b:[a:1], d:[c:3], f:[e:5]], 0:[c:[b:2], e:[d:4], g:[f:6]]]</pre> * If an empty array of closures is supplied the IDENTITY Closure will be used. * * @param self a map to group * @param closures an array of closures that map entries on keys * @return a new map grouped by keys on each criterion * @since 1.8.1 * @see Closure#IDENTITY */ public static Map<Object, Map> groupBy(Map self, Object... closures) { @SuppressWarnings("unchecked") final Closure<Object> head = closures.length == 0 ? Closure.IDENTITY : (Closure) closures[0]; @SuppressWarnings("unchecked") Map<Object, Map> first = groupBy(self, head); if (closures.length < 2) return first; final Object[] tail = new Object[closures.length - 1]; System.arraycopy(closures, 1, tail, 0, closures.length - 1); // Arrays.copyOfRange only since JDK 1.6 Map<Object, Map> acc = new LinkedHashMap<Object, Map>(); for (Map.Entry<Object, Map> item: first.entrySet()) { acc.put(item.getKey(), groupBy(item.getValue(), tail)); } return acc; } /** * Groups the members of a map into sub maps determined by the supplied * mapping closures. Each closure will be passed a Map.Entry or key and * value (depending on the number of parameters the closure accepts) and * should return the key that each item should be grouped under. The * resulting map will have an entry for each 'group path' returned by all * closures, with values being the map members from the original map that * belong to each such 'group path'. <p> * * If the <code>self</code> map is one of TreeMap, Hashtable, or Properties, * the returned Map will preserve that type, otherwise a LinkedHashMap will * be returned. * * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy([{ it.value % 2 }, { it.key.next() }]) * assert result == [1:[b:[a:1], d:[c:3], f:[e:5]], 0:[c:[b:2], e:[d:4], g:[f:6]]]</pre> * If an empty list of closures is supplied the IDENTITY Closure will be used. * * @param self a map to group * @param closures a list of closures that map entries on keys * @return a new map grouped by keys on each criterion * @since 1.8.1 * @see Closure#IDENTITY */ public static Map<Object, Map> groupBy(Map self, List<Closure> closures) { return groupBy(self, closures.toArray()); } /** * Groups the members of a map into groups determined by the * supplied mapping closure and counts the frequency of the created groups. * The closure will be passed a Map.Entry or * key and value (depending on the number of parameters the closure accepts) * and should return the key that each item should be grouped under. The * resulting map will have an entry for each 'group' key returned by the * closure, with values being the frequency counts for that 'group'. * <p> * <pre class="groovyTestCase">def result = [a:1,b:2,c:3,d:4,e:5].countBy { it.value % 2 } * assert result == [0:2, 1:3]</pre> * * @param self a map to group and count * @param closure a closure mapping entries to frequency count keys * @return a new Map grouped by keys with frequency counts * @since 1.8.0 */ public static <K> Map<K, Integer> countBy(Map self, Closure<K> closure) { Map<K, Integer> answer = new LinkedHashMap<K, Integer>(); for (Object entry : self.entrySet()) { countAnswer(answer, callClosureForMapEntry(closure, (Map.Entry) entry)); } return answer; } /** * Groups the current element according to the value * * @param answer the map containing the results * @param element the element to be placed * @param value the value according to which the element will be placed * @since 1.5.0 */ protected static <K, T> void groupAnswer(final Map<K, List<T>> answer, T element, K value) { if (answer.containsKey(value)) { answer.get(value).add(element); } else { List<T> groupedElements = new ArrayList<T>(); groupedElements.add(element); answer.put(value, groupedElements); } } private static <T> void countAnswer(final Map<T, Integer> answer, T mappedKey) { if (!answer.containsKey(mappedKey)) { answer.put(mappedKey, 0); } int current = answer.get(mappedKey); answer.put(mappedKey, current + 1); } // internal helper method protected static <T> T callClosureForMapEntry(Closure<T> closure, Map.Entry entry) { if (closure.getMaximumNumberOfParameters() == 2) { return closure.call(new Object[]{entry.getKey(), entry.getValue()}); } return closure.call(entry); } // internal helper method protected static <T> T callClosureForLine(Closure<T> closure, String line, int counter) { if (closure.getMaximumNumberOfParameters() == 2) { return closure.call(new Object[]{line, counter}); } return closure.call(line); } // internal helper method protected static <T> T callClosureForMapEntryAndCounter(Closure<T> closure, Map.Entry entry, int counter) { if (closure.getMaximumNumberOfParameters() == 3) { return closure.call(new Object[]{entry.getKey(), entry.getValue(), counter}); } if (closure.getMaximumNumberOfParameters() == 2) { return closure.call(new Object[]{entry, counter}); } return closure.call(entry); } /** * Iterates through the given Collection, passing in the initial value to * the 2-arg closure along with the first item. The result is passed back (injected) into * the closure along with the second item. The new result is injected back into * the closure along with the third item and so on until the entire collection * has been used. Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional parlance. * * Examples: * <pre class="groovyTestCase"> * assert 1*1*2*3*4 == [1,2,3,4].inject(1) { acc, val -> acc * val } * * assert 0+1+2+3+4 == [1,2,3,4].inject(0) { acc, val -> acc + val } * * assert 'The quick brown fox' == * ['quick', 'brown', 'fox'].inject('The') { acc, val -> acc + ' ' + val } * * assert 'bat' == * ['rat', 'bat', 'cat'].inject('zzz') { min, next -> next < min ? next : min } * * def max = { a, b -> [a, b].max() } * def animals = ['bat', 'rat', 'cat'] * assert 'rat' == animals.inject('aaa', max) * </pre> * Visual representation of the last example above: * <pre> * initVal animals[0] * v v * max('aaa', 'bat') => 'bat' animals[1] * v v * max('bat', 'rat') => 'rat' animals[2] * v v * max('rat', 'cat') => 'rat' * </pre> * * @param self a Collection * @param initialValue some initial value * @param closure a closure * @return the result of the last closure call * @since 1.0 */ public static <T, U extends T, V extends T> T inject(Collection self, U initialValue, Closure<V> closure) { return (T) inject(self.iterator(), initialValue, closure); } /** * Iterates through the given Map, passing in the initial value to * the 2-arg Closure along with the first item (or 3-arg Closure along with the first key and value). * The result is passed back (injected) into * the closure along with the second item. The new result is injected back into * the closure along with the third item and so on until the entire collection * has been used. Also known as <tt>foldLeft</tt> or <tt>reduce</tt> in functional parlance. * * Examples: * <pre class="groovyTestCase"> * def map = [a:1, b:2, c:3] * assert map.inject([]) { list, k, v -> * list + [k] * v * } == ['a', 'b', 'b', 'c', 'c', 'c'] * </pre> * * @param self a Map * @param initialValue some initial value * @param closure a 2 or 3 arg Closure * @return the result of the last closure call * @since 1.8.1 */ public static <T, U extends T, V extends T> T inject(Map<?, ?> self, U initialValue, Closure<V> closure) { T value = initialValue; for (Map.Entry<?, ?> entry : self.entrySet()) { if (closure.getMaximumNumberOfParameters() == 3) { value = closure.call(value, entry.getKey(), entry.getValue()); } else { value = closure.call(value, entry); } } return value; } /** * Iterates through the given Iterator, passing in the initial value to * the closure along with the first item. The result is passed back (injected) into * the closure along with the second item. The new result is injected back into * the closure along with the third item and so on until the Iterator has been * expired of values. Also known as foldLeft in functional parlance. * * @param self an Iterator * @param initialValue some initial value * @param closure a closure * @return the result of the last closure call * @see #inject(Collection, Object, Closure) * @since 1.5.0 */ public static <T, U extends T, V extends T> T inject(Iterator self, U initialValue, Closure<V> closure) { T value = initialValue; Object[] params = new Object[2]; while (self.hasNext()) { Object item = self.next(); params[0] = value; params[1] = item; value = closure.call(params); } return value; } /** * Iterates through the given Object, passing in the initial value to * the closure along with the first item. The result is passed back (injected) into * the closure along with the second item. The new result is injected back into * the closure along with the third item and so on until further iteration of * the object is not possible. Also known as foldLeft in functional parlance. * * @param self an Object * @param initialValue some initial value * @param closure a closure * @return the result of the last closure call * @see #inject(Collection, Object, Closure) * @since 1.5.0 */ public static <T, U extends T, V extends T> T inject(Object self, U initialValue, Closure<V> closure) { Iterator iter = InvokerHelper.asIterator(self); return (T) inject(iter, initialValue, closure); } /** * Iterates through the given array, passing in the initial value to * the closure along with the first item. The result is passed back (injected) into * the closure along with the second item. The new result is injected back into * the closure along with the third item and so on until all elements of the array * have been used. Also known as foldLeft in functional parlance. * * @param self an Object[] * @param initialValue some initial value * @param closure a closure * @return the result of the last closure call * @see #inject(Collection, Object, Closure) * @since 1.5.0 */ public static <T, U extends T, V extends T> T inject(Object[] self, U initialValue, Closure<V> closure) { Object[] params = new Object[2]; T value = initialValue; for (Object next : self) { params[0] = value; params[1] = next; value = closure.call(params); } return value; } /** * Sums the items in a collection. This is equivalent to invoking the * "plus" method on all items in the collection. * <pre class="groovyTestCase">assert 1+2+3+4 == [1,2,3,4].sum()</pre> * * @param self Collection of values to add together * @return The sum of all of the items * @since 1.0 */ public static Object sum(Collection self) { return sum(self, null, true); } /** * Sums the items in an array. This is equivalent to invoking the * "plus" method on all items in the array. * * @param self The array of values to add together * @return The sum of all of the items * @see #sum(java.util.Collection) * @since 1.7.1 */ public static Object sum(Object[] self) { return sum(toList(self), null, true); } /** * Sums the items from an Iterator. This is equivalent to invoking the * "plus" method on all items from the Iterator. The iterator will become * exhausted of elements after determining the sum value. * * @param self an Iterator for the values to add together * @return The sum of all of the items * @since 1.5.5 */ public static Object sum(Iterator<Object> self) { return sum(toList(self), null, true); } /** * Sums the items in a collection, adding the result to some initial value. * <pre class="groovyTestCase">assert 5+1+2+3+4 == [1,2,3,4].sum(5)</pre> * * @param self a collection of values to sum * @param initialValue the items in the collection will be summed to this initial value * @return The sum of all of the items. * @since 1.5.0 */ public static Object sum(Collection self, Object initialValue) { return sum(self, initialValue, false); } /** * Sums the items in an array, adding the result to some initial value. * * @param self an array of values to sum * @param initialValue the items in the array will be summed to this initial value * @return The sum of all of the items. * @since 1.7.1 */ public static Object sum(Object[] self, Object initialValue) { return sum(toList(self), initialValue, false); } /** * Sums the items from an Iterator, adding the result to some initial value. This is * equivalent to invoking the "plus" method on all items from the Iterator. The iterator * will become exhausted of elements after determining the sum value. * * @param self an Iterator for the values to add together * @param initialValue the items in the collection will be summed to this initial value * @return The sum of all of the items * @since 1.5.5 */ public static Object sum(Iterator<Object> self, Object initialValue) { return sum(toList(self), initialValue, false); } private static Object sum(Collection self, Object initialValue, boolean first) { Object result = initialValue; Object[] param = new Object[1]; for (Object next : self) { param[0] = next; if (first) { result = param[0]; first = false; continue; } MetaClass metaClass = InvokerHelper.getMetaClass(result); result = metaClass.invokeMethod(result, "plus", param); } return result; } /** * Sums the result of apply a closure to each item of a collection. * <code>coll.sum(closure)</code> is equivalent to: * <code>coll.collect(closure).sum()</code>. * <pre class="groovyTestCase">assert 4+6+10+12 == [2,3,5,6].sum() { it * 2 }</pre> * * @param self a Collection * @param closure a single parameter closure that returns a numeric value. * @return The sum of the values returned by applying the closure to each * item of the collection. * @since 1.0 */ public static Object sum(Collection self, Closure closure) { return sum(self, null, closure, true); } /** * Sums the result of apply a closure to each item of an array. * <code>array.sum(closure)</code> is equivalent to: * <code>array.collect(closure).sum()</code>. * * @param self An array * @param closure a single parameter closure that returns a numeric value. * @return The sum of the values returned by applying the closure to each * item of the array. * @since 1.7.1 */ public static Object sum(Object[] self, Closure closure) { return sum(toList(self), null, closure, true); } /** * Sums the result of apply a closure to each item returned from an iterator. * <code>iter.sum(closure)</code> is equivalent to: * <code>iter.collect(closure).sum()</code>. The iterator will become * exhausted of elements after determining the sum value. * * @param self An Iterator * @param closure a single parameter closure that returns a numeric value. * @return The sum of the values returned by applying the closure to each * item from the Iterator. * @since 1.7.1 */ public static Object sum(Iterator<Object> self, Closure closure) { return sum(toList(self), null, closure, true); } /** * Sums the result of applying a closure to each item of a collection to some initial value. * <code>coll.sum(initVal, closure)</code> is equivalent to: * <code>coll.collect(closure).sum(initVal)</code>. * <pre class="groovyTestCase">assert 50+4+6+10+12 == [2,3,5,6].sum(50) { it * 2 }</pre> * * @param self a Collection * @param closure a single parameter closure that returns a numeric value. * @param initialValue the closure results will be summed to this initial value * @return The sum of the values returned by applying the closure to each * item of the collection. * @since 1.5.0 */ public static Object sum(Collection self, Object initialValue, Closure closure) { return sum(self, initialValue, closure, false); } /** * Sums the result of applying a closure to each item of an array to some initial value. * <code>array.sum(initVal, closure)</code> is equivalent to: * <code>array.collect(closure).sum(initVal)</code>. * * @param self an array * @param closure a single parameter closure that returns a numeric value. * @param initialValue the closure results will be summed to this initial value * @return The sum of the values returned by applying the closure to each * item of the array. * @since 1.7.1 */ public static Object sum(Object[] self, Object initialValue, Closure closure) { return sum(toList(self), initialValue, closure, false); } /** * Sums the result of applying a closure to each item of an Iterator to some initial value. * <code>iter.sum(initVal, closure)</code> is equivalent to: * <code>iter.collect(closure).sum(initVal)</code>. The iterator will become * exhausted of elements after determining the sum value. * * @param self an Iterator * @param closure a single parameter closure that returns a numeric value. * @param initialValue the closure results will be summed to this initial value * @return The sum of the values returned by applying the closure to each * item from the Iterator. * @since 1.7.1 */ public static Object sum(Iterator<Object> self, Object initialValue, Closure closure) { return sum(toList(self), initialValue, closure, false); } private static Object sum(Collection self, Object initialValue, Closure closure, boolean first) { Object result = initialValue; Object[] closureParam = new Object[1]; Object[] plusParam = new Object[1]; for (Object next : self) { closureParam[0] = next; plusParam[0] = closure.call(closureParam); if (first) { result = plusParam[0]; first = false; continue; } MetaClass metaClass = InvokerHelper.getMetaClass(result); result = metaClass.invokeMethod(result, "plus", plusParam); } return result; } /** * Concatenates the <code>toString()</code> representation of each * item from the iterator, with the given String as a separator between * each item. The iterator will become exhausted of elements after * determining the resulting conjoined value. * * @param self an Iterator of items * @param separator a String separator * @return the joined String * @since 1.5.5 */ public static String join(Iterator<Object> self, String separator) { return join(toList(self), separator); } /** * Concatenates the <code>toString()</code> representation of each * item in this collection, with the given String as a separator between * each item. * <pre class="groovyTestCase">assert "1, 2, 3" == [1,2,3].join(", ")</pre> * * @param self a Collection of objects * @param separator a String separator * @return the joined String * @since 1.0 */ public static String join(Collection self, String separator) { StringBuilder buffer = new StringBuilder(); boolean first = true; if (separator == null) separator = ""; for (Object value : self) { if (first) { first = false; } else { buffer.append(separator); } buffer.append(InvokerHelper.toString(value)); } return buffer.toString(); } /** * Concatenates the <code>toString()</code> representation of each * items in this array, with the given String as a separator between each * item. * * @param self an array of Object * @param separator a String separator * @return the joined String * @since 1.0 */ public static String join(Object[] self, String separator) { StringBuilder buffer = new StringBuilder(); boolean first = true; if (separator == null) separator = ""; for (Object next : self) { String value = InvokerHelper.toString(next); if (first) { first = false; } else { buffer.append(separator); } buffer.append(value); } return buffer.toString(); } /** * Adds min() method to Collection objects. * <pre class="groovyTestCase">assert 2 == [4,2,5].min()</pre> * * @param self a Collection * @return the minimum value * @see groovy.util.GroovyCollections#min(java.util.Collection) * @since 1.0 */ public static <T> T min(Collection<T> self) { return GroovyCollections.min(self); } /** * Adds min() method to Iterator objects. The iterator will become * exhausted of elements after determining the minimum value. * * @param self an Iterator * @return the minimum value * @see #min(java.util.Collection) * @since 1.5.5 */ public static <T> T min(Iterator<T> self) { return min(toList(self)); } /** * Adds min() method to Object arrays. * * @param self an Object array * @return the minimum value * @see #min(java.util.Collection) * @since 1.5.5 */ public static <T> T min(T[] self) { return min(toList(self)); } /** * Selects the minimum value found in the collection using the given comparator. * <pre class="groovyTestCase">assert "hi" == ["hello","hi","hey"].min( { a, b -> a.length() <=> b.length() } as Comparator )</pre> * * @param self a Collection * @param comparator a Comparator * @return the minimum value * @since 1.0 */ public static <T> T min(Collection<T> self, Comparator<T> comparator) { T answer = null; for (T value : self) { if (answer == null || comparator.compare(value, answer) < 0) { answer = value; } } return answer; } /** * Selects the minimum value found from the Iterator using the given comparator. * * @param self an Iterator * @param comparator a Comparator * @return the minimum value * @see #min(java.util.Collection, java.util.Comparator) * @since 1.5.5 */ public static <T> T min(Iterator<T> self, Comparator<T> comparator) { return min(toList(self), comparator); } /** * Selects the minimum value found from the Object array using the given comparator. * * @param self an Object array * @param comparator a Comparator * @return the minimum value * @see #min(java.util.Collection, java.util.Comparator) * @since 1.5.5 */ public static <T> T min(T[] self, Comparator<T> comparator) { return min(toList(self), comparator); } /** * Selects an item in the collection having the minimum * value as determined by the supplied closure. * If more than one item has the minimum value, * an arbitrary choice is made between the items having the minimum value. * </p> * 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. * <pre class="groovyTestCase"> * assert "hi" == ["hello","hi","hey"].min { it.length() } * </pre> * <pre class="groovyTestCase"> * def lastDigit = { a, b -> a % 10 <=> b % 10 } * assert [19, 55, 91].min(lastDigit) == 91 * </pre> * <pre class="groovyTestCase"> * def pets = ['dog', 'cat', 'anaconda'] * def shortestName = pets.min{ it.size() } // one of 'dog' or 'cat' * assert shortestName.size() == 3 * </pre> * * @param self a Collection * @param closure a 1 or 2 arg Closure used to determine the correct ordering * @return the minimum value * @since 1.0 */ public static <T> T min(Collection<T> self, Closure closure) { int params = closure.getMaximumNumberOfParameters(); if (params != 1) { return min(self, new ClosureComparator<T>(closure)); } T answer = null; Object answer_value = null; for (T item : self) { Object value = closure.call(item); if (answer == null || ScriptBytecodeAdapter.compareLessThan(value, answer_value)) { answer = item; answer_value = value; } } return answer; } /** * Selects an entry in the map having the minimum * calculated value as determined by the supplied closure. * If more than one entry has the minimum value, * an arbitrary choice is made between the entries having the minimum value. * </p> * 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. * <pre class="groovyTestCase"> * def zoo = [monkeys:6, lions:5, tigers:7] * def leastCommonEntry = zoo.min{ it.value } * assert leastCommonEntry.value == 5 * def mostCommonEntry = zoo.min{ a, b -> b.value <=> a.value } // double negative! * assert mostCommonEntry.value == 7 * </pre> * Edge case for multiple min values: * <pre class="groovyTestCase"> * def zoo = [monkeys:6, lions:5, tigers:7] * def lastCharOfName = { e -> e.key[-1] } * def ans = zoo.min(lastCharOfName) // some random entry * assert lastCharOfName(ans) == 's' * </pre> * * @param self a Map * @param closure a 1 or 2 arg Closure used to determine the correct ordering * @return the Map.Entry having the minimum value as determined by the closure * @since 1.7.6 */ public static <K, V> Map.Entry<K, V> min(Map<K, V> self, Closure closure) { return min(self.entrySet(), closure); } /** * Selects an entry in the map having the maximum * calculated value as determined by the supplied closure. * If more than one entry has the maximum value, * an arbitrary choice is made between the entries having the maximum value. * </p> * 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. An example: * <pre class="groovyTestCase"> * def zoo = [monkeys:6, lions:5, tigers:7] * def mostCommonEntry = zoo.max{ it.value } * assert mostCommonEntry.value == 7 * def leastCommonEntry = zoo.max{ a, b -> b.value <=> a.value } // double negative! * assert leastCommonEntry.value == 5 * </pre> * Edge case for multiple max values: * <pre class="groovyTestCase"> * def zoo = [monkeys:6, lions:5, tigers:7] * def lengthOfNamePlusNumber = { e -> e.key.size() + e.value } * def ans = zoo.max(lengthOfNamePlusNumber) // one of [monkeys:6, tigers:7] * assert lengthOfNamePlusNumber(ans) == 13 * </pre> * * @param self a Map * @param closure a 1 or 2 arg Closure used to determine the correct ordering * @return the Map.Entry having the maximum value as determined by the closure * @since 1.7.6 */ public static <K, V> Map.Entry<K, V> max(Map<K, V> self, Closure closure) { return max(self.entrySet(), closure); } /** * Selects the minimum value found from the Iterator * using the closure to determine the correct ordering. * The iterator will become * exhausted of elements after this operation. * </p> * 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> T min(Iterator<T> 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. * </p> * 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> T min(T[] self, Closure closure) { return min(toList(self), closure); } /** * Adds max() method to Collection objects. * <pre class="groovyTestCase">assert 5 == [2,3,1,5,4].max()</pre> * * @param self a Collection * @return the maximum value * @see groovy.util.GroovyCollections#max(java.util.Collection) * @since 1.0 */ public static <T> T max(Collection<T> 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> T max(Iterator<T> 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> T max(T[] self) { return max(toList(self)); } /** * Selects an item in the collection having the maximum * value as determined by the supplied closure. * If more than one item has the maximum value, * an arbitrary choice is made between the items having the maximum value. * </p> * 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. * <pre class="groovyTestCase">assert "hello" == ["hello","hi","hey"].max { it.length() }</pre> * <pre class="groovyTestCase">assert "hello" == ["hello","hi","hey"].max { a, b -> a.length() <=> b.length() }</pre> * <pre class="groovyTestCase"> * def pets = ['dog', 'elephant', 'anaconda'] * def longestName = pets.max{ it.size() } // one of 'elephant' or 'anaconda' * assert longestName.size() == 8 * </pre> * * @param self a Collection * @param closure a 1 or 2 arg Closure used to determine the correct ordering * @return the maximum value * @since 1.0 */ public static <T> T max(Collection<T> self, Closure closure) { int params = closure.getMaximumNumberOfParameters(); if (params != 1) { return max(self, new ClosureComparator<T>(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. * </p> * 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> T max(Iterator<T> 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. * </p> * 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> T max(T[] self, Closure closure) { return max(toList(self), closure); } /** * Selects the maximum value found in the collection using the given comparator. * <pre class="groovyTestCase">assert "hello" == ["hello","hi","hey"].max( { a, b -> a.length() <=> b.length() } as Comparator )</pre> * * @param self a Collection * @param comparator a Comparator * @return the maximum value * @since 1.0 */ public static <T> T max(Collection<T> self, Comparator<T> 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> T max(Iterator<T> self, Comparator<T> 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> T max(T[] self, Comparator<T> comparator) { return max(toList(self), comparator); } /** * Provide the standard Groovy <code>size()</code> method for <code>Iterator</code>. * 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 <code>size()</code> method for <code>String</code>. * * @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 <code>size()</code> method for <code>CharSequence</code>. * * @param text a CharSequence * @return the length of the CharSequence * @since 1.8.2 */ public static int size(CharSequence text) { return text.length(); } /** * Provide the standard Groovy <code>size()</code> method for <code>StringBuffer</code>. * * @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 <code>size()</code> method for <code>File</code>. * * @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 <code>size()</code> method for <code>Matcher</code>. * * @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 <code>size()</code> 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 java.lang.StringBuilder#reverse() */ public static String reverse(String self) { return new StringBuilder(self).reverse().toString(); } /** * Creates a new CharSequence which is the reverse (backwards) of this string * * @param self a CharSequence * @return a new CharSequence with all the characters reversed. * @see #reverse(String) * @since 1.8.2 */ public static CharSequence reverse(CharSequence self) { return new StringBuilder(self).reverse().toString(); } /** * <p>Strip leading whitespace/control characters followed by '|' from * every line in a String.</p> * <pre class="groovyTestCase"> * assert 'ABC\n123\n456' == '''ABC * |123 * |456'''.stripMargin() * </pre> * * @param self The String to strip the margin from * @return the stripped String * @see #stripMargin(String, char) * @since 1.7.3 */ public static String stripMargin(String self) { return stripMargin(self, '|'); } /** * <p>Strip leading whitespace/control characters followed by '|' from * every line in a CharSequence.</p> * * @param self The CharSequence to strip the margin from * @return the stripped CharSequence * @see #stripMargin(CharSequence, char) * @since 1.8.2 */ public static CharSequence stripMargin(CharSequence self) { return stripMargin(self, '|'); } /** * <p>Strip leading whitespace/control characters followed by <tt>marginChar</tt> from * every line in a String.</p> * * @param self The String to strip the margin from * @param marginChar Any character that serves as margin delimiter * @return the stripped String * @see #stripMargin(String, char) * @since 1.7.3 */ public static String stripMargin(String self, String marginChar) { if (marginChar == null || marginChar.length() == 0) return stripMargin(self, '|'); // TODO IllegalArgumentException for marginChar.length() > 1 ? Or support String as marker? return stripMargin(self, marginChar.charAt(0)); } /** * <p>Strip leading whitespace/control characters followed by <tt>marginChar</tt> from * every line in a CharSequence.</p> * * @param self The CharSequence to strip the margin from * @param marginChar Any character that serves as margin delimiter * @return the stripped CharSequence * @see #stripMargin(String, String) * @since 1.8.2 */ public static String stripMargin(CharSequence self, CharSequence marginChar) { return stripMargin(self.toString(), marginChar.toString()); } /** * <p>Strip leading whitespace/control characters followed by <tt>marginChar</tt> from * every line in a String.</p> * <pre class="groovyTestCase"> * assert 'ABC\n123\n456' == '''ABC * *123 * *456'''.stripMargin('*') * </pre> * * @param self The String to strip the margin from * @param marginChar Any character that serves as margin delimiter * @return the stripped String * @since 1.7.3 */ public static String stripMargin(String self, char marginChar) { if (self.length() == 0) return self; try { StringBuilder builder = new StringBuilder(); for (String line : readLines(self)) { builder.append(stripMarginFromLine(line, marginChar)); builder.append("\n"); } // remove the normalized ending line ending if it was not present if (!self.endsWith("\n")) { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } catch (IOException e) { /* ignore */ } return self; } /** * <p>Strip leading whitespace/control characters followed by <tt>marginChar</tt> from * every line in a String.</p> * * @param self The CharSequence to strip the margin from * @param marginChar Any character that serves as margin delimiter * @return the stripped CharSequence * @see #stripMargin(String, char) * @since 1.8.2 */ public static CharSequence stripMargin(CharSequence self, char marginChar) { return stripMargin(self.toString(), marginChar); } // TODO expose this for stream based stripping? private static String stripMarginFromLine(String line, char marginChar) { int length = line.length(); int index = 0; while (index < length && line.charAt(index) <= ' ') index++; return (index < length && line.charAt(index) == marginChar) ? line.substring(index + 1) : line; } /** * <p>Strip leading spaces from every line in a String. The * line with the least number of leading spaces determines * the number to remove. Lines only containing whitespace are * ignored when calculating the number of leading spaces to strip.</p> * <pre class="groovyTestCase"> * assert ' A\n B\nC' == ' A\n B\n C'.stripIndent() * </pre> * * @param self The String to strip the leading spaces from * @return the stripped String * @see #stripIndent(String, int) * @since 1.7.3 */ public static String stripIndent(String self) { if (self.length() == 0) return self; int runningCount = -1; try { for (String line : readLines(self)) { // don't take blank lines into account for calculating the indent if (isAllWhitespace(line)) continue; if (runningCount == -1) runningCount = line.length(); runningCount = findMinimumLeadingSpaces(line, runningCount); if (runningCount == 0) break; } } catch (IOException e) { /* ignore */ } return stripIndent(self, runningCount == -1 ? 0 : runningCount); } /** * <p>Strip leading spaces from every line in a CharSequence. The * line with the least number of leading spaces determines * the number to remove. Lines only containing whitespace are * ignored when calculating the number of leading spaces to strip.</p> * * @param self The CharSequence to strip the leading spaces from * @return the stripped CharSequence * @see #stripIndent(String) * @since 1.8.2 */ public static CharSequence stripIndent(CharSequence self) { return stripIndent(self.toString()); } /** * True if a String only contains whitespace characters. * * @param self The String to check the characters in * @return true If all characters are whitespace characters * @see Character#isWhitespace(char) * @since 1.6 */ public static boolean isAllWhitespace(String self) { for (int i = 0; i < self.length(); i++) { if (!Character.isWhitespace(self.charAt(i))) return false; } return true; } /** * True if a CharSequence only contains whitespace characters. * * @param self The CharSequence to check the characters in * @return true If all characters are whitespace characters * @see #isAllWhitespace(String) * @since 1.8.2 */ public static boolean isAllWhitespace(CharSequence self) { return isAllWhitespace(self.toString()); } // TODO expose this for stream based scenarios? private static int findMinimumLeadingSpaces(String line, int count) { int length = line.length(); int index = 0; while (index < length && index < count && Character.isWhitespace(line.charAt(index))) index++; return index; } /** * <p>Strip <tt>numChar</tt> leading characters from * every line in a String.</p> * <pre class="groovyTestCase"> * assert 'DEF\n456' == '''ABCDEF\n123456'''.stripIndent(3) * </pre> * * @param self The String to strip the characters from * @param numChars The number of characters to strip * @return the stripped String * @since 1.7.3 */ public static String stripIndent(String self, int numChars) { if (self.length() == 0 || numChars <= 0) return self; try { StringBuilder builder = new StringBuilder(); for (String line : readLines(self)) { // normalize an empty or whitespace line to \n // or strip the indent for lines containing non-space characters if (!isAllWhitespace(line)) { builder.append(stripIndentFromLine(line, numChars)); } builder.append("\n"); } // remove the normalized ending line ending if it was not present if (!self.endsWith("\n")) { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } catch (IOException e) { /* ignore */ } return self; } /** * <p>Strip <tt>numChar</tt> leading characters from * every line in a CharSequence.</p> * * @param self The CharSequence to strip the characters from * @param numChars The number of characters to strip * @return the stripped CharSequence * @since 1.8.2 */ public static CharSequence stripIndent(CharSequence self, int numChars) { return stripIndent(self); } // TODO expose this for stream based stripping? private static String stripIndentFromLine(String line, int numChars) { int length = line.length(); return numChars <= length ? line.substring(numChars) : ""; } /** * 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 CharSequence representing a URL into a URL object. * * @param self the CharSequence representing a URL * @return a URL * @throws MalformedURLException is thrown if the URL is not well formed. * @since 1.8.2 */ public static URL toURL(CharSequence self) throws MalformedURLException { return new URL(self.toString()); } /** * 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); } /** * Transforms a CharSequence representing a URI into a URI object. * * @param self the CharSequence representing a URI * @return a URI * @throws URISyntaxException is thrown if the URI is not well formed. * @since 1.8.2 */ public static URI toURI(CharSequence self) throws URISyntaxException { return new URI(self.toString()); } /** * 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); } /** * Turns a CharSequence into a regular expression Pattern * * @param self a String to convert into a regular expression * @return the regular expression pattern * @since 1.8.2 */ public static Pattern bitwiseNegate(CharSequence self) { return Pattern.compile(self.toString()); } /** * Replaces the first substring of a String that matches the given * compiled regular expression with the given replacement. * <p/> * Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) 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. * <p/> * <pre class="groovyTestCase"> * assert "foo".replaceFirst('o', 'X') == 'fXo' * </pre> * * @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 <tt>String</tt> * @see java.lang.String#replaceFirst(java.lang.String, java.lang.String) * @since 1.6.1 */ public static String replaceFirst(String self, Pattern pattern, String replacement) { return pattern.matcher(self).replaceFirst(replacement); } /** * Replaces the first substring of a CharSequence that matches the given * compiled regular expression with the given replacement. * * @param self the CharSequence that is to be matched * @param pattern the regex Pattern to which the CharSequence of interest is to be matched * @param replacement the CharSequence to be substituted for the first match * @return The resulting <tt>CharSequence</tt> * @see #replaceFirst(String, Pattern, String) * @since 1.8.2 */ public static CharSequence replaceFirst(CharSequence self, Pattern pattern, CharSequence replacement) { return pattern.matcher(self).replaceFirst(replacement.toString()); } /** * Replaces all substrings of a String that match the given * compiled regular expression with the given replacement. * <p> * Note that backslashes (<tt>\</tt>) and dollar signs (<tt>$</tt>) 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. * <p/> * <pre class="groovyTestCase"> * assert "foo".replaceAll('o', 'X') == 'fXX' * </pre> * * @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 <tt>String</tt> * @see java.lang.String#replaceAll(java.lang.String, java.lang.String) * @since 1.6.1 */ public static String replaceAll(String self, Pattern pattern, String replacement) { return pattern.matcher(self).replaceAll(replacement); } /** * Replaces all substrings of a CharSequence that match the given * compiled regular expression with the given replacement. * * @param self the CharSequence that is to be matched * @param pattern the regex Pattern to which the CharSequence of interest is to be matched * @param replacement the CharSequence to be substituted for the first match * @return The resulting <tt>CharSequence</tt> * @see #replaceAll(String, Pattern, String) * @since 1.8.2 */ public static CharSequence replaceAll(CharSequence self, Pattern pattern, CharSequence replacement) { return pattern.matcher(self).replaceAll(replacement.toString()); } /** * Translates a string by replacing characters from the sourceSet with characters from replacementSet. * If the first character from sourceSet appears in the string, it will be replaced with the first character from replacementSet. * If the second character from sourceSet appears in the string, it will be replaced with the second character from replacementSet. * and so on for all provided replacement characters. * <p/> * Here is an example which converts the vowels in a word from lower to uppercase: * <pre> * assert 'hello'.tr('aeiou', 'AEIOU') == 'hEllO' * </pre> * A character range using regex-style syntax can also be used, e.g. here is an example which converts a word from lower to uppercase: * <pre> * assert 'hello'.tr('a-z', 'A-Z') == 'HELLO' * </pre> * Hyphens at the start or end of sourceSet or replacementSet are treated as normal hyphens and are not * considered to be part of a range specification. Similarly, a hyphen immediately after an earlier range * is treated as a normal hyphen. So, '-x', 'x-' have no ranges while 'a-c-e' has the range 'a-c' plus * the '-' character plus the 'e' character. * <p/> * Unlike the unix tr command, Groovy's tr command supports reverse ranges, e.g.: * <pre> * assert 'hello'.tr('z-a', 'Z-A') == 'HELLO' * </pre> * If replacementSet is smaller than sourceSet, then the last character from replacementSet is used as the replacement for all remaining source characters as shown here: * <pre> * assert 'Hello World!'.tr('a-z', 'A') == 'HAAAA WAAAA!' * </pre> * If sourceSet contains repeated characters, the last specified replacement is used as shown here: * <pre> * assert 'Hello World!'.tr('lloo', '1234') == 'He224 W4r2d!' * </pre> * The functionality provided by tr can be achieved using regular expressions but tr provides a much more compact * notation and efficient implementation for certain scenarios. * * @param self the string that is to be translated * @param sourceSet the set of characters to translate from * @param replacementSet the set of replacement characters * @return The resulting translated <tt>String</tt> * @see org.codehaus.groovy.util.StringUtil#tr(String, String, String) * @since 1.7.3 */ public static String tr(final String self, String sourceSet, String replacementSet) throws ClassNotFoundException { return (String) InvokerHelper.invokeStaticMethod("org.codehaus.groovy.util.StringUtil", "tr", new Object[]{self, sourceSet, replacementSet}); } /** * Translates a string by replacing characters from the sourceSet with characters from replacementSet. * * @param self the CharSequence that is to be translated * @param sourceSet the set of characters to translate from * @param replacementSet the set of replacement characters * @return The resulting translated <tt>CharSequence</tt> * @see #tr(String, String, String) * @since 1.8.2 */ public static CharSequence tr(final CharSequence self, CharSequence sourceSet, CharSequence replacementSet) throws ClassNotFoundException { return tr(self.toString(), sourceSet.toString(), replacementSet.toString()); } /** * 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 true if the string matches * @see java.lang.String#matches(java.lang.String) * @since 1.6.1 */ public static boolean matches(String self, Pattern pattern) { return pattern.matcher(self).matches(); } /** * Tells whether or not a CharSequence matches the given * compiled regular expression Pattern. * * @param self the CharSequence that is to be matched * @param pattern the regex Pattern to which the string of interest is to be matched * @return true if the CharSequence matches * @see java.lang.String#matches(java.lang.String) * @since 1.8.2 */ public static boolean matches(CharSequence 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. * <p/> * <p> For example, if the regex doesn't match the result is null: * <pre> * assert null == "New York, NY".find(/\d{5}/) * </pre> * </p> * <p> If it does match, we get the matching string back: * <pre> * assert "10292" == "New York, NY 10292-0098".find(/\d{5}/) * </pre> * </p> * <p> If we have capture groups in our expression, we still get back the full match * <pre> * assert "10292-0098" == "New York, NY 10292-0098".find(/(\d{5})-?(\d{4})/) * </pre> * </p> * * @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 regular expression CharSequence within a CharSequence. * * @param self a CharSequence * @param regex the capturing regex * @return a CharSequence containing the matched portion, or null if the regex doesn't match * @see #find(String, Pattern) * @since 1.8.2 */ public static CharSequence find(CharSequence self, CharSequence regex) { return find(self.toString(), Pattern.compile(regex.toString())); } /** * Finds the first occurrence of a compiled regular expression Pattern within a String. * If the pattern doesn't match, null will be returned. * <p/> * <p> For example, if the pattern doesn't match the result is null: * <pre> * assert null == "New York, NY".find(~/\d{5}/) * </pre> * </p> * <p> If it does match, we get the matching string back: * <pre> * assert "10292" == "New York, NY 10292-0098".find(~/\d{5}/) * </pre> * </p> * <p> If we have capture groups in our expression, the groups are ignored and * we get back the full match: * <pre> * assert "10292-0098" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/) * </pre> * If you need to work with capture groups, then use the closure version * of this method or use Groovy's matcher operators or use <tt>eachMatch</tt>. * </p> * * @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 compiled regular expression Pattern within a CharSequence. * * @param self a CharSequence * @param pattern the compiled regex Pattern * @return a CharSequence containing the matched portion, or null if the regex pattern doesn't match * @see #find(String, Pattern) * @since 1.8.2 */ public static CharSequence find(CharSequence self, Pattern pattern) { return find(self.toString(), pattern); } /** * Returns the result of calling a closure with the first occurrence of a regular expression found within a String. * If the regex doesn't match, the closure will not be called and find will return null. * <p/> * <p> For example, if the regex doesn't match, the result is null: * <pre> * assert null == "New York, NY".find(~/\d{5}/) { match -> return "-$match-"} * </pre> * </p> * <p> 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: * <pre> * assert "-10292-" == "New York, NY 10292-0098".find(~/\d{5}/) { match -> return "-$match-"} * </pre> * </p> * <p> 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: * <pre> * 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 * } * </pre> * <p> 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: * <pre> * 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] * } * </pre> * <p> 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: * <pre> * 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" * } * </pre> * </p> * * @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); } /** * Returns the result of calling a closure with the first occurrence of a regular expression found within a CharSequence. * If the regex doesn't match, the closure will not be called and find will return null. * * @param self a CharSequence * @param regex the capturing regex CharSequence * @param closure the closure that will be passed the full match, plus each of the capturing groups * @return a CharSequence containing the result of the closure, or null if the regex pattern doesn't match * @see #find(String, Pattern, Closure) * @since 1.8.2 */ public static CharSequence find(CharSequence self, CharSequence regex, Closure closure) { return find(self.toString(), Pattern.compile(regex.toString()), closure); } /** * Returns the result of calling a closure with the first occurrence of a compiled regular expression found within a String. * If the regex doesn't match, the closure will not be called and find will return null. * <p/> * <p> For example, if the pattern doesn't match, the result is null: * <pre> * assert null == "New York, NY".find(~/\d{5}/) { match -> return "-$match-"} * </pre> * </p> * <p> 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: * <pre> * assert "-10292-" == "New York, NY 10292-0098".find(~/\d{5}/) { match -> return "-$match-"} * </pre> * </p> * <p> 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: * <pre> * 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 * } * </pre> * <p> 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: * <pre> * 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] * } * </pre> * <p> 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: * <pre> * 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" * } * </pre> * </p> * * @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; } /** * Returns the result of calling a closure with the first occurrence of a regular expression found within a * CharSequence. If the regex doesn't match, the closure will not be called and find will return null. * * @param self a CharSequence * @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 CharSequence containing the result of the closure, or null if the regex pattern doesn't match * @see #find(String, java.util.regex.Pattern, groovy.lang.Closure) * @since 1.8.2 */ public static CharSequence find(CharSequence self, Pattern pattern, Closure closure) { return find(self.toString(), pattern, closure); } /** * Returns a (possibly empty) list of all occurrences of a regular expression (in String format) found within a String. * <p/> * <p>For example, if the regex doesn't match, it returns an empty list: * <pre> * assert [] == "foo".findAll(/(\w*) Fish/) * </pre> * <p>Any regular expression matches are returned in a list, and all regex capture groupings are ignored, only the full match is returned: * <pre> * def expected = ["One Fish", "Two Fish", "Red Fish", "Blue Fish"] * assert expected == "One Fish, Two Fish, Red Fish, Blue Fish".findAll(/(\w*) Fish/) * </pre> * If you need to work with capture groups, then use the closure version * of this method or use Groovy's matcher operators or use <tt>eachMatch</tt>. * </p> * * @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<String> findAll(String self, String regex) { return findAll(self, Pattern.compile(regex)); } /** * Returns a (possibly empty) list of all occurrences of a regular expression (in CharSequence format) found within a CharSequence. * * @param self a CharSequence * @param regex the capturing regex CharSequence * @return a List containing all full matches of the regex within the CharSequence, an empty list will be returned if there are no matches * @see #findAll(String, String) * @since 1.8.2 */ public static List<CharSequence> findAll(CharSequence self, CharSequence regex) { return new ArrayList<CharSequence>(findAll(self.toString(), regex.toString())); } /** * Returns a (possibly empty) list of all occurrences of a regular expression (in Pattern format) found within a String. * <p/> * <p>For example, if the pattern doesn't match, it returns an empty list: * <pre> * assert [] == "foo".findAll(~/(\w*) Fish/) * </pre> * <p>Any regular expression matches are returned in a list, and all regex capture groupings are ignored, only the full match is returned: * <pre> * def expected = ["One Fish", "Two Fish", "Red Fish", "Blue Fish"] * assert expected == "One Fish, Two Fish, Red Fish, Blue Fish".findAll(~/(\w*) Fish/) * </pre> * * @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<String> findAll(String self, Pattern pattern) { Matcher matcher = pattern.matcher(self); List<String> list = new ArrayList<String>(); for (Iterator iter = iterator(matcher); iter.hasNext();) { if (hasGroup(matcher)) { list.add((String) ((List) iter.next()).get(0)); } else { list.add((String) iter.next()); } } return list; } /** * Returns a (possibly empty) list of all occurrences of a regular expression (in Pattern format) found within a CharSequence. * * @param self a CharSequence * @param pattern the compiled regex Pattern * @return a List containing all full matches of the Pattern within the CharSequence, an empty list will be returned if there are no matches * @see #findAll(String, Pattern) * @since 1.8.2 */ public static List<CharSequence> findAll(CharSequence self, Pattern pattern) { return new ArrayList<CharSequence>(findAll(self.toString(), pattern)); } /** * 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. * <p/> * If there are no matches, the closure will not be called, and an empty List will be returned. * <p/> * <p>For example, if the regex doesn't match, it returns an empty list: * <pre> * assert [] == "foo".findAll(/(\w*) Fish/) { match, firstWord -> return firstWord } * </pre> * <p>Any regular expression matches are passed to the closure, if there are no capture groups, there will be one parameter for the match: * <pre> * assert ["couldn't", "wouldn't"] == "I could not, would not, with a fox.".findAll(/.ould/) { match -> "${match}n't"} * </pre> * <p>If there are capture groups, the first parameter will be the match followed by one parameter for each capture group: * <pre> * def orig = "There's a Wocket in my Pocket" * assert ["W > Wocket", "P > Pocket"] == orig.findAll(/(.)ocket/) { match, firstLetter -> "$firstLetter > $match" } * </pre> * * @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 <T> List<T> findAll(String self, String regex, Closure<T> closure) { return findAll(self, Pattern.compile(regex), closure); } /** * Finds all occurrences of a capturing regular expression CharSequence within a CharSequence. * * @param self a CharSequence * @param regex the capturing regex CharSequence * @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 CharSequence, an empty list will be returned if there are no matches * @see #findAll(String, String, Closure) * @since 1.8.2 */ public static <T> List<T> findAll(CharSequence self, CharSequence regex, Closure<T> closure) { return findAll(self.toString(), regex.toString(), 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. * <p/> * If there are no matches, the closure will not be called, and an empty List will be returned. * <p/> * <p>For example, if the pattern doesn't match, it returns an empty list: * <pre> * assert [] == "foo".findAll(~/(\w*) Fish/) { match, firstWord -> return firstWord } * </pre> * <p>Any regular expression matches are passed to the closure, if there are no capture groups, there will be one parameter for the match: * <pre> * assert ["couldn't", "wouldn't"] == "I could not, would not, with a fox.".findAll(~/.ould/) { match -> "${match}n't"} * </pre> * <p>If there are capture groups, the first parameter will be the match followed by one parameter for each capture group: * <pre> * def orig = "There's a Wocket in my Pocket" * assert ["W > Wocket", "P > Pocket"] == orig.findAll(~/(.)ocket/) { match, firstLetter -> "$firstLetter > $match" } * </pre> * * @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 <T> List<T> findAll(String self, Pattern pattern, Closure<T> closure) { Matcher matcher = pattern.matcher(self); return collect(matcher, closure); } /** * Finds all occurrences of a compiled regular expression Pattern within a CharSequence. * * @param self a CharSequence * @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 CharSequence, an empty list will be returned if there are no matches * @see #findAll(String, Pattern, Closure) * @since 1.8.2 */ public static <T> List<T> findAll(CharSequence self, Pattern pattern, Closure<T> closure) { return findAll(self.toString(), pattern, closure); } /** * Replaces all occurrences of a captured group by the result of a closure on that text. * <p/> * <p> For examples, * <pre> * assert "hellO wOrld" == "hello world".replaceAll("(o)") { it[0].toUpperCase() } * <p/> * assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { Object[] it -> it[0].toUpperCase() }) * <p/> * 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 * <p/> * <p/> * assert "FOO-FOO-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { x, y, z -> z.toUpperCase() }) * <p/> * Here, * x is the global string of the matched group * y is the first string in the matched group * z is the second string in the matched group * </pre> * <p>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.</p> * * @param self a String * @param regex the capturing regex * @param closure the closure to apply on each captured group * @return a String with replaced content * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @since 1.0 * @see java.util.regex.Matcher#quoteReplacement(java.lang.String) * @see #replaceAll(String, Pattern, Closure) */ public static String replaceAll(final String self, final String regex, final Closure closure) { return replaceAll(self, Pattern.compile(regex), closure); } /** * Replaces all occurrences of a captured group by the result of a closure on that text. * * @param self a CharSequence * @param regex the capturing regex * @param closure the closure to apply on each captured group * @return a CharSequence with replaced content * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @since 1.8.2 * @see #replaceAll(String, Pattern, Closure) */ public static CharSequence replaceAll(final CharSequence self, final CharSequence regex, final Closure closure) { return replaceAll(self.toString(), Pattern.compile(regex.toString()), closure); } /** * Replaces each substring of this CharSequence that matches the given * regular expression with the given replacement. * * @param self a CharSequence * @param regex the capturing regex * @param replacement the capturing regex * @return a CharSequence with replaced content * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @see String#replaceAll(String, String) * @since 1.8.2 */ public static CharSequence replaceAll(final CharSequence self, final CharSequence regex, final CharSequence replacement) { return self.toString().replaceAll(regex.toString(), replacement.toString()); } /** * Replaces the first occurrence of a captured group by the result of a closure call on that text. * <p/> * <p> For example (with some replaceAll variants thrown in for comparison purposes), * <pre> * assert "hellO world" == "hello world".replaceFirst("(o)") { it[0].toUpperCase() } // first match * assert "hellO wOrld" == "hello world".replaceAll("(o)") { it[0].toUpperCase() } // all matches * <p/> * assert '1-FISH, two fish' == "one fish, two fish".replaceFirst(/([a-z]{3})\s([a-z]{4})/) { [one:1, two:2][it[1]] + '-' + it[2].toUpperCase() } * assert '1-FISH, 2-FISH' == "one fish, two fish".replaceAll(/([a-z]{3})\s([a-z]{4})/) { [one:1, two:2][it[1]] + '-' + it[2].toUpperCase() } * </pre> * * @param self a String * @param regex the capturing regex * @param closure the closure to apply on the first captured group * @return a String with replaced content * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @since 1.7.7 * @see java.util.regex.Matcher#quoteReplacement(java.lang.String) * @see #replaceFirst(String, Pattern, Closure) */ public static String replaceFirst(final String self, final String regex, final Closure closure) { return replaceFirst(self, Pattern.compile(regex), closure); } /** * Replaces the first substring of this CharSequence that matches the given * regular expression with the given replacement. * * @param self a CharSequence * @param regex the capturing regex * @param replacement the capturing regex * @return a CharSequence with replaced content * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @see String#replaceAll(String, String) * @since 1.8.2 */ public static String replaceFirst(final CharSequence self, final CharSequence regex, final CharSequence replacement) { return self.toString().replaceFirst(regex.toString(), replacement.toString()); } /** * Replaces the first occurrence of a captured group by the result of a closure call on that text. * * @param self a CharSequence * @param regex the capturing regex * @param closure the closure to apply on the first captured group * @return a CharSequence with replaced content * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @see #replaceFirst(String, String, Closure) * @since 1.8.2 */ public static String replaceFirst(final CharSequence self, final CharSequence regex, final Closure closure) { return replaceFirst(self.toString(), regex.toString(), closure); } /** * Get a replacement corresponding to the matched pattern for {@link org.codehaus.groovy.runtime.DefaultGroovyMethods#replaceAll(String, Pattern, Closure)}. * The closure take parameter: * <ul> * <li>Whole of match if the pattern include no capturing group</li> * <li>Object[] of capturing groups if the closure takes Object[] as parameter</li> * <li>List of capturing groups</li> * </ul> * * @param matcher the matcher object used for matching * @param closure specified with replaceAll() to get replacement * @return replacement correspond replacement for a match */ private static String getReplacement(Matcher matcher, Closure closure) { if (!hasGroup(matcher)) { return InvokerHelper.toString(closure.call(matcher.group())); } int count = matcher.groupCount(); List<String> groups = new ArrayList<String>(); for (int i = 0; i <= count; i++) { groups.add(matcher.group(i)); } if (closure.getParameterTypes().length == 1 && closure.getParameterTypes()[0] == Object[].class) { return InvokerHelper.toString(closure.call(groups.toArray())); } return InvokerHelper.toString(closure.call(groups)); } /** * Replaces all occurrences of a captured group by the result of a closure call on that text. * <p/> * <p> For examples, * <pre> * assert "hellO wOrld" == "hello world".replaceAll(~"(o)") { it[0].toUpperCase() } * <p/> * assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll(~"(([fF][oO]{2})[bB]ar)", { it[0].toUpperCase() }) * <p/> * 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 * <p/> * <p/> * assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll(~"(([fF][oO]{2})[bB]ar)", { Object[] it -> it[0].toUpperCase() }) * <p/> * 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 * <p/> * <p/> * assert "FOO-FOO-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { x, y, z -> z.toUpperCase() }) * <p/> * Here, * x is the global string of the matched group * y is the first string in the matched group * z is the second string in the matched group * </pre> * <p>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.</p> * * @param self a String * @param pattern the capturing regex Pattern * @param closure the closure to apply on each captured group * @return a String with replaced content * @since 1.6.8 * @see java.util.regex.Matcher#quoteReplacement(java.lang.String) */ public static String replaceAll(final String self, final Pattern pattern, final Closure closure) { final Matcher matcher = pattern.matcher(self); if (matcher.find()) { final StringBuffer sb = new StringBuffer(self.length() + 16); do { String replacement = getReplacement(matcher, closure); matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); } while (matcher.find()); matcher.appendTail(sb); return sb.toString(); } else { return self; } } /** * Replaces all occurrences of a captured group by the result of a closure call on that text. * * @param self a CharSequence * @param pattern the capturing regex Pattern * @param closure the closure to apply on each captured group * @return a CharSequence with replaced content * @since 1.8.2 * @see #replaceAll(String, Pattern, Closure) */ public static String replaceAll(final CharSequence self, final Pattern pattern, final Closure closure) { return replaceAll(self.toString(), pattern, closure); } /** * Replaces the first occurrence of a captured group by the result of a closure call on that text. * <p/> * <p> For example (with some replaceAll variants thrown in for comparison purposes), * <pre> * assert "hellO world" == "hello world".replaceFirst(~"(o)") { it[0].toUpperCase() } // first match * assert "hellO wOrld" == "hello world".replaceAll(~"(o)") { it[0].toUpperCase() } // all matches * <p/> * assert '1-FISH, two fish' == "one fish, two fish".replaceFirst(~/([a-z]{3})\s([a-z]{4})/) { [one:1, two:2][it[1]] + '-' + it[2].toUpperCase() } * assert '1-FISH, 2-FISH' == "one fish, two fish".replaceAll(~/([a-z]{3})\s([a-z]{4})/) { [one:1, two:2][it[1]] + '-' + it[2].toUpperCase() } * </pre> * * @param self a String * @param pattern the capturing regex Pattern * @param closure the closure to apply on the first captured group * @return a String with replaced content * @since 1.7.7 * @see #replaceAll(String, Pattern, Closure) */ public static String replaceFirst(final String self, final Pattern pattern, final Closure closure) { final Matcher matcher = pattern.matcher(self); if (matcher.find()) { final StringBuffer sb = new StringBuffer(self.length() + 16); String replacement = getReplacement(matcher, closure); matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); matcher.appendTail(sb); return sb.toString(); } else { return self; } } /** * Replaces the first occurrence of a captured group by the result of a closure call on that text. * * @param self a CharSequence * @param pattern the capturing regex Pattern * @param closure the closure to apply on the first captured group * @return a CharSequence with replaced content * @see #replaceFirst(String, Pattern, Closure) * @since 1.8.2 */ public static String replaceFirst(final CharSequence self, final Pattern pattern, final Closure closure) { return replaceFirst(self.toString(), pattern, closure); } 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 to a minimum length specified by <tt>numberOfChars</tt>, adding the supplied padding String as many times as needed to the left. * * If the String is already the same size or bigger than the target <tt>numberOfChars</tt>, then the original String is returned. An example: * <pre> * println 'Numbers:' * [1, 10, 100, 1000].each{ println it.toString().padLeft(5, '*') } * [2, 20, 200, 2000].each{ println it.toString().padLeft(5, '*_') } * </pre> * will produce output like: * <pre> * Numbers: * ****1 * ***10 * **100 * *1000 * *_*_2 * *_*20 * *_200 * *2000 * </pre> * * @param self a String object * @param numberOfChars the total minimum number of characters of the resulting string * @param padding the characters 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 CharSequence to a minimum length specified by <tt>numberOfChars</tt>, adding the supplied padding CharSequence as many times as needed to the left. * * @param self a CharSequence object * @param numberOfChars the total minimum number of characters of the resulting CharSequence * @param padding the characters used for padding * @return the CharSequence padded to the left * @see #padLeft(String, Number, String) * @since 1.8.2 */ public static CharSequence padLeft(CharSequence self, Number numberOfChars, CharSequence padding) { return padLeft(self.toString(), numberOfChars, padding.toString()); } /** * Pad a String to a minimum length specified by <tt>numberOfChars</tt> by adding the space character to the left as many times as needed. * * If the String is already the same size or bigger than the target <tt>numberOfChars</tt>, then the original String is returned. An example: * <pre> * println 'Numbers:' * [1, 10, 100, 1000].each{ println it.toString().padLeft(5) } * </pre> * will produce output like: * <pre> * Numbers: * 1 * 10 * 100 * 1000 * </pre> * * @param self a String object * @param numberOfChars the total minimum number of characters of the resulting string * @return the String padded to the left * @see #padLeft(String, Number, String) * @since 1.0 */ public static String padLeft(String self, Number numberOfChars) { return padLeft(self, numberOfChars, " "); } /** * Pad a CharSequence to a minimum length specified by <tt>numberOfChars</tt> by adding the space character to the left as many times as needed. * * @param self a CharSequence object * @param numberOfChars the total minimum number of characters of the resulting CharSequence * @return the CharSequence padded to the left * @see #padLeft(CharSequence, Number, CharSequence) * @since 1.8.2 */ public static CharSequence padLeft(CharSequence self, Number numberOfChars) { return padLeft(self, numberOfChars, " "); } /** * Pad a String to a minimum length specified by <tt>numberOfChars</tt>, adding the supplied padding String as many times as needed to the right. * * If the String is already the same size or bigger than the target <tt>numberOfChars</tt>, then the original String is returned. An example: * <pre> * ['A', 'BB', 'CCC', 'DDDD'].each{ println it.padRight(5, '#') + it.size() } * </pre> * will produce output like: * <pre> * A####1 * BB###2 * CCC##3 * DDDD#4 * </pre> * * @param self a String object * @param numberOfChars the total minimum number of characters of the resulting string * @param padding the characters 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 CharSequence to a minimum length specified by <tt>numberOfChars</tt>, adding the supplied padding CharSequence as many times as needed to the right. * * @param self a CharSequence object * @param numberOfChars the total minimum number of characters of the resulting CharSequence * @param padding the characters used for padding * @return the CharSequence padded to the right * @see #padRight(String, Number, String) * @since 1.8.2 */ public static CharSequence padRight(CharSequence self, Number numberOfChars, CharSequence padding) { return padRight(self.toString(), numberOfChars, padding.toString()); } /** * Pad a String to a minimum length specified by <tt>numberOfChars</tt> by adding the space character to the right as many times as needed. * * If the String is already the same size or bigger than the target <tt>numberOfChars</tt>, then the original String is returned. An example: * <pre> * ['A', 'BB', 'CCC', 'DDDD'].each{ println it.padRight(5) + it.size() } * </pre> * will produce output like: * <pre> * A 1 * BB 2 * CCC 3 * DDDD 4 * </pre> * * @param self a String object * @param numberOfChars the total minimum number of characters of the resulting string * @return the String padded to the right * @since 1.0 */ public static String padRight(String self, Number numberOfChars) { return padRight(self, numberOfChars, " "); } /** * Pad a CharSequence to a minimum length specified by <tt>numberOfChars</tt> by adding the space character to the right as many times as needed. * * @param self a CharSequence object * @param numberOfChars the total minimum number of characters of the resulting string * @return the CharSequence padded to the right * @see #padRight(String, Number) * @since 1.8.2 */ public static CharSequence padRight(CharSequence self, Number numberOfChars) { return padRight(self.toString(), numberOfChars); } /** * Pad a String to a minimum length specified by <tt>numberOfChars</tt>, appending the supplied padding String around the original as many times as needed keeping it centered. * * If the String is already the same size or bigger than the target <tt>numberOfChars</tt>, then the original String is returned. An example: * <pre> * ['A', 'BB', 'CCC', 'DDDD'].each{ println '|' + it.center(6, '+') + '|' } * </pre> * will produce output like: * <pre> * |++A+++| * |++BB++| * |+CCC++| * |+DDDD+| * </pre> * * @param self a String object * @param numberOfChars the total minimum number of characters of the resulting string * @param padding the characters used for padding * @return the String centered with padded characters around it * @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; } } /** * Pad a CharSequence to a minimum length specified by <tt>numberOfChars</tt>, appending the supplied padding CharSequence around the original as many times as needed keeping it centered. * * @param self a CharSequence object * @param numberOfChars the total minimum number of characters of the resulting CharSequence * @param padding the characters used for padding * @return the CharSequence centered with padded characters around it * @see #center(String, Number, String) * @since 1.8.2 */ public static CharSequence center(CharSequence self, Number numberOfChars, CharSequence padding) { return center(self.toString(), numberOfChars, padding.toString()); } /** * Pad a String to a minimum length specified by <tt>numberOfChars</tt> by adding the space character around it as many times as needed so that it remains centered. * * If the String is already the same size or bigger than the target <tt>numberOfChars</tt>, then the original String is returned. An example: * <pre> * ['A', 'BB', 'CCC', 'DDDD'].each{ println '|' + it.center(6) + '|' } * </pre> * will produce output like: * <pre> * | A | * | BB | * | CCC | * | DDDD | * </pre> * * @param self a String object * @param numberOfChars the total minimum number of characters of the resulting string * @return the String centered with padded characters around it * @see #center(String, Number, String) * @since 1.0 */ public static String center(String self, Number numberOfChars) { return center(self, numberOfChars, " "); } /** * Pad a CharSequence to a minimum length specified by <tt>numberOfChars</tt> by adding the space character around it as many times as needed so that it remains centered. * * @param self a CharSequence object * @param numberOfChars the total minimum number of characters of the resulting CharSequence * @return the CharSequence centered with padded characters around it * @see #center(String, Number) * @since 1.8.2 */ public static CharSequence center(CharSequence self, Number numberOfChars) { return center(self.toString(), numberOfChars); } /** * Support the subscript operator, e.g. matcher[index], for a regex Matcher. * <p/> * For an example using no group match, * <pre> * 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] * } * </pre> * <p/> * For an example using group matches, * <pre> * 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] * } * </pre> * <p/> * For another example using group matches, * <pre> * 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] * } * </pre> * * @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 <code>true</code> 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. * <pre class="groovyTestCase">def list = [1, "a", 4.5, true] * assert list[1..2] == ["a", 4.5]</pre> * * @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 <T> List<T> getAt(List<T> self, Range range) { RangeInfo info = subListBorders(self.size(), range); List<T> answer = self.subList(info.from, info.to); // sublist is always exclusive, but Ranges are not if (info.reverse) { answer = reverse(answer); } return answer; } /** * Support the range subscript operator for a List. * <pre class="groovyTestCase">def list = [true, 1, 3.4] * assert list[0..<0] == []</pre> * * @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 <T> List<T> getAt(List<T> self, EmptyRange range) { return new ArrayList<T> (); } /** * Select a List of items from a List using a Collection to * identify the indices to be selected. * <pre class="groovyTestCase">def list = [true, 1, 3.4, false] * assert list[1,0,2] == [1, true, 3.4]</pre> * * @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 <T> List<T> getAt(List<T> self, Collection indices) { List<T> answer = new ArrayList<T>(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 <T> List<T> getAt(T[] self, Collection indices) { List<T> answer = new ArrayList<T>(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. * <pre class="groovyTestCase">assert [1:10, 2:20, 4:40].subMap( [2, 4] ) == [2:20, 4:40]</pre> * * @param map a Map * @param keys a Collection of keys * @return a new Map containing the given keys * @since 1.0 */ public static <K, V> Map<K, V> subMap(Map<K, V> map, Collection<K> keys) { Map<K, V> answer = new LinkedHashMap<K, V>(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. * <pre class="groovyTestCase">def map=[:] * map.get("a", []) << 5 * assert map == [a:[5]]</pre> * * @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 <K, V> V get(Map<K, V> 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 <T> List<T> getAt(T[] array, Range range) { List<T> 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 <T> List<T> getAt(T[] array, IntRange range) { List<T> 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 <T> List<T> getAt(T[] array, EmptyRange range) { return new ArrayList<T>(); } /** * * @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 <T> List<T> getAt(T[] array, ObjectRange range) { List<T> list = Arrays.asList(array); return getAt(list, range); } private static <T> 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 <T> List<T> toList(T[] array) { return new ArrayList<T>(Arrays.asList(array)); } /** * Support the subscript operator for a List. * <pre class="groovyTestCase">def list = [2, "a", 5.3] * assert list[1] == "a"</pre> * * @param self a List * @param idx an index * @return the value at the given index * @since 1.0 */ public static <T> T getAt(List<T> 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 an Iterator. The iterator * will be partially exhausted up until the idx entry after returning * if a +ve or 0 idx is used, or fully exhausted if a -ve idx is used * or no corresponding entry was found. Typical usage: * <pre class="groovyTestCase"> * def iter = [2, "a", 5.3].iterator() * assert iter[1] == "a" * </pre> * A more elaborate example: * <pre class="groovyTestCase"> * def items = [2, "a", 5.3] * def iter = items.iterator() * assert iter[-1] == 5.3 * // iter exhausted, so reset * iter = items.iterator() * assert iter[1] == "a" * // iter partially exhausted so now idx starts after "a" * assert iter[0] == 5.3 * </pre> * * @param self an Iterator * @param idx an index value (-self.size() <= idx < self.size()) * @return the value at the given index (after normalisation) or null if no corresponding value was found * @since 1.7.2 */ public static <T> T getAt(Iterator<T> self, int idx) { if (idx < 0) { // calculate whole list in this case // recommend avoiding -ve's as this is not as efficient List<T> list = toList(self); int adjustedIndex = idx + list.size(); if (adjustedIndex < 0 || adjustedIndex >= list.size()) return null; return list.get(adjustedIndex); } int count = 0; while (self.hasNext()) { if (count == idx) { return self.next(); } else { count++; self.next(); } } return null; } /** * A helper method to allow lists to work with subscript operators. * <pre class="groovyTestCase">def list = [2, 3] * list[0] = 1 * assert list == [1, 3]</pre> * * @param self a List * @param idx an index * @param value the value to put at the given index * @since 1.0 */ public static <T> void putAt(List<T> 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. * <pre class="groovyTestCase">def list = ["a", true] * list[1..<1] = 5 * assert list == ["a", 5, true]</pre> * * @param self a List * @param range the (in this case empty) 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); } } /** * A helper method to allow lists to work with subscript operators. * <pre class="groovyTestCase">def list = ["a", true] * list[1..<1] = [4, 3, 2] * assert list == ["a", 4, 3, 2, true]</pre> * * @param self a List * @param range the (in this case empty) subset of the list to set * @param value the Collection of values * @since 1.0 * @see #putAt(java.util.List, groovy.lang.EmptyRange, java.lang.Object) */ public static void putAt(List self, EmptyRange range, Collection value) { putAt(self, range, (Object)value); } private static <T> List<T> resizeListWithRangeAndGetSublist(List<T> 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<T> 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: <pre class="groovyTestCase">def myList = [4, 3, 5, 1, 2, 8, 10] * myList[3..5] = ["a", true] * assert myList == [4, 3, 5, "a", true, 10]</pre> * * Items in the given * range are replaced 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: <pre class="groovyTestCase">def myList = [4, 3, 5, 1, 2, 8, 10] * myList[3..5] = "b" * assert myList == [4, 3, 5, "b", 10]</pre> * * Items in the given * range are replaced with the operand. The <code>value</code> 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. * <pre class="groovyTestCase">def list = ["a", true, 42, 9.4] * list[1, 4] = ["x", false] * assert list == ["a", "x", 42, 9.4, false]</pre> * * @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. * <pre class="groovyTestCase">def list = ["a", true, 42, 9.4] * list[1, 3] = 5 * assert list == ["a", 5, 42, 5]</pre> * * @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.get(0); 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. * <pre class="groovyTestCase">def map = [a:10] * assert map["a"] == 10</pre> * * @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 <K,V> V getAt(Map<K,V> self, K key) { return self.get(key); } /** * Returns a new <code>Map</code> containing all entries from <code>left</code> and <code>right</code>, * giving precedence to <code>right</code>. Any keys appearing in both Maps * will appear in the resultant map with values from the <code>right</code> * operand. If the <code>left</code> map is one of TreeMap, LinkedHashMap, Hashtable * or Properties, the returned Map will preserve that type, otherwise a HashMap will * be returned. * </p> * <p/> * Roughly equivalent to <code>Map m = new HashMap(); m.putAll(left); m.putAll(right); return m;</code> * but with some additional logic to preserve the <code>left</code> Map type for common cases as * described above. * </p> * <pre class="groovyTestCase">assert [a:10, b:20] + [a:5, c:7] == [a:5, b:20, c:7]</pre> * * @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 <K, V> Map<K, V> plus(Map<K, V> left, Map<K, V> right) { Map<K, V> map = cloneSimilarMap(left); map.putAll(right); return map; } /** * A helper method to allow maps 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 <K,V> V putAt(Map<K,V> self, K key, V value) { self.put(key, value); return value; } /** * Support the subscript operator for Collection. * <pre class="groovyTestCase">assert [String, Long, Integer] == ["a",5L,2]["class"]</pre> * * @param coll a Collection * @param property a String * @return a List * @since 1.0 */ public static List getAt(Collection coll, String property) { List<Object> answer = new ArrayList<Object>(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 <K,V> Map<K,V> asImmutable(Map<? extends K, ? extends V> 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 <K,V> SortedMap<K,V> asImmutable(SortedMap<K, ? extends V> 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 <T> List<T> asImmutable(List<? extends T> 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 <T> Set<T> asImmutable(Set<? extends T> 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 <T> SortedSet<T> asImmutable(SortedSet<T> self) { return Collections.unmodifiableSortedSet(self); } /** * A convenience method for creating an immutable Collection. * <pre class="groovyTestCase">def mutable = [1,2,3] * def immutable = mutable.asImmutable() * mutable << 4 * try { * immutable << 4 * assert false * } catch (UnsupportedOperationException) { * assert true * }</pre> * * @param self a Collection * @return an immutable Collection * @see java.util.Collections#unmodifiableCollection(java.util.Collection) * @since 1.5.0 */ public static <T> Collection<T> asImmutable(Collection<? extends T> 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 <K,V> Map<K,V> asSynchronized(Map<K,V> 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 <K,V> SortedMap<K,V> asSynchronized(SortedMap<K,V> 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 <T> Collection<T> asSynchronized(Collection<T> 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 <T> List<T> asSynchronized(List<T> 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 <T> Set<T> asSynchronized(Set<T> 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 <T> SortedSet<T> asSynchronized(SortedSet<T> self) { return Collections.synchronizedSortedSet(self); } /** * Synonym for {@link #toSpreadMap(java.util.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 <code>SpreadMap</code> from this map. * <p/> * The example below shows the various possible use cases: * <pre class="groovyTestCase"> * def fn(Map m) { return m.a + m.b + m.c + m.d } * * assert fn(a:1, b:2, c:3, d:4) == 10 * assert fn(a:1, *:[b:2, c:3], d:4) == 10 * assert fn([a:1, b:2, c:3, d:4].toSpreadMap()) == 10 * assert fn((['a', 1, 'b', 2, 'c', 3, 'd', 4] as Object[]).toSpreadMap()) == 10 * assert fn(['a', 1, 'b', 2, 'c', 3, 'd', 4].toSpreadMap()) == 10 * assert fn(['abcd'.toList(), 1..4].transpose().flatten().toSpreadMap()) == 10 * </pre> * Note that toSpreadMap() is not normally used explicitly but under the covers by Groovy. * * @param self a map to be converted into a SpreadMap * @return a newly created SpreadMap if this map 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. * <p/> * @param self an object array * @return a newly created SpreadMap * @see groovy.lang.SpreadMap#SpreadMap(java.lang.Object[]) * @see #toSpreadMap(java.util.Map) * @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); } /** * Creates a spreadable map from this list. * <p/> * @param self a list * @return a newly created SpreadMap * @see groovy.lang.SpreadMap#SpreadMap(java.util.List) * @see #toSpreadMap(java.util.Map) * @since 1.8.0 */ public static SpreadMap toSpreadMap(List self) { if (self == null) throw new GroovyRuntimeException("Fail to convert List to SpreadMap, because it is null."); else if (self.size() % 2 != 0) throw new GroovyRuntimeException("Fail to convert List to SpreadMap, because it's size is not even."); else return new SpreadMap(self); } /** * Wraps a map using the delegate pattern with a wrapper that intercepts all calls * to <code>get(key)</code>. If an unknown key is found, a default value will be * stored into the Map before being returned. The default value stored will be the * result of calling the supplied Closure with the key as the parameter to the Closure. * Example usage: * <pre class="groovyTestCase"> * def map = [a:1, b:2].withDefault{ k -> k.toCharacter().isLowerCase() ? 10 : -10 } * def expected = [a:1, b:2, c:10, D:-10] * assert expected.every{ e -> e.value == map[e.key] } * * def constMap = [:].withDefault{ 42 } * assert constMap.foo == 42 * assert constMap.size() == 1 * </pre> * * @param self a Map * @param init a Closure which is passed the unknown key * @return the wrapped Map * @since 1.7.1 */ public static <K, V> Map<K, V> withDefault(Map<K, V> self, Closure init) { return MapWithDefault.newInstance(self, init); } /** * Sorts the Collection. Assumes that the collection items are comparable * and uses their natural ordering to determine the resulting order. * If the Collection is a List, it is sorted in place and returned. * Otherwise, the elements are first placed into a new list which is then * sorted and returned - leaving the original Collection unchanged. * <pre class="groovyTestCase">assert [1,2,3] == [3,1,2].sort()</pre> * * @param self the collection to be sorted * @return the sorted collection as a List * @see #sort(Collection, boolean) * @since 1.0 */ public static <T> List<T> sort(Collection<T> self) { return sort(self, true); } /** * Sorts the Collection. Assumes that the collection items are * comparable and uses their natural ordering to determine the resulting order. * If the Collection is a List and mutate is true, * it is sorted in place and returned. Otherwise, the elements are first placed * into a new list which is then sorted and returned - leaving the original Collection unchanged. * <pre class="groovyTestCase">assert [1,2,3] == [3,1,2].sort()</pre> * <pre class="groovyTestCase"> * def orig = [1, 3, 2] * def sorted = orig.sort(false) * assert orig == [1, 3, 2] * assert sorted == [1, 2, 3] * </pre> * * @param self the collection to be sorted * @param mutate false will always cause a new list to be created, true will mutate lists in place * @return the sorted collection as a List * @since 1.8.1 */ public static <T> List<T> sort(Collection<T> self, boolean mutate) { List<T> answer = mutate ? asList(self) : toList(self); Collections.sort(answer, new NumberAwareComparator<T>()); return answer; } /** * Sorts the elements from the given map into a new ordered map using * the closure as a comparator to determine the ordering. * The original map is unchanged. * <pre class="groovyTestCase">def map = [a:5, b:3, c:6, d:4].sort { a, b -> a.value <=> b.value } * assert map == [b:3, d:4, a:5, c:6]</pre> * * @param self the original unsorted map * @param closure a Closure used as a comparator * @return the sorted map * @since 1.6.0 */ public static <K, V> Map<K, V> sort(Map<K, V> self, Closure closure) { Map<K, V> result = new LinkedHashMap<K, V>(); List<Map.Entry<K, V>> entries = asList(self.entrySet()); sort(entries, closure); for (Map.Entry<K, V> entry : entries) { result.put(entry.getKey(), entry.getValue()); } return result; } /** * Sorts the elements from the given map into a new ordered Map using * the specified key comparator to determine the ordering. * The original map is unchanged. * <pre class="groovyTestCase">def map = [ba:3, cz:6, ab:5].sort({ a, b -> a[-1] <=> b[-1] } as Comparator) * assert map*.value == [3, 5, 6]</pre> * * @param self the original unsorted map * @param comparator a Comparator * @return the sorted map * @since 1.7.2 */ public static <K, V> Map<K, V> sort(Map<K, V> self, Comparator<K> comparator) { Map<K, V> result = new TreeMap<K, V>(comparator); result.putAll(self); return result; } /** * Sorts the elements from the given map into a new ordered Map using * the natural ordering of the keys to determine the ordering. * The original map is unchanged. * <pre class="groovyTestCase">map = [ba:3, cz:6, ab:5].sort() * assert map*.value == [5, 3, 6] * </pre> * * @param self the original unsorted map * @return the sorted map * @since 1.7.2 */ public static <K, V> Map<K, V> sort(Map<K, V> self) { return new TreeMap<K, V>(self); } /** * Modifies this array so that its elements are in 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> T[] sort(T[] self) { Arrays.sort(self, new NumberAwareComparator<T>()); return self; } /** * Sorts the given array into sorted order. * The array items are assumed to be comparable. * If mutate is true, the array is sorted in place and returned. Otherwise, a new sorted * array is returned and the original array remains unchanged. * <pre class="groovyTestCase"> * def orig = ["hello","hi","Hey"] as String[] * def sorted = orig.sort(false) * assert orig == ["hello","hi","Hey"] as String[] * assert sorted == ["Hey","hello","hi"] as String[] * orig.sort(true) * assert orig == ["Hey","hello","hi"] as String[] * </pre> * * @param self the array to be sorted * @param mutate false will always cause a new array to be created, true will mutate the array in place * @return the sorted array * @since 1.8.1 */ public static <T> T[] sort(T[] self, boolean mutate) { T[] answer = mutate ? self : self.clone(); Arrays.sort(answer, new NumberAwareComparator<T>()); return answer; } /** * 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 <T> Iterator<T> sort(Iterator<T> self) { return sort(toList(self)).listIterator(); } /** * Sorts the given iterator items into a sorted iterator using the comparator. 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 * @param comparator a Comparator used for comparing items * @return the sorted items as an Iterator * @since 1.5.5 */ public static <T> Iterator<T> sort(Iterator<T> self, Comparator<T> comparator) { return sort(toList(self), comparator).listIterator(); } /** * Sorts the Collection using the given Comparator. If the Collection is a List, * it is sorted in place and returned. Otherwise, the elements are first placed * into a new list which is then sorted and returned - leaving the original Collection unchanged. * <pre class="groovyTestCase"> * assert ["hi","hey","hello"] == ["hello","hi","hey"].sort( { a, b -> a.length() <=> b.length() } as Comparator ) * </pre> * <pre class="groovyTestCase"> * assert ["hello","Hey","hi"] == ["hello","hi","Hey"].sort(String.CASE_INSENSITIVE_ORDER) * </pre> * * @param self a collection to be sorted * @param comparator a Comparator used for the comparison * @return a sorted List * @see #sort(Collection, boolean, Comparator) * @since 1.0 */ public static <T> List<T> sort(Collection<T> self, Comparator<T> comparator) { return sort(self, true, comparator); } /** * Sorts the Collection using the given Comparator. If the Collection is a List and mutate * is true, it is sorted in place and returned. Otherwise, the elements are first placed * into a new list which is then sorted and returned - leaving the original Collection unchanged. * <pre class="groovyTestCase"> * assert ["hi","hey","hello"] == ["hello","hi","hey"].sort( { a, b -> a.length() <=> b.length() } as Comparator ) * </pre> * <pre class="groovyTestCase"> * def orig = ["hello","hi","Hey"] * def sorted = orig.sort(false, String.CASE_INSENSITIVE_ORDER) * assert orig == ["hello","hi","Hey"] * assert sorted == ["hello","Hey","hi"] * </pre> * * @param self a collection to be sorted * @param mutate false will always cause a new list to be created, true will mutate lists in place * @param comparator a Comparator used for the comparison * @return a sorted List * @since 1.8.1 */ public static <T> List<T> sort(Collection<T> self, boolean mutate, Comparator<T> comparator) { List<T> list = mutate ? asList(self) : toList(self); Collections.sort(list, comparator); return list; } /** * Sorts the given 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> T[] sort(T[] self, Comparator<T> comparator) { return sort(self, true, comparator); } /** * Modifies this array so that its elements are in sorted order as determined by the given comparator. * If mutate is true, the array is sorted in place and returned. Otherwise, a new sorted * array is returned and the original array remains unchanged. * <pre class="groovyTestCase"> * def orig = ["hello","hi","Hey"] as String[] * def sorted = orig.sort(false, String.CASE_INSENSITIVE_ORDER) * assert orig == ["hello","hi","Hey"] as String[] * assert sorted == ["hello","Hey","hi"] as String[] * orig.sort(true, String.CASE_INSENSITIVE_ORDER) * assert orig == ["hello","Hey","hi"] as String[] * </pre> * * @param self the array containing elements to be sorted * @param mutate false will always cause a new array to be created, true will mutate arrays in place * @param comparator a Comparator used for the comparison * @return a sorted array * @since 1.8.1 */ public static <T> T[] sort(T[] self, boolean mutate, Comparator<T> comparator) { T[] answer = mutate ? self : self.clone(); Arrays.sort(answer, comparator); return answer; } /** * 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. * </p> * 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 <T> Iterator<T> sort(Iterator<T> self, Closure closure) { return sort(toList(self), closure).listIterator(); } /** * Sorts the elements from this array into a newly created array using * the Closure to determine the correct ordering. * </p> * 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 containing the elements 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> T[] sort(T[] self, Closure closure) { return sort(self, false, closure); } /** * Modifies this array so that its elements are in sorted order using the Closure to determine the correct ordering. * If mutate is false, a new array is returned and the original array remains unchanged. * Otherwise, the original array is sorted in place and returned. * </p> * 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. * <pre class="groovyTestCase"> * def orig = ["hello","hi","Hey"] as String[] * def sorted = orig.sort(false) { it.size() } * assert orig == ["hello","hi","Hey"] as String[] * assert sorted == ["hi","Hey","hello"] as String[] * orig.sort(true) { it.size() } * assert orig == ["hi","Hey","hello"] as String[] * </pre> * * @param self the array to be sorted * @param mutate false will always cause a new array to be created, true will mutate arrays in place * @param closure a Closure used to determine the correct ordering * @return the sorted array * @since 1.8.1 */ @SuppressWarnings("unchecked") public static <T> T[] sort(T[] self, boolean mutate, Closure closure) { T[] answer = (T[]) sort(toList(self), closure).toArray(); if (mutate) { System.arraycopy(answer, 0, self, 0, answer.length); } return mutate ? self : answer; } /** * Sorts this Collection using the given Closure to determine the correct ordering. If the Collection is a List, * it is sorted in place and returned. Otherwise, the elements are first placed * into a new list which is then sorted and returned - leaving the original Collection unchanged. * </p> * 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. * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }</pre> * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b -> a.length() <=> b.length() }</pre> * * @param self a Collection to be sorted * @param closure a 1 or 2 arg Closure used to determine the correct ordering * @return a newly created sorted List * @see #sort(Collection, boolean, Closure) * @since 1.0 */ public static <T> List<T> sort(Collection<T> self, Closure closure) { return sort(self, true, closure); } /** * Sorts this Collection using the given Closure to determine the correct ordering. If the Collection is a List * and mutate is true, it is sorted in place and returned. Otherwise, the elements are first placed * into a new list which is then sorted and returned - leaving the original Collection unchanged. * </p> * 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. * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }</pre> * <pre class="groovyTestCase">assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b -> a.length() <=> b.length() }</pre> * <pre class="groovyTestCase"> * def orig = ["hello","hi","Hey"] * def sorted = orig.sort(false) { it.toUpperCase() } * assert orig == ["hello","hi","Hey"] * assert sorted == ["hello","Hey","hi"] * </pre> * * @param self a Collection to be sorted * @param mutate false will always cause a new list to be created, true will mutate lists in place * @param closure a 1 or 2 arg Closure used to determine the correct ordering * @return a newly created sorted List * @since 1.8.1 */ public static <T> List<T> sort(Collection<T> self, boolean mutate, Closure closure) { List<T> list = mutate ? asList(self) : toList(self); // use a comparator of one item or two int params = closure.getMaximumNumberOfParameters(); if (params == 1) { Collections.sort(list, new OrderBy<T>(closure)); } else { Collections.sort(list, new ClosureComparator<T>(closure)); } return list; } /** * Avoids doing unnecessary work when sorting an already sorted set (i.e. an identity function for an already sorted set). * * @param self an already sorted set * @return the set * @since 1.0 */ public static <T> SortedSet<T> sort(SortedSet<T> self) { return self; } /** * Avoids doing unnecessary work when sorting an already sorted map (i.e. an identity function for an already sorted map). * * @param self an already sorted map * @return the map * @since 1.8.1 */ public static <K, V> SortedMap<K, V> sort(SortedMap<K, V> self) { return self; } /** * Removes the last item from the List. Using add() and pop() * is similar to push and pop on a Stack. * <pre class="groovyTestCase">def list = ["a", false, 2] * assert list.pop() == 2 * assert list == ["a", false]</pre> * * @param self a List * @return the item removed from the List * @throws NoSuchElementException if the list is empty and you try to pop() it. * @since 1.0 */ public static <T> T pop(List<T> self) { if (self.isEmpty()) { throw new NoSuchElementException("Cannot pop() an empty List"); } return self.remove(self.size() - 1); } /** * Provides an easy way to append multiple Map.Entry values to a Map. * * @param self a Map * @param entries a Collection of Map.Entry items to be added to the Map. * @return the same map, after the items have been added to it. * @since 1.6.1 */ public static <K, V> Map<K, V> putAll(Map<K, V> self, Collection<Map.Entry<K, V>> entries) { for (Map.Entry<K, V> entry : entries) { self.put(entry.getKey(), entry.getValue()); } return self; } /** * Returns a new <code>Map</code> containing all entries from <code>self</code> and <code>entries</code>, * giving precedence to <code>entries</code>. Any keys appearing in both Maps * will appear in the resultant map with values from the <code>entries</code> * operand. If <code>self</code> map is one of TreeMap, LinkedHashMap, Hashtable * or Properties, the returned Map will preserve that type, otherwise a HashMap will * be returned. * </p> * * @param self a Map * @param entries a Collection of Map.Entry items to be added to the Map. * @return a new Map containing all key, value pairs from self and entries * @since 1.6.1 */ public static <K, V> Map<K, V> plus(Map<K, V> self, Collection<Map.Entry<K, V>> entries) { Map<K, V> map = cloneSimilarMap(self); putAll(map, entries); return map; } /** * Appends an item to the List. Synonym for add(). * <pre class="groovyTestCase">def list = [3, 4, 2] * list.push("x") * assert list == [3, 4, 2, "x"]</pre> * * @param self a List * @param value element to be appended to this list. * @return <tt>true</tt> (as per the general contract of the * <tt>Collection.add</tt> method). * @throws NoSuchElementException if the list is empty and you try to pop() it. * @since 1.5.5 */ public static <T> boolean push(List<T> self, T value) { return self.add(value); } /** * Returns the last item from the List. * <pre class="groovyTestCase">def list = [3, 4, 2] * assert list.last() == 2 * assert list == [3, 4, 2]</pre> * * @param self a List * @return the last item from the List * @throws NoSuchElementException if the list is empty and you try to access the last() item. * @since 1.5.5 */ public static <T> T last(List<T> self) { if (self.isEmpty()) { throw new NoSuchElementException("Cannot access last() element from an empty List"); } return self.get(self.size() - 1); } /** * Returns the last item from the Object array. * <pre class="groovyTestCase">def array = [3, 4, 2].toArray() * assert array.last() == 2</pre> * * @param self an ObjectArray * @return the last item from the Object array * @throws NoSuchElementException if the array is empty and you try to access the last() item. * @since 1.7.3 */ public static <T> T last(T[] self) { if (self.length == 0) { throw new NoSuchElementException("Cannot access last() element from an empty Array"); } return self[self.length - 1]; } /** * Returns the first item from the List. * <pre class="groovyTestCase">def list = [3, 4, 2] * assert list.first() == 3 * assert list == [3, 4, 2]</pre> * * @param self a List * @return the first item from the List * @throws NoSuchElementException if the list is empty and you try to access the first() item. * @since 1.5.5 */ public static <T> T first(List<T> self) { if (self.isEmpty()) { throw new NoSuchElementException("Cannot access first() element from an empty List"); } return self.get(0); } /** * Returns the first item from the Object array. * <pre class="groovyTestCase">def array = [3, 4, 2].toArray() * assert array.first() == 3</pre> * * @param self an Object array * @return the first item from the Object array * @throws NoSuchElementException if the array is empty and you try to access the first() item. * @since 1.7.3 */ public static <T> T first(T[] self) { if (self.length == 0) { throw new NoSuchElementException("Cannot access first() element from an empty List"); } return self[0]; } /** * Returns the first item from the List. * <pre class="groovyTestCase">def list = [3, 4, 2] * assert list.head() == 3 * assert list == [3, 4, 2]</pre> * * @param self a List * @return the first item from the List * @throws NoSuchElementException if the list is empty and you try to access the head() item. * @since 1.5.5 */ public static <T> T head(List<T> self) { return first(self); } /** * Returns the first item from the Object array. * <pre class="groovyTestCase">def array = [3, 4, 2].toArray() * assert array.head() == 3</pre> * * @param self an Object array * @return the first item from the Object array * @throws NoSuchElementException if the array is empty and you try to access the head() item. * @since 1.7.3 */ public static <T> T head(T[] self) { return first(self); } /** * Returns the items from the List excluding the first item. * <pre class="groovyTestCase">def list = [3, 4, 2] * assert list.tail() == [4, 2] * assert list == [3, 4, 2]</pre> * * @param self a List * @return a list without its first element * @throws NoSuchElementException if the list is empty and you try to access the tail() item. * @since 1.5.6 */ public static <T> List<T> tail(List<T> self) { if (self.isEmpty()) { throw new NoSuchElementException("Cannot access tail() for an empty List"); } List<T> result = new ArrayList<T>(self); result.remove(0); return result; } /** * Returns the items from the Object array excluding the first item. * <pre class="groovyTestCase"> * String[] strings = ["a", "b", "c"] * def result = strings.tail() * assert strings.class.componentType == String * </pre> * * @param self an Object array * @return an Object array without its first element * @throws NoSuchElementException if the list is empty and you try to access the tail() item. * @since 1.7.3 */ public static <T> T[] tail(T[] self) { if (self.length == 0) { throw new NoSuchElementException("Cannot access tail() for an empty Object array"); } Class<T> componentType = (Class<T>) self.getClass().getComponentType(); T[] result = (T[]) Array.newInstance(componentType, self.length - 1); System.arraycopy(self, 1, result, 0, self.length - 1); return result; } /** * Returns the first <code>num</code> elements from the head of this list. * <pre class="groovyTestCase"> * def strings = [ 'a', 'b', 'c' ] * assert strings.take( 0 ) == [] * assert strings.take( 2 ) == [ 'a', 'b' ] * assert strings.take( 5 ) == [ 'a', 'b', 'c' ] * </pre> * * @param self the original list * @param num the number of elements to take from this list * @return a list consisting of the first <code>num</code> elements of this list, * or else the whole list if it has less then <code>num</code> elements. * @since 1.8.1 */ public static <T> List<T> take( List<T> self, int num ) { if( self.isEmpty() || num <= 0 ) { return createSimilarList( self, 0 ) ; } if( self.size() <= num ) { List<T> ret = createSimilarList( self, self.size() ) ; ret.addAll( self ) ; return ret ; } List<T> ret = createSimilarList( self, num ) ; ret.addAll( self.subList( 0, num ) ) ; return ret ; } /** * Returns the first <code>num</code> elements from the head of this array. * <pre class="groovyTestCase"> * String[] strings = [ 'a', 'b', 'c' ] * assert strings.take( 0 ) == [] as String[] * assert strings.take( 2 ) == [ 'a', 'b' ] as String[] * assert strings.take( 5 ) == [ 'a', 'b', 'c' ] as String[] * </pre> * * @param self the original array * @param num the number of elements to take from this array * @return an array consisting of the first <code>num</code> elements of this array, * or else the whole array if it has less then <code>num</code> elements. * @since 1.8.1 */ public static <T> T[] take( T[] self, int num ) { Class<T> componentType = (Class<T>) self.getClass().getComponentType(); if( self.length == 0 || num <= 0 ) { return (T[]) Array.newInstance(componentType, 0); } if( self.length <= num ) { T[] ret = (T[]) Array.newInstance(componentType, self.length); System.arraycopy(self, 0, ret, 0, self.length); return ret; } T[] ret = (T[]) Array.newInstance(componentType, num); System.arraycopy(self, 0, ret, 0, num); return ret; } /** * Returns a new map containing the first <code>num</code> elements from the head of this map. * If the map instance does not have ordered keys,then this function could return a random <code>n<code> * entries. Groovy by default used LinkedHashMap, so this shouldn't be an issue in the main. * <pre class="groovyTestCase"> * def strings = [ 'a':10, 'b':20, 'c':30 ] * assert strings.take( 0 ) == [:] * assert strings.take( 2 ) == [ 'a':10, 'b':20 ] * assert strings.take( 5 ) == [ 'a':10, 'b':20, 'c':30 ] * </pre> * * @param self the original map * @param num the number of elements to take from this map * @return a new map consisting of the first <code>num</code> elements of this map, * or else the whole map if it has less then <code>num</code> elements. * @since 1.8.1 */ public static <K,V> Map<K,V> take( Map<K,V> self, int num ) { if( self.isEmpty() || num <= 0 ) { return createSimilarMap( self ) ; } Map<K,V> ret = createSimilarMap( self ) ; for( K key : self.keySet() ) { ret.put( key, self.get( key ) ) ; if( --num <= 0 ) { break ; } } return ret ; } /** * Returns an iterator to up to the first <code>num</code> elements from this iterator. * The original iterator is stepped along by <code>num</code> elements. * <pre class="groovyTestCase"> * def a = 0 * def iter = [ hasNext:{ true }, next:{ a++ } ] as Iterator * * def iteratorCompare( Iterator a, List b ) { * a.collect { it } == b * } * assert iteratorCompare( iter.take( 0 ), [] ) * assert iteratorCompare( iter.take( 2 ), [ 0, 1 ] ) * assert iteratorCompare( iter.take( 5 ), [ 2, 3, 4, 5, 6 ] ) * </pre> * * @param self the Iterator * @param num the number of elements to take from this iterator * @return a list consisting of up to the first <code>num</code> elements of this iterator. * @since 1.8.1 */ public static <T> Iterator<T> take( Iterator<T> self, int num ) { List<T> ret = new ArrayList<T>() ; while( num-- > 0 && self.hasNext() ) { ret.add( self.next() ) ; } return ret.listIterator() ; } /** * Returns the first <code>num</code> elements from this CharSequence. * <pre class="groovyTestCase"> * def text = "Groovy" * assert text.take( 0 ) == '' * assert text.take( 2 ) == 'Gr' * assert text.take( 7 ) == 'Groovy' * </pre> * * @param self the original CharSequence * @param num the number of chars to take from this CharSequence * @return a CharSequence consisting of the first <code>num</code> chars, * or else the whole CharSequence if it has less then <code>num</code> elements. * @since 1.8.1 */ public static CharSequence take( CharSequence self, int num ) { if( num < 0 ) { return self.subSequence( 0, 0 ) ; } if( self.length() <= num ) { return self ; } return self.subSequence( 0, num ) ; } /** * Drops the given number of elements from the head of this list * if they are available. * <pre class="groovyTestCase"> * def strings = [ 'a', 'b', 'c' ] * assert strings.drop( 0 ) == [ 'a', 'b', 'c' ] * assert strings.drop( 2 ) == [ 'c' ] * assert strings.drop( 5 ) == [] * </pre> * * @param self the original list * @param num the number of elements to drop from this list * @return a list consisting of all elements of this list except the first * <code>num</code> ones, or else the empty list, if this list has * less than <code>num</code> elements. * @since 1.8.1 */ public static <T> List<T> drop(List<T> self, int num) { if (self.size() <= num) { return createSimilarList( self, 0 ) ; } if (num <= 0) { List<T> ret = createSimilarList( self, self.size() ) ; ret.addAll( self ) ; return ret ; } List<T> ret = createSimilarList( self, self.size() - num ) ; ret.addAll(self.subList(num, self.size())) ; return ret ; } /** * Drops the given number of elements from the head of this array * if they are available. * <pre class="groovyTestCase"> * String[] strings = [ 'a', 'b', 'c' ] * assert strings.drop( 0 ) == [ 'a', 'b', 'c' ] as String[] * assert strings.drop( 2 ) == [ 'c' ] as String[] * assert strings.drop( 5 ) == [] as String[] * </pre> * * @param self the original array * @param num the number of elements to drop from this array * @return an array consisting of all elements of this array except the * first <code>num</code> ones, or else the empty array, if this * array has less than <code>num</code> elements. * @since 1.8.1 */ public static <T> T[] drop(T[] self, int num) { Class<T> componentType = (Class<T>) self.getClass().getComponentType(); if (self.length <= num) { return (T[]) Array.newInstance(componentType, 0); } if (num <= 0) { T[] ret = (T[]) Array.newInstance(componentType, self.length); System.arraycopy(self, 0, ret, 0, self.length); return ret; } T[] ret = (T[]) Array.newInstance(componentType, self.length - num); System.arraycopy(self, num, ret, 0, self.length - num); return ret; } /** * Drops the given number of key/value pairs from the head of this map if they are available. * If the map instance does not have ordered keys,then this function could return a random <code>n<code> * entries. Groovy by default used LinkedHashMap, so this shouldn't be an issue in the main. * <pre class="groovyTestCase"> * def strings = [ 'a':10, 'b':20, 'c':30 ] * assert strings.drop( 0 ) == [ 'a':10, 'b':20, 'c':30 ] * assert strings.drop( 2 ) == [ 'c':30 ] * assert strings.drop( 5 ) == [:] * </pre> * * @param self the original map * @param num the number of elements to drop from this map * @return a map consisting of all key/value pairs of this map except the first * <code>num</code> ones, or else the empty map, if this map has * less than <code>num</code> elements. * @since 1.8.1 */ public static <K,V> Map<K,V> drop( Map<K,V> self, int num ) { if( self.size() <= num ) { return createSimilarMap( self ) ; } if( num == 0 ) { return cloneSimilarMap( self ) ; } Map<K,V> ret = createSimilarMap( self ) ; for( K key : self.keySet() ) { if( num-- <= 0 ) { ret.put( key, self.get( key ) ) ; } } return ret ; } /** * Drops the given number of elements from the head of this iterator if they are available. * The original iterator is stepped along by <code>num</code> elements. * <pre class="groovyTestCase"> * def iteratorCompare( Iterator a, List b ) { * a.collect { it } == b * } * def iter = [ 1, 2, 3, 4, 5 ].listIterator() * assert iteratorCompare( iter.drop( 0 ), [ 1, 2, 3, 4, 5 ] ) * iter = [ 1, 2, 3, 4, 5 ].listIterator() * assert iteratorCompare( iter.drop( 2 ), [ 3, 4, 5 ] ) * iter = [ 1, 2, 3, 4, 5 ].listIterator() * assert iteratorCompare( iter.drop( 5 ), [] ) * </pre> * * @param self the original iterator * @param num the number of elements to drop from this iterator * @return The iterator stepped along by <code>num</code> elements if they exist. * @since 1.8.1 */ public static <T> Iterator<T> drop(Iterator<T> self, int num) { while (num-- > 0 && self.hasNext()) { self.next(); } return self ; } /** * Drops the given number of chars from the head of this CharSequence * if they are available. * <pre class="groovyTestCase"> * def text = "Groovy" * assert text.drop( 0 ) == 'Groovy' * assert text.drop( 2 ) == 'oovy' * assert text.drop( 7 ) == '' * </pre> * * @param self the original CharSequence * @param num the number of characters to drop from this iterator * @return a CharSequence consisting of all characters except the first <code>num</code> ones, * or else an empty String, if this CharSequence has less than <code>num</code> characters. * @since 1.8.1 */ public static CharSequence drop(CharSequence self, int num) { if( num <= 0 ) { return self ; } if( self.length() <= num ) { return self.subSequence( 0, 0 ) ; } return self.subSequence( num, self.length() ) ; } /** * Converts this Collection to a List. Returns the original Collection * if it is already a List. * <p> * Example usage: * <pre class="groovyTestCase">assert new HashSet().asList() instanceof List</pre> * * @param self a collection to be converted into a List * @return a newly created List if this collection is not already a List * @since 1.0 */ public static <T> List<T> asList(Collection<T> self) { if (self instanceof List) { return (List<T>) self; } else { return toList(self); } } /** * Coerce an object instance to a boolean value. * An object is coerced to true if it's not null, to false if it is null. * * @param object the object to coerce * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Object object) { return object != null; } /** * Coerce an Boolean instance to a boolean value. * * @param bool the Boolean * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Boolean bool) { return bool; } /** * Coerce a Matcher instance to a boolean value. * * @param matcher the matcher * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Matcher matcher) { RegexSupport.setLastMatcher(matcher); return matcher.find(); } /** * Coerce a collection instance to a boolean value. * A collection is coerced to false if it's empty, and to true otherwise. * <pre class="groovyTestCase">assert [1,2].asBoolean() == true</pre> * <pre class="groovyTestCase">assert [].asBoolean() == false</pre> * * @param collection the collection * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Collection collection) { return !collection.isEmpty(); } /** * Coerce a map instance to a boolean value. * A map is coerced to false if it's empty, and to true otherwise. * <pre class="groovyTestCase">assert [:] as Boolean == false * assert [a:2] as Boolean == true</pre> * * @param map the map * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Map map) { return !map.isEmpty(); } /** * Coerce an iterator instance to a boolean value. * An iterator is coerced to false if there are no more elements to iterate over, * and to true otherwise. * * @param iterator the iterator * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Iterator iterator) { return iterator.hasNext(); } /** * Coerce an enumeration instance to a boolean value. * An enumeration is coerced to false if there are no more elements to enumerate, * and to true otherwise. * * @param enumeration the enumeration * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Enumeration enumeration) { return enumeration.hasMoreElements(); } /** * Coerce a string (an instance of CharSequence) to a boolean value. * A string is coerced to false if it is of length 0, * and to true otherwise. * * @param string the character sequence * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(CharSequence string) { return string.length() > 0; } /** * Coerce an Object array to a boolean value. * An Object array is false if the array is of length 0. * and to true otherwise * * @param array the array * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Object[] array) { return array.length > 0; } /** * Coerces a byte array to a boolean value. * A byte array is false if the array is of length 0, * and true otherwise. * * @param array an array * @return the array's boolean value * @since 1.7.4 */ public static boolean asBoolean(byte[] array) { return array.length > 0; } /** * Coerces a short array to a boolean value. * A short array is false if the array is of length 0, * and true otherwise. * * @param array an array * @return the array's boolean value * @since 1.7.4 */ public static boolean asBoolean(short[] array) { return array.length > 0; } /** * Coerces an int array to a boolean value. * An int array is false if the array is of length 0, * and true otherwise. * * @param array an array * @return the array's boolean value * @since 1.7.4 */ public static boolean asBoolean(int[] array) { return array.length > 0; } /** * Coerces a long array to a boolean value. * A long array is false if the array is of length 0, * and true otherwise. * * @param array an array * @return the array's boolean value * @since 1.7.4 */ public static boolean asBoolean(long[] array) { return array.length > 0; } /** * Coerces a float array to a boolean value. * A float array is false if the array is of length 0, * and true otherwise. * * @param array an array * @return the array's boolean value * @since 1.7.4 */ public static boolean asBoolean(float[] array) { return array.length > 0; } /** * Coerces a double array to a boolean value. * A double array is false if the array is of length 0, * and true otherwise. * * @param array an array * @return the array's boolean value * @since 1.7.4 */ public static boolean asBoolean(double[] array) { return array.length > 0; } /** * Coerces a boolean array to a boolean value. * A boolean array is false if the array is of length 0, * and true otherwise. * * @param array an array * @return the array's boolean value * @since 1.7.4 */ public static boolean asBoolean(boolean[] array) { return array.length > 0; } /** * Coerces a char array to a boolean value. * A char array is false if the array is of length 0, * and true otherwise. * * @param array an array * @return the array's boolean value * @since 1.7.4 */ public static boolean asBoolean(char[] array) { return array.length > 0; } /** * Coerce a character to a boolean value. * A character is coerced to false if it's character value is equal to 0, * and to true otherwise. * * @param character the character * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Character character) { return character != 0; } /** * Coerce a number to a boolean value. * A number is coerced to false if its double value is equal to 0, and to true otherwise, * and to true otherwise. * * @param number the number * @return the boolean value * @since 1.7.0 */ public static boolean asBoolean(Number number) { return number.doubleValue() != 0; } /** * Converts the given collection to another type. A default concrete * type is used for List, Set, or SortedSet. If the given type has * a constructor taking a collection, that is used. Otherwise, the * call is deferred to {link #asType(Object,Class)}. If this * collection is already of the given type, the same instance is * returned. * * @param col a collection * @param clazz the desired class * @return the object resulting from this type conversion * @see #asType(java.lang.Object, java.lang.Class) * @since 1.0 */ @SuppressWarnings("unchecked") public static <T> T asType(Collection col, Class<T> clazz) { if (col.getClass() == clazz) { return (T) col; } if (clazz == List.class) { return (T) asList(col); } if (clazz == Set.class) { if (col instanceof Set) return (T) col; return (T) new HashSet(col); } if (clazz == SortedSet.class) { if (col instanceof SortedSet) return (T) col; return (T) new TreeSet(col); } if (clazz == Queue.class) { if (col instanceof Queue) return (T) col; return (T) new LinkedList(col); } if (clazz == Stack.class) { if (col instanceof Stack) return (T) col; final Stack stack = new Stack(); stack.addAll(col); return (T) stack; } if (clazz!=String[].class && ReflectionCache.isArray(clazz)) { try { return (T) asArrayType(col, clazz); } catch (GroovyCastException e) { /* ignore */ } } Object[] args = {col}; try { return (T) InvokerHelper.invokeConstructorOf(clazz, args); } catch (Exception e) { // ignore, the constructor that takes a Collection as an argument may not exist } if (Collection.class.isAssignableFrom(clazz)) { try { Collection result = (Collection) InvokerHelper.invokeConstructorOf(clazz, null); result.addAll(col); return (T)result; } catch (Exception e) { // ignore, the no arg constructor might not exist. } } return asType((Object) col, clazz); } /** * Converts the given array to either a List, Set, or * SortedSet. If the given class is something else, the * call is deferred to {link #asType(Object,Class)}. * * @param ary an array * @param clazz the desired class * @return the object resulting from this type conversion * @see #asType(java.lang.Object, java.lang.Class) * @since 1.5.1 */ @SuppressWarnings("unchecked") public static <T> T asType(Object[] ary, Class<T> clazz) { if (clazz == List.class) { return (T) new ArrayList(Arrays.asList(ary)); } if (clazz == Set.class) { return (T) new HashSet(Arrays.asList(ary)); } if (clazz == SortedSet.class) { return (T) new TreeSet(Arrays.asList(ary)); } return asType((Object) ary, clazz); } /** * Coerces the closure to an implementation of the given class. The class * is assumed to be an interface or class with a single method definition. * The closure is used as the implementation of that single method. * * @param cl the implementation of the single method * @param clazz the target type * @return a Proxy of the given type which wraps this closure. * @since 1.0 */ @SuppressWarnings("unchecked") public static <T> T asType(Closure cl, Class<T> clazz) { if (clazz.isInterface() && !(clazz.isInstance(cl))) { return (T) Proxy.newProxyInstance( clazz.getClassLoader(), new Class[]{clazz}, new ConvertedClosure(cl)); } try { return asType((Object) cl, clazz); } catch (GroovyCastException ce) { try { return (T) ProxyGenerator.INSTANCE.instantiateAggregateFromBaseClass(cl, clazz); } catch (GroovyRuntimeException cause) { throw new GroovyCastException("Error casting closure to " + clazz.getName() + ", Reason: " + cause.getMessage()); } } } /** * Coerces this map to the given type, using the map's keys as the public * method names, and values as the implementation. Typically the value * would be a closure which behaves like the method implementation. * * @param map this map * @param clazz the target type * @return a Proxy of the given type, which defers calls to this map's elements. * @since 1.0 */ @SuppressWarnings("unchecked") public static <T> T asType(Map map, Class<T> clazz) { if (!(clazz.isInstance(map)) && clazz.isInterface()) { return (T) Proxy.newProxyInstance( clazz.getClassLoader(), new Class[]{clazz}, new ConvertedMap(map)); } try { return asType((Object) map, clazz); } catch (GroovyCastException ce) { try { return (T) ProxyGenerator.INSTANCE.instantiateAggregateFromBaseClass(map, clazz); } catch (GroovyRuntimeException cause) { throw new GroovyCastException("Error casting map to " + clazz.getName() + ", Reason: " + cause.getMessage()); } } } /** * Creates a new List with the identical contents to this list * but in reverse order. * <pre class="groovyTestCase"> * def list = ["a", 4, false] * assert list.reverse() == [false, 4, "a"] * assert list == ["a", 4, false] * </pre> * * @param self a List * @return a reversed List * @see #reverse(List, boolean) * @since 1.0 */ public static <T> List<T> reverse(List<T> self) { return reverse(self, false); } /** * Reverses the elements in a list. If mutate is true, the original list is modified in place and returned. * Otherwise, a new list containing the reversed items is produced. * <pre class="groovyTestCase"> * def list = ["a", 4, false] * assert list.reverse(false) == [false, 4, "a"] * assert list == ["a", 4, false] * assert list.reverse(true) == [false, 4, "a"] * assert list == [false, 4, "a"] * </pre> * * @param self a List * @param mutate true if the list itself should be reversed in place and returned, false if a new list should be created * @return a reversed List * @since 1.8.1 */ public static <T> List<T> reverse(List<T> self, boolean mutate) { if (mutate) { Collections.reverse(self); return self; } int size = self.size(); List<T> answer = new ArrayList<T>(size); ListIterator<T> iter = self.listIterator(size); while (iter.hasPrevious()) { answer.add(iter.previous()); } return answer; } /** * Creates a new array containing items which are the same as this array but in reverse order. * * @param self an array * @return an array containing the reversed items * @see #reverse(Object[], boolean) * @since 1.5.5 */ @SuppressWarnings("unchecked") public static <T> T[] reverse(T[] self) { return reverse(self, false); } /** * Reverse the items in an array. If mutate is true, the original array is modified in place and returned. * Otherwise, a new array containing the reversed items is produced. * * @param self an array * @param mutate true if the array itself should be reversed in place and returned, false if a new array should be created * @return an array containing the reversed items * @since 1.8.1 */ @SuppressWarnings("unchecked") public static <T> T[] reverse(T[] self, boolean mutate) { if (!mutate) { return (T[]) toList(new ReverseListIterator<T>(Arrays.asList(self))).toArray(); } List<T> items = Arrays.asList(self); Collections.reverse(items); System.arraycopy((T[])items.toArray(), 0, self, 0, items.size()); return self; } /** * Reverses the iterator. The original iterator will become * exhausted of elements after determining the reversed values. * A new iterator for iterating through the reversed values is returned. * * @param self an Iterator * @return a reversed Iterator * @since 1.5.5 */ public static <T> Iterator<T> reverse(Iterator<T> self) { return new ReverseListIterator<T>(toList(self)); } /** * Create a Collection as a union of two collections. If the left collection * is a Set, then the returned collection will be a Set otherwise a List. * This operation will always create a new object for the result, * while the operands remain unchanged. * <pre class="groovyTestCase">assert [1,2,3,4] == [1,2] + [3,4]</pre> * * @param left the left Collection * @param right the right Collection * @return the merged Collection * @since 1.5.0 */ public static <T> Collection<T> plus(Collection<T> left, Collection<T> right) { final Collection<T> answer = cloneSimilarCollection(left, left.size() + right.size()); answer.addAll(right); return answer; } /** * Creates a new list by adding all of the elements in the specified array to the elements from the original list at the specified index. * Shifts the element currently at that index (if any) and any subsequent * elements to the right (increasing their indices). The new elements * will appear in this list in the order that they occur in the array. * The behavior of this operation is undefined if the specified array * is modified while the operation is in progress. The original list remains unchanged. * * <pre class="groovyTestCase"> * def items = [1, 2, 3] * def newItems = items.plus(2, 'a'..'c' as String[]) * assert newItems == [1, 2, 'a', 'b', 'c', 3] * assert items == [1, 2, 3] * </pre> * * See also <code>addAll</code> for similar functionality with modify semantics, i.e. which performs * the changes on the original list itself. * * @param self an original list * @param items array containing elements to be merged with elements from the original list * @param index index at which to insert the first element from the specified array * @return the new list * @see #plus(List, int, List) * @since 1.8.1 */ public static <T> List<T> plus(List<T> self, int index, T[] items) { return plus(self, index, Arrays.asList(items)); } /** * Creates a new list by adding all of the elements in the specified list * to the elements from this list at the specified index. * Shifts the element currently at that index (if any) and any subsequent * elements to the right (increasing their indices). The new elements * will appear in this list in the order that they occur in the array. * The behavior of this operation is undefined if the specified array * is modified while the operation is in progress. The original list remains unchanged. * * <pre class="groovyTestCase"> * def items = [1, 2, 3] * def newItems = items.plus(2, 'a'..'c') * assert newItems == [1, 2, 'a', 'b', 'c', 3] * assert items == [1, 2, 3] * </pre> * * See also <code>addAll</code> for similar functionality with modify semantics, i.e. which performs * the changes on the original list itself. * * @param self an original list * @param additions array containing elements to be merged with elements from the original list * @param index index at which to insert the first element from the specified list * @return the new list * @since 1.8.1 */ public static <T> List<T> plus(List<T> self, int index, List<T> additions) { final List<T> answer = new ArrayList<T>(self); answer.addAll(index, additions); return answer; } /** * Create a collection as a union of a Collection and an Object. If the collection * is a Set, then the returned collection will be a Set otherwise a List. * This operation will always create a new object for the result, * while the operands remain unchanged. * <pre class="groovyTestCase">assert [1,2,3] == [1,2] + 3</pre> * * @param left a Collection * @param right an object to add/append * @return the resulting Collection * @since 1.5.0 */ public static <T> Collection<T> plus(Collection<T> left, T right) { final Collection<T> answer = cloneSimilarCollection(left, left.size() + 1); answer.add(right); return answer; } /** * Create a List composed of the elements of this list, repeated * a certain number of times. Note that for non-primitive * elements, multiple references to the same instance will be added. * <pre class="groovyTestCase">assert [1,2,3,1,2,3] == [1,2,3] * 2</pre> * * @param self a Collection * @param factor the number of times to append * @return the multiplied list * @since 1.0 */ public static <T> List<T> multiply(Collection<T> self, Number factor) { int size = factor.intValue(); List<T> answer = new ArrayList<T>(self.size() * size); for (int i = 0; i < size; i++) { answer.addAll(self); } return answer; } /** * Create a Collection composed of the intersection of both collections. Any * elements that exist in both collections are added to the resultant collection. * <pre class="groovyTestCase">assert [4,5] == [1,2,3,4,5].intersect([4,5,6,7,8])</pre> * * @param left a Collection * @param right a Collection * @return a Collection as an intersection of both collections * @since 1.5.6 */ public static <T> Collection<T> intersect(Collection<T> left, Collection<T> right) { if (left.isEmpty()) return createSimilarCollection(left, 0); if (left.size() < right.size()) { Collection<T> swaptemp = left; left = right; right = swaptemp; } // TODO optimise if same type? // boolean nlgnSort = sameType(new Collection[]{left, right}); Collection<T> result = createSimilarCollection(left, left.size()); //creates the collection to look for values. Collection<T> pickFrom = new TreeSet<T>(new NumberAwareComparator<T>()); pickFrom.addAll(left); for (final T t : right) { if (pickFrom.contains(t)) result.add(t); } return result; } /** * Create a Map composed of the intersection of both maps. * Any entries that exist in both maps are added to the resultant map. * <pre class="groovyTestCase">assert [4:4,5:5] == [1:1,2:2,3:3,4:4,5:5].intersect([4:4,5:5,6:6,7:7,8:8])</pre> * <pre class="groovyTestCase">assert [1: 1, 2: 2, 3: 3, 4: 4].intersect( [1: 1.0, 2: 2, 5: 5] ) == [1:1, 2:2]</pre> * * @param left a map * @param right a map * @return a Map as an intersection of both maps * @since 1.7.4 */ public static <K,V> Map<K,V> intersect(Map<K,V> left, Map<K,V> right) { final Map<K,V> ansMap = createSimilarMap(left); if (right != null && right.size() > 0) { final Iterator<Map.Entry<K,V>> it1 = left.entrySet().iterator(); while (it1.hasNext()) { final Map.Entry<K,V> e1 = it1.next(); final Iterator<Map.Entry<K,V>> it2 = right.entrySet().iterator(); while (it2.hasNext()) { final Map.Entry<K,V> e2 = it2.next(); if (DefaultTypeTransformation.compareEqual(e1, e2)) { ansMap.put(e1.getKey(), e1.getValue()); } } } } return ansMap; } /** * Returns <code>true</code> if the intersection of two collections is empty. * <pre class="groovyTestCase">assert [1,2,3].disjoint([3,4,5]) == false</pre> * <pre class="groovyTestCase">assert [1,2].disjoint([3,4]) == true</pre> * * @param left a Collection * @param right a Collection * @return boolean <code>true</code> if the intersection of two collections * is empty, <code>false</code> otherwise. * @since 1.0 */ public static boolean disjoint(Collection left, Collection right) { if (left.isEmpty() || right.isEmpty()) return true; Collection pickFrom = new TreeSet(new NumberAwareComparator()); pickFrom.addAll(right); for (final Object o : left) { if (pickFrom.contains(o)) return false; } return true; } /** * Compare the contents of this array to the contents of the given array. * * @param left an int array * @param right the operand array. * @return true if the contents of both arrays are equal. * @since 1.5.0 */ public static boolean equals(int[] left, int[] right) { if (left == null) { return right == null; } if (right == null) { return false; } if (left == right) { return true; } if (left.length != right.length) { return false; } for (int i = 0; i < left.length; i++) { if (left[i] != right[i]) return false; } return true; } /** * Determines if the contents of this array are equal to the * contents of the given list, in the same order. This returns * <code>false</code> if either collection is <code>null</code>. * * @param left this array * @param right the list being compared * @return true if the contents of both collections are equal * @since 1.5.0 */ public static boolean equals(Object[] left, List right) { return coercedEquals(left, right); } /** * Determines if the contents of this list are equal to the * contents of the given array in the same order. This returns * <code>false</code> if either collection is <code>null</code>. * <pre class="groovyTestCase">assert [1, "a"].equals( [ 1, "a" ] as Object[] )</pre> * * @param left this List * @param right this Object[] being compared to * @return true if the contents of both collections are equal * @since 1.5.0 */ public static boolean equals(List left, Object[] right) { return coercedEquals(right, left); } private static boolean coercedEquals(Object[] left, List right) { if (left == null) { return right == null; } if (right == null) { return false; } if (left.length != right.size()) { return false; } for (int i = left.length - 1; i >= 0; i--) { final Object o1 = left[i]; final Object o2 = right.get(i); if (o1 == null) { if (o2 != null) return false; } else if (!coercedEquals(o1, o2)) { return false; } } return true; } private static boolean coercedEquals(Object o1, Object o2) { if (o1 instanceof Comparable) { if (!(o2 instanceof Comparable && numberAwareCompareTo((Comparable) o1, (Comparable) o2) == 0)) { return false; } } return DefaultTypeTransformation.compareEqual(o1, o2); } /** * Compare the contents of two Lists. Order matters. * If numbers exist in the Lists, then they are compared as numbers, * for example 2 == 2L. If both lists are <code>null</code>, the result * is true; otherwise if either list is <code>null</code>, the result * is <code>false</code>. * <pre class="groovyTestCase">assert ["a", 2].equals(["a", 2]) * assert ![2, "a"].equals("a", 2) * assert [2.0, "a"].equals(2L, "a") // number comparison at work</pre> * * @param left this List * @param right the List being compared to. * @return boolean <code>true</code> if the contents of both lists are identical, * <code>false</code> otherwise. * @since 1.0 */ public static boolean equals(List left, List right) { if (left == null) { return right == null; } if (right == null) { return false; } if (left == right) { return true; } if (left.size() != right.size()) { return false; } final Iterator it1 = left.iterator(), it2 = right.iterator(); while (it1.hasNext()) { final Object o1 = it1.next(); final Object o2 = it2.next(); if (o1 == null) { if (o2 != null) return false; } else if (!coercedEquals(o1, o2)) { return false; } } return true; } /** * Compare the contents of two Sets for equality using Groovy's coercion rules. * <p/> * Returns <tt>true</tt> if the two sets have the same size, and every member * of the specified set is contained in this set (or equivalently, every member * of this set is contained in the specified set). * If numbers exist in the sets, then they are compared as numbers, * for example 2 == 2L. If both sets are <code>null</code>, the result * is true; otherwise if either set is <code>null</code>, the result * is <code>false</code>. Example usage: * <pre class="groovyTestCase"> * Set s1 = ["a", 2] * def s2 = [2, 'a'] as Set * Set s3 = [3, 'a'] * def s4 = [2.0, 'a'] as Set * def s5 = [2L, 'a'] as Set * assert s1.equals(s2) * assert !s1.equals(s3) * assert s1.equals(s4) * assert s1.equals(s5)</pre> * * @param self this Set * @param other the Set being compared to * @return <tt>true</tt> if the contents of both sets are identical * @since 1.8.0 */ public static <T> boolean equals(Set<T> self, Set<T> other) { if (self == null) { return other == null; } if (other == null) { return false; } if (self == other) { return true; } if (self.size() != other.size()) { return false; } final Iterator<T> it1 = self.iterator(); Collection<T> otherItems = new HashSet<T>(other); while (it1.hasNext()) { final Object o1 = it1.next(); final Iterator<T> it2 = otherItems.iterator(); T foundItem = null; boolean found = false; while (it2.hasNext() && foundItem == null) { final T o2 = it2.next(); if (coercedEquals(o1, o2)) { foundItem = o2; found = true; } } if (!found) return false; otherItems.remove(foundItem); } return otherItems.size() == 0; } /** * Compares two Maps treating coerced numerical values as identical. * <p> * Example usage: * <pre class="groovyTestCase">assert [a:2, b:3] == [a:2L, b:3.0]</pre> * * @param self this Map * @param other the Map being compared to * @return <tt>true</tt> if the contents of both maps are identical * @since 1.8.0 */ public static boolean equals(Map self, Map other) { if (self == null) { return other == null; } if (other == null) { return false; } if (self == other) { return true; } if (self.size() != other.size()) { return false; } if (!self.keySet().equals(other.keySet())) { return false; } for (Object key : self.keySet()) { if (!coercedEquals(self.get(key), other.get(key))) { return false; } } return true; } /** * Create a Set composed of the elements of the first set minus the * elements of the given collection. * <p/> * * @param self a set object * @param operands the items to remove from the set * @return the resulting set * @since 1.5.0 */ public static <T> Set<T> minus(Set<T> self, Collection operands) { final Set<T> ansSet = createSimilarSet(self); ansSet.addAll(self); if (operands != null && operands.size() > 0) { final Iterator it1 = self.iterator(); while (it1.hasNext()) { final Object o1 = it1.next(); final Iterator it2 = operands.iterator(); while (it2.hasNext()) { final Object o2 = it2.next(); if (coercedEquals(o1, o2)) { ansSet.remove(o1); } } } } return ansSet; } /** * Create a Set composed of the elements of the first set minus the operand. * * @param self a set object * @param operand the operand to remove from the set * @return the resulting set * @since 1.5.0 */ public static <T> Set<T> minus(Set<T> self, Object operand) { final Set<T> ansSet = createSimilarSet(self); for (T t : self) { if (!coercedEquals(t, operand)) ansSet.add(t); } return ansSet; } /** * Create an array composed of the elements of the first array minus the * elements of the given collection. * * @param self an object array * @param removeMe a Collection of elements to remove * @return an array with the supplied elements removed * @since 1.5.5 */ @SuppressWarnings("unchecked") public static <T> T[] minus(T[] self, Collection<T> removeMe) { return (T[]) minus(toList(self), removeMe).toArray(); } /** * Create an array composed of the elements of the first array minus the * elements of the given array. * * @param self an object array * @param removeMe an array of elements to remove * @return an array with the supplied elements removed * @since 1.5.5 */ @SuppressWarnings("unchecked") public static <T> T[] minus(T[] self, T[] removeMe) { return (T[]) minus(toList(self), toList(removeMe)).toArray(); } /** * Create a List composed of the elements of the first list minus * every occurrence of elements of the given collection. * <pre class="groovyTestCase">assert [1, "a", true, true, false, 5.3] - [true, 5.3] == [1, "a", false]</pre> * * @param self a List * @param removeMe a Collection of elements to remove * @return a List with the supplied elements removed * @since 1.0 */ public static <T> List<T> minus(List<T> self, Collection<T> removeMe) { if (self.size() == 0) return new ArrayList<T>(); boolean nlgnSort = sameType(new Collection[]{self, removeMe}); // We can't use the same tactic as for intersection // since AbstractCollection only does a remove on the first // element it encounters. Comparator<T> numberComparator = new NumberAwareComparator<T>(); if (nlgnSort && (self.get(0) instanceof Comparable)) { //n*LOG(n) version Set<T> answer; if (Number.class.isInstance(self.get(0))) { answer = new TreeSet<T>(numberComparator); answer.addAll(self); for (T t : self) { if (Number.class.isInstance(t)) { for (T t2 : removeMe) { if (Number.class.isInstance(t2)) { if (numberComparator.compare(t, t2) == 0) answer.remove(t); } } } else { if (removeMe.contains(t)) answer.remove(t); } } } else { answer = new TreeSet<T>(numberComparator); answer.addAll(self); answer.removeAll(removeMe); } List<T> ansList = new ArrayList<T>(); for (T o : self) { if (answer.contains(o)) ansList.add(o); } return ansList; } else { //n*n version List<T> tmpAnswer = new LinkedList<T>(self); for (Iterator<T> iter = tmpAnswer.iterator(); iter.hasNext();) { T element = iter.next(); boolean elementRemoved = false; for (Iterator<T> iterator = removeMe.iterator(); iterator.hasNext() && !elementRemoved;) { T elt = iterator.next(); if (numberComparator.compare(element, elt) == 0) { iter.remove(); elementRemoved = true; } } } //remove duplicates //can't use treeset since the base classes are different return new ArrayList<T>(tmpAnswer); } } /** * Create a new List composed of the elements of the first list minus every occurrence of the * operand. * <pre class="groovyTestCase">assert ["a", 5, 5, true] - 5 == ["a", true]</pre> * * @param self a List object * @param operand an element to remove from the list * @return the resulting List with the operand removed * @since 1.0 */ public static <T> List<T> minus(List<T> self, Object operand) { List<T> ansList = new ArrayList<T>(); for (T t : self) { if (!coercedEquals(t, operand)) ansList.add(t); } return ansList; } /** * Create a new object array composed of the elements of the first array * minus the operand. * * @param self an object array * @param operand an element to remove from the array * @return a new array with the operand removed * @since 1.5.5 */ public static <T> T[] minus(T[] self, Object operand) { return (T[]) minus(toList(self), operand).toArray(); } /** * Create a Map composed of the entries of the first map minus the * entries of the given map. * * @param self a map object * @param operands the entries to remove from the map * @return the resulting map * @since 1.7.4 */ public static <K,V> Map<K,V> minus(Map<K,V> self, Map<K,V> operands) { final Map<K,V> ansMap = createSimilarMap(self); ansMap.putAll(self); if (operands != null && operands.size() > 0) { final Iterator<Map.Entry<K,V>> it1 = self.entrySet().iterator(); while (it1.hasNext()) { final Map.Entry<K,V> e1 = it1.next(); final Iterator<Map.Entry<K,V>> it2 = operands.entrySet().iterator(); while (it2.hasNext()) { final Map.Entry<K,V> e2 = it2.next(); if (DefaultTypeTransformation.compareEqual(e1, e2)) { ansMap.remove(e1.getKey()); } } } } return ansMap; } /** * Flatten a collection. This collection and any nested arrays or * collections have their contents (recursively) added to the new collection. * <pre class="groovyTestCase">assert [1,2,3,4,5] == [1,[2,3],[[4]],[],5].flatten()</pre> * * @param self a Collection to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection<?> flatten(Collection<?> self) { return flatten(self, createSimilarCollection(self)); } /** * Flatten an array. This array and any nested arrays or * collections have their contents (recursively) added to the new collection. * * @param self an Array to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection flatten(Object[] self) { return flatten(toList(self), new ArrayList()); } /** * Flatten an array. This array and any nested arrays or * collections have their contents (recursively) added to the new collection. * * @param self a boolean Array to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection flatten(boolean[] self) { return flatten(toList(self), new ArrayList()); } /** * Flatten an array. This array and any nested arrays or * collections have their contents (recursively) added to the new collection. * * @param self a byte Array to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection flatten(byte[] self) { return flatten(toList(self), new ArrayList()); } /** * Flatten an array. This array and any nested arrays or * collections have their contents (recursively) added to the new collection. * * @param self a char Array to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection flatten(char[] self) { return flatten(toList(self), new ArrayList()); } /** * Flatten an array. This array and any nested arrays or * collections have their contents (recursively) added to the new collection. * * @param self a short Array to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection flatten(short[] self) { return flatten(toList(self), new ArrayList()); } /** * Flatten an array. This array and any nested arrays or * collections have their contents (recursively) added to the new collection. * * @param self an int Array to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection flatten(int[] self) { return flatten(toList(self), new ArrayList()); } /** * Flatten an array. This array and any nested arrays or * collections have their contents (recursively) added to the new collection. * * @param self a long Array to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection flatten(long[] self) { return flatten(toList(self), new ArrayList()); } /** * Flatten an array. This array and any nested arrays or * collections have their contents (recursively) added to the new collection. * * @param self a float Array to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection flatten(float[] self) { return flatten(toList(self), new ArrayList()); } /** * Flatten an array. This array and any nested arrays or * collections have their contents (recursively) added to the new collection. * * @param self a double Array to flatten * @return a flattened Collection * @since 1.6.0 */ public static Collection flatten(double[] self) { return flatten(toList(self), new ArrayList()); } private static Collection flatten(Collection elements, Collection addTo) { for (Object element : elements) { if (element instanceof Collection) { flatten((Collection) element, addTo); } else if (element != null && element.getClass().isArray()) { flatten(DefaultTypeTransformation.arrayAsCollection(element), addTo); } else { // found a leaf addTo.add(element); } } return addTo; } /** * Flatten a collection. This collection and any nested arrays or * collections have their contents (recursively) added to the new collection. * For any non-Array, non-Collection object which represents some sort * of collective type, the supplied closure should yield the contained items; * otherwise, the closure should just return any element which corresponds to a leaf. * * @param self a Collection * @param flattenUsing a closure to determine how to flatten non-Array, non-Collection elements * @return a flattened Collection * @since 1.6.0 */ public static <T> Collection<T> flatten(Collection<T> self, Closure<? extends T> flattenUsing) { return flatten(self, createSimilarCollection(self), flattenUsing); } private static <T> Collection<T> flatten(Collection elements, Collection<T> addTo, Closure<? extends T> flattenUsing) { for (Object element : elements) { if (element instanceof Collection) { flatten((Collection) element, addTo, flattenUsing); } else if (element != null && element.getClass().isArray()) { flatten(DefaultTypeTransformation.arrayAsCollection(element), addTo, flattenUsing); } else { T flattened = flattenUsing.call(new Object[]{element}); boolean returnedSelf = flattened == element; if (!returnedSelf && flattened instanceof Collection) { List<?> list = toList((Collection<?>) flattened); if (list.size() == 1 && list.get(0) == element) { returnedSelf = true; } } if (flattened instanceof Collection && !returnedSelf) { flatten((Collection) flattened, addTo, flattenUsing); } else { addTo.add(flattened); } } } return addTo; } /** * Overloads the left shift operator to provide an easy way to append * objects to a Collection. * <pre class="groovyTestCase">def list = [1,2] * list << 3 * assert list == [1,2,3]</pre> * * @param self a Collection * @param value an Object to be added to the collection. * @return same collection, after the value was added to it. * @since 1.0 */ public static <T> Collection<T> leftShift(Collection<T> self, T value) { self.add(value); return self; } /** * Overloads the left shift operator to provide an easy way to append * objects to a BlockingQueue. * In case of bounded queue the method will block till space in the queue become available * <pre class="groovyTestCase">def list = new java.util.concurrent.LinkedBlockingQueue () * list << 3 << 2 << 1 * assert list.iterator().collect{it} == [3,2,1]</pre> * * @param self a Collection * @param value an Object to be added to the collection. * @return same collection, after the value was added to it. * @since 1.7.1 */ public static <T> BlockingQueue<T> leftShift(BlockingQueue<T> self, T value) throws InterruptedException { self.put(value); return self; } /** * Overloads the left shift operator to provide an easy way to append * Map.Entry values to a Map. * * @param self a Map * @param entry a Map.Entry to be added to the Map. * @return same map, after the value has been added to it. * @since 1.6.0 */ public static <K, V> Map<K, V> leftShift(Map<K, V> self, Map.Entry<K, V> entry) { self.put(entry.getKey(), entry.getValue()); return self; } /** * Overloads the left shift operator to provide an easy way to put * one maps entries into another map. This allows the compact syntax * <code>map1 << map2</code>; otherwise it's just a synonym for * <code>putAll</code> though it returns the original map rather than * being a <code>void</code> method. Example usage: * <pre class="groovyTestCase">def map = [a:1, b:2] * map << [c:3, d:4] * assert map == [a:1, b:2, c:3, d:4]</pre> * * @param self a Map * @param other another Map whose entries should be added to the original Map. * @return same map, after the values have been added to it. * @since 1.7.2 */ public static <K, V> Map<K, V> leftShift(Map<K, V> self, Map<K, V> other) { self.putAll(other); return self; } /** * Overloads the left shift operator to provide an easy way to append multiple * objects as string representations to a String. * * @param self a String * @param value an Object * @return a StringBuffer built from this string * @since 1.0 */ public static StringBuffer leftShift(String self, Object value) { return new StringBuffer(self).append(value); } /** * Overloads the left shift operator to provide an easy way to append multiple * objects as string representations to a CharSequence. * * @param self a CharSequence * @param value an Object * @return a StringBuilder built from this CharSequence * @since 1.8.2 */ public static StringBuilder leftShift(CharSequence self, Object value) { return new StringBuilder(self).append(value); } /** * Overloads the left shift operator to provide syntactic sugar for appending to a StringBuilder. * * @param self a StringBuilder * @param value an Object * @return the original StringBuilder * @since 1.8.2 */ public static StringBuilder leftShift(StringBuilder self, Object value) { self.append(value); return self; } protected static StringWriter createStringWriter(String self) { StringWriter answer = new StringWriter(); answer.write(self); return answer; } protected static StringBufferWriter createStringBufferWriter(StringBuffer self) { return new StringBufferWriter(self); } /** * Overloads the left shift operator to provide an easy way to append multiple * objects as string representations to a StringBuffer. * * @param self a StringBuffer * @param value a value to append * @return the StringBuffer on which this operation was invoked * @since 1.0 */ public static StringBuffer leftShift(StringBuffer self, Object value) { self.append(value); return self; } /** * Overloads the left shift operator to provide a mechanism to append * values to a writer. * * @param self a Writer * @param value a value to append * @return the writer on which this operation was invoked * @throws IOException if an I/O error occurs. * @since 1.0 */ public static Writer leftShift(Writer self, Object value) throws IOException { InvokerHelper.write(self, value); return self; } /** * Implementation of the left shift operator for integral types. Non integral * Number types throw UnsupportedOperationException. * * @param self a Number object * @param operand the shift distance by which to left shift the number * @return the resulting number * @since 1.5.0 */ public static Number leftShift(Number self, Number operand) { return NumberMath.leftShift(self, operand); } /** * Implementation of the right shift operator for integral types. Non integral * Number types throw UnsupportedOperationException. * * @param self a Number object * @param operand the shift distance by which to right shift the number * @return the resulting number * @since 1.5.0 */ public static Number rightShift(Number self, Number operand) { return NumberMath.rightShift(self, operand); } /** * Implementation of the right shift (unsigned) operator for integral types. Non integral * Number types throw UnsupportedOperationException. * * @param self a Number object * @param operand the shift distance by which to right shift (unsigned) the number * @return the resulting number * @since 1.5.0 */ public static Number rightShiftUnsigned(Number self, Number operand) { return NumberMath.rightShiftUnsigned(self, operand); } /** * A helper method so that dynamic dispatch of the writer.write(object) method * will always use the more efficient Writable.writeTo(writer) mechanism if the * object implements the Writable interface. * * @param self a Writer * @param writable an object implementing the Writable interface * @throws IOException if an I/O error occurs. * @since 1.0 */ public static void write(Writer self, Writable writable) throws IOException { writable.writeTo(self); } /** * Overloads the leftShift operator to provide an append mechanism to add values to a stream. * * @param self an OutputStream * @param value a value to append * @return a Writer * @throws IOException if an I/O error occurs. * @since 1.0 */ public static Writer leftShift(OutputStream self, Object value) throws IOException { OutputStreamWriter writer = new FlushingStreamWriter(self); leftShift(writer, value); return writer; } /** * Overloads the leftShift operator to add objects to an ObjectOutputStream. * * @param self an ObjectOutputStream * @param value an object to write to the stream * @throws IOException if an I/O error occurs. * @since 1.5.0 */ public static void leftShift(ObjectOutputStream self, Object value) throws IOException { self.writeObject(value); } /** * Pipe an InputStream into an OutputStream for efficient stream copying. * * @param self stream on which to write * @param in stream to read from * @return the outputstream itself * @throws IOException if an I/O error occurs. * @since 1.0 */ public static OutputStream leftShift(OutputStream self, InputStream in) throws IOException { byte[] buf = new byte[1024]; while (true) { int count = in.read(buf, 0, buf.length); if (count == -1) break; if (count == 0) { Thread.yield(); continue; } self.write(buf, 0, count); } self.flush(); return self; } /** * Overloads the leftShift operator to provide an append mechanism to add bytes to a stream. * * @param self an OutputStream * @param value a value to append * @return an OutputStream * @throws IOException if an I/O error occurs. * @since 1.0 */ public static OutputStream leftShift(OutputStream self, byte[] value) throws IOException { self.write(value); self.flush(); return self; } // Primitive type array methods //------------------------------------------------------------------------- /** * Support the subscript operator with a range for a byte array * * @param array a byte array * @param range a range indicating the indices for the items to retrieve * @return list of the retrieved bytes * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Byte> getAt(byte[] array, Range range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with a range for a char array * * @param array a char array * @param range a range indicating the indices for the items to retrieve * @return list of the retrieved chars * @since 1.5.0 */ @SuppressWarnings("unchecked") public static List<Character> getAt(char[] array, Range range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with a range for a short array * * @param array a short array * @param range a range indicating the indices for the items to retrieve * @return list of the retrieved shorts * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Short> getAt(short[] array, Range range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with a range for an int array * * @param array an int array * @param range a range indicating the indices for the items to retrieve * @return list of the ints at the given indices * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Integer> getAt(int[] array, Range range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with a range for a long array * * @param array a long array * @param range a range indicating the indices for the items to retrieve * @return list of the retrieved longs * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Long> getAt(long[] array, Range range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with a range for a float array * * @param array a float array * @param range a range indicating the indices for the items to retrieve * @return list of the retrieved floats * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Float> getAt(float[] array, Range range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with a range for a double array * * @param array a double array * @param range a range indicating the indices for the items to retrieve * @return list of the retrieved doubles * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Double> getAt(double[] array, Range range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with a range for a boolean array * * @param array a boolean array * @param range a range indicating the indices for the items to retrieve * @return list of the retrieved booleans * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Boolean> getAt(boolean[] array, Range range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an IntRange for a byte array * * @param array a byte array * @param range an IntRange indicating the indices for the items to retrieve * @return list of the retrieved bytes * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Byte> getAt(byte[] array, IntRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an IntRange for a char array * * @param array a char array * @param range an IntRange indicating the indices for the items to retrieve * @return list of the retrieved chars * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Character> getAt(char[] array, IntRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an IntRange for a short array * * @param array a short array * @param range an IntRange indicating the indices for the items to retrieve * @return list of the retrieved shorts * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Short> getAt(short[] array, IntRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an IntRange for an int array * * @param array an int array * @param range an IntRange indicating the indices for the items to retrieve * @return list of the retrieved ints * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Integer> getAt(int[] array, IntRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an IntRange for a long array * * @param array a long array * @param range an IntRange indicating the indices for the items to retrieve * @return list of the retrieved longs * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Long> getAt(long[] array, IntRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an IntRange for a float array * * @param array a float array * @param range an IntRange indicating the indices for the items to retrieve * @return list of the retrieved floats * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Float> getAt(float[] array, IntRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an IntRange for a double array * * @param array a double array * @param range an IntRange indicating the indices for the items to retrieve * @return list of the retrieved doubles * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Double> getAt(double[] array, IntRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an IntRange for a boolean array * * @param array a boolean array * @param range an IntRange indicating the indices for the items to retrieve * @return list of the retrieved booleans * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Boolean> getAt(boolean[] array, IntRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an ObjectRange for a byte array * * @param array a byte array * @param range an ObjectRange indicating the indices for the items to retrieve * @return list of the retrieved bytes * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Byte> getAt(byte[] array, ObjectRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an ObjectRange for a char array * * @param array a char array * @param range an ObjectRange indicating the indices for the items to retrieve * @return list of the retrieved chars * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Character> getAt(char[] array, ObjectRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an ObjectRange for a short array * * @param array a short array * @param range an ObjectRange indicating the indices for the items to retrieve * @return list of the retrieved shorts * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Short> getAt(short[] array, ObjectRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an ObjectRange for an int array * * @param array an int array * @param range an ObjectRange indicating the indices for the items to retrieve * @return list of the retrieved ints * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Integer> getAt(int[] array, ObjectRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an ObjectRange for a long array * * @param array a long array * @param range an ObjectRange indicating the indices for the items to retrieve * @return list of the retrieved longs * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Long> getAt(long[] array, ObjectRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an ObjectRange for a float array * * @param array a float array * @param range an ObjectRange indicating the indices for the items to retrieve * @return list of the retrieved floats * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Float> getAt(float[] array, ObjectRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an ObjectRange for a double array * * @param array a double array * @param range an ObjectRange indicating the indices for the items to retrieve * @return list of the retrieved doubles * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Double> getAt(double[] array, ObjectRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with an ObjectRange for a byte array * * @param array a byte array * @param range an ObjectRange indicating the indices for the items to retrieve * @return list of the retrieved bytes * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Boolean> getAt(boolean[] array, ObjectRange range) { return primitiveArrayGet(array, range); } /** * Support the subscript operator with a collection for a byte array * * @param array a byte array * @param indices a collection of indices for the items to retrieve * @return list of the bytes at the given indices * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Byte> getAt(byte[] array, Collection indices) { return primitiveArrayGet(array, indices); } /** * Support the subscript operator with a collection for a char array * * @param array a char array * @param indices a collection of indices for the items to retrieve * @return list of the chars at the given indices * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Character> getAt(char[] array, Collection indices) { return primitiveArrayGet(array, indices); } /** * Support the subscript operator with a collection for a short array * * @param array a short array * @param indices a collection of indices for the items to retrieve * @return list of the shorts at the given indices * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Short> getAt(short[] array, Collection indices) { return primitiveArrayGet(array, indices); } /** * Support the subscript operator with a collection for an int array * * @param array an int array * @param indices a collection of indices for the items to retrieve * @return list of the ints at the given indices * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Integer> getAt(int[] array, Collection indices) { return primitiveArrayGet(array, indices); } /** * Support the subscript operator with a collection for a long array * * @param array a long array * @param indices a collection of indices for the items to retrieve * @return list of the longs at the given indices * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Long> getAt(long[] array, Collection indices) { return primitiveArrayGet(array, indices); } /** * Support the subscript operator with a collection for a float array * * @param array a float array * @param indices a collection of indices for the items to retrieve * @return list of the floats at the given indices * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Float> getAt(float[] array, Collection indices) { return primitiveArrayGet(array, indices); } /** * Support the subscript operator with a collection for a double array * * @param array a double array * @param indices a collection of indices for the items to retrieve * @return list of the doubles at the given indices * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Double> getAt(double[] array, Collection indices) { return primitiveArrayGet(array, indices); } /** * Support the subscript operator with a collection for a boolean array * * @param array a boolean array * @param indices a collection of indices for the items to retrieve * @return list of the booleans at the given indices * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Boolean> getAt(boolean[] array, Collection indices) { return primitiveArrayGet(array, indices); } /** * Support the subscript operator for a Bitset * * @param self a BitSet * @param index index to retrieve * @return value of the bit at the given index * @see java.util.BitSet * @since 1.5.0 */ public static boolean getAt(BitSet self, int index) { return self.get(index); } /** * Support retrieving a subset of a BitSet using a Range * * @param self a BitSet * @param range a Range defining the desired subset * @return a new BitSet that represents the requested subset * @see java.util.BitSet * @see groovy.lang.IntRange * @since 1.5.0 */ public static BitSet getAt(BitSet self, IntRange range) { int from = DefaultTypeTransformation.intUnbox(range.getFrom()); int to = DefaultTypeTransformation.intUnbox(range.getTo()); BitSet result = new BitSet(); int numberOfBits = to - from + 1; int adjuster = 1; int offset = from; if (range.isReverse()) { adjuster = -1; offset = to; } for (int i = 0; i < numberOfBits; i++) { result.set(i, self.get(offset + (adjuster * i))); } return result; } // public static Boolean putAt(boolean[] array, int idx, Boolean newValue) { // return (Boolean) primitiveArrayPut(array, idx, newValue); // } // // public static Byte putAt(byte[] array, int idx, Object newValue) { // if (!(newValue instanceof Byte)) { // Number n = (Number) newValue; // newValue = new Byte(n.byteValue()); // } // return (Byte) primitiveArrayPut(array, idx, newValue); // } // // public static Character putAt(char[] array, int idx, Object newValue) { // if (newValue instanceof String) { // String s = (String) newValue; // if (s.length() != 1) throw new IllegalArgumentException("String of length 1 expected but got a bigger one"); // char c = s.charAt(0); // newValue = new Character(c); // } // return (Character) primitiveArrayPut(array, idx, newValue); // } // // public static Short putAt(short[] array, int idx, Object newValue) { // if (!(newValue instanceof Short)) { // Number n = (Number) newValue; // newValue = new Short(n.shortValue()); // } // return (Short) primitiveArrayPut(array, idx, newValue); // } // // public static Integer putAt(int[] array, int idx, Object newValue) { // if (!(newValue instanceof Integer)) { // Number n = (Number) newValue; // newValue = Integer.valueOf(n.intValue()); // } // array [normaliseIndex(idx,array.length)] = ((Integer)newValue).intValue(); // return (Integer) newValue; // } // // public static Long putAt(long[] array, int idx, Object newValue) { // if (!(newValue instanceof Long)) { // Number n = (Number) newValue; // newValue = new Long(n.longValue()); // } // return (Long) primitiveArrayPut(array, idx, newValue); // } // // public static Float putAt(float[] array, int idx, Object newValue) { // if (!(newValue instanceof Float)) { // Number n = (Number) newValue; // newValue = new Float(n.floatValue()); // } // return (Float) primitiveArrayPut(array, idx, newValue); // } // // public static Double putAt(double[] array, int idx, Object newValue) { // if (!(newValue instanceof Double)) { // Number n = (Number) newValue; // newValue = new Double(n.doubleValue()); // } // return (Double) primitiveArrayPut(array, idx, newValue); // } /** * Support assigning a range of values with a single assignment statement. * * @param self a BitSet * @param range the range of values to set * @param value value * @see java.util.BitSet * @see groovy.lang.Range * @since 1.5.0 */ public static void putAt(BitSet self, IntRange range, boolean value) { int from = DefaultTypeTransformation.intUnbox(range.getFrom()); int to = DefaultTypeTransformation.intUnbox(range.getTo()); // If this is a backwards range, reverse the arguments to set. if (from > to) { int tmp = to; to = from; from = tmp; } self.set(from, to + 1, value); } /** * Support subscript-style assignment for a BitSet. * * @param self a BitSet * @param index index of the entry to set * @param value value * @see java.util.BitSet * @since 1.5.0 */ public static void putAt(BitSet self, int index, boolean value) { self.set(index, value); } /** * Allows arrays to behave similar to collections. * @param array a boolean array * @return the length of the array * @see java.lang.reflect.Array#getLength(java.lang.Object) * @since 1.5.0 */ public static int size(boolean[] array) { return Array.getLength(array); } /** * Allows arrays to behave similar to collections. * @param array a byte array * @return the length of the array * @see java.lang.reflect.Array#getLength(java.lang.Object) * @since 1.0 */ public static int size(byte[] array) { return Array.getLength(array); } /** * Allows arrays to behave similar to collections. * @param array a char array * @return the length of the array * @see java.lang.reflect.Array#getLength(java.lang.Object) * @since 1.0 */ public static int size(char[] array) { return Array.getLength(array); } /** * Allows arrays to behave similar to collections. * @param array a short array * @return the length of the array * @see java.lang.reflect.Array#getLength(java.lang.Object) * @since 1.0 */ public static int size(short[] array) { return Array.getLength(array); } /** * Allows arrays to behave similar to collections. * @param array an int array * @return the length of the array * @see java.lang.reflect.Array#getLength(java.lang.Object) * @since 1.0 */ public static int size(int[] array) { return Array.getLength(array); } /** * Allows arrays to behave similar to collections. * @param array a long array * @return the length of the array * @see java.lang.reflect.Array#getLength(java.lang.Object) * @since 1.0 */ public static int size(long[] array) { return Array.getLength(array); } /** * Allows arrays to behave similar to collections. * @param array a float array * @return the length of the array * @see java.lang.reflect.Array#getLength(java.lang.Object) * @since 1.0 */ public static int size(float[] array) { return Array.getLength(array); } /** * Allows arrays to behave similar to collections. * @param array a double array * @return the length of the array * @see java.lang.reflect.Array#getLength(java.lang.Object) * @since 1.0 */ public static int size(double[] array) { return Array.getLength(array); } /** * Converts this array to a List of the same size, with each element * added to the list. * * @param array a byte array * @return a list containing the contents of this array. * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Byte> toList(byte[] array) { return DefaultTypeTransformation.primitiveArrayToList(array); } /** * Converts this array to a List of the same size, with each element * added to the list. * * @param array a boolean array * @return a list containing the contents of this array. * @since 1.6.0 */ @SuppressWarnings("unchecked") public static List<Boolean> toList(boolean[] array) { return DefaultTypeTransformation.primitiveArrayToList(array); } /** * Converts this array to a List of the same size, with each element * added to the list. * * @param array a char array * @return a list containing the contents of this array. * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Character> toList(char[] array) { return DefaultTypeTransformation.primitiveArrayToList(array); } /** * Converts this array to a List of the same size, with each element * added to the list. * * @param array a short array * @return a list containing the contents of this array. * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Short> toList(short[] array) { return DefaultTypeTransformation.primitiveArrayToList(array); } /** * Converts this array to a List of the same size, with each element * added to the list. * * @param array an int array * @return a list containing the contents of this array. * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Integer> toList(int[] array) { return DefaultTypeTransformation.primitiveArrayToList(array); } /** * Converts this array to a List of the same size, with each element * added to the list. * * @param array a long array * @return a list containing the contents of this array. * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Long> toList(long[] array) { return DefaultTypeTransformation.primitiveArrayToList(array); } /** * Converts this array to a List of the same size, with each element * added to the list. * * @param array a float array * @return a list containing the contents of this array. * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Float> toList(float[] array) { return DefaultTypeTransformation.primitiveArrayToList(array); } /** * Converts this array to a List of the same size, with each element * added to the list. * * @param array a double array * @return a list containing the contents of this array. * @since 1.0 */ @SuppressWarnings("unchecked") public static List<Double> toList(double[] array) { return DefaultTypeTransformation.primitiveArrayToList(array); } /** * Converts this array to a Set, with each unique element * added to the set. * * @param array a byte array * @return a set containing the unique contents of this array. * @since 1.8.0 */ @SuppressWarnings("unchecked") public static Set<Byte> toSet(byte[] array) { return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); } /** * Converts this array to a Set, with each unique element * added to the set. * * @param array a boolean array * @return a set containing the unique contents of this array. * @since 1.8.0 */ @SuppressWarnings("unchecked") public static Set<Boolean> toSet(boolean[] array) { return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); } /** * Converts this array to a Set, with each unique element * added to the set. * * @param array a char array * @return a set containing the unique contents of this array. * @since 1.8.0 */ @SuppressWarnings("unchecked") public static Set<Character> toSet(char[] array) { return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); } /** * Converts this array to a Set, with each unique element * added to the set. * * @param array a short array * @return a set containing the unique contents of this array. * @since 1.8.0 */ @SuppressWarnings("unchecked") public static Set<Short> toSet(short[] array) { return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); } /** * Converts this array to a Set, with each unique element * added to the set. * * @param array an int array * @return a set containing the unique contents of this array. * @since 1.8.0 */ @SuppressWarnings("unchecked") public static Set<Integer> toSet(int[] array) { return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); } /** * Converts this array to a Set, with each unique element * added to the set. * * @param array a long array * @return a set containing the unique contents of this array. * @since 1.8.0 */ @SuppressWarnings("unchecked") public static Set<Long> toSet(long[] array) { return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); } /** * Converts this array to a Set, with each unique element * added to the set. * * @param array a float array * @return a set containing the unique contents of this array. * @since 1.8.0 */ @SuppressWarnings("unchecked") public static Set<Float> toSet(float[] array) { return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); } /** * Converts this array to a Set, with each unique element * added to the set. * * @param array a double array * @return a set containing the unique contents of this array. * @since 1.8.0 */ @SuppressWarnings("unchecked") public static Set<Double> toSet(double[] array) { return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); } /** * Convert a Collection to a Set. Always returns a new Set * even if the Collection is already a Set. * <p> * Example usage: * <pre class="groovyTestCase"> * def result = [1, 2, 2, 2, 3].toSet() * assert result instanceof Set * assert result == [1, 2, 3] as Set * </pre> * * @param self a collection * @return a Set * @since 1.8.0 */ public static <T> Set<T> toSet(Collection<T> self) { Set<T> answer = new HashSet<T>(self.size()); answer.addAll(self); return answer; } /** * Convert an iterator to a Set. The iterator will become * exhausted of elements after making this conversion. * * @param self an iterator * @return a Set * @since 1.8.0 */ public static <T> Set<T> toSet(Iterator<T> self) { Set<T> answer = new HashSet<T>(); while (self.hasNext()) { answer.add(self.next()); } return answer; } /** * Convert an enumeration to a Set. * * @param self an enumeration * @return a Set * @since 1.8.0 */ public static <T> Set<T> toSet(Enumeration<T> self) { Set<T> answer = new HashSet<T>(); while (self.hasMoreElements()) { answer.add(self.nextElement()); } return answer; } /** * Implements the getAt(int) method for primitive type arrays. * * @param self an array object * @param idx the index of interest * @return the returned value from the array * @since 1.5.0 */ protected static Object primitiveArrayGet(Object self, int idx) { return Array.get(self, normaliseIndex(idx, Array.getLength(self))); } /** * Implements the getAt(Range) method for primitive type arrays. * * @param self an array object * @param range the range of indices of interest * @return the returned values from the array corresponding to the range * @since 1.5.0 */ protected static List primitiveArrayGet(Object self, Range range) { List answer = new ArrayList(); for (Object next : range) { int idx = DefaultTypeTransformation.intUnbox(next); answer.add(primitiveArrayGet(self, idx)); } return answer; } /** * Implements the getAt(Collection) method for primitive type arrays. Each * value in the collection argument is assumed to be a valid array index. * The value at each index is then added to a list which is returned. * * @param self an array object * @param indices the indices of interest * @return the returned values from the array * @since 1.0 */ protected static List primitiveArrayGet(Object self, Collection indices) { List answer = new ArrayList(); for (Object value : indices) { if (value instanceof Range) { answer.addAll(primitiveArrayGet(self, (Range) value)); } else if (value instanceof List) { answer.addAll(primitiveArrayGet(self, (List) value)); } else { int idx = DefaultTypeTransformation.intUnbox(value); answer.add(primitiveArrayGet(self, idx)); } } return answer; } /** * Implements the setAt(int idx) method for primitive type arrays. * * @param self an object * @param idx the index of interest * @param newValue the new value to be put into the index of interest * @return the added value * @since 1.5.0 */ protected static Object primitiveArrayPut(Object self, int idx, Object newValue) { Array.set(self, normaliseIndex(idx, Array.getLength(self)), newValue); return newValue; } // String methods //------------------------------------------------------------------------- /** * Converts the given string into a Character object * using the first character in the string. * * @param self a String * @return the first Character * @since 1.0 */ public static Character toCharacter(String self) { return self.charAt(0); } /** * Converts the given string into a Boolean object. * If the trimmed string is "true", "y" or "1" (ignoring case) * then the result is true otherwise it is false. * * @param self a String * @return The Boolean value * @since 1.0 */ public static Boolean toBoolean(String self) { final String trimmed = self.trim(); if ("true".equalsIgnoreCase(trimmed) || "y".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) { return Boolean.TRUE; } else { return Boolean.FALSE; } } /** * Identity conversion which returns Boolean.TRUE for a true Boolean and Boolean.FALSE for a false Boolean. * * @param self a Boolean * @return the original Boolean * @since 1.7.6 */ public static Boolean toBoolean(Boolean self) { return self; } /** * Convenience method to split a string (with whitespace as delimiter) * Like tokenize, but returns an Array of Strings instead of a List * * @param self the string to split * @return String[] result of split * @since 1.5.0 */ public static String[] split(String self) { StringTokenizer st = new StringTokenizer(self); String[] strings = new String[st.countTokens()]; for (int i = 0; i < strings.length; i++) { strings[i] = st.nextToken(); } return strings; } /** * Convenience method to split a CharSequence (with whitespace as delimiter). * Similar to tokenize, but returns an Array of CharSequence instead of a List. * * @param self the CharSequence to split * @return CharSequence[] result of split * @see #split(String) * @since 1.8.2 */ public static CharSequence[] split(CharSequence self) { return split(self.toString()); } /** * Convenience method to capitalize the first letter of a string * (typically the first letter of a word). Example usage: * <pre class="groovyTestCase"> * assert 'h'.capitalize() == 'H' * assert 'hello'.capitalize() == 'Hello' * assert 'hello world'.capitalize() == 'Hello world' * assert 'Hello World' == * 'hello world'.split(' ').collect{ it.capitalize() }.join(' ') * </pre> * * @param self The string to capitalize * @return The capitalized String * @since 1.7.3 */ public static String capitalize(String self) { if (self == null || self.length() == 0) return self; return Character.toUpperCase(self.charAt(0)) + self.substring(1); } /** * Convenience method to capitalize the first letter of a CharSequence. * * @param self The CharSequence to capitalize * @return The capitalized CharSequence * @see #capitalize(String) * @since 1.8.2 */ public static CharSequence capitalize(CharSequence self) { return capitalize(self.toString()); } /** * Expands all tabs into spaces with tabStops of size 8. * * @param self A String to expand * @return The expanded String * @since 1.7.3 * @see #expand(java.lang.String, int) */ public static String expand(String self) { return expand(self, 8); } /** * Expands all tabs into spaces with tabStops of size 8. * * @param self A CharSequence to expand * @return The expanded CharSequence * @see #expand(java.lang.String) * @since 1.8.2 */ public static CharSequence expand(CharSequence self) { return expand(self.toString(), 8); } /** * Expands all tabs into spaces. If the String has multiple * lines, expand each line - restarting tab stops at the start * of each line. * * @param self A String to expand * @param tabStop The number of spaces a tab represents * @return The expanded String * @since 1.7.3 */ public static String expand(String self, int tabStop) { if (self.length() == 0) return self; try { StringBuilder builder = new StringBuilder(); for (String line : readLines(self)) { builder.append(expandLine(line, tabStop)); builder.append("\n"); } // remove the normalized ending line ending if it was not present if (!self.endsWith("\n")) { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } catch (IOException e) { /* ignore */ } return self; } /** * Expands all tabs into spaces. If the CharSequence has multiple * lines, expand each line - restarting tab stops at the start * of each line. * * @param self A CharSequence to expand * @param tabStop The number of spaces a tab represents * @return The expanded CharSequence * @see #expand(String, int) * @since 1.8.2 */ public static CharSequence expand(CharSequence self, int tabStop) { return expand(self.toString(), tabStop); } /** * Expands all tabs into spaces. Assumes the String represents a single line of text. * * @param self A line to expand * @param tabStop The number of spaces a tab represents * @return The expanded String * @since 1.7.3 */ public static String expandLine(String self, int tabStop) { int index; while ((index = self.indexOf('\t')) != -1) { StringBuilder builder = new StringBuilder(self); int count = tabStop - index % tabStop; builder.deleteCharAt(index); for (int i = 0; i < count; i++) builder.insert(index, " "); self = builder.toString(); } return self; } /** * Expands all tabs into spaces. Assumes the CharSequence represents a single line of text. * * @param self A line to expand * @param tabStop The number of spaces a tab represents * @return The expanded CharSequence * @see #expandLine(String, int) * @since 1.8.2 */ public static CharSequence expandLine(CharSequence self, int tabStop) { return expandLine(self.toString(), tabStop); } /** * Replaces sequences of whitespaces with tabs using tabStops of size 8. * * @param self A String to unexpand * @return The unexpanded String * @since 1.7.3 * @see #unexpand(java.lang.String, int) */ public static String unexpand(String self) { return unexpand(self, 8); } /** * Replaces sequences of whitespaces with tabs using tabStops of size 8. * * @param self A CharSequence to unexpand * @return The unexpanded CharSequence * @see #unexpand(java.lang.String) * @since 1.8.2 */ public static CharSequence unexpand(CharSequence self) { return unexpand(self.toString()); } /** * Replaces sequences of whitespaces with tabs. * * @param self A String to unexpand * @param tabStop The number of spaces a tab represents * @return The unexpanded String * @since 1.7.3 */ public static String unexpand(String self, int tabStop) { if (self.length() == 0) return self; try { StringBuilder builder = new StringBuilder(); for (String line : readLines(self)) { builder.append(unexpandLine(line, tabStop)); builder.append("\n"); } // remove the normalized ending line ending if it was not present if (!self.endsWith("\n")) { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } catch (IOException e) { /* ignore */ } return self; } /** * Replaces sequences of whitespaces with tabs. * * @param self A CharSequence to unexpand * @param tabStop The number of spaces a tab represents * @return The unexpanded CharSequence * @see #unexpand(String, int) * @since 1.8.2 */ public static CharSequence unexpand(CharSequence self, int tabStop) { return unexpand(self.toString(), tabStop); } /** * Replaces sequences of whitespaces with tabs within a line. * * @param self A line to unexpand * @param tabStop The number of spaces a tab represents * @return The unexpanded String * @since 1.7.3 */ public static String unexpandLine(String self, int tabStop) { StringBuilder builder = new StringBuilder(self); int index = 0; while (index + tabStop < builder.length()) { // cut original string in tabstop-length pieces String piece = builder.substring(index, index + tabStop); // count trailing whitespace characters int count = 0; while ((count < tabStop) && (Character.isWhitespace(piece.charAt(tabStop - (count + 1))))) count++; // replace if whitespace was found if (count > 0) { piece = piece.substring(0, tabStop - count) + '\t'; builder.replace(index, index + tabStop, piece); index = index + tabStop - (count - 1); } else index = index + tabStop; } return builder.toString(); } /** * Replaces sequences of whitespaces with tabs within a line. * * @param self A line to unexpand * @param tabStop The number of spaces a tab represents * @return The unexpanded CharSequence * @see #unexpandLine(String, int) * @since 1.8.2 */ public static CharSequence unexpandLine(CharSequence self, int tabStop) { return unexpandLine(self.toString(), tabStop); } /** * Convenience method to split a GString (with whitespace as delimiter). * * @param self the GString to split * @return String[] result of split * @see #split(java.lang.String) * @since 1.6.1 */ public static String[] split(GString self) { return split(self.toString()); } /** * Tokenize a String based on the given string delimiter. * * @param self a String * @param token the delimiter * @return a List of tokens * @see java.util.StringTokenizer#StringTokenizer(java.lang.String, java.lang.String) * @since 1.0 */ @SuppressWarnings("unchecked") public static List<String> tokenize(String self, String token) { return InvokerHelper.asList(new StringTokenizer(self, token)); } /** * Tokenize a CharSequence based on the given CharSequence delimiter. * * @param self a CharSequence * @param token the delimiter * @return a List of tokens * @see #tokenize(String, String) * @since 1.8.2 */ public static List<CharSequence> tokenize(CharSequence self, CharSequence token) { return new ArrayList<CharSequence>(tokenize(self.toString(), token.toString())); } /** * Tokenize a String based on the given character delimiter. * For example: * <pre class="groovyTestCase"> * char pathSep = ':' * assert "/tmp:/usr".tokenize(pathSep) == ["/tmp", "/usr"] * </pre> * * @param self a String * @param token the delimiter * @return a List of tokens * @see java.util.StringTokenizer#StringTokenizer(java.lang.String, java.lang.String) * @since 1.7.2 */ public static List<String> tokenize(String self, Character token) { return tokenize(self, token.toString()); } /** * Tokenize a CharSequence based on the given character delimiter. * * @param self a CharSequence * @param token the delimiter * @return a List of tokens * @see #tokenize(String, Character) * @since 1.8.2 */ public static List<CharSequence> tokenize(CharSequence self, Character token) { return tokenize(self, token.toString()); } /** * Tokenize a String (with a whitespace as the delimiter). * * @param self a String * @return a List of tokens * @see java.util.StringTokenizer#StringTokenizer(java.lang.String) * @since 1.0 */ @SuppressWarnings("unchecked") public static List<String> tokenize(String self) { return InvokerHelper.asList(new StringTokenizer(self)); } /** * Tokenize a CharSequence (with a whitespace as the delimiter). * * @param self a CharSequence * @return a List of tokens * @see #tokenize(String) * @since 1.8.2 */ public static List<CharSequence> tokenize(CharSequence self) { return new ArrayList<CharSequence>(tokenize(self.toString())); } /** * Appends the String representation of the given operand to this string. * * @param left a String * @param value any Object * @return the new string with the object appended * @since 1.0 */ public static String plus(String left, Object value) { return left + toString(value); } /** * Appends the String representation of the given operand to this string. * * @param left a CharSequence * @param value any Object * @return the new CharSequence with the object appended * @since 1.8.2 */ public static CharSequence plus(CharSequence left, Object value) { return left + toString(value); } /** * Appends a String to the string representation of this number. * * @param value a Number * @param right a String * @return a String * @since 1.0 */ public static String plus(Number value, String right) { return toString(value) + right; } /** * Appends a String to this StringBuffer. * * @param left a StringBuffer * @param value a String * @return a String * @since 1.0 */ public static String plus(StringBuffer left, String value) { return left + value; } /** * Remove a part of a String. This replaces the first occurrence * of target within self with '' and returns the result. If * target is a regex Pattern, the first occurrence of that * pattern will be removed (using regex matching), otherwise * the first occurrence of target.toString() will be removed. * * @param self a String * @param target an object representing the part to remove * @return a String minus the part to be removed * @since 1.0 */ public static String minus(String self, Object target) { if (target instanceof Pattern) { return ((Pattern)target).matcher(self).replaceFirst(""); } String text = toString(target); int index = self.indexOf(text); if (index == -1) return self; int end = index + text.length(); if (self.length() > end) { return self.substring(0, index) + self.substring(end); } return self.substring(0, index); } /** * Remove a part of a CharSequence by replacing the first occurrence * of target within self with '' and returns the result. * * @param self a CharSequence * @param target an object representing the part to remove * @return a CharSequence minus the part to be removed * @see #minus(String, Object) * @since 1.8.2 */ public static CharSequence minus(CharSequence self, Object target) { return minus(self.toString(), target); } /** * Provide an implementation of contains() like * {@link java.util.Collection#contains(java.lang.Object)} to make Strings more polymorphic. * This method is not required on JDK 1.5 onwards * * @param self a String * @param text a String to look for * @return true if this string contains the given text * @since 1.0 */ public static boolean contains(String self, String text) { int idx = self.indexOf(text); return idx >= 0; } /** * Provide an implementation of contains() like * {@link java.util.Collection#contains(java.lang.Object)} to make CharSequences more polymorphic. * * @param self a CharSequence * @param text the CharSequence to look for * @return true if this CharSequence contains the given text * @see #contains(String, String) * @since 1.8.2 */ public static boolean contains(CharSequence self, CharSequence text) { return contains(self.toString(), text.toString()); } /** * Count the number of occurrences of a substring. * * @param self a String * @param text a substring * @return the number of occurrences of the given string inside this String * @since 1.0 */ public static int count(String self, String text) { int answer = 0; for (int idx = 0; true; idx++) { idx = self.indexOf(text, idx); if (idx >= 0) { ++answer; } else { break; } } return answer; } /** * Count the number of occurrences of a sub CharSequence. * * @param self a CharSequence * @param text a sub CharSequence * @return the number of occurrences of the given CharSequence inside this CharSequence * @see #count(String, String) * @since 1.8.2 */ public static int count(CharSequence self, CharSequence text) { return count(self.toString(), text.toString()); } /** * This method is called by the ++ operator for the class String. * It increments the last character in the given string. If the * character in the string is Character.MAX_VALUE a Character.MIN_VALUE * will be appended. The empty string is incremented to a string * consisting of the character Character.MIN_VALUE. * * @param self a String * @return an incremented String * @since 1.0 */ public static String next(String self) { StringBuilder buffer = new StringBuilder(self); if (buffer.length() == 0) { buffer.append(Character.MIN_VALUE); } else { char last = buffer.charAt(buffer.length() - 1); if (last == Character.MAX_VALUE) { buffer.append(Character.MIN_VALUE); } else { char next = last; next++; buffer.setCharAt(buffer.length() - 1, next); } } return buffer.toString(); } /** * This method is called by the ++ operator for the class CharSequence. * * @param self a CharSequence * @return an incremented CharSequence * @see #next(String) * @since 1.8.2 */ public static CharSequence next(CharSequence self) { return next(self.toString()); } /** * This method is called by the -- operator for the class String. * It decrements the last character in the given string. If the * character in the string is Character.MIN_VALUE it will be deleted. * The empty string can't be decremented. * * @param self a String * @return a String with a decremented digit at the end * @since 1.0 */ public static String previous(String self) { StringBuilder buffer = new StringBuilder(self); if (buffer.length() == 0) throw new IllegalArgumentException("the string is empty"); char last = buffer.charAt(buffer.length() - 1); if (last == Character.MIN_VALUE) { buffer.deleteCharAt(buffer.length() - 1); } else { char next = last; next--; buffer.setCharAt(buffer.length() - 1, next); } return buffer.toString(); } /** * This method is called by the -- operator for the class CharSequence. * * @param self a CharSequence * @return a CharSequence with a decremented digit at the end * @see #previous(String) * @since 1.8.2 */ public static CharSequence previous(CharSequence self) { return previous(self.toString()); } /** * Executes the command specified by <code>self</code> as a command-line process. * <p>For more control over Process construction you can use * <code>java.lang.ProcessBuilder</code> (JDK 1.5+).</p> * * @param self a command line String * @return the Process which has just started for this command line representation * @throws IOException if an IOException occurs. * @since 1.0 */ public static Process execute(final String self) throws IOException { return Runtime.getRuntime().exec(self); } /** * Executes the command specified by <code>self</code> with environment defined by <code>envp</code> * and under the working directory <code>dir</code>. * <p>For more control over Process construction you can use * <code>java.lang.ProcessBuilder</code> (JDK 1.5+).</p> * * @param self a command line String to be executed. * @param envp an array of Strings, each element of which * has environment variable settings in the format * <i>name</i>=<i>value</i>, or * <tt>null</tt> if the subprocess should inherit * the environment of the current process. * @param dir the working directory of the subprocess, or * <tt>null</tt> if the subprocess should inherit * the working directory of the current process. * @return the Process which has just started for this command line representation. * @throws IOException if an IOException occurs. * @since 1.0 */ public static Process execute(final String self, final String[] envp, final File dir) throws IOException { return Runtime.getRuntime().exec(self, envp, dir); } /** * Executes the command specified by <code>self</code> with environment defined * by <code>envp</code> and under the working directory <code>dir</code>. * <p>For more control over Process construction you can use * <code>java.lang.ProcessBuilder</code> (JDK 1.5+).</p> * * @param self a command line String to be executed. * @param envp a List of Objects (converted to Strings using toString), each member of which * has environment variable settings in the format * <i>name</i>=<i>value</i>, or * <tt>null</tt> if the subprocess should inherit * the environment of the current process. * @param dir the working directory of the subprocess, or * <tt>null</tt> if the subprocess should inherit * the working directory of the current process. * @return the Process which has just started for this command line representation. * @throws IOException if an IOException occurs. * @since 1.0 */ public static Process execute(final String self, final List envp, final File dir) throws IOException { return execute(self, stringify(envp), dir); } /** * Executes the command specified by the given <code>String</code> array. * The first item in the array is the command; the others are the parameters. * <p>For more control over Process construction you can use * <code>java.lang.ProcessBuilder</code> (JDK 1.5+).</p> * * @param commandArray an array of <code>String<code> containing the command name and * parameters as separate items in the array. * @return the Process which has just started for this command line representation. * @throws IOException if an IOException occurs. * @since 1.0 */ public static Process execute(final String[] commandArray) throws IOException { return Runtime.getRuntime().exec(commandArray); } /** * Executes the command specified by the <code>String</code> array given in the first parameter, * with the environment defined by <code>envp</code> and under the working directory <code>dir</code>. * The first item in the array is the command; the others are the parameters. * <p>For more control over Process construction you can use * <code>java.lang.ProcessBuilder</code> (JDK 1.5+).</p> * * @param commandArray an array of <code>String<code> containing the command name and * parameters as separate items in the array. * @param envp an array of Strings, each member of which * has environment variable settings in the format * <i>name</i>=<i>value</i>, or * <tt>null</tt> if the subprocess should inherit * the environment of the current process. * @param dir the working directory of the subprocess, or * <tt>null</tt> if the subprocess should inherit * the working directory of the current process. * @return the Process which has just started for this command line representation. * @throws IOException if an IOException occurs. * @since 1.7.1 */ public static Process execute(final String[] commandArray, final String[] envp, final File dir) throws IOException { return Runtime.getRuntime().exec(commandArray, envp, dir); } /** * Executes the command specified by the <code>String</code> array given in the first parameter, * with the environment defined by <code>envp</code> and under the working directory <code>dir</code>. * The first item in the array is the command; the others are the parameters. * <p>For more control over Process construction you can use * <code>java.lang.ProcessBuilder</code> (JDK 1.5+).</p> * * @param commandArray an array of <code>String<code> containing the command name and * parameters as separate items in the array. * @param envp a List of Objects (converted to Strings using toString), each member of which * has environment variable settings in the format * <i>name</i>=<i>value</i>, or * <tt>null</tt> if the subprocess should inherit * the environment of the current process. * @param dir the working directory of the subprocess, or * <tt>null</tt> if the subprocess should inherit * the working directory of the current process. * @return the Process which has just started for this command line representation. * @throws IOException if an IOException occurs. * @since 1.7.1 */ public static Process execute(final String[] commandArray, final List envp, final File dir) throws IOException { return Runtime.getRuntime().exec(commandArray, stringify(envp), dir); } /** * Executes the command specified by the given list. The toString() method is called * for each item in the list to convert into a resulting String. * The first item in the list is the command the others are the parameters. * <p>For more control over Process construction you can use * <code>java.lang.ProcessBuilder</code> (JDK 1.5+).</p> * * @param commands a list containing the command name and * parameters as separate items in the list. * @return the Process which has just started for this command line representation. * @throws IOException if an IOException occurs. * @since 1.0 */ public static Process execute(final List commands) throws IOException { return execute(stringify(commands)); } /** * Executes the command specified by the given list, * with the environment defined by <code>envp</code> and under the working directory <code>dir</code>. * The first item in the list is the command; the others are the parameters. The toString() * method is called on items in the list to convert them to Strings. * <p>For more control over Process construction you can use * <code>java.lang.ProcessBuilder</code> (JDK 1.5+).</p> * * @param commands a List containing the command name and * parameters as separate items in the list. * @param envp an array of Strings, each member of which * has environment variable settings in the format * <i>name</i>=<i>value</i>, or * <tt>null</tt> if the subprocess should inherit * the environment of the current process. * @param dir the working directory of the subprocess, or * <tt>null</tt> if the subprocess should inherit * the working directory of the current process. * @return the Process which has just started for this command line representation. * @throws IOException if an IOException occurs. * @since 1.7.1 */ public static Process execute(final List commands, final String[] envp, final File dir) throws IOException { return Runtime.getRuntime().exec(stringify(commands), envp, dir); } /** * Executes the command specified by the given list, * with the environment defined by <code>envp</code> and under the working directory <code>dir</code>. * The first item in the list is the command; the others are the parameters. The toString() * method is called on items in the list to convert them to Strings. * <p>For more control over Process construction you can use * <code>java.lang.ProcessBuilder</code> (JDK 1.5+).</p> * * @param commands a List containing the command name and * parameters as separate items in the list. * @param envp a List of Objects (converted to Strings using toString), each member of which * has environment variable settings in the format * <i>name</i>=<i>value</i>, or * <tt>null</tt> if the subprocess should inherit * the environment of the current process. * @param dir the working directory of the subprocess, or * <tt>null</tt> if the subprocess should inherit * the working directory of the current process. * @return the Process which has just started for this command line representation. * @throws IOException if an IOException occurs. * @since 1.7.1 */ public static Process execute(final List commands, final List envp, final File dir) throws IOException { return Runtime.getRuntime().exec(stringify(commands), stringify(envp), dir); } private static String[] stringify(final List orig) { if (orig == null) return null; String[] result = new String[orig.size()]; for (int i = 0; i < orig.size(); i++) { result[i] = orig.get(i).toString(); } return result; } /** * Repeat a String a certain number of times. * * @param self a String to be repeated * @param factor the number of times the String should be repeated * @return a String composed of a repetition * @throws IllegalArgumentException if the number of repetitions is < 0 * @since 1.0 */ public static String multiply(String self, Number factor) { int size = factor.intValue(); if (size == 0) return ""; else if (size < 0) { throw new IllegalArgumentException("multiply() should be called with a number of 0 or greater not: " + size); } StringBuilder answer = new StringBuilder(self); for (int i = 1; i < size; i++) { answer.append(self); } return answer.toString(); } /** * Repeat a CharSequence a certain number of times. * * @param self a CharSequence to be repeated * @param factor the number of times the CharSequence should be repeated * @return a CharSequence composed of a repetition * @throws IllegalArgumentException if the number of repetitions is < 0 * @since 1.8.2 */ public static CharSequence multiply(CharSequence self, Number factor) { return multiply(self.toString(), factor); } /** * Returns the string representation of the given array. * * @param self an array * @return the string representation * @since 1.6.0 */ public static String toString(boolean[] self) { return InvokerHelper.toString(self); } /** * Returns the string representation of the given array. * * @param self an array * @return the string representation * @since 1.6.0 */ public static String toString(byte[] self) { return InvokerHelper.toString(self); } /** * Returns the string representation of the given array. * * @param self an array * @return the string representation * @since 1.6.0 */ public static String toString(char[] self) { return InvokerHelper.toString(self); } /** * Returns the string representation of the given array. * * @param self an array * @return the string representation * @since 1.6.0 */ public static String toString(short[] self) { return InvokerHelper.toString(self); } /** * Returns the string representation of the given array. * * @param self an array * @return the string representation * @since 1.6.0 */ public static String toString(int[] self) { return InvokerHelper.toString(self); } /** * Returns the string representation of the given array. * * @param self an array * @return the string representation * @since 1.6.0 */ public static String toString(long[] self) { return InvokerHelper.toString(self); } /** * Returns the string representation of the given array. * * @param self an array * @return the string representation * @since 1.6.0 */ public static String toString(float[] self) { return InvokerHelper.toString(self); } /** * Returns the string representation of the given array. * * @param self an array * @return the string representation * @since 1.6.0 */ public static String toString(double[] self) { return InvokerHelper.toString(self); } /** * Returns the string representation of the given map. * * @param self a Map * @return the string representation * @see #toMapString(java.util.Map) * @since 1.0 */ public static String toString(AbstractMap self) { return toMapString(self); } /** * Returns the string representation of this map. The string displays the * contents of the map, i.e. <code>[one:1, two:2, three:3]</code>. * * @param self a Map * @return the string representation * @since 1.0 */ public static String toMapString(Map self) { return toMapString(self, -1); } /** * Returns the string representation of this map. The string displays the * contents of the map, i.e. <code>[one:1, two:2, three:3]</code>. * * @param self a Map * @param maxSize stop after approximately this many characters and append '...' * @return the string representation * @since 1.0 */ public static String toMapString(Map self, int maxSize) { return (self == null) ? "null" : InvokerHelper.toMapString(self, maxSize); } /** * Returns the string representation of the given collection. The string * displays the contents of the collection, i.e. * <code>[1, 2, a]</code>. * * @param self a Collection * @return the string representation * @see #toListString(java.util.Collection) * @since 1.0 */ public static String toString(AbstractCollection self) { return toListString(self); } /** * Returns the string representation of the given list. The string * displays the contents of the list, similar to a list literal, i.e. * <code>[1, 2, a]</code>. * * @param self a Collection * @return the string representation * @since 1.0 */ public static String toListString(Collection self) { return toListString(self, -1); } /** * Returns the string representation of the given list. The string * displays the contents of the list, similar to a list literal, i.e. * <code>[1, 2, a]</code>. * * @param self a Collection * @param maxSize stop after approximately this many characters and append '...' * @return the string representation * @since 1.7.3 */ public static String toListString(Collection self, int maxSize) { return (self == null) ? "null" : InvokerHelper.toListString(self, maxSize); } /** * Returns the string representation of this array's contents. * * @param self an Object[] * @return the string representation * @see #toArrayString(java.lang.Object[]) * @since 1.0 */ public static String toString(Object[] self) { return toArrayString(self); } /** * Returns the string representation of the given array. The string * displays the contents of the array, similar to an array literal, i.e. * <code>{1, 2, "a"}</code>. * * @param self an Object[] * @return the string representation * @since 1.0 */ public static String toArrayString(Object[] self) { return (self == null) ? "null" : InvokerHelper.toArrayString(self); } /** * Create a String representation of this object. * @param value an object * @return a string. * @since 1.0 */ public static String toString(Object value) { return InvokerHelper.toString(value); } // Number based methods //------------------------------------------------------------------------- /** * Increment a Character by one. * * @param self a Character * @return an incremented Character * @since 1.5.7 */ public static Character next(Character self) { return (char) (self + 1); } /** * Increment a Number by one. * * @param self a Number * @return an incremented Number * @since 1.0 */ public static Number next(Number self) { return NumberNumberPlus.plus(self, ONE); } /** * Decrement a Character by one. * * @param self a Character * @return a decremented Character * @since 1.5.7 */ public static Character previous(Character self) { return (char) (self - 1); } /** * Decrement a Number by one. * * @param self a Number * @return a decremented Number * @since 1.0 */ public static Number previous(Number self) { return NumberNumberMinus.minus(self, ONE); } /** * Add a Character and a Number. The ordinal value of the Character * is used in the addition (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * This operation will always create a new object for the result, * while the operands remain unchanged. * * @see java.lang.Integer#valueOf(int) * @param left a Character * @param right a Number * @return the Number corresponding to the addition of left and right * @since 1.0 */ public static Number plus(Character left, Number right) { return NumberNumberPlus.plus(Integer.valueOf(left), right); } /** * Add a Number and a Character. The ordinal value of the Character * is used in the addition (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @see java.lang.Integer#valueOf(int) * @param left a Number * @param right a Character * @return The Number corresponding to the addition of left and right * @since 1.0 */ public static Number plus(Number left, Character right) { return NumberNumberPlus.plus(left, Integer.valueOf(right)); } /** * Add one Character to another. The ordinal values of the Characters * are used in the addition (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * This operation will always create a new object for the result, * while the operands remain unchanged. * * @see #plus(java.lang.Number, java.lang.Character) * @param left a Character * @param right a Character * @return the Number corresponding to the addition of left and right * @since 1.0 */ public static Number plus(Character left, Character right) { return plus(Integer.valueOf(left), right); } /** * Compare a Character and a Number. The ordinal value of the Character * is used in the comparison (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right a Number * @return the result of the comparison * @since 1.0 */ public static int compareTo(Character left, Number right) { return compareTo(Integer.valueOf(left), right); } /** * Compare a Number and a Character. The ordinal value of the Character * is used in the comparison (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Number * @param right a Character * @return the result of the comparison * @since 1.0 */ public static int compareTo(Number left, Character right) { return compareTo(left, Integer.valueOf(right)); } /** * Compare two Characters. The ordinal values of the Characters * are compared (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right a Character * @return the result of the comparison * @since 1.0 */ public static int compareTo(Character left, Character right) { return compareTo(Integer.valueOf(left), right); } /** * Compare two Numbers. Equality (==) for numbers dispatches to this. * * @param left a Number * @param right another Number to compare to * @return the comparison of both numbers * @since 1.0 */ public static int compareTo(Number left, Number right) { /** @todo maybe a double dispatch thing to handle new large numbers? */ return NumberMath.compareTo(left, right); } /** * Subtract a Number from a Character. The ordinal value of the Character * is used in the subtraction (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right a Number * @return the Number corresponding to the subtraction of right from left * @since 1.0 */ public static Number minus(Character left, Number right) { return NumberNumberMinus.minus(Integer.valueOf(left), right); } /** * Subtract a Character from a Number. The ordinal value of the Character * is used in the subtraction (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Number * @param right a Character * @return the Number corresponding to the subtraction of right from left * @since 1.0 */ public static Number minus(Number left, Character right) { return NumberNumberMinus.minus(left, Integer.valueOf(right)); } /** * Subtract one Character from another. The ordinal values of the Characters * is used in the comparison (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right a Character * @return the Number corresponding to the subtraction of right from left * @since 1.0 */ public static Number minus(Character left, Character right) { return minus(Integer.valueOf(left), right); } /** * Multiply a Character by a Number. The ordinal value of the Character * is used in the multiplcation (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right a Number * @return the Number corresponding to the multiplication of left by right * @since 1.0 */ public static Number multiply(Character left, Number right) { return NumberNumberMultiply.multiply(Integer.valueOf(left), right); } /** * Multiply a Number by a Character. The ordinal value of the Character * is used in the multiplication (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Number * @param right a Character * @return the multiplication of left by right * @since 1.0 */ public static Number multiply(Number left, Character right) { return NumberNumberMultiply.multiply(Integer.valueOf(right), left); } /** * Multiply two Characters. The ordinal values of the Characters * are used in the multiplication (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right another Character * @return the Number corresponding to the multiplication of left by right * @since 1.0 */ public static Number multiply(Character left, Character right) { return multiply(Integer.valueOf(left), right); } /** * Multiply a BigDecimal and a Double. * Note: This method was added to enforce the Groovy rule of * BigDecimal*Double == Double. Without this method, the * multiply(BigDecimal) method in BigDecimal would respond * and return a BigDecimal instead. Since BigDecimal is preferred * over Number, the Number*Number method is not chosen as in older * versions of Groovy. * * @param left a BigDecimal * @param right a Double * @return the multiplication of left by right * @since 1.0 */ public static Number multiply(BigDecimal left, Double right) { return NumberMath.multiply(left, right); } /** * Multiply a BigDecimal and a BigInteger. * Note: This method was added to enforce the Groovy rule of * BigDecimal*long == long. Without this method, the * multiply(BigDecimal) method in BigDecimal would respond * and return a BigDecimal instead. Since BigDecimal is preferred * over Number, the Number*Number method is not chosen as in older * versions of Groovy. BigInteger is the fallback for all integer * types in Groovy * * @param left a BigDecimal * @param right a BigInteger * @return the multiplication of left by right * @since 1.0 */ public static Number multiply(BigDecimal left, BigInteger right) { return NumberMath.multiply(left, right); } /** * Power of a Number to a certain exponent. Called by the '**' operator. * * @param self a Number * @param exponent a Number exponent * @return a Number to the power of a certain exponent * @since 1.0 */ public static Number power(Number self, Number exponent) { double base, exp, answer; base = self.doubleValue(); exp = exponent.doubleValue(); answer = Math.pow(base, exp); if ((double) ((int) answer) == answer) { return (int) answer; } else if ((double) ((long) answer) == answer) { return (long) answer; } else { return answer; } } /** * Power of a BigDecimal to an integer certain exponent. If the * exponent is positive, call the BigDecimal.pow(int) method to * maintain precision. Called by the '**' operator. * * @param self a BigDecimal * @param exponent an Integer exponent * @return a Number to the power of a the exponent */ public static Number power(BigDecimal self, Integer exponent) { if (exponent >= 0) { return self.pow(exponent); } else { return power(self, (double) exponent); } } /** * Power of a BigInteger to an integer certain exponent. If the * exponent is positive, call the BigInteger.pow(int) method to * maintain precision. Called by the '**' operator. * * @param self a BigInteger * @param exponent an Integer exponent * @return a Number to the power of a the exponent */ public static Number power(BigInteger self, Integer exponent) { if (exponent >= 0) { return self.pow(exponent); } else { return power(self, (double) exponent); } } /** * Power of an integer to an integer certain exponent. If the * exponent is positive, convert to a BigInteger and call * BigInteger.pow(int) method to maintain precision. Called by the * '**' operator. * * @param self an Integer * @param exponent an Integer exponent * @return a Number to the power of a the exponent */ public static Number power(Integer self, Integer exponent) { if (exponent >= 0) { BigInteger answer = BigInteger.valueOf(self).pow(exponent); if (answer.compareTo(BI_INT_MIN) >= 0 && answer.compareTo(BI_INT_MAX) <= 0) { return answer.intValue(); } else { return answer; } } else { return power(self, (double) exponent); } } /** * Power of a long to an integer certain exponent. If the * exponent is positive, convert to a BigInteger and call * BigInteger.pow(int) method to maintain precision. Called by the * '**' operator. * * @param self a Long * @param exponent an Integer exponent * @return a Number to the power of a the exponent */ public static Number power(Long self, Integer exponent) { if (exponent >= 0) { BigInteger answer = BigInteger.valueOf(self).pow(exponent); if (answer.compareTo(BI_LONG_MIN) >= 0 && answer.compareTo(BI_LONG_MAX) <= 0) { return answer.longValue(); } else { return answer; } } else { return power(self, (double) exponent); } } /** * Divide a Character by a Number. The ordinal value of the Character * is used in the division (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right a Number * @return the Number corresponding to the division of left by right * @since 1.0 */ public static Number div(Character left, Number right) { return NumberNumberDiv.div(Integer.valueOf(left), right); } /** * Divide a Number by a Character. The ordinal value of the Character * is used in the division (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Number * @param right a Character * @return the Number corresponding to the division of left by right * @since 1.0 */ public static Number div(Number left, Character right) { return NumberNumberDiv.div(left, Integer.valueOf(right)); } /** * Divide one Character by another. The ordinal values of the Characters * are used in the division (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right another Character * @return the Number corresponding to the division of left by right * @since 1.0 */ public static Number div(Character left, Character right) { return div(Integer.valueOf(left), right); } /** * Integer Divide a Character by a Number. The ordinal value of the Character * is used in the division (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right a Number * @return a Number (an Integer) resulting from the integer division operation * @since 1.0 */ public static Number intdiv(Character left, Number right) { return intdiv(Integer.valueOf(left), right); } /** * Integer Divide a Number by a Character. The ordinal value of the Character * is used in the division (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Number * @param right a Character * @return a Number (an Integer) resulting from the integer division operation * @since 1.0 */ public static Number intdiv(Number left, Character right) { return intdiv(left, Integer.valueOf(right)); } /** * Integer Divide two Characters. The ordinal values of the Characters * are used in the division (the ordinal value is the unicode * value which for simple character sets is the ASCII value). * * @param left a Character * @param right another Character * @return a Number (an Integer) resulting from the integer division operation * @since 1.0 */ public static Number intdiv(Character left, Character right) { return intdiv(Integer.valueOf(left), right); } /** * Integer Divide two Numbers. * * @param left a Number * @param right another Number * @return a Number (an Integer) resulting from the integer division operation * @since 1.0 */ public static Number intdiv(Number left, Number right) { return NumberMath.intdiv(left, right); } /** * Bitwise OR together two numbers. * * @param left a Number * @param right another Number to bitwise OR * @return the bitwise OR of both Numbers * @since 1.0 */ public static Number or(Number left, Number right) { return NumberMath.or(left, right); } /** * Bitwise AND together two Numbers. * * @param left a Number * @param right another Number to bitwise AND * @return the bitwise AND of both Numbers * @since 1.0 */ public static Number and(Number left, Number right) { return NumberMath.and(left, right); } /** * Bitwise AND together two BitSets. * * @param left a BitSet * @param right another BitSet to bitwise AND * @return the bitwise AND of both BitSets * @since 1.5.0 */ public static BitSet and(BitSet left, BitSet right) { BitSet result = (BitSet) left.clone(); result.and(right); return result; } /** * Bitwise XOR together two BitSets. Called when the '^' operator is used * between two bit sets. * * @param left a BitSet * @param right another BitSet to bitwise AND * @return the bitwise XOR of both BitSets * @since 1.5.0 */ public static BitSet xor(BitSet left, BitSet right) { BitSet result = (BitSet) left.clone(); result.xor(right); return result; } /** * Bitwise NEGATE a BitSet. * * @param self a BitSet * @return the bitwise NEGATE of the BitSet * @since 1.5.0 */ public static BitSet bitwiseNegate(BitSet self) { BitSet result = (BitSet) self.clone(); result.flip(0, result.size() - 1); return result; } /** * Bitwise OR together two BitSets. Called when the '|' operator is used * between two bit sets. * * @param left a BitSet * @param right another BitSet to bitwise AND * @return the bitwise OR of both BitSets * @since 1.5.0 */ public static BitSet or(BitSet left, BitSet right) { BitSet result = (BitSet) left.clone(); result.or(right); return result; } /** * Bitwise XOR together two Numbers. Called when the '|' operator is used. * * @param left a Number * @param right another Number to bitwse XOR * @return the bitwise XOR of both Numbers * @since 1.0 */ public static Number xor(Number left, Number right) { return NumberMath.xor(left, right); } /** * Performs a division modulus operation. Called by the '%' operator. * * @param left a Number * @param right another Number to mod * @return the modulus result * @since 1.0 */ public static Number mod(Number left, Number right) { return NumberMath.mod(left, right); } /** * Negates the number. Equivalent to the '-' operator when it preceeds * a single operand, i.e. <code>-10</code> * * @param left a Number * @return the negation of the number * @since 1.5.0 */ public static Number unaryMinus(Number left) { return NumberMath.unaryMinus(left); } /** * Executes the closure this many times, starting from zero. The current * index is passed to the closure each time. * Example: * <pre>10.times { * println it * }</pre> * Prints the numbers 0 through 9. * * @param self a Number * @param closure the closure to call a number of times * @since 1.0 */ public static void times(Number self, Closure closure) { for (int i = 0, size = self.intValue(); i < size; i++) { closure.call(i); if (closure.getDirective() == Closure.DONE) { break; } } } /** * Iterates from this number up to the given number, inclusive, * incrementing by one each time. * * @param self a Number * @param to another Number to go up to * @param closure the closure to call * @since 1.0 */ public static void upto(Number self, Number to, Closure closure) { int self1 = self.intValue(); int to1 = to.intValue(); if (self1 <= to1) { for (int i = self1; i <= to1; i++) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } /** * Iterates from this number up to the given number, inclusive, * incrementing by one each time. * * @param self a long * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void upto(long self, Number to, Closure closure) { long to1 = to.longValue(); if (self <= to1) { for (long i = self; i <= to1; i++) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } /** * Iterates from this number up to the given number, inclusive, * incrementing by one each time. * * @param self a Long * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void upto(Long self, Number to, Closure closure) { long to1 = to.longValue(); if (self <= to1) { for (long i = self; i <= to1; i++) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } /** * Iterates from this number up to the given number, inclusive, * incrementing by one each time. * * @param self a float * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void upto(float self, Number to, Closure closure) { float to1 = to.floatValue(); if (self <= to1) { for (float i = self; i <= to1; i++) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } /** * Iterates from this number up to the given number, inclusive, * incrementing by one each time. * * @param self a Float * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void upto(Float self, Number to, Closure closure) { float to1 = to.floatValue(); if (self <= to1) { for (float i = self; i <= to1; i++) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } /** * Iterates from this number up to the given number, inclusive, * incrementing by one each time. * * @param self a double * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void upto(double self, Number to, Closure closure) { double to1 = to.doubleValue(); if (self <= to1) { for (double i = self; i <= to1; i++) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } /** * Iterates from this number up to the given number, inclusive, * incrementing by one each time. * * @param self a Double * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void upto(Double self, Number to, Closure closure) { double to1 = to.doubleValue(); if (self <= to1) { for (double i = self; i <= to1; i++) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } /** * Iterates from this number up to the given number, inclusive, * incrementing by one each time. Example: * <pre>0.upto( 10 ) { * println it * }</pre> * Prints numbers 0 to 10 * * @param self a BigInteger * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void upto(BigInteger self, Number to, Closure closure) { if (to instanceof BigDecimal) { final BigDecimal one = BigDecimal.valueOf(10, 1); BigDecimal self1 = new BigDecimal(self); BigDecimal to1 = (BigDecimal) to; if (self1.compareTo(to1) <= 0) { for (BigDecimal i = self1; i.compareTo(to1) <= 0; i = i.add(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } else if (to instanceof BigInteger) { final BigInteger one = BigInteger.valueOf(1); BigInteger to1 = (BigInteger) to; if (self.compareTo(to1) <= 0) { for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } else { final BigInteger one = BigInteger.valueOf(1); BigInteger to1 = new BigInteger(to.toString()); if (self.compareTo(to1) <= 0) { for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } } /** * Iterates from this number up to the given number, inclusive, * incrementing by one each time. * <pre>0.1.upto( 10 ) { * println it * }</pre> * Prints numbers 0.1, 1.1, 2.1... to 9.1 * * @param self a BigDecimal * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void upto(BigDecimal self, Number to, Closure closure) { final BigDecimal one = BigDecimal.valueOf(10, 1); // That's what you get for "1.0". if (to instanceof BigDecimal) { BigDecimal to1 = (BigDecimal) to; if (self.compareTo(to1) <= 0) { for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } else if (to instanceof BigInteger) { BigDecimal to1 = new BigDecimal((BigInteger) to); if (self.compareTo(to1) <= 0) { for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } else { BigDecimal to1 = new BigDecimal(to.toString()); if (self.compareTo(to1) <= 0) { for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); } } /** * Iterates from this number down to the given number, inclusive, * decrementing by one each time. * * @param self a Number * @param to another Number to go down to * @param closure the closure to call * @since 1.0 */ public static void downto(Number self, Number to, Closure closure) { int self1 = self.intValue(); int to1 = to.intValue(); if (self1 >= to1) { for (int i = self1; i >= to1; i--) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } /** * Iterates from this number down to the given number, inclusive, * decrementing by one each time. * * @param self a long * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void downto(long self, Number to, Closure closure) { long to1 = to.longValue(); if (self >= to1) { for (long i = self; i >= to1; i--) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } /** * Iterates from this number down to the given number, inclusive, * decrementing by one each time. * * @param self a Long * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void downto(Long self, Number to, Closure closure) { long to1 = to.longValue(); if (self >= to1) { for (long i = self; i >= to1; i--) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } /** * Iterates from this number down to the given number, inclusive, * decrementing by one each time. * * @param self a float * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void downto(float self, Number to, Closure closure) { float to1 = to.floatValue(); if (self >= to1) { for (float i = self; i >= to1; i--) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } /** * Iterates from this number down to the given number, inclusive, * decrementing by one each time. * * @param self a Float * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void downto(Float self, Number to, Closure closure) { float to1 = to.floatValue(); if (self >= to1) { for (float i = self; i >= to1; i--) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } /** * Iterates from this number down to the given number, inclusive, * decrementing by one each time. * * @param self a double * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void downto(double self, Number to, Closure closure) { double to1 = to.doubleValue(); if (self >= to1) { for (double i = self; i >= to1; i--) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } /** * Iterates from this number down to the given number, inclusive, * decrementing by one each time. * * @param self a Double * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void downto(Double self, Number to, Closure closure) { double to1 = to.doubleValue(); if (self >= to1) { for (double i = self; i >= to1; i--) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } /** * Iterates from this number down to the given number, inclusive, * decrementing by one each time. * * @param self a BigInteger * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void downto(BigInteger self, Number to, Closure closure) { if (to instanceof BigDecimal) { final BigDecimal one = BigDecimal.valueOf(10, 1); // That's what you get for "1.0". final BigDecimal to1 = (BigDecimal) to; final BigDecimal selfD = new BigDecimal(self); if (selfD.compareTo(to1) >= 0) { for (BigDecimal i = selfD; i.compareTo(to1) >= 0; i = i.subtract(one)) { closure.call(i.toBigInteger()); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } else if (to instanceof BigInteger) { final BigInteger one = BigInteger.valueOf(1); final BigInteger to1 = (BigInteger) to; if (self.compareTo(to1) >= 0) { for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } else { final BigInteger one = BigInteger.valueOf(1); final BigInteger to1 = new BigInteger(to.toString()); if (self.compareTo(to1) >= 0) { for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } } /** * Iterates from this number down to the given number, inclusive, * decrementing by one each time. Each number is passed to the closure. * Example: * <pre>10.5.downto(0) { * println it * }</pre> * Prints numbers 10.5, 9.5 ... to 0.5. * * @param self a BigDecimal * @param to the end number * @param closure the code to execute for each number * @since 1.0 */ public static void downto(BigDecimal self, Number to, Closure closure) { final BigDecimal one = BigDecimal.valueOf(10, 1); // Quick way to get "1.0". if (to instanceof BigDecimal) { BigDecimal to1 = (BigDecimal) to; if (self.compareTo(to1) >= 0) { for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } else if (to instanceof BigInteger) { BigDecimal to1 = new BigDecimal((BigInteger) to); if (self.compareTo(to1) >= 0) { for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } else { BigDecimal to1 = new BigDecimal(to.toString()); if (self.compareTo(to1) >= 0) { for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { closure.call(i); } } else throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); } } /** * Iterates from this number up to the given number using a step increment. * Each intermediate number is passed to the given closure. Example: * <pre>0.step( 10, 2 ) { * println it * }</pre> * Prints even numbers 0 through 8. * * @param self a Number to start with * @param to a Number to go up to, exclusive * @param stepNumber a Number representing the step increment * @param closure the closure to call * @since 1.0 */ public static void step(Number self, Number to, Number stepNumber, Closure closure) { if (self instanceof BigDecimal || to instanceof BigDecimal || stepNumber instanceof BigDecimal) { final BigDecimal zero = BigDecimal.valueOf(0, 1); // Same as "0.0". BigDecimal self1 = (self instanceof BigDecimal) ? (BigDecimal) self : new BigDecimal(self.toString()); BigDecimal to1 = (to instanceof BigDecimal) ? (BigDecimal) to : new BigDecimal(to.toString()); BigDecimal stepNumber1 = (stepNumber instanceof BigDecimal) ? (BigDecimal) stepNumber : new BigDecimal(stepNumber.toString()); if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) { for (BigDecimal i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) { closure.call(i); } } else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) { for (BigDecimal i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) { closure.call(i); } } else if(self1.compareTo(to1) != 0) throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")"); } else if (self instanceof BigInteger || to instanceof BigInteger || stepNumber instanceof BigInteger) { final BigInteger zero = BigInteger.valueOf(0); BigInteger self1 = (self instanceof BigInteger) ? (BigInteger) self : new BigInteger(self.toString()); BigInteger to1 = (to instanceof BigInteger) ? (BigInteger) to : new BigInteger(to.toString()); BigInteger stepNumber1 = (stepNumber instanceof BigInteger) ? (BigInteger) stepNumber : new BigInteger(stepNumber.toString()); if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) { for (BigInteger i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) { closure.call(i); } } else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) { for (BigInteger i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) { closure.call(i); } } else if(self1.compareTo(to1) != 0) throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")"); } else { int self1 = self.intValue(); int to1 = to.intValue(); int stepNumber1 = stepNumber.intValue(); if (stepNumber1 > 0 && to1 > self1) { for (int i = self1; i < to1; i += stepNumber1) { closure.call(i); } } else if (stepNumber1 < 0 && to1 < self1) { for (int i = self1; i > to1; i += stepNumber1) { closure.call(i); } } else if(self1 != to1) throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")"); } } /** * Get the absolute value * * @param number a Number * @return the absolute value of that Number * @since 1.0 */ //Note: This method is NOT called if number is a BigInteger or BigDecimal because //those classes implement a method with a better exact match. public static int abs(Number number) { return Math.abs(number.intValue()); } /** * Get the absolute value * * @param number a Long * @return the absolute value of that Long * @since 1.0 */ public static long abs(Long number) { return Math.abs(number.longValue()); } /** * Get the absolute value * * @param number a Float * @return the absolute value of that Float * @since 1.0 */ public static float abs(Float number) { return Math.abs(number.floatValue()); } /** * Get the absolute value * * @param number a Double * @return the absolute value of that Double * @since 1.0 */ public static double abs(Double number) { return Math.abs(number); } /** * Round the value * * @param number a Float * @return the rounded value of that Float * @since 1.0 */ public static int round(Float number) { return Math.round(number.floatValue()); } /** * Round the value * * @param number a Float * @param precision the number of decimal places to keep * @return the Float rounded to the number of decimal places specified by precision * @since 1.6.0 */ public static float round(Float number, int precision) { return (float)(Math.floor(number.doubleValue()*Math.pow(10,precision)+0.5)/Math.pow(10,precision)); } /** * Truncate the value * * @param number a Float * @param precision the number of decimal places to keep * @return the Float truncated to the number of decimal places specified by precision * @since 1.6.0 */ public static float trunc(Float number, int precision) { return (float)(Math.floor(number.doubleValue()*Math.pow(10,precision))/Math.pow(10,precision)); } /** * Truncate the value * * @param number a Double * @return the Double truncated to 0 decimal places (i.e. a synonym for floor) * @since 1.6.0 */ public static float trunc(Float number) { return (float)Math.floor(number.doubleValue()); } /** * Round the value * * @param number a Double * @return the rounded value of that Double * @since 1.0 */ public static long round(Double number) { return Math.round(number); } /** * Round the value * * @param number a Double * @param precision the number of decimal places to keep * @return the Double rounded to the number of decimal places specified by precision * @since 1.6.4 */ public static double round(Double number, int precision) { return Math.floor(number *Math.pow(10,precision)+0.5)/Math.pow(10,precision); } /** * Truncate the value * * @param number a Double * @return the Double truncated to 0 decimal places (i.e. a synonym for floor) * @since 1.6.4 */ public static double trunc(Double number) { return Math.floor(number); } /** * Truncate the value * * @param number a Double * @param precision the number of decimal places to keep * @return the Double truncated to the number of decimal places specified by precision * @since 1.6.4 */ public static double trunc(Double number, int precision) { return Math.floor(number *Math.pow(10,precision))/Math.pow(10,precision); } /** * Parse a String into an Integer * * @param self a String * @return an Integer * @since 1.0 */ public static Integer toInteger(String self) { return Integer.valueOf(self.trim()); } /** * Parse a String into a Long * * @param self a String * @return a Long * @since 1.0 */ public static Long toLong(String self) { return Long.valueOf(self.trim()); } /** * Parse a String into a Short * * @param self a String * @return a Short * @since 1.5.7 */ public static Short toShort(String self) { return Short.valueOf(self.trim()); } /** * Parse a String into a Float * * @param self a String * @return a Float * @since 1.0 */ public static Float toFloat(String self) { return Float.valueOf(self.trim()); } /** * Parse a String into a Double * * @param self a String * @return a Double * @since 1.0 */ public static Double toDouble(String self) { return Double.valueOf(self.trim()); } /** * Parse a String into a BigInteger * * @param self a String * @return a BigInteger * @since 1.0 */ public static BigInteger toBigInteger(String self) { return new BigInteger(self.trim()); } /** * Parse a String into a BigDecimal * * @param self a String * @return a BigDecimal * @since 1.0 */ public static BigDecimal toBigDecimal(String self) { return new BigDecimal(self.trim()); } /** * Determine if a String can be parsed into an Integer. * * @param self a String * @return true if the string can be parsed * @since 1.5.0 */ public static boolean isInteger(String self) { try { Integer.valueOf(self.trim()); return true; } catch (NumberFormatException nfe) { return false; } } /** * Determine if a String can be parsed into a Long. * * @param self a String * @return true if the string can be parsed * @since 1.5.0 */ public static boolean isLong(String self) { try { Long.valueOf(self.trim()); return true; } catch (NumberFormatException nfe) { return false; } } /** * Determine if a String can be parsed into a Float. * * @param self a String * @return true if the string can be parsed * @since 1.5.0 */ public static boolean isFloat(String self) { try { Float.valueOf(self.trim()); return true; } catch (NumberFormatException nfe) { return false; } } /** * Determine if a String can be parsed into a Double. * * @param self a String * @return true if the string can be parsed * @since 1.5.0 */ public static boolean isDouble(String self) { try { Double.valueOf(self.trim()); return true; } catch (NumberFormatException nfe) { return false; } } /** * Determine if a String can be parsed into a BigInteger. * * @param self a String * @return true if the string can be parsed * @since 1.5.0 */ public static boolean isBigInteger(String self) { try { new BigInteger(self.trim()); return true; } catch (NumberFormatException nfe) { return false; } } /** * Determine if a String can be parsed into a BigDecimal. * * @param self a String * @return true if the string can be parsed * @since 1.5.0 */ public static boolean isBigDecimal(String self) { try { new BigDecimal(self.trim()); return true; } catch (NumberFormatException nfe) { return false; } } /** * Determine if a String can be parsed into a Number. * Synonym for 'isBigDecimal()'. * * @param self a String * @return true if the string can be parsed * @see #isBigDecimal(java.lang.String) * @since 1.5.0 */ public static boolean isNumber(String self) { return isBigDecimal(self); } /** * Parse a CharSequence into an Integer * * @param self a CharSequence * @return an Integer * @see #toInteger(java.lang.String) * @since 1.8.2 */ public static Integer toInteger(CharSequence self) { return toInteger(self.toString()); } /** * Parse a CharSequence into a Long * * @param self a CharSequence * @return a Long * @see #toLong(java.lang.String) * @since 1.8.2 */ public static Long toLong(CharSequence self) { return toLong(self.toString()); } /** * Parse a CharSequence into a Short * * @param self a CharSequence * @return a Short * @see #toShort(java.lang.String) * @since 1.8.2 */ public static Short toShort(CharSequence self) { return toShort(self.toString()); } /** * Parse a CharSequence into a Float * * @param self a CharSequence * @return a Float * @see #toFloat(java.lang.String) * @since 1.8.2 */ public static Float toFloat(CharSequence self) { return toFloat(self.toString()); } /** * Parse a CharSequence into a Double * * @param self a CharSequence * @return a Double * @see #toDouble(java.lang.String) * @since 1.8.2 */ public static Double toDouble(CharSequence self) { return toDouble(self.toString()); } /** * Parse a CharSequence into a BigInteger * * @param self a CharSequence * @return a BigInteger * @see #toBigInteger(java.lang.String) * @since 1.8.2 */ public static BigInteger toBigInteger(CharSequence self) { return toBigInteger(self.toString()); } /** * Parse a CharSequence into a BigDecimal * * @param self a CharSequence * @return a BigDecimal * @see #toBigDecimal(java.lang.String) * @since 1.8.2 */ public static BigDecimal toBigDecimal(CharSequence self) { return toBigDecimal(self.toString()); } /** * Determine if a CharSequence can be parsed as an Integer. * * @param self a CharSequence * @return true if the CharSequence can be parsed * @see #isInteger(java.lang.String) * @since 1.8.2 */ public static boolean isInteger(CharSequence self) { return isInteger(self.toString()); } /** * Determine if a CharSequence can be parsed as a Long. * * @param self a CharSequence * @return true if the CharSequence can be parsed * @see #isLong(java.lang.String) * @since 1.8.2 */ public static boolean isLong(CharSequence self) { return isLong(self.toString()); } /** * Determine if a CharSequence can be parsed as a Float. * * @param self a CharSequence * @return true if the CharSequence can be parsed * @see #isFloat(java.lang.String) * @since 1.8.2 */ public static boolean isFloat(CharSequence self) { return isFloat(self.toString()); } /** * Determine if a CharSequence can be parsed as a Double. * * @param self a CharSequence * @return true if the CharSequence can be parsed * @see #isDouble(java.lang.String) * @since 1.8.2 */ public static boolean isDouble(CharSequence self) { return isDouble(self.toString()); } /** * Determine if a CharSequence can be parsed as a BigInteger. * * @param self a CharSequence * @return true if the CharSequence can be parsed * @see #isBigInteger(java.lang.String) * @since 1.8.2 */ public static boolean isBigInteger(CharSequence self) { return isBigInteger(self.toString()); } /** * Determine if a CharSequence can be parsed as a BigDecimal. * * @param self a CharSequence * @return true if the CharSequence can be parsed * @see #isBigDecimal(java.lang.String) * @since 1.8.2 */ public static boolean isBigDecimal(CharSequence self) { return isBigDecimal(self.toString()); } /** * Determine if a CharSequence can be parsed as a Number. * Synonym for 'isBigDecimal()'. * * @param self a CharSequence * @return true if the CharSequence can be parsed * @see #isNumber(java.lang.String) * @since 1.8.2 */ public static boolean isNumber(CharSequence self) { return isNumber(self.toString()); } /** * Determine if a Character is uppercase. * Synonym for 'Character.isUpperCase(this)'. * * @param self a Character * @return true if the character is uppercase * @see java.lang.Character#isUpperCase(char) * @since 1.5.7 */ public static boolean isUpperCase(Character self) { return Character.isUpperCase(self); } /** * Determine if a Character is lowercase. * Synonym for 'Character.isLowerCase(this)'. * * @param self a Character * @return true if the character is lowercase * @see java.lang.Character#isLowerCase(char) * @since 1.5.7 */ public static boolean isLowerCase(Character self) { return Character.isLowerCase(self); } /** * Determines if a character is a letter. * Synonym for 'Character.isLetter(this)'. * * @param self a Character * @return true if the character is a letter * @see java.lang.Character#isLetter(char) * @since 1.5.7 */ public static boolean isLetter(Character self) { return Character.isLetter(self); } /** * Determines if a character is a digit. * Synonym for 'Character.isDigit(this)'. * * @param self a Character * @return true if the character is a digit * @see java.lang.Character#isDigit(char) * @since 1.5.7 */ public static boolean isDigit(Character self) { return Character.isDigit(self); } /** * Determines if a character is a letter or digit. * Synonym for 'Character.isLetterOrDigit(this)'. * * @param self a Character * @return true if the character is a letter or digit * @see java.lang.Character#isLetterOrDigit(char) * @since 1.5.7 */ public static boolean isLetterOrDigit(Character self) { return Character.isLetterOrDigit(self); } /** * Determines if a character is a whitespace character. * Synonym for 'Character.isWhitespace(this)'. * * @param self a Character * @return true if the character is a whitespace character * @see java.lang.Character#isWhitespace(char) * @since 1.5.7 */ public static boolean isWhitespace(Character self) { return Character.isWhitespace(self); } /** * Converts the character to uppercase. * Synonym for 'Character.toUpperCase(this)'. * * @param self a Character to convert * @return the uppercase equivalent of the character, if any; * otherwise, the character itself. * @see java.lang.Character#isUpperCase(char) * @see java.lang.String#toUpperCase() * @since 1.5.7 */ public static char toUpperCase(Character self) { return Character.toUpperCase(self); } /** * Converts the character to lowercase. * Synonym for 'Character.toLowerCase(this)'. * * @param self a Character to convert * @return the lowercase equivalent of the character, if any; * otherwise, the character itself. * @see java.lang.Character#isLowerCase(char) * @see java.lang.String#toLowerCase() * @since 1.5.7 */ public static char toLowerCase(Character self) { return Character.toLowerCase(self); } /** * Transform a Number into an Integer * * @param self a Number * @return an Integer * @since 1.0 */ public static Integer toInteger(Number self) { return self.intValue(); } /** * Transform a Number into a Long * * @param self a Number * @return an Long * @since 1.0 */ public static Long toLong(Number self) { return self.longValue(); } /** * Transform a Number into a Float * * @param self a Number * @return an Float * @since 1.0 */ public static Float toFloat(Number self) { return self.floatValue(); } /** * Transform a Number into a Double * * @param self a Number * @return an Double * @since 1.0 */ public static Double toDouble(Number self) { // Conversions in which all decimal digits are known to be good. if ((self instanceof Double) || (self instanceof Long) || (self instanceof Integer) || (self instanceof Short) || (self instanceof Byte)) { return self.doubleValue(); } // Chances are this is a Float or a Big. // With Float we're extending binary precision and that gets ugly in decimal. // If we used Float.doubleValue() on 0.1f we get 0.10000000149011612. // Note that this is different than casting '(double) 0.1f' which will do the // binary extension just like in Java. // With Bigs and other unknowns, this is likely to be the same. return Double.valueOf(self.toString()); } /** * Transform a Number into a BigDecimal * * @param self a Number * @return an BigDecimal * @since 1.0 */ public static BigDecimal toBigDecimal(Number self) { // Quick method for scalars. if ((self instanceof Long) || (self instanceof Integer) || (self instanceof Short) || (self instanceof Byte)) { return BigDecimal.valueOf(self.longValue()); } return new BigDecimal(self.toString()); } /** * Transform this number to a the given type, using the 'as' operator. The * following types are supported in addition to the default * {@link #asType(java.lang.Object, java.lang.Class)}: * <ul> * <li>BigDecimal</li> * <li>BigInteger</li> * <li>Double</li> * <li>Float</li> * </ul> * @param self this number * @param c the desired type of the transformed result * @return an instance of the given type * @since 1.0 */ @SuppressWarnings("unchecked") public static <T> T asType(Number self, Class<T> c) { if (c == BigDecimal.class) { return (T) toBigDecimal(self); } else if (c == BigInteger.class) { return (T) toBigInteger(self); } else if (c == Double.class) { return (T) toDouble(self); } else if (c == Float.class) { return (T) toFloat(self); } return asType((Object) self, c); } /** * Transform this Number into a BigInteger. * * @param self a Number * @return an BigInteger * @since 1.0 */ public static BigInteger toBigInteger(Number self) { if (self instanceof BigInteger) { return (BigInteger) self; } else if (self instanceof BigDecimal) { return ((BigDecimal) self).toBigInteger(); } else if (self instanceof Double) { return new BigDecimal((Double)self).toBigInteger(); } else if (self instanceof Float) { return new BigDecimal((Float)self).toBigInteger(); } else { return new BigInteger(Long.toString(self.longValue())); } } // Boolean based methods //------------------------------------------------------------------------- /** * Logical conjunction of two boolean operators. * * @param left left operator * @param right right operator * @return result of logical conjunction * @since 1.0 */ public static Boolean and(Boolean left, Boolean right) { return left && right; } /** * Logical disjunction of two boolean operators * * @param left left operator * @param right right operator * @return result of logical disjunction * @since 1.0 */ public static Boolean or(Boolean left, Boolean right) { return left || right; } /** * Exclusive disjunction of two boolean operators * * @param left left operator * @param right right operator * @return result of exclusive disjunction * @since 1.0 */ public static Boolean xor(Boolean left, Boolean right) { return left ^ right; } // public static Boolean negate(Boolean left) { // return Boolean.valueOf(!left.booleanValue()); // } // File and stream based methods //------------------------------------------------------------------------- /** * Create an object output stream for this file. * * @param file a file * @return an object output stream * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static ObjectOutputStream newObjectOutputStream(File file) throws IOException { return new ObjectOutputStream(new FileOutputStream(file)); } /** * Create an object output stream for this output stream. * * @param outputStream an output stream * @return an object output stream * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static ObjectOutputStream newObjectOutputStream(OutputStream outputStream) throws IOException { return new ObjectOutputStream(outputStream); } /** * Create a new ObjectOutputStream for this file and then pass it to the * closure. This method ensures the stream is closed after the closure * returns. * * @param file a File * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.OutputStream, groovy.lang.Closure) * @since 1.5.0 */ public static <T> T withObjectOutputStream(File file, Closure<T> closure) throws IOException { return withStream(newObjectOutputStream(file), closure); } /** * Create a new ObjectOutputStream for this output stream and then pass it to the * closure. This method ensures the stream is closed after the closure * returns. * * @param outputStream am output stream * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.OutputStream, groovy.lang.Closure) * @since 1.5.0 */ public static <T> T withObjectOutputStream(OutputStream outputStream, Closure<T> closure) throws IOException { return withStream(newObjectOutputStream(outputStream), closure); } /** * Create an object input stream for this file. * * @param file a file * @return an object input stream * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static ObjectInputStream newObjectInputStream(File file) throws IOException { return new ObjectInputStream(new FileInputStream(file)); } /** * Create an object input stream for this input stream. * * @param inputStream an input stream * @return an object input stream * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static ObjectInputStream newObjectInputStream(InputStream inputStream) throws IOException { return new ObjectInputStream(inputStream); } /** * Create an object input stream for this input stream using the given class loader. * * @param inputStream an input stream * @param classLoader the class loader to use when loading the class * @return an object input stream * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static ObjectInputStream newObjectInputStream(InputStream inputStream, final ClassLoader classLoader) throws IOException { return new ObjectInputStream(inputStream) { protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { return Class.forName(desc.getName(), true, classLoader); } }; } /** * Create an object input stream for this file using the given class loader. * * @param file a file * @param classLoader the class loader to use when loading the class * @return an object input stream * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static ObjectInputStream newObjectInputStream(File file, final ClassLoader classLoader) throws IOException { return newObjectInputStream(new FileInputStream(file), classLoader); } /** * Iterates through the given file object by object. * * @param self a File * @param closure a closure * @throws IOException if an IOException occurs. * @throws ClassNotFoundException if the class is not found. * @see #eachObject(java.io.ObjectInputStream, groovy.lang.Closure) * @since 1.0 */ public static void eachObject(File self, Closure closure) throws IOException, ClassNotFoundException { eachObject(newObjectInputStream(self), closure); } /** * Iterates through the given object stream object by object. The * ObjectInputStream is closed afterwards. * * @param ois an ObjectInputStream, closed after the operation * @param closure a closure * @throws IOException if an IOException occurs. * @throws ClassNotFoundException if the class is not found. * @since 1.0 */ public static void eachObject(ObjectInputStream ois, Closure closure) throws IOException, ClassNotFoundException { try { while (true) { try { Object obj = ois.readObject(); // we allow null objects in the object stream closure.call(obj); } catch (EOFException e) { break; } } InputStream temp = ois; ois = null; temp.close(); } finally { closeWithWarning(ois); } } /** * Create a new ObjectInputStream for this file and pass it to the closure. * This method ensures the stream is closed after the closure returns. * * @param file a File * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.InputStream, groovy.lang.Closure) * @since 1.5.2 */ public static <T> T withObjectInputStream(File file, Closure<T> closure) throws IOException { return withStream(newObjectInputStream(file), closure); } /** * Create a new ObjectInputStream for this file associated with the given class loader and pass it to the closure. * This method ensures the stream is closed after the closure returns. * * @param file a File * @param classLoader the class loader to use when loading the class * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.InputStream, groovy.lang.Closure) * @since 1.5.2 */ public static <T> T withObjectInputStream(File file, ClassLoader classLoader, Closure<T> closure) throws IOException { return withStream(newObjectInputStream(file, classLoader), closure); } /** * Create a new ObjectInputStream for this file and pass it to the closure. * This method ensures the stream is closed after the closure returns. * * @param inputStream an input stream * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.InputStream, groovy.lang.Closure) * @since 1.5.0 */ public static <T> T withObjectInputStream(InputStream inputStream, Closure<T> closure) throws IOException { return withStream(newObjectInputStream(inputStream), closure); } /** * Create a new ObjectInputStream for this file and pass it to the closure. * This method ensures the stream is closed after the closure returns. * * @param inputStream an input stream * @param classLoader the class loader to use when loading the class * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.InputStream, groovy.lang.Closure) * @since 1.5.0 */ public static <T> T withObjectInputStream(InputStream inputStream, ClassLoader classLoader, Closure<T> closure) throws IOException { return withStream(newObjectInputStream(inputStream, classLoader), closure); } /** * Iterates through this String line by line. Each line is passed * to the given 1 or 2 arg closure. If a 2 arg closure is found * the line count is passed as the second argument. * * @param self a String * @param closure a closure * @return the last value returned by the closure * @throws java.io.IOException if an error occurs * @see #eachLine(java.lang.String, int, groovy.lang.Closure) * @since 1.5.5 */ public static <T> T eachLine(String self, Closure<T> closure) throws IOException { return eachLine(self, 0, closure); } /** * Iterates through this CharSequence line by line. Each line is passed * to the given 1 or 2 arg closure. If a 2 arg closure is found * the line count is passed as the second argument. * * @param self a CharSequence * @param closure a closure * @return the last value returned by the closure * @throws java.io.IOException if an error occurs * @see #eachLine(java.lang.String, groovy.lang.Closure) * @since 1.8.2 */ public static <T> T eachLine(CharSequence self, Closure<T> closure) throws IOException { return eachLine(self.toString(), closure); } /** * Iterates through this String line by line. Each line is passed * to the given 1 or 2 arg closure. If a 2 arg closure is found * the line count is passed as the second argument. * * @param self a String * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) * @param closure a closure (arg 1 is line, optional arg 2 is line number) * @return the last value returned by the closure * @throws java.io.IOException if an error occurs * @since 1.5.7 */ public static <T> T eachLine(String self, int firstLine, Closure<T> closure) throws IOException { int count = firstLine; T result = null; for (String line : readLines(self)) { result = callClosureForLine(closure, line, count); count++; } return result; } /** * Iterates through this CharSequence line by line. Each line is passed * to the given 1 or 2 arg closure. If a 2 arg closure is found * the line count is passed as the second argument. * * @param self a CharSequence * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) * @param closure a closure (arg 1 is line, optional arg 2 is line number) * @return the last value returned by the closure * @throws java.io.IOException if an error occurs * @see #eachLine(java.lang.String, int, groovy.lang.Closure) * @since 1.8.2 */ public static <T> T eachLine(CharSequence self, int firstLine, Closure<T> closure) throws IOException { return eachLine(self.toString(), firstLine, closure); } /** * Iterates through this file line by line. Each line is passed to the * given 1 or 2 arg closure. The file is read using a reader which * is closed before this method returns. * * @param self a File * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #eachLine(java.io.File, int, groovy.lang.Closure) * @since 1.5.5 */ public static <T> T eachLine(File self, Closure<T> closure) throws IOException { return eachLine(self, 1, closure); } /** * Iterates through this file line by line. Each line is passed to the * given 1 or 2 arg closure. The file is read using a reader which * is closed before this method returns. * * @param self a File * @param charset opens the file with a specified charset * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #eachLine(java.io.File, java.lang.String, int, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T eachLine(File self, String charset, Closure<T> closure) throws IOException { return eachLine(self, charset, 1, closure); } /** * Iterates through this file line by line. Each line is passed * to the given 1 or 2 arg closure. The file is read using a reader * which is closed before this method returns. * * @param self a File * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) * @param closure a closure (arg 1 is line, optional arg 2 is line number) * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) * @since 1.5.7 */ public static <T> T eachLine(File self, int firstLine, Closure<T> closure) throws IOException { return eachLine(newReader(self), firstLine, closure); } /** * Iterates through this file line by line. Each line is passed * to the given 1 or 2 arg closure. The file is read using a reader * which is closed before this method returns. * * @param self a File * @param charset opens the file with a specified charset * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) * @param closure a closure (arg 1 is line, optional arg 2 is line number) * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T eachLine(File self, String charset, int firstLine, Closure<T> closure) throws IOException { return eachLine(newReader(self, charset), firstLine, closure); } /** * Iterates through this stream reading with the provided charset, passing each line to the * given 1 or 2 arg closure. The stream is closed before this method returns. * * @param stream a stream * @param charset opens the stream with a specified charset * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #eachLine(java.io.InputStream, java.lang.String, int, groovy.lang.Closure) * @since 1.5.5 */ public static <T> T eachLine(InputStream stream, String charset, Closure<T> closure) throws IOException { return eachLine(stream, charset, 1, closure); } /** * Iterates through this stream reading with the provided charset, passing each line to * the given 1 or 2 arg closure. The stream is closed after this method returns. * * @param stream a stream * @param charset opens the stream with a specified charset * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) * @param closure a closure (arg 1 is line, optional arg 2 is line number) * @return the last value returned by the closure * @throws IOException if an IOException occurs. * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) * @since 1.5.7 */ public static <T> T eachLine(InputStream stream, String charset, int firstLine, Closure<T> closure) throws IOException { return eachLine(new InputStreamReader(stream, charset), firstLine, closure); } /** * Iterates through this stream, passing each line to the given 1 or 2 arg closure. * The stream is closed before this method returns. * * @param stream a stream * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #eachLine(java.io.InputStream, int, groovy.lang.Closure) * @since 1.5.6 */ public static <T> T eachLine(InputStream stream, Closure<T> closure) throws IOException { return eachLine(stream, 1, closure); } /** * Iterates through this stream, passing each line to the given 1 or 2 arg closure. * The stream is closed before this method returns. * * @param stream a stream * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) * @param closure a closure (arg 1 is line, optional arg 2 is line number) * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) * @since 1.5.7 */ public static <T> T eachLine(InputStream stream, int firstLine, Closure<T> closure) throws IOException { return eachLine(new InputStreamReader(stream), firstLine, closure); } /** * Iterates through the lines read from the URL's associated input stream passing each * line to the given 1 or 2 arg closure. The stream is closed before this method returns. * * @param url a URL to open and read * @param closure a closure to apply on each line (arg 1 is line, optional arg 2 is line number starting at line 1) * @return the last value returned by the closure * @throws IOException if an IOException occurs. * @see #eachLine(java.net.URL, int, groovy.lang.Closure) * @since 1.5.6 */ public static <T> T eachLine(URL url, Closure<T> closure) throws IOException { return eachLine(url, 1, closure); } /** * Iterates through the lines read from the URL's associated input stream passing each * line to the given 1 or 2 arg closure. The stream is closed before this method returns. * * @param url a URL to open and read * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) * @param closure a closure to apply on each line (arg 1 is line, optional arg 2 is line number) * @return the last value returned by the closure * @throws IOException if an IOException occurs. * @see #eachLine(java.io.InputStream, int, groovy.lang.Closure) * @since 1.5.7 */ public static <T> T eachLine(URL url, int firstLine, Closure<T> closure) throws IOException { return eachLine(url.openConnection().getInputStream(), firstLine, closure); } /** * Iterates through the lines read from the URL's associated input stream passing each * line to the given 1 or 2 arg closure. The stream is closed before this method returns. * * @param url a URL to open and read * @param charset opens the stream with a specified charset * @param closure a closure to apply on each line (arg 1 is line, optional arg 2 is line number starting at line 1) * @return the last value returned by the closure * @throws IOException if an IOException occurs. * @see #eachLine(java.net.URL, java.lang.String, int, groovy.lang.Closure) * @since 1.5.6 */ public static <T> T eachLine(URL url, String charset, Closure<T> closure) throws IOException { return eachLine(url, charset, 1, closure); } /** * Iterates through the lines read from the URL's associated input stream passing each * line to the given 1 or 2 arg closure. The stream is closed before this method returns. * * @param url a URL to open and read * @param charset opens the stream with a specified charset * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) * @param closure a closure to apply on each line (arg 1 is line, optional arg 2 is line number) * @return the last value returned by the closure * @throws IOException if an IOException occurs. * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) * @since 1.5.7 */ public static <T> T eachLine(URL url, String charset, int firstLine, Closure<T> closure) throws IOException { return eachLine(newReader(url, charset), firstLine, closure); } /** * Iterates through the given reader line by line. Each line is passed to the * given 1 or 2 arg closure. If the closure has two arguments, the line count is passed * as the second argument. The Reader is closed before this method returns. * * @param self a Reader, closed after the method returns * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) * @since 1.5.6 */ public static <T> T eachLine(Reader self, Closure<T> closure) throws IOException { return eachLine(self, 1, closure); } /** * Iterates through the given reader line by line. Each line is passed to the * given 1 or 2 arg closure. If the closure has two arguments, the line count is passed * as the second argument. The Reader is closed before this method returns. * * @param self a Reader, closed after the method returns * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) * @param closure a closure which will be passed each line (or for 2 arg closures the line and line count) * @return the last value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.7 */ public static <T> T eachLine(Reader self, int firstLine, Closure<T> closure) throws IOException { BufferedReader br; int count = firstLine; T result = null; if (self instanceof BufferedReader) br = (BufferedReader) self; else br = new BufferedReader(self); try { while (true) { String line = br.readLine(); if (line == null) { break; } else { result = callClosureForLine(closure, line, count); count++; } } Reader temp = self; self = null; temp.close(); return result; } finally { closeWithWarning(self); closeWithWarning(br); } } /** * Iterates through this file line by line, splitting each line using * the given regex separator. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression. * Finally the resources used for processing the file are closed. * * @param self a File * @param regex the delimiting regular expression * @param closure a closure * @throws IOException if an IOException occurs. * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) * @since 1.5.5 */ public static <T> T splitEachLine(File self, String regex, Closure<T> closure) throws IOException { return splitEachLine(newReader(self), regex, closure); } /** * Iterates through this file line by line, splitting each line using * the given separator Pattern. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression Pattern. * Finally the resources used for processing the file are closed. * * @param self a File * @param pattern the regular expression Pattern for the delimiter * @param closure a closure * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T splitEachLine(File self, Pattern pattern, Closure<T> closure) throws IOException { return splitEachLine(newReader(self), pattern, closure); } /** * Iterates through this file line by line, splitting each line using * the given regex separator. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression. * Finally the resources used for processing the file are closed. * * @param self a File * @param regex the delimiting regular expression * @param charset opens the file with a specified charset * @param closure a closure * @throws IOException if an IOException occurs. * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T splitEachLine(File self, String regex, String charset, Closure<T> closure) throws IOException { return splitEachLine(newReader(self, charset), regex, closure); } /** * Iterates through this file line by line, splitting each line using * the given regex separator Pattern. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression. * Finally the resources used for processing the file are closed. * * @param self a File * @param pattern the regular expression Pattern for the delimiter * @param charset opens the file with a specified charset * @param closure a closure * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T splitEachLine(File self, Pattern pattern, String charset, Closure<T> closure) throws IOException { return splitEachLine(newReader(self, charset), pattern, closure); } /** * Iterates through the input stream associated with this URL line by line, splitting each line using * the given regex separator. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression. * Finally the resources used for processing the URL are closed. * * @param self a URL to open and read * @param regex the delimiting regular expression * @param closure a closure * @throws IOException if an IOException occurs. * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T splitEachLine(URL self, String regex, Closure<T> closure) throws IOException { return splitEachLine(newReader(self), regex, closure); } /** * Iterates through the input stream associated with this URL line by line, splitting each line using * the given regex separator Pattern. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression. * Finally the resources used for processing the URL are closed. * * @param self a URL to open and read * @param pattern the regular expression Pattern for the delimiter * @param closure a closure * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T splitEachLine(URL self, Pattern pattern, Closure<T> closure) throws IOException { return splitEachLine(newReader(self), pattern, closure); } /** * Iterates through the input stream associated with this URL line by line, splitting each line using * the given regex separator. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression. * Finally the resources used for processing the URL are closed. * * @param self a URL to open and read * @param regex the delimiting regular expression * @param charset opens the file with a specified charset * @param closure a closure * @throws IOException if an IOException occurs. * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T splitEachLine(URL self, String regex, String charset, Closure<T> closure) throws IOException { return splitEachLine(newReader(self, charset), regex, closure); } /** * Iterates through the input stream associated with this URL line by line, splitting each line using * the given regex separator Pattern. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression. * Finally the resources used for processing the URL are closed. * * @param self a URL to open and read * @param pattern the regular expression Pattern for the delimiter * @param charset opens the file with a specified charset * @param closure a closure * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T splitEachLine(URL self, Pattern pattern, String charset, Closure<T> closure) throws IOException { return splitEachLine(newReader(self, charset), pattern, closure); } /** * Iterates through the given reader line by line, splitting each line using * the given regex separator. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression. The Reader is closed afterwards. * <p/> * Here is an example: * <pre> * def s = 'The 3 quick\nbrown 4 fox' * def result = '' * new StringReader(s).splitEachLine(/\d/){ parts -> * result += "${parts[0]}_${parts[1]}|" * } * assert result == 'The _ quick|brown _ fox|' * </pre> * * @param self a Reader, closed after the method returns * @param regex the delimiting regular expression * @param closure a closure * @throws IOException if an IOException occurs. * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @return the last value returned by the closure * @see java.lang.String#split(java.lang.String) * @since 1.5.5 */ public static <T> T splitEachLine(Reader self, String regex, Closure<T> closure) throws IOException { return splitEachLine(self, Pattern.compile(regex), closure); } /** * Iterates through the given reader line by line, splitting each line using * the given regex separator Pattern. For each line, the given closure is called with * a single parameter being the list of strings computed by splitting the line * around matches of the given regular expression. The Reader is closed afterwards. * <p/> * Here is an example: * <pre> * def s = 'The 3 quick\nbrown 4 fox' * def result = '' * new StringReader(s).splitEachLine(~/\d/){ parts -> * result += "${parts[0]}_${parts[1]}|" * } * assert result == 'The _ quick|brown _ fox|' * </pre> * * @param self a Reader, closed after the method returns * @param pattern the regular expression Pattern for the delimiter * @param closure a closure * @throws IOException if an IOException occurs. * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @return the last value returned by the closure * @see java.lang.String#split(java.lang.String) * @since 1.6.8 */ public static <T> T splitEachLine(Reader self, Pattern pattern, Closure<T> closure) throws IOException { BufferedReader br; T result = null; if (self instanceof BufferedReader) br = (BufferedReader) self; else br = new BufferedReader(self); try { while (true) { String line = br.readLine(); if (line == null) { break; } else { List vals = Arrays.asList(pattern.split(line)); result = closure.call(vals); } } Reader temp = self; self = null; temp.close(); return result; } finally { closeWithWarning(self); closeWithWarning(br); } } /** * Iterates through the given InputStream line by line using the specified * encoding, splitting each line using the given separator. The list of tokens * for each line is then passed to the given closure. Finally, the stream * is closed. * * @param stream an InputStream * @param regex the delimiting regular expression * @param charset opens the stream with a specified charset * @param closure a closure * @throws IOException if an IOException occurs. * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) * @since 1.5.5 */ public static <T> T splitEachLine(InputStream stream, String regex, String charset, Closure<T> closure) throws IOException { return splitEachLine(new BufferedReader(new InputStreamReader(stream, charset)), regex, closure); } /** * Iterates through the given InputStream line by line using the specified * encoding, splitting each line using the given separator Pattern. The list of tokens * for each line is then passed to the given closure. Finally, the stream * is closed. * * @param stream an InputStream * @param pattern the regular expression Pattern for the delimiter * @param charset opens the stream with a specified charset * @param closure a closure * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T splitEachLine(InputStream stream, Pattern pattern, String charset, Closure<T> closure) throws IOException { return splitEachLine(new BufferedReader(new InputStreamReader(stream, charset)), pattern, closure); } /** * Iterates through the given InputStream line by line, splitting each line using * the given separator. The list of tokens for each line is then passed to * the given closure. The stream is closed before the method returns. * * @param stream an InputStream * @param regex the delimiting regular expression * @param closure a closure * @throws IOException if an IOException occurs. * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) * @since 1.5.6 */ public static <T> T splitEachLine(InputStream stream, String regex, Closure<T> closure) throws IOException { return splitEachLine(new BufferedReader(new InputStreamReader(stream)), regex, closure); } /** * Iterates through the given InputStream line by line, splitting each line using * the given separator Pattern. The list of tokens for each line is then passed to * the given closure. The stream is closed before the method returns. * * @param stream an InputStream * @param pattern the regular expression Pattern for the delimiter * @param closure a closure * @throws IOException if an IOException occurs. * @return the last value returned by the closure * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) * @since 1.6.8 */ public static <T> T splitEachLine(InputStream stream, Pattern pattern, Closure<T> closure) throws IOException { return splitEachLine(new BufferedReader(new InputStreamReader(stream)), pattern, closure); } /** * Iterates through the given String line by line, splitting each line using * the given separator. The list of tokens for each line is then passed to * the given closure. * * @param self a String * @param regex the delimiting regular expression * @param closure a closure * @return the last value returned by the closure * @throws java.io.IOException if an error occurs * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @see java.lang.String#split(java.lang.String) * @since 1.5.5 */ public static <T> T splitEachLine(String self, String regex, Closure<T> closure) throws IOException { return splitEachLine(self, Pattern.compile(regex), closure); } /** * Iterates through the given CharSequence line by line, splitting each line using * the given separator. The list of tokens for each line is then passed to * the given closure. * * @param self a CharSequence * @param regex the delimiting regular expression * @param closure a closure * @return the last value returned by the closure * @throws java.io.IOException if an error occurs * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid * @see #splitEachLine(String, String, Closure) * @since 1.8.2 */ public static <T> T splitEachLine(CharSequence self, CharSequence regex, Closure<T> closure) throws IOException { return splitEachLine(self.toString(), regex.toString(), closure); } /** * Iterates through the given String line by line, splitting each line using * the given separator Pattern. The list of tokens for each line is then passed to * the given closure. * * @param self a String * @param pattern the regular expression Pattern for the delimiter * @param closure a closure * @return the last value returned by the closure * @throws java.io.IOException if an error occurs * @see java.util.regex.Pattern#split(java.lang.CharSequence) * @since 1.6.8 */ public static <T> T splitEachLine(String self, Pattern pattern, Closure<T> closure) throws IOException { final List<String> list = readLines(self); T result = null; for (String line : list) { List vals = Arrays.asList(pattern.split(line)); result = closure.call(vals); } return result; } /** * Iterates through the given CharSequence line by line, splitting each line using * the given separator Pattern. The list of tokens for each line is then passed to * the given closure. * * @param self a CharSequence * @param pattern the regular expression Pattern for the delimiter * @param closure a closure * @return the last value returned by the closure * @throws java.io.IOException if an error occurs * @see #splitEachLine(String, Pattern, Closure) * @since 1.8.2 */ public static <T> T splitEachLine(CharSequence self, Pattern pattern, Closure<T> closure) throws IOException { return splitEachLine(self.toString(), pattern, closure); } /** * Read a single, whole line from the given Reader. * * @param self a Reader * @return a line * @throws IOException if an IOException occurs. * @since 1.0 */ public static String readLine(Reader self) throws IOException { if (self instanceof BufferedReader) { BufferedReader br = (BufferedReader) self; return br.readLine(); } if (self.markSupported()) { return readLineFromReaderWithMark(self); } return readLineFromReaderWithoutMark(self); } private static int charBufferSize = 4096; // half the default stream buffer size private static int expectedLineLength = 160; // double the default line length private static int EOF = -1; // End Of File /* * This method tries to read subsequent buffers from the reader using a mark */ private static String readLineFromReaderWithMark(final Reader input) throws IOException { char[] cbuf = new char[charBufferSize]; try { input.mark(charBufferSize); } catch (IOException e) { // this should never happen LOG.warning("Caught exception setting mark on supporting reader: " + e); // fallback return readLineFromReaderWithoutMark(input); } // could be changed into do..while, but then // we might create an additional StringBuffer // instance at the end of the stream int count = input.read(cbuf); if (count == EOF) // we are at the end of the input data return null; StringBuffer line = new StringBuffer(expectedLineLength); // now work on the buffer(s) int ls = lineSeparatorIndex(cbuf, count); while (ls == -1) { line.append(cbuf, 0, count); count = input.read(cbuf); if (count == EOF) { // we are at the end of the input data return line.toString(); } ls = lineSeparatorIndex(cbuf, count); } line.append(cbuf, 0, ls); // correct ls if we have \r\n int skipLS = 1; if (ls + 1 < count) { // we are not at the end of the buffer if (cbuf[ls] == '\r' && cbuf[ls + 1] == '\n') { skipLS++; } } else { if (cbuf[ls] == '\r' && input.read() == '\n') { skipLS++; } } //reset() and skip over last linesep input.reset(); input.skip(line.length() + skipLS); return line.toString(); } /* * This method reads without a buffer. * It returns too many empty lines if \r\n combinations * are used. Nothing can be done because we can't push * back the character we have just read. */ private static String readLineFromReaderWithoutMark(Reader input) throws IOException { int c = input.read(); if (c == -1) return null; StringBuffer line = new StringBuffer(expectedLineLength); while (c != EOF && c != '\n' && c != '\r') { char ch = (char) c; line.append(ch); c = input.read(); } return line.toString(); } /* * searches for \n or \r * Returns -1 if not found. */ private static int lineSeparatorIndex(char[] array, int length) { for (int k = 0; k < length; k++) { if (isLineSeparator(array[k])) { return k; } } return -1; } /* * true if either \n or \r */ private static boolean isLineSeparator(char c) { return c == '\n' || c == '\r'; } static String lineSeparator = null; /** * Return a String with lines (separated by LF, CR/LF, or CR) * terminated by the platform specific line separator. * * @param self a String object * @return the denormalized string * @since 1.6.0 */ public static String denormalize(final String self) { // Don't do this in static initializer because we may never be needed. // TODO: Put this lineSeparator property somewhere everyone can use it. if (lineSeparator == null) { final StringWriter sw = new StringWriter(2); try { // We use BufferedWriter rather than System.getProperty because // it has the security manager rigamarole to deal with the possible exception. final BufferedWriter bw = new BufferedWriter(sw); bw.newLine(); bw.flush(); lineSeparator = sw.toString(); } catch (IOException ioe) { // This shouldn't happen, but this is the same default used by // BufferedWriter on a security exception. lineSeparator = "\n"; } } final int len = self.length(); if (len < 1) { return self; } final StringBuilder sb = new StringBuilder((110 * len) / 100); int i = 0; while (i < len) { final char ch = self.charAt(i++); switch (ch) { case '\r': sb.append(lineSeparator); // Eat the following LF if any. if ((i < len) && (self.charAt(i) == '\n')) { ++i; } break; case '\n': sb.append(lineSeparator); break; default: sb.append(ch); break; } } return sb.toString(); } /** * Return a CharSequence with lines (separated by LF, CR/LF, or CR) * terminated by the platform specific line separator. * * @param self a CharSequence object * @return the denormalized CharSequence * @see #denormalize(String) * @since 1.8.2 */ public static CharSequence denormalize(final CharSequence self) { return denormalize(self.toString()); } /** * Return a String with linefeeds and carriage returns normalized to linefeeds. * * @param self a String object * @return the normalized string * @since 1.6.0 */ public static String normalize(final String self) { int nx = self.indexOf('\r'); if (nx < 0) { return self; } final int len = self.length(); final StringBuilder sb = new StringBuilder(len); int i = 0; do { sb.append(self, i, nx); sb.append('\n'); if ((i = nx + 1) >= len) break; if (self.charAt(i) == '\n') { // skip the LF in CR LF if (++i >= len) break; } nx = self.indexOf('\r', i); } while (nx > 0); sb.append(self, i, len); return sb.toString(); } /** * Return a CharSequence with linefeeds and carriage returns normalized to linefeeds. * * @param self a CharSequence object * @return the normalized CharSequence * @see #normalize(String) * @since 1.8.2 */ public static CharSequence normalize(final CharSequence self) { return normalize(self.toString()); } /** * Return the lines of a String as a List of Strings. * * @param self a String object * @return a list of lines * @throws java.io.IOException if an error occurs * @since 1.5.5 */ public static List<String> readLines(String self) throws IOException { return readLines(new StringReader(self)); } /** * Return the lines of a CharSequence as a List of CharSequence. * * @param self a CharSequence object * @return a list of lines * @throws java.io.IOException if an error occurs * @since 1.8.2 */ public static List<CharSequence> readLines(CharSequence self) throws IOException { return new ArrayList<CharSequence>(readLines(self.toString())); } /** * Reads the file into a list of Strings, with one item for each line. * * @param file a File * @return a List of lines * @throws IOException if an IOException occurs. * @see #readLines(java.io.Reader) * @since 1.0 */ public static List<String> readLines(File file) throws IOException { return readLines(newReader(file)); } /** * Reads the file into a list of Strings, with one item for each line. * * @param file a File * @param charset opens the file with a specified charset * @return a List of lines * @throws IOException if an IOException occurs. * @see #readLines(java.io.Reader) * @since 1.6.8 */ public static List<String> readLines(File file, String charset) throws IOException { return readLines(newReader(file, charset)); } /** * Reads the stream into a list, with one element for each line. * * @param stream a stream * @return a List of lines * @throws IOException if an IOException occurs. * @see #readLines(java.io.Reader) * @since 1.0 */ public static List<String> readLines(InputStream stream) throws IOException { return readLines(newReader(stream)); } /** * Reads the stream into a list, with one element for each line. * * @param stream a stream * @param charset opens the stream with a specified charset * @return a List of lines * @throws IOException if an IOException occurs. * @see #readLines(java.io.Reader) * @since 1.6.8 */ public static List<String> readLines(InputStream stream, String charset) throws IOException { return readLines(newReader(stream, charset)); } /** * Reads the URL contents into a list, with one element for each line. * * @param self a URL * @return a List of lines * @throws IOException if an IOException occurs. * @see #readLines(java.io.Reader) * @since 1.6.8 */ public static List<String> readLines(URL self) throws IOException { return readLines(newReader(self)); } /** * Reads the URL contents into a list, with one element for each line. * * @param self a URL * @param charset opens the URL with a specified charset * @return a List of lines * @throws IOException if an IOException occurs. * @see #readLines(java.io.Reader) * @since 1.6.8 */ public static List<String> readLines(URL self, String charset) throws IOException { return readLines(newReader(self, charset)); } /** * Reads the reader into a list of Strings, with one entry for each line. * The reader is closed before this method returns. * * @param reader a Reader * @return a List of lines * @throws IOException if an IOException occurs. * @since 1.0 */ public static List<String> readLines(Reader reader) throws IOException { IteratorClosureAdapter closure = new IteratorClosureAdapter(reader); eachLine(reader, closure); return closure.asList(); } /** * Read the content of the File using the specified encoding and return it * as a String. * * @param file the file whose content we want to read * @param charset the charset used to read the content of the file * @return a String containing the content of the file * @throws IOException if an IOException occurs. * @since 1.0 */ public static String getText(File file, String charset) throws IOException { return getText(newReader(file, charset)); } /** * Read the content of the File and returns it as a String. * * @param file the file whose content we want to read * @return a String containing the content of the file * @throws IOException if an IOException occurs. * @since 1.0 */ public static String getText(File file) throws IOException { return getText(newReader(file)); } /** * Read the content of this URL and returns it as a String. * * @param url URL to read content from * @return the text from that URL * @throws IOException if an IOException occurs. * @since 1.0 */ public static String getText(URL url) throws IOException { return getText(url, CharsetToolkit.getDefaultSystemCharset().toString()); } /** * Read the content of this URL and returns it as a String. * * @param url URL to read content from * @param parameters connection parameters * @return the text from that URL * @throws IOException if an IOException occurs. * @since 1.8.1 */ public static String getText(URL url, Map parameters) throws IOException { return getText(url, parameters, CharsetToolkit.getDefaultSystemCharset().toString()); } /** * Read the data from this URL and return it as a String. The connection * stream is closed before this method returns. * * @param url URL to read content from * @param charset opens the stream with a specified charset * @return the text from that URL * @throws IOException if an IOException occurs. * @see java.net.URLConnection#getInputStream() * @since 1.0 */ public static String getText(URL url, String charset) throws IOException { BufferedReader reader = newReader(url, charset); return getText(reader); } /** * Read the data from this URL and return it as a String. The connection * stream is closed before this method returns. * * @param url URL to read content from * @param parameters connection parameters * @param charset opens the stream with a specified charset * @return the text from that URL * @throws IOException if an IOException occurs. * @see java.net.URLConnection#getInputStream() * @since 1.8.1 */ public static String getText(URL url, Map parameters, String charset) throws IOException { BufferedReader reader = newReader(url, parameters, charset); return getText(reader); } /** * Read the content of this InputStream and return it as a String. * The stream is closed before this method returns. * * @param is an input stream * @return the text from that URL * @throws IOException if an IOException occurs. * @since 1.0 */ public static String getText(InputStream is) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); return getText(reader); } /** * Read the content of this InputStream using specified charset and return * it as a String. The stream is closed before this method returns. * * @param is an input stream * @param charset opens the stream with a specified charset * @return the text from that URL * @throws IOException if an IOException occurs. * @since 1.0 */ public static String getText(InputStream is, String charset) throws IOException { BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset)); return getText(reader); } /** * Read the content of the Reader and return it as a String. The reader * is closed before this method returns. * * @param reader a Reader whose content we want to read * @return a String containing the content of the buffered reader * @throws IOException if an IOException occurs. * @see #getText(java.io.BufferedReader) * @since 1.0 */ public static String getText(Reader reader) throws IOException { BufferedReader bufferedReader = new BufferedReader(reader); return getText(bufferedReader); } /** * Read the content of the BufferedReader and return it as a String. * The BufferedReader is closed afterwards. * * @param reader a BufferedReader whose content we want to read * @return a String containing the content of the buffered reader * @throws IOException if an IOException occurs. * @since 1.0 */ public static String getText(BufferedReader reader) throws IOException { StringBuilder answer = new StringBuilder(); // reading the content of the file within a char buffer // allow to keep the correct line endings char[] charBuffer = new char[8192]; int nbCharRead /* = 0*/; try { while ((nbCharRead = reader.read(charBuffer)) != -1) { // appends buffer answer.append(charBuffer, 0, nbCharRead); } Reader temp = reader; reader = null; temp.close(); } finally { closeWithWarning(reader); } return answer.toString(); } /** * Read the content of the File and returns it as a byte[]. * * @param file the file whose content we want to read * @return a String containing the content of the file * @throws IOException if an IOException occurs. * @since 1.7.1 */ public static byte[] getBytes(File file) throws IOException { return getBytes(new FileInputStream(file)); } /** * Read the content of this URL and returns it as a byte[]. * * @param url URL to read content from * @return the byte[] from that URL * @throws IOException if an IOException occurs. * @since 1.7.1 */ public static byte[] getBytes(URL url) throws IOException { return getBytes(url.openConnection().getInputStream()); } /** * Read the content of this InputStream and return it as a byte[]. * The stream is closed before this method returns. * * @param is an input stream * @return the byte[] from that InputStream * @throws IOException if an IOException occurs. * @since 1.7.1 */ public static byte[] getBytes(InputStream is) throws IOException { ByteArrayOutputStream answer = new ByteArrayOutputStream(); // reading the content of the file within a byte buffer byte[] byteBuffer = new byte[8192]; int nbByteRead /* = 0*/; try { while ((nbByteRead = is.read(byteBuffer)) != -1) { // appends buffer answer.write(byteBuffer, 0, nbByteRead); } } finally { closeWithWarning(is); } return answer.toByteArray(); } /** * Write the bytes from the byte array to the File. * * @param file the file to write to * @param bytes the byte[] to write to the file * @throws IOException if an IOException occurs. * @since 1.7.1 */ public static void setBytes(File file, byte[] bytes) throws IOException { setBytes(new FileOutputStream(file), bytes); } /** * Write the byte[] to the output stream. * The stream is closed before this method returns. * * @param os an output stream * @param bytes the byte[] to write to the output stream * @throws IOException if an IOException occurs. * @since 1.7.1 */ public static void setBytes(OutputStream os, byte[] bytes) throws IOException { try { os.write(bytes); } finally { closeWithWarning(os); } } /** * Write the text and append a newline (using the platform's line-ending). * * @param writer a BufferedWriter * @param line the line to write * @throws IOException if an IOException occurs. * @since 1.0 */ public static void writeLine(BufferedWriter writer, String line) throws IOException { writer.write(line); writer.newLine(); } /** * Write the text to the File. * * @param file a File * @param text the text to write to the File * @throws IOException if an IOException occurs. * @since 1.0 */ public static void write(File file, String text) throws IOException { BufferedWriter writer = null; try { writer = newWriter(file); writer.write(text); writer.flush(); Writer temp = writer; writer = null; temp.close(); } finally { closeWithWarning(writer); } } /** * Synonym for write(text) allowing file.text = 'foo'. * * @param file a File * @param text the text to write to the File * @throws IOException if an IOException occurs. * @see #write(java.io.File, java.lang.String) * @since 1.5.1 */ public static void setText(File file, String text) throws IOException { write(file, text); } /** * Synonym for write(text, charset) allowing: * <pre> * myFile.setText('some text', charset) * </pre> * or with some help from <code>ExpandoMetaClass</code>, you could do something like: * <pre> * myFile.metaClass.setText = { String s -> delegate.setText(s, 'UTF-8') } * myfile.text = 'some text' * </pre> * * @param file A File * @param charset The charset used when writing to the file * @param text The text to write to the File * @throws IOException if an IOException occurs. * @see #write(java.io.File, java.lang.String, java.lang.String) * @since 1.7.3 */ public static void setText(File file, String text, String charset) throws IOException { write(file, text, charset); } /** * Write the text to the File. * * @param file a File * @param text the text to write to the File * @return the original file * @throws IOException if an IOException occurs. * @since 1.0 */ public static File leftShift(File file, Object text) throws IOException { append(file, text); return file; } /** * Write bytes to a File. * * @param file a File * @param bytes the byte array to append to the end of the File * @return the original file * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static File leftShift(File file, byte[] bytes) throws IOException { append(file, bytes); return file; } /** * Append binary data to the file. See {@link #append(java.io.File, java.io.InputStream)} * @param file a File * @param data an InputStream of data to write to the file * @return the file * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static File leftShift(File file, InputStream data) throws IOException { append(file, data); return file; } /** * Write the text to the File, using the specified encoding. * * @param file a File * @param text the text to write to the File * @param charset the charset used * @throws IOException if an IOException occurs. * @since 1.0 */ public static void write(File file, String text, String charset) throws IOException { BufferedWriter writer = null; try { writer = newWriter(file, charset); writer.write(text); writer.flush(); Writer temp = writer; writer = null; temp.close(); } finally { closeWithWarning(writer); } } /** * Append the text at the end of the File. * * @param file a File * @param text the text to append at the end of the File * @throws IOException if an IOException occurs. * @since 1.0 */ public static void append(File file, Object text) throws IOException { BufferedWriter writer = null; try { writer = newWriter(file, true); InvokerHelper.write(writer, text); writer.flush(); Writer temp = writer; writer = null; temp.close(); } finally { closeWithWarning(writer); } } /** * Append bytes to the end of a File. * * @param file a File * @param bytes the byte array to append to the end of the File * @throws IOException if an IOException occurs. * @since 1.5.1 */ public static void append(File file, byte[] bytes) throws IOException { BufferedOutputStream stream = null; try { stream = new BufferedOutputStream( new FileOutputStream(file,true) ); stream.write(bytes, 0, bytes.length); stream.flush(); OutputStream temp = stream; stream = null; temp.close(); } finally { closeWithWarning(stream); } } /** * Append binary data to the file. It <strong>will not</strong> be * interpreted as text. * @param self a File * @param stream stream to read data from. * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static void append(File self, InputStream stream ) throws IOException { OutputStream out = new FileOutputStream( self, true ); try { leftShift( out, stream ); } finally { closeWithWarning( out ); } } /** * Append the text at the end of the File, using a specified encoding. * * @param file a File * @param text the text to append at the end of the File * @param charset the charset used * @throws IOException if an IOException occurs. * @since 1.0 */ public static void append(File file, Object text, String charset) throws IOException { BufferedWriter writer = null; try { writer = newWriter(file, charset, true); InvokerHelper.write(writer, text); writer.flush(); Writer temp = writer; writer = null; temp.close(); } finally { closeWithWarning(writer); } } /** * This method is used to throw useful exceptions when the eachFile* and eachDir closure methods * are used incorrectly. * * @param dir The directory to check * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @since 1.0 */ private static void checkDir(File dir) throws FileNotFoundException, IllegalArgumentException { if (!dir.exists()) throw new FileNotFoundException(dir.getAbsolutePath()); if (!dir.isDirectory()) throw new IllegalArgumentException("The provided File object is not a directory: " + dir.getAbsolutePath()); } /** * Invokes the closure for each 'child' file in this 'parent' folder/directory. * Both regular files and subfolders/subdirectories can be processed depending * on the fileType enum value. * * @param self a file object * @param fileType if normal files or directories or both should be processed * @param closure the closure to invoke * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @since 1.7.1 */ public static void eachFile(final File self, final FileType fileType, final Closure closure) throws FileNotFoundException, IllegalArgumentException { checkDir(self); final File[] files = self.listFiles(); // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836 if (files == null) return; for (File file : files) { if (fileType == FileType.ANY || (fileType != FileType.FILES && file.isDirectory()) || (fileType != FileType.DIRECTORIES && file.isFile())) { closure.call(file); } } } /** * Invokes the closure for each 'child' file in this 'parent' folder/directory. * Both regular files and subfolders/subdirectories are processed. * * @param self a File (that happens to be a folder/directory) * @param closure a closure (first parameter is the 'child' file) * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @see java.io.File#listFiles() * @see #eachFile(java.io.File, groovy.io.FileType, groovy.lang.Closure) * @since 1.5.0 */ public static void eachFile(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException { eachFile(self, FileType.ANY, closure); } /** * Invokes the closure for each subdirectory in this directory, * ignoring regular files. * * @param self a File (that happens to be a folder/directory) * @param closure a closure (first parameter is the subdirectory file) * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @see java.io.File#listFiles() * @see #eachFile(java.io.File, groovy.io.FileType, groovy.lang.Closure) * @since 1.0 */ public static void eachDir(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException { eachFile(self, FileType.DIRECTORIES, closure); } /** * Invokes the closure for each descendant file in this directory. * Sub-directories are recursively searched in a depth-first fashion. * Both regular files and subdirectories may be passed to the closure * depending on the value of fileType. * * @param self a file object * @param fileType if normal files or directories or both should be processed * @param closure the closure to invoke on each file * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @since 1.7.1 */ public static void eachFileRecurse(final File self, final FileType fileType, final Closure closure) throws FileNotFoundException, IllegalArgumentException { checkDir(self); final File[] files = self.listFiles(); // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836 if (files == null) return; for (File file : files) { if (file.isDirectory()) { if (fileType != FileType.FILES) closure.call(file); eachFileRecurse(file, fileType, closure); } else if (fileType != FileType.DIRECTORIES) { closure.call(file); } } } /** * Invokes <code>closure</code> for each descendant file in this directory tree. * Sub-directories are recursively traversed as found. * The traversal can be adapted by providing various options in the <code>options</code> Map according * to the following keys:<dl> * <dt>type</dt><dd>A {@link groovy.io.FileType} enum to determine if normal files or directories or both are processed</dd> * <dt>preDir</dt><dd>A {@link groovy.lang.Closure} run before each directory is processed and optionally returning a {@link groovy.io.FileVisitResult} value * which can be used to control subsequent processing.</dd> * <dt>preRoot</dt><dd>A boolean indicating that the 'preDir' closure should be applied at the root level</dd> * <dt>postDir</dt><dd>A {@link groovy.lang.Closure} run after each directory is processed and optionally returning a {@link groovy.io.FileVisitResult} value * which can be used to control subsequent processing.</dd> * <dt>postRoot</dt><dd>A boolean indicating that the 'postDir' closure should be applied at the root level</dd> * <dt>visitRoot</dt><dd>A boolean indicating that the given closure should be applied for the root dir * (not applicable if the 'type' is set to {@link groovy.io.FileType#FILES})</dd> * <dt>maxDepth</dt><dd>The maximum number of directory levels when recursing * (default is -1 which means infinite, set to 0 for no recursion)</dd> * <dt>filter</dt><dd>A filter to perform on traversed files/directories (using the {@link #isCase(java.lang.Object, java.lang.Object)} method). If set, * only files/dirs which match are candidates for visiting.</dd> * <dt>nameFilter</dt><dd>A filter to perform on the name of traversed files/directories (using the {@link #isCase(java.lang.Object, java.lang.Object)} method). If set, * only files/dirs which match are candidates for visiting. (Must not be set if 'filter' is set)</dd> * <dt>excludeFilter</dt><dd>A filter to perform on traversed files/directories (using the {@link #isCase(java.lang.Object, java.lang.Object)} method). * If set, any candidates which match won't be visited.</dd> * <dt>excludeNameFilter</dt><dd>A filter to perform on the names of traversed files/directories (using the {@link #isCase(java.lang.Object, java.lang.Object)} method). * If set, any candidates which match won't be visited. (Must not be set if 'excludeFilter' is set)</dd> * <dt>sort</dt><dd>A {@link groovy.lang.Closure} which if set causes the files and subdirectories for each directory to be processed in sorted order. * Note that even when processing only files, the order of visited subdirectories will be affected by this parameter.</dd> * </dl> * This example prints out file counts and size aggregates for groovy source files within a directory tree: * <pre> * def totalSize = 0 * def count = 0 * def sortByTypeThenName = { a, b -> * a.isFile() != b.isFile() ? a.isFile() <=> b.isFile() : a.name <=> b.name * } * rootDir.traverse( * type : FILES, * nameFilter : ~/.*\.groovy/, * preDir : { if (it.name == '.svn') return SKIP_SUBTREE }, * postDir : { println "Found $count files in $it.name totalling $totalSize bytes" * totalSize = 0; count = 0 }, * postRoot : true * sort : sortByTypeThenName * ) {it -> totalSize += it.size(); count++ } * </pre> * * @param self a File * @param options a Map of options to alter the traversal behavior * @param closure the Closure to invoke on each file/directory and optionally returning a {@link groovy.io.FileVisitResult} value * which can be used to control subsequent processing * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory or illegal filter combinations are supplied * @see #sort(java.util.Collection, groovy.lang.Closure) * @see groovy.io.FileVisitResult * @see groovy.io.FileType * @since 1.7.1 */ public static void traverse(final File self, final Map<String, Object> options, final Closure closure) throws FileNotFoundException, IllegalArgumentException { Number maxDepthNumber = asType(options.remove("maxDepth"), Number.class); int maxDepth = maxDepthNumber == null ? -1 : maxDepthNumber.intValue(); Boolean visitRoot = asType(get(options, "visitRoot", false), Boolean.class); Boolean preRoot = asType(get(options, "preRoot", false), Boolean.class); Boolean postRoot = asType(get(options, "postRoot", false), Boolean.class); final Closure pre = (Closure) options.get("preDir"); final Closure post = (Closure) options.get("postDir"); final FileType type = (FileType) options.get("type"); final Object filter = options.get("filter"); final Object nameFilter = options.get("nameFilter"); final Object excludeFilter = options.get("excludeFilter"); final Object excludeNameFilter = options.get("excludeNameFilter"); Object preResult = null; if (preRoot && pre != null) { preResult = pre.call(self); } if (preResult == FileVisitResult.TERMINATE || preResult == FileVisitResult.SKIP_SUBTREE) return; FileVisitResult terminated = traverse(self, options, closure, maxDepth); if (type != FileType.FILES && visitRoot) { if (closure != null && notFiltered(self, filter, nameFilter, excludeFilter, excludeNameFilter)) { Object closureResult = closure.call(self); if (closureResult == FileVisitResult.TERMINATE) return; } } if (postRoot && post != null && terminated != FileVisitResult.TERMINATE) post.call(self); } private static boolean notFiltered(File file, Object filter, Object nameFilter, Object excludeFilter, Object excludeNameFilter) { if (filter == null && nameFilter == null && excludeFilter == null && excludeNameFilter == null) return true; if (filter != null && nameFilter != null) throw new IllegalArgumentException("Can't set both 'filter' and 'nameFilter'"); if (excludeFilter != null && excludeNameFilter != null) throw new IllegalArgumentException("Can't set both 'excludeFilter' and 'excludeNameFilter'"); Object filterToUse = null; Object filterParam = null; if (filter != null) { filterToUse = filter; filterParam = file; } else if (nameFilter != null) { filterToUse = nameFilter; filterParam = file.getName(); } Object excludeFilterToUse = null; Object excludeParam = null; if (excludeFilter != null) { excludeFilterToUse = excludeFilter; excludeParam = file; } else if (excludeNameFilter != null) { excludeFilterToUse = excludeNameFilter; excludeParam = file.getName(); } final MetaClass filterMC = filterToUse == null ? null : InvokerHelper.getMetaClass(filterToUse); final MetaClass excludeMC = excludeFilterToUse == null ? null : InvokerHelper.getMetaClass(excludeFilterToUse); boolean included = filterToUse == null || DefaultTypeTransformation.castToBoolean(filterMC.invokeMethod(filterToUse, "isCase", filterParam)); boolean excluded = excludeFilterToUse != null && DefaultTypeTransformation.castToBoolean(excludeMC.invokeMethod(excludeFilterToUse, "isCase", excludeParam)); return included && !excluded; } /** * Invokes the closure for each descendant file in this directory tree. * Sub-directories are recursively traversed in a depth-first fashion. * Convenience method for {@link #traverse(java.io.File, java.util.Map, groovy.lang.Closure)} when * no options to alter the traversal behavior are required. * * @param self a File * @param closure the Closure to invoke on each file/directory and optionally returning a {@link groovy.io.FileVisitResult} value * which can be used to control subsequent processing * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @see #traverse(java.io.File, java.util.Map, groovy.lang.Closure) * @since 1.7.1 */ public static void traverse(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException { traverse(self, new HashMap<String, Object>(), closure); } /** * Invokes the closure specified with key 'visit' in the options Map * for each descendant file in this directory tree. Convenience method * for {@link #traverse(java.io.File, java.util.Map, groovy.lang.Closure)} allowing the 'visit' closure * to be included in the options Map rather than as a parameter. * * @param self a File * @param options a Map of options to alter the traversal behavior * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory or illegal filter combinations are supplied * @see #traverse(java.io.File, java.util.Map, groovy.lang.Closure) * @since 1.7.1 */ public static void traverse(final File self, final Map<String, Object> options) throws FileNotFoundException, IllegalArgumentException { final Closure visit = (Closure) options.remove("visit"); traverse(self, options, visit); } private static FileVisitResult traverse(final File self, final Map<String, Object> options, final Closure closure, final int maxDepth) throws FileNotFoundException, IllegalArgumentException { checkDir(self); final Closure pre = (Closure) options.get("preDir"); final Closure post = (Closure) options.get("postDir"); final FileType type = (FileType) options.get("type"); final Object filter = options.get("filter"); final Object nameFilter = options.get("nameFilter"); final Object excludeFilter = options.get("excludeFilter"); final Object excludeNameFilter = options.get("excludeNameFilter"); final Closure sort = (Closure) options.get("sort"); final File[] origFiles = self.listFiles(); // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836 if (origFiles != null) { List<File> files = Arrays.asList(origFiles); if (sort != null) files = sort(files, sort); for (File file : files) { if (file.isDirectory()) { if (type != FileType.FILES) { if (closure != null && notFiltered(file, filter, nameFilter, excludeFilter, excludeNameFilter)) { Object closureResult = closure.call(file); if (closureResult == FileVisitResult.SKIP_SIBLINGS) break; if (closureResult == FileVisitResult.TERMINATE) return FileVisitResult.TERMINATE; } } if (maxDepth != 0) { Object preResult = null; if (pre != null) { preResult = pre.call(file); } if (preResult == FileVisitResult.SKIP_SIBLINGS) break; if (preResult == FileVisitResult.TERMINATE) return FileVisitResult.TERMINATE; if (preResult != FileVisitResult.SKIP_SUBTREE) { FileVisitResult terminated = traverse(file, options, closure, maxDepth - 1); if (terminated == FileVisitResult.TERMINATE) return terminated; } Object postResult = null; if (post != null) { postResult = post.call(file); } if (postResult == FileVisitResult.SKIP_SIBLINGS) break; if (postResult == FileVisitResult.TERMINATE) return FileVisitResult.TERMINATE; } } else if (type != FileType.DIRECTORIES) { if (closure != null && notFiltered(file, filter, nameFilter, excludeFilter, excludeNameFilter)) { Object closureResult = closure.call(file); if (closureResult == FileVisitResult.SKIP_SIBLINGS) break; if (closureResult == FileVisitResult.TERMINATE) return FileVisitResult.TERMINATE; } } } } return FileVisitResult.CONTINUE; } /** * Invokes the closure for each descendant file in this directory. * Sub-directories are recursively searched in a depth-first fashion. * Both regular files and subdirectories are passed to the closure. * * @param self a File * @param closure a closure * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @see #eachFileRecurse(java.io.File, groovy.io.FileType, groovy.lang.Closure) * @since 1.0 */ public static void eachFileRecurse(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException { eachFileRecurse(self, FileType.ANY, closure); } /** * Invokes the closure for each descendant directory of this directory. * Sub-directories are recursively searched in a depth-first fashion. * Only subdirectories are passed to the closure; regular files are ignored. * * @param self a directory * @param closure a closure * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @see #eachFileRecurse(java.io.File, groovy.io.FileType, groovy.lang.Closure) * @since 1.5.0 */ public static void eachDirRecurse(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException { eachFileRecurse(self, FileType.DIRECTORIES, closure); } /** * Invokes the closure for each file whose name (file.name) matches the given nameFilter in the given directory * - calling the {@link #isCase(java.lang.Object, java.lang.Object)} method to determine if a match occurs. This method can be used * with different kinds of filters like regular expressions, classes, ranges etc. * Both regular files and subdirectories may be candidates for matching depending * on the value of fileType. * <pre> * // collect names of files in baseDir matching supplied regex pattern * import static groovy.io.FileType.* * def names = [] * baseDir.eachFileMatch FILES, ~/foo\d\.txt/, { names << it.name } * assert names == ['foo1.txt', 'foo2.txt'] * * // remove all *.bak files in baseDir * baseDir.eachFileMatch FILES, ~/.*\.bak/, { File bak -> bak.delete() } * * // print out files > 4K in size from baseDir * baseDir.eachFileMatch FILES, { new File(baseDir, it).size() > 4096 }, { println "$it.name ${it.size()}" } * </pre> * * @param self a file * @param fileType whether normal files or directories or both should be processed * @param nameFilter the filter to perform on the name of the file/directory (using the {@link #isCase(java.lang.Object, java.lang.Object)} method) * @param closure the closure to invoke * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @since 1.7.1 */ public static void eachFileMatch(final File self, final FileType fileType, final Object nameFilter, final Closure closure) throws FileNotFoundException, IllegalArgumentException { checkDir(self); final File[] files = self.listFiles(); // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836 if (files == null) return; final MetaClass metaClass = InvokerHelper.getMetaClass(nameFilter); for (final File currentFile : files) { if ((fileType != FileType.FILES && currentFile.isDirectory()) || (fileType != FileType.DIRECTORIES && currentFile.isFile())) { if (DefaultTypeTransformation.castToBoolean(metaClass.invokeMethod(nameFilter, "isCase", currentFile.getName()))) closure.call(currentFile); } } } /** * Invokes the closure for each file whose name (file.name) matches the given nameFilter in the given directory * - calling the {@link #isCase(java.lang.Object, java.lang.Object)} method to determine if a match occurs. This method can be used * with different kinds of filters like regular expressions, classes, ranges etc. * Both regular files and subdirectories are matched. * * @param self a file * @param nameFilter the nameFilter to perform on the name of the file (using the {@link #isCase(java.lang.Object, java.lang.Object)} method) * @param closure the closure to invoke * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @see #eachFileMatch(java.io.File, groovy.io.FileType, java.lang.Object, groovy.lang.Closure) * @since 1.5.0 */ public static void eachFileMatch(final File self, final Object nameFilter, final Closure closure) throws FileNotFoundException, IllegalArgumentException { eachFileMatch(self, FileType.ANY, nameFilter, closure); } /** * Invokes the closure for each subdirectory whose name (dir.name) matches the given nameFilter in the given directory * - calling the {@link #isCase(java.lang.Object, java.lang.Object)} method to determine if a match occurs. This method can be used * with different kinds of filters like regular expressions, classes, ranges etc. * Only subdirectories are matched; regular files are ignored. * * @param self a file * @param nameFilter the nameFilter to perform on the name of the directory (using the {@link #isCase(java.lang.Object, java.lang.Object)} method) * @param closure the closure to invoke * @throws FileNotFoundException if the given directory does not exist * @throws IllegalArgumentException if the provided File object does not represent a directory * @see #eachFileMatch(java.io.File, groovy.io.FileType, java.lang.Object, groovy.lang.Closure) * @since 1.5.0 */ public static void eachDirMatch(final File self, final Object nameFilter, final Closure closure) throws FileNotFoundException, IllegalArgumentException { eachFileMatch(self, FileType.DIRECTORIES, nameFilter, closure); } /** * Deletes a directory with all contained files and subdirectories. * <p>The method returns * <ul> * <li>true, when deletion was successful</li> * <li>true, when it is called for a non existing directory</li> * <li>false, when it is called for a file which isn't a directory</li> * <li>false, when directory couldn't be deleted</li> * </ul> * </p> * * @param self a File * @return true if the file doesn't exist or deletion was successful * @since 1.6.0 */ public static boolean deleteDir(final File self) { if (!self.exists()) return true; if (!self.isDirectory()) return false; File[] files = self.listFiles(); if (files == null) // couldn't access files return false; // delete contained files boolean result = true; for (File file : files) { if (file.isDirectory()) { if (!deleteDir(file)) result = false; } else { if (!file.delete()) result = false; } } // now delete directory itself if(!self.delete()) result = false; return result; } /** * Renames the file. It's a shortcut for {@link java.io.File#renameTo(File)} * * @param self a File * @param newPathName The new pathname for the named file * @return <code>true</code> if and only if the renaming succeeded; * <code>false</code> otherwise * @since 1.7.4 */ public static boolean renameTo(final File self, String newPathName) { return self.renameTo(new File(newPathName)); } /** * Allows a simple syntax for using timers. This timer will execute the * given closure after the given delay. * * @param timer a timer object * @param delay the delay in milliseconds before running the closure code * @param closure the closure to invoke * @return The timer task which has been scheduled. * @since 1.5.0 */ public static TimerTask runAfter(Timer timer, int delay, final Closure closure) { TimerTask timerTask = new TimerTask() { public void run() { closure.call(); } }; timer.schedule(timerTask, delay); return timerTask; } /** * Create a buffered reader for this file. * * @param file a File * @return a BufferedReader * @throws IOException if an IOException occurs. * @since 1.0 */ public static BufferedReader newReader(File file) throws IOException { CharsetToolkit toolkit = new CharsetToolkit(file); return toolkit.getReader(); } /** * Create a buffered reader for this file, using the specified * charset as the encoding. * * @param file a File * @param charset the charset for this File * @return a BufferedReader * @throws FileNotFoundException if the File was not found * @throws UnsupportedEncodingException if the encoding specified is not supported * @since 1.0 */ public static BufferedReader newReader(File file, String charset) throws FileNotFoundException, UnsupportedEncodingException { return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset)); } /** * Creates a reader for this input stream. * * @param self an input stream * @return a reader * @since 1.0 */ public static BufferedReader newReader(InputStream self) { return new BufferedReader(new InputStreamReader(self)); } /** * Creates a reader for this input stream, using the specified * charset as the encoding. * * @param self an input stream * @param charset the charset for this input stream * @return a reader * @throws UnsupportedEncodingException if the encoding specified is not supported * @since 1.6.0 */ public static BufferedReader newReader(InputStream self, String charset) throws UnsupportedEncodingException { return new BufferedReader(new InputStreamReader(self, charset)); } /** * Create a new BufferedReader for this file and then * passes it into the closure, ensuring the reader is closed after the * closure returns. * * @param file a file object * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withReader(File file, Closure<T> closure) throws IOException { return withReader(newReader(file), closure); } /** * Create a new BufferedReader for this file using the specified charset and then * passes it into the closure, ensuring the reader is closed after the * closure returns. * * @param file a file object * @param charset the charset for this input stream * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.6.0 */ public static <T> T withReader(File file, String charset, Closure<T> closure) throws IOException { return withReader(newReader(file, charset), closure); } /** * Create a buffered output stream for this file. * * @param file a file object * @return the created OutputStream * @throws IOException if an IOException occurs. * @since 1.0 */ public static BufferedOutputStream newOutputStream(File file) throws IOException { return new BufferedOutputStream(new FileOutputStream(file)); } /** * Creates a new data output stream for this file. * * @param file a file object * @return the created DataOutputStream * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static DataOutputStream newDataOutputStream(File file) throws IOException { return new DataOutputStream(new FileOutputStream(file)); } /** * Creates a new OutputStream for this file and passes it into the closure. * This method ensures the stream is closed after the closure returns. * * @param file a File * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.OutputStream, groovy.lang.Closure) * @since 1.5.2 */ public static Object withOutputStream(File file, Closure closure) throws IOException { return withStream(newOutputStream(file), closure); } /** * Create a new InputStream for this file and passes it into the closure. * This method ensures the stream is closed after the closure returns. * * @param file a File * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.InputStream, groovy.lang.Closure) * @since 1.5.2 */ public static Object withInputStream(File file, Closure closure) throws IOException { return withStream(newInputStream(file), closure); } /** * Creates a new InputStream for this URL and passes it into the closure. * This method ensures the stream is closed after the closure returns. * * @param url a URL * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.InputStream, groovy.lang.Closure) * @since 1.5.2 */ public static <T> T withInputStream(URL url, Closure<T> closure) throws IOException { return withStream(newInputStream(url), closure); } /** * Create a new DataOutputStream for this file and passes it into the closure. * This method ensures the stream is closed after the closure returns. * * @param file a File * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.OutputStream, groovy.lang.Closure) * @since 1.5.2 */ public static <T> T withDataOutputStream(File file, Closure<T> closure) throws IOException { return withStream(newDataOutputStream(file), closure); } /** * Create a new DataInputStream for this file and passes it into the closure. * This method ensures the stream is closed after the closure returns. * * @param file a File * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withStream(java.io.InputStream, groovy.lang.Closure) * @since 1.5.2 */ public static <T> T withDataInputStream(File file, Closure<T> closure) throws IOException { return withStream(newDataInputStream(file), closure); } /** * Create a buffered writer for this file. * * @param file a File * @return a BufferedWriter * @throws IOException if an IOException occurs. * @since 1.0 */ public static BufferedWriter newWriter(File file) throws IOException { return new BufferedWriter(new FileWriter(file)); } /** * Creates a buffered writer for this file, optionally appending to the * existing file content. * * @param file a File * @param append true if data should be appended to the file * @return a BufferedWriter * @throws IOException if an IOException occurs. * @since 1.0 */ public static BufferedWriter newWriter(File file, boolean append) throws IOException { return new BufferedWriter(new FileWriter(file, append)); } /** * Helper method to create a buffered writer for a file. If the given * charset is "UTF-16BE" or "UTF-16LE", the requisite byte order mark is * written to the stream before the writer is returned. * * @param file a File * @param charset the name of the encoding used to write in this file * @param append true if in append mode * @return a BufferedWriter * @throws IOException if an IOException occurs. * @since 1.0 */ public static BufferedWriter newWriter(File file, String charset, boolean append) throws IOException { if (append) { return new EncodingAwareBufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charset)); } else { // first write the Byte Order Mark for Unicode encodings FileOutputStream stream = new FileOutputStream(file); if ("UTF-16BE".equals(charset)) { writeUtf16Bom(stream, true); } else if ("UTF-16LE".equals(charset)) { writeUtf16Bom(stream, false); } return new EncodingAwareBufferedWriter(new OutputStreamWriter(stream, charset)); } } /** * Creates a buffered writer for this file, writing data using the given * encoding. * * @param file a File * @param charset the name of the encoding used to write in this file * @return a BufferedWriter * @throws IOException if an IOException occurs. * @since 1.0 */ public static BufferedWriter newWriter(File file, String charset) throws IOException { return newWriter(file, charset, false); } /** * Write a Byte Order Mark at the beginning of the file * * @param stream the FileOutputStream to write the BOM to * @param bigEndian true if UTF 16 Big Endian or false if Low Endian * @throws IOException if an IOException occurs. * @since 1.0 */ private static void writeUtf16Bom(FileOutputStream stream, boolean bigEndian) throws IOException { if (bigEndian) { stream.write(-2); stream.write(-1); } else { stream.write(-1); stream.write(-2); } } /** * Creates a new BufferedWriter for this file, passes it to the closure, and * ensures the stream is flushed and closed after the closure returns. * * @param file a File * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withWriter(File file, Closure<T> closure) throws IOException { return withWriter(newWriter(file), closure); } /** * Creates a new BufferedWriter for this file, passes it to the closure, and * ensures the stream is flushed and closed after the closure returns. * The writer will use the given charset encoding. * * @param file a File * @param charset the charset used * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withWriter(File file, String charset, Closure<T> closure) throws IOException { return withWriter(newWriter(file, charset), closure); } /** * Create a new BufferedWriter which will append to this * file. The writer is passed to the closure and will be closed before * this method returns. * * @param file a File * @param charset the charset used * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withWriterAppend(File file, String charset, Closure<T> closure) throws IOException { return withWriter(newWriter(file, charset, true), closure); } /** * Create a new BufferedWriter for this file in append mode. The writer * is passed to the closure and is closed after the closure returns. * * @param file a File * @param closure a closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withWriterAppend(File file, Closure<T> closure) throws IOException { return withWriter(newWriter(file, true), closure); } /** * Create a new PrintWriter for this file. * * @param file a File * @return the created PrintWriter * @throws IOException if an IOException occurs. * @since 1.0 */ public static PrintWriter newPrintWriter(File file) throws IOException { return new GroovyPrintWriter(newWriter(file)); } /** * Create a new PrintWriter for this file, using specified * charset. * * @param file a File * @param charset the charset * @return a PrintWriter * @throws IOException if an IOException occurs. * @since 1.0 */ public static PrintWriter newPrintWriter(File file, String charset) throws IOException { return new GroovyPrintWriter(newWriter(file, charset)); } /** * Create a new PrintWriter for this file, using specified * charset. * * @param writer a writer * @return a PrintWriter * @since 1.6.0 */ public static PrintWriter newPrintWriter(Writer writer) { return new GroovyPrintWriter(writer); } /** * Create a new PrintWriter for this file which is then * passed it into the given closure. This method ensures its the writer * is closed after the closure returns. * * @param file a File * @param closure the closure to invoke with the PrintWriter * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withPrintWriter(File file, Closure<T> closure) throws IOException { return withWriter(newPrintWriter(file), closure); } /** * Create a new PrintWriter with a specified charset for * this file. The writer is passed to the closure, and will be closed * before this method returns. * * @param file a File * @param charset the charset * @param closure the closure to invoke with the PrintWriter * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withPrintWriter(File file, String charset, Closure<T> closure) throws IOException { return withWriter(newPrintWriter(file, charset), closure); } /** * Create a new PrintWriter with a specified charset for * this file. The writer is passed to the closure, and will be closed * before this method returns. * * @param writer a writer * @param closure the closure to invoke with the PrintWriter * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.6.0 */ public static <T> T withPrintWriter(Writer writer, Closure<T> closure) throws IOException { return withWriter(newPrintWriter(writer), closure); } /** * Allows this writer to be used within the closure, ensuring that it * is flushed and closed before this method returns. * * @param writer the writer which is used and then closed * @param closure the closure that the writer is passed into * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withWriter(Writer writer, Closure<T> closure) throws IOException { try { T result = closure.call(writer); try { writer.flush(); } catch (IOException e) { // try to continue even in case of error } Writer temp = writer; writer = null; temp.close(); return result; } finally { closeWithWarning(writer); } } /** * Allows this reader to be used within the closure, ensuring that it * is closed before this method returns. * * @param reader the reader which is used and then closed * @param closure the closure that the writer is passed into * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withReader(Reader reader, Closure<T> closure) throws IOException { try { T result = closure.call(reader); Reader temp = reader; reader = null; temp.close(); return result; } finally { closeWithWarning(reader); } } /** * Allows this input stream to be used within the closure, ensuring that it * is flushed and closed before this method returns. * * @param stream the stream which is used and then closed * @param closure the closure that the stream is passed into * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withStream(InputStream stream, Closure<T> closure) throws IOException { try { T result = closure.call(stream); InputStream temp = stream; stream = null; temp.close(); return result; } finally { closeWithWarning(stream); } } /** * Helper method to create a new BufferedReader for a URL and then * passes it to the closure. The reader is closed after the closure returns. * * @param url a URL * @param closure the closure to invoke with the reader * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withReader(URL url, Closure<T> closure) throws IOException { return withReader(url.openConnection().getInputStream(), closure); } /** * Helper method to create a new Reader for a URL and then * passes it to the closure. The reader is closed after the closure returns. * * @param url a URL * @param charset the charset used * @param closure the closure to invoke with the reader * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.6 */ public static <T> T withReader(URL url, String charset, Closure<T> closure) throws IOException { return withReader(url.openConnection().getInputStream(), charset, closure); } /** * Helper method to create a new Reader for a stream and then * passes it into the closure. The reader (and this stream) is closed after * the closure returns. * * @see java.io.InputStreamReader * @param in a stream * @param closure the closure to invoke with the InputStream * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withReader(InputStream in, Closure<T> closure) throws IOException { return withReader(new InputStreamReader(in), closure); } /** * Helper method to create a new Reader for a stream and then * passes it into the closure. The reader (and this stream) is closed after * the closure returns. * * @see java.io.InputStreamReader * @param in a stream * @param charset the charset used to decode the stream * @param closure the closure to invoke with the reader * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.6 */ public static <T> T withReader(InputStream in, String charset, Closure<T> closure) throws IOException { return withReader(new InputStreamReader(in, charset), closure); } /** * Creates a writer from this stream, passing it to the given closure. * This method ensures the stream is closed after the closure returns. * * @param stream the stream which is used and then closed * @param closure the closure that the writer is passed into * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withWriter(java.io.Writer, groovy.lang.Closure) * @since 1.5.2 */ public static <T> T withWriter(OutputStream stream, Closure<T> closure) throws IOException { return withWriter(new OutputStreamWriter(stream), closure); } /** * Creates a writer from this stream, passing it to the given closure. * This method ensures the stream is closed after the closure returns. * * @param stream the stream which is used and then closed * @param charset the charset used * @param closure the closure that the writer is passed into * @return the value returned by the closure * @throws IOException if an IOException occurs. * @see #withWriter(java.io.Writer, groovy.lang.Closure) * @since 1.5.2 */ public static <T> T withWriter(OutputStream stream, String charset, Closure<T> closure) throws IOException { return withWriter(new OutputStreamWriter(stream, charset), closure); } /** * Passes this OutputStream to the closure, ensuring that the stream * is closed after the closure returns, regardless of errors. * * @param os the stream which is used and then closed * @param closure the closure that the stream is passed into * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withStream(OutputStream os, Closure<T> closure) throws IOException { try { T result = closure.call(os); os.flush(); OutputStream temp = os; os = null; temp.close(); return result; } finally { closeWithWarning(os); } } /** * Creates a buffered input stream for this file. * * @param file a File * @return a BufferedInputStream of the file * @throws FileNotFoundException if the file is not found. * @since 1.0 */ public static BufferedInputStream newInputStream(File file) throws FileNotFoundException { return new BufferedInputStream(new FileInputStream(file)); } /** * Creates an inputstream for this URL, with the possibility to set different connection parameters using the * <i>parameters map</i>: * <ul> * <li>connectTimeout : the connection timeout</li> * <li>readTimeout : the read timeout</li> * <li>useCaches : set the use cache property for the URL connection</li> * <li>allowUserInteraction : set the user interaction flag for the URL connection</li> * <li>requestProperties : a map of properties to be passed to the URL connection</li> * </ul> * @param parameters an optional map specifying part or all of supported connection parameters * @param url the url for which to create the inputstream * @return an InputStream from the underlying URLConnection * @throws IOException if an I/O error occurs while creating the input stream * @since 1.8.1 */ private static InputStream configuredInputStream(Map parameters, URL url) throws IOException { final URLConnection connection = url.openConnection(); if (parameters!=null) { if (parameters.containsKey("connectTimeout")) { connection.setConnectTimeout(asType(parameters.get("connectTimeout"), Integer.class)); } if (parameters.containsKey("readTimeout")) { connection.setReadTimeout(asType(parameters.get("readTimeout"), Integer.class)); } if (parameters.containsKey("useCaches")) { connection.setUseCaches(asType(parameters.get("useCaches"), Boolean.class)); } if (parameters.containsKey("allowUserInteraction")) { connection.setAllowUserInteraction(asType(parameters.get("allowUserInteraction"), Boolean.class)); } if (parameters.containsKey("requestProperties")) { @SuppressWarnings("unchecked") Map<String,String> properties = (Map<String, String>) parameters.get("requestProperties"); for (Map.Entry<String, String> entry : properties.entrySet()) { connection.setRequestProperty(entry.getKey(), entry.getValue()); } } } return connection.getInputStream(); } /** * Creates a buffered input stream for this URL. * * @param url a URL * @return a BufferedInputStream for the URL * @throws MalformedURLException is thrown if the URL is not well formed * @throws IOException if an I/O error occurs while creating the input stream * @since 1.5.2 */ public static BufferedInputStream newInputStream(URL url) throws MalformedURLException, IOException { return new BufferedInputStream(configuredInputStream(null, url)); } /** * Creates a buffered input stream for this URL. * * @param url a URL * @param parameters connection parameters * @return a BufferedInputStream for the URL * @throws MalformedURLException is thrown if the URL is not well formed * @throws IOException if an I/O error occurs while creating the input stream * @since 1.8.1 */ public static BufferedInputStream newInputStream(URL url, Map parameters) throws MalformedURLException, IOException { return new BufferedInputStream(configuredInputStream(parameters, url)); } /** * Creates a buffered reader for this URL. * * @param url a URL * @return a BufferedReader for the URL * @throws MalformedURLException is thrown if the URL is not well formed * @throws IOException if an I/O error occurs while creating the input stream * @since 1.5.5 */ public static BufferedReader newReader(URL url) throws MalformedURLException, IOException { return newReader(configuredInputStream(null, url)); } /** * Creates a buffered reader for this URL. * * @param url a URL * @param parameters connection parameters * @return a BufferedReader for the URL * @throws MalformedURLException is thrown if the URL is not well formed * @throws IOException if an I/O error occurs while creating the input stream * @since 1.8.1 */ public static BufferedReader newReader(URL url, Map parameters) throws MalformedURLException, IOException { return newReader(configuredInputStream(parameters, url)); } /** * Creates a buffered reader for this URL using the given encoding. * * @param url a URL * @param charset opens the stream with a specified charset * @return a BufferedReader for the URL * @throws MalformedURLException is thrown if the URL is not well formed * @throws IOException if an I/O error occurs while creating the input stream * @since 1.5.5 */ public static BufferedReader newReader(URL url, String charset) throws MalformedURLException, IOException { return new BufferedReader(new InputStreamReader(configuredInputStream(null, url), charset)); } /** * Creates a buffered reader for this URL using the given encoding. * * @param url a URL * @param parameters connection parameters * @param charset opens the stream with a specified charset * @return a BufferedReader for the URL * @throws MalformedURLException is thrown if the URL is not well formed * @throws IOException if an I/O error occurs while creating the input stream * @since 1.8.1 */ public static BufferedReader newReader(URL url, Map parameters, String charset) throws MalformedURLException, IOException { return new BufferedReader(new InputStreamReader(configuredInputStream(parameters, url), charset)); } /** * Create a data input stream for this file * * @param file a File * @return a DataInputStream of the file * @throws FileNotFoundException if the file is not found. * @since 1.5.0 */ public static DataInputStream newDataInputStream(File file) throws FileNotFoundException { return new DataInputStream(new FileInputStream(file)); } /** * Traverse through each byte of this File * * @param self a File * @param closure a closure * @throws IOException if an IOException occurs. * @see #eachByte(java.io.InputStream, groovy.lang.Closure) * @since 1.0 */ public static void eachByte(File self, Closure closure) throws IOException { BufferedInputStream is = newInputStream(self); eachByte(is, closure); } /** * Traverse through the bytes of this File, bufferLen bytes at a time. * * @param self a File * @param bufferLen the length of the buffer to use. * @param closure a 2 parameter closure which is passed the byte[] and a number of bytes successfully read. * @throws IOException if an IOException occurs. * @see #eachByte(java.io.InputStream, int, groovy.lang.Closure) * @since 1.7.4 */ public static void eachByte(File self, int bufferLen, Closure closure) throws IOException { BufferedInputStream is = newInputStream(self); eachByte(is, bufferLen, closure); } /** * Traverse through each byte of this Byte array. Alias for each. * * @param self a Byte array * @param closure a closure * @see #each(java.lang.Object, groovy.lang.Closure) * @since 1.5.5 */ public static void eachByte(Byte[] self, Closure closure) { each(self, closure); } /** * Traverse through each byte of this byte array. Alias for each. * * @param self a byte array * @param closure a closure * @see #each(java.lang.Object, groovy.lang.Closure) * @since 1.5.5 */ public static void eachByte(byte[] self, Closure closure) { each(self, closure); } /** * Traverse through each byte of the specified stream. The * stream is closed after the closure returns. * * @param is stream to iterate over, closed after the method call * @param closure closure to apply to each byte * @throws IOException if an IOException occurs. * @since 1.0 */ public static void eachByte(InputStream is, Closure closure) throws IOException { try { while (true) { int b = is.read(); if (b == -1) { break; } else { closure.call((byte) b); } } InputStream temp = is; is = null; temp.close(); } finally { closeWithWarning(is); } } /** * Traverse through each the specified stream reading bytes into a buffer * and calling the 2 parameter closure with this buffer and the number of bytes. * * @param is stream to iterate over, closed after the method call. * @param bufferLen the length of the buffer to use. * @param closure a 2 parameter closure which is passed the byte[] and a number of bytes successfully read. * @throws IOException if an IOException occurs. * @since 1.8 */ public static void eachByte(InputStream is, int bufferLen, Closure closure) throws IOException { byte[] buffer = new byte[ bufferLen ] ; int bytesRead = 0 ; try { while ( ( bytesRead = is.read( buffer, 0, bufferLen ) ) > 0 ) { closure.call( new Object[]{ buffer, bytesRead } ) ; } InputStream temp = is; is = null; temp.close(); } finally { closeWithWarning(is); } } /** * Reads the InputStream from this URL, passing each byte to the given * closure. The URL stream will be closed before this method returns. * * @param url url to iterate over * @param closure closure to apply to each byte * @throws IOException if an IOException occurs. * @see #eachByte(java.io.InputStream, groovy.lang.Closure) * @since 1.0 */ public static void eachByte(URL url, Closure closure) throws IOException { InputStream is = url.openConnection().getInputStream(); eachByte(is, closure); } /** * Reads the InputStream from this URL, passing a byte[] and a number of bytes * to the given closure. The URL stream will be closed before this method returns. * * @param url url to iterate over * @param bufferLen the length of the buffer to use. * @param closure a 2 parameter closure which is passed the byte[] and a number of bytes successfully read. * @throws IOException if an IOException occurs. * @see #eachByte(java.io.InputStream, int, groovy.lang.Closure) * @since 1.8 */ public static void eachByte(URL url, int bufferLen, Closure closure) throws IOException { InputStream is = url.openConnection().getInputStream(); eachByte(is, bufferLen, closure); } /** * Transforms each character from this reader by passing it to the given * closure. The Closure should return each transformed character, which * will be passed to the Writer. The reader and writer will be both be * closed before this method returns. * * @param self a Reader object * @param writer a Writer to receive the transformed characters * @param closure a closure that performs the required transformation * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static void transformChar(Reader self, Writer writer, Closure closure) throws IOException { int c; try { char[] chars = new char[1]; while ((c = self.read()) != -1) { chars[0] = (char) c; writer.write((String) closure.call(new String(chars))); } writer.flush(); Writer temp2 = writer; writer = null; temp2.close(); Reader temp1 = self; self = null; temp1.close(); } finally { closeWithWarning(self); closeWithWarning(writer); } } /** * Transforms the lines from a reader with a Closure and * write them to a writer. Both Reader and Writer are * closed after the operation. * * @param reader Lines of text to be transformed. Reader is closed afterwards. * @param writer Where transformed lines are written. Writer is closed afterwards. * @param closure Single parameter closure that is called to transform each line of * text from the reader, before writing it to the writer. * @throws IOException if an IOException occurs. * @since 1.0 */ public static void transformLine(Reader reader, Writer writer, Closure closure) throws IOException { BufferedReader br = new BufferedReader(reader); BufferedWriter bw = new BufferedWriter(writer); String line; try { while ((line = br.readLine()) != null) { Object o = closure.call(line); if (o != null) { bw.write(o.toString()); bw.newLine(); } } bw.flush(); Writer temp2 = writer; writer = null; temp2.close(); Reader temp1 = reader; reader = null; temp1.close(); } finally { closeWithWarning(br); closeWithWarning(reader); closeWithWarning(bw); closeWithWarning(writer); } } /** * Filter the lines from a reader and write them on the writer, * according to a closure which returns true if the line should be included. * Both Reader and Writer are closed after the operation. * * @param reader a reader, closed after the call * @param writer a writer, closed after the call * @param closure the closure which returns booleans * @throws IOException if an IOException occurs. * @since 1.0 */ public static void filterLine(Reader reader, Writer writer, Closure closure) throws IOException { BufferedReader br = new BufferedReader(reader); BufferedWriter bw = new BufferedWriter(writer); String line; try { while ((line = br.readLine()) != null) { if (DefaultTypeTransformation.castToBoolean(closure.call(line))) { bw.write(line); bw.newLine(); } } bw.flush(); Writer temp2 = writer; writer = null; temp2.close(); Reader temp1 = reader; reader = null; temp1.close(); } finally { closeWithWarning(br); closeWithWarning(reader); closeWithWarning(bw); closeWithWarning(writer); } } /** * Filters the lines of a File and creates a Writable in return to * stream the filtered lines. * * @param self a File * @param closure a closure which returns a boolean indicating to filter * the line or not * @return a Writable closure * @throws IOException if <code>self</code> is not readable * @see #filterLine(java.io.Reader, groovy.lang.Closure) * @since 1.0 */ public static Writable filterLine(File self, Closure closure) throws IOException { return filterLine(newReader(self), closure); } /** * Filters the lines of a File and creates a Writable in return to * stream the filtered lines. * * @param self a File * @param charset opens the file with a specified charset * @param closure a closure which returns a boolean indicating to filter * the line or not * @return a Writable closure * @throws IOException if an IOException occurs * @see #filterLine(java.io.Reader, groovy.lang.Closure) * @since 1.6.8 */ public static Writable filterLine(File self, String charset, Closure closure) throws IOException { return filterLine(newReader(self, charset), closure); } /** * Filter the lines from this File, and write them to the given writer based * on the given closure predicate. * * @param self a File * @param writer a writer destination to write filtered lines to * @param closure a closure which takes each line as a parameter and returns * <code>true</code> if the line should be written to this writer. * @throws IOException if <code>self</code> is not readable * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) * @since 1.0 */ public static void filterLine(File self, Writer writer, Closure closure) throws IOException { filterLine(newReader(self), writer, closure); } /** * Filter the lines from this File, and write them to the given writer based * on the given closure predicate. * * @param self a File * @param writer a writer destination to write filtered lines to * @param charset opens the file with a specified charset * @param closure a closure which takes each line as a parameter and returns * <code>true</code> if the line should be written to this writer. * @throws IOException if an IO error occurs * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) * @since 1.6.8 */ public static void filterLine(File self, Writer writer, String charset, Closure closure) throws IOException { filterLine(newReader(self, charset), writer, closure); } /** * Filter the lines from this Reader, and return a Writable which can be * used to stream the filtered lines to a destination. The closure should * return <code>true</code> if the line should be passed to the writer. * * @param reader this reader * @param closure a closure used for filtering * @return a Writable which will use the closure to filter each line * from the reader when the Writable#writeTo(Writer) is called. * @since 1.0 */ public static Writable filterLine(Reader reader, final Closure closure) { final BufferedReader br = new BufferedReader(reader); return new Writable() { public Writer writeTo(Writer out) throws IOException { BufferedWriter bw = new BufferedWriter(out); String line; while ((line = br.readLine()) != null) { if (DefaultTypeTransformation.castToBoolean(closure.call(line))) { bw.write(line); bw.newLine(); } } bw.flush(); return out; } public String toString() { StringWriter buffer = new StringWriter(); try { writeTo(buffer); } catch (IOException e) { throw new StringWriterIOException(e); } return buffer.toString(); } }; } /** * Filter lines from an input stream using a closure predicate. The closure * will be passed each line as a String, and it should return * <code>true</code> if the line should be passed to the writer. * * @param self an input stream * @param predicate a closure which returns boolean and takes a line * @return a writable which writes out the filtered lines * @see #filterLine(java.io.Reader, groovy.lang.Closure) * @since 1.0 */ public static Writable filterLine(InputStream self, Closure predicate) { return filterLine(newReader(self), predicate); } /** * Filter lines from an input stream using a closure predicate. The closure * will be passed each line as a String, and it should return * <code>true</code> if the line should be passed to the writer. * * @param self an input stream * @param charset opens the stream with a specified charset * @param predicate a closure which returns boolean and takes a line * @return a writable which writes out the filtered lines * @throws UnsupportedEncodingException if the encoding specified is not supported * @see #filterLine(java.io.Reader, groovy.lang.Closure) * @since 1.6.8 */ public static Writable filterLine(InputStream self, String charset, Closure predicate) throws UnsupportedEncodingException { return filterLine(newReader(self, charset), predicate); } /** * Uses a closure to filter lines from this InputStream and pass them to * the given writer. The closure will be passed each line as a String, and * it should return <code>true</code> if the line should be passed to the * writer. * * @param self the InputStream * @param writer a writer to write output to * @param predicate a closure which returns true if a line should be accepted * @throws IOException if an IOException occurs. * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) * @since 1.0 */ public static void filterLine(InputStream self, Writer writer, Closure predicate) throws IOException { filterLine(newReader(self), writer, predicate); } /** * Uses a closure to filter lines from this InputStream and pass them to * the given writer. The closure will be passed each line as a String, and * it should return <code>true</code> if the line should be passed to the * writer. * * @param self the InputStream * @param writer a writer to write output to * @param charset opens the stream with a specified charset * @param predicate a closure which returns true if a line should be accepted * @throws IOException if an IOException occurs. * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) * @since 1.6.8 */ public static void filterLine(InputStream self, Writer writer, String charset, Closure predicate) throws IOException { filterLine(newReader(self, charset), writer, predicate); } /** * Filter lines from a URL using a closure predicate. The closure * will be passed each line as a String, and it should return * <code>true</code> if the line should be passed to the writer. * * @param self a URL * @param predicate a closure which returns boolean and takes a line * @return a writable which writes out the filtered lines * @throws IOException if an IO exception occurs * @see #filterLine(java.io.Reader, groovy.lang.Closure) * @since 1.6.8 */ public static Writable filterLine(URL self, Closure predicate) throws IOException { return filterLine(newReader(self), predicate); } /** * Filter lines from a URL using a closure predicate. The closure * will be passed each line as a String, and it should return * <code>true</code> if the line should be passed to the writer. * * @param self the URL * @param charset opens the URL with a specified charset * @param predicate a closure which returns boolean and takes a line * @return a writable which writes out the filtered lines * @throws IOException if an IO exception occurs * @see #filterLine(java.io.Reader, groovy.lang.Closure) * @since 1.6.8 */ public static Writable filterLine(URL self, String charset, Closure predicate) throws IOException { return filterLine(newReader(self, charset), predicate); } /** * Uses a closure to filter lines from this URL and pass them to * the given writer. The closure will be passed each line as a String, and * it should return <code>true</code> if the line should be passed to the * writer. * * @param self the URL * @param writer a writer to write output to * @param predicate a closure which returns true if a line should be accepted * @throws IOException if an IOException occurs. * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) * @since 1.6.8 */ public static void filterLine(URL self, Writer writer, Closure predicate) throws IOException { filterLine(newReader(self), writer, predicate); } /** * Uses a closure to filter lines from this URL and pass them to * the given writer. The closure will be passed each line as a String, and * it should return <code>true</code> if the line should be passed to the * writer. * * @param self the URL * @param writer a writer to write output to * @param charset opens the URL with a specified charset * @param predicate a closure which returns true if a line should be accepted * @throws IOException if an IOException occurs. * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) * @since 1.6.8 */ public static void filterLine(URL self, Writer writer, String charset, Closure predicate) throws IOException { filterLine(newReader(self, charset), writer, predicate); } /** * Reads the content of the file into a byte array. * * @param file a File * @return a byte array with the contents of the file. * @throws IOException if an IOException occurs. * @since 1.0 */ public static byte[] readBytes(File file) throws IOException { byte[] bytes = new byte[(int) file.length()]; FileInputStream fileInputStream = new FileInputStream(file); DataInputStream dis = new DataInputStream(fileInputStream); try { dis.readFully(bytes); InputStream temp = dis; dis = null; temp.close(); } finally { closeWithWarning(dis); } return bytes; } // ================================ // Socket and ServerSocket methods /** * Passes the Socket's InputStream and OutputStream to the closure. The * streams will be closed after the closure returns, even if an exception * is thrown. * * @param socket a Socket * @param closure a Closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.2 */ public static <T> T withStreams(Socket socket, Closure<T> closure) throws IOException { InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream(); try { T result = closure.call(new Object[]{input, output}); InputStream temp1 = input; input = null; temp1.close(); OutputStream temp2 = output; output = null; temp2.close(); return result; } finally { closeWithWarning(input); closeWithWarning(output); } } /** * Creates an InputObjectStream and an OutputObjectStream from a Socket, and * passes them to the closure. The streams will be closed after the closure * returns, even if an exception is thrown. * * @param socket this Socket * @param closure a Closure * @return the value returned by the closure * @throws IOException if an IOException occurs. * @since 1.5.0 */ public static <T> T withObjectStreams(Socket socket, Closure<T> closure) throws IOException { InputStream input = socket.getInputStream(); OutputStream output = socket.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(output); ObjectInputStream ois = new ObjectInputStream(input); try { T result = closure.call(new Object[]{ois, oos}); InputStream temp1 = ois; ois = null; temp1.close(); temp1 = input; input = null; temp1.close(); OutputStream temp2 = oos; oos = null; temp2.close(); temp2 = output; output = null; temp2.close(); return result; } finally { closeWithWarning(ois); closeWithWarning(input); closeWithWarning(oos); closeWithWarning(output); } } /** * Overloads the left shift operator to provide an append mechanism to * add things to the output stream of a socket * * @param self a Socket * @param value a value to append * @return a Writer * @throws IOException if an IOException occurs. * @since 1.0 */ public static Writer leftShift(Socket self, Object value) throws IOException { return leftShift(self.getOutputStream(), value); } /** * Overloads the left shift operator to provide an append mechanism * to add bytes to the output stream of a socket * * @param self a Socket * @param value a value to append * @return an OutputStream * @throws IOException if an IOException occurs. * @since 1.0 */ public static OutputStream leftShift(Socket self, byte[] value) throws IOException { return leftShift(self.getOutputStream(), value); } /** * Accepts a connection and passes the resulting Socket to the closure * which runs in a new Thread. * * @param serverSocket a ServerSocket * @param closure a Closure * @return a Socket * @throws IOException if an IOException occurs. * @see java.net.ServerSocket#accept() * @since 1.0 */ public static Socket accept(ServerSocket serverSocket, final Closure closure) throws IOException { return accept(serverSocket, true, closure); } /** * Accepts a connection and passes the resulting Socket to the closure * which runs in a new Thread or the calling thread, as needed. * * @param serverSocket a ServerSocket * @param runInANewThread This flag should be true, if the closure should be invoked in a new thread, else false. * @param closure a Closure * @return a Socket * @throws IOException if an IOException occurs. * @see java.net.ServerSocket#accept() * @since 1.7.6 */ public static Socket accept(ServerSocket serverSocket, final boolean runInANewThread, final Closure closure) throws IOException { final Socket socket = serverSocket.accept(); if(runInANewThread) { new Thread(new Runnable() { public void run() { invokeClosureWithSocket(socket, closure); } }).start(); } else { invokeClosureWithSocket(socket, closure); } return socket; } private static void invokeClosureWithSocket(Socket socket, Closure closure) { try { closure.call(socket); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { LOG.warning("Caught exception closing socket: " + e); } } } } /** * Converts this File to a {@link groovy.lang.Writable}. * * @param file a File * @return a File which wraps the input file and which implements Writable * @since 1.0 */ public static File asWritable(File file) { return new WritableFile(file); } /** * Converts this File to a {@link groovy.lang.Writable} or delegates to default * {@link #asType(java.lang.Object, java.lang.Class)}. * * @param f a File * @param c the desired class * @return the converted object * @since 1.0 */ @SuppressWarnings("unchecked") public static <T> T asType(File f, Class<T> c) { if (c == Writable.class) { return (T) asWritable(f); } return asType((Object) f, c); } /** * Allows a file to return a Writable implementation that can output itself * to a Writer stream. * * @param file a File * @param encoding the encoding to be used when reading the file's contents * @return File which wraps the input file and which implements Writable * @since 1.0 */ public static File asWritable(File file, String encoding) { return new WritableFile(file, encoding); } /** * Converts the given String into a List of strings of one character. * * @param self a String * @return a List of characters (a 1-character String) * @since 1.0 */ public static List<String> toList(String self) { int size = self.length(); List<String> answer = new ArrayList<String>(size); for (int i = 0; i < size; i++) { answer.add(self.substring(i, i + 1)); } return answer; } /** * Converts the given CharSequence into a List of CharSequence of one character. * * @param self a CharSequence * @return a List of characters (a 1-character CharSequence) * @see #toSet(String) * @since 1.8.2 */ public static List<CharSequence> toList(CharSequence self) { return new ArrayList<CharSequence>(toList(self.toString())); } /** * Converts the given String into a Set of unique strings of one character. * <p> * Example usage: * <pre class="groovyTestCase"> * assert 'groovy'.toSet() == ['v', 'g', 'r', 'o', 'y'] as Set * assert "abc".toSet().iterator()[0] instanceof String * </pre> * * @param self a String * @return a Set of unique character Strings (each a 1-character String) * @since 1.8.0 */ public static Set<String> toSet(String self) { return new HashSet<String>(toList(self)); } /** * Converts the given CharSequence into a Set of unique CharSequence of one character. * * @param self a CharSequence * @return a Set of unique character CharSequence (each a 1-character CharSequence) * @see #toSet(String) * @since 1.8.2 */ public static Set<CharSequence> toSet(CharSequence self) { return new HashSet<CharSequence>(toList(self)); } /** * Converts the given String into an array of characters. * Alias for toCharArray. * * @param self a String * @return an array of characters * @see java.lang.String#toCharArray() * @since 1.6.0 */ public static char[] getChars(String self) { return self.toCharArray(); } /** * Converts the given CharSequence into an array of characters. * * @param self a CharSequence * @return an array of characters * @see #getChars(String) * @since 1.8.2 */ public static char[] getChars(CharSequence self) { return getChars(self.toString()); } /** * Converts the GString to a File, or delegates to the default * {@link #asType(java.lang.Object, java.lang.Class)} * * @param self a GString * @param c the desired class * @return the converted object * @since 1.5.0 */ @SuppressWarnings("unchecked") public static <T> T asType(GString self, Class<T> c) { if (c == File.class) { return (T) new File(self.toString()); } else if (Number.class.isAssignableFrom(c)) { return asType(self.toString(), c); } return asType((Object) self, c); } /** * <p>Provides a method to perform custom 'dynamic' type conversion * to the given class using the <code>as</code> operator.</p> * <strong>Example:</strong> <code>'123' as Double</code> * <p>By default, the following types are supported: * <ul> * <li>List</li> * <li>BigDecimal</li> * <li>BigInteger</li> * <li>Long</li> * <li>Integer</li> * <li>Short</li> * <li>Byte</li> * <li>Character</li> * <li>Double</li> * <li>Float</li> * <li>File</li> * <li>Subclasses of Enum (Java 5 and above)</li> * </ul> * If any other type is given, the call is delegated to * {@link #asType(java.lang.Object, java.lang.Class)}. * * @param self a String * @param c the desired class * @return the converted object * @since 1.0 */ @SuppressWarnings("unchecked") public static <T> T asType(String self, Class<T> c) { if (c == List.class) { return (T) toList(self); } else if (c == BigDecimal.class) { return (T) toBigDecimal(self); } else if (c == BigInteger.class) { return (T) toBigInteger(self); } else if (c == Long.class || c == Long.TYPE) { return (T) toLong(self); } else if (c == Integer.class || c == Integer.TYPE) { return (T) toInteger(self); } else if (c == Short.class || c == Short.TYPE) { return (T) toShort(self); } else if (c == Byte.class || c == Byte.TYPE) { return (T) Byte.valueOf(self.trim()); } else if (c == Character.class || c == Character.TYPE) { return (T) toCharacter(self); } else if (c == Double.class || c == Double.TYPE) { return (T) toDouble(self); } else if (c == Float.class || c == Float.TYPE) { return (T) toFloat(self); } else if (c == File.class) { return (T) new File(self); } else if (DefaultTypeTransformation.isEnumSubclass(c)) { return (T) InvokerHelper.invokeMethod(c, "valueOf", new Object[]{ self }); } return asType((Object) self, c); } /** * <p>Provides a method to perform custom 'dynamic' type conversion * to the given class using the <code>as</code> operator. * * @param self a CharSequence * @param c the desired class * @return the converted object * @see #asType(String, Class) * @since 1.8.2 */ public static <T> T asType(CharSequence self, Class<T> c) { return asType(self.toString(), c); } /** * Process each regex group matched substring of the given string. If the closure * parameter takes one argument, an array with all match groups is passed to it. * If the closure takes as many arguments as there are match groups, then each * parameter will be one match group. * * @param self the source string * @param regex a Regex string * @param closure a closure with one parameter or as much parameters as groups * @return the source string * @since 1.6.0 */ public static String eachMatch(String self, String regex, Closure closure) { return eachMatch(self, Pattern.compile(regex), closure); } /** * Process each regex group matched substring of the given CharSequence. If the closure * parameter takes one argument, an array with all match groups is passed to it. * If the closure takes as many arguments as there are match groups, then each * parameter will be one match group. * * @param self the source CharSequence * @param regex a Regex CharSequence * @param closure a closure with one parameter or as much parameters as groups * @return the source CharSequence * @see #eachMatch(String, String, groovy.lang.Closure) * @since 1.8.2 */ public static String eachMatch(CharSequence self, CharSequence regex, Closure closure) { return eachMatch(self.toString(), regex.toString(), closure); } /** * Process each regex group matched substring of the given pattern. If the closure * parameter takes one argument, an array with all match groups is passed to it. * If the closure takes as many arguments as there are match groups, then each * parameter will be one match group. * * @param self the source string * @param pattern a regex Pattern * @param closure a closure with one parameter or as much parameters as groups * @return the source string * @since 1.6.1 */ public static String eachMatch(String self, Pattern pattern, Closure closure) { Matcher m = pattern.matcher(self); each(m, closure); return self; } /** * Process each regex group matched substring of the given pattern. If the closure * parameter takes one argument, an array with all match groups is passed to it. * If the closure takes as many arguments as there are match groups, then each * parameter will be one match group. * * @param self the source CharSequence * @param pattern a regex Pattern * @param closure a closure with one parameter or as much parameters as groups * @return the source CharSequence * @see #eachMatch(String, Pattern, groovy.lang.Closure) * @since 1.8.2 */ public static String eachMatch(CharSequence self, Pattern pattern, Closure closure) { return eachMatch(self.toString(), pattern, closure); } /** * Iterates over the elements of an iterable collection of items and returns * the index of the first item that matches the condition specified in the closure. * * @param self the iteration object over which to iterate * @param closure the filter to perform a match on the collection * @return an integer that is the index of the first matched object or -1 if no match was found * @since 1.0 */ public static int findIndexOf(Object self, Closure closure) { return findIndexOf(self, 0, closure); } /** * Iterates over the elements of an iterable collection of items, starting from a * specified startIndex, and returns the index of the first item that matches the * condition specified in the closure. * * @param self the iteration object over which to iterate * @param startIndex start matching from this index * @param closure the filter to perform a match on the collection * @return an integer that is the index of the first matched object or -1 if no match was found * @since 1.5.0 */ public static int findIndexOf(Object self, int startIndex, Closure closure) { int result = -1; int i = 0; for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) { Object value = iter.next(); if (i < startIndex) { continue; } if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { result = i; break; } } return result; } /** * Iterates over the elements of an iterable collection of items and returns * the index of the last item that matches the condition specified in the closure. * * @param self the iteration object over which to iterate * @param closure the filter to perform a match on the collection * @return an integer that is the index of the last matched object or -1 if no match was found * @since 1.5.2 */ public static int findLastIndexOf(Object self, Closure closure) { return findLastIndexOf(self, 0, closure); } /** * Iterates over the elements of an iterable collection of items, starting * from a specified startIndex, and returns the index of the last item that * matches the condition specified in the closure. * * @param self the iteration object over which to iterate * @param startIndex start matching from this index * @param closure the filter to perform a match on the collection * @return an integer that is the index of the last matched object or -1 if no match was found * @since 1.5.2 */ public static int findLastIndexOf(Object self, int startIndex, Closure closure) { int result = -1; int i = 0; for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) { Object value = iter.next(); if (i < startIndex) { continue; } if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { result = i; } } return result; } /** * Iterates over the elements of an iterable collection of items and returns * the index values of the items that match the condition specified in the closure. * * @param self the iteration object over which to iterate * @param closure the filter to perform a match on the collection * @return a list of numbers corresponding to the index values of all matched objects * @since 1.5.2 */ public static List<Number> findIndexValues(Object self, Closure closure) { return findIndexValues(self, 0, closure); } /** * Iterates over the elements of an iterable collection of items, starting from * a specified startIndex, and returns the index values of the items that match * the condition specified in the closure. * * @param self the iteration object over which to iterate * @param startIndex start matching from this index * @param closure the filter to perform a match on the collection * @return a list of numbers corresponding to the index values of all matched objects * @since 1.5.2 */ public static List<Number> findIndexValues(Object self, Number startIndex, Closure closure) { List<Number> result = new ArrayList<Number>(); long count = 0; long startCount = startIndex.longValue(); for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); count++) { Object value = iter.next(); if (count < startCount) { continue; } if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { result.add(count); } } return result; } /** * Iterates through the classloader parents until it finds a loader with a class * named "org.codehaus.groovy.tools.RootLoader". If there is no such class * <code>null</code> will be returned. The name is used for comparison because * a direct comparison using == may fail as the class may be loaded through * different classloaders. * * @param self a ClassLoader * @return the rootLoader for the ClassLoader * @see org.codehaus.groovy.tools.RootLoader * @since 1.5.0 */ public static ClassLoader getRootLoader(ClassLoader self) { while (true) { if (self == null) return null; if (isRootLoaderClassOrSubClass(self)) return self; self = self.getParent(); } } private static boolean isRootLoaderClassOrSubClass(ClassLoader self) { Class current = self.getClass(); while(!current.getName().equals(Object.class.getName())) { if(current.getName().equals(RootLoader.class.getName())) return true; current = current.getSuperclass(); } return false; } /** * Converts a given object to a type. This method is used through * the "as" operator and is overloadable as any other operator. * * @param obj the object to convert * @param type the goal type * @return the resulting object * @since 1.0 */ @SuppressWarnings("unchecked") public static <T> T asType(Object obj, Class<T> type) { if (String.class == type) { return (T) InvokerHelper.toString(obj); } // fall back to cast try { return (T) DefaultTypeTransformation.castToType(obj, type); } catch (GroovyCastException e) { MetaClass mc = InvokerHelper.getMetaClass(obj); if (mc instanceof ExpandoMetaClass) { ExpandoMetaClass emc = (ExpandoMetaClass) mc; Object mixedIn = emc.castToMixedType(obj, type); if (mixedIn != null) return (T) mixedIn; } if (type.isInterface()) { try { List<Class> interfaces = new ArrayList<Class>(); interfaces.add(type); return (T) ProxyGenerator.INSTANCE.instantiateDelegate(interfaces, obj); } catch (GroovyRuntimeException cause) { // ignore } } throw e; } } private static Object asArrayType(Object object, Class type) { if (type.isAssignableFrom(object.getClass())) { return object; } Collection list = DefaultTypeTransformation.asCollection(object); int size = list.size(); Class elementType = type.getComponentType(); Object array = Array.newInstance(elementType, size); int idx = 0; if (boolean.class.equals(elementType)) { for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { Object element = iter.next(); Array.setBoolean(array, idx, (Boolean) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, boolean.class})); } } else if (byte.class.equals(elementType)) { for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { Object element = iter.next(); Array.setByte(array, idx, (Byte) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, byte.class})); } } else if (char.class.equals(elementType)) { for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { Object element = iter.next(); Array.setChar(array, idx, (Character) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, char.class})); } } else if (double.class.equals(elementType)) { for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { Object element = iter.next(); Array.setDouble(array, idx, (Double) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, double.class})); } } else if (float.class.equals(elementType)) { for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { Object element = iter.next(); Array.setFloat(array, idx, (Float) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, float.class})); } } else if (int.class.equals(elementType)) { for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { Object element = iter.next(); Array.setInt(array, idx, (Integer) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, int.class})); } } else if (long.class.equals(elementType)) { for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { Object element = iter.next(); Array.setLong(array, idx, (Long) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, long.class})); } } else if (short.class.equals(elementType)) { for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { Object element = iter.next(); Array.setShort(array, idx, (Short) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, short.class})); } } else for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { Object element = iter.next(); Array.set(array, idx, InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, elementType})); } return array; } /** * Convenience method to dynamically create a new instance of this * class. Calls the default constructor. * * @param c a class * @return a new instance of this class * @since 1.0 */ @SuppressWarnings("unchecked") public static <T> T newInstance(Class<T> c) { return (T) InvokerHelper.invokeConstructorOf(c, null); } /** * Helper to construct a new instance from the given arguments. * The constructor is called based on the number and types in the * args array. Use <code>newInstance(null)</code> or simply * <code>newInstance()</code> for the default (no-arg) constructor. * * @param c a class * @param args the constructor arguments * @return a new instance of this class. * @since 1.0 */ @SuppressWarnings("unchecked") public static <T> T newInstance(Class<T> c, Object[] args) { if (args == null) args = new Object[]{null}; return (T) InvokerHelper.invokeConstructorOf(c, args); } /** * Adds a "metaClass" property to all class objects so you can use the syntax * <code>String.metaClass.myMethod = { println "foo" }</code> * * @param c The java.lang.Class instance * @return An MetaClass instance * @since 1.5.0 */ public static MetaClass getMetaClass(Class c) { MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry(); MetaClass mc = metaClassRegistry.getMetaClass(c); if (mc instanceof ExpandoMetaClass || mc instanceof DelegatingMetaClass && ((DelegatingMetaClass) mc).getAdaptee() instanceof ExpandoMetaClass) return mc; else { return new HandleMetaClass(mc); } } /** * Obtains a MetaClass for an object either from the registry or in the case of * a GroovyObject from the object itself. * * @param obj The object in question * @return The MetaClass * @since 1.5.0 */ public static MetaClass getMetaClass(Object obj) { MetaClass mc = InvokerHelper.getMetaClass(obj); return new HandleMetaClass(mc, obj); } /** * Obtains a MetaClass for an object either from the registry or in the case of * a GroovyObject from the object itself. * * @param obj The object in question * @return The MetaClass * @since 1.6.0 */ public static MetaClass getMetaClass(GroovyObject obj) { // we need this method as trick to guarantee correct method selection return getMetaClass((Object)obj); } /** * Sets the metaclass for a given class. * * @param self the class whose metaclass we wish to set * @param metaClass the new MetaClass * @since 1.6.0 */ public static void setMetaClass(Class self, MetaClass metaClass) { final MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry(); if (metaClass == null) metaClassRegistry.removeMetaClass(self); else { if (metaClass instanceof HandleMetaClass) { metaClassRegistry.setMetaClass(self, ((HandleMetaClass)metaClass).getAdaptee()); } else { metaClassRegistry.setMetaClass(self, metaClass); } if (self==NullObject.class) { NullObject.getNullObject().setMetaClass(metaClass); } } } /** * Set the metaclass for an object * @param self the object whose metaclass we want to set * @param metaClass the new metaclass value * @since 1.6.0 */ public static void setMetaClass(Object self, MetaClass metaClass) { if (metaClass instanceof HandleMetaClass) metaClass = ((HandleMetaClass)metaClass).getAdaptee(); if (self instanceof GroovyObject) { ((GroovyObject)self).setMetaClass(metaClass); disablePrimitiveOptimization(self); } else if (self instanceof Class) { ((MetaClassRegistryImpl)GroovySystem.getMetaClassRegistry()).setMetaClass((Class)self, metaClass); } else { ((MetaClassRegistryImpl)GroovySystem.getMetaClassRegistry()).setMetaClass(self, metaClass); } } private static void disablePrimitiveOptimization(Object self) { Field sdyn; Class c = self.getClass(); try { sdyn = c.getDeclaredField(Verifier.STATIC_METACLASS_BOOL); sdyn.setBoolean(null, true); } catch (Throwable e) { //DO NOTHING } } /** * Sets/updates the metaclass for a given class to a closure. * * @param self the class whose metaclass we wish to update * @param closure the closure representing the new metaclass * @return the new metaclass value * @throws GroovyRuntimeException if the metaclass can't be set for this class * @since 1.6.0 */ public static MetaClass metaClass (Class self, Closure closure){ MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry(); MetaClass mc = metaClassRegistry.getMetaClass(self); if (mc instanceof ExpandoMetaClass) { ((ExpandoMetaClass) mc).define(closure); return mc; } else { if (mc instanceof DelegatingMetaClass && ((DelegatingMetaClass) mc).getAdaptee() instanceof ExpandoMetaClass) { ((ExpandoMetaClass)((DelegatingMetaClass) mc).getAdaptee()).define(closure); return mc; } else { if (mc instanceof DelegatingMetaClass && ((DelegatingMetaClass) mc).getAdaptee().getClass() == MetaClassImpl.class) { ExpandoMetaClass emc = new ExpandoMetaClass(self, false, true); emc.initialize(); emc.define(closure); ((DelegatingMetaClass) mc).setAdaptee(emc); return mc; } else { if (mc.getClass() == MetaClassImpl.class) { // default case mc = new ExpandoMetaClass(self, false, true); mc.initialize(); ((ExpandoMetaClass)mc).define(closure); metaClassRegistry.setMetaClass(self, mc); return mc; } else { throw new GroovyRuntimeException("Can't add methods to custom meta class " + mc); } } } } } /** * Sets/updates the metaclass for a given object to a closure. * * @param self the object whose metaclass we wish to update * @param closure the closure representing the new metaclass * @return the new metaclass value * @throws GroovyRuntimeException if the metaclass can't be set for this object * @since 1.6.0 */ public static MetaClass metaClass (Object self, Closure closure){ MetaClass emc = hasPerInstanceMetaClass(self); if (emc == null) { final ExpandoMetaClass metaClass = new ExpandoMetaClass(self.getClass(), false, true); metaClass.initialize(); metaClass.define(closure); setMetaClass(self, metaClass); return metaClass; } else { if (emc instanceof ExpandoMetaClass) { ((ExpandoMetaClass)emc).define(closure); return emc; } else { if (emc instanceof DelegatingMetaClass && ((DelegatingMetaClass)emc).getAdaptee() instanceof ExpandoMetaClass) { ((ExpandoMetaClass)((DelegatingMetaClass)emc).getAdaptee()).define(closure); return emc; } else { throw new RuntimeException("Can't add methods to non-ExpandoMetaClass " + emc); } } } } private static MetaClass hasPerInstanceMetaClass(Object object) { if (object instanceof GroovyObject) { MetaClass mc = ((GroovyObject)object).getMetaClass(); if (mc == GroovySystem.getMetaClassRegistry().getMetaClass(object.getClass()) || mc.getClass() == MetaClassImpl.class) return null; else return mc; } else { ClassInfo info = ClassInfo.getClassInfo(object.getClass()); info.lock(); try { return info.getPerInstanceMetaClass(object); } finally { info.unlock(); } } } /** * Attempts to create an Iterator for the given object by first * converting it to a Collection. * * @param a an array * @return an Iterator for the given Array. * @see org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation#asCollection(java.lang.Object[]) * @since 1.6.4 */ public static <T> Iterator<T> iterator(T[] a) { return DefaultTypeTransformation.asCollection(a).iterator(); } /** * Attempts to create an Iterator for the given object by first * converting it to a Collection. * * @param o an object * @return an Iterator for the given Object. * @see org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation#asCollection(java.lang.Object) * @since 1.0 */ public static Iterator iterator(Object o) { return DefaultTypeTransformation.asCollection(o).iterator(); } /** * Allows an Enumeration to behave like an Iterator. Note that the * {@link java.util.Iterator#remove() remove()} method is unsupported since the * underlying Enumeration does not provide a mechanism for removing items. * * @param enumeration an Enumeration object * @return an Iterator for the given Enumeration * @since 1.0 */ public static <T> Iterator<T> iterator(final Enumeration<T> enumeration) { return new Iterator<T>() { private T last; public boolean hasNext() { return enumeration.hasMoreElements(); } public T next() { last = enumeration.nextElement(); return last; } public void remove() { throw new UnsupportedOperationException("Cannot remove() from an Enumeration"); } }; } /** * Returns an {@link java.util.Iterator} which traverses each match. * * @param matcher a Matcher object * @return an Iterator for a Matcher * @see java.util.regex.Matcher#group() * @since 1.0 */ public static Iterator iterator(final Matcher matcher) { matcher.reset(); return new Iterator() { private boolean found /* = false */; private boolean done /* = false */; public boolean hasNext() { if (done) { return false; } if (!found) { found = matcher.find(); if (!found) { done = true; } } return found; } public Object next() { if (!found) { if (!hasNext()) { throw new NoSuchElementException(); } } found = false; if (hasGroup(matcher)) { // are we using groups? // yes, so return the specified group as list List<String> list = new ArrayList<String>(matcher.groupCount()); for (int i = 0; i <= matcher.groupCount(); i++) { list.add(matcher.group(i)); } return list; } else { // not using groups, so return the nth // occurrence of the pattern return matcher.group(); } } public void remove() { throw new UnsupportedOperationException(); } }; } /** * Creates an iterator which will traverse through the reader a line at a time. * * @param self a Reader object * @return an Iterator for the Reader * @see java.io.BufferedReader#readLine() * @since 1.5.0 */ public static Iterator<String> iterator(Reader self) { final BufferedReader bufferedReader; if (self instanceof BufferedReader) bufferedReader = (BufferedReader) self; else bufferedReader = new BufferedReader(self); return new Iterator<String>() { String nextVal /* = null */; boolean nextMustRead = true; boolean hasNext = true; public boolean hasNext() { if (nextMustRead && hasNext) { try { nextVal = readNext(); nextMustRead = false; } catch (IOException e) { hasNext = false; } } return hasNext; } public String next() { String retval = null; if (nextMustRead) { try { retval = readNext(); } catch (IOException e) { hasNext = false; } } else retval = nextVal; nextMustRead = true; return retval; } private String readNext() throws IOException { String nv = bufferedReader.readLine(); if (nv == null) hasNext = false; return nv; } public void remove() { throw new UnsupportedOperationException("Cannot remove() from a Reader Iterator"); } }; } /** * Standard iterator for a input stream which iterates through the stream * content in a byte-based fashion. * * @param self an InputStream object * @return an Iterator for the InputStream * @since 1.5.0 */ public static Iterator<Byte> iterator(InputStream self) { return iterator(new DataInputStream(self)); } /** * Standard iterator for a data input stream which iterates through the * stream content a Byte at a time. * * @param self a DataInputStream object * @return an Iterator for the DataInputStream * @since 1.5.0 */ public static Iterator<Byte> iterator(final DataInputStream self) { return new Iterator<Byte>() { Byte nextVal; boolean nextMustRead = true; boolean hasNext = true; public boolean hasNext() { if (nextMustRead && hasNext) { try { nextVal = self.readByte(); nextMustRead = false; } catch (IOException e) { hasNext = false; } } return hasNext; } public Byte next() { Byte retval = null; if (nextMustRead) { try { retval = self.readByte(); } catch (IOException e) { hasNext = false; } } else retval = nextVal; nextMustRead = true; return retval; } public void remove() { throw new UnsupportedOperationException("Cannot remove() from a DataInputStream Iterator"); } }; } /** * An identity function for iterators, supporting 'duck-typing' when trying to get an * iterator for each object within a collection, some of which may already be iterators. * * @param self an iterator object * @return itself * @since 1.5.0 */ public static <T> Iterator<T> iterator(Iterator<T> self) { return self; } /** * <p>Returns an object satisfying Groovy truth if the implementing MetaClass responds to * a method with the given name and arguments types. * * <p>Note that this method's return value is based on realised methods and does not take into account * objects or classes that implement invokeMethod or methodMissing * * <p>This method is "safe" in that it will always return a value and never throw an exception * * @param self The object to inspect * @param name The name of the method of interest * @param argTypes The argument types to match against * @return A List of MetaMethods matching the argument types which will be empty if no matching methods exist * @see groovy.lang.MetaObjectProtocol#respondsTo(java.lang.Object, java.lang.String, java.lang.Object[]) * @since 1.6.0 */ public static List<MetaMethod> respondsTo(Object self, String name, Object[] argTypes) { return InvokerHelper.getMetaClass(self).respondsTo(self, name, argTypes); } /** * <p>Returns an object satisfying Groovy truth if the implementing MetaClass responds to * a method with the given name regardless of the arguments. * * <p>Note that this method's return value is based on realised methods and does not take into account * objects or classes that implement invokeMethod or methodMissing * * <p>This method is "safe" in that it will always return a value and never throw an exception * * @param self The object to inspect * @param name The name of the method of interest * @return A List of MetaMethods matching the given name or an empty list if no matching methods exist * @see groovy.lang.MetaObjectProtocol#respondsTo(java.lang.Object, java.lang.String) * @since 1.6.1 */ public static List<MetaMethod> respondsTo(Object self, String name) { return InvokerHelper.getMetaClass(self).respondsTo(self, name); } /** * <p>Returns true of the implementing MetaClass has a property of the given name * * <p>Note that this method will only return true for realised properties and does not take into * account implementation of getProperty or propertyMissing * * @param self The object to inspect * @param name The name of the property of interest * @return The found MetaProperty or null if it doesn't exist * @see groovy.lang.MetaObjectProtocol#hasProperty(java.lang.Object, java.lang.String) * @since 1.6.1 */ public static MetaProperty hasProperty(Object self, String name) { return InvokerHelper.getMetaClass(self).hasProperty(self, name); } }