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