View Javadoc

    1   /*
    2    * $Id: DefaultGroovyMethods.java,v 1.207 2006/06/27 15:53:35 tug Exp $
    3    *
    4    * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5    *
    6    * Redistribution and use of this software and associated documentation
    7    * ("Software"), with or without modification, are permitted provided that the
    8    * following conditions are met:
    9    *  1. Redistributions of source code must retain copyright statements and
    10   * notices. Redistributions must also contain a copy of this document.
    11   *  2. Redistributions in binary form must reproduce the above copyright
    12   * notice, this list of conditions and the following disclaimer in the
    13   * documentation and/or other materials provided with the distribution.
    14   *  3. The name "groovy" must not be used to endorse or promote products
    15   * derived from this Software without prior written permission of The Codehaus.
    16   * For written permission, please contact info@codehaus.org.
    17   *  4. Products derived from this Software may not be called "groovy" nor may
    18   * "groovy" appear in their names without prior written permission of The
    19   * Codehaus. "groovy" is a registered trademark of The Codehaus.
    20   *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
    21   *
    22   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
    23   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    24   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    25   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
    26   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    27   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    28   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    29   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    30   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    31   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
    32   * DAMAGE.
    33   *
    34   */
    35  package org.codehaus.groovy.runtime;
    36
    37  import groovy.lang.*;
    38  import groovy.util.CharsetToolkit;
    39  import groovy.util.ClosureComparator;
    40  import groovy.util.OrderBy;
    41
    42  import java.io.*;
    43  import java.lang.reflect.Array;
    44  import java.lang.reflect.Field;
    45  import java.lang.reflect.Modifier;
    46  import java.math.BigDecimal;
    47  import java.math.BigInteger;
    48  import java.net.MalformedURLException;
    49  import java.net.ServerSocket;
    50  import java.net.Socket;
    51  import java.net.URI;
    52  import java.net.URISyntaxException;
    53  import java.net.URL;
    54  import java.security.AccessController;
    55  import java.security.PrivilegedAction;
    56  import java.util.*;
    57  import java.util.logging.Logger;
    58  import java.util.regex.Matcher;
    59  import java.util.regex.Pattern;
    60
    61  import org.codehaus.groovy.tools.RootLoader;
    62
    63  /***
    64   * This class defines all the new groovy methods which appear on normal JDK
    65   * classes inside the Groovy environment. Static methods are used with the
    66   * first parameter the destination class.
    67   *
    68   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    69   * @author Jeremy Rayner
    70   * @author Sam Pullara
    71   * @author Rod Cope
    72   * @author Guillaume Laforge
    73   * @author John Wilson
    74   * @author Hein Meling
    75   * @author Dierk Koenig
    76   * @author Pilho Kim
    77   * @author Marc Guillemot
    78   * @version $Revision: 1.207 $
    79   */
    80  public class DefaultGroovyMethods {
    81
    82      private static Logger log = Logger.getLogger(DefaultGroovyMethods.class.getName());
    83
    84      private static final Integer ONE = new Integer(1);
    85      private static final char ZERO_CHAR = '\u0000';
    86
    87      /***
    88       * Identity check. Since == is overridden in Groovy with the meaning of equality
    89       * we need some fallback to check for object identity.
    90       * @param self
    91       * @param other
    92       * @return true if self and other are identical, false otherwise
    93       */
    94      public static boolean is(Object self, Object other){
    95          return self == other;
    96      }
    97
    98      /***
    99       * Allows the closure to be called for the object reference self
    100      *
    101      * @param self     the object to have a closure act upon
    102      * @param closure  the closure to call on the object
    103      * @return         result of calling the closure
    104      */
    105     public static Object identity(Object self, Closure closure) {
    106         closure.setDelegate(self);
    107         return closure.call(self);
    108     }
    109
    110     /***
    111      * Allows the subscript operator to be used to lookup dynamic property values.
    112      * <code>bean[somePropertyNameExpression]</code>. The normal property notation
    113      * of groovy is neater and more concise but only works with compile time known
    114      * property names.
    115      *
    116      * @param self
    117      */
    118     public static Object getAt(Object self, String property) {
    119         return InvokerHelper.getProperty(self, property);
    120     }
    121
    122     /***
    123      * Allows the subscript operator to be used to set dynamically named property values.
    124      * <code>bean[somePropertyNameExpression] = foo</code>. The normal property notation
    125      * of groovy is neater and more concise but only works with compile time known
    126      * property names.
    127      *
    128      * @param self
    129      */
    130     public static void putAt(Object self, String property, Object newValue) {
    131         InvokerHelper.setProperty(self, property, newValue);
    132     }
    133
    134     /***
    135      * Generates a detailed dump string of an object showing its class,
    136      * hashCode and fields
    137      */
    138     public static String dump(Object self) {
    139         if (self == null) {
    140             return "null";
    141         }
    142         StringBuffer buffer = new StringBuffer("<");
    143         Class klass = self.getClass();
    144         buffer.append(klass.getName());
    145         buffer.append("@");
    146         buffer.append(Integer.toHexString(self.hashCode()));
    147         boolean groovyObject = self instanceof GroovyObject;
    148
    149         /*jes this may be rewritten to use the new getProperties() stuff
    150          * but the original pulls out private variables, whereas getProperties()
    151          * does not. What's the real use of dump() here?
    152          */
    153         while (klass != null) {
    154             Field[] fields = klass.getDeclaredFields();
    155             for (int i = 0; i < fields.length; i++) {
    156                 final Field field = fields[i];
    157                 if ((field.getModifiers() & Modifier.STATIC) == 0) {
    158                     if (groovyObject && field.getName().equals("metaClass")) {
    159                         continue;
    160                     }
    161                     AccessController.doPrivileged(new PrivilegedAction() {
    162                         public Object run() {
    163                             field.setAccessible(true);
    164                             return null;
    165                         }
    166                     });
    167                     buffer.append(" ");
    168                     buffer.append(field.getName());
    169                     buffer.append("=");
    170                     try {
    171                         buffer.append(InvokerHelper.toString(field.get(self)));
    172                     } catch (Exception e) {
    173                         buffer.append(e);
    174                     }
    175                 }
    176             }
    177
    178             klass = klass.getSuperclass();
    179         }
    180
    181         /* here is a different implementation that uses getProperties(). I have left
    182          * it commented out because it returns a slightly different list of properties;
    183          * ie it does not return privates. I don't know what dump() really should be doing,
    184          * although IMO showing private fields is a no-no
    185          */
    186         /*
    187         List props = getProperties(self);
    188             for(Iterator itr = props.keySet().iterator(); itr.hasNext(); ) {
    189             String propName = itr.next().toString();
    190
    191             // the original skipped this, so I will too
    192             if(pv.getName().equals("metaClass")) continue;
    193             if(pv.getName().equals("class")) continue;
    194
    195             buffer.append(" ");
    196             buffer.append(propName);
    197             buffer.append("=");
    198             try {
    199                 buffer.append(InvokerHelper.toString(props.get(propName)));
    200             }
    201             catch (Exception e) {
    202                 buffer.append(e);
    203             }
    204         }
    205         */
    206
    207         buffer.append(">");
    208         return buffer.toString();
    209     }
    210
    211     /***
    212      * Retrieves the list of {@link MetaProperty} objects for 'self' and wraps it
    213      * in a list of {@link PropertyValue} objects that additionally provide
    214      * the value for each property of 'self'.
    215      * @param self the receiver object
    216      * @return list of {@link PropertyValue} objects
    217      * @see groovy.util.Expando#getMetaPropertyValues()
    218      */
    219     public static List getMetaPropertyValues(Object self) {
    220         MetaClass metaClass = InvokerHelper.getMetaClass(self);
    221         List mps = metaClass.getProperties();
    222         List props = new ArrayList(mps.size());
    223         for (Iterator itr = mps.iterator(); itr.hasNext();) {
    224             MetaProperty mp = (MetaProperty) itr.next();
    225             PropertyValue pv = new PropertyValue(self, mp);
    226             props.add(pv);
    227         }
    228         return props;
    229     }
    230
    231     /***
    232      * Convenience method that calls {@link #getMetaPropertyValues(Object)}(self)
    233      * and provides the data in form of simple key/value pairs, i.e. without
    234      * type() information.
    235      * @param self the receiver object
    236      * @return meta properties as Map of key/value pairs
    237      */
    238     public static Map getProperties(Object self) {
    239         List metaProps = getMetaPropertyValues(self);
    240         Map props = new HashMap(metaProps.size());
    241
    242         for (Iterator itr = metaProps.iterator(); itr.hasNext();) {
    243             PropertyValue pv = (PropertyValue) itr.next();
    244             try {
    245                 props.put(pv.getName(), pv.getValue());
    246             } catch (Exception e) {
    247                 log.throwing(self.getClass().getName(), "getProperty("+pv.getName()+")", e );
    248             }
    249         }
    250         return props;
    251     }
    252
    253     /***
    254      * Scoped use method
    255      */
    256     public static void use(Object self, Class categoryClass, Closure closure) {
    257         GroovyCategorySupport.use(categoryClass, closure);
    258     }
    259
    260     /***
    261      * Scoped use method with list of categories
    262      */
    263     public static void use(Object self, List categoryClassList, Closure closure) {
    264         GroovyCategorySupport.use(categoryClassList, closure);
    265     }
    266
    267
    268     /***
    269      * Print to a console in interactive format
    270      */
    271     public static void print(Object self, Object value) {
    272         System.out.print(InvokerHelper.toString(value));
    273     }
    274
    275     /***
    276      * Print a linebreak to the standard out.
    277      */
    278     public static void println(Object self) {
    279         System.out.println();
    280     }
    281
    282     /***
    283      * Print to a console in interactive format along with a newline
    284      */
    285     public static void println(Object self, Object value) {
    286         System.out.println(InvokerHelper.toString(value));
    287     }
    288
    289   /***
    290    *  Printf to a console.  Only works with JDK1.5 or later.
    291    *
    292    *  @author Russel Winder
    293    *  @version 2005.02.01.15.53
    294    */
    295   public static void printf(final Object self, final String format, final Object[] values) {
    296     if ( System.getProperty("java.version").charAt(2) == '5' ) {
    297       //
    298       //  Cannot just do:
    299       //
    300       //        System.out.printf(format, values) ;
    301       //
    302       //  because this fails to compile on JDK1.4.x and earlier.  So until the entire world is using
    303       //  JDK1.5 or later then we have to do things by reflection so as to hide the use of printf
    304       //  from the compiler.  In JDK1.5 you might try:
    305       //
    306       //        System.out.getClass().getMethod("printf", String.class, Object[].class).invoke(System.out, format, values) ;
    307       //
    308       //  but of course this doesn't work on JDK1.4 as it relies on varargs.  argh.  So we are
    309       //  forced into:
    310       //
    311       try {
    312         System.out.getClass().getMethod("printf", new Class[] {String.class, Object[].class}).invoke(System.out, new Object[] {format, values}) ;
    313       } catch ( NoSuchMethodException nsme ) {
    314         throw new RuntimeException ("getMethod threw a NoSuchMethodException.  This is impossible.") ;
    315       } catch ( IllegalAccessException iae ) {
    316         throw new RuntimeException ("invoke threw a IllegalAccessException.  This is impossible.") ;
    317       } catch ( java.lang.reflect.InvocationTargetException ite ) {
    318         throw new RuntimeException ("invoke threw a InvocationTargetException.  This is impossible.") ;
    319       }
    320     } else {
    321       throw new RuntimeException ("printf requires JDK1.5 or later.") ;
    322     }
    323   }
    324
    325   /***
    326    * Returns a formatted string using the specified format string and
    327    * arguments.
    328    *
    329    * <p>
    330    * For examples, <pre>
    331    *     printf ( "Hello, %s!\n" , [ "world" ] as String[] )
    332    *     printf ( "Hello, %s!\n" , [ "Groovy" ])
    333    *     printf ( "%d + %d = %d\n" , [ 1 , 2 , 1+2 ] as Integer[] )
    334    *     printf ( "%d + %d = %d\n" , [ 3 , 3 , 3+3 ])
    335    *
    336    *     ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as Integer[] ) }
    337    *     ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as int[] ) }
    338    *     ( 0x41..0x45 ).each { printf ( "-- %c\n" , [ it ] as char[] ) }
    339    *     ( 07..011 ).each { printf ( "-- %d\n" , [ it ] as byte[] ) }
    340    *     ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as short[] ) }
    341    *     ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as long[] ) }
    342    *     ( 7..11 ).each { printf ( "-- %5.2f\n" , [ it ] as float[] ) }
    343    *     ( 7..11 ).each { printf ( "-- %5.2g\n" , [ it ] as double[] ) }
    344    * </pre>
    345    * <p>
    346    *
    347    * @param  format
    348    *         A format string
    349    *
    350    * @param  arg
    351    *         Argument which is referenced by the format specifiers in the format
    352    *         string.  The type of <code>arg</code> should be one of Object[], List,
    353    *         int[], short[], byte[], char[], boolean[], long[], float[], or double[].
    354    *
    355    * @since  JDK 1.5
    356    *
    357    * @author Pilho Kim
    358    * @version 2005.07.25.02.31
    359    */
    360   public static void printf(final Object self, final String format, Object arg) {
    361       if (arg instanceof Object[]) {
    362           printf(self, format, (Object[]) arg);
    363           return;
    364       } else if (arg instanceof List) {
    365           printf(self, format, ((List) arg).toArray());
    366           return;
    367       } else if (!arg.getClass().isArray()) {
    368           Object[] o = (Object[]) java.lang.reflect.Array.newInstance(arg.getClass(), 1);
    369           o[0]=arg;
    370           printf(self, format, o);
    371           return;
    372       }
    373
    374       Object[] ans = null;
    375       String elemType = arg.getClass().getName();
    376       if (elemType.equals("[I")) {
    377           int[] ia = (int[]) arg;
    378           ans = new Integer[ia.length];
    379           for (int i = 0; i < ia.length; i++) {
    380               ans[i] = new Integer(ia[i]);
    381           }
    382       }
    383       else if (elemType.equals("[C")) {
    384           char[] ia = (char[]) arg;
    385           ans = new Character[ia.length];
    386           for (int i = 0; i < ia.length; i++) {
    387               ans[i] = new Character(ia[i]);
    388           }
    389       }
    390       else if (elemType.equals("[Z")) {
    391           boolean[] ia = (boolean[]) arg;
    392           ans = new Boolean[ia.length];
    393           for (int i = 0; i < ia.length; i++) {
    394               ans[i] = new Boolean(ia[i]);
    395           }
    396       }
    397       else if (elemType.equals("[B")) {
    398           byte[] ia = (byte[]) arg;
    399           ans = new Byte[ia.length];
    400           for (int i = 0; i < ia.length; i++) {
    401               ans[i] = new Byte(ia[i]);
    402           }
    403       }
    404       else if (elemType.equals("[S")) {
    405           short[] ia = (short[]) arg;
    406           ans = new Short[ia.length];
    407           for (int i = 0; i < ia.length; i++) {
    408               ans[i] = new Short(ia[i]);
    409           }
    410       }
    411       else if (elemType.equals("[F")) {
    412           float[] ia = (float[]) arg;
    413           ans = new Float[ia.length];
    414           for (int i = 0; i < ia.length; i++) {
    415               ans[i] = new Float(ia[i]);
    416           }
    417       }
    418       else if (elemType.equals("[J")) {
    419           long[] ia = (long[]) arg;
    420           ans = new Long[ia.length];
    421           for (int i = 0; i < ia.length; i++) {
    422               ans[i] = new Long(ia[i]);
    423           }
    424       }
    425       else if (elemType.equals("[D")) {
    426           double[] ia = (double[]) arg;
    427           ans = new Double[ia.length];
    428           for (int i = 0; i < ia.length; i++) {
    429               ans[i] = new Double(ia[i]);
    430           }
    431       }
    432       else {
    433           throw new RuntimeException("printf(String," + arg + ")");
    434       }
    435       printf(self, format, (Object[]) ans);
    436   }
    437
    438
    439     /***
    440      * @return a String that matches what would be typed into a terminal to
    441      *         create this object. e.g. [1, 'hello'].inspect() -> [1, "hello"]
    442      */
    443     public static String inspect(Object self) {
    444         return InvokerHelper.inspect(self);
    445     }
    446
    447     /***
    448      * Print to a console in interactive format
    449      */
    450     public static void print(Object self, PrintWriter out) {
    451         if (out == null) {
    452             out = new PrintWriter(System.out);
    453         }
    454         out.print(InvokerHelper.toString(self));
    455     }
    456
    457     /***
    458      * Print to a console in interactive format
    459      *
    460      * @param out the PrintWriter used for printing
    461      */
    462     public static void println(Object self, PrintWriter out) {
    463         if (out == null) {
    464             out = new PrintWriter(System.out);
    465         }
    466         InvokerHelper.invokeMethod(self, "print", out);
    467         out.println();
    468     }
    469
    470     /***
    471      * Provide a dynamic method invocation method which can be overloaded in
    472      * classes to implement dynamic proxies easily.
    473      */
    474     public static Object invokeMethod(Object object, String method, Object arguments) {
    475         return InvokerHelper.invokeMethod(object, method, arguments);
    476     }
    477
    478     // isCase methods
    479     //-------------------------------------------------------------------------
    480     public static boolean isCase(Object caseValue, Object switchValue) {
    481         return caseValue.equals(switchValue);
    482     }
    483
    484     public static boolean isCase(String caseValue, Object switchValue) {
    485         if (switchValue == null) {
    486             return caseValue == null;
    487         }
    488         return caseValue.equals(switchValue.toString());
    489     }
    490
    491     public static boolean isCase(Class caseValue, Object switchValue) {
    492         if (switchValue instanceof Class) {
    493             Class val = (Class) switchValue;
    494             return caseValue.isAssignableFrom(val);
    495         }
    496         return caseValue.isInstance(switchValue);
    497     }
    498
    499     public static boolean isCase(Collection caseValue, Object switchValue) {
    500         return caseValue.contains(switchValue);
    501     }
    502
    503     public static boolean isCase(Pattern caseValue, Object switchValue) {
    504         if (switchValue == null) {
    505             return caseValue == null;
    506         }
    507         final Matcher matcher = caseValue.matcher(switchValue.toString());
    508         if (matcher.matches()) {
    509             RegexSupport.setLastMatcher(matcher);
    510             return true;
    511         } else {
    512             return false;
    513         }
    514     }
    515
    516     private static Object packArray(Object object) {
    517         if (object instanceof Object[])
    518             return new Object[] {object};
    519         else
    520             return object;
    521     }
    522
    523     // Collection based methods
    524     //-------------------------------------------------------------------------
    525
    526     /***
    527      * Remove all duplicates from a given Collection.
    528      * Works on the receiver object and returns it.
    529      * For each duplicate, only the first member which is returned
    530      * by the given Collection's iterator is retained, but all other ones are removed.
    531      * The given Collection's original order is retained.
    532      * If there exists numbers in the Collection, then they are compared
    533      * as numbers, that is, 2, 2.0, 3L, (short)4 are comparable.
    534      *
    535      * <code><pre>
    536      *     def x = [2, 2.0, 3L, 1.0, (short)4, 1]
    537      *     def y = x.unique()
    538      *     assert( y == x && x == [2, 3L, 1.0, (short)4] )
    539      * </pre></code>
    540      *
    541      * @param self
    542      * @return self without duplicates
    543      */
    544    /*
    545     public static Collection unique(Collection self){
    546         if (self instanceof Set) return self;
    547         if (self.size() == new HashSet(self).size()) return self;
    548         Collection seen = new HashSet(self.size());
    549         for (Iterator iter = self.iterator(); iter.hasNext();) {
    550             Object o =  iter.next();
    551             if (seen.contains(o)){
    552                 iter.remove();
    553             } else {
    554                 seen.add(o);
    555             }
    556         }
    557         return self;
    558     }
    559    */
    560     public static Collection unique(Collection self) {
    561         if (self instanceof Set)
    562             return self;
    563         List answer = new ArrayList();
    564         NumberComparator comparator = new NumberComparator();
    565         for (Iterator it = self.iterator(); it.hasNext();) {
    566             Object o =  it.next();
    567             boolean duplicated = false;
    568             for (Iterator it2 = answer.iterator(); it2.hasNext();) {
    569                 Object o2 =  it2.next();
    570                 if (comparator.compare(o, o2) == 0) {
    571                     duplicated = true;
    572                     break;
    573                 }
    574             }
    575             if (!duplicated)
    576                 answer.add(o);
    577         }
    578         self.clear();
    579         self.addAll(answer);
    580         return self;
    581     }
    582
    583     /***
    584      * Remove all duplicates from a given Collection.
    585      * Works on the receiver object and returns it.
    586      * The order of members in the Collection are compared by the given Comparator.
    587      * For eachy duplicate, the first member which is returned
    588      * by the given Collection's iterator is retained, but all other ones are removed.
    589      * The given Collection's original order is retained.
    590      *
    591      * <code><pre>
    592      *     class Person {
    593      *         @Property fname, lname
    594      *         public String toString() {
    595      *             return fname + " " + lname
    596      *         }
    597      *     }
    598      *
    599      *     class PersonComparator implements Comparator {
    600      *         public int compare(Object o1, Object o2) {
    601      *             Person p1 = (Person) o1
    602      *             Person p2 = (Person) o2
    603      *             if (p1.lname != p2.lname)
    604      *                 return p1.lname.compareTo(p2.lname)
    605      *             else
    606      *                 return p1.fname.compareTo(p2.fname)
    607      *         }
    608      *
    609      *         public boolean equals(Object obj) {
    610      *             return this.equals(obj)
    611      *         }
    612      *     }
    613      *
    614      *     Person a = new Person(fname:"John", lname:"Taylor")
    615      *     Person b = new Person(fname:"Clark", lname:"Taylor")
    616      *     Person c = new Person(fname:"Tom", lname:"Cruz")
    617      *     Person d = new Person(fname:"Clark", lname:"Taylor")
    618      *
    619      *     def list = [a, b, c, d]
    620      *     List list2 = list.unique(new PersonComparator())
    621      *     assert( list2 == list && list == [a, b, c] )
    622      *
    623      * </pre></code>
    624      *
    625      * @param self        a Collection
    626      * @param comparator  a Comparator.
    627      * @return self       without duplicates
    628      */
    629     public static Collection unique(Collection self, Comparator comparator) {
    630         if (self instanceof Set)
    631             return self;
    632         List answer = new ArrayList();
    633         for (Iterator it = self.iterator(); it.hasNext();) {
    634             Object o =  it.next();
    635             boolean duplicated = false;
    636             for (Iterator it2 = answer.iterator(); it2.hasNext();) {
    637                 Object o2 =  it2.next();
    638                 if (comparator.compare(o, o2) == 0) {
    639                     duplicated = true;
    640                     break;
    641                 }
    642             }
    643             if (!duplicated)
    644                 answer.add(o);
    645         }
    646         self.clear();
    647         self.addAll(answer);
    648         return self;
    649     }
    650
    651     /***
    652      * Allows objects to be iterated through using a closure
    653      *
    654      * @param self    the object over which we iterate
    655      * @param closure the closure applied on each element found
    656      */
    657     public static void each(Object self, Closure closure) {
    658         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
    659             closure.call(iter.next());
    660         }
    661     }
    662
    663     /***
    664      * Allows object to be iterated through a closure with a counter
    665      *
    666      * @param self    an Object
    667      * @param closure a Closure
    668      */
    669     public static void eachWithIndex(Object self, Closure closure) {
    670         int counter = 0;
    671         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
    672             closure.call(new Object[]{iter.next(), new Integer(counter++)});
    673         }
    674     }
    675
    676     /***
    677      * Allows objects to be iterated through using a closure
    678      *
    679      * @param self    the collection over which we iterate
    680      * @param closure the closure applied on each element of the collection
    681      */
    682     public static void each(Collection self, Closure closure) {
    683         for (Iterator iter = self.iterator(); iter.hasNext();) {
    684             closure.call(iter.next());
    685         }
    686     }
    687
    688     /***
    689      * Allows a Map to be iterated through using a closure. If the
    690      * closure takes one parameter then it will be passed the Map.Entry
    691      * otherwise if the closure takes two parameters then it will be
    692      * passed the key and the value.
    693      *
    694      * @param self    the map over which we iterate
    695      * @param closure the closure applied on each entry of the map
    696      */
    697     public static void each(Map self, Closure closure) {
    698         for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
    699             Map.Entry entry = (Map.Entry) iter.next();
    700             callClosureForMapEntry(closure, entry);
    701         }
    702     }
    703
    704
    705     /***
    706      * Iterates over every element of a collection, and check whether a predicate is valid for all elements.
    707      *
    708      * @param self    the object over which we iterate
    709      * @param closure the closure predicate used for matching
    710      * @return true if every item in the collection matches the closure
    711      *         predicate
    712      */
    713     public static boolean every(Object self, Closure closure) {
    714         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
    715             if (!InvokerHelper.asBool(closure.call(iter.next()))) {
    716                 return false;
    717             }
    718         }
    719         return true;
    720     }
    721
    722     /***
    723      * Iterates over every element of a collection, and check whether a predicate is valid for at least one element
    724      *
    725      * @param self    the object over which we iterate
    726      * @param closure the closure predicate used for matching
    727      * @return true if any item in the collection matches the closure predicate
    728      */
    729     public static boolean any(Object self, Closure closure) {
    730         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
    731             if (InvokerHelper.asBool(closure.call(iter.next()))) {
    732                 return true;
    733             }
    734         }
    735         return false;
    736     }
    737
    738     /***
    739      * Iterates over every element of the collection and return each object that matches
    740      * the given filter - calling the isCase() method used by switch statements.
    741      * This method can be used with different kinds of filters like regular expresions, classes, ranges etc.
    742      *
    743      * @param self   the object over which we iterate
    744      * @param filter the filter to perform on the collection (using the isCase(object) method)
    745      * @return a list of objects which match the filter
    746      */
    747     public static List grep(Object self, Object filter) {
    748         List answer = new ArrayList();
    749         MetaClass metaClass = InvokerHelper.getMetaClass(filter);
    750         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
    751             Object object = iter.next();
    752             if (InvokerHelper.asBool(metaClass.invokeMethod(filter, "isCase", object))) {
    753                 answer.add(object);
    754             }
    755         }
    756         return answer;
    757     }
    758
    759     /***
    760      * Counts the number of occurencies of the given value inside this collection
    761      *
    762      * @param self  the collection within which we count the number of occurencies
    763      * @param value the value
    764      * @return the number of occurrencies
    765      */
    766     public static int count(Collection self, Object value) {
    767         int answer = 0;
    768         for (Iterator iter = self.iterator(); iter.hasNext();) {
    769             if (InvokerHelper.compareEqual(iter.next(), value)) {
    770                 ++answer;
    771             }
    772         }
    773         return answer;
    774     }
    775
    776     /***
    777      * Convert a collection to a List.
    778      *
    779      * @param self a collection
    780      * @return a List
    781      */
    782     public static List toList(Collection self) {
    783         List answer = new ArrayList(self.size());
    784         answer.addAll(self);
    785         return answer;
    786     }
    787
    788     /***
    789      * Iterates through this object transforming each object into a new value using the closure
    790      * as a transformer, returning a list of transformed values.
    791      *
    792      * @param self    the values of the object to map
    793      * @param closure the closure used to map each element of the collection
    794      * @return a List of the mapped values
    795      */
    796     public static List collect(Object self, Closure closure) {
    797         return (List) collect(self, new ArrayList(), closure);
    798     }
    799
    800     /***
    801      * Iterates through this object transforming each object into a new value using the closure
    802      * as a transformer and adding it to the collection, returning the resulting collection.
    803      *
    804      * @param self       the values of the object to map
    805      * @param collection the Collection to which the mapped values are added
    806      * @param closure    the closure used to map each element of the collection
    807      * @return the resultant collection
    808      */
    809     public static Collection collect(Object self, Collection collection, Closure closure) {
    810         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
    811             collection.add(closure.call(iter.next()));
    812         }
    813         return collection;
    814     }
    815
    816     /***
    817      * Iterates through this collection transforming each entry into a new value using the closure
    818      * as a transformer, returning a list of transformed values.
    819      *
    820      * @param self    a collection
    821      * @param closure the closure used for mapping
    822      * @return a List of the mapped values
    823      */
    824     public static List collect(Collection self, Closure closure) {
    825         return (List) collect(self, new ArrayList(self.size()), closure);
    826     }
    827
    828     /***
    829      * Iterates through this collection transforming each entry into a new value using the closure
    830      * as a transformer, returning a list of transformed values.
    831      *
    832      * @param self       a collection
    833      * @param collection the Collection to which the mapped values are added
    834      * @param closure    the closure used to map each element of the collection
    835      * @return the resultant collection
    836      */
    837     public static Collection collect(Collection self, Collection collection, Closure closure) {
    838         for (Iterator iter = self.iterator(); iter.hasNext();) {
    839             collection.add(closure.call(iter.next()));
    840             if (closure.getDirective() == Closure.DONE) {
    841                 break;
    842             }
    843         }
    844         return collection;
    845     }
    846
    847     /***
    848      * Iterates through this Map transforming each entry into a new value using the closure
    849      * as a transformer, returning a list of transformed values.
    850      *
    851      * @param self    a Map
    852      * @param closure the closure used for mapping, which can be with one(Map.Entry) or two(key, value) parameters
    853      * @return a List of the mapped values
    854      */
    855     public static Collection collect(Map self, Collection collection, Closure closure) {
    856         boolean isTwoParams = (closure.getParameterTypes().length == 2);
    857         for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
    858             if (isTwoParams) {
    859                 Map.Entry entry = (Map.Entry) iter.next();
    860                 collection.add(closure.call(new Object[]{entry.getKey(), entry.getValue()}));
    861             } else {
    862                 collection.add(closure.call(iter.next()));
    863             }
    864         }
    865         return collection;
    866     }
    867
    868     /***
    869      * Iterates through this Map transforming each entry into a new value using the closure
    870      * as a transformer, returning a list of transformed values.
    871      *
    872      * @param self       a Map
    873      * @param closure    the closure used to map each element of the collection
    874      * @return the resultant collection
    875      */
    876     public static List collect(Map self, Closure closure) {
    877         return (List) collect(self, new ArrayList(self.size()), closure);
    878     }
    879
    880     /***
    881      * Finds the first value matching the closure condition
    882      *
    883      * @param self    an Object with an iterator returning its values
    884      * @param closure a closure condition
    885      * @return the first Object found
    886      */
    887     public static Object find(Object self, Closure closure) {
    888         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
    889             Object value = iter.next();
    890             if (InvokerHelper.asBool(closure.call(value))) {
    891                 return value;
    892             }
    893         }
    894         return null;
    895     }
    896
    897     /***
    898      * Finds the first value matching the closure condition
    899      *
    900      * @param self    a Collection
    901      * @param closure a closure condition
    902      * @return the first Object found
    903      */
    904     public static Object find(Collection self, Closure closure) {
    905         for (Iterator iter = self.iterator(); iter.hasNext();) {
    906             Object value = iter.next();
    907             if (InvokerHelper.asBool(closure.call(value))) {
    908                 return value;
    909             }
    910         }
    911         return null;
    912     }
    913
    914     /***
    915      * Finds the first value matching the closure condition
    916      *
    917      * @param self    a Map
    918      * @param closure a closure condition
    919      * @return the first Object found
    920      */
    921     public static Object find(Map self, Closure closure) {
    922         for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
    923             Object value = iter.next();
    924             if (InvokerHelper.asBool(closure.call(value))) {
    925                 return value;
    926             }
    927         }
    928         return null;
    929     }
    930
    931     /***
    932      * Finds all values matching the closure condition
    933      *
    934      * @param self    an Object with an Iterator returning its values
    935      * @param closure a closure condition
    936      * @return a List of the values found
    937      */
    938     public static List findAll(Object self, Closure closure) {
    939         List answer = new ArrayList();
    940         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
    941             Object value = iter.next();
    942             if (InvokerHelper.asBool(closure.call(value))) {
    943                 answer.add(value);
    944             }
    945         }
    946         return answer;
    947     }
    948
    949     /***
    950      * Finds all values matching the closure condition
    951      *
    952      * @param self    a Collection
    953      * @param closure a closure condition
    954      * @return a List of the values found
    955      */
    956     public static List findAll(Collection self, Closure closure) {
    957         List answer = new ArrayList(self.size());
    958         for (Iterator iter = self.iterator(); iter.hasNext();) {
    959             Object value = iter.next();
    960             if (InvokerHelper.asBool(closure.call(value))) {
    961                 answer.add(value);
    962             }
    963         }
    964         return answer;
    965     }
    966
    967     /***
    968      * Finds all entries matching the closure condition. If the
    969      * closure takes one parameter then it will be passed the Map.Entry
    970      * otherwise if the closure takes two parameters then it will be
    971      * passed the key and the value.
    972      *
    973      * @param self    a Map
    974      * @param closure a closure condition applying on the entries
    975      * @return a new subMap
    976      */
    977     public static Map findAll(Map self, Closure closure) {
    978         Map answer = new HashMap(self.size());
    979         for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
    980             Map.Entry entry = (Map.Entry) iter.next();
    981             if (InvokerHelper.asBool(callClosureForMapEntry(closure, entry))) {
    982                 answer.put(entry.getKey(),entry.getValue());
    983             }
    984         }
    985         return answer;
    986     }
    987
    988     // internal helper method
    989     protected static Object callClosureForMapEntry(Closure closure, Map.Entry entry) {
    990         if (closure.getMaximumNumberOfParameters() == 2) {
    991             return closure.call(new Object[]{entry.getKey(), entry.getValue()});
    992         }
    993         return closure.call(entry);
    994     }
    995
    996
    997     /***
    998      * Iterates through the given collection, passing in the initial value to
    999      * the closure along with the current iterated item then passing into the
    1000      * next iteration the value of the previous closure.
    1001      *
    1002      * @param self    a Collection
    1003      * @param value   a value
    1004      * @param closure a closure
    1005      * @return the last value of the last iteration
    1006      */
    1007     public static Object inject(Collection self, Object value, Closure closure) {
    1008         Object[] params = new Object[2];
    1009         for (Iterator iter = self.iterator(); iter.hasNext();) {
    1010             Object item = iter.next();
    1011             params[0] = value;
    1012             params[1] = item;
    1013             value = closure.call(params);
    1014         }
    1015         return value;
    1016     }
    1017
    1018     /***
    1019      * Iterates through the given array of objects, passing in the initial value to
    1020      * the closure along with the current iterated item then passing into the
    1021      * next iteration the value of the previous closure.
    1022      *
    1023      * @param self    an Object[]
    1024      * @param value   a value
    1025      * @param closure a closure
    1026      * @return the last value of the last iteration
    1027      */
    1028     public static Object inject(Object[] self, Object value, Closure closure) {
    1029         Object[] params = new Object[2];
    1030         for (int i = 0; i < self.length; i++) {
    1031             params[0] = value;
    1032             params[1] = self[i];
    1033             value = closure.call(params);
    1034         }
    1035         return value;
    1036     }
    1037
    1038     /***
    1039      * Sums a collection of numeric values. <code>coll.sum()</code> is equivalent to:
    1040      * <code>coll.inject(0) {value, item -> value + item}</code>.
    1041      *
    1042      * @param self Collection of values to add together.
    1043      * @return The sum of all of the list itmems.
    1044      */
    1045     public static Object sum(Collection self) {
    1046         Object result = null;
    1047
    1048         if (self.size() == 0) return result;
    1049
    1050         boolean isNumber = true;
    1051
    1052         Class classref = null;
    1053         try {
    1054           classref = Class.forName("java.lang.Number");
    1055         } catch (Exception ex) {
    1056         }
    1057
    1058         for (Iterator iter = self.iterator(); iter.hasNext(); ) {
    1059           if (!classref.isInstance(iter.next())) {
    1060             isNumber = false;
    1061             break;
    1062           }
    1063         }
    1064
    1065         if (isNumber) {
    1066     	  result = new Integer(0);
    1067         } else {
    1068           result = new String();
    1069         }
    1070
    1071         Object[] param = new Object[1];
    1072         for (Iterator iter = self.iterator(); iter.hasNext();) {
    1073           Object operand = iter.next();
    1074           param[0] = operand;
    1075           MetaClass metaClass = InvokerHelper.getMetaClass(result);
    1076           result = metaClass.invokeMethod(result, "plus", param);
    1077         }
    1078         return result;
    1079     }
    1080
    1081     /***
    1082      * Sums the result of apply a closure to each item of a collection.
    1083      * <code>coll.sum(closure)</code> is equivalent to:
    1084      * <code>coll.collect(closure).sum()</code>.
    1085      *
    1086      * @param self a Collection
    1087      * @param closure a single parameter closure that returns a numeric value.
    1088      * @return The sum of the values returned by applying the closure to each
    1089      *         item of the list.
    1090      */
    1091     public static Object sum(Collection self, Closure closure) {
    1092     	Object result = new Integer(0);
    1093 		Object[] closureParam = new Object[1];
    1094 		Object[] plusParam = new Object[1];
    1095 		for (Iterator iter = self.iterator(); iter.hasNext();) {
    1096 			Object item = iter.next();
    1097 			closureParam[0] = item;
    1098 			plusParam[0] = closure.call(closureParam);
    1099 			MetaClass metaClass = InvokerHelper.getMetaClass(result);
    1100 			result = metaClass.invokeMethod(result, "plus", plusParam);
    1101 		}
    1102 		return result;
    1103     }
    1104
    1105     /***
    1106      * Concatenates all of the items of the collection together with the given String as a separator
    1107      *
    1108      * @param self      a Collection of objects
    1109      * @param separator a String separator
    1110      * @return the joined String
    1111      */
    1112     public static String join(Collection self, String separator) {
    1113         StringBuffer buffer = new StringBuffer();
    1114         boolean first = true;
    1115
    1116         if (separator == null) separator = "";
    1117
    1118         for (Iterator iter = self.iterator(); iter.hasNext();) {
    1119             Object value = iter.next();
    1120             if (first) {
    1121                 first = false;
    1122             } else {
    1123                 buffer.append(separator);
    1124             }
    1125             buffer.append(InvokerHelper.toString(value));
    1126         }
    1127         return buffer.toString();
    1128     }
    1129
    1130     /***
    1131      * Concatenates all of the elements of the array together with the given String as a separator
    1132      *
    1133      * @param self      an array of Object
    1134      * @param separator a String separator
    1135      * @return the joined String
    1136      */
    1137     public static String join(Object[] self, String separator) {
    1138         StringBuffer buffer = new StringBuffer();
    1139         boolean first = true;
    1140
    1141         if (separator == null) separator = "";
    1142
    1143         for (int i = 0; i < self.length; i++) {
    1144             String value = InvokerHelper.toString(self[i]);
    1145             if (first) {
    1146                 first = false;
    1147             } else {
    1148                 buffer.append(separator);
    1149             }
    1150             buffer.append(value);
    1151         }
    1152         return buffer.toString();
    1153     }
    1154
    1155     /***
    1156      * Selects the maximum value found in the collection
    1157      *
    1158      * @param self a Collection
    1159      * @return the maximum value
    1160      */
    1161     public static Object max(Collection self) {
    1162         Object answer = null;
    1163         for (Iterator iter = self.iterator(); iter.hasNext();) {
    1164             Object value = iter.next();
    1165             if (value != null) {
    1166                 if (answer == null || InvokerHelper.compareGreaterThan(value, answer)) {
    1167                     answer = value;
    1168                 }
    1169             }
    1170         }
    1171         return answer;
    1172     }
    1173
    1174     /***
    1175      * Selects the maximum value found in the collection using the given comparator
    1176      *
    1177      * @param self       a Collection
    1178      * @param comparator a Comparator
    1179      * @return the maximum value
    1180      */
    1181     public static Object max(Collection self, Comparator comparator) {
    1182         Object answer = null;
    1183         for (Iterator iter = self.iterator(); iter.hasNext();) {
    1184             Object value = iter.next();
    1185             if (answer == null || comparator.compare(value, answer) > 0) {
    1186                 answer = value;
    1187             }
    1188         }
    1189         return answer;
    1190     }
    1191
    1192     /***
    1193      * Selects the minimum value found in the collection
    1194      *
    1195      * @param self a Collection
    1196      * @return the minimum value
    1197      */
    1198     public static Object min(Collection self) {
    1199         Object answer = null;
    1200         for (Iterator iter = self.iterator(); iter.hasNext();) {
    1201             Object value = iter.next();
    1202             if (value != null) {
    1203                 if (answer == null || InvokerHelper.compareLessThan(value, answer)) {
    1204                     answer = value;
    1205                 }
    1206             }
    1207         }
    1208         return answer;
    1209     }
    1210
    1211     /***
    1212      * Selects the minimum value found in the collection using the given comparator
    1213      *
    1214      * @param self       a Collection
    1215      * @param comparator a Comparator
    1216      * @return the minimum value
    1217      */
    1218     public static Object min(Collection self, Comparator comparator) {
    1219         Object answer = null;
    1220         for (Iterator iter = self.iterator(); iter.hasNext();) {
    1221             Object value = iter.next();
    1222             if (answer == null || comparator.compare(value, answer) < 0) {
    1223                 answer = value;
    1224             }
    1225         }
    1226         return answer;
    1227     }
    1228
    1229     /***
    1230      * Selects the minimum value found in the collection using the given closure as a comparator
    1231      *
    1232      * @param self    a Collection
    1233      * @param closure a closure used as a comparator
    1234      * @return the minimum value
    1235      */
    1236     public static Object min(Collection self, Closure closure) {
    1237         int params = closure.getMaximumNumberOfParameters();
    1238         if (params == 1) {
    1239             Object answer = null;
    1240             Object answer_value = null;
    1241             for (Iterator iter = self.iterator(); iter.hasNext();) {
    1242                 Object item = iter.next();
    1243                 Object value = closure.call(item);
    1244                 if (answer == null || InvokerHelper.compareLessThan(value, answer_value)) {
    1245                     answer = item;
    1246                     answer_value = value;
    1247                 }
    1248             }
    1249             return answer;
    1250         } else {
    1251             return min(self, new ClosureComparator(closure));
    1252         }
    1253     }
    1254
    1255     /***
    1256      * Selects the maximum value found in the collection using the given closure as a comparator
    1257      *
    1258      * @param self    a Collection
    1259      * @param closure a closure used as a comparator
    1260      * @return the maximum value
    1261      */
    1262     public static Object max(Collection self, Closure closure) {
    1263         int params = closure.getMaximumNumberOfParameters();
    1264         if (params == 1) {
    1265             Object answer = null;
    1266             Object answer_value = null;
    1267             for (Iterator iter = self.iterator(); iter.hasNext();) {
    1268                 Object item = iter.next();
    1269                 Object value = closure.call(item);
    1270                 if (answer == null || InvokerHelper.compareLessThan(answer_value, value)) {
    1271                     answer = item;
    1272                     answer_value = value;
    1273                 }
    1274             }
    1275             return answer;
    1276         } else {
    1277             return max(self, new ClosureComparator(closure));
    1278         }
    1279     }
    1280
    1281     /***
    1282      * Makes a String look like a Collection by adding support for the size() method
    1283      *
    1284      * @param text a String
    1285      * @return the length of the String
    1286      */
    1287     public static int size(String text) {
    1288         return text.length();
    1289     }
    1290
    1291     /***
    1292      * Provide standard Groovy size() method for StringBuffers
    1293      *
    1294      * @param buffer a StringBuffer
    1295      * @return the length of the StringBuffer
    1296      */
    1297     public static int size(StringBuffer buffer) {
    1298         return buffer.length();
    1299     }
    1300
    1301     /***
    1302      * Provide the standard Groovy size method
    1303      */
    1304     public static long size(File file){
    1305         return file.length();
    1306     }
    1307
    1308
    1309     /***
    1310      * Provide the standard Groovy size method
    1311      */
    1312     public static long size(Matcher matcher){
    1313         return getCount(matcher);
    1314     }
    1315
    1316     /***
    1317      * Makes an Array look like a Collection by adding support for the size() method
    1318      *
    1319      * @param self an Array of Object
    1320      * @return the size of the Array
    1321      */
    1322     public static int size(Object[] self) {
    1323         return self.length;
    1324     }
    1325
    1326     /***
    1327      * Support the subscript operator for String.
    1328      *
    1329      * @param text  a String
    1330      * @param index the index of the Character to get
    1331      * @return the Character at the given index
    1332      */
    1333     public static CharSequence getAt(CharSequence text, int index) {
    1334         index = normaliseIndex(index, text.length());
    1335         return text.subSequence(index, index + 1);
    1336     }
    1337
    1338     /***
    1339      * Support the subscript operator for String
    1340      *
    1341      * @param text a String
    1342      * @return the Character object at the given index
    1343      */
    1344     public static String getAt(String text, int index) {
    1345         index = normaliseIndex(index, text.length());
    1346         return text.substring(index, index + 1);
    1347     }
    1348
    1349     /***
    1350      * Support the range subscript operator for CharSequence
    1351      *
    1352      * @param text  a CharSequence
    1353      * @param range a Range
    1354      * @return the subsequence CharSequence
    1355      */
    1356     public static CharSequence getAt(CharSequence text, Range range) {
    1357         int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), text.length());
    1358         int to = normaliseIndex(InvokerHelper.asInt(range.getTo()), text.length());
    1359
    1360         // If this is a backwards range, reverse the arguments to substring.
    1361         if (from > to) {
    1362             int tmp = from;
    1363             from = to;
    1364             to = tmp;
    1365         }
    1366
    1367         return text.subSequence(from, to + 1);
    1368     }
    1369
    1370     /***
    1371      * Support the range subscript operator for CharSequence or StringBuffer with IntRange
    1372      *
    1373      * @param text  a CharSequence
    1374      * @param range an IntRange
    1375      * @return the subsequence CharSequence
    1376      */
    1377     public static CharSequence getAt(CharSequence text, IntRange range) {
    1378         return getAt(text, (Range) range);
    1379     }
    1380
    1381     /***
    1382      * Support the range subscript operator for String with IntRange
    1383      *
    1384      * @param text  a String
    1385      * @param range an IntRange
    1386      * @return the resulting String
    1387      */
    1388     public static String getAt(String text, IntRange range) {
    1389         return getAt(text,(Range)range);
    1390     }
    1391
    1392     /***
    1393      * Support the range subscript operator for String
    1394      *
    1395      * @param text  a String
    1396      * @param range a Range
    1397      * @return a substring corresponding to the Range
    1398      */
    1399     public static String getAt(String text, Range range) {
    1400         int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), text.length());
    1401         int to = normaliseIndex(InvokerHelper.asInt(range.getTo()), text.length());
    1402
    1403         // If this is a backwards range, reverse the arguments to substring.
    1404         boolean reverse = range.isReverse();
    1405         if (from > to) {
    1406             int tmp = to;
    1407             to = from;
    1408             from = tmp;
    1409             reverse = !reverse;
    1410         }
    1411
    1412         String answer = text.substring(from, to + 1);
    1413         if (reverse) {
    1414             answer = reverse(answer);
    1415         }
    1416         return answer;
    1417     }
    1418
    1419     /***
    1420      * Creates a new string which is the reverse (backwards) of this string
    1421      *
    1422      * @param self a String
    1423      * @return a new string with all the characters reversed.
    1424      */
    1425     public static String reverse(String self) {
    1426         int size = self.length();
    1427         StringBuffer buffer = new StringBuffer(size);
    1428         for (int i = size - 1; i >= 0; i--) {
    1429             buffer.append(self.charAt(i));
    1430         }
    1431         return buffer.toString();
    1432     }
    1433
    1434     /***
    1435      * Transforms a String representing a URL into a URL object.
    1436      *
    1437      * @param self the String representing a URL
    1438      * @return a URL
    1439      * @throws MalformedURLException is thrown if the URL is not well formed.
    1440      */
    1441     public static URL toURL(String self) throws MalformedURLException {
    1442         return new URL(self);
    1443     }
    1444
    1445     /***
    1446      * Transforms a String representing a URI into a URI object.
    1447      *
    1448      * @param self the String representing a URI
    1449      * @return a URI
    1450      * @throws URISyntaxException
    1451      * @throws URISyntaxException is thrown if the URI is not well formed.
    1452      */
    1453     public static URI toURI(String self) throws URISyntaxException {
    1454         return new URI(self);
    1455     }
    1456
    1457     /***
    1458      * Turns a String into a regular expression pattern
    1459      *
    1460      * @param self a String to convert into a regular expression
    1461      * @return the regular expression pattern
    1462      */
    1463     public static Pattern negate(String self) {
    1464         return InvokerHelper.regexPattern(self);
    1465     }
    1466
    1467     /***
    1468      * Replaces all occurrencies of a captured group by the result of a closure on that text.
    1469      *
    1470      * <p> For examples,
    1471      * <pre>
    1472      *     assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { Object[] it -> it[0].toUpperCase() })
    1473      *
    1474      *     Here,
    1475      *          it[0] is the global string of the matched group
    1476      *          it[1] is the first string in the matched group
    1477      *          it[2] is the second string in the matched group
    1478      *
    1479      *
    1480      *     assert "FOO-FOO-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { x, y, z -> z.toUpperCase() })
    1481      *
    1482      *     Here,
    1483      *          x is the global string of the matched group
    1484      *          y is the first string in the matched group
    1485      *          z is the second string in the matched group
    1486      * </pre>
    1487      *
    1488      * @param self a String
    1489      * @param regex the capturing regex
    1490      * @param closure the closure to apply on each captured group
    1491      * @return a String with replaced content
    1492      */
    1493     public static String replaceAll(String self, String regex, Closure closure) {
    1494         Matcher matcher = Pattern.compile(regex).matcher(self);
    1495         if (matcher.find()) {
    1496             matcher.reset();
    1497             StringBuffer sb = new StringBuffer();
    1498             while (matcher.find()) {
    1499                 int count = matcher.groupCount();
    1500                 ArrayList groups = new ArrayList();
    1501                 for (int i = 0; i <= count; i++) {
    1502                     groups.add(matcher.group(i));
    1503                 }
    1504                 matcher.appendReplacement(sb, String.valueOf(closure.call((Object[]) groups.toArray() )));
    1505             }
    1506             matcher.appendTail(sb);
    1507             return sb.toString();
    1508         } else {
    1509             return self;
    1510         }
    1511     }
    1512
    1513     private static String getPadding(String padding, int length) {
    1514         if (padding.length() < length) {
    1515             return multiply(padding, new Integer(length / padding.length() + 1)).substring(0, length);
    1516         } else {
    1517             return padding.substring(0, length);
    1518         }
    1519     }
    1520
    1521     /***
    1522      * Pad a String with the characters appended to the left
    1523      *
    1524      * @param numberOfChars the total number of characters
    1525      * @param padding       the charaters used for padding
    1526      * @return the String padded to the left
    1527      */
    1528     public static String padLeft(String self, Number numberOfChars, String padding) {
    1529         int numChars = numberOfChars.intValue();
    1530         if (numChars <= self.length()) {
    1531             return self;
    1532         } else {
    1533             return getPadding(padding, numChars - self.length()) + self;
    1534         }
    1535     }
    1536
    1537     /***
    1538      * Pad a String with the spaces appended to the left
    1539      *
    1540      * @param numberOfChars the total number of characters
    1541      * @return the String padded to the left
    1542      */
    1543
    1544     public static String padLeft(String self, Number numberOfChars) {
    1545         return padLeft(self, numberOfChars, " ");
    1546     }
    1547
    1548     /***
    1549      * Pad a String with the characters appended to the right
    1550      *
    1551      * @param numberOfChars the total number of characters
    1552      * @param padding       the charaters used for padding
    1553      * @return the String padded to the right
    1554      */
    1555
    1556     public static String padRight(String self, Number numberOfChars, String padding) {
    1557         int numChars = numberOfChars.intValue();
    1558         if (numChars <= self.length()) {
    1559             return self;
    1560         } else {
    1561             return self + getPadding(padding, numChars - self.length());
    1562         }
    1563     }
    1564
    1565     /***
    1566      * Pad a String with the spaces appended to the right
    1567      *
    1568      * @param numberOfChars the total number of characters
    1569      * @return the String padded to the right
    1570      */
    1571
    1572     public static String padRight(String self, Number numberOfChars) {
    1573         return padRight(self, numberOfChars, " ");
    1574     }
    1575
    1576     /***
    1577      * Center a String and padd it with the characters appended around it
    1578      *
    1579      * @param numberOfChars the total number of characters
    1580      * @param padding       the charaters used for padding
    1581      * @return the String centered with padded character around
    1582      */
    1583     public static String center(String self, Number numberOfChars, String padding) {
    1584         int numChars = numberOfChars.intValue();
    1585         if (numChars <= self.length()) {
    1586             return self;
    1587         } else {
    1588             int charsToAdd = numChars - self.length();
    1589             String semiPad = charsToAdd % 2 == 1 ?
    1590                     getPadding(padding, charsToAdd / 2 + 1) :
    1591                     getPadding(padding, charsToAdd / 2);
    1592             if (charsToAdd % 2 == 0)
    1593                 return semiPad + self + semiPad;
    1594             else
    1595                 return semiPad.substring(0, charsToAdd / 2) + self + semiPad;
    1596         }
    1597     }
    1598
    1599     /***
    1600      * Center a String and padd it with spaces appended around it
    1601      *
    1602      * @param numberOfChars the total number of characters
    1603      * @return the String centered with padded character around
    1604      */
    1605     public static String center(String self, Number numberOfChars) {
    1606         return center(self, numberOfChars, " ");
    1607     }
    1608
    1609     /***
    1610      * Support the subscript operator, e.g. matcher[index], for a regex Matcher.
    1611      *
    1612      * For an example using no group match, <code><pre>
    1613      *    def p = /ab[d|f]/
    1614      *    def m = "abcabdabeabf" =~ p
    1615      *    for (i in 0..<m.count) {
    1616      *        println( "m.groupCount() = " + m.groupCount())
    1617      *        println( "  " + i + ": " + m[i] )   // m[i] is a String
    1618      *    }
    1619      * </pre></code>
    1620      *
    1621      * For an example using group matches, <code><pre>
    1622      *    def p = /(?:ab([c|d|e|f]))/
    1623      *    def m = "abcabdabeabf" =~ p
    1624      *    for (i in 0..<m.count) {
    1625      *        println( "m.groupCount() = " + m.groupCount())
    1626      *        println( "  " + i + ": " + m[i] )   // m[i] is a List
    1627      *    }
    1628      * </pre></code>
    1629      *
    1630      * For another example using group matches, <code><pre>
    1631      *    def m = "abcabdabeabfabxyzabx" =~ /(?:ab([d|x-z]+))/
    1632      *    m.count.times {
    1633      *        println( "m.groupCount() = " + m.groupCount())
    1634      *        println( "  " + it + ": " + m[it] )   // m[it] is a List
    1635      *    }
    1636      * </pre></code>
    1637      *
    1638      * @param matcher a Matcher
    1639      * @param idx     an index
    1640      * @return object a matched String if no groups matched, list of matched groups otherwise.
    1641      */
    1642     public static Object getAt(Matcher matcher, int idx) {
    1643         try {
    1644             int count = getCount(matcher);
    1645             if (idx < -count || idx >= count) {
    1646                 throw new IndexOutOfBoundsException("index is out of range " + (-count) + ".." + (count - 1) + " (index = " + idx + ")");
    1647             }
    1648             idx = normaliseIndex(idx, count);
    1649             matcher.reset();
    1650             for (int i = 0; i <= idx; i++) {
    1651                 matcher.find();
    1652             }
    1653
    1654             if (hasGroup(matcher)) {
    1655                 // are we using groups?
    1656                 // yes, so return the specified group as list
    1657                 ArrayList list = new ArrayList(matcher.groupCount());
    1658                 for (int i = 0; i <= matcher.groupCount(); i++) {
    1659                     list.add(matcher.group(i));
    1660                 }
    1661                 return list;
    1662             } else {
    1663                 // not using groups, so return the nth
    1664                 // occurrence of the pattern
    1665                 return matcher.group();
    1666             }
    1667         }
    1668         catch (IllegalStateException ex) {
    1669             return null;
    1670         }
    1671     }
    1672
    1673     /***
    1674      * Set the position of the given Matcher to the given index.
    1675      *
    1676      * @param matcher a Matcher
    1677      * @param idx the index number
    1678      */
    1679     public static void setIndex(Matcher matcher, int idx) {
    1680         int count = getCount(matcher);
    1681         if (idx < -count || idx >= count) {
    1682             throw new IndexOutOfBoundsException("index is out of range " + (-count) + ".." + (count - 1) + " (index = " + idx + ")");
    1683         }
    1684         if (idx == 0) {
    1685             matcher.reset();
    1686         }
    1687         else if (idx > 0) {
    1688             matcher.reset();
    1689             for (int i = 0; i < idx; i++) {
    1690                 matcher.find();
    1691             }
    1692         }
    1693         else if (idx < 0) {
    1694             matcher.reset();
    1695             idx += getCount(matcher);
    1696             for (int i = 0; i < idx; i++) {
    1697                 matcher.find();
    1698             }
    1699         }
    1700     }
    1701
    1702     /***
    1703      * Find the number of Strings matched to the given Matcher.
    1704      *
    1705      * @param matcher a Matcher
    1706      * @return int  the number of Strings matched to the given matcher.
    1707      */
    1708     public static int getCount(Matcher matcher) {
    1709         int counter = 0;
    1710         matcher.reset();
    1711         while (matcher.find()) {
    1712             counter++;
    1713         }
    1714         matcher.reset();
    1715         return counter;
    1716     }
    1717
    1718     /***
    1719      * Check whether a Matcher contains a group or not.
    1720      *
    1721      * @param matcher a Matcher
    1722      * @return boolean  <code>true</code> if matcher contains at least one group.
    1723      */
    1724     public static boolean hasGroup(Matcher matcher) {
    1725         return matcher.groupCount() > 0;
    1726     }
    1727
    1728     /***
    1729      * Support the range subscript operator for a List
    1730      *
    1731      * @param self  a List
    1732      * @param range a Range
    1733      * @return a sublist based on range borders or a new list if range is reversed
    1734      * @see java.util.List#subList(int, int)
    1735      */
    1736     public static List getAt(List self, IntRange range) {
    1737         RangeInfo info = subListBorders(self.size(), range);
    1738         List answer = self.subList(info.from, info.to);  // sublist is always exclusive, but Ranges are not
    1739         if (info.reverse) {
    1740             answer = reverse(answer);
    1741         }
    1742         return answer;
    1743     }
    1744
    1745     // helper method for getAt and putAt
    1746     protected static RangeInfo subListBorders(int size, IntRange range){
    1747         int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), size);
    1748         int to = normaliseIndex(InvokerHelper.asInt(range.getTo()), size);
    1749         boolean reverse = range.isReverse();
    1750         if (from > to) {                        // support list[1..-1]
    1751             int tmp = to;
    1752             to = from;
    1753             from = tmp;
    1754             reverse = !reverse;
    1755         }
    1756         return new RangeInfo(from, to+1, reverse);
    1757     }
    1758
    1759     // helper method for getAt and putAt
    1760     protected static RangeInfo subListBorders(int size, EmptyRange range){
    1761         int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), size);
    1762         return new RangeInfo(from, from, false);
    1763     }
    1764
    1765     /***
    1766      * Allows a List to be used as the indices to be used on a List
    1767      *
    1768      * @param self    a List
    1769      * @param indices a Collection of indices
    1770      * @return a new list of the values at the given indices
    1771      */
    1772     public static List getAt(List self, Collection indices) {
    1773         List answer = new ArrayList(indices.size());
    1774         for (Iterator iter = indices.iterator(); iter.hasNext();) {
    1775             Object value = iter.next();
    1776             if (value instanceof Range) {
    1777                 answer.addAll(getAt(self, (Range) value));
    1778             } else if (value instanceof List) {
    1779                 answer.addAll(getAt(self, (List) value));
    1780             } else {
    1781                 int idx = InvokerHelper.asInt(value);
    1782                 answer.add(getAt(self, idx));
    1783             }
    1784         }
    1785         return answer;
    1786     }
    1787
    1788     /***
    1789      * Allows a List to be used as the indices to be used on a List
    1790      *
    1791      * @param self    an Array of Objects
    1792      * @param indices a Collection of indices
    1793      * @return a new list of the values at the given indices
    1794      */
    1795     public static List getAt(Object[] self, Collection indices) {
    1796         List answer = new ArrayList(indices.size());
    1797         for (Iterator iter = indices.iterator(); iter.hasNext();) {
    1798             Object value = iter.next();
    1799             if (value instanceof Range) {
    1800                 answer.addAll(getAt(self, (Range) value));
    1801             } else if (value instanceof Collection) {
    1802                 answer.addAll(getAt(self, (Collection) value));
    1803             } else {
    1804                 int idx = InvokerHelper.asInt(value);
    1805                 answer.add(getAt(self, idx));
    1806             }
    1807         }
    1808         return answer;
    1809     }
    1810
    1811     /***
    1812      * Allows a List to be used as the indices to be used on a CharSequence
    1813      *
    1814      * @param self    a CharSequence
    1815      * @param indices a Collection of indices
    1816      * @return a String of the values at the given indices
    1817      */
    1818     public static CharSequence getAt(CharSequence self, Collection indices) {
    1819         StringBuffer answer = new StringBuffer();
    1820         for (Iterator iter = indices.iterator(); iter.hasNext();) {
    1821             Object value = iter.next();
    1822             if (value instanceof Range) {
    1823                 answer.append(getAt(self, (Range) value));
    1824             } else if (value instanceof Collection) {
    1825                 answer.append(getAt(self, (Collection) value));
    1826             } else {
    1827                 int idx = InvokerHelper.asInt(value);
    1828                 answer.append(getAt(self, idx));
    1829             }
    1830         }
    1831         return answer.toString();
    1832     }
    1833
    1834     /***
    1835      * Allows a List to be used as the indices to be used on a String
    1836      *
    1837      * @param self    a String
    1838      * @param indices a Collection of indices
    1839      * @return a String of the values at the given indices
    1840      */
    1841     public static String getAt(String self, Collection indices) {
    1842         return (String) getAt((CharSequence) self, indices);
    1843     }
    1844
    1845     /***
    1846      * Allows a List to be used as the indices to be used on a Matcher
    1847      *
    1848      * @param self    a Matcher
    1849      * @param indices a Collection of indices
    1850      * @return a String of the values at the given indices
    1851      */
    1852     public static String getAt(Matcher self, Collection indices) {
    1853         StringBuffer answer = new StringBuffer();
    1854         for (Iterator iter = indices.iterator(); iter.hasNext();) {
    1855             Object value = iter.next();
    1856             if (value instanceof Range) {
    1857                 answer.append(getAt(self, (Range) value));
    1858             } else if (value instanceof Collection) {
    1859                 answer.append(getAt(self, (Collection) value));
    1860             } else {
    1861                 int idx = InvokerHelper.asInt(value);
    1862                 answer.append(getAt(self, idx));
    1863             }
    1864         }
    1865         return answer.toString();
    1866     }
    1867
    1868     /***
    1869      * Creates a sub-Map containing the given keys. This method is similar to
    1870      * List.subList() but uses keys rather than index ranges.
    1871      *
    1872      * @param map  a Map
    1873      * @param keys a Collection of keys
    1874      * @return a new Map containing the given keys
    1875      */
    1876     public static Map subMap(Map map, Collection keys) {
    1877         Map answer = new HashMap(keys.size());
    1878         for (Iterator iter = keys.iterator(); iter.hasNext();) {
    1879             Object key = iter.next();
    1880             answer.put(key, map.get(key));
    1881         }
    1882         return answer;
    1883     }
    1884
    1885     /***
    1886      * Looks up an item in a Map for the given key and returns the value - unless
    1887      * there is no entry for the given key in which case add the default value
    1888      * to the map and return that.
    1889      *
    1890      * @param map          a Map
    1891      * @param key          the key to lookup the value of
    1892      * @param defaultValue the value to return and add to the map for this key if
    1893      *                     there is no entry for the given key
    1894      * @return the value of the given key or the default value, added to the map if the
    1895      *         key did not exist
    1896      */
    1897     public static Object get(Map map, Object key, Object defaultValue) {
    1898         Object answer = map.get(key);
    1899         if (answer == null) {
    1900             answer = defaultValue;
    1901             map.put(key, answer);
    1902         }
    1903         return answer;
    1904     }
    1905
    1906     /***
    1907      * Support the range subscript operator for an Array
    1908      *
    1909      * @param array an Array of Objects
    1910      * @param range a Range
    1911      * @return a range of a list from the range's from index up to but not
    1912      *         including the ranges's to value
    1913      */
    1914     public static List getAt(Object[] array, Range range) {
    1915         List list = Arrays.asList(array);
    1916         return getAt(list, range);
    1917     }
    1918
    1919     public static List getAt(Object[] array, IntRange range) {
    1920         List list = Arrays.asList(array);
    1921         return getAt(list, range);
    1922     }
    1923
    1924     public static List getAt(Object[] array, ObjectRange range) {
    1925         List list = Arrays.asList(array);
    1926         return getAt(list, range);
    1927     }
    1928
    1929     /***
    1930      * Support the subscript operator for an Array
    1931      *
    1932      * @param array an Array of Objects
    1933      * @param idx   an index
    1934      * @return the value at the given index
    1935      */
    1936     public static Object getAt(Object[] array, int idx) {
    1937         return array[normaliseIndex(idx, array.length)];
    1938     }
    1939
    1940     /***
    1941      * Support the subscript operator for an Array
    1942      *
    1943      * @param array an Array of Objects
    1944      * @param idx   an index
    1945      * @param value an Object to put at the given index
    1946      */
    1947     public static void putAt(Object[] array, int idx, Object value) {
    1948         if (value instanceof Number) {
    1949             Class arrayComponentClass = array.getClass().getComponentType();
    1950
    1951             if (!arrayComponentClass.equals(value.getClass())) {
    1952                 Object newVal = InvokerHelper.asType(value, arrayComponentClass);
    1953                 array[normaliseIndex(idx, array.length)] = newVal;
    1954                 return;
    1955             }
    1956         }
    1957         array[normaliseIndex(idx, array.length)] = value;
    1958     }
    1959
    1960     /***
    1961      * Allows conversion of arrays into a mutable List
    1962      *
    1963      * @param array an Array of Objects
    1964      * @return the array as a List
    1965      */
    1966     public static List toList(Object[] array) {
    1967         int size = array.length;
    1968         List list = new ArrayList(size);
    1969         for (int i = 0; i < size; i++) {
    1970             list.add(array[i]);
    1971         }
    1972         return list;
    1973     }
    1974
    1975     /***
    1976      * Support the subscript operator for a List
    1977      *
    1978      * @param self a List
    1979      * @param idx  an index
    1980      * @return the value at the given index
    1981      */
    1982     public static Object getAt(List self, int idx) {
    1983         int size = self.size();
    1984         int i = normaliseIndex(idx, size);
    1985         if (i < size) {
    1986             return self.get(i);
    1987         } else {
    1988             return null;
    1989         }
    1990     }
    1991
    1992     /***
    1993      * A helper method to allow lists to work with subscript operators
    1994      *
    1995      * @param self  a List
    1996      * @param idx   an index
    1997      * @param value the value to put at the given index
    1998      */
    1999     public static void putAt(List self, int idx, Object value) {
    2000         int size = self.size();
    2001         idx = normaliseIndex(idx, size);
    2002         if (idx < size) {
    2003             self.set(idx, value);
    2004         } else {
    2005             while (size < idx) {
    2006                 self.add(size++, null);
    2007             }
    2008             self.add(idx, value);
    2009         }
    2010     }
    2011
    2012
    2013      /***
    2014      * Support the range subscript operator for StringBuffer
    2015      *
    2016      * @param self  a StringBuffer
    2017      * @param range a Range
    2018      * @param value the object that's toString() will be inserted
    2019      */
    2020     public static void putAt(StringBuffer self, IntRange range, Object value) {
    2021         RangeInfo info = subListBorders(self.length(), range);
    2022         self.replace(info.from, info.to,  value.toString());
    2023     }
    2024     /***
    2025      * Support the range subscript operator for StringBuffer
    2026      *
    2027      * @param self  a StringBuffer
    2028      * @param range a Range
    2029      * @param value the object that's toString() will be inserted
    2030      */
    2031     public static void putAt(StringBuffer self, EmptyRange range, Object value) {
    2032         RangeInfo info = subListBorders(self.length(), range);
    2033         self.replace(info.from, info.to,  value.toString());
    2034     }
    2035
    2036     /***
    2037      * A helper method to allow lists to work with subscript operators
    2038      *
    2039      * @param self  a List
    2040      * @param range  the subset of the list to set
    2041      * @param value the values to put at the given sublist or a Collection of values
    2042      */
    2043     public static void putAt(List self, EmptyRange range, Object value) {
    2044         RangeInfo info = subListBorders(self.size(), range);
    2045         List sublist = self.subList(info.from,  info.to);
    2046         sublist.clear();
    2047         if (value instanceof Collection){
    2048             Collection col = (Collection) value;
    2049             if (col.size() == 0) return;
    2050             sublist.addAll(col);
    2051         } else {
    2052             sublist.add(value);
    2053         }
    2054     }
    2055
    2056     /***
    2057      * A helper method to allow lists to work with subscript operators
    2058      *
    2059      * @param self  a List
    2060      * @param range  the subset of the list to set
    2061      * @param value the value to put at the given sublist or a Collection of values
    2062      */
    2063     public static void putAt(List self, IntRange range, Object value) {
    2064         RangeInfo info = subListBorders(self.size(), range);
    2065         List sublist = self.subList(info.from,  info.to);
    2066         sublist.clear();
    2067         if (value instanceof Collection){
    2068             Collection col = (Collection) value;
    2069             if (col.size() == 0) return;
    2070             sublist.addAll(col);
    2071         } else {
    2072             sublist.add(value);
    2073         }
    2074     }
    2075
    2076     /***
    2077      * A helper method to allow lists to work with subscript operators
    2078      *
    2079      * @param self  a List
    2080      * @param splice  the subset of the list to set
    2081      * @param values the value to put at the given sublist
    2082      * @deprecated replace with putAt(List self, Range range, List value)
    2083      */
    2084      public static void putAt(List self, List splice, List values) {
    2085          List sublist = getSubList(self, splice);
    2086          sublist.clear();
    2087          sublist.addAll(values);
    2088      }
    2089
    2090     /***
    2091      * A helper method to allow lists to work with subscript operators
    2092      *
    2093      * @param self  a List
    2094      * @param splice  the subset of the list to set
    2095      * @param value the value to put at the given sublist
    2096      * @deprecated replace with putAt(List self, Range range, Object value)
    2097      */
    2098     public static void putAt(List self, List splice, Object value) {
    2099         List sublist = getSubList(self, splice);
    2100         sublist.clear();
    2101         sublist.add(value);
    2102     }
    2103
    2104     // helper method for putAt(Splice)
    2105     // todo: remove after putAt(Splice) gets deleted
    2106     protected static List getSubList(List self, List splice) {
    2107         int left = 0;
    2108         int right = 0;
    2109         boolean emptyRange = false;
    2110         if (splice.size() == 2) {
    2111             left = InvokerHelper.asInt(splice.get(0));
    2112             right = InvokerHelper.asInt(splice.get(1));
    2113         } else if (splice instanceof IntRange) {
    2114             IntRange range = (IntRange) splice;
    2115             left = range.getFromInt();
    2116             right = range.getToInt();
    2117         } else if (splice instanceof EmptyRange) {
    2118             RangeInfo info = subListBorders(self.size(), (EmptyRange) splice);
    2119             left = info.from;
    2120             emptyRange = true;
    2121         } else {
    2122             throw new IllegalArgumentException("You must specify a list of 2 indexes to create a sub-list");
    2123         }
    2124         int size = self.size();
    2125         left = normaliseIndex(left, size);
    2126         right = normaliseIndex(right, size);
    2127         List sublist = null;
    2128         if (!emptyRange) {
    2129             sublist = self.subList(left, right + 1);
    2130         } else {
    2131             sublist = self.subList(left, left);
    2132         }
    2133         return sublist;
    2134     }
    2135
    2136     /***
    2137      * Support the subscript operator for a List
    2138      *
    2139      * @param self a Map
    2140      * @param key  an Object as a key for the map
    2141      * @return the value corresponding to the given key
    2142      */
    2143     public static Object getAt(Map self, Object key) {
    2144         return self.get(key);
    2145     }
    2146
    2147     /***
    2148      * A helper method to allow lists to work with subscript operators
    2149      *
    2150      * @param self a Map
    2151      * @param key  an Object as a key for the map
    2152      * @return the value corresponding to the given key
    2153      */
    2154     public static Object putAt(Map self, Object key, Object value) {
    2155         return self.put(key, value);
    2156     }
    2157
    2158     /***
    2159      * This converts a possibly negative index to a real index into the array.
    2160      *
    2161      * @param i
    2162      * @param size
    2163      */
    2164     protected static int normaliseIndex(int i, int size) {
    2165         int temp = i;
    2166         if (i < 0) {
    2167             i += size;
    2168         }
    2169         if (i < 0) {
    2170             throw new ArrayIndexOutOfBoundsException("Negative array index [" + temp + "] too large for array size " + size);
    2171         }
    2172         return i;
    2173     }
    2174
    2175     /***
    2176      * Support the subscript operator for List
    2177      *
    2178      * @param coll     a Collection
    2179      * @param property a String
    2180      * @return a List
    2181      */
    2182     public static List getAt(Collection coll, String property) {
    2183         List answer = new ArrayList(coll.size());
    2184         for (Iterator iter = coll.iterator(); iter.hasNext();) {
    2185             Object item = iter.next();
    2186             Object value = InvokerHelper.getProperty(item, property);
    2187             if (value instanceof Collection) {
    2188                 answer.addAll((Collection) value);
    2189             } else {
    2190                 answer.add(value);
    2191             }
    2192         }
    2193         return answer;
    2194     }
    2195
    2196     /***
    2197      * A convenience method for creating an immutable map
    2198      *
    2199      * @param self a Map
    2200      * @return an immutable Map
    2201      */
    2202     public static Map asImmutable(Map self) {
    2203         return Collections.unmodifiableMap(self);
    2204     }
    2205
    2206     /***
    2207      * A convenience method for creating an immutable sorted map
    2208      *
    2209      * @param self a SortedMap
    2210      * @return an immutable SortedMap
    2211      */
    2212     public static SortedMap asImmutable(SortedMap self) {
    2213         return Collections.unmodifiableSortedMap(self);
    2214     }
    2215
    2216     /***
    2217      * A convenience method for creating an immutable list
    2218      *
    2219      * @param self a List
    2220      * @return an immutable List
    2221      */
    2222     public static List asImmutable(List self) {
    2223         return Collections.unmodifiableList(self);
    2224     }
    2225
    2226     /***
    2227      * A convenience method for creating an immutable list
    2228      *
    2229      * @param self a Set
    2230      * @return an immutable Set
    2231      */
    2232     public static Set asImmutable(Set self) {
    2233         return Collections.unmodifiableSet(self);
    2234     }
    2235
    2236     /***
    2237      * A convenience method for creating an immutable sorted set
    2238      *
    2239      * @param self a SortedSet
    2240      * @return an immutable SortedSet
    2241      */
    2242     public static SortedSet asImmutable(SortedSet self) {
    2243         return Collections.unmodifiableSortedSet(self);
    2244     }
    2245
    2246     /***
    2247      * A convenience method for creating an immutable Collection
    2248      *
    2249      * @param self a Collection
    2250      * @return an immutable Collection
    2251      */
    2252     public static Collection asImmutable(Collection self) {
    2253         return Collections.unmodifiableCollection(self);
    2254     }
    2255
    2256     /***
    2257      * A convenience method for creating a synchronized Map
    2258      *
    2259      * @param self a Map
    2260      * @return a synchronized Map
    2261      */
    2262     public static Map asSynchronized(Map self) {
    2263         return Collections.synchronizedMap(self);
    2264     }
    2265
    2266     /***
    2267      * A convenience method for creating a synchronized SortedMap
    2268      *
    2269      * @param self a SortedMap
    2270      * @return a synchronized SortedMap
    2271      */
    2272     public static SortedMap asSynchronized(SortedMap self) {
    2273         return Collections.synchronizedSortedMap(self);
    2274     }
    2275
    2276     /***
    2277      * A convenience method for creating a synchronized Collection
    2278      *
    2279      * @param self a Collection
    2280      * @return a synchronized Collection
    2281      */
    2282     public static Collection asSynchronized(Collection self) {
    2283         return Collections.synchronizedCollection(self);
    2284     }
    2285
    2286     /***
    2287      * A convenience method for creating a synchronized List
    2288      *
    2289      * @param self a List
    2290      * @return a synchronized List
    2291      */
    2292     public static List asSynchronized(List self) {
    2293         return Collections.synchronizedList(self);
    2294     }
    2295
    2296     /***
    2297      * A convenience method for creating a synchronized Set
    2298      *
    2299      * @param self a Set
    2300      * @return a synchronized Set
    2301      */
    2302     public static Set asSynchronized(Set self) {
    2303         return Collections.synchronizedSet(self);
    2304     }
    2305
    2306     /***
    2307      * A convenience method for creating a synchronized SortedSet
    2308      *
    2309      * @param self a SortedSet
    2310      * @return a synchronized SortedSet
    2311      */
    2312     public static SortedSet asSynchronized(SortedSet self) {
    2313         return Collections.synchronizedSortedSet(self);
    2314     }
    2315
    2316     /***
    2317      * Returns the converted <code>SpreadList</code> of the given <code>self</code>.
    2318      * <p>
    2319      * This is the same method to <code>toSpreadList(List self)</code>.
    2320      * <p>
    2321      * For examples, if there is defined a function like as
    2322      * <blockquote><pre>
    2323      *     def fn(a, b, c, d) { return a + b + c + d }
    2324      * </pre></blockquote>, then all of the following three have the same meaning.
    2325      * <blockquote><pre>
    2326      *     println fn(1, [2, 3].spread(), 4)
    2327      *     println fn(1, *[2, 3], 4)
    2328      *     println fn(1, 2, 3, 4)
    2329      * </pre></blockquote>
    2330      * <p>
    2331      * </pre><br>
    2332      *
    2333      * @param self a list to be converted into a spreadlist
    2334      * @return a newly created SpreadList if this list is not null and its size is positive.
    2335      */
    2336     public static SpreadList spread(List self) {
    2337         return toSpreadList(self);
    2338     }
    2339
    2340     /***
    2341      * Returns the converted <code>SpreadList</code> of the given <code>self</code>.
    2342      * <p>
    2343      * This is the same method to <code>toSpreadList(Object[] self)</code>.
    2344      * <p>
    2345      * For examples, if there is defined a function like as
    2346      * <blockquote><pre>
    2347      *     def fn(a, b, c, d) { return a + b + c + d }
    2348      * </pre></blockquote>, then all of the following three have the same meaning.
    2349      * <blockquote><pre>
    2350      *     println fn(([1, 2, 3] as Object[]).spread(), 4)
    2351      *     println fn(*[1, 2, 3], 4)
    2352      *     println fn(1, 2, 3, 4)
    2353      * </pre></blockquote>
    2354      * <p>
    2355      * @param self an array of objects to be converted into a spreadlist
    2356      * @return a newly created SpreadList if this array is not null and its size is positive.
    2357      */
    2358     public static SpreadList spread(Object[] self) {
    2359         return toSpreadList(self);
    2360     }
    2361
    2362     /***
    2363      * Returns the converted <code>SpreadList</code> of the given <code>self</code>.
    2364      * <p>
    2365      * For examples, if there is defined a function like as
    2366      * <blockquote><pre>
    2367      *     def fn(a, b, c, d) { return a + b + c + d }
    2368      * </pre></blockquote>, then all of the following three have the same meaning.
    2369      * <blockquote><pre>
    2370      *     println fn(1, [2, 3].toSpreadList(), 4)
    2371      *     println fn(1, *[2, 3], 4)
    2372      *     println fn(1, 2, 3, 4)
    2373      * </pre></blockquote>
    2374      * <p>
    2375      * @param self a list to be converted into a spreadlist
    2376      * @return a newly created SpreadList if this list is not null and its size is positive.
    2377      */
    2378     public static SpreadList toSpreadList(List self) {
    2379         if (self == null)
    2380             throw new GroovyRuntimeException("Fail to convert Object[] to SpreadList, because it is null.");
    2381         else
    2382             return toSpreadList(self.toArray());
    2383     }
    2384
    2385     /***
    2386      * Returns the converted <code>SpreadList</code> of the given <code>self</code>.
    2387      * <p>
    2388      * For examples, if there is defined a function like as
    2389      * <blockquote><pre>
    2390      *     def fn(a, b, c, d) { return a + b + c + d }
    2391      * </pre></blockquote>, then all of the following three have the same meaning.
    2392      * <blockquote><pre>
    2393      *     println fn(([1, 2, 3] as Object[]).toSpreadList(), 4)
    2394      *     println fn(*[1, 2, 3], 4)
    2395      *     println fn(1, 2, 3, 4)
    2396      * </pre></blockquote>
    2397      * <p>
    2398      * @param self an array of objects to be converted into a spreadlist
    2399      * @return a newly created SpreadList if this array is not null and its size is positive.
    2400      */
    2401     public static SpreadList toSpreadList(Object[] self) {
    2402         if (self == null)
    2403             throw new GroovyRuntimeException("Fail to convert Object[] to SpreadList, because it is null.");
    2404         else if (self.length == 0)
    2405             throw new GroovyRuntimeException("Fail to convert Object[] to SpreadList, because its length is 0.");
    2406         else
    2407            return new SpreadList(self);
    2408     }
    2409
    2410     public static SpreadMap spread(Map self) {
    2411         return toSpreadMap(self);
    2412     }
    2413
    2414     /***
    2415      * Returns the converted <code>SpreadList</code> of the given <code>self</code>.
    2416      * <p>
    2417      * For examples, if there is defined a function like as
    2418      * <blockquote><pre>
    2419      *     def fn(a, b, c, d) { return a + b + c + d }
    2420      * </pre></blockquote>, then all of the following three have the same meaning.
    2421      * <blockquote><pre>
    2422      *     println fn(a:1, [b:2, c:3].toSpreadMap(), d:4)
    2423      *     println fn(a:1, *:[b:2, c:3], d:4)
    2424      *     println fn(a:1, b:2, c:3, d:4)
    2425      * </pre></blockquote>
    2426      * <p>
    2427      * @param self a list to be converted into a spreadlist
    2428      * @return a newly created SpreadList if this list is not null and its size is positive.
    2429      */
    2430     public static SpreadMap toSpreadMap(Map self) {
    2431         if (self == null)
    2432             throw new GroovyRuntimeException("Fail to convert Map to SpreadMap, because it is null.");
    2433         else
    2434             return new SpreadMap(self);
    2435     }
    2436
    2437     public static SpreadMap toSpreadMap(Object[] self) {
    2438         if (self == null)
    2439             throw new GroovyRuntimeException("Fail to convert Object[] to SpreadMap, because it is null.");
    2440         else if (self.length % 2 != 0)
    2441             throw new GroovyRuntimeException("Fail to convert Object[] to SpreadMap, because it's size is not even.");
    2442         else
    2443             return new SpreadMap(self);
    2444     }
    2445
    2446     /***
    2447      * Sorts the given collection into a sorted list.
    2448      *
    2449      * @param self the collection to be sorted
    2450      * @return the sorted collection as a List
    2451      */
    2452     public static List sort(Collection self) {
    2453         List answer = asList(self);
    2454         Collections.sort(answer, new NumberComparator());
    2455         return answer;
    2456     }
    2457
    2458     /***
    2459      * Avoids doing unnecessary work when sorting an already sorted set
    2460      *
    2461      * @param self
    2462      * @return the sorted set
    2463      */
    2464     public static SortedSet sort(SortedSet self) {
    2465         return self;
    2466     }
    2467
    2468     /***
    2469      * A convenience method for sorting a List
    2470      *
    2471      * @param self a List to be sorted
    2472      * @return the sorted List
    2473      */
    2474     public static List sort(List self) {
    2475         Collections.sort(self);
    2476         return self;
    2477     }
    2478
    2479     /***
    2480      * Removes the last item from the List. Using add() and pop()
    2481      * is similar to push and pop on a Stack.
    2482      *
    2483      * @param self a List
    2484      * @return the item removed from the List
    2485      * @throws NoSuchElementException if the list is empty and you try to pop() it.
    2486      */
    2487     public static Object pop(List self) {
    2488         if (self.isEmpty()) {
    2489             throw new NoSuchElementException("Cannot pop() an empty List");
    2490         }
    2491         return self.remove(self.size() - 1);
    2492     }
    2493
    2494     /***
    2495      * A convenience method for sorting a List with a specific comparator
    2496      *
    2497      * @param self       a List
    2498      * @param comparator a Comparator used for the comparison
    2499      * @return a sorted List
    2500      */
    2501     public static List sort(List self, Comparator comparator) {
    2502         Collections.sort(self, comparator);
    2503         return self;
    2504     }
    2505
    2506     /***
    2507      * A convenience method for sorting a Collection with a specific comparator
    2508      *
    2509      * @param self       a collection to be sorted
    2510      * @param comparator a Comparator used for the comparison
    2511      * @return a newly created sorted List
    2512      */
    2513     public static List sort(Collection self, Comparator comparator) {
    2514         return sort(asList(self), comparator);
    2515     }
    2516
    2517     /***
    2518      * A convenience method for sorting a List using a closure as a comparator
    2519      *
    2520      * @param self    a List
    2521      * @param closure a Closure used as a comparator
    2522      * @return a sorted List
    2523      */
    2524     public static List sort(List self, Closure closure) {
    2525         // use a comparator of one item or two
    2526         int params = closure.getMaximumNumberOfParameters();
    2527         if (params == 1) {
    2528             Collections.sort(self, new OrderBy(closure));
    2529         } else {
    2530             Collections.sort(self, new ClosureComparator(closure));
    2531         }
    2532         return self;
    2533     }
    2534
    2535     /***
    2536      * A convenience method for sorting a Collection using a closure as a comparator
    2537      *
    2538      * @param self    a Collection to be sorted
    2539      * @param closure a Closure used as a comparator
    2540      * @return a newly created sorted List
    2541      */
    2542     public static List sort(Collection self, Closure closure) {
    2543         return sort(asList(self), closure);
    2544     }
    2545
    2546     /***
    2547      * Converts the given collection into a List
    2548      *
    2549      * @param self a collection to be converted into a List
    2550      * @return a newly created List if this collection is not already a List
    2551      */
    2552     public static List asList(Collection self) {
    2553         if (self instanceof List) {
    2554             return (List) self;
    2555         } else {
    2556             return new ArrayList(self);
    2557         }
    2558     }
    2559
    2560     /***
    2561      * Reverses the list
    2562      *
    2563      * @param self a List
    2564      * @return a reversed List
    2565      */
    2566     public static List reverse(List self) {
    2567         int size = self.size();
    2568         List answer = new ArrayList(size);
    2569         ListIterator iter = self.listIterator(size);
    2570         while (iter.hasPrevious()) {
    2571             answer.add(iter.previous());
    2572         }
    2573         return answer;
    2574     }
    2575
    2576     /***
    2577      * Create a List as a union of both Collections
    2578      *
    2579      * @param left  the left Collection
    2580      * @param right the right Collection
    2581      * @return a List
    2582      */
    2583     public static List plus(Collection left, Collection right) {
    2584         List answer = new ArrayList(left.size() + right.size());
    2585         answer.addAll(left);
    2586         answer.addAll(right);
    2587         return answer;
    2588     }
    2589
    2590     /***
    2591      * Create a List as a union of a Collection and an Object
    2592      *
    2593      * @param left  a Collection
    2594      * @param right an object to append
    2595      * @return a List
    2596      */
    2597     public static List plus(Collection left, Object right) {
    2598         List answer = new ArrayList(left.size() + 1);
    2599         answer.addAll(left);
    2600         answer.add(right);
    2601         return answer;
    2602     }
    2603
    2604     /***
    2605      * Create a List composed of the same elements repeated a certain number of times.
    2606      *
    2607      * @param self   a Collection
    2608      * @param factor the number of times to append
    2609      * @return a List
    2610      */
    2611     public static List multiply(Collection self, Number factor) {
    2612         int size = factor.intValue();
    2613         List answer = new ArrayList(self.size() * size);
    2614         for (int i = 0; i < size; i++) {
    2615             answer.addAll(self);
    2616         }
    2617         return answer;
    2618     }
    2619
    2620     /***
    2621      * Create a List composed of the intersection of both collections
    2622      *
    2623      * @param left  a List
    2624      * @param right a Collection
    2625      * @return a List as an intersection of both collections
    2626      */
    2627     public static List intersect(List left, Collection right) {
    2628
    2629         if (left.size() == 0)
    2630             return new ArrayList();
    2631
    2632         boolean nlgnSort = sameType(new Collection[]{left, right});
    2633
    2634         ArrayList result = new ArrayList();
    2635         //creates the collection to look for values.
    2636         Collection pickFrom = (Collection) new TreeSet(new NumberComparator());
    2637         ((TreeSet) pickFrom).addAll(left);
    2638
    2639         for (Iterator iter = right.iterator(); iter.hasNext();) {
    2640             final Object o = iter.next();
    2641             if (pickFrom.contains(o))
    2642                 result.add(o);
    2643         }
    2644         return result;
    2645     }
    2646
    2647     /***
    2648      * Returns <code>true</code> if the intersection of two collenctions is empty.
    2649      *
    2650      * @param left       a Collection
    2651      * @param right      a Collection
    2652      * @return boolean   <code>true</code> if the intersection of two collenctions is empty, <code>false</code> otherwise.
    2653      */
    2654     public static boolean disjoint(Collection left, Collection right) {
    2655
    2656         if (left.size() == 0 || right.size() == 0)
    2657             return true;
    2658
    2659         boolean nlgnSort = sameType(new Collection[]{left, right});
    2660
    2661         Collection pickFrom = (Collection) new TreeSet(new NumberComparator());
    2662         ((TreeSet) pickFrom).addAll(right);
    2663
    2664         for (Iterator iter = left.iterator(); iter.hasNext();) {
    2665             final Object o = iter.next();
    2666             if (pickFrom.contains(o))
    2667                 return false;
    2668         }
    2669         return true;
    2670     }
    2671
    2672     // Default comparator for numbers of different types.
    2673     private static class NumberComparator implements Comparator {
    2674         public int compare(Object o1, Object o2) {
    2675              if (o1 instanceof Number && o2 instanceof Number) {
    2676                  BigDecimal x1 = new BigDecimal("" + o1);
    2677                  BigDecimal x2 = new BigDecimal("" + o2);
    2678                  return x1.compareTo(x2);
    2679             }
    2680             else if (o1.getClass() == o2.getClass() && o1 instanceof Comparable) {
    2681                 return ((Comparable) o1).compareTo((Comparable) o2);
    2682             }
    2683             else {
    2684                  int x1 = o1.hashCode();
    2685                  int x2 = o2.hashCode();
    2686                  return (x1 - x2);
    2687             }
    2688         }
    2689
    2690         public boolean equals(Object obj) {
    2691              return this.equals(obj);
    2692         }
    2693     }
    2694
    2695     /***
    2696      * Compare two Lists.
    2697      * If numbers exits in the Lists, then they are compared as numbers,
    2698      * for example 2 == 2L.
    2699      *
    2700      * @param  left      a List
    2701      * @param  right     a List
    2702      * @return boolean   <code>true</code> if two Lists equals, <code>false</code> otherwise.
    2703      */
    2704     public static boolean equals(final List left, final List right) {
    2705         if (left == null) {
    2706             return right == null;
    2707         } else if (right == null) {
    2708             return false;
    2709         } else if (left.size() != right.size()) {
    2710             return false;
    2711         } else {
    2712         final NumberComparator numberComparator = new NumberComparator();
    2713         final Iterator it1 = left.iterator(), it2 = right.iterator();
    2714
    2715             while (it1.hasNext()) {
    2716             final Object o1 = it1.next();
    2717             final Object o2 = it2.next();
    2718
    2719                 if (o1 == null) {
    2720                     if (o2 != null) return false;
    2721                 } else {
    2722                     if (o1 instanceof Number) {
    2723                         if (!(o2 instanceof Number && numberComparator.compare(o1, o2) == 0)) {
    2724                             return false;
    2725                         }
    2726                     } else {
    2727                         // Use this way of calling equals in case the elament is a List
    2728                         // or any other type which has an equals in DGM
    2729                         if (!((Boolean)InvokerHelper.invokeMethod(o1, "equals", new Object[]{o2})).booleanValue()) return false;
    2730                     }
    2731                 }
    2732             }
    2733
    2734             return true;
    2735         }
    2736     }
    2737
    2738     /***
    2739      * Create a List composed of the elements of the first list minus the elements of the collection
    2740      *
    2741      * @param self     a List
    2742      * @param removeMe a Collection of elements to remove
    2743      * @return a List with the common elements removed
    2744      */
    2745     public static List minus(List self, Collection removeMe) {
    2746
    2747         if (self.size() == 0)
    2748             return new ArrayList();
    2749
    2750         boolean nlgnSort = sameType(new Collection[]{self, removeMe});
    2751
    2752         //we can't use the same tactic as for intersection
    2753         //since AbstractCollection only does a remove on the first
    2754         //element it encounter.
    2755
    2756         Comparator numberComparator = new NumberComparator();
    2757
    2758         if (nlgnSort && (self.get(0) instanceof Comparable)) {
    2759             //n*log(n) version
    2760             Set answer = null;
    2761             if (Number.class.isInstance(self.get(0))) {
    2762                 BigDecimal zero = new BigDecimal("0.0");
    2763                 answer = new TreeSet(numberComparator);
    2764                 answer.addAll(self);
    2765                 for (Iterator it = self.iterator(); it.hasNext(); ) {
    2766                     Object o = it.next();
    2767                     if (Number.class.isInstance(o)) {
    2768                         for (Iterator it2 = removeMe.iterator(); it2.hasNext(); ) {
    2769                             Object o2 = it2.next();
    2770                             if (Number.class.isInstance(o2)) {
    2771                                 if (numberComparator.compare(o, o2) == 0)
    2772                                     answer.remove(o);
    2773                             }
    2774                         }
    2775                     }
    2776                     else {
    2777                         if (removeMe.contains(o))
    2778                             answer.remove(o);
    2779                     }
    2780                 }
    2781             }
    2782             else {
    2783                 answer = new TreeSet(numberComparator);
    2784                 answer.addAll(self);
    2785                 answer.removeAll(removeMe);
    2786             }
    2787
    2788             List ansList = new ArrayList();
    2789             for (Iterator it = self.iterator(); it.hasNext(); ) {
    2790                 Object o = it.next();
    2791                 if (answer.contains(o))
    2792                     ansList.add(o);
    2793             }
    2794             return ansList;
    2795         } else {
    2796             //n*n version
    2797             List tmpAnswer = new LinkedList(self);
    2798             for (Iterator iter = tmpAnswer.iterator(); iter.hasNext();) {
    2799                 Object element = iter.next();
    2800                 //boolean removeElement = false;
    2801                 for (Iterator iterator = removeMe.iterator(); iterator.hasNext();) {
    2802                     Object elt = iterator.next();
    2803                     if (elt != null && numberComparator.compare(element, elt) == 0) {
    2804                         iter.remove();
    2805                     }
    2806                 }
    2807             }
    2808
    2809             //remove duplicates
    2810             //can't use treeset since the base classes are different
    2811             return new ArrayList(tmpAnswer);
    2812         }
    2813     }
    2814
    2815     /***
    2816      * Flatten a list
    2817      *
    2818      * @param self a List
    2819      * @return a flattened List
    2820      */
    2821     public static List flatten(List self) {
    2822         return new ArrayList(flatten(self, new LinkedList()));
    2823     }
    2824
    2825     /***
    2826      * Iterate over each element of the list in the reverse order.
    2827      *
    2828      * @param self    a List
    2829      * @param closure a closure
    2830      */
    2831     public static void reverseEach(List self, Closure closure) {
    2832         List reversed = reverse(self);
    2833         for (Iterator iter = reversed.iterator(); iter.hasNext();) {
    2834             closure.call(iter.next());
    2835         }
    2836     }
    2837
    2838     private static List flatten(Collection elements, List addTo) {
    2839         Iterator iter = elements.iterator();
    2840         while (iter.hasNext()) {
    2841             Object element = iter.next();
    2842             if (element instanceof Collection) {
    2843                 flatten((Collection) element, addTo);
    2844             } else if (element instanceof Map) {
    2845                 flatten(((Map) element).values(), addTo);
    2846             } else {
    2847                 addTo.add(element);
    2848             }
    2849         }
    2850         return addTo;
    2851     }
    2852
    2853     /***
    2854      * Overloads the left shift operator to provide an easy way to append objects to a list
    2855      *
    2856      * @param self  a Collection
    2857      * @param value an Object to be added to the collection.
    2858      * @return a Collection with an Object added to it.
    2859      */
    2860     public static Collection leftShift(Collection self, Object value) {
    2861         self.add(value);
    2862         return self;
    2863     }
    2864
    2865     /***
    2866      * Overloads the left shift operator to provide an easy way to append multiple
    2867      * objects as string representations to a String
    2868      *
    2869      * @param self  a String
    2870      * @param value an Obect
    2871      * @return a StringBuffer
    2872      */
    2873     public static StringBuffer leftShift(String self, Object value) {
    2874         return new StringBuffer(self).append(value);
    2875     }
    2876
    2877     protected static StringWriter createStringWriter(String self) {
    2878         StringWriter answer = new StringWriter();
    2879         answer.write(self);
    2880         return answer;
    2881     }
    2882
    2883     protected static StringBufferWriter createStringBufferWriter(StringBuffer self) {
    2884         return new StringBufferWriter(self);
    2885     }
    2886
    2887     /***
    2888      * Overloads the left shift operator to provide an easy way to append multiple
    2889      * objects as string representations to a StringBuffer
    2890      *
    2891      * @param self  a StringBuffer
    2892      * @param value a value to append
    2893      * @return a StringBuffer
    2894      */
    2895     public static StringBuffer leftShift(StringBuffer self, Object value) {
    2896         self.append(value);
    2897         return self;
    2898     }
    2899
    2900     /***
    2901      * Overloads the left shift operator to provide an append mechanism to add things to a writer
    2902      *
    2903      * @param self  a Writer
    2904      * @param value a value to append
    2905      * @return a StringWriter
    2906      */
    2907     public static Writer leftShift(Writer self, Object value) throws IOException {
    2908         InvokerHelper.write(self, value);
    2909         return self;
    2910     }
    2911
    2912     /***
    2913      * Implementation of the left shift operator for integral types.  Non integral
    2914      * Number types throw UnsupportedOperationException.
    2915      */
    2916     public static Number leftShift(Number left, Number right) {
    2917         return NumberMath.leftShift(left, right);
    2918     }
    2919
    2920     /***
    2921      * Implementation of the right shift operator for integral types.  Non integral
    2922      * Number types throw UnsupportedOperationException.
    2923      */
    2924     public static Number rightShift(Number left, Number right) {
    2925         return NumberMath.rightShift(left, right);
    2926     }
    2927
    2928     /***
    2929      * Implementation of the right shift (unsigned) operator for integral types.  Non integral
    2930      * Number types throw UnsupportedOperationException.
    2931      */
    2932     public static Number rightShiftUnsigned(Number left, Number right) {
    2933         return NumberMath.rightShiftUnsigned(left, right);
    2934     }
    2935
    2936     /***
    2937      * A helper method so that dynamic dispatch of the writer.write(object) method
    2938      * will always use the more efficient Writable.writeTo(writer) mechanism if the
    2939      * object implements the Writable interface.
    2940      *
    2941      * @param self     a Writer
    2942      * @param writable an object implementing the Writable interface
    2943      */
    2944     public static void write(Writer self, Writable writable) throws IOException {
    2945         writable.writeTo(self);
    2946     }
    2947
    2948     /***
    2949      * Overloads the left shift operator to provide an append mechanism to add things to a stream
    2950      *
    2951      * @param self  an OutputStream
    2952      * @param value a value to append
    2953      * @return a Writer
    2954      */
    2955     public static Writer leftShift(OutputStream self, Object value) throws IOException {
    2956         OutputStreamWriter writer = new FlushingStreamWriter(self);
    2957         leftShift(writer, value);
    2958         return writer;
    2959     }
    2960
    2961     /***
    2962      * Pipe an inputstream into an outputstream for efficient stream copying.
    2963      *
    2964      * @param self stream on which to write
    2965      * @param in stream to read from
    2966      * @return the outputstream itself
    2967      * @throws IOException
    2968      */
    2969     public static OutputStream leftShift(OutputStream self, InputStream in) throws IOException {
    2970         byte[] buf = new byte[1024];
    2971         while (true) {
    2972             int count = in.read(buf,0,buf.length);
    2973             if (count == -1) break;
    2974             if (count == 0) {
    2975                 Thread.yield();
    2976                 continue;
    2977             }
    2978             self.write(buf, 0, count);
    2979         }
    2980         self.flush();
    2981         return self;
    2982     }
    2983
    2984     /***
    2985      * Overloads the left shift operator to provide an append mechanism to add bytes to a stream
    2986      *
    2987      * @param self  an OutputStream
    2988      * @param value a value to append
    2989      * @return an OutputStream
    2990      */
    2991     public static OutputStream leftShift(OutputStream self, byte[] value) throws IOException {
    2992         self.write(value);
    2993         self.flush();
    2994         return self;
    2995     }
    2996
    2997     private static boolean sameType(Collection[] cols) {
    2998         List all = new LinkedList();
    2999         for (int i = 0; i < cols.length; i++) {
    3000             all.addAll(cols[i]);
    3001         }
    3002         if (all.size() == 0)
    3003             return true;
    3004
    3005         Object first = all.get(0);
    3006
    3007         //trying to determine the base class of the collections
    3008         //special case for Numbers
    3009         Class baseClass;
    3010         if (first instanceof Number) {
    3011             baseClass = Number.class;
    3012         } else {
    3013             baseClass = first.getClass();
    3014         }
    3015
    3016         for (int i = 0; i < cols.length; i++) {
    3017             for (Iterator iter = cols[i].iterator(); iter.hasNext();) {
    3018                 if (!baseClass.isInstance(iter.next())) {
    3019                     return false;
    3020                 }
    3021             }
    3022         }
    3023         return true;
    3024     }
    3025
    3026     // Primitive type array methods
    3027     //-------------------------------------------------------------------------
    3028
    3029     public static Object getAt(byte[] array, int idx) {
    3030         return primitiveArrayGet(array, idx);
    3031     }
    3032
    3033     public static Object getAt(char[] array, int idx) {
    3034         return primitiveArrayGet(array, idx);
    3035     }
    3036
    3037     public static Object getAt(short[] array, int idx) {
    3038         return primitiveArrayGet(array, idx);
    3039     }
    3040
    3041     public static Object getAt(int[] array, int idx) {
    3042         return primitiveArrayGet(array, idx);
    3043     }
    3044
    3045     public static Object getAt(long[] array, int idx) {
    3046         return primitiveArrayGet(array, idx);
    3047     }
    3048
    3049     public static Object getAt(float[] array, int idx) {
    3050         return primitiveArrayGet(array, idx);
    3051     }
    3052
    3053     public static Object getAt(double[] array, int idx) {
    3054         return primitiveArrayGet(array, idx);
    3055     }
    3056
    3057     public static Object getAt(boolean[] array, int idx) {
    3058         return primitiveArrayGet(array, idx);
    3059     }
    3060
    3061     public static Object getAt(byte[] array, Range range) {
    3062         return primitiveArrayGet(array, range);
    3063     }
    3064
    3065     public static Object getAt(char[] array, Range range) {
    3066         return primitiveArrayGet(array, range);
    3067     }
    3068
    3069     public static Object getAt(short[] array, Range range) {
    3070         return primitiveArrayGet(array, range);
    3071     }
    3072
    3073     public static Object getAt(int[] array, Range range) {
    3074         return primitiveArrayGet(array, range);
    3075     }
    3076
    3077     public static Object getAt(long[] array, Range range) {
    3078         return primitiveArrayGet(array, range);
    3079     }
    3080
    3081     public static Object getAt(float[] array, Range range) {
    3082         return primitiveArrayGet(array, range);
    3083     }
    3084
    3085     public static Object getAt(double[] array, Range range) {
    3086         return primitiveArrayGet(array, range);
    3087     }
    3088
    3089     public static Object getAt(boolean[] array, Range range) {
    3090         return primitiveArrayGet(array, range);
    3091     }
    3092
    3093     public static Object getAt(byte[] array, IntRange range) {
    3094         return primitiveArrayGet(array, range);
    3095     }
    3096
    3097     public static Object getAt(char[] array, IntRange range) {
    3098         return primitiveArrayGet(array, range);
    3099     }
    3100
    3101     public static Object getAt(short[] array, IntRange range) {
    3102         return primitiveArrayGet(array, range);
    3103     }
    3104
    3105     public static Object getAt(int[] array, IntRange range) {
    3106         return primitiveArrayGet(array, range);
    3107     }
    3108
    3109     public static Object getAt(long[] array, IntRange range) {
    3110         return primitiveArrayGet(array, range);
    3111     }
    3112
    3113     public static Object getAt(float[] array, IntRange range) {
    3114         return primitiveArrayGet(array, range);
    3115     }
    3116
    3117     public static Object getAt(double[] array, IntRange range) {
    3118         return primitiveArrayGet(array, range);
    3119     }
    3120
    3121     public static Object getAt(boolean[] array, IntRange range) {
    3122         return primitiveArrayGet(array, range);
    3123     }
    3124
    3125     public static Object getAt(byte[] array, ObjectRange range) {
    3126         return primitiveArrayGet(array, range);
    3127     }
    3128
    3129     public static Object getAt(char[] array, ObjectRange range) {
    3130         return primitiveArrayGet(array, range);
    3131     }
    3132
    3133     public static Object getAt(short[] array, ObjectRange range) {
    3134         return primitiveArrayGet(array, range);
    3135     }
    3136
    3137     public static Object getAt(int[] array, ObjectRange range) {
    3138         return primitiveArrayGet(array, range);
    3139     }
    3140
    3141     public static Object getAt(long[] array, ObjectRange range) {
    3142         return primitiveArrayGet(array, range);
    3143     }
    3144
    3145     public static Object getAt(float[] array, ObjectRange range) {
    3146         return primitiveArrayGet(array, range);
    3147     }
    3148
    3149     public static Object getAt(double[] array, ObjectRange range) {
    3150         return primitiveArrayGet(array, range);
    3151     }
    3152
    3153     public static Object getAt(boolean[] array, ObjectRange range) {
    3154         return primitiveArrayGet(array, range);
    3155     }
    3156
    3157     public static Object getAt(byte[] array, Collection indices) {
    3158         return primitiveArrayGet(array, indices);
    3159     }
    3160
    3161     public static Object getAt(char[] array, Collection indices) {
    3162         return primitiveArrayGet(array, indices);
    3163     }
    3164
    3165     public static Object getAt(short[] array, Collection indices) {
    3166         return primitiveArrayGet(array, indices);
    3167     }
    3168
    3169     public static Object getAt(int[] array, Collection indices) {
    3170         return primitiveArrayGet(array, indices);
    3171     }
    3172
    3173     public static Object getAt(long[] array, Collection indices) {
    3174         return primitiveArrayGet(array, indices);
    3175     }
    3176
    3177     public static Object getAt(float[] array, Collection indices) {
    3178         return primitiveArrayGet(array, indices);
    3179     }
    3180
    3181     public static Object getAt(double[] array, Collection indices) {
    3182         return primitiveArrayGet(array, indices);
    3183     }
    3184
    3185     public static Object getAt(boolean[] array, Collection indices) {
    3186         return primitiveArrayGet(array, indices);
    3187     }
    3188
    3189     public static void putAt(boolean[] array, int idx, Boolean newValue) {
    3190         primitiveArrayPut(array, idx, newValue);
    3191     }
    3192
    3193     public static void putAt(byte[] array, int idx, Object newValue) {
    3194         if (!(newValue instanceof Byte)) {
    3195             Number n = (Number) newValue;
    3196             newValue = new Byte(n.byteValue());
    3197         }
    3198         primitiveArrayPut(array, idx, newValue);
    3199     }
    3200
    3201     public static void putAt(char[] array, int idx, Object newValue) {
    3202         if (newValue instanceof String) {
    3203             String s = (String) newValue;
    3204             if (s.length()!=1) throw new IllegalArgumentException("String of length 1 expected but got a bigger one");
    3205             char c = s.charAt(0);
    3206             newValue = new Character(c);
    3207         }
    3208         primitiveArrayPut(array, idx, newValue);
    3209     }
    3210
    3211     public static void putAt(short[] array, int idx, Object newValue) {
    3212         if (!(newValue instanceof Short)) {
    3213             Number n = (Number) newValue;
    3214             newValue = new Short(n.shortValue());
    3215         }
    3216         primitiveArrayPut(array, idx, newValue);
    3217     }
    3218
    3219     public static void putAt(int[] array, int idx, Object newValue) {
    3220         if (!(newValue instanceof Integer)) {
    3221             Number n = (Number) newValue;
    3222             newValue = new Integer(n.intValue());
    3223         }
    3224         primitiveArrayPut(array, idx, newValue);
    3225     }
    3226
    3227     public static void putAt(long[] array, int idx, Object newValue) {
    3228         if (!(newValue instanceof Long)) {
    3229             Number n = (Number) newValue;
    3230             newValue = new Long(n.longValue());
    3231         }
    3232         primitiveArrayPut(array, idx, newValue);
    3233     }
    3234
    3235     public static void putAt(float[] array, int idx, Object newValue) {
    3236         if (!(newValue instanceof Float)) {
    3237             Number n = (Number) newValue;
    3238             newValue = new Float(n.floatValue());
    3239         }
    3240         primitiveArrayPut(array, idx, newValue);
    3241     }
    3242
    3243     public static void putAt(double[] array, int idx, Object newValue) {
    3244         if (!(newValue instanceof Double)) {
    3245             Number n = (Number) newValue;
    3246             newValue = new Double(n.doubleValue());
    3247         }
    3248         primitiveArrayPut(array, idx, newValue);
    3249     }
    3250
    3251     public static int size(byte[] array) {
    3252         return Array.getLength(array);
    3253     }
    3254
    3255     public static int size(char[] array) {
    3256         return Array.getLength(array);
    3257     }
    3258
    3259     public static int size(short[] array) {
    3260         return Array.getLength(array);
    3261     }
    3262
    3263     public static int size(int[] array) {
    3264         return Array.getLength(array);
    3265     }
    3266
    3267     public static int size(long[] array) {
    3268         return Array.getLength(array);
    3269     }
    3270
    3271     public static int size(float[] array) {
    3272         return Array.getLength(array);
    3273     }
    3274
    3275     public static int size(double[] array) {
    3276         return Array.getLength(array);
    3277     }
    3278
    3279     public static List toList(byte[] array) {
    3280         return InvokerHelper.primitiveArrayToList(array);
    3281     }
    3282
    3283     public static List toList(char[] array) {
    3284         return InvokerHelper.primitiveArrayToList(array);
    3285     }
    3286
    3287     public static List toList(short[] array) {
    3288         return InvokerHelper.primitiveArrayToList(array);
    3289     }
    3290
    3291     public static List toList(int[] array) {
    3292         return InvokerHelper.primitiveArrayToList(array);
    3293     }
    3294
    3295     public static List toList(long[] array) {
    3296         return InvokerHelper.primitiveArrayToList(array);
    3297     }
    3298
    3299     public static List toList(float[] array) {
    3300         return InvokerHelper.primitiveArrayToList(array);
    3301     }
    3302
    3303     public static List toList(double[] array) {
    3304         return InvokerHelper.primitiveArrayToList(array);
    3305     }
    3306
    3307     private static final char[] tTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
    3308
    3309     public static Writable encodeBase64(final Byte[] data) {
    3310         return encodeBase64(InvokerHelper.convertToByteArray(data));
    3311     }
    3312
    3313     /***
    3314      * Produce a Writable object which writes the base64 encoding of the byte array
    3315      * Calling toString() on the result rerurns the encoding as a String
    3316      *
    3317      * @param data byte array to be encoded
    3318      * @return object which will write the base64 encoding of the byte array
    3319      */
    3320     public static Writable encodeBase64(final byte[] data) {
    3321         return new Writable() {
    3322             public Writer writeTo(final Writer writer) throws IOException {
    3323                 int charCount = 0;
    3324                 final int dLimit = (data.length / 3) * 3;
    3325
    3326                 for (int dIndex = 0; dIndex != dLimit; dIndex += 3) {
    3327                     int d = ((data[dIndex] & 0XFF) << 16) | ((data[dIndex + 1] & 0XFF) << 8) | (data[dIndex + 2] & 0XFF);
    3328
    3329                     writer.write(tTable[d >> 18]);
    3330                     writer.write(tTable[(d >> 12) & 0X3F]);
    3331                     writer.write(tTable[(d >> 6) & 0X3F]);
    3332                     writer.write(tTable[d & 0X3F]);
    3333
    3334                     if (++charCount == 18) {
    3335                         writer.write('\n');
    3336                         charCount = 0;
    3337                     }
    3338                 }
    3339
    3340                 if (dLimit != data.length) {
    3341                     int d = (data[dLimit] & 0XFF) << 16;
    3342
    3343                     if (dLimit + 1 != data.length) {
    3344                         d |= (data[dLimit + 1] & 0XFF) << 8;
    3345                     }
    3346
    3347                     writer.write(tTable[d >> 18]);
    3348                     writer.write(tTable[(d >> 12) & 0X3F]);
    3349                     writer.write((dLimit + 1 < data.length) ? tTable[(d >> 6) & 0X3F] : '=');
    3350                     writer.write('=');
    3351                 }
    3352
    3353                 return writer;
    3354             }
    3355
    3356             public String toString() {
    3357                 StringWriter buffer = new StringWriter();
    3358
    3359                 try {
    3360                     writeTo(buffer);
    3361                 } catch (IOException e) {
    3362                     throw new RuntimeException(e); // TODO: change this exception type
    3363                 }
    3364
    3365                 return buffer.toString();
    3366             }
    3367         };
    3368     }
    3369
    3370     private static final byte[] translateTable = (
    3371             //
    3372             "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
    3373             //                    \t    \n                \r
    3374             + "\u0042\u0042\u0041\u0041\u0042\u0042\u0041\u0042"
    3375             //
    3376             + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
    3377             //
    3378             + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
    3379             //        sp    !     "     #     $     %     &     '
    3380             + "\u0041\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
    3381             //         (    )     *     +     ,     -     .     /
    3382             + "\u0042\u0042\u0042\u003E\u0042\u0042\u0042\u003F"
    3383             //         0    1     2     3     4     5     6     7
    3384             + "\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B"
    3385             //         8    9     :     ;     <     =     >     ?
    3386             + "\u003C\u003D\u0042\u0042\u0042\u0040\u0042\u0042"
    3387             //         @    A     B     C     D     E     F     G
    3388             + "\u0042\u0000\u0001\u0002\u0003\u0004\u0005\u0006"
    3389             //         H    I   J K   L     M   N   O
    3390             + "\u0007\u0008\t\n\u000B\u000C\r\u000E"
    3391             //         P    Q     R     S     T     U     V    W
    3392             + "\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016"
    3393             //         X    Y     Z     [     \     ]     ^    _
    3394             + "\u0017\u0018\u0019\u0042\u0042\u0042\u0042\u0042"
    3395             //         '    a     b     c     d     e     f     g
    3396             + "\u0042\u001A\u001B\u001C\u001D\u001E\u001F\u0020"
    3397             //        h   i   j     k     l     m     n     o    p
    3398             + "\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028"
    3399             //        p     q     r     s     t     u     v     w
    3400             + "\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030"
    3401             //        x     y     z
    3402             + "\u0031\u0032\u0033").getBytes();
    3403
    3404     /***
    3405      * Decode the Sting from base64 into a byte array
    3406      *
    3407      * @param value the string to be decoded
    3408      * @return the decoded bytes as an array
    3409      */
    3410     public static byte[] decodeBase64(final String value) {
    3411         int byteShift = 4;
    3412         int tmp = 0;
    3413         boolean done = false;
    3414         final StringBuffer buffer = new StringBuffer();
    3415
    3416         for (int i = 0; i != value.length(); i++) {
    3417             final char c = value.charAt(i);
    3418             final int sixBit = (c < 123) ? translateTable[c] : 66;
    3419
    3420             if (sixBit < 64) {
    3421                 if (done) throw new RuntimeException("= character not at end of base64 value"); // TODO: change this exception type
    3422
    3423                 tmp = (tmp << 6) | sixBit;
    3424
    3425                 if (byteShift-- != 4) {
    3426                     buffer.append((char) ((tmp >> (byteShift * 2)) & 0XFF));
    3427                 }
    3428
    3429             } else if (sixBit == 64) {
    3430
    3431                 byteShift--;
    3432                 done = true;
    3433
    3434             } else if (sixBit == 66) {
    3435                 // RFC 2045 says that I'm allowed to take the presence of
    3436                 // these characters as evedence of data corruption
    3437                 // So I will
    3438                 throw new RuntimeException("bad character in base64 value"); // TODO: change this exception type
    3439             }
    3440
    3441             if (byteShift == 0) byteShift = 4;
    3442         }
    3443
    3444         try {
    3445             return buffer.toString().getBytes("ISO-8859-1");
    3446         } catch (UnsupportedEncodingException e) {
    3447             throw new RuntimeException("Base 64 decode produced byte values > 255"); // TODO: change this exception type
    3448         }
    3449     }
    3450
    3451     /***
    3452      * Implements the getAt(int) method for primitve type arrays
    3453      */
    3454     protected static Object primitiveArrayGet(Object array, int idx) {
    3455         return Array.get(array, normaliseIndex(idx, Array.getLength(array)));
    3456     }
    3457
    3458     /***
    3459      * Implements the getAt(Range) method for primitve type arrays
    3460      */
    3461     protected static List primitiveArrayGet(Object array, Range range) {
    3462         List answer = new ArrayList();
    3463         for (Iterator iter = range.iterator(); iter.hasNext();) {
    3464             int idx = InvokerHelper.asInt(iter.next());
    3465             answer.add(primitiveArrayGet(array, idx));
    3466         }
    3467         return answer;
    3468     }
    3469
    3470     /***
    3471      * Implements the getAt(Collection) method for primitve type arrays
    3472      */
    3473     protected static List primitiveArrayGet(Object self, Collection indices) {
    3474         List answer = new ArrayList();
    3475         for (Iterator iter = indices.iterator(); iter.hasNext();) {
    3476             Object value = iter.next();
    3477             if (value instanceof Range) {
    3478                 answer.addAll(primitiveArrayGet(self, (Range) value));
    3479             } else if (value instanceof List) {
    3480                 answer.addAll(primitiveArrayGet(self, (List) value));
    3481             } else {
    3482                 int idx = InvokerHelper.asInt(value);
    3483                 answer.add(primitiveArrayGet(self, idx));
    3484             }
    3485         }
    3486         return answer;
    3487     }
    3488
    3489     /***
    3490      * Implements the set(int idx) method for primitve type arrays
    3491      */
    3492     protected static void primitiveArrayPut(Object array, int idx, Object newValue) {
    3493         Array.set(array, normaliseIndex(idx, Array.getLength(array)), newValue);
    3494     }
    3495
    3496     // String methods
    3497     //-------------------------------------------------------------------------
    3498
    3499     /***
    3500      * Converts the given string into a Character object
    3501      * using the first character in the string
    3502      *
    3503      * @param self a String
    3504      * @return the first Character
    3505      */
    3506     public static Character toCharacter(String self) {
    3507         /*** @todo use cache? */
    3508         return new Character(self.charAt(0));
    3509     }
    3510
    3511     /***
    3512      * Converts the given string into a Boolean object
    3513      * If the trimmed string is "true", "y" or "1" (ignoring case)
    3514      * then the result is true othewrwise it is false
    3515      *
    3516      * @param self a String
    3517      * @return The Boolean value
    3518      */
    3519     public static Boolean toBoolean(String self) {
    3520     final String trimmed = self.trim();
    3521
    3522         if ("true".equalsIgnoreCase(trimmed) || "y".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) {
    3523             return Boolean.TRUE;
    3524         } else {
    3525             return Boolean.FALSE;
    3526         }
    3527     }
    3528
    3529     /***
    3530      * Tokenize a String
    3531      *
    3532      * @param self  a String
    3533      * @param token the delimiter
    3534      * @return a List of tokens
    3535      */
    3536     public static List tokenize(String self, String token) {
    3537         return InvokerHelper.asList(new StringTokenizer(self, token));
    3538     }
    3539
    3540     /***
    3541      * Tokenize a String (with a whitespace as delimiter)
    3542      *
    3543      * @param self a String
    3544      * @return a List of tokens
    3545      */
    3546     public static List tokenize(String self) {
    3547         return InvokerHelper.asList(new StringTokenizer(self));
    3548     }
    3549
    3550     /***
    3551      * Appends a String
    3552      *
    3553      * @param left  a String
    3554      * @param value a String
    3555      * @return a String
    3556      */
    3557     public static String plus(String left, Object value) {
    3558         //return left + value;
    3559         return left + toString(value);
    3560     }
    3561
    3562     /***
    3563      * Appends a String
    3564      *
    3565      * @param value a Number
    3566      * @param right a String
    3567      * @return a String
    3568      */
    3569     public static String plus(Number value, String right) {
    3570         return toString(value) + right;
    3571     }
    3572
    3573     /***
    3574      * Appends a String
    3575      *
    3576      * @param left  a StringBuffer
    3577      * @param value a String
    3578      * @return a String
    3579      */
    3580     public static String plus(StringBuffer left, String value) {
    3581         return left + value;
    3582     }
    3583
    3584
    3585     /***
    3586      * Remove a part of a String
    3587      *
    3588      * @param left  a String
    3589      * @param value a String part to remove
    3590      * @return a String minus the part to be removed
    3591      */
    3592     public static String minus(String left, Object value) {
    3593         String text = toString(value);
    3594         return left.replaceFirst(text, "");
    3595     }
    3596
    3597     /***
    3598      * Provide an implementation of contains() like Collection to make Strings more polymorphic
    3599      * This method is not required on JDK 1.5 onwards
    3600      *
    3601      * @param self a String
    3602      * @param text a String to look for
    3603      * @return true if this string contains the given text
    3604      */
    3605     public static boolean contains(String self, String text) {
    3606         int idx = self.indexOf(text);
    3607         return idx >= 0;
    3608     }
    3609
    3610     /***
    3611      * Count the number of occurencies of a substring
    3612      *
    3613      * @param self a String
    3614      * @param text a substring
    3615      * @return the number of occurrencies of the given string inside this String
    3616      */
    3617     public static int count(String self, String text) {
    3618         int answer = 0;
    3619         for (int idx = 0; true; idx++) {
    3620             idx = self.indexOf(text, idx);
    3621             if (idx >= 0) {
    3622                 ++answer;
    3623             } else {
    3624                 break;
    3625             }
    3626         }
    3627         return answer;
    3628     }
    3629
    3630     /***
    3631      * This method is called by the ++ operator for the class String.
    3632      * It increments the last character in the given string. If the
    3633      * character in the string is Character.MAX_VALUE a Character.MIN_VALUE
    3634      * will be appended. The empty string is incremented to a string
    3635      * consisting of the character Character.MIN_VALUE.
    3636      *
    3637      * @param self a String
    3638      * @return an incremented String
    3639      */
    3640     public static String next(String self) {
    3641         StringBuffer buffer = new StringBuffer(self);
    3642         if (buffer.length()==0) {
    3643             buffer.append(Character.MIN_VALUE);
    3644         } else {
    3645             char last = buffer.charAt(buffer.length()-1);
    3646             if (last==Character.MAX_VALUE) {
    3647                 buffer.append(Character.MIN_VALUE);
    3648             } else {
    3649                 char next = last;
    3650                 next++;
    3651                 buffer.setCharAt(buffer.length()-1,next);
    3652             }
    3653         }
    3654         return buffer.toString();
    3655     }
    3656
    3657     /***
    3658      * This method is called by the -- operator for the class String.
    3659      * It decrements the last character in the given string. If the
    3660      * character in the string is Character.MIN_VALUE it will be deleted.
    3661      * The empty string can't be decremented.
    3662      *
    3663      * @param self a String
    3664      * @return a String with a decremented digit at the end
    3665      */
    3666     public static String previous(String self) {
    3667        StringBuffer buffer = new StringBuffer(self);
    3668        if (buffer.length()==0) throw new IllegalArgumentException("the string is empty");
    3669        char last = buffer.charAt(buffer.length()-1);
    3670        if (last==Character.MIN_VALUE) {
    3671            buffer.deleteCharAt(buffer.length()-1);
    3672        } else {
    3673             char next = last;
    3674             next--;
    3675             buffer.setCharAt(buffer.length()-1,next);
    3676        }
    3677        return buffer.toString();
    3678     }
    3679
    3680     /***
    3681      * Executes the given string as a command line process. For more control
    3682      * over the process mechanism in JDK 1.5 you can use java.lang.ProcessBuilder.
    3683      *
    3684      * @param self a command line String
    3685      * @return the Process which has just started for this command line string
    3686      */
    3687     public static Process execute(String self) throws IOException {
    3688         return Runtime.getRuntime().exec(self);
    3689     }
    3690
    3691     /***
    3692      * Executes the command specified by the <code>String</code> array that is the parameter.
    3693      * The first item in the array is the command the others are the parameters. For more
    3694      * control over the process mechanism in JDK 1.5 you can use
    3695      * <code>java.lang.ProcessBuilder</code>.
    3696      *
    3697      * @param commandArray an array of <code>String<code> containing the command name and
    3698      * parameters as separate items in the array.
    3699      * @return the Process which has just started for this command line string.
    3700      */
    3701     public static Process execute(final String[] commandArray) throws IOException {
    3702         return Runtime.getRuntime().exec(commandArray) ;
    3703     }
    3704
    3705     /***
    3706      * Executes the command specified by the <code>self</code> with environments <code>envp</code>
    3707      * under the working directory <code>dir</code>.
    3708      * For more control over the process mechanism in JDK 1.5 you can use <code>java.lang.ProcessBuilder</code>.
    3709      *
    3710      * @param   self      a command line String to be executed.
    3711      * @param   envp      an array of Strings, each element of which
    3712      *                    has environment variable settings in the format
    3713      *                    <i>name</i>=<i>value</i>, or
    3714      *                    <tt>null</tt> if the subprocess should inherit
    3715      *                    the environment of the current process.
    3716      * @param   dir       the working directory of the subprocess, or
    3717      *                    <tt>null</tt> if the subprocess should inherit
    3718      *                    the working directory of the current process.
    3719      * @return   the Process which has just started for this command line string.
    3720      *
    3721      */
    3722     public static Process execute(String self,  final String[] envp, File dir) throws IOException {
    3723         return Runtime.getRuntime().exec(self, envp, dir) ;
    3724     }
    3725
    3726     /***
    3727      * Executes the command specified by the <code>String</code> list that is the parameter.
    3728      * The first item in the array is the command the others are the parameters. All entries
    3729      * must be <code>String</code>s.  For more control over the process mechanism in JDK 1.5 you
    3730      * can use <code>java.lang.ProcessBuilder</code>.
    3731      *
    3732      * @param commandList a list of <code>String<code> containing the command name and
    3733      * parameters as separate items in the list.
    3734      * @return the Process which has just started for this command line string.
    3735      */
    3736     public static Process execute(final List commandList) throws IOException {
    3737       final String[] commandArray = new String[commandList.size()] ;
    3738       Iterator it = commandList.iterator();
    3739       for (int i = 0; it.hasNext(); ++i) {
    3740           commandArray[i] = it.next().toString();
    3741       }
    3742       return execute(commandArray) ;
    3743     }
    3744
    3745     /***
    3746      * Executes the command specified by the <code>self</code> with environments <code>envp</code>
    3747      * under the working directory <code>dir</code>.
    3748      * For more control over the process mechanism in JDK 1.5 you can use <code>java.lang.ProcessBuilder</code>.
    3749      *
    3750      * @param   self      a command line String to be executed.
    3751      * @param   envp      a List of Strings, each member of which
    3752      *                    has environment variable settings in the format
    3753      *                    <i>name</i>=<i>value</i>, or
    3754      *                    <tt>null</tt> if the subprocess should inherit
    3755      *                    the environment of the current process.
    3756      * @param   dir       the working directory of the subprocess, or
    3757      *                    <tt>null</tt> if the subprocess should inherit
    3758      *                    the working directory of the current process.
    3759      * @return   the Process which has just started for this command line string.
    3760      *
    3761      */
    3762     public static Process execute(String self, final List envp, File dir) throws IOException {
    3763       final String[] commandArray = new String[envp.size()] ;
    3764       Iterator it = envp.iterator();
    3765       for (int i = 0; it.hasNext(); ++i) {
    3766           commandArray[i] = it.next().toString();
    3767       }
    3768       return execute(self, commandArray, dir);
    3769     }
    3770
    3771     /***
    3772      * Repeat a String a certain number of times
    3773      *
    3774      * @param self   a String to be repeated
    3775      * @param factor the number of times the String should be repeated
    3776      * @return a String composed of a repeatition
    3777      * @throws IllegalArgumentException if the number of repeatition is &lt; 0
    3778      */
    3779     public static String multiply(String self, Number factor) {
    3780         int size = factor.intValue();
    3781         if (size == 0)
    3782             return "";
    3783         else if (size < 0) {
    3784             throw new IllegalArgumentException("multiply() should be called with a number of 0 or greater not: " + size);
    3785         }
    3786         StringBuffer answer = new StringBuffer(self);
    3787         for (int i = 1; i < size; i++) {
    3788             answer.append(self);
    3789         }
    3790         return answer.toString();
    3791     }
    3792
    3793     /***
    3794      * Returns the string representation of the given map with bracket boundaries.
    3795      *
    3796      * @param self a Map
    3797      * @return the string representation
    3798      */
    3799     public static String toString(Map self) {
    3800         return toMapString(self);
    3801     }
    3802
    3803     /***
    3804      * Returns the string representation of the given map with bracket boundaries.
    3805      *
    3806      * @param self a Map
    3807      * @return the string representation
    3808      */
    3809     public static String toMapString(Map self) {
    3810         return (self == null) ? "null" : InvokerHelper.toMapString(self);
    3811     }
    3812
    3813     /***
    3814      * Returns the string representation of the given collection with the bracket boundaries.
    3815      *
    3816      * @param self a Collection
    3817      * @return the string representation
    3818      */
    3819     public static String toString(Collection self) {
    3820         return toListString(self);
    3821     }
    3822
    3823     /***
    3824      * Returns the string representation of the given collection with the bracket boundaries.
    3825      *
    3826      * @param self a Collection
    3827      * @return the string representation
    3828      */
    3829     public static String toListString(Collection self) {
    3830         return (self == null) ? "null" : InvokerHelper.toListString(self);
    3831     }
    3832
    3833     /***
    3834      * Returns the string representation of the given array with the brace boundaries.
    3835      *
    3836      * @param self an Object[]
    3837      * @return the string representation
    3838      */
    3839     public static String toString(Object[] self) {
    3840         return toArrayString(self);
    3841     }
    3842
    3843     /***
    3844      * Returns the string representation of the given array with the brace boundaries.
    3845      *
    3846      * @param self an Object[]
    3847      * @return the string representation
    3848      */
    3849     public static String toArrayString(Object[] self) {
    3850         return (self == null) ? "null" : InvokerHelper.toArrayString(self);
    3851     }
    3852
    3853
    3854     protected static String toString(Object value) {
    3855         if (value instanceof Map)
    3856             return toMapString((Map)value);
    3857         else if (value instanceof Collection)
    3858             return toListString((Collection)value);
    3859         else if (value instanceof Object[])
    3860             return toArrayString((Object[])value);
    3861         return (value == null) ? "null" : value.toString();
    3862     }
    3863
    3864     // Number based methods
    3865     //-------------------------------------------------------------------------
    3866
    3867     /***
    3868      * Increment a Character by one
    3869      *
    3870      * @param self a Character
    3871      * @return an incremented Number
    3872      */
    3873     public static Number next(Character self) {
    3874         return plus(self, ONE);
    3875     }
    3876
    3877     /***
    3878      * Increment a Number by one
    3879      *
    3880      * @param self a Number
    3881      * @return an incremented Number
    3882      */
    3883     public static Number next(Number self) {
    3884         return plus(self, ONE);
    3885     }
    3886
    3887     /***
    3888      * Decrement a Character by one
    3889      *
    3890      * @param self a Character
    3891      * @return a decremented Number
    3892      */
    3893     public static Number previous(Character self) {
    3894         return minus(self, ONE);
    3895     }
    3896
    3897     /***
    3898      * Decrement a Number by one
    3899      *
    3900      * @param self a Number
    3901      * @return a decremented Number
    3902      */
    3903     public static Number previous(Number self) {
    3904         return minus(self, ONE);
    3905     }
    3906
    3907     /***
    3908      * Add a Character and a Number
    3909      *
    3910      * @param left  a Character
    3911      * @param right a Number
    3912      * @return the addition of the Character and the Number
    3913      */
    3914     public static Number plus(Character left, Number right) {
    3915         return plus(new Integer(left.charValue()), right);
    3916     }
    3917
    3918     /***
    3919      * Add a Number and a Character
    3920      *
    3921      * @param left  a Number
    3922      * @param right a Character
    3923      * @return the addition of the Character and the Number
    3924      */
    3925     public static Number plus(Number left, Character right) {
    3926         return plus(left, new Integer(right.charValue()));
    3927     }
    3928
    3929     /***
    3930      * Add two Characters
    3931      *
    3932      * @param left  a Character
    3933      * @param right a Character
    3934      * @return the addition of both Characters
    3935      */
    3936     public static Number plus(Character left, Character right) {
    3937         return plus(new Integer(left.charValue()), right);
    3938     }
    3939
    3940     /***
    3941      * Add two numbers and return the result.
    3942      *
    3943      * @param left  a Number
    3944      * @param right another Number to add
    3945      * @return the addition of both Numbers
    3946      */
    3947     public static Number plus(Number left, Number right) {
    3948         return NumberMath.add(left, right);
    3949     }
    3950
    3951     /***
    3952      * Compare a Character and a Number
    3953      *
    3954      * @param left  a Character
    3955      * @param right a Number
    3956      * @return the result of the comparison
    3957      */
    3958     public static int compareTo(Character left, Number right) {
    3959         return compareTo(new Integer(left.charValue()), right);
    3960     }
    3961
    3962     /***
    3963      * Compare a Number and a Character
    3964      *
    3965      * @param left  a Number
    3966      * @param right a Character
    3967      * @return the result of the comparison
    3968      */
    3969     public static int compareTo(Number left, Character right) {
    3970         return compareTo(left, new Integer(right.charValue()));
    3971     }
    3972
    3973     /***
    3974      * Compare two Characters
    3975      *
    3976      * @param left  a Character
    3977      * @param right a Character
    3978      * @return the result of the comparison
    3979      */
    3980     public static int compareTo(Character left, Character right) {
    3981         return compareTo(new Integer(left.charValue()), right);
    3982     }
    3983
    3984     /***
    3985      * Compare two Numbers
    3986      *
    3987      * @param left  a Number
    3988      * @param right another Number to compare to
    3989      * @return the comparision of both numbers
    3990      */
    3991     public static int compareTo(Number left, Number right) {
    3992         /*** @todo maybe a double dispatch thing to handle new large numbers? */
    3993         return NumberMath.compareTo(left, right);
    3994     }
    3995
    3996     /***
    3997      * Subtract a Number from a Character
    3998      *
    3999      * @param left  a Character
    4000      * @param right a Number
    4001      * @return the addition of the Character and the Number
    4002      */
    4003     public static Number minus(Character left, Number right) {
    4004         return minus(new Integer(left.charValue()), right);
    4005     }
    4006
    4007     /***
    4008      * Subtract a Character from a Number
    4009      *
    4010      * @param left  a Number
    4011      * @param right a Character
    4012      * @return the addition of the Character and the Number
    4013      */
    4014     public static Number minus(Number left, Character right) {
    4015         return minus(left, new Integer(right.charValue()));
    4016     }
    4017
    4018     /***
    4019      * Subtraction two Characters
    4020      *
    4021      * @param left  a Character
    4022      * @param right a Character
    4023      * @return the addition of both Characters
    4024      */
    4025     public static Number minus(Character left, Character right) {
    4026         return minus(new Integer(left.charValue()), right);
    4027     }
    4028
    4029     /***
    4030      * Substraction of two Numbers
    4031      *
    4032      * @param left  a Number
    4033      * @param right another Number to substract to the first one
    4034      * @return the substraction
    4035      */
    4036     public static Number minus(Number left, Number right) {
    4037         return NumberMath.subtract(left, right);
    4038     }
    4039
    4040     /***
    4041      * Multiply a Character by a Number
    4042      *
    4043      * @param left  a Character
    4044      * @param right a Number
    4045      * @return the multiplication of both
    4046      */
    4047     public static Number multiply(Character left, Number right) {
    4048         return multiply(new Integer(left.charValue()), right);
    4049     }
    4050
    4051     /***
    4052      * Multiply a Number by a Character
    4053      *
    4054      * @param left  a Number
    4055      * @param right a Character
    4056      * @return the multiplication of both
    4057      */
    4058     public static Number multiply(Number left, Character right) {
    4059         return multiply(left, new Integer(right.charValue()));
    4060     }
    4061
    4062     /***
    4063      * Multiply two Characters
    4064      *
    4065      * @param left  a Character
    4066      * @param right another Character
    4067      * @return the multiplication of both
    4068      */
    4069     public static Number multiply(Character left, Character right) {
    4070         return multiply(new Integer(left.charValue()), right);
    4071     }
    4072
    4073     /***
    4074      * Multiply two Numbers
    4075      *
    4076      * @param left  a Number
    4077      * @param right another Number
    4078      * @return the multiplication of both
    4079      */
    4080     //Note:  This method is NOT called if left AND right are both BigIntegers or BigDecimals because
    4081     //those classes implement a method with a better exact match.
    4082     public static Number multiply(Number left, Number right) {
    4083         return NumberMath.multiply(left, right);
    4084     }
    4085
    4086     /***
    4087      * Power of a Number to a certain exponent
    4088      *
    4089      * @param self     a Number
    4090      * @param exponent a Number exponent
    4091      * @return a Number to the power of a certain exponent
    4092      */
    4093     public static Number power(Number self, Number exponent) {
    4094 	double base, exp, answer;
    4095 	base = self.doubleValue();
    4096 	exp = exponent.doubleValue();
    4097
    4098         answer = Math.pow(base, exp);
    4099 	if ((double)((int)answer) == answer) {
    4100             return new Integer((int)answer);
    4101 	}
    4102         else if ((double)((long)answer) == answer) {
    4103             return new Long((long)answer);
    4104 	}
    4105 	else {
    4106             return new Double(answer);
    4107 	}
    4108     }
    4109
    4110     /***
    4111      * Divide a Character by a Number
    4112      *
    4113      * @param left  a Character
    4114      * @param right a Number
    4115      * @return the multiplication of both
    4116      */
    4117     public static Number div(Character left, Number right) {
    4118         return div(new Integer(left.charValue()), right);
    4119     }
    4120
    4121     /***
    4122      * Divide a Number by a Character
    4123      *
    4124      * @param left  a Number
    4125      * @param right a Character
    4126      * @return the multiplication of both
    4127      */
    4128     public static Number div(Number left, Character right) {
    4129         return div(left, new Integer(right.charValue()));
    4130     }
    4131
    4132     /***
    4133      * Divide two Characters
    4134      *
    4135      * @param left  a Character
    4136      * @param right another Character
    4137      * @return the multiplication of both
    4138      */
    4139     public static Number div(Character left, Character right) {
    4140         return div(new Integer(left.charValue()), right);
    4141     }
    4142
    4143     /***
    4144      * Divide two Numbers
    4145      *
    4146      * @param left  a Number
    4147      * @param right another Number
    4148      * @return a Number resulting of the divide operation
    4149      */
    4150     //Method name changed from 'divide' to avoid collision with BigInteger method that has
    4151     //different semantics.  We want a BigDecimal result rather than a BigInteger.
    4152     public static Number div(Number left, Number right) {
    4153         return NumberMath.divide(left, right);
    4154     }
    4155
    4156     /***
    4157      * Integer Divide a Character by a Number
    4158      *
    4159      * @param left  a Character
    4160      * @param right a Number
    4161      * @return the integer division of both
    4162      */
    4163     public static Number intdiv(Character left, Number right) {
    4164         return intdiv(new Integer(left.charValue()), right);
    4165     }
    4166
    4167     /***
    4168      * Integer Divide a Number by a Character
    4169      *
    4170      * @param left  a Number
    4171      * @param right a Character
    4172      * @return the integer division of both
    4173      */
    4174     public static Number intdiv(Number left, Character right) {
    4175         return intdiv(left, new Integer(right.charValue()));
    4176     }
    4177
    4178     /***
    4179      * Integer Divide two Characters
    4180      *
    4181      * @param left  a Character
    4182      * @param right another Character
    4183      * @return the integer division of both
    4184      */
    4185     public static Number intdiv(Character left, Character right) {
    4186         return intdiv(new Integer(left.charValue()), right);
    4187     }
    4188
    4189     /***
    4190      * Integer Divide two Numbers
    4191      *
    4192      * @param left  a Number
    4193      * @param right another Number
    4194      * @return a Number (an Integer) resulting of the integer division operation
    4195      */
    4196     public static Number intdiv(Number left, Number right) {
    4197         return NumberMath.intdiv(left, right);
    4198     }
    4199
    4200     /***
    4201      * Bitwise OR together two numbers
    4202      *
    4203      * @param left  a Number
    4204      * @param right another Number to bitwise OR
    4205      * @return the bitwise OR of both Numbers
    4206      */
    4207     public static Number or(Number left, Number right) {
    4208         return NumberMath.or(left, right);
    4209     }
    4210
    4211     /***
    4212      * Bitwise AND together two Numbers
    4213      *
    4214      * @param left  a Number
    4215      * @param right another Number to bitwse AND
    4216      * @return the bitwise AND of both Numbers
    4217      */
    4218     public static Number and(Number left, Number right) {
    4219         return NumberMath.and(left, right);
    4220     }
    4221
    4222      /***
    4223      * Bitwise XOR together two Numbers
    4224      *
    4225      * @param left  a Number
    4226      * @param right another Number to bitwse XOR
    4227      * @return the bitwise XOR of both Numbers
    4228      */
    4229     public static Number xor(Number left, Number right) {
    4230         return NumberMath.xor(left, right);
    4231     }
    4232
    4233     /***
    4234      * Performs a division modulus operation
    4235      *
    4236      * @param left  a Number
    4237      * @param right another Number to mod
    4238      * @return the modulus result
    4239      */
    4240     public static Number mod(Number left, Number right) {
    4241         return NumberMath.mod(left, right);
    4242     }
    4243
    4244     /***
    4245      * Negates the number
    4246      *
    4247      * @param left a Number
    4248      * @return the negation of the number
    4249      */
    4250     public static Number negate(Number left) {
    4251         return NumberMath.negate(left);
    4252     }
    4253
    4254
    4255     /***
    4256      * Iterates a number of times
    4257      *
    4258      * @param self    a Number
    4259      * @param closure the closure to call a number of times
    4260      */
    4261     public static void times(Number self, Closure closure) {
    4262         for (int i = 0, size = self.intValue(); i < size; i++) {
    4263             closure.call(new Integer(i));
    4264             if (closure.getDirective() == Closure.DONE) {
    4265                 break;
    4266             }
    4267         }
    4268     }
    4269
    4270     /***
    4271      * Iterates from this number up to the given number
    4272      *
    4273      * @param self    a Number
    4274      * @param to      another Number to go up to
    4275      * @param closure the closure to call
    4276      */
    4277     public static void upto(Number self, Number to, Closure closure) {
    4278         int self1 = self.intValue();
    4279         int to1 = to.intValue();
    4280         if (self1 <= to1) {
    4281             for (int i = self1; i <= to1; i++) {
    4282                 closure.call(new Integer(i));
    4283             }
    4284         }
    4285         else
    4286             throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4287     }
    4288
    4289     public static void upto(long self, Number to, Closure closure) {
    4290         long to1 = to.longValue();
    4291         if (self <= to1) {
    4292             for (long i = self; i <= to1; i++) {
    4293                 closure.call(new Long(i));
    4294             }
    4295         }
    4296         else
    4297             throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4298     }
    4299
    4300     public static void upto(Long self, Number to, Closure closure) {
    4301         long self1 = self.longValue();
    4302         long to1 = to.longValue();
    4303         if (self1 <= to1) {
    4304             for (long i = self1; i <= to1; i++) {
    4305                 closure.call(new Long(i));
    4306             }
    4307         }
    4308         else
    4309             throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4310     }
    4311
    4312     public static void upto(float self, Number to, Closure closure) {
    4313         float to1 = to.floatValue();
    4314         if (self <= to1) {
    4315             for (float i = self; i <= to1; i++) {
    4316                 closure.call(new Float(i));
    4317             }
    4318         }
    4319         else
    4320             throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4321     }
    4322
    4323     public static void upto(Float self, Number to, Closure closure) {
    4324         float self1 = self.floatValue();
    4325         float to1 = to.floatValue();
    4326         if (self1 <= to1) {
    4327             for (float i = self1; i <= to1; i++) {
    4328                 closure.call(new Float(i));
    4329             }
    4330         }
    4331         else
    4332             throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4333     }
    4334
    4335     public static void upto(Double self, Number to, Closure closure) {
    4336         double self1 = self.doubleValue();
    4337         double to1 = to.doubleValue();
    4338         if (self1 <= to1) {
    4339             for (double i = self1; i <= to1; i++) {
    4340                 closure.call(new Double(i));
    4341             }
    4342         }
    4343         else
    4344             throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4345     }
    4346
    4347     public static void upto(BigInteger self, Number to, Closure closure) {
    4348         if (to instanceof BigDecimal) {
    4349             final BigDecimal one = new BigDecimal("1.0");
    4350             BigDecimal self1 = new BigDecimal(self);
    4351             BigDecimal to1 = (BigDecimal) to;
    4352             if (self1.compareTo(to1) <= 0) {
    4353                 for (BigDecimal i = self1; i.compareTo(to1) <= 0; i = i.add(one)) {
    4354                     closure.call(i);
    4355                 }
    4356             }
    4357             else
    4358                 throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4359         }
    4360         else if (to instanceof BigInteger) {
    4361             final BigInteger one = new BigInteger("1");
    4362             BigInteger to1 = (BigInteger) to;
    4363             if (self.compareTo(to1) <= 0) {
    4364                 for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
    4365                     closure.call(i);
    4366                 }
    4367             }
    4368             else
    4369                 throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4370         }
    4371         else {
    4372             final BigInteger one = new BigInteger("1");
    4373             BigInteger to1 = new BigInteger("" + to);
    4374             if (self.compareTo(to1) <= 0) {
    4375                 for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
    4376                     closure.call(i);
    4377                 }
    4378             }
    4379             else
    4380                 throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4381         }
    4382     }
    4383
    4384     public static void upto(BigDecimal self, Number to, Closure closure) {
    4385         final BigDecimal one = new BigDecimal("1.0");
    4386         if (to instanceof BigDecimal) {
    4387             BigDecimal to1 = (BigDecimal) to;
    4388             if (self.compareTo(to1) <= 0) {
    4389                 for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
    4390                     closure.call(i);
    4391                 }
    4392             }
    4393             else
    4394                 throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4395         }
    4396         else if (to instanceof BigInteger) {
    4397             BigDecimal to1 = new BigDecimal((BigInteger) to);
    4398             if (self.compareTo(to1) <= 0) {
    4399                 for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
    4400                     closure.call(i);
    4401                 }
    4402             }
    4403             else
    4404                 throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4405         }
    4406         else {
    4407             BigDecimal to1 = new BigDecimal(""+to);
    4408             if (self.compareTo(to1) <= 0) {
    4409                 for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
    4410                     closure.call(i);
    4411                 }
    4412             }
    4413             else
    4414                 throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to +")");
    4415         }
    4416     }
    4417
    4418     /***
    4419      * Iterates from this number down to the given number
    4420      *
    4421      * @param self    a Number
    4422      * @param to      another Number to go down to
    4423      * @param closure the closure to call
    4424      */
    4425     public static void downto(Number self, Number to, Closure closure) {
    4426         int self1 = self.intValue();
    4427         int to1 = to.intValue();
    4428         if (self1 >= to1) {
    4429             for (int i = self1; i >= to1; i--) {
    4430                 closure.call(new Integer(i));
    4431             }
    4432         }
    4433         else
    4434             throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4435     }
    4436
    4437     public static void downto(long self, Number to, Closure closure) {
    4438         long to1 = to.longValue();
    4439         if (self >= to1) {
    4440             for (long i = self; i >= to1; i--) {
    4441                 closure.call(new Long(i));
    4442             }
    4443         }
    4444         else
    4445             throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4446     }
    4447
    4448     public static void downto(Long self, Number to, Closure closure) {
    4449         long self1 = self.longValue();
    4450         long to1 = to.longValue();
    4451         if (self1 >= to1) {
    4452             for (long i = self1; i >= to1; i--) {
    4453                 closure.call(new Long(i));
    4454             }
    4455         }
    4456         else
    4457             throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4458     }
    4459
    4460     public static void downto(float self, Number to, Closure closure) {
    4461         float to1 = to.floatValue();
    4462         if (self >= to1) {
    4463             for (float i = self; i >= to1; i--) {
    4464                closure.call(new Float(i));
    4465             }
    4466         }
    4467         else
    4468             throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4469     }
    4470
    4471     public static void downto(Float self, Number to, Closure closure) {
    4472         float self1 = self.floatValue();
    4473         float to1 = to.floatValue();
    4474         if (self1 >= to1) {
    4475             for (float i = self1; i >= to1; i--) {
    4476                closure.call(new Float(i));
    4477             }
    4478         }
    4479         else
    4480             throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4481     }
    4482
    4483     public static void downto(double self, Number to, Closure closure) {
    4484         double to1 = to.doubleValue();
    4485         if (self >= to1) {
    4486             for (double i = self; i >= to1; i--) {
    4487                 closure.call(new Double(i));
    4488             }
    4489         }
    4490         else
    4491             throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4492     }
    4493
    4494     public static void downto(Double self, Number to, Closure closure) {
    4495         double self1 = self.doubleValue();
    4496         double to1 = to.doubleValue();
    4497         if (self1 >= to1) {
    4498             for (double i = self1; i >= to1; i--) {
    4499                 closure.call(new Double(i));
    4500             }
    4501         }
    4502         else
    4503             throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4504     }
    4505
    4506     public static void downto(final BigInteger self, final Number to, final Closure closure) {
    4507         if (to instanceof BigDecimal) {
    4508             final BigDecimal one = new BigDecimal("1.0");
    4509             final BigDecimal to1 = (BigDecimal) to;
    4510             final BigDecimal selfD = new BigDecimal(self);
    4511             if (selfD.compareTo(to1) >= 0) {
    4512                 for (BigDecimal i = selfD; i.compareTo(to1) >= 0; i = i.subtract(one)) {
    4513                     closure.call(i.toBigInteger());
    4514                 }
    4515             }
    4516             else
    4517                 throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4518         }
    4519         else if (to instanceof BigInteger) {
    4520             final BigInteger one = new BigInteger("1");
    4521             final BigInteger to1 = (BigInteger) to;
    4522             if (self.compareTo(to1) >= 0) {
    4523                 for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
    4524                     closure.call(i);
    4525                 }
    4526             }
    4527             else
    4528                 throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4529         }
    4530         else {
    4531             final BigInteger one = new BigInteger("1");
    4532             final BigInteger to1 = new BigInteger("" + to);
    4533             if (self.compareTo(to1) >= 0) {
    4534                 for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
    4535                     closure.call(i);
    4536                 }
    4537             }
    4538             else
    4539                 throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4540         }
    4541     }
    4542
    4543     public static void downto(BigDecimal self, Number to, Closure closure) {
    4544         final BigDecimal one = new BigDecimal("1.0");
    4545         if (to instanceof BigDecimal) {
    4546             BigDecimal to1 = (BigDecimal) to;
    4547             if (self.compareTo(to1) >= 0) {
    4548                 for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
    4549                     closure.call(i);
    4550                 }
    4551             }
    4552             else
    4553                 throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4554         }
    4555         else if (to instanceof BigInteger) {
    4556             BigDecimal to1 = new BigDecimal((BigInteger) to);
    4557             if (self.compareTo(to1) >= 0) {
    4558                 for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
    4559                     closure.call(i);
    4560                 }
    4561             }
    4562             else
    4563                 throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to +")");
    4564         }
    4565         else {
    4566             BigDecimal to1 = new BigDecimal(""+to);
    4567             if (self.compareTo(to1) >= 0) {
    4568                 for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
    4569                     closure.call(i);
    4570                 }
    4571             }
    4572             else
    4573                 throw new GroovyRuntimeException("Infinite loop in " + self +".downto(" + to +")");
    4574         }
    4575     }
    4576
    4577     /***
    4578      * Iterates from this number up to the given number using a step increment
    4579      *
    4580      * @param self       a Number to start with
    4581      * @param to         a Number to go up to
    4582      * @param stepNumber a Number representing the step increment
    4583      * @param closure    the closure to call
    4584      */
    4585     public static void step(Number self, Number to, Number stepNumber, Closure closure) {
    4586         if (self instanceof BigDecimal || to instanceof BigDecimal || stepNumber instanceof BigDecimal) {
    4587             final BigDecimal zero = new BigDecimal("0.0");
    4588             BigDecimal self1 = (self instanceof BigDecimal) ? (BigDecimal) self : new BigDecimal("" + self);
    4589             BigDecimal to1 = (to instanceof BigDecimal) ? (BigDecimal) to : new BigDecimal("" + to);
    4590             BigDecimal stepNumber1 = (stepNumber instanceof BigDecimal) ? (BigDecimal) stepNumber : new BigDecimal("" + stepNumber);
    4591             if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) {
    4592                 for (BigDecimal i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) {
    4593                     closure.call(i);
    4594                 }
    4595             }
    4596             else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) {
    4597                 for (BigDecimal i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) {
    4598                     closure.call(i);
    4599                 }
    4600             }
    4601             else
    4602                 throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")");
    4603         }
    4604         else if (self instanceof BigInteger || to instanceof BigInteger || stepNumber instanceof BigInteger) {
    4605             final BigInteger zero = new BigInteger("0");
    4606             BigInteger self1 = (self instanceof BigInteger) ? (BigInteger) self : new BigInteger("" + self);
    4607             BigInteger to1 = (to instanceof BigInteger) ? (BigInteger) to : new BigInteger("" + to);
    4608             BigInteger stepNumber1 = (stepNumber instanceof BigInteger) ? (BigInteger) stepNumber : new BigInteger("" + stepNumber);
    4609             if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) {
    4610                 for (BigInteger i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) {
    4611                     closure.call(i);
    4612                 }
    4613             }
    4614             else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) {
    4615                 for (BigInteger i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) {
    4616                     closure.call(i);
    4617                 }
    4618             }
    4619             else
    4620                 throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")");
    4621         }
    4622         else {
    4623             int self1 = self.intValue();
    4624             int to1 = to.intValue();
    4625             int stepNumber1 = stepNumber.intValue();
    4626             if (stepNumber1 > 0 && to1 > self1) {
    4627                 for (int i = self1; i < to1; i += stepNumber1) {
    4628                     closure.call(new Integer(i));
    4629                 }
    4630             }
    4631             else if (stepNumber1 < 0 && to1 < self1) {
    4632                 for (int i = self1; i > to1; i += stepNumber1) {
    4633                     closure.call(new Integer(i));
    4634                 }
    4635             }
    4636             else
    4637                 throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")");
    4638         }
    4639     }
    4640
    4641     /***
    4642      * Get the absolute value
    4643      *
    4644      * @param number a Number
    4645      * @return the absolute value of that Number
    4646      */
    4647     //Note:  This method is NOT called if number is a BigInteger or BigDecimal because
    4648     //those classes implement a method with a better exact match.
    4649     public static int abs(Number number) {
    4650         return Math.abs(number.intValue());
    4651     }
    4652
    4653     /***
    4654      * Get the absolute value
    4655      *
    4656      * @param number a Long
    4657      * @return the absolute value of that Long
    4658      */
    4659     public static long abs(Long number) {
    4660         return Math.abs(number.longValue());
    4661     }
    4662
    4663     /***
    4664      * Get the absolute value
    4665      *
    4666      * @param number a Float
    4667      * @return the absolute value of that Float
    4668      */
    4669     public static float abs(Float number) {
    4670         return Math.abs(number.floatValue());
    4671     }
    4672
    4673     /***
    4674      * Get the absolute value
    4675      *
    4676      * @param number a Double
    4677      * @return the absolute value of that Double
    4678      */
    4679     public static double abs(Double number) {
    4680         return Math.abs(number.doubleValue());
    4681     }
    4682
    4683     /***
    4684      * Get the absolute value
    4685      *
    4686      * @param number a Float
    4687      * @return the absolute value of that Float
    4688      */
    4689     public static int round(Float number) {
    4690         return Math.round(number.floatValue());
    4691     }
    4692
    4693     /***
    4694      * Round the value
    4695      *
    4696      * @param number a Double
    4697      * @return the absolute value of that Double
    4698      */
    4699     public static long round(Double number) {
    4700         return Math.round(number.doubleValue());
    4701     }
    4702
    4703     /***
    4704      * Parse a String into an Integer
    4705      *
    4706      * @param self a String
    4707      * @return an Integer
    4708      */
    4709     public static Integer toInteger(String self) {
    4710         return Integer.valueOf(self.trim());
    4711     }
    4712
    4713     /***
    4714      * Parse a String into a Long
    4715      *
    4716      * @param self a String
    4717      * @return a Long
    4718      */
    4719     public static Long toLong(String self) {
    4720         return Long.valueOf(self.trim());
    4721     }
    4722
    4723     /***
    4724      * Parse a String into a Float
    4725      *
    4726      * @param self a String
    4727      * @return a Float
    4728      */
    4729     public static Float toFloat(String self) {
    4730         return Float.valueOf(self.trim());
    4731     }
    4732
    4733     /***
    4734      * Parse a String into a Double
    4735      *
    4736      * @param self a String
    4737      * @return a Double
    4738      */
    4739     public static Double toDouble(String self) {
    4740         return Double.valueOf(self.trim());
    4741     }
    4742
    4743     /***
    4744      * Parse a String into a BigInteger
    4745      *
    4746      * @param self a String
    4747      * @return a BigInteger
    4748      */
    4749     public static BigInteger toBigInteger(String self) {
    4750         return new BigInteger(self.trim());
    4751     }
    4752
    4753     /***
    4754      * Parse a String into a BigDecimal
    4755      *
    4756      * @param self a String
    4757      * @return a BigDecimal
    4758      */
    4759     public static BigDecimal toBigDecimal(String self) {
    4760         return new BigDecimal(self.trim());
    4761     }
    4762
    4763     /***
    4764      * Transform a Number into an Integer
    4765      *
    4766      * @param self a Number
    4767      * @return an Integer
    4768      */
    4769     public static Integer toInteger(Number self) {
    4770         return new Integer(self.intValue());
    4771     }
    4772
    4773     /***
    4774      * Transform a Number into a Long
    4775      *
    4776      * @param self a Number
    4777      * @return an Long
    4778      */
    4779     public static Long toLong(Number self) {
    4780         return new Long(self.longValue());
    4781     }
    4782
    4783     /***
    4784      * Transform a Number into a Float
    4785      *
    4786      * @param self a Number
    4787      * @return an Float
    4788      */
    4789     public static Float toFloat(Number self) {
    4790         return new Float(self.floatValue());
    4791     }
    4792
    4793     /***
    4794      * Transform a Number into a Double
    4795      *
    4796      * @param self a Number
    4797      * @return an Double
    4798      */
    4799     public static Double toDouble(Number self) {
    4800         return new Double(self.doubleValue());
    4801     }
    4802
    4803     /***
    4804      * Transform a Number into a BigDecimal
    4805      *
    4806      * @param self a Number
    4807      * @return an BigDecimal
    4808      */
    4809     public static BigDecimal toBigDecimal(Number self) {
    4810         return new BigDecimal(self.doubleValue());
    4811     }
    4812
    4813     /***
    4814      * Transform a Number into a BigInteger
    4815      *
    4816      * @param self a Number
    4817      * @return an BigInteger
    4818      */
    4819     public static BigInteger toBigInteger(Number self) {
    4820         return new BigInteger(Long.toString(self.longValue()));
    4821     }
    4822
    4823     // Date methods
    4824     //-------------------------------------------------------------------------
    4825
    4826     /***
    4827      * Increments a Date by a day
    4828      *
    4829      * @param self a Date
    4830      * @return the next days date
    4831      */
    4832     public static Date next(Date self) {
    4833         return plus(self, 1);
    4834     }
    4835
    4836     /***
    4837      * Increments a java.sql.Date by a day
    4838      *
    4839      * @param self a java.sql.Date
    4840      * @return the next days date
    4841      */
    4842     public static java.sql.Date next(java.sql.Date self) {
    4843     	return new java.sql.Date(next((Date)self).getTime());
    4844     }
    4845
    4846     /***
    4847      * Decrement a Date by a day
    4848      *
    4849      * @param self a Date
    4850      * @return the previous days date
    4851      */
    4852     public static Date previous(Date self) {
    4853         return minus(self, 1);
    4854     }
    4855
    4856     /***
    4857      * Decrement a java.sql.Date by a day
    4858      *
    4859      * @param self a java.sql.Date
    4860      * @return the previous days date
    4861      */
    4862     public static java.sql.Date previous(java.sql.Date self) {
    4863     	return new java.sql.Date(previous((Date)self).getTime());
    4864     }
    4865
    4866     /***
    4867      * Adds a number of days to this date and returns the new date
    4868      *
    4869      * @param self a Date
    4870      * @param days the number of days to increase
    4871      * @return the new date
    4872      */
    4873     public static Date plus(Date self, int days) {
    4874         Calendar calendar = (Calendar) Calendar.getInstance().clone();
    4875         calendar.setTime(self);
    4876         calendar.add(Calendar.DAY_OF_YEAR, days);
    4877         return calendar.getTime();
    4878     }
    4879
    4880     /***
    4881      * Adds a number of days to this date and returns the new date
    4882      *
    4883      * @param self a java.sql.Date
    4884      * @param days the number of days to increase
    4885      * @return the new date
    4886      */
    4887     public static java.sql.Date plus(java.sql.Date self, int days) {
    4888     	return new java.sql.Date(plus((Date)self, days).getTime());
    4889     }
    4890
    4891     /***
    4892      * Subtracts a number of days from this date and returns the new date
    4893      *
    4894      * @param self a Date
    4895      * @return the new date
    4896      */
    4897     public static Date minus(Date self, int days) {
    4898         return plus(self, -days);
    4899     }
    4900
    4901     /***
    4902      * Subtracts a number of days from this date and returns the new date
    4903      *
    4904      * @param self a java.sql.Date
    4905      * @return the new date
    4906      */
    4907     public static java.sql.Date minus(java.sql.Date self, int days) {
    4908     	return new java.sql.Date(minus((Date)self, days).getTime());
    4909     }
    4910
    4911     // Boolean based methods
    4912     //-------------------------------------------------------------------------
    4913
    4914     public static Boolean and(Boolean left, Boolean right) {
    4915         return Boolean.valueOf(left.booleanValue() & right.booleanValue());
    4916     }
    4917
    4918     public static Boolean or(Boolean left, Boolean right) {
    4919         return Boolean.valueOf(left.booleanValue() | right.booleanValue());
    4920     }
    4921
    4922     public static Boolean xor(Boolean left, Boolean right) {
    4923         return Boolean.valueOf(left.booleanValue() ^ right.booleanValue());
    4924     }
    4925
    4926 //    public static Boolean negate(Boolean left) {
    4927 //        return Boolean.valueOf(!left.booleanValue());
    4928 //    }
    4929
    4930     // File and stream based methods
    4931     //-------------------------------------------------------------------------
    4932
    4933     /***
    4934      * Helper method to create an object input stream from the given file.
    4935      *
    4936      * @param file a file
    4937      * @return an object input stream
    4938      * @throws FileNotFoundException
    4939      * @throws IOException
    4940      */
    4941     public static ObjectInputStream newObjectInputStream(File file) throws FileNotFoundException, IOException {
    4942         return new ObjectInputStream(new FileInputStream(file));
    4943     }
    4944
    4945     /***
    4946      * Iterates through the given file object by object
    4947      *
    4948      * @param self    a File
    4949      * @param closure a closure
    4950      * @throws IOException
    4951      * @throws ClassNotFoundException
    4952      */
    4953     public static void eachObject(File self, Closure closure) throws IOException, ClassNotFoundException {
    4954         eachObject(newObjectInputStream(self), closure);
    4955     }
    4956
    4957     /***
    4958      * Iterates through the given object stream object by object
    4959      *
    4960      * @param ois    an ObjectInputStream
    4961      * @param closure a closure
    4962      * @throws IOException
    4963      * @throws ClassNotFoundException
    4964      */
    4965     public static void eachObject(ObjectInputStream ois, Closure closure) throws IOException, ClassNotFoundException {
    4966         try {
    4967             while (true) {
    4968                 try {
    4969                     Object obj = ois.readObject();
    4970                     // we allow null objects in the object stream
    4971                     closure.call(obj);
    4972                 } catch (EOFException e) {
    4973                     break;
    4974                 }
    4975             }
    4976             ois.close();
    4977         } catch (ClassNotFoundException e) {
    4978             try {
    4979                 ois.close();
    4980             } catch (Exception e2) {
    4981                 // ignore as we're already throwing
    4982             }
    4983             throw e;
    4984         } catch (IOException e) {
    4985             try {
    4986                ois.close();
    4987             } catch (Exception e2) {
    4988                // ignore as we're already throwing
    4989             }
    4990             throw e;
    4991         }
    4992     }
    4993
    4994     /***
    4995      * Iterates through the given file line by line
    4996      *
    4997      * @param self    a File
    4998      * @param closure a closure
    4999      * @throws IOException
    5000      */
    5001     public static void eachLine(File self, Closure closure) throws IOException {
    5002         eachLine(newReader(self), closure);
    5003     }
    5004
    5005     /***
    5006      * Iterates through the given reader line by line
    5007      *
    5008      * @param self    a Reader
    5009      * @param closure a closure
    5010      * @throws IOException
    5011      */
    5012     public static void eachLine(Reader self, Closure closure) throws IOException {
    5013         BufferedReader br = null;
    5014
    5015         if (self instanceof BufferedReader)
    5016             br = (BufferedReader) self;
    5017         else
    5018             br = new BufferedReader(self);
    5019
    5020         try {
    5021             while (true) {
    5022                 String line = br.readLine();
    5023                 if (line == null) {
    5024                     break;
    5025                 } else {
    5026                     closure.call(line);
    5027                 }
    5028             }
    5029             br.close();
    5030         } catch (IOException e) {
    5031             if (self != null) {
    5032                 try {
    5033                     br.close();
    5034                 } catch (Exception e2) {
    5035                     // ignore as we're already throwing
    5036                 }
    5037                 throw e;
    5038             }
    5039         }
    5040     }
    5041
    5042     /***
    5043      * Iterates through the given file line by line, splitting on the seperator
    5044      *
    5045      * @param self    a File
    5046      * @param sep     a String separator
    5047      * @param closure a closure
    5048      * @throws IOException
    5049      */
    5050     public static void splitEachLine(File self, String sep, Closure closure) throws IOException {
    5051         splitEachLine(newReader(self), sep, closure);
    5052     }
    5053
    5054     /***
    5055      * Iterates through the given reader line by line, splitting on the seperator
    5056      *
    5057      * @param self    a Reader
    5058      * @param sep     a String separator
    5059      * @param closure a closure
    5060      * @throws IOException
    5061      */
    5062     public static void splitEachLine(Reader self, String sep, Closure closure) throws IOException {
    5063         BufferedReader br = null;
    5064
    5065         if (self instanceof BufferedReader)
    5066             br = (BufferedReader) self;
    5067         else
    5068             br = new BufferedReader(self);
    5069
    5070         try {
    5071             while (true) {
    5072                 String line = br.readLine();
    5073                 if (line == null) {
    5074                     break;
    5075                 } else {
    5076                     List vals = Arrays.asList(line.split(sep));
    5077                     closure.call(vals);
    5078                 }
    5079             }
    5080             br.close();
    5081         } catch (IOException e) {
    5082             if (self != null) {
    5083                 try {
    5084                     br.close();
    5085                 } catch (Exception e2) {
    5086                     // ignore as we're already throwing
    5087                 }
    5088                 throw e;
    5089             }
    5090         }
    5091     }
    5092
    5093     /***
    5094      * Read a single, whole line from the given Reader
    5095      *
    5096      * @param self a Reader
    5097      * @return a line
    5098      * @throws IOException
    5099      */
    5100     public static String readLine(Reader self) throws IOException {
    5101         BufferedReader br = null;
    5102
    5103         if (self instanceof BufferedReader) {
    5104             br = (BufferedReader) self;
    5105         } else {
    5106             br = new BufferedReader(self); // todo dk: bug! will return null on second call
    5107         }
    5108         return br.readLine();
    5109     }
    5110
    5111     /***
    5112      * Read a single, whole line from the given InputStream
    5113      *
    5114      * @param stream an InputStream
    5115      * @return a line
    5116      * @throws IOException
    5117      */
    5118     public static String readLine(InputStream stream) throws IOException {
    5119         return readLine(new InputStreamReader(stream));
    5120     }
    5121
    5122     /***
    5123      * Reads the file into a list of Strings for each line
    5124      *
    5125      * @param file a File
    5126      * @return a List of lines
    5127      * @throws IOException
    5128      */
    5129     public static List readLines(File file) throws IOException {
    5130         IteratorClosureAdapter closure = new IteratorClosureAdapter(file);
    5131         eachLine(file, closure);
    5132         return closure.asList();
    5133     }
    5134
    5135     /***
    5136      * Reads the content of the File opened with the specified encoding and returns it as a String
    5137      *
    5138      * @param file    the file whose content we want to read
    5139      * @param charset the charset used to read the content of the file
    5140      * @return a String containing the content of the file
    5141      * @throws IOException
    5142      */
    5143     public static String getText(File file, String charset) throws IOException {
    5144         BufferedReader reader = newReader(file, charset);
    5145         return getText(reader);
    5146     }
    5147
    5148     /***
    5149      * Reads the content of the File and returns it as a String
    5150      *
    5151      * @param file the file whose content we want to read
    5152      * @return a String containing the content of the file
    5153      * @throws IOException
    5154      */
    5155     public static String getText(File file) throws IOException {
    5156         BufferedReader reader = newReader(file);
    5157         return getText(reader);
    5158     }
    5159
    5160     /***
    5161      * Reads the content of this URL and returns it as a String
    5162      *
    5163      * @param url URL to read content from
    5164      * @return the text from that URL
    5165      * @throws IOException
    5166      */
    5167     public static String getText(URL url) throws IOException {
    5168         return getText(url, CharsetToolkit.getDefaultSystemCharset().toString());
    5169     }
    5170
    5171     /***
    5172      * Reads the content of this URL and returns it as a String
    5173      *
    5174      * @param url     URL to read content from
    5175      * @param charset opens the stream with a specified charset
    5176      * @return the text from that URL
    5177      * @throws IOException
    5178      */
    5179     public static String getText(URL url, String charset) throws IOException {
    5180         BufferedReader reader = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), charset));
    5181         return getText(reader);
    5182     }
    5183
    5184     /***
    5185      * Reads the content of this InputStream and returns it as a String
    5186      *
    5187      * @param is an input stream
    5188      * @return the text from that URL
    5189      * @throws IOException
    5190      */
    5191     public static String getText(InputStream is) throws IOException {
    5192         BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    5193         return getText(reader);
    5194     }
    5195
    5196     /***
    5197      * Reads the content of this InputStream with a specified charset and returns it as a String
    5198      *
    5199      * @param is      an input stream
    5200      * @param charset opens the stream with a specified charset
    5201      * @return the text from that URL
    5202      * @throws IOException
    5203      */
    5204     public static String getText(InputStream is, String charset) throws IOException {
    5205         BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
    5206         return getText(reader);
    5207     }
    5208
    5209     /***
    5210      * Reads the content of the Reader and returns it as a String
    5211      *
    5212      * @param reader a Reader whose content we want to read
    5213      * @return a String containing the content of the buffered reader
    5214      * @throws IOException
    5215      */
    5216     public static String getText(Reader reader) throws IOException {
    5217         BufferedReader bufferedReader = new BufferedReader(reader);
    5218         return getText(bufferedReader);
    5219     }
    5220
    5221     /***
    5222      * Reads the content of the BufferedReader and returns it as a String
    5223      *
    5224      * @param reader a BufferedReader whose content we want to read
    5225      * @return a String containing the content of the buffered reader
    5226      * @throws IOException
    5227      */
    5228     public static String getText(BufferedReader reader) throws IOException {
    5229         StringBuffer answer = new StringBuffer();
    5230         // reading the content of the file within a char buffer allow to keep the correct line endings
    5231         char[] charBuffer = new char[4096];
    5232         int nbCharRead = 0;
    5233         while ((nbCharRead = reader.read(charBuffer)) != -1) {
    5234             // appends buffer
    5235             answer.append(charBuffer, 0, nbCharRead);
    5236         }
    5237         reader.close();
    5238         return answer.toString();
    5239     }
    5240
    5241     /***
    5242      * Write the text and append a new line (depending on the platform line-ending)
    5243      *
    5244      * @param writer a BufferedWriter
    5245      * @param line   the line to write
    5246      * @throws IOException
    5247      */
    5248     public static void writeLine(BufferedWriter writer, String line) throws IOException {
    5249         writer.write(line);
    5250         writer.newLine();
    5251     }
    5252
    5253     /***
    5254      * Write the text to the File.
    5255      *
    5256      * @param file a File
    5257      * @param text the text to write to the File
    5258      * @throws IOException
    5259      */
    5260     public static void write(File file, String text) throws IOException {
    5261         BufferedWriter writer = newWriter(file);
    5262         writer.write(text);
    5263         writer.close();
    5264     }
    5265
    5266     /***
    5267      * Write the text to the File.
    5268      *
    5269      * @param file a File
    5270      * @param text the text to write to the File
    5271      * @throws IOException
    5272      */
    5273     public static File leftShift(File file, Object text) throws IOException {
    5274 		append(file, text);
    5275 		return file;
    5276     }
    5277
    5278     /***
    5279      * Write the text to the File with a specified encoding.
    5280      *
    5281      * @param file    a File
    5282      * @param text    the text to write to the File
    5283      * @param charset the charset used
    5284      * @throws IOException
    5285      */
    5286     public static void write(File file, String text, String charset) throws IOException {
    5287         BufferedWriter writer = newWriter(file, charset);
    5288         writer.write(text);
    5289         writer.close();
    5290     }
    5291
    5292     /***
    5293      * Append the text at the end of the File
    5294      *
    5295      * @param file a File
    5296      * @param text the text to append at the end of the File
    5297      * @throws IOException
    5298      */
    5299     public static void append(File file, Object text) throws IOException {
    5300 		BufferedWriter writer = null;
    5301 		try {
    5302 			writer = newWriter(file, true);
    5303 			InvokerHelper.write(writer, text);
    5304 		} finally {
    5305     		if(writer != null)
    5306     			try {
    5307     				writer.close();
    5308     			}
    5309     			catch(Exception e){
    5310     				// ignore this exception
    5311     			}
    5312 		}
    5313     }
    5314     /***
    5315      * Append the text at the end of the File with a specified encoding
    5316      *
    5317      * @param file    a File
    5318      * @param text    the text to append at the end of the File
    5319      * @param charset the charset used
    5320      * @throws IOException
    5321      */
    5322     public static void append(File file, Object text, String charset) throws IOException {
    5323         BufferedWriter writer = null;
    5324         try {
    5325             writer = newWriter(file, charset, true);
    5326             InvokerHelper.write(writer, text);
    5327         } finally {
    5328             if(writer != null)
    5329                 try {
    5330                     writer.close();
    5331                 }
    5332                 catch(Exception e){
    5333                     // ignore this exception
    5334                 }
    5335         }
    5336     }
    5337
    5338     /***
    5339      * Reads the reader into a list of Strings for each line
    5340      *
    5341      * @param reader a Reader
    5342      * @return a List of lines
    5343      * @throws IOException
    5344      */
    5345     public static List readLines(Reader reader) throws IOException {
    5346         IteratorClosureAdapter closure = new IteratorClosureAdapter(reader);
    5347         eachLine(reader, closure);
    5348         return closure.asList();
    5349     }
    5350
    5351     /***
    5352      * This method is used to throw useful exceptions when the eachFile* and eachDir closure methods
    5353      * are used incorrectly.
    5354      *
    5355      * @param dir The directory to check
    5356      * @throws FileNotFoundException Thrown if the given directory does not exist
    5357      * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
    5358      */
    5359     private static void checkDir(File dir) throws FileNotFoundException, IllegalArgumentException {
    5360         if (!dir.exists())
    5361           throw new FileNotFoundException(dir.getAbsolutePath());
    5362         if (!dir.isDirectory())
    5363           throw new IllegalArgumentException("The provided File object is not a directory: " + dir.getAbsolutePath());
    5364     }
    5365
    5366     /***
    5367      * Invokes the closure for each file in the given directory
    5368      *
    5369      * @param self    a File
    5370      * @param closure a closure
    5371      * @throws FileNotFoundException Thrown if the given directory does not exist
    5372      * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
    5373      */
    5374     public static void eachFile(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
    5375         checkDir(self);
    5376         File[] files = self.listFiles();
    5377         for (int i = 0; i < files.length; i++) {
    5378             closure.call(files[i]);
    5379         }
    5380     }
    5381
    5382     /***
    5383      * Invokes the closure for each file in the given directory and recursively.
    5384      * It is a depth-first exploration, directories are included in the search.
    5385      *
    5386      * @param self    a File
    5387      * @param closure a closure
    5388      * @throws FileNotFoundException Thrown if the given directory does not exist
    5389      * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
    5390      */
    5391     public static void eachFileRecurse(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
    5392         checkDir(self);
    5393         File[] files = self.listFiles();
    5394         for (int i = 0; i < files.length; i++) {
    5395             if (files[i].isDirectory()) {
    5396                 closure.call(files[i]);
    5397                 eachFileRecurse(files[i], closure);
    5398             } else {
    5399                 closure.call(files[i]);
    5400             }
    5401         }
    5402     }
    5403
    5404     /***
    5405      * Invokes the closure for each directory in the given directory,
    5406      * ignoring regular files.
    5407      *
    5408      * @param self    a directory
    5409      * @param closure a closure
    5410      * @throws FileNotFoundException Thrown if the given directory does not exist
    5411      * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
    5412      */
    5413     public static void eachDir(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
    5414         checkDir(self);
    5415         File[] files = self.listFiles();
    5416         for (int i = 0; i < files.length; i++) {
    5417             if (files[i].isDirectory()) {
    5418                 closure.call(files[i]);
    5419             }
    5420         }
    5421     }
    5422
    5423     /***
    5424      * Invokes the closure for each file matching the given filter in the given directory
    5425      * - calling the isCase() method used by switch statements.  This method can be used
    5426      * with different kinds of filters like regular expresions, classes, ranges etc.
    5427      *
    5428      * @param self   a file
    5429      * @param filter the filter to perform on the directory (using the isCase(object) method)
    5430      * @param closure
    5431      * @throws FileNotFoundException Thrown if the given directory does not exist
    5432      * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
    5433      */
    5434     public static void eachFileMatch(File self, Object filter, Closure closure) throws FileNotFoundException, IllegalArgumentException {
    5435         checkDir(self);
    5436         File[] files = self.listFiles();
    5437         MetaClass metaClass = InvokerHelper.getMetaClass(filter);
    5438         for (int i = 0; i < files.length; i++) {
    5439             if (InvokerHelper.asBool(metaClass.invokeMethod(filter, "isCase", files[i].getName()))) {
    5440                 closure.call(files[i]);
    5441             }
    5442         }
    5443     }
    5444
    5445     /***
    5446      * Allow simple syntax for using timers.
    5447      *
    5448      * @param timer a timer object
    5449      * @param delay the delay in milliseconds before running the closure code
    5450      * @param closure
    5451      */
    5452     public static void runAfter(Timer timer, int delay, final Closure closure) {
    5453         TimerTask timerTask = new TimerTask() {
    5454             public void run() {
    5455                 closure.call();
    5456             }
    5457         };
    5458         timer.schedule(timerTask, delay);
    5459     }
    5460
    5461     /***
    5462      * Helper method to create a buffered reader for a file
    5463      *
    5464      * @param file a File
    5465      * @return a BufferedReader
    5466      * @throws IOException
    5467      */
    5468     public static BufferedReader newReader(File file) throws IOException {
    5469         CharsetToolkit toolkit = new CharsetToolkit(file);
    5470         return toolkit.getReader();
    5471     }
    5472
    5473     /***
    5474      * Helper method to create a buffered reader for a file, with a specified charset
    5475      *
    5476      * @param file    a File
    5477      * @param charset the charset with which we want to write in the File
    5478      * @return a BufferedReader
    5479      * @throws FileNotFoundException        if the File was not found
    5480      * @throws UnsupportedEncodingException if the encoding specified is not supported
    5481      */
    5482     public static BufferedReader newReader(File file, String charset)
    5483             throws FileNotFoundException, UnsupportedEncodingException {
    5484         return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
    5485     }
    5486
    5487     /***
    5488      * Provides a reader for an arbitrary input stream
    5489      *
    5490      * @param self an input stream
    5491      * @return a reader
    5492      */
    5493     public static BufferedReader newReader(final InputStream self) {
    5494         return new BufferedReader(new InputStreamReader(self));
    5495     }
    5496
    5497     /***
    5498      * Helper method to create a new BufferedReader for a file and then
    5499      * passes it into the closure and ensures its closed again afterwords
    5500      *
    5501      * @param file
    5502      * @throws FileNotFoundException
    5503      */
    5504     public static void withReader(File file, Closure closure) throws IOException {
    5505         withReader(newReader(file), closure);
    5506     }
    5507
    5508     /***
    5509      * Helper method to create a buffered output stream for a file
    5510      *
    5511      * @param file
    5512      * @throws FileNotFoundException
    5513      */
    5514     public static BufferedOutputStream newOutputStream(File file) throws IOException {
    5515         return new BufferedOutputStream(new FileOutputStream(file));
    5516     }
    5517
    5518     /***
    5519      * Helper method to create a new OutputStream for a file and then
    5520      * passes it into the closure and ensures its closed again afterwords
    5521      *
    5522      * @param file a File
    5523      * @throws FileNotFoundException
    5524      */
    5525     public static void withOutputStream(File file, Closure closure) throws IOException {
    5526         withStream(newOutputStream(file), closure);
    5527     }
    5528
    5529     /***
    5530      * Helper method to create a new InputStream for a file and then
    5531      * passes it into the closure and ensures its closed again afterwords
    5532      *
    5533      * @param file a File
    5534      * @throws FileNotFoundException
    5535      */
    5536     public static void withInputStream(File file, Closure closure) throws IOException {
    5537         withStream(newInputStream(file), closure);
    5538     }
    5539
    5540     /***
    5541      * Helper method to create a buffered writer for a file
    5542      *
    5543      * @param file a File
    5544      * @return a BufferedWriter
    5545      * @throws FileNotFoundException
    5546      */
    5547     public static BufferedWriter newWriter(File file) throws IOException {
    5548         return new BufferedWriter(new FileWriter(file));
    5549     }
    5550
    5551     /***
    5552      * Helper method to create a buffered writer for a file in append mode
    5553      *
    5554      * @param file   a File
    5555      * @param append true if in append mode
    5556      * @return a BufferedWriter
    5557      * @throws FileNotFoundException
    5558      */
    5559     public static BufferedWriter newWriter(File file, boolean append) throws IOException {
    5560         return new BufferedWriter(new FileWriter(file, append));
    5561     }
    5562
    5563     /***
    5564      * Helper method to create a buffered writer for a file
    5565      *
    5566      * @param file    a File
    5567      * @param charset the name of the encoding used to write in this file
    5568      * @param append  true if in append mode
    5569      * @return a BufferedWriter
    5570      * @throws FileNotFoundException
    5571      */
    5572     public static BufferedWriter newWriter(File file, String charset, boolean append) throws IOException {
    5573         if (append) {
    5574             return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charset));
    5575         } else {
    5576             // first write the Byte Order Mark for Unicode encodings
    5577             FileOutputStream stream = new FileOutputStream(file);
    5578             if ("UTF-16BE".equals(charset)) {
    5579                 writeUtf16Bom(stream, true);
    5580             } else if ("UTF-16LE".equals(charset)) {
    5581                 writeUtf16Bom(stream, false);
    5582             }
    5583             return new BufferedWriter(new OutputStreamWriter(stream, charset));
    5584         }
    5585     }
    5586
    5587     /***
    5588      * Helper method to create a buffered writer for a file
    5589      *
    5590      * @param file    a File
    5591      * @param charset the name of the encoding used to write in this file
    5592      * @return a BufferedWriter
    5593      * @throws FileNotFoundException
    5594      */
    5595     public static BufferedWriter newWriter(File file, String charset) throws IOException {
    5596         return newWriter(file, charset, false);
    5597     }
    5598
    5599     /***
    5600      * Write a Byte Order Mark at the begining of the file
    5601      *
    5602      * @param stream    the FileOuputStream to write the BOM to
    5603      * @param bigEndian true if UTF 16 Big Endian or false if Low Endian
    5604      * @throws IOException
    5605      */
    5606     private static void writeUtf16Bom(FileOutputStream stream, boolean bigEndian) throws IOException {
    5607         if (bigEndian) {
    5608             stream.write(-2);
    5609             stream.write(-1);
    5610         } else {
    5611             stream.write(-1);
    5612             stream.write(-2);
    5613         }
    5614     }
    5615
    5616     /***
    5617      * Helper method to create a new BufferedWriter for a file and then
    5618      * passes it into the closure and ensures it is closed again afterwords
    5619      *
    5620      * @param file    a File
    5621      * @param closure a closure
    5622      * @throws FileNotFoundException
    5623      */
    5624     public static void withWriter(File file, Closure closure) throws IOException {
    5625         withWriter(newWriter(file), closure);
    5626     }
    5627
    5628     /***
    5629      * Helper method to create a new BufferedWriter for a file in a specified encoding
    5630      * and then passes it into the closure and ensures it is closed again afterwords
    5631      *
    5632      * @param file    a File
    5633      * @param charset the charset used
    5634      * @param closure a closure
    5635      * @throws FileNotFoundException
    5636      */
    5637     public static void withWriter(File file, String charset, Closure closure) throws IOException {
    5638         withWriter(newWriter(file, charset), closure);
    5639     }
    5640
    5641     /***
    5642      * Helper method to create a new BufferedWriter for a file in a specified encoding
    5643      * in append mode and then passes it into the closure and ensures it is closed again afterwords
    5644      *
    5645      * @param file    a File
    5646      * @param charset the charset used
    5647      * @param closure a closure
    5648      * @throws FileNotFoundException
    5649      */
    5650     public static void withWriterAppend(File file, String charset, Closure closure) throws IOException {
    5651         withWriter(newWriter(file, charset, true), closure);
    5652     }
    5653
    5654     /***
    5655      * Helper method to create a new PrintWriter for a file
    5656      *
    5657      * @param file a File
    5658      * @throws FileNotFoundException
    5659      */
    5660     public static PrintWriter newPrintWriter(File file) throws IOException {
    5661         return new PrintWriter(newWriter(file));
    5662     }
    5663
    5664     /***
    5665      * Helper method to create a new PrintWriter for a file with a specified charset
    5666      *
    5667      * @param file    a File
    5668      * @param charset the charset
    5669      * @return a PrintWriter
    5670      * @throws FileNotFoundException
    5671      */
    5672     public static PrintWriter newPrintWriter(File file, String charset) throws IOException {
    5673         return new PrintWriter(newWriter(file, charset));
    5674     }
    5675
    5676     /***
    5677      * Helper method to create a new PrintWriter for a file and then
    5678      * passes it into the closure and ensures its closed again afterwords
    5679      *
    5680      * @param file a File
    5681      * @throws FileNotFoundException
    5682      */
    5683     public static void withPrintWriter(File file, Closure closure) throws IOException {
    5684         withWriter(newPrintWriter(file), closure);
    5685     }
    5686
    5687     /***
    5688      * Allows a writer to be used, calling the closure with the writer
    5689      * and then ensuring that the writer is closed down again irrespective
    5690      * of whether exceptions occur or the
    5691      *
    5692      * @param writer  the writer which is used and then closed
    5693      * @param closure the closure that the writer is passed into
    5694      * @throws IOException
    5695      */
    5696     public static void withWriter(Writer writer, Closure closure) throws IOException {
    5697         try {
    5698             closure.call(writer);
    5699
    5700             // lets try close the writer & throw the exception if it fails
    5701             // but not try to reclose it in the finally block
    5702             Writer temp = writer;
    5703             writer = null;
    5704             temp.close();
    5705         } finally {
    5706             if (writer != null) {
    5707                 try {
    5708                     writer.close();
    5709                 } catch (IOException e) {
    5710                     log.warning("Caught exception closing writer: " + e);
    5711                 }
    5712             }
    5713         }
    5714     }
    5715
    5716     /***
    5717      * Allows a Reader to be used, calling the closure with the writer
    5718      * and then ensuring that the writer is closed down again irrespective
    5719      * of whether exceptions occur or the
    5720      *
    5721      * @param writer  the writer which is used and then closed
    5722      * @param closure the closure that the writer is passed into
    5723      * @throws IOException
    5724      */
    5725     public static void withReader(Reader writer, Closure closure) throws IOException {
    5726         try {
    5727             closure.call(writer);
    5728
    5729             // lets try close the writer & throw the exception if it fails
    5730             // but not try to reclose it in the finally block
    5731             Reader temp = writer;
    5732             writer = null;
    5733             temp.close();
    5734         } finally {
    5735             if (writer != null) {
    5736                 try {
    5737                     writer.close();
    5738                 } catch (IOException e) {
    5739                     log.warning("Caught exception closing writer: " + e);
    5740                 }
    5741             }
    5742         }
    5743     }
    5744
    5745     /***
    5746      * Allows a InputStream to be used, calling the closure with the stream
    5747      * and then ensuring that the stream is closed down again irrespective
    5748      * of whether exceptions occur or the
    5749      *
    5750      * @param stream  the stream which is used and then closed
    5751      * @param closure the closure that the stream is passed into
    5752      * @throws IOException
    5753      */
    5754     public static void withStream(InputStream stream, Closure closure) throws IOException {
    5755         try {
    5756             closure.call(stream);
    5757
    5758             // lets try close the stream & throw the exception if it fails
    5759             // but not try to reclose it in the finally block
    5760             InputStream temp = stream;
    5761             stream = null;
    5762             temp.close();
    5763         } finally {
    5764             if (stream != null) {
    5765                 try {
    5766                     stream.close();
    5767                 } catch (IOException e) {
    5768                     log.warning("Caught exception closing stream: " + e);
    5769                 }
    5770             }
    5771         }
    5772     }
    5773
    5774     /***
    5775      * Reads the stream into a list of Strings for each line
    5776      *
    5777      * @param stream a stream
    5778      * @return a List of lines
    5779      * @throws IOException
    5780      */
    5781     public static List readLines(InputStream stream) throws IOException {
    5782         return readLines(new BufferedReader(new InputStreamReader(stream)));
    5783     }
    5784
    5785     /***
    5786      * Iterates through the given stream line by line
    5787      *
    5788      * @param stream  a stream
    5789      * @param closure a closure
    5790      * @throws IOException
    5791      */
    5792     public static void eachLine(InputStream stream, Closure closure) throws IOException {
    5793         eachLine(new InputStreamReader(stream), closure);
    5794     }
    5795
    5796     /***
    5797      * Iterates through the lines read from the URL's associated input stream
    5798      *
    5799      * @param url     a URL to open and read
    5800      * @param closure a closure to apply on each line
    5801      * @throws IOException
    5802      */
    5803     public static void eachLine(URL url, Closure closure) throws IOException {
    5804         eachLine(url.openConnection().getInputStream(), closure);
    5805     }
    5806
    5807     /***
    5808      * Helper method to create a new BufferedReader for a URL and then
    5809      * passes it into the closure and ensures its closed again afterwords
    5810      *
    5811      * @param url a URL
    5812      * @throws FileNotFoundException
    5813      */
    5814     public static void withReader(URL url, Closure closure) throws IOException {
    5815         withReader(url.openConnection().getInputStream(), closure);
    5816     }
    5817
    5818     /***
    5819      * Helper method to create a new BufferedReader for a stream and then
    5820      * passes it into the closure and ensures its closed again afterwords
    5821      *
    5822      * @param in a stream
    5823      * @throws FileNotFoundException
    5824      */
    5825     public static void withReader(InputStream in, Closure closure) throws IOException {
    5826         withReader(new InputStreamReader(in), closure);
    5827     }
    5828
    5829     /***
    5830      * Allows an output stream to be used, calling the closure with the output stream
    5831      * and then ensuring that the output stream is closed down again irrespective
    5832      * of whether exceptions occur
    5833      *
    5834      * @param stream  the stream which is used and then closed
    5835      * @param closure the closure that the writer is passed into
    5836      * @throws IOException
    5837      */
    5838     public static void withWriter(OutputStream stream, Closure closure) throws IOException {
    5839         withWriter(new OutputStreamWriter(stream), closure);
    5840     }
    5841
    5842     /***
    5843      * Allows an output stream to be used, calling the closure with the output stream
    5844      * and then ensuring that the output stream is closed down again irrespective
    5845      * of whether exceptions occur.
    5846      *
    5847      * @param stream  the stream which is used and then closed
    5848      * @param charset the charset used
    5849      * @param closure the closure that the writer is passed into
    5850      * @throws IOException
    5851      */
    5852     public static void withWriter(OutputStream stream, String charset, Closure closure) throws IOException {
    5853         withWriter(new OutputStreamWriter(stream, charset), closure);
    5854     }
    5855
    5856     /***
    5857      * Allows a OutputStream to be used, calling the closure with the stream
    5858      * and then ensuring that the stream is closed down again irrespective
    5859      * of whether exceptions occur.
    5860      *
    5861      * @param stream  the stream which is used and then closed
    5862      * @param closure the closure that the stream is passed into
    5863      * @throws IOException
    5864      */
    5865     public static void withStream(OutputStream stream, Closure closure) throws IOException {
    5866         try {
    5867             closure.call(stream);
    5868
    5869             // lets try close the stream & throw the exception if it fails
    5870             // but not try to reclose it in the finally block
    5871             OutputStream temp = stream;
    5872             stream = null;
    5873             temp.close();
    5874         } finally {
    5875             if (stream != null) {
    5876                 try {
    5877                     stream.close();
    5878                 } catch (IOException e) {
    5879                     log.warning("Caught exception closing stream: " + e);
    5880                 }
    5881             }
    5882         }
    5883     }
    5884
    5885     /***
    5886      * Helper method to create a buffered input stream for a file
    5887      *
    5888      * @param file a File
    5889      * @return a BufferedInputStream of the file
    5890      * @throws FileNotFoundException
    5891      */
    5892     public static BufferedInputStream newInputStream(File file) throws FileNotFoundException {
    5893         return new BufferedInputStream(new FileInputStream(file));
    5894     }
    5895
    5896     /***
    5897      * Traverse through each byte of the specified File
    5898      *
    5899      * @param self    a File
    5900      * @param closure a closure
    5901      */
    5902     public static void eachByte(File self, Closure closure) throws IOException {
    5903         BufferedInputStream is = newInputStream(self);
    5904         eachByte(is, closure);
    5905     }
    5906
    5907     /***
    5908      * Traverse through each byte of the specified stream
    5909      *
    5910      * @param is      stream to iterate over
    5911      * @param closure closure to apply to each byte
    5912      * @throws IOException
    5913      */
    5914     public static void eachByte(InputStream is, Closure closure) throws IOException {
    5915         try {
    5916             while (true) {
    5917                 int b = is.read();
    5918                 if (b == -1) {
    5919                     break;
    5920                 } else {
    5921                     closure.call(new Byte((byte) b));
    5922                 }
    5923             }
    5924             is.close();
    5925         } catch (IOException e) {
    5926             if (is != null) {
    5927                 try {
    5928                     is.close();
    5929                 } catch (Exception e2) {
    5930                     // ignore as we're already throwing
    5931                 }
    5932                 throw e;
    5933             }
    5934         }
    5935     }
    5936
    5937     /***
    5938      * Traverse through each byte of the specified URL
    5939      *
    5940      * @param url     url to iterate over
    5941      * @param closure closure to apply to each byte
    5942      * @throws IOException
    5943      */
    5944     public static void eachByte(URL url, Closure closure) throws IOException {
    5945         InputStream is = url.openConnection().getInputStream();
    5946         eachByte(is, closure);
    5947     }
    5948
    5949     /***
    5950      * Transforms the characters from a reader with a Closure and write them to a writer
    5951      *
    5952      * @param reader
    5953      * @param writer
    5954      * @param closure
    5955      */
    5956     public static void transformChar(Reader reader, Writer writer, Closure closure) {
    5957         int c;
    5958         try {
    5959             char[] chars = new char[1];
    5960             while ((c = reader.read()) != -1) {
    5961                 chars[0] = (char) c;
    5962                 writer.write((String) closure.call(new String(chars)));
    5963             }
    5964         } catch (IOException e) {
    5965         }
    5966     }
    5967
    5968     /***
    5969      * Transforms the lines from a reader with a Closure and write them to a writer
    5970      *
    5971      * @param reader  Lines of text to be transformed.
    5972      * @param writer  Where transformed lines are written.
    5973      * @param closure Single parameter closure that is called to transform each line of
    5974      *                text from the reader, before writing it to the writer.
    5975      */
    5976     public static void transformLine(Reader reader, Writer writer, Closure closure) throws IOException {
    5977         BufferedReader br = new BufferedReader(reader);
    5978         BufferedWriter bw = new BufferedWriter(writer);
    5979         String line;
    5980         while ((line = br.readLine()) != null) {
    5981             Object o = closure.call(line);
    5982             if (o != null) {
    5983                 bw.write(o.toString());
    5984                 bw.newLine();
    5985             }
    5986         }
    5987         bw.flush();
    5988     }
    5989
    5990     /***
    5991      * Filter the lines from a reader and write them on the writer, according to a closure
    5992      * which returns true or false.
    5993      *
    5994      * @param reader  a reader
    5995      * @param writer  a writer
    5996      * @param closure the closure which returns booleans
    5997      * @throws IOException
    5998      */
    5999     public static void filterLine(Reader reader, Writer writer, Closure closure) throws IOException {
    6000         BufferedReader br = new BufferedReader(reader);
    6001         BufferedWriter bw = new BufferedWriter(writer);
    6002         String line;
    6003         while ((line = br.readLine()) != null) {
    6004             if (InvokerHelper.asBool(closure.call(line))) {
    6005                 bw.write(line);
    6006                 bw.newLine();
    6007             }
    6008         }
    6009         bw.flush();
    6010     }
    6011
    6012     /***
    6013      * Filters the lines of a File and creates a Writeable in return to stream the filtered lines
    6014      *
    6015      * @param self    a File
    6016      * @param closure a closure which returns a boolean indicating to filter the line or not
    6017      * @return a Writable closure
    6018      * @throws IOException if <code>self</code> is not readable
    6019      */
    6020     public static Writable filterLine(final File self, final Closure closure) throws IOException {
    6021         return filterLine(newReader(self), closure);
    6022     }
    6023
    6024     /***
    6025      * Filter the lines from a File and write them on a writer, according to a closure
    6026      * which returns true or false
    6027      *
    6028      * @param self    a File
    6029      * @param writer  a writer
    6030      * @param closure a closure which returns a boolean value and takes a line as input
    6031      * @throws IOException if <code>self</code> is not readable
    6032      */
    6033     public static void filterLine(final File self, final Writer writer, final Closure closure) throws IOException {
    6034         filterLine(newReader(self), writer, closure);
    6035     }
    6036
    6037     /***
    6038      * Filter the lines of a Reader and create a Writable in return to stream the filtered lines
    6039      *
    6040      * @param reader  a reader
    6041      * @param closure a closure returning a boolean indicating to filter or not a line
    6042      * @return a Writable closure
    6043      */
    6044     public static Writable filterLine(Reader reader, final Closure closure) {
    6045         final BufferedReader br = new BufferedReader(reader);
    6046         return new Writable() {
    6047             public Writer writeTo(Writer out) throws IOException {
    6048                 BufferedWriter bw = new BufferedWriter(out);
    6049                 String line;
    6050                 while ((line = br.readLine()) != null) {
    6051                     if (InvokerHelper.asBool(closure.call(line))) {
    6052                         bw.write(line);
    6053                         bw.newLine();
    6054                     }
    6055                 }
    6056                 bw.flush();
    6057                 return out;
    6058             }
    6059
    6060             public String toString() {
    6061                 StringWriter buffer = new StringWriter();
    6062                 try {
    6063                     writeTo(buffer);
    6064                 } catch (IOException e) {
    6065                     throw new RuntimeException(e); // TODO: change this exception type
    6066                 }
    6067                 return buffer.toString();
    6068             }
    6069         };
    6070     }
    6071
    6072     /***
    6073      * Filter lines from an input stream using a closure predicate
    6074      *
    6075      * @param self      an input stream
    6076      * @param predicate a closure which returns boolean and takes a line
    6077      * @return a filtered writer
    6078      */
    6079     public static Writable filterLine(final InputStream self, final Closure predicate) {
    6080         return filterLine(newReader(self), predicate);
    6081     }
    6082
    6083     /***
    6084      * Filters lines from an input stream, writing to a writer, using a closure which
    6085      * returns boolean and takes a line.
    6086      *
    6087      * @param self      an InputStream
    6088      * @param writer    a writer to write output to
    6089      * @param predicate a closure which returns a boolean and takes a line as input
    6090      */
    6091     public static void filterLine(final InputStream self, final Writer writer, final Closure predicate)
    6092             throws IOException {
    6093         filterLine(newReader(self), writer, predicate);
    6094     }
    6095
    6096     /***
    6097      * Reads the content of the file into an array of byte
    6098      *
    6099      * @param file a File
    6100      * @return a List of Bytes
    6101      */
    6102     public static byte[] readBytes(File file) throws IOException {
    6103         byte[] bytes = new byte[(int) file.length()];
    6104         FileInputStream fileInputStream = new FileInputStream(file);
    6105         DataInputStream dis = new DataInputStream(fileInputStream);
    6106         dis.readFully(bytes);
    6107         dis.close();
    6108         return bytes;
    6109     }
    6110
    6111
    6112
    6113     // ================================
    6114     // Socket and ServerSocket methods
    6115
    6116     /***
    6117      * Allows an InputStream and an OutputStream from a Socket to be used,
    6118      * calling the closure with the streams and then ensuring that the streams are closed down again
    6119      * irrespective of whether exceptions occur.
    6120      *
    6121      * @param socket  a Socket
    6122      * @param closure a Closure
    6123      * @throws IOException
    6124      */
    6125     public static void withStreams(Socket socket, Closure closure) throws IOException {
    6126         InputStream input = socket.getInputStream();
    6127         OutputStream output = socket.getOutputStream();
    6128         try {
    6129             closure.call(new Object[]{input, output});
    6130         } finally {
    6131             try {
    6132                 input.close();
    6133             } catch (IOException e) {
    6134                 // noop
    6135             }
    6136             try {
    6137                 output.close();
    6138             } catch (IOException e) {
    6139                 // noop
    6140             }
    6141         }
    6142     }
    6143
    6144     /***
    6145      * Overloads the left shift operator to provide an append mechanism
    6146      * to add things to the output stream of a socket
    6147      *
    6148      * @param self  a Socket
    6149      * @param value a value to append
    6150      * @return a Writer
    6151      */
    6152     public static Writer leftShift(Socket self, Object value) throws IOException {
    6153         return leftShift(self.getOutputStream(), value);
    6154     }
    6155
    6156     /***
    6157      * Overloads the left shift operator to provide an append mechanism
    6158      * to add bytes to the output stream of a socket
    6159      *
    6160      * @param self  a Socket
    6161      * @param value a value to append
    6162      * @return an OutputStream
    6163      */
    6164     public static OutputStream leftShift(Socket self, byte[] value) throws IOException {
    6165         return leftShift(self.getOutputStream(), value);
    6166     }
    6167
    6168     /***
    6169      * Allow to pass a Closure to the accept methods of ServerSocket
    6170      *
    6171      * @param serverSocket a ServerSocket
    6172      * @param closure      a Closure
    6173      * @return a Socket
    6174      * @throws IOException
    6175      */
    6176     public static Socket accept(ServerSocket serverSocket, final Closure closure) throws IOException {
    6177         final Socket socket = serverSocket.accept();
    6178         new Thread(new Runnable() {
    6179             public void run() {
    6180                 try {
    6181                     closure.call(socket);
    6182                 } finally {
    6183                     try {
    6184                         socket.close();
    6185                     } catch (IOException e) {
    6186                         // noop
    6187                     }
    6188                 }
    6189             }
    6190         }).start();
    6191         return socket;
    6192     }
    6193
    6194
    6195     /***
    6196      * @param file a File
    6197      * @return a File which wraps the input file and which implements Writable
    6198      */
    6199     public static File asWritable(File file) {
    6200         return new WritableFile(file);
    6201     }
    6202
    6203     /***
    6204      * @param file     a File
    6205      * @param encoding the encoding to be used when reading the file's contents
    6206      * @return File which wraps the input file and which implements Writable
    6207      */
    6208     public static File asWritable(File file, String encoding) {
    6209         return new WritableFile(file, encoding);
    6210     }
    6211
    6212     /***
    6213      * Converts the given String into a List of strings of one character
    6214      *
    6215      * @param self a String
    6216      * @return a List of characters (a 1-character String)
    6217      */
    6218     public static List toList(String self) {
    6219         int size = self.length();
    6220         List answer = new ArrayList(size);
    6221         for (int i = 0; i < size; i++) {
    6222             answer.add(self.substring(i, i + 1));
    6223         }
    6224         return answer;
    6225     }
    6226
    6227     // Process methods
    6228     //-------------------------------------------------------------------------
    6229
    6230     /***
    6231      * An alias method so that a process appears similar to System.out, System.in, System.err;
    6232      * you can use process.in, process.out, process.err in a similar way
    6233      *
    6234      * @return an InputStream
    6235      */
    6236     public static InputStream getIn(Process self) {
    6237         return self.getInputStream();
    6238     }
    6239
    6240     /***
    6241      * Read the text of the output stream of the Process.
    6242      *
    6243      * @param self a Process
    6244      * @return the text of the output
    6245      * @throws IOException
    6246      */
    6247     public static String getText(Process self) throws IOException {
    6248         return getText(new BufferedReader(new InputStreamReader(self.getInputStream())));
    6249     }
    6250
    6251     /***
    6252      * An alias method so that a process appears similar to System.out, System.in, System.err;
    6253      * you can use process.in, process.out, process.err in a similar way
    6254      *
    6255      * @return an InputStream
    6256      */
    6257     public static InputStream getErr(Process self) {
    6258         return self.getErrorStream();
    6259     }
    6260
    6261     /***
    6262      * An alias method so that a process appears similar to System.out, System.in, System.err;
    6263      * you can use process.in, process.out, process.err in a similar way
    6264      *
    6265      * @return an OutputStream
    6266      */
    6267     public static OutputStream getOut(Process self) {
    6268         return self.getOutputStream();
    6269     }
    6270
    6271     /***
    6272      * Overloads the left shift operator to provide an append mechanism
    6273      * to pipe into a Process
    6274      *
    6275      * @param self  a Process
    6276      * @param value a value to append
    6277      * @return a Writer
    6278      */
    6279     public static Writer leftShift(Process self, Object value) throws IOException {
    6280         return leftShift(self.getOutputStream(), value);
    6281     }
    6282
    6283     /***
    6284      * Overloads the left shift operator to provide an append mechanism
    6285      * to pipe into a Process
    6286      *
    6287      * @param self  a Process
    6288      * @param value a value to append
    6289      * @return an OutputStream
    6290      */
    6291     public static OutputStream leftShift(Process self, byte[] value) throws IOException {
    6292         return leftShift(self.getOutputStream(), value);
    6293     }
    6294
    6295     /***
    6296      * Wait for the process to finish during a certain amount of time, otherwise stops the process.
    6297      *
    6298      * @param self           a Process
    6299      * @param numberOfMillis the number of milliseconds to wait before stopping the process
    6300      */
    6301     public static void waitForOrKill(Process self, long numberOfMillis) {
    6302         ProcessRunner runnable = new ProcessRunner(self);
    6303         Thread thread = new Thread(runnable);
    6304         thread.start();
    6305         runnable.waitForOrKill(numberOfMillis);
    6306     }
    6307
    6308     /***
    6309      * gets the input and error streams from a process and reads them
    6310      * to avoid the process to block due to a full ouput buffer. For this
    6311      * two Threads are started, so this method will return immediately
    6312      *
    6313      * @param self           a Process
    6314      */
    6315     public static void consumeProcessOutput(Process self) {
    6316         Dumper d = new Dumper(self.getErrorStream());
    6317         Thread t = new Thread(d);
    6318         t.start();
    6319         d = new Dumper(self.getInputStream());
    6320         t = new Thread(d);
    6321         t.start();
    6322     }
    6323
    6324     /***
    6325      * Process each regex group matched substring of the given string. If the closure
    6326      * parameter takes one argument an array with all match groups is passed to it.
    6327      * If the closure takes as many arguments as there are match groups, then each
    6328      * parameter will be one match group.
    6329      *
    6330      * @param self    the source string
    6331      * @param regex   a Regex string
    6332      * @param closure a closure with one parameter or as much parameters as groups
    6333      * @author bing ran
    6334      * @author Pilho Kim
    6335      * @author Jochen Theodorou
    6336      */
    6337     public static void eachMatch(String self, String regex, Closure closure) {
    6338         Pattern p = Pattern.compile(regex);
    6339         Matcher m = p.matcher(self);
    6340         while (m.find()) {
    6341             int count = m.groupCount();
    6342             ArrayList groups = new ArrayList();
    6343             for (int i = 0; i <= count; i++) {
    6344                 groups.add(m.group(i));
    6345             }
    6346             if (groups.size()==1 || closure.getMaximumNumberOfParameters()<groups.size()) {
    6347                 // not enough parameters there to give each group part
    6348                 // it's own parameter, so try a closure with one parameter
    6349                 // and give it all groups as a array
    6350                 closure.call((Object)groups.toArray());
    6351             } else {
    6352                 closure.call((Object[])groups.toArray());
    6353             }
    6354         }
    6355     }
    6356
    6357     /***
    6358      * Process each matched substring of the given group matcher. The object
    6359      * passed to the closure is an array of strings, matched per a successful match.
    6360      *
    6361      * @param self    the source matcher
    6362      * @param closure a closure
    6363      * @author bing ran
    6364      * @author Pilho Kim
    6365      */
    6366     public static void each(Matcher self, Closure closure) {
    6367         Matcher m = self;
    6368         while (m.find()) {
    6369             int count = m.groupCount();
    6370             ArrayList groups = new ArrayList();
    6371             for (int i = 0; i <= count; i++) {
    6372                 groups.add(m.group(i));
    6373             }
    6374             closure.call((Object[])groups.toArray());
    6375         }
    6376     }
    6377
    6378     /***
    6379      * Iterates over every element of the collection and return the index of the first object
    6380      * that matches the condition specified in the closure
    6381      *
    6382      * @param self    the iteration object over which we iterate
    6383      * @param closure the filter to perform a match on the collection
    6384      * @return an integer that is the index of the first macthed object.
    6385      */
    6386     public static int findIndexOf(Object self, Closure closure) {
    6387         int i = 0;
    6388         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) {
    6389             Object value = iter.next();
    6390             if (InvokerHelper.asBool(closure.call(value))) {
    6391                 break;
    6392             }
    6393         }
    6394         return i;
    6395     }
    6396
    6397     /***
    6398      * use() a list of categories, specifying the list as varargs:<br>
    6399      * use(CategoryClass1, CategoryClass2) { ... }<br>
    6400      * This method prevents the error of forgetting to wrap the the category
    6401      * classes in a list.
    6402      * @param self
    6403      * @param array
    6404      */
    6405 	public static void use(Object self, Object[] array) {
    6406 		if (array.length < 2)
    6407 			throw new IllegalArgumentException(
    6408 				"Expecting at least 2 arguments, a category class and a Closure");
    6409 		Closure closure;
    6410 		try {
    6411 			closure = (Closure)array[array.length - 1];
    6412 		} catch (ClassCastException e) {
    6413 			throw new IllegalArgumentException("Expecting a Closure to be the last argument");
    6414 		}
    6415 		List list = new ArrayList(array.length - 1);
    6416 		for (int i = 0; i < array.length - 1; ++i)
    6417 			list.add(array[i]);
    6418 		GroovyCategorySupport.use(list, closure);
    6419 	}
    6420
    6421     /***
    6422      * Iterates through the class loader parents until it finds a loader with a class
    6423      * named equal to org.codehaus.groovy.tools.RootLoader. If there is no such class
    6424      * null will be returned. The name has to be used because a direct compare with
    6425      * == may fail as the class may be loaded through different classloaders.
    6426      * @see org.codehaus.groovy.tools.RootLoader
    6427      */
    6428     public static ClassLoader getRootLoader(ClassLoader cl) {
    6429         while (true) {
    6430             if (cl==null) return null;
    6431             if (cl.getClass().getName().equals(RootLoader.class.getName())) return cl;
    6432             cl = cl.getParent();
    6433         }
    6434     }
    6435
    6436     /***
    6437      * A Runnable which waits for a process to complete together with a notification scheme
    6438      * allowing another thread to wait a maximum number of seconds for the process to complete
    6439      * before killing it.
    6440      */
    6441     protected static class ProcessRunner implements Runnable {
    6442         Process process;
    6443         private boolean finished;
    6444
    6445         public ProcessRunner(Process process) {
    6446             this.process = process;
    6447         }
    6448
    6449         public void run() {
    6450             try {
    6451                 process.waitFor();
    6452             } catch (InterruptedException e) {
    6453             }
    6454             synchronized (this) {
    6455                 notifyAll();
    6456                 finished = true;
    6457             }
    6458         }
    6459
    6460         public synchronized void waitForOrKill(long millis) {
    6461             if (!finished) {
    6462                 try {
    6463                     wait(millis);
    6464                 } catch (InterruptedException e) {
    6465                 }
    6466                 if (!finished) {
    6467                     process.destroy();
    6468                 }
    6469             }
    6470         }
    6471     }
    6472
    6473     protected static class RangeInfo {
    6474         protected int from, to;
    6475         protected boolean reverse;
    6476
    6477         public RangeInfo(int from, int to, boolean reverse) {
    6478             this.from = from;
    6479             this.to = to;
    6480             this.reverse = reverse;
    6481         }
    6482     }
    6483
    6484     private static class Dumper implements Runnable{
    6485         InputStream in;
    6486
    6487         public Dumper(InputStream in) {
    6488             this.in = in;
    6489         }
    6490
    6491         public void run() {
    6492             InputStreamReader isr = new InputStreamReader(in);
    6493             BufferedReader br = new BufferedReader(isr);
    6494             try {
    6495                 while (br.readLine()!=null) {}
    6496             } catch (IOException e) {
    6497                 throw new GroovyRuntimeException("exception while reading process stream",e);
    6498             }
    6499         }
    6500     }
    6501 }