View Javadoc

1   /*
2    * $Id: DefaultGroovyMethods.java,v 1.193 2005/11/21 20:10:57 glaforge Exp $
3    *
4    * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5    *
6    * Redistribution and use of this software and associated documentation
7    * ("Software"), with or without modification, are permitted provided that the
8    * following conditions are met:
9    *  1. Redistributions of source code must retain copyright statements and
10   * notices. Redistributions must also contain a copy of this document.
11   *  2. Redistributions in binary form must reproduce the above copyright
12   * notice, this list of conditions and the following disclaimer in the
13   * documentation and/or other materials provided with the distribution.
14   *  3. The name "groovy" must not be used to endorse or promote products
15   * derived from this Software without prior written permission of The Codehaus.
16   * For written permission, please contact info@codehaus.org.
17   *  4. Products derived from this Software may not be called "groovy" nor may
18   * "groovy" appear in their names without prior written permission of The
19   * Codehaus. "groovy" is a registered trademark of The Codehaus.
20   *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
21   *
22   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
23   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
26   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
32   * DAMAGE.
33   *
34   */
35  package org.codehaus.groovy.runtime;
36  
37  import groovy.lang.*;
38  import groovy.util.CharsetToolkit;
39  import groovy.util.ClosureComparator;
40  import groovy.util.OrderBy;
41  
42  import java.io.*;
43  import java.lang.reflect.Array;
44  import java.lang.reflect.Field;
45  import java.lang.reflect.Modifier;
46  import java.math.BigDecimal;
47  import java.math.BigInteger;
48  import java.net.MalformedURLException;
49  import java.net.ServerSocket;
50  import java.net.Socket;
51  import java.net.URL;
52  import java.security.AccessController;
53  import java.security.PrivilegedAction;
54  import java.util.*;
55  import java.util.logging.Logger;
56  import java.util.regex.Matcher;
57  import java.util.regex.Pattern;
58  
59  import org.codehaus.groovy.tools.RootLoader;
60  
61  /***
62   * This class defines all the new groovy methods which appear on normal JDK
63   * classes inside the Groovy environment. Static methods are used with the
64   * first parameter the destination class.
65   *
66   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
67   * @author Jeremy Rayner
68   * @author Sam Pullara
69   * @author Rod Cope
70   * @author Guillaume Laforge
71   * @author John Wilson
72   * @author Hein Meling
73   * @author Dierk Koenig
74   * @author Pilho Kim
75   * @version $Revision: 1.193 $
76   */
77  public class DefaultGroovyMethods {
78  
79      private static Logger log = Logger.getLogger(DefaultGroovyMethods.class.getName());
80  
81      private static final Integer ONE = new Integer(1);
82      private static final char ZERO_CHAR = '\u0000';
83  
84      /***
85       * Identity check. Since == is overridden in Groovy with the meaning of equality
86       * we need some fallback to check for object identity.
87       * @param self
88       * @param other
89       * @return true if self and other are identical, false otherwise
90       */
91      public static boolean is(Object self, Object other){
92          return self == other;
93      }
94  
95      /***
96       * Allows the closure to be called for the object reference self
97       *
98       * @param self     the object to have a closure act upon
99       * @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 &lt; 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 }