View Javadoc

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