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