View Javadoc

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