View Javadoc

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