001    /*
002     * $Id: DefaultGroovyMethods.java 4598 2006-12-22 20:21:21Z blackdrag $
003     *
004     * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005     *
006     * Redistribution and use of this software and associated documentation
007     * ("Software"), with or without modification, are permitted provided that the
008     * following conditions are met:
009     *  1. Redistributions of source code must retain copyright statements and
010     * notices. Redistributions must also contain a copy of this document.
011     *  2. Redistributions in binary form must reproduce the above copyright
012     * notice, this list of conditions and the following disclaimer in the
013     * documentation and/or other materials provided with the distribution.
014     *  3. The name "groovy" must not be used to endorse or promote products
015     * derived from this Software without prior written permission of The Codehaus.
016     * For written permission, please contact info@codehaus.org.
017     *  4. Products derived from this Software may not be called "groovy" nor may
018     * "groovy" appear in their names without prior written permission of The
019     * Codehaus. "groovy" is a registered trademark of The Codehaus.
020     *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
021     *
022     * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
023     * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
024     * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
025     * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
026     * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
027     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
028     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
029     * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
030     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
031     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
032     * DAMAGE.
033     *
034     */
035    package org.codehaus.groovy.runtime;
036    
037    import groovy.lang.*;
038    import groovy.util.CharsetToolkit;
039    import groovy.util.ClosureComparator;
040    import groovy.util.OrderBy;
041    
042    import java.io.*;
043    import java.lang.reflect.Array;
044    import java.lang.reflect.Field;
045    import java.lang.reflect.Modifier;
046    import java.lang.reflect.Proxy;
047    import java.math.BigDecimal;
048    import java.math.BigInteger;
049    import java.net.MalformedURLException;
050    import java.net.ServerSocket;
051    import java.net.Socket;
052    import java.net.URI;
053    import java.net.URISyntaxException;
054    import java.net.URL;
055    import java.security.AccessController;
056    import java.security.PrivilegedAction;
057    import java.util.*;
058    import java.util.logging.Logger;
059    import java.util.regex.Matcher;
060    import java.util.regex.Pattern;
061    
062    import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
063    import org.codehaus.groovy.runtime.typehandling.NumberMath;
064    import org.codehaus.groovy.tools.RootLoader;
065    import org.w3c.dom.NodeList;
066    
067    /**
068     * This class defines all the new groovy methods which appear on normal JDK
069     * classes inside the Groovy environment. Static methods are used with the
070     * first parameter the destination class.
071     *
072     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
073     * @author Jeremy Rayner
074     * @author Sam Pullara
075     * @author Rod Cope
076     * @author Guillaume Laforge
077     * @author John Wilson
078     * @author Hein Meling
079     * @author Dierk Koenig
080     * @author Pilho Kim
081     * @author Marc Guillemot
082     * @author Russel Winder
083     * @author bing ran
084     * @author Jochen Theodorou
085     * @version $Revision: 4598 $
086     */
087    public class DefaultGroovyMethods {
088    
089        private static final Logger LOG = Logger.getLogger(DefaultGroovyMethods.class.getName());
090        private static final Integer ONE = new Integer(1);
091    
092        /**
093         * Identity check. Since == is overridden in Groovy with the meaning of equality
094         * we need some fallback to check for object identity.
095         *
096         * @param self
097         * @param other
098         * @return true if self and other are identical, false otherwise
099         */
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 < 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    }