View Javadoc

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