View Javadoc

1   /*
2    * $Id: DefaultGroovyMethods.java,v 1.125 2005/01/04 01:52:28 spullara 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.net.MalformedURLException;
47  import java.net.ServerSocket;
48  import java.net.Socket;
49  import java.net.URL;
50  import java.security.AccessController;
51  import java.security.PrivilegedAction;
52  import java.util.*;
53  import java.util.logging.Logger;
54  import java.util.regex.Matcher;
55  import java.util.regex.Pattern;
56  
57  /***
58   * This class defines all the new groovy methods which appear on normal JDK
59   * classes inside the Groovy environment. Static methods are used with the
60   * first parameter the destination class.
61   *
62   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
63   * @author Jeremy Rayner
64   * @author Sam Pullara
65   * @author Rod Cope
66   * @author Guillaume Laforge
67   * @author John Wilson
68   * @version $Revision: 1.125 $
69   */
70  public class DefaultGroovyMethods {
71  
72      private static Logger log = Logger.getLogger(DefaultGroovyMethods.class.getName());
73  
74      private static final Integer ONE = new Integer(1);
75      private static final char ZERO_CHAR = '\u0000';
76  
77      /***
78       * Allows the subscript operator to be used to lookup dynamic property values.
79       * <code>bean[somePropertyNameExpression]</code>. The normal property notation
80       * of groovy is neater and more concise but only works with compile time known
81       * property names.
82       *
83       * @param self
84       * @return
85       */
86      public static Object getAt(Object self, String property) {
87          return InvokerHelper.getProperty(self, property);
88      }
89  
90      /***
91       * Allows the subscript operator to be used to set dynamically named property values.
92       * <code>bean[somePropertyNameExpression] = foo</code>. The normal property notation
93       * of groovy is neater and more concise but only works with compile time known
94       * property names.
95       *
96       * @param self
97       */
98      public static void putAt(Object self, String property, Object newValue) {
99          InvokerHelper.setProperty(self, property, newValue);
100     }
101 
102     /***
103      * Generates a detailed dump string of an object showing its class,
104      * hashCode and fields
105      */
106     public static String dump(Object self) {
107         if (self == null) {
108             return "null";
109         }
110         StringBuffer buffer = new StringBuffer("<");
111         Class klass = self.getClass();
112         buffer.append(klass.getName());
113         buffer.append("@");
114         buffer.append(Integer.toHexString(self.hashCode()));
115         boolean groovyObject = self instanceof GroovyObject;
116 
117         /*jes this may be rewritten to use the new allProperties() stuff
118          * but the original pulls out private variables, whereas allProperties()
119          * does not. What's the real use of dump() here?
120          */
121         while (klass != null) {
122             Field[] fields = klass.getDeclaredFields();
123             for (int i = 0; i < fields.length; i++) {
124                 final Field field = fields[i];
125                 if ((field.getModifiers() & Modifier.STATIC) == 0) {
126                     if (groovyObject && field.getName().equals("metaClass")) {
127                         continue;
128                     }
129                     AccessController.doPrivileged(new PrivilegedAction() {
130                         public Object run() {
131                             field.setAccessible(true);
132                             return null;
133                         }
134                     });
135                     buffer.append(" ");
136                     buffer.append(field.getName());
137                     buffer.append("=");
138                     try {
139                         buffer.append(InvokerHelper.toString(field.get(self)));
140                     } catch (Exception e) {
141                         buffer.append(e);
142                     }
143                 }
144             }
145 
146             klass = klass.getSuperclass();
147         }
148 
149         /* here is a different implementation that uses allProperties(). I have left
150          * it commented out because it returns a slightly different list of properties;
151          * ie it does not return privates. I don't know what dump() really should be doing,
152          * although IMO showing private fields is a no-no
153          */
154         /*
155         List props = allProperties(self);
156 for(Iterator itr = props.iterator(); itr.hasNext(); ) {
157 PropertyValue pv = (PropertyValue) itr.next();
158 
159             // the original skipped this, so I will too
160             if(pv.getName().equals("metaClass")) continue;
161             if(pv.getName().equals("class")) continue;
162 
163             buffer.append(" ");
164             buffer.append(pv.getName());
165             buffer.append("=");
166             try {
167                 buffer.append(InvokerHelper.toString(pv.getValue()));
168             }
169             catch (Exception e) {
170                 buffer.append(e);
171             }
172 }
173         */
174 
175         buffer.append(">");
176         return buffer.toString();
177     }
178 
179     public static void eachPropertyName(Object self, Closure closure) {
180         List props = allProperties(self);
181         for (Iterator itr = props.iterator(); itr.hasNext();) {
182             PropertyValue pv = (PropertyValue) itr.next();
183             closure.call(pv.getName());
184         }
185     }
186 
187     public static void eachProperty(Object self, Closure closure) {
188         List props = allProperties(self);
189         for (Iterator itr = props.iterator(); itr.hasNext();) {
190             PropertyValue pv = (PropertyValue) itr.next();
191             closure.call(pv);
192         }
193     }
194 
195     public static List allProperties(Object self) {
196         List props = new ArrayList();
197         MetaClass metaClass = InvokerHelper.getMetaClass(self);
198 
199         List mps;
200 
201         if (self instanceof groovy.util.Expando) {
202             mps = ((groovy.util.Expando) self).getProperties();
203         } else {
204             // get the MetaProperty list from the MetaClass
205             mps = metaClass.getProperties();
206         }
207 
208         for (Iterator itr = mps.iterator(); itr.hasNext();) {
209             MetaProperty mp = (MetaProperty) itr.next();
210             PropertyValue pv = new PropertyValue(self, mp);
211             props.add(pv);
212         }
213 
214         return props;
215     }
216 
217     /***
218      * Scoped use method
219      */
220     public static void use(Object self, Class categoryClass, Closure closure) {
221         GroovyCategorySupport.use(categoryClass, closure);
222     }
223 
224     /***
225      * Scoped use method with list of categories
226      */
227     public static void use(Object self, List categoryClassList, Closure closure) {
228         GroovyCategorySupport.use(categoryClassList, closure);
229     }
230 
231 
232     /***
233      * Print to a console in interactive format
234      */
235     public static void print(Object self, Object value) {
236         System.out.print(InvokerHelper.toString(value));
237     }
238 
239     /***
240      * Print a linebreak to the standard out.
241      */
242     public static void println(Object self) {
243         System.out.println();
244     }
245 
246     /***
247      * Print to a console in interactive format along with a newline
248      */
249     public static void println(Object self, Object value) {
250         System.out.println(InvokerHelper.toString(value));
251     }
252 
253     /***
254      * @return a String that matches what would be typed into a terminal to
255      *         create this object. e.g. [1, 'hello'].inspect() -> [1, "hello"]
256      */
257     public static String inspect(Object self) {
258         return InvokerHelper.inspect(self);
259     }
260 
261     /***
262      * Print to a console in interactive format
263      */
264     public static void print(Object self, PrintWriter out) {
265         if (out == null) {
266             out = new PrintWriter(System.out);
267         }
268         out.print(InvokerHelper.toString(self));
269     }
270 
271     /***
272      * Print to a console in interactive format
273      *
274      * @param out the PrintWriter used for printing
275      */
276     public static void println(Object self, PrintWriter out) {
277         if (out == null) {
278             out = new PrintWriter(System.out);
279         }
280         InvokerHelper.invokeMethod(self, "print", out);
281         out.println();
282     }
283 
284     /***
285      * Provide a dynamic method invocation method which can be overloaded in
286      * classes to implement dynamic proxies easily.
287      */
288     public static Object invokeMethod(Object object, String method, Object arguments) {
289         return InvokerHelper.invokeMethod(object, method, arguments);
290     }
291 
292     // isCase methods
293     //-------------------------------------------------------------------------
294     public static boolean isCase(Object caseValue, Object switchValue) {
295         return caseValue.equals(switchValue);
296     }
297 
298     public static boolean isCase(String caseValue, Object switchValue) {
299         if (switchValue == null) {
300             return caseValue == null;
301         }
302         return caseValue.equals(switchValue.toString());
303     }
304 
305     public static boolean isCase(Class caseValue, Object switchValue) {
306         return caseValue.isInstance(switchValue);
307     }
308 
309     public static boolean isCase(Collection caseValue, Object switchValue) {
310         return caseValue.contains(switchValue);
311     }
312 
313     public static boolean isCase(Pattern caseValue, Object switchValue) {
314         Matcher matcher = caseValue.matcher(switchValue.toString());
315         if (matcher.matches()) {
316             RegexSupport.setLastMatcher(matcher);
317             return true;
318         } else {
319             return false;
320         }
321     }
322 
323     // Collection based methods
324     //-------------------------------------------------------------------------
325 
326     /***
327      * Allows objects to be iterated through using a closure
328      *
329      * @param self    the object over which we iterate
330      * @param closure the closure applied on each element found
331      */
332     public static void each(Object self, Closure closure) {
333         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
334             closure.call(iter.next());
335         }
336     }
337 
338     /***
339      * Allows object to be iterated through a closure with a counter
340      *
341      * @param self    an Object
342      * @param closure a Closure
343      */
344     public static void eachWithIndex(Object self, Closure closure) {
345         int counter = 0;
346         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
347             closure.call(new Object[]{iter.next(), new Integer(counter++)});
348         }
349     }
350 
351     /***
352      * Allows objects to be iterated through using a closure
353      *
354      * @param self    the collection over which we iterate
355      * @param closure the closure applied on each element of the collection
356      */
357     public static void each(Collection self, Closure closure) {
358         for (Iterator iter = self.iterator(); iter.hasNext();) {
359             closure.call(iter.next());
360         }
361     }
362 
363     /***
364      * Allows a Map to be iterated through using a closure. If the
365      * closure takes one parameter then it will be passed the Map.Entry
366      * otherwise if the closure takes two parameters then it will be
367      * passed the key and the value.
368      *
369      * @param self    the map over which we iterate
370      * @param closure the closure applied on each entry of the map
371      */
372     public static void each(Map self, Closure closure) {
373         if (closure.getParameterTypes().length == 2) {
374             for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
375                 Map.Entry entry = (Map.Entry) iter.next();
376                 closure.call(new Object[]{entry.getKey(), entry.getValue()});
377             }
378         } else {
379             for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
380                 closure.call(iter.next());
381             }
382         }
383     }
384 
385     /***
386      * Iterates over every element of a collection, and check whether a predicate is valid for all elements.
387      *
388      * @param self    the object over which we iterate
389      * @param closure the closure predicate used for matching
390      * @return true if every item in the collection matches the closure
391      *         predicate
392      */
393     public static boolean every(Object self, Closure closure) {
394         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
395             if (!InvokerHelper.asBool(closure.call(iter.next()))) {
396                 return false;
397             }
398         }
399         return true;
400     }
401 
402     /***
403      * Iterates over every element of a collection, and check whether a predicate is valid for at least one element
404      *
405      * @param self    the object over which we iterate
406      * @param closure the closure predicate used for matching
407      * @return true if any item in the collection matches the closure predicate
408      */
409     public static boolean any(Object self, Closure closure) {
410         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
411             if (InvokerHelper.asBool(closure.call(iter.next()))) {
412                 return true;
413             }
414         }
415         return false;
416     }
417 
418     /***
419      * Iterates over every element of the collection and return each object that matches
420      * the given filter - calling the isCase() method used by switch statements.
421      * This method can be used with different kinds of filters like regular expresions, classes, ranges etc.
422      *
423      * @param self   the object over which we iterate
424      * @param filter the filter to perform on the collection (using the isCase(object) method)
425      * @return a list of objects which match the filter
426      */
427     public static List grep(Object self, Object filter) {
428         List answer = new ArrayList();
429         MetaClass metaClass = InvokerHelper.getMetaClass(filter);
430         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
431             Object object = iter.next();
432             if (InvokerHelper.asBool(metaClass.invokeMethod(filter, "isCase", object))) {
433                 answer.add(object);
434             }
435         }
436         return answer;
437     }
438 
439     /***
440      * Counts the number of occurencies of the given value inside this collection
441      *
442      * @param self  the collection within which we count the number of occurencies
443      * @param value the value
444      * @return the number of occurrencies
445      */
446     public static int count(Collection self, Object value) {
447         int answer = 0;
448         for (Iterator iter = self.iterator(); iter.hasNext();) {
449             if (InvokerHelper.compareEqual(iter.next(), value)) {
450                 ++answer;
451             }
452         }
453         return answer;
454     }
455 
456     /***
457      * Convert a collection to a List.
458      *
459      * @param self a collection
460      * @return a List
461      */
462     public static List toList(Collection self) {
463         List answer = new ArrayList(self.size());
464         answer.addAll(self);
465         return answer;
466     }
467 
468     /***
469      * Iterates through this object transforming each object into a new value using the closure
470      * as a transformer, returning a list of transformed values.
471      *
472      * @param self    the values of the object to map
473      * @param closure the closure used to map each element of the collection
474      * @return a List of the mapped values
475      */
476     public static List collect(Object self, Closure closure) {
477         return (List) collect(self, new ArrayList(), closure);
478     }
479 
480     /***
481      * Iterates through this object transforming each object into a new value using the closure
482      * as a transformer and adding it to the collection, returning the resulting collection.
483      *
484      * @param self       the values of the object to map
485      * @param collection the Collection to which the mapped values are added
486      * @param closure    the closure used to map each element of the collection
487      * @return the resultant collection
488      */
489     public static Collection collect(Object self, Collection collection, Closure closure) {
490         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
491             collection.add(closure.call(iter.next()));
492         }
493         return collection;
494     }
495 
496     /***
497      * Iterates through this collection transforming each entry into a new value using the closure
498      * as a transformer, returning a list of transformed values.
499      *
500      * @param self    a collection
501      * @param closure the closure used for mapping
502      * @return a List of the mapped values
503      */
504     public static List collect(Collection self, Closure closure) {
505         return (List) collect(self, new ArrayList(self.size()), closure);
506     }
507 
508     /***
509      * Iterates through this collection transforming each entry into a new value using the closure
510      * as a transformer, returning a list of transformed values.
511      *
512      * @param self       a collection
513      * @param collection the Collection to which the mapped values are added
514      * @param closure    the closure used to map each element of the collection
515      * @return the resultant collection
516      */
517     public static Collection collect(Collection self, Collection collection, Closure closure) {
518         for (Iterator iter = self.iterator(); iter.hasNext();) {
519             collection.add(closure.call(iter.next()));
520             if (closure.getDirective() == Closure.DONE) {
521                 break;
522             }
523         }
524         return collection;
525     }
526 
527     /***
528      * Iterates through this Map transforming each entry into a new value using the closure
529      * as a transformer, returning a list of transformed values.
530      *
531      * @param self    a Map
532      * @param closure the closure used for mapping
533      * @return a List of the mapped values
534      */
535     public static Collection collect(Map self, Collection collection, Closure closure) {
536         for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
537             collection.add(closure.call(iter.next()));
538         }
539         return collection;
540     }
541 
542     /***
543      * Iterates through this Map transforming each entry into a new value using the closure
544      * as a transformer, returning a list of transformed values.
545      *
546      * @param self       a Map
547      * @param collection the Collection to which the mapped values are added
548      * @param closure    the closure used to map each element of the collection
549      * @return the resultant collection
550      */
551     public static List collect(Map self, Closure closure) {
552         return (List) collect(self, new ArrayList(self.size()), closure);
553     }
554 
555     /***
556      * Finds the first value matching the closure condition
557      *
558      * @param self    an Object with an iterator returning its values
559      * @param closure a closure condition
560      * @return the first Object found
561      */
562     public static Object find(Object self, Closure closure) {
563         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
564             Object value = iter.next();
565             if (InvokerHelper.asBool(closure.call(value))) {
566                 return value;
567             }
568         }
569         return null;
570     }
571 
572     /***
573      * Finds the first value matching the closure condition
574      *
575      * @param self    a Collection
576      * @param closure a closure condition
577      * @return the first Object found
578      */
579     public static Object find(Collection self, Closure closure) {
580         for (Iterator iter = self.iterator(); iter.hasNext();) {
581             Object value = iter.next();
582             if (InvokerHelper.asBool(closure.call(value))) {
583                 return value;
584             }
585         }
586         return null;
587     }
588 
589     /***
590      * Finds the first value matching the closure condition
591      *
592      * @param self    a Map
593      * @param closure a closure condition
594      * @return the first Object found
595      */
596     public static Object find(Map self, Closure closure) {
597         for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
598             Object value = iter.next();
599             if (InvokerHelper.asBool(closure.call(value))) {
600                 return value;
601             }
602         }
603         return null;
604     }
605 
606     /***
607      * Finds all values matching the closure condition
608      *
609      * @param self    an Object with an Iterator returning its values
610      * @param closure a closure condition
611      * @return a List of the values found
612      */
613     public static List findAll(Object self, Closure closure) {
614         List answer = new ArrayList();
615         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
616             Object value = iter.next();
617             if (InvokerHelper.asBool(closure.call(value))) {
618                 answer.add(value);
619             }
620         }
621         return answer;
622     }
623 
624     /***
625      * Finds all values matching the closure condition
626      *
627      * @param self    a Collection
628      * @param closure a closure condition
629      * @return a List of the values found
630      */
631     public static List findAll(Collection self, Closure closure) {
632         List answer = new ArrayList(self.size());
633         for (Iterator iter = self.iterator(); iter.hasNext();) {
634             Object value = iter.next();
635             if (InvokerHelper.asBool(closure.call(value))) {
636                 answer.add(value);
637             }
638         }
639         return answer;
640     }
641 
642     /***
643      * Finds all values matching the closure condition
644      *
645      * @param self    a Map
646      * @param closure a closure condition applying on the keys
647      * @return a List of keys
648      */
649     public static List findAll(Map self, Closure closure) {
650         List answer = new ArrayList(self.size());
651         for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
652             Object value = iter.next();
653             if (InvokerHelper.asBool(closure.call(value))) {
654                 answer.add(value);
655             }
656         }
657         return answer;
658     }
659 
660     /***
661      * Iterates through the given collection, passing in the initial value to
662      * the closure along with the current iterated item then passing into the
663      * next iteration the value of the previous closure.
664      *
665      * @param self    a Collection
666      * @param value   a value
667      * @param closure a closure
668      * @return the last value of the last iteration
669      */
670     public static Object inject(Collection self, Object value, Closure closure) {
671         Object[] params = new Object[2];
672         for (Iterator iter = self.iterator(); iter.hasNext();) {
673             Object item = iter.next();
674             params[0] = value;
675             params[1] = item;
676             value = closure.call(params);
677         }
678         return value;
679     }
680 
681     /***
682      * Concatenates all of the items of the collection together with the given String as a separator
683      *
684      * @param self      a Collection of objects
685      * @param separator a String separator
686      * @return the joined String
687      */
688     public static String join(Collection self, String separator) {
689         StringBuffer buffer = new StringBuffer();
690         boolean first = true;
691         for (Iterator iter = self.iterator(); iter.hasNext();) {
692             Object value = iter.next();
693             if (first) {
694                 first = false;
695             } else {
696                 buffer.append(separator);
697             }
698             buffer.append(InvokerHelper.toString(value));
699         }
700         return buffer.toString();
701     }
702 
703     /***
704      * Concatenates all of the elements of the array together with the given String as a separator
705      *
706      * @param self      an array of Object
707      * @param separator a String separator
708      * @return the joined String
709      */
710     public static String join(Object[] self, String separator) {
711         StringBuffer buffer = new StringBuffer();
712         boolean first = true;
713         for (int i = 0; i < self.length; i++) {
714             String value = InvokerHelper.toString(self[i]);
715             if (first) {
716                 first = false;
717             } else {
718                 buffer.append(separator);
719             }
720             buffer.append(value);
721         }
722         return buffer.toString();
723     }
724 
725     /***
726      * Selects the maximum value found in the collection
727      *
728      * @param self a Collection
729      * @return the maximum value
730      */
731     public static Object max(Collection self) {
732         Object answer = null;
733         for (Iterator iter = self.iterator(); iter.hasNext();) {
734             Object value = iter.next();
735             if (value != null) {
736                 if (answer == null || InvokerHelper.compareGreaterThan(value, answer)) {
737                     answer = value;
738                 }
739             }
740         }
741         return answer;
742     }
743 
744     /***
745      * Selects the maximum value found in the collection using the given comparator
746      *
747      * @param self       a Collection
748      * @param comparator a Comparator
749      * @return the maximum value
750      */
751     public static Object max(Collection self, Comparator comparator) {
752         Object answer = null;
753         for (Iterator iter = self.iterator(); iter.hasNext();) {
754             Object value = iter.next();
755             if (answer == null || comparator.compare(value, answer) > 0) {
756                 answer = value;
757             }
758         }
759         return answer;
760     }
761 
762     /***
763      * Selects the minimum value found in the collection
764      *
765      * @param self a Collection
766      * @return the minimum value
767      */
768     public static Object min(Collection self) {
769         Object answer = null;
770         for (Iterator iter = self.iterator(); iter.hasNext();) {
771             Object value = iter.next();
772             if (value != null) {
773                 if (answer == null || InvokerHelper.compareLessThan(value, answer)) {
774                     answer = value;
775                 }
776             }
777         }
778         return answer;
779     }
780 
781     /***
782      * Selects the minimum value found in the collection using the given comparator
783      *
784      * @param self       a Collection
785      * @param comparator a Comparator
786      * @return the minimum value
787      */
788     public static Object min(Collection self, Comparator comparator) {
789         Object answer = null;
790         for (Iterator iter = self.iterator(); iter.hasNext();) {
791             Object value = iter.next();
792             if (answer == null || comparator.compare(value, answer) < 0) {
793                 answer = value;
794 
795             }
796         }
797         return answer;
798     }
799 
800     /***
801      * Selects the minimum value found in the collection using the given closure as a comparator
802      *
803      * @param self    a Collection
804      * @param closure a closure used as a comparator
805      * @return the minimum value
806      */
807     public static Object min(Collection self, Closure closure) {
808         return min(self, new ClosureComparator(closure));
809     }
810 
811     /***
812      * Selects the maximum value found in the collection using the given closure as a comparator
813      *
814      * @param self    a Collection
815      * @param closure a closure used as a comparator
816      * @return the maximum value
817      */
818     public static Object max(Collection self, Closure closure) {
819         return max(self, new ClosureComparator(closure));
820     }
821 
822     /***
823      * Makes a String look like a Collection by adding support for the size() method
824      *
825      * @param text a String
826      * @return the length of the String
827      */
828     public static int size(String text) {
829         return text.length();
830     }
831 
832     /***
833      * Makes an Array look like a Collection by adding support for the size() method
834      *
835      * @param self an Array of Object
836      * @return the size of the Array
837      */
838     public static int size(Object[] self) {
839         return self.length;
840     }
841 
842     /***
843      * Support the subscript operator for String.
844      *
845      * @param text  a String
846      * @param index the index of the Character to get
847      * @return the Character at the given index
848      */
849     public static CharSequence getAt(CharSequence text, int index) {
850         index = normaliseIndex(index, text.length());
851         return text.subSequence(index, index + 1);
852     }
853 
854     /***
855      * Support the subscript operator for String
856      *
857      * @param text a String
858      * @return the Character object at the given index
859      */
860     public static String getAt(String text, int index) {
861         index = normaliseIndex(index, text.length());
862         return text.substring(index, index + 1);
863     }
864 
865     /***
866      * Support the range subscript operator for CharSequence
867      *
868      * @param text  a CharSequence
869      * @param range a Range
870      * @return the subsequence CharSequence
871      */
872     public static CharSequence getAt(CharSequence text, Range range) {
873         int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), text.length());
874         int to = normaliseIndex(InvokerHelper.asInt(range.getTo()), text.length());
875 
876         // if this is a backwards range, reverse the arguments to substring
877         if (from > to) {
878             int tmp = from;
879             from = to;
880             to = tmp;
881         }
882 
883         return text.subSequence(from, to + 1);
884     }
885 
886     /***
887      * Support the range subscript operator for String
888      *
889      * @param text  a String
890      * @param range a Range
891      * @return a substring corresponding to the Range
892      */
893     public static String getAt(String text, Range range) {
894         int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), text.length());
895         int to = normaliseIndex(InvokerHelper.asInt(range.getTo()), text.length());
896 
897         // if this is a backwards range, reverse the arguments to substring
898         boolean reverse = range.isReverse();
899         if (from > to) {
900             int tmp = to;
901             to = from;
902             from = tmp;
903             reverse = !reverse;
904         }
905 
906         String answer = text.substring(from, to + 1);
907         if (reverse) {
908             answer = reverse(answer);
909         }
910         return answer;
911     }
912 
913     /***
914      * Creates a new string which is the reverse (backwards) of this string
915      *
916      * @param self a String
917      * @return a new string with all the characters reversed.
918      */
919     public static String reverse(String self) {
920         int size = self.length();
921         StringBuffer buffer = new StringBuffer(size);
922         for (int i = size - 1; i >= 0; i--) {
923             buffer.append(self.charAt(i));
924         }
925         return buffer.toString();
926     }
927 
928     /***
929      * Transforms a String representing a URL into a URL object.
930      *
931      * @param self the String representing a URL
932      * @return a URL
933      * @throws MalformedURLException is thrown if the URL is not well formed.
934      */
935     public static URL toURL(String self) throws MalformedURLException {
936         return new URL(self);
937     }
938 
939     private static String getPadding(String padding, int length) {
940         if (padding.length() < length) {
941             return multiply(padding, new Integer(length / padding.length() + 1)).substring(0, length);
942         } else {
943             return padding.substring(0, length);
944         }
945     }
946 
947     /***
948      * Pad a String with the characters appended to the left
949      *
950      * @param numberOfChars the total number of characters
951      * @param padding       the charaters used for padding
952      * @return the String padded to the left
953      */
954     public static String padLeft(String self, Number numberOfChars, String padding) {
955         int numChars = numberOfChars.intValue();
956         if (numChars <= self.length()) {
957             return self;
958         } else {
959             return getPadding(padding, numChars - self.length()) + self;
960         }
961     }
962 
963     /***
964      * Pad a String with the spaces appended to the left
965      *
966      * @param numberOfChars the total number of characters
967      * @return the String padded to the left
968      */
969 
970     public static String padLeft(String self, Number numberOfChars) {
971         return padLeft(self, numberOfChars, " ");
972     }
973 
974     /***
975      * Pad a String with the characters appended to the right
976      *
977      * @param numberOfChars the total number of characters
978      * @param padding       the charaters used for padding
979      * @return the String padded to the right
980      */
981 
982     public static String padRight(String self, Number numberOfChars, String padding) {
983         int numChars = numberOfChars.intValue();
984         if (numChars <= self.length()) {
985             return self;
986         } else {
987             return self + getPadding(padding, numChars - self.length());
988         }
989     }
990 
991     /***
992      * Pad a String with the spaces appended to the right
993      *
994      * @param numberOfChars the total number of characters
995      * @return the String padded to the right
996      */
997 
998     public static String padRight(String self, Number numberOfChars) {
999         return padRight(self, numberOfChars, " ");
1000     }
1001 
1002     /***
1003      * Center a String and padd it with the characters appended around it
1004      *
1005      * @param numberOfChars the total number of characters
1006      * @param padding       the charaters used for padding
1007      * @return the String centered with padded character around
1008      */
1009     public static String center(String self, Number numberOfChars, String padding) {
1010         int numChars = numberOfChars.intValue();
1011         if (numChars <= self.length()) {
1012             return self;
1013         } else {
1014             int charsToAdd = numChars - self.length();
1015             String semiPad = charsToAdd % 2 == 1 ?
1016                     getPadding(padding, charsToAdd / 2 + 1) :
1017                     getPadding(padding, charsToAdd / 2);
1018             if (charsToAdd % 2 == 0)
1019                 return semiPad + self + semiPad;
1020             else
1021                 return semiPad.substring(0, charsToAdd / 2) + self + semiPad;
1022         }
1023     }
1024 
1025     /***
1026      * Center a String and padd it with spaces appended around it
1027      *
1028      * @param numberOfChars the total number of characters
1029      * @return the String centered with padded character around
1030      */
1031     public static String center(String self, Number numberOfChars) {
1032         return center(self, numberOfChars, " ");
1033     }
1034 
1035     /***
1036      * Support the subscript operator for a regex Matcher
1037      *
1038      * @param matcher a Matcher
1039      * @param idx     an index
1040      * @return the group at the given index
1041      */
1042     public static String getAt(Matcher matcher, int idx) {
1043         matcher.reset();
1044         idx = normaliseIndex(idx, matcher.groupCount());
1045 
1046         // are we using groups?
1047         if (matcher.groupCount() > 0) {
1048             // yes, so return the specified group
1049             matcher.find();
1050             return matcher.group(idx);
1051         } else {
1052             // not using groups, so return the nth
1053             // occurrence of the pattern
1054             for (int i = 0; i <= idx; i++) {
1055                 matcher.find();
1056             }
1057             return matcher.group();
1058         }
1059     }
1060 
1061     /***
1062      * Support the range subscript operator for a List
1063      *
1064      * @param self  a List
1065      * @param range a Range
1066      * @return a range of a list from the range's from index up to but not including the ranges's to value
1067      */
1068     public static List getAt(List self, Range range) {
1069         int size = self.size();
1070         int from = normaliseIndex(InvokerHelper.asInt(range.getFrom()), size);
1071         int to = normaliseIndex(InvokerHelper.asInt(range.getTo()), size);
1072         boolean reverse = range.isReverse();
1073         if (from > to) {
1074             int tmp = to;
1075             to = from;
1076             from = tmp;
1077             reverse = !reverse;
1078         }
1079         if (++to > size) {
1080             to = size;
1081         }
1082         List answer = self.subList(from, to);
1083         if (reverse) {
1084             answer = reverse(answer);
1085         }
1086         return answer;
1087     }
1088 
1089     /***
1090      * Allows a List to be used as the indices to be used on a List
1091      *
1092      * @param self    a List
1093      * @param indices a Collection of indices
1094      * @return a new list of the values at the given indices
1095      */
1096     public static List getAt(List self, Collection indices) {
1097         List answer = new ArrayList(indices.size());
1098         for (Iterator iter = indices.iterator(); iter.hasNext();) {
1099             Object value = iter.next();
1100             if (value instanceof Range) {
1101                 answer.addAll(getAt(self, (Range) value));
1102             } else if (value instanceof List) {
1103                 answer.addAll(getAt(self, (List) value));
1104             } else {
1105                 int idx = InvokerHelper.asInt(value);
1106                 answer.add(getAt(self, idx));
1107             }
1108         }
1109         return answer;
1110     }
1111 
1112     /***
1113      * Allows a List to be used as the indices to be used on a List
1114      *
1115      * @param self    an Array of Objects
1116      * @param indices a Collection of indices
1117      * @return a new list of the values at the given indices
1118      */
1119     public static List getAt(Object[] self, Collection indices) {
1120         List answer = new ArrayList(indices.size());
1121         for (Iterator iter = indices.iterator(); iter.hasNext();) {
1122             Object value = iter.next();
1123             if (value instanceof Range) {
1124                 answer.addAll(getAt(self, (Range) value));
1125             } else if (value instanceof Collection) {
1126                 answer.addAll(getAt(self, (Collection) value));
1127             } else {
1128                 int idx = InvokerHelper.asInt(value);
1129                 answer.add(getAt(self, idx));
1130             }
1131         }
1132         return answer;
1133     }
1134 
1135     /***
1136      * Allows a List to be used as the indices to be used on a CharSequence
1137      *
1138      * @param self    a CharSequence
1139      * @param indices a Collection of indices
1140      * @return a String of the values at the given indices
1141      */
1142     public static CharSequence getAt(CharSequence self, Collection indices) {
1143         StringBuffer answer = new StringBuffer();
1144         for (Iterator iter = indices.iterator(); iter.hasNext();) {
1145             Object value = iter.next();
1146             if (value instanceof Range) {
1147                 answer.append(getAt(self, (Range) value));
1148             } else if (value instanceof Collection) {
1149                 answer.append(getAt(self, (Collection) value));
1150             } else {
1151                 int idx = InvokerHelper.asInt(value);
1152                 answer.append(getAt(self, idx));
1153             }
1154         }
1155         return answer.toString();
1156     }
1157 
1158     /***
1159      * Allows a List to be used as the indices to be used on a String
1160      *
1161      * @param self    a String
1162      * @param indices a Collection of indices
1163      * @return a String of the values at the given indices
1164      */
1165     public static String getAt(String self, Collection indices) {
1166         return (String) getAt((CharSequence) self, indices);
1167     }
1168 
1169     /***
1170      * Allows a List to be used as the indices to be used on a Matcher
1171      *
1172      * @param self    a Matcher
1173      * @param indices a Collection of indices
1174      * @return a String of the values at the given indices
1175      */
1176     public static String getAt(Matcher self, Collection indices) {
1177         StringBuffer answer = new StringBuffer();
1178         for (Iterator iter = indices.iterator(); iter.hasNext();) {
1179             Object value = iter.next();
1180             if (value instanceof Range) {
1181                 answer.append(getAt(self, (Range) value));
1182             } else if (value instanceof Collection) {
1183                 answer.append(getAt(self, (Collection) value));
1184             } else {
1185                 int idx = InvokerHelper.asInt(value);
1186                 answer.append(getAt(self, idx));
1187             }
1188         }
1189         return answer.toString();
1190     }
1191 
1192     /***
1193      * Creates a sub-Map containing the given keys. This method is similar to
1194      * List.subList() but uses keys rather than index ranges.
1195      *
1196      * @param map  a Map
1197      * @param keys a Collection of keys
1198      * @return a new Map containing the given keys
1199      */
1200     public static Map subMap(Map map, Collection keys) {
1201         Map answer = new HashMap(keys.size());
1202         for (Iterator iter = keys.iterator(); iter.hasNext();) {
1203             Object key = iter.next();
1204             answer.put(key, map.get(key));
1205         }
1206         return answer;
1207     }
1208 
1209     /***
1210      * Looks up an item in a Map for the given key and returns the value - unless
1211      * there is no entry for the given key in which case add the default value
1212      * to the map and return that.
1213      *
1214      * @param map          a Map
1215      * @param key          the key to lookup the value of
1216      * @param defaultValue the value to return and add to the map for this key if
1217      *                     there is no entry for the given key
1218      * @return the value of the given key or the default value, added to the map if the
1219      *         key did not exist
1220      */
1221     public static Object get(Map map, Object key, Object defaultValue) {
1222         Object answer = map.get(key);
1223         if (answer == null) {
1224             answer = defaultValue;
1225             map.put(key, answer);
1226         }
1227         return answer;
1228     }
1229 
1230     /***
1231      * Support the range subscript operator for an Array
1232      *
1233      * @param array an Array of Objects
1234      * @param range a Range
1235      * @return a range of a list from the range's from index up to but not
1236      *         including the ranges's to value
1237      */
1238     public static List getAt(Object[] array, Range range) {
1239         List list = Arrays.asList(array);
1240         return getAt(list, range);
1241     }
1242 
1243     /***
1244      * Support the subscript operator for an Array
1245      *
1246      * @param array an Array of Objects
1247      * @param idx   an index
1248      * @return the value at the given index
1249      */
1250     public static Object getAt(Object[] array, int idx) {
1251         return array[normaliseIndex(idx, array.length)];
1252     }
1253 
1254     /***
1255      * Support the subscript operator for an Array
1256      *
1257      * @param array an Array of Objects
1258      * @param idx   an index
1259      * @param value an Object to put at the given index
1260      */
1261     public static void putAt(Object[] array, int idx, Object value) {
1262         if (value instanceof Number) {
1263             Class arrayComponentClass = array.getClass().getComponentType();
1264 
1265             if (!arrayComponentClass.equals(value.getClass())) {
1266                 Object newVal = InvokerHelper.asType(value, arrayComponentClass);
1267                 array[normaliseIndex(idx, array.length)] = newVal;
1268                 return;
1269             }
1270         }
1271         array[normaliseIndex(idx, array.length)] = value;
1272     }
1273 
1274     /***
1275      * Allows conversion of arrays into a mutable List
1276      *
1277      * @param array an Array of Objects
1278      * @return the array as a List
1279      */
1280     public static List toList(Object[] array) {
1281         int size = array.length;
1282         List list = new ArrayList(size);
1283         for (int i = 0; i < size; i++) {
1284             list.add(array[i]);
1285         }
1286         return list;
1287     }
1288 
1289     /***
1290      * Support the subscript operator for a List
1291      *
1292      * @param self a List
1293      * @param idx  an index
1294      * @return the value at the given index
1295      */
1296     public static Object getAt(List self, int idx) {
1297         int size = self.size();
1298         int i = normaliseIndex(idx, size);
1299         if (i < size) {
1300             return self.get(i);
1301         } else {
1302             return null;
1303         }
1304     }
1305 
1306     /***
1307      * A helper method to allow lists to work with subscript operators
1308      *
1309      * @param self  a List
1310      * @param idx   an index
1311      * @param value the value to put at the given index
1312      */
1313     public static void putAt(List self, int idx, Object value) {
1314         int size = self.size();
1315         idx = normaliseIndex(idx, size);
1316         if (idx < size) {
1317             self.set(idx, value);
1318         } else {
1319             while (size < idx) {
1320                 self.add(size++, null);
1321             }
1322             self.add(idx, value);
1323         }
1324     }
1325 
1326     /***
1327      * Support the subscript operator for a List
1328      *
1329      * @param self a Map
1330      * @param key  an Object as a key for the map
1331      * @return the value corresponding to the given key
1332      */
1333     public static Object getAt(Map self, Object key) {
1334         return self.get(key);
1335     }
1336 
1337     /***
1338      * A helper method to allow lists to work with subscript operators
1339      *
1340      * @param self a Map
1341      * @param key  an Object as a key for the map
1342      * @return the value corresponding to the given key
1343      */
1344     public static Object putAt(Map self, Object key, Object value) {
1345         return self.put(key, value);
1346     }
1347 
1348     /***
1349      * This converts a possibly negative index to a real index into the array.
1350      *
1351      * @param i
1352      * @param size
1353      * @return
1354      */
1355     protected static int normaliseIndex(int i, int size) {
1356         int temp = i;
1357         if (i < 0) {
1358             i += size;
1359         }
1360         if (i < 0) {
1361             throw new ArrayIndexOutOfBoundsException("Negative array index [" + temp + "] too large for array size " + size);
1362         }
1363         return i;
1364     }
1365 
1366     /***
1367      * Support the subscript operator for List
1368      *
1369      * @param coll     a Collection
1370      * @param property a String
1371      * @return a List
1372      */
1373     public static List getAt(Collection coll, String property) {
1374         List answer = new ArrayList(coll.size());
1375         for (Iterator iter = coll.iterator(); iter.hasNext();) {
1376             Object item = iter.next();
1377             Object value = InvokerHelper.getProperty(item, property);
1378             if (value instanceof Collection) {
1379                 answer.addAll((Collection) value);
1380             } else {
1381                 answer.add(value);
1382             }
1383         }
1384         return answer;
1385     }
1386 
1387     /***
1388      * A convenience method for creating an immutable map
1389      *
1390      * @param self a Map
1391      * @return an immutable Map
1392      */
1393     public static Map asImmutable(Map self) {
1394         return Collections.unmodifiableMap(self);
1395     }
1396 
1397     /***
1398      * A convenience method for creating an immutable sorted map
1399      *
1400      * @param self a SortedMap
1401      * @return an immutable SortedMap
1402      */
1403     public static SortedMap asImmutable(SortedMap self) {
1404         return Collections.unmodifiableSortedMap(self);
1405     }
1406 
1407     /***
1408      * A convenience method for creating an immutable list
1409      *
1410      * @param self a List
1411      * @return an immutable List
1412      */
1413     public static List asImmutable(List self) {
1414         return Collections.unmodifiableList(self);
1415     }
1416 
1417     /***
1418      * A convenience method for creating an immutable list
1419      *
1420      * @param self a Set
1421      * @return an immutable Set
1422      */
1423     public static Set asImmutable(Set self) {
1424         return Collections.unmodifiableSet(self);
1425     }
1426 
1427     /***
1428      * A convenience method for creating an immutable sorted set
1429      *
1430      * @param self a SortedSet
1431      * @return an immutable SortedSet
1432      */
1433     public static SortedSet asImmutable(SortedSet self) {
1434         return Collections.unmodifiableSortedSet(self);
1435     }
1436 
1437     /***
1438      * A convenience method for creating an immutable Collection
1439      *
1440      * @param self a Collection
1441      * @return an immutable Collection
1442      */
1443     public static Collection asImmutable(Collection self) {
1444         return Collections.unmodifiableCollection(self);
1445     }
1446 
1447     /***
1448      * A convenience method for creating a synchronized Map
1449      *
1450      * @param self a Map
1451      * @return a synchronized Map
1452      */
1453     public static Map asSynchronized(Map self) {
1454         return Collections.synchronizedMap(self);
1455     }
1456 
1457     /***
1458      * A convenience method for creating a synchronized SortedMap
1459      *
1460      * @param self a SortedMap
1461      * @return a synchronized SortedMap
1462      */
1463     public static SortedMap asSynchronized(SortedMap self) {
1464         return Collections.synchronizedSortedMap(self);
1465     }
1466 
1467     /***
1468      * A convenience method for creating a synchronized Collection
1469      *
1470      * @param self a Collection
1471      * @return a synchronized Collection
1472      */
1473     public static Collection asSynchronized(Collection self) {
1474         return Collections.synchronizedCollection(self);
1475     }
1476 
1477     /***
1478      * A convenience method for creating a synchronized List
1479      *
1480      * @param self a List
1481      * @return a synchronized List
1482      */
1483     public static List asSynchronized(List self) {
1484         return Collections.synchronizedList(self);
1485     }
1486 
1487     /***
1488      * A convenience method for creating a synchronized Set
1489      *
1490      * @param self a Set
1491      * @return a synchronized Set
1492      */
1493     public static Set asSynchronized(Set self) {
1494         return Collections.synchronizedSet(self);
1495     }
1496 
1497     /***
1498      * A convenience method for creating a synchronized SortedSet
1499      *
1500      * @param self a SortedSet
1501      * @return a synchronized SortedSet
1502      */
1503     public static SortedSet asSynchronized(SortedSet self) {
1504         return Collections.synchronizedSortedSet(self);
1505     }
1506 
1507     /***
1508      * Sorts the given collection into a sorted list
1509      *
1510      * @param self the collection to be sorted
1511      * @return the sorted collection as a List
1512      */
1513     public static List sort(Collection self) {
1514         List answer = asList(self);
1515         Collections.sort(answer);
1516         return answer;
1517     }
1518 
1519     /***
1520      * Avoids doing unnecessary work when sorting an already sorted set
1521      *
1522      * @param self
1523      * @return the sorted set
1524      */
1525     public static SortedSet sort(SortedSet self) {
1526         return self;
1527     }
1528 
1529     /***
1530      * A convenience method for sorting a List
1531      *
1532      * @param self a List to be sorted
1533      * @return the sorted List
1534      */
1535     public static List sort(List self) {
1536         Collections.sort(self);
1537         return self;
1538     }
1539 
1540     /***
1541      * Removes the last item from the List. Using add() and pop()
1542      * is similar to push and pop on a Stack.
1543      *
1544      * @param self a List
1545      * @return the item removed from the List
1546      * @throws UnsupportedOperationException if the list is empty and you try to pop() it.
1547      */
1548     public static Object pop(List self) {
1549         if (self.isEmpty()) {
1550             throw new UnsupportedOperationException("Cannot pop() an empty List");
1551         }
1552         return self.remove(self.size() - 1);
1553     }
1554 
1555     /***
1556      * A convenience method for sorting a List with a specific comparator
1557      *
1558      * @param self       a List
1559      * @param comparator a Comparator used for the comparison
1560      * @return a sorted List
1561      */
1562     public static List sort(List self, Comparator comparator) {
1563         Collections.sort(self, comparator);
1564         return self;
1565     }
1566 
1567     /***
1568      * A convenience method for sorting a Collection with a specific comparator
1569      *
1570      * @param self       a collection to be sorted
1571      * @param comparator a Comparator used for the comparison
1572      * @return a newly created sorted List
1573      */
1574     public static List sort(Collection self, Comparator comparator) {
1575         return sort(asList(self), comparator);
1576     }
1577 
1578     /***
1579      * A convenience method for sorting a List using a closure as a comparator
1580      *
1581      * @param self    a List
1582      * @param closure a Closure used as a comparator
1583      * @return a sorted List
1584      */
1585     public static List sort(List self, Closure closure) {
1586         // use a comparator of one item or two
1587         Class[] params = closure.getParameterTypes();
1588         if (params.length == 1) {
1589             Collections.sort(self, new OrderBy(closure));
1590         } else {
1591             Collections.sort(self, new ClosureComparator(closure));
1592         }
1593         return self;
1594     }
1595 
1596     /***
1597      * A convenience method for sorting a Collection using a closure as a comparator
1598      *
1599      * @param self    a Collection to be sorted
1600      * @param closure a Closure used as a comparator
1601      * @return a newly created sorted List
1602      */
1603     public static List sort(Collection self, Closure closure) {
1604         return sort(asList(self), closure);
1605     }
1606 
1607     /***
1608      * Converts the given collection into a List
1609      *
1610      * @param self a collection to be converted into a List
1611      * @return a newly created List if this collection is not already a List
1612      */
1613     public static List asList(Collection self) {
1614         if (self instanceof List) {
1615             return (List) self;
1616         } else {
1617             return new ArrayList(self);
1618         }
1619     }
1620 
1621     /***
1622      * Reverses the list
1623      *
1624      * @param self a List
1625      * @return a reversed List
1626      */
1627     public static List reverse(List self) {
1628         int size = self.size();
1629         List answer = new ArrayList(size);
1630         ListIterator iter = self.listIterator(size);
1631         while (iter.hasPrevious()) {
1632             answer.add(iter.previous());
1633         }
1634         return answer;
1635     }
1636 
1637     /***
1638      * Create a List as a union of both Collections
1639      *
1640      * @param left  the left Collection
1641      * @param right the right Collection
1642      * @return a List
1643      */
1644     public static List plus(Collection left, Collection right) {
1645         List answer = new ArrayList(left.size() + right.size());
1646         answer.addAll(left);
1647         answer.addAll(right);
1648         return answer;
1649     }
1650 
1651     /***
1652      * Create a List as a union of a Collection and an Object
1653      *
1654      * @param left  a Collection
1655      * @param right an object to append
1656      * @return a List
1657      */
1658     public static List plus(Collection left, Object right) {
1659         List answer = new ArrayList(left.size() + 1);
1660         answer.addAll(left);
1661         answer.add(right);
1662         return answer;
1663     }
1664 
1665     /***
1666      * Create a List composed of the same elements repeated a certain number of times.
1667      *
1668      * @param self   a Collection
1669      * @param factor the number of times to append
1670      * @return a List
1671      */
1672     public static List multiply(Collection self, Number factor) {
1673         int size = factor.intValue();
1674         List answer = new ArrayList(self.size() * size);
1675         for (int i = 0; i < size; i++) {
1676             answer.addAll(self);
1677         }
1678         return answer;
1679     }
1680 
1681     /***
1682      * Create a List composed of the intersection of both collections
1683      *
1684      * @param left  a List
1685      * @param right a Collection
1686      * @return a List as an intersection of both collections
1687      */
1688     public static List intersect(List left, Collection right) {
1689 
1690         if (left.size() == 0)
1691             return new ArrayList();
1692 
1693         boolean nlgnSort = sameType(new Collection[]{left, right});
1694 
1695         ArrayList result = new ArrayList();
1696         //creates the collection to look for values.
1697         Collection pickFrom = nlgnSort ? (Collection) new TreeSet(left) : left;
1698 
1699         for (Iterator iter = right.iterator(); iter.hasNext();) {
1700             final Object o = iter.next();
1701             if (pickFrom.contains(o))
1702                 result.add(o);
1703         }
1704         return result;
1705     }
1706 
1707     /***
1708      * Create a List composed of the elements of the first list minus the elements of the collection
1709      *
1710      * @param self     a List
1711      * @param removeMe a Collection of elements to remove
1712      * @return a List with the common elements removed
1713      */
1714     public static List minus(List self, Collection removeMe) {
1715 
1716         if (self.size() == 0)
1717             return new ArrayList();
1718 
1719         boolean nlgnSort = sameType(new Collection[]{self, removeMe});
1720 
1721         //we can't use the same tactic as for intersection
1722         //since AbstractCollection only does a remove on the first
1723         //element it encounter.
1724 
1725         if (nlgnSort) {
1726             //n*log(n) version
1727             Set answer = new TreeSet(self);
1728             answer.removeAll(removeMe);
1729             return new ArrayList(answer);
1730         } else {
1731             //n*n version
1732             List tmpAnswer = new LinkedList(self);
1733             for (Iterator iter = tmpAnswer.iterator(); iter.hasNext();) {
1734                 Object element = iter.next();
1735                 //boolean removeElement = false;
1736                 for (Iterator iterator = removeMe.iterator(); iterator.hasNext();) {
1737                     if (element.equals(iterator.next())) {
1738                         iter.remove();
1739                     }
1740                 }
1741             }
1742             //remove duplicates
1743             //can't use treeset since the base classes are different
1744             List answer = new LinkedList();
1745             Object[] array = tmpAnswer.toArray(new Object[tmpAnswer.size()]);
1746 
1747             for (int i = 0; i < array.length; i++) {
1748                 if (array[i] != null) {
1749                     for (int j = i + 1; j < array.length; j++) {
1750                         if (array[i].equals(array[j])) {
1751                             array[j] = null;
1752                         }
1753                     }
1754                     answer.add(array[i]);
1755                 }
1756             }
1757             return new ArrayList(answer);
1758         }
1759     }
1760 
1761     /***
1762      * Flatten a list
1763      *
1764      * @param self a List
1765      * @return a flattened List
1766      */
1767     public static List flatten(List self) {
1768         return new ArrayList(flatten(self, new LinkedList()));
1769     }
1770 
1771     /***
1772      * Iterate over each element of the list in the reverse order.
1773      *
1774      * @param self    a List
1775      * @param closure a closure
1776      */
1777     public static void reverseEach(List self, Closure closure) {
1778         List reversed = reverse(self);
1779         for (Iterator iter = reversed.iterator(); iter.hasNext();) {
1780             closure.call(iter.next());
1781         }
1782     }
1783 
1784     private static List flatten(Collection elements, List addTo) {
1785         Iterator iter = elements.iterator();
1786         while (iter.hasNext()) {
1787             Object element = iter.next();
1788             if (element instanceof Collection) {
1789                 flatten((Collection) element, addTo);
1790             } else if (element instanceof Map) {
1791                 flatten(((Map) element).values(), addTo);
1792             } else {
1793                 addTo.add(element);
1794             }
1795         }
1796         return addTo;
1797     }
1798 
1799     /***
1800      * Overloads the left shift operator to provide an easy way to append objects to a list
1801      *
1802      * @param self  a Collection
1803      * @param value an Object to be added to the collection.
1804      * @return a Collection with an Object added to it.
1805      */
1806     public static Collection leftShift(Collection self, Object value) {
1807         self.add(value);
1808         return self;
1809     }
1810 
1811     /***
1812      * Overloads the left shift operator to provide an easy way to append multiple
1813      * objects as string representations to a String
1814      *
1815      * @param self  a String
1816      * @param value an Obect
1817      * @return a StringWriter
1818      */
1819     public static StringWriter leftShift(String self, Object value) {
1820         StringWriter answer = createStringWriter(self);
1821         try {
1822             leftShift(answer, value);
1823         } catch (IOException e) {
1824             throw new StringWriterIOException(e);
1825         }
1826         return answer;
1827     }
1828 
1829     protected static StringWriter createStringWriter(String self) {
1830         StringWriter answer = new StringWriter();
1831         answer.write(self);
1832         return answer;
1833     }
1834 
1835     protected static StringBufferWriter createStringBufferWriter(StringBuffer self) {
1836         return new StringBufferWriter(self);
1837     }
1838 
1839     /***
1840      * Overloads the left shift operator to provide an easy way to append multiple
1841      * objects as string representations to a StringBuffer
1842      *
1843      * @param self  a StringBuffer
1844      * @param value a value to append
1845      * @return a StringWriter
1846      */
1847     public static Writer leftShift(StringBuffer self, Object value) {
1848         StringBufferWriter answer = createStringBufferWriter(self);
1849         try {
1850             leftShift(answer, value);
1851         } catch (IOException e) {
1852             throw new StringWriterIOException(e);
1853         }
1854         return answer;
1855     }
1856 
1857     /***
1858      * Overloads the left shift operator to provide an append mechanism to add things to a writer
1859      *
1860      * @param self  a Writer
1861      * @param value a value to append
1862      * @return a StringWriter
1863      */
1864     public static Writer leftShift(Writer self, Object value) throws IOException {
1865         InvokerHelper.write(self, value);
1866         return self;
1867     }
1868 
1869     /***
1870      * Implementation of the left shift operator for integral types.  Non integral
1871      * Number types throw UnsupportedOperationException.
1872      */
1873     public static Number leftShift(Number left, Number right) {
1874         return NumberMath.leftShift(left, right);
1875     }
1876 
1877     /***
1878      * Implementation of the right shift operator for integral types.  Non integral
1879      * Number types throw UnsupportedOperationException.
1880      */
1881     public static Number rightShift(Number left, Number right) {
1882         return NumberMath.rightShift(left, right);
1883     }
1884 
1885     /***
1886      * Implementation of the right shift (unsigned) operator for integral types.  Non integral
1887      * Number types throw UnsupportedOperationException.
1888      */
1889     public static Number rightShiftUnsigned(Number left, Number right) {
1890         return NumberMath.rightShiftUnsigned(left, right);
1891     }
1892 
1893     /***
1894      * A helper method so that dynamic dispatch of the writer.write(object) method
1895      * will always use the more efficient Writable.writeTo(writer) mechanism if the
1896      * object implements the Writable interface.
1897      *
1898      * @param self     a Writer
1899      * @param writable an object implementing the Writable interface
1900      */
1901     public static void write(Writer self, Writable writable) throws IOException {
1902         writable.writeTo(self);
1903     }
1904 
1905     /***
1906      * Overloads the left shift operator to provide an append mechanism to add things to a stream
1907      *
1908      * @param self  an OutputStream
1909      * @param value a value to append
1910      * @return a Writer
1911      */
1912     public static Writer leftShift(OutputStream self, Object value) throws IOException {
1913         OutputStreamWriter writer = new FlushingStreamWriter(self);
1914         leftShift(writer, value);
1915         return writer;
1916     }
1917 
1918     /***
1919      * Overloads the left shift operator to provide an append mechanism to add bytes to a stream
1920      *
1921      * @param self  an OutputStream
1922      * @param value a value to append
1923      * @return an OutputStream
1924      */
1925     public static OutputStream leftShift(OutputStream self, byte[] value) throws IOException {
1926         self.write(value);
1927         self.flush();
1928         return self;
1929     }
1930 
1931     private static boolean sameType(Collection[] cols) {
1932         List all = new LinkedList();
1933         for (int i = 0; i < cols.length; i++) {
1934             all.addAll(cols[i]);
1935         }
1936         if (all.size() == 0)
1937             return true;
1938 
1939         Object first = all.get(0);
1940 
1941         //trying to determine the base class of the collections
1942         //special case for Numbers
1943         Class baseClass;
1944         if (first instanceof Number) {
1945             baseClass = Number.class;
1946         } else {
1947             baseClass = first.getClass();
1948         }
1949 
1950         for (int i = 0; i < cols.length; i++) {
1951             for (Iterator iter = cols[i].iterator(); iter.hasNext();) {
1952                 if (!baseClass.isInstance(iter.next())) {
1953                     return false;
1954                 }
1955             }
1956         }
1957         return true;
1958     }
1959 
1960     // Primitive type array methods
1961     //-------------------------------------------------------------------------
1962 
1963     public static Object getAt(byte[] array, int idx) {
1964         return primitiveArrayGet(array, idx);
1965     }
1966 
1967     public static Object getAt(char[] array, int idx) {
1968         return primitiveArrayGet(array, idx);
1969     }
1970 
1971     public static Object getAt(short[] array, int idx) {
1972         return primitiveArrayGet(array, idx);
1973     }
1974 
1975     public static Object getAt(int[] array, int idx) {
1976         return primitiveArrayGet(array, idx);
1977     }
1978 
1979     public static Object getAt(long[] array, int idx) {
1980         return primitiveArrayGet(array, idx);
1981     }
1982 
1983     public static Object getAt(float[] array, int idx) {
1984         return primitiveArrayGet(array, idx);
1985     }
1986 
1987     public static Object getAt(double[] array, int idx) {
1988         return primitiveArrayGet(array, idx);
1989     }
1990 
1991     public static Object getAt(byte[] array, Range range) {
1992         return primitiveArrayGet(array, range);
1993     }
1994 
1995     public static Object getAt(char[] array, Range range) {
1996         return primitiveArrayGet(array, range);
1997     }
1998 
1999     public static Object getAt(short[] array, Range range) {
2000         return primitiveArrayGet(array, range);
2001     }
2002 
2003     public static Object getAt(int[] array, Range range) {
2004         return primitiveArrayGet(array, range);
2005     }
2006 
2007     public static Object getAt(long[] array, Range range) {
2008         return primitiveArrayGet(array, range);
2009     }
2010 
2011     public static Object getAt(float[] array, Range range) {
2012         return primitiveArrayGet(array, range);
2013     }
2014 
2015     public static Object getAt(double[] array, Range range) {
2016         return primitiveArrayGet(array, range);
2017     }
2018 
2019     public static Object getAt(byte[] array, Collection indices) {
2020         return primitiveArrayGet(array, indices);
2021     }
2022 
2023     public static Object getAt(char[] array, Collection indices) {
2024         return primitiveArrayGet(array, indices);
2025     }
2026 
2027     public static Object getAt(short[] array, Collection indices) {
2028         return primitiveArrayGet(array, indices);
2029     }
2030 
2031     public static Object getAt(int[] array, Collection indices) {
2032         return primitiveArrayGet(array, indices);
2033     }
2034 
2035     public static Object getAt(long[] array, Collection indices) {
2036         return primitiveArrayGet(array, indices);
2037     }
2038 
2039     public static Object getAt(float[] array, Collection indices) {
2040         return primitiveArrayGet(array, indices);
2041     }
2042 
2043     public static Object getAt(double[] array, Collection indices) {
2044         return primitiveArrayGet(array, indices);
2045     }
2046 
2047     public static void putAt(byte[] array, int idx, Object newValue) {
2048         primitiveArrayPut(array, idx, newValue);
2049     }
2050 
2051     public static void putAt(char[] array, int idx, Object newValue) {
2052         primitiveArrayPut(array, idx, newValue);
2053     }
2054 
2055     public static void putAt(short[] array, int idx, Object newValue) {
2056         primitiveArrayPut(array, idx, newValue);
2057     }
2058 
2059     public static void putAt(int[] array, int idx, Object newValue) {
2060         primitiveArrayPut(array, idx, newValue);
2061     }
2062 
2063     public static void putAt(long[] array, int idx, Object newValue) {
2064         primitiveArrayPut(array, idx, newValue);
2065     }
2066 
2067     public static void putAt(float[] array, int idx, Object newValue) {
2068         primitiveArrayPut(array, idx, newValue);
2069     }
2070 
2071     public static void putAt(double[] array, int idx, Object newValue) {
2072         primitiveArrayPut(array, idx, newValue);
2073     }
2074 
2075     public static int size(byte[] array) {
2076         return Array.getLength(array);
2077     }
2078 
2079     public static int size(char[] array) {
2080         return Array.getLength(array);
2081     }
2082 
2083     public static int size(short[] array) {
2084         return Array.getLength(array);
2085     }
2086 
2087     public static int size(int[] array) {
2088         return Array.getLength(array);
2089     }
2090 
2091     public static int size(long[] array) {
2092         return Array.getLength(array);
2093     }
2094 
2095     public static int size(float[] array) {
2096         return Array.getLength(array);
2097     }
2098 
2099     public static int size(double[] array) {
2100         return Array.getLength(array);
2101     }
2102 
2103     public static List toList(byte[] array) {
2104         return InvokerHelper.primitiveArrayToList(array);
2105     }
2106 
2107     public static List toList(char[] array) {
2108         return InvokerHelper.primitiveArrayToList(array);
2109     }
2110 
2111     public static List toList(short[] array) {
2112         return InvokerHelper.primitiveArrayToList(array);
2113     }
2114 
2115     public static List toList(int[] array) {
2116         return InvokerHelper.primitiveArrayToList(array);
2117     }
2118 
2119     public static List toList(long[] array) {
2120         return InvokerHelper.primitiveArrayToList(array);
2121     }
2122 
2123     public static List toList(float[] array) {
2124         return InvokerHelper.primitiveArrayToList(array);
2125     }
2126 
2127     public static List toList(double[] array) {
2128         return InvokerHelper.primitiveArrayToList(array);
2129     }
2130 
2131     private static final char[] tTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
2132 
2133     public static Writable encodeBase64(final Byte[] data) {
2134         return encodeBase64(InvokerHelper.convertToByteArray(data));
2135     }
2136 
2137     /***
2138      * Produce a Writable object which writes the base64 encoding of the byte array
2139      * Calling toString() on the result rerurns the encoding as a String
2140      *
2141      * @param data byte array to be encoded
2142      * @return object which will write the base64 encoding of the byte array
2143      */
2144     public static Writable encodeBase64(final byte[] data) {
2145         return new Writable() {
2146             public Writer writeTo(final Writer writer) throws IOException {
2147                 int charCount = 0;
2148                 final int dLimit = (data.length / 3) * 3;
2149 
2150                 for (int dIndex = 0; dIndex != dLimit; dIndex += 3) {
2151                     int d = ((data[dIndex] & 0XFF) << 16) | ((data[dIndex + 1] & 0XFF) << 8) | (data[dIndex + 2] & 0XFF);
2152 
2153                     writer.write(tTable[d >> 18]);
2154                     writer.write(tTable[(d >> 12) & 0X3F]);
2155                     writer.write(tTable[(d >> 6) & 0X3F]);
2156                     writer.write(tTable[d & 0X3F]);
2157 
2158                     if (++charCount == 18) {
2159                         writer.write('\n');
2160                         charCount = 0;
2161                     }
2162                 }
2163 
2164                 if (dLimit != data.length) {
2165                     int d = (data[dLimit] & 0XFF) << 16;
2166 
2167                     if (dLimit + 1 != data.length) {
2168                         d |= (data[dLimit + 1] & 0XFF) << 8;
2169                     }
2170 
2171                     writer.write(tTable[d >> 18]);
2172                     writer.write(tTable[(d >> 12) & 0X3F]);
2173                     writer.write((dLimit + 1 < data.length) ? tTable[(d >> 6) & 0X3F] : '=');
2174                     writer.write('=');
2175                 }
2176 
2177                 return writer;
2178             }
2179 
2180             public String toString() {
2181                 StringWriter buffer = new StringWriter();
2182 
2183                 try {
2184                     writeTo(buffer);
2185                 } catch (IOException e) {
2186                     throw new RuntimeException(e); // TODO: change this exception type
2187                 }
2188 
2189                 return buffer.toString();
2190             }
2191         };
2192     }
2193 
2194     private static final byte[] translateTable = (
2195             //
2196             "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
2197             //                    \t    \n                \r
2198             + "\u0042\u0042\u0041\u0041\u0042\u0042\u0041\u0042"
2199             //
2200             + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
2201             //
2202             + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
2203             //        sp    !     "     #     $     %     &     '
2204             + "\u0041\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
2205             //         (    )     *     +     ,     -     .     /
2206             + "\u0042\u0042\u0042\u003E\u0042\u0042\u0042\u003F"
2207             //         0    1     2     3     4     5     6     7
2208             + "\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B"
2209             //         8    9     :     ;     <     =     >     ?
2210             + "\u003C\u003D\u0042\u0042\u0042\u0040\u0042\u0042"
2211             //         @    A     B     C     D     E     F     G
2212             + "\u0042\u0000\u0001\u0002\u0003\u0004\u0005\u0006"
2213             //         H    I   J K   L     M   N   O
2214             + "\u0007\u0008\t\n\u000B\u000C\r\u000E"
2215             //         P    Q     R     S     T     U     V    W
2216             + "\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016"
2217             //         X    Y     Z     [     \     ]     ^    _
2218             + "\u0017\u0018\u0019\u0042\u0042\u0042\u0042\u0042"
2219             //         '    a     b     c     d     e     f     g
2220             + "\u0042\u001A\u001B\u001C\u001D\u001E\u001F\u0020"
2221             //        h   i   j     k     l     m     n     o    p
2222             + "\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028"
2223             //        p     q     r     s     t     u     v     w
2224             + "\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030"
2225             //        x     y     z
2226             + "\u0031\u0032\u0033").getBytes();
2227 
2228     /***
2229      * Decode the Sting from base64 into a byte array
2230      *
2231      * @param value the string to be decoded
2232      * @return the decoded bytes as an array
2233      */
2234     public static byte[] decodeBase64(final String value) {
2235         int byteShift = 4;
2236         int tmp = 0;
2237         boolean done = false;
2238         final StringBuffer buffer = new StringBuffer();
2239 
2240         for (int i = 0; i != value.length(); i++) {
2241             final char c = value.charAt(i);
2242             final int sixBit = (c < 123) ? translateTable[c] : 66;
2243 
2244             if (sixBit < 64) {
2245                 if (done) throw new RuntimeException("= character not at end of base64 value"); // TODO: change this exception type
2246 
2247                 tmp = (tmp << 6) | sixBit;
2248 
2249                 if (byteShift-- != 4) {
2250                     buffer.append((char) ((tmp >> (byteShift * 2)) & 0XFF));
2251                 }
2252 
2253             } else if (sixBit == 64) {
2254 
2255                 byteShift--;
2256                 done = true;
2257 
2258             } else if (sixBit == 66) {
2259                 // RFC 2045 says that I'm allowed to take the presence of
2260                 // these characters as evedence of data corruption
2261                 // So I will
2262                 throw new RuntimeException("bad character in base64 value"); // TODO: change this exception type
2263             }
2264 
2265             if (byteShift == 0) byteShift = 4;
2266         }
2267 
2268         try {
2269             return buffer.toString().getBytes("ISO-8859-1");
2270         } catch (UnsupportedEncodingException e) {
2271             throw new RuntimeException("Base 64 decode produced byte values > 255"); // TODO: change this exception type
2272         }
2273     }
2274 
2275     /***
2276      * Implements the getAt(int) method for primitve type arrays
2277      */
2278     protected static Object primitiveArrayGet(Object array, int idx) {
2279         return Array.get(array, normaliseIndex(idx, Array.getLength(array)));
2280     }
2281 
2282     /***
2283      * Implements the getAt(Range) method for primitve type arrays
2284      */
2285     protected static List primitiveArrayGet(Object array, Range range) {
2286         List answer = new ArrayList();
2287         for (Iterator iter = range.iterator(); iter.hasNext();) {
2288             int idx = InvokerHelper.asInt(iter.next());
2289             answer.add(primitiveArrayGet(array, idx));
2290         }
2291         return answer;
2292     }
2293 
2294     /***
2295      * Implements the getAt(Collection) method for primitve type arrays
2296      */
2297     protected static List primitiveArrayGet(Object self, Collection indices) {
2298         List answer = new ArrayList();
2299         for (Iterator iter = indices.iterator(); iter.hasNext();) {
2300             Object value = iter.next();
2301             if (value instanceof Range) {
2302                 answer.addAll(primitiveArrayGet(self, (Range) value));
2303             } else if (value instanceof List) {
2304                 answer.addAll(primitiveArrayGet(self, (List) value));
2305             } else {
2306                 int idx = InvokerHelper.asInt(value);
2307                 answer.add(primitiveArrayGet(self, idx));
2308             }
2309         }
2310         return answer;
2311     }
2312 
2313     /***
2314      * Implements the set(int idx) method for primitve type arrays
2315      */
2316     protected static void primitiveArrayPut(Object array, int idx, Object newValue) {
2317         Array.set(array, normaliseIndex(idx, Array.getLength(array)), newValue);
2318     }
2319 
2320     // String methods
2321     //-------------------------------------------------------------------------
2322 
2323     /***
2324      * Converts the given string into a Character object
2325      * using the first character in the string
2326      *
2327      * @param self a String
2328      * @return the first Character
2329      */
2330     public static Character toCharacter(String self) {
2331         /*** @todo use cache? */
2332         return new Character(self.charAt(0));
2333     }
2334 
2335     /***
2336      * Tokenize a String
2337      *
2338      * @param self  a String
2339      * @param token the delimiter
2340      * @return a List of tokens
2341      */
2342     public static List tokenize(String self, String token) {
2343         return InvokerHelper.asList(new StringTokenizer(self, token));
2344     }
2345 
2346     /***
2347      * Tokenize a String (with a whitespace as delimiter)
2348      *
2349      * @param self a String
2350      * @return a List of tokens
2351      */
2352     public static List tokenize(String self) {
2353         return InvokerHelper.asList(new StringTokenizer(self));
2354     }
2355 
2356     /***
2357      * Appends a String
2358      *
2359      * @param left  a String
2360      * @param value a String
2361      * @return a String
2362      */
2363     public static String plus(String left, Object value) {
2364         //return left + value;
2365         return left + toString(value);
2366     }
2367 
2368     /***
2369      * Appends a String
2370      *
2371      * @param value a Number
2372      * @param right a String
2373      * @return a String
2374      */
2375     public static String plus(Number value, String right) {
2376         return toString(value) + right;
2377     }
2378 
2379     /***
2380      * Appends a String
2381      *
2382      * @param left  a StringBuffer
2383      * @param value a String
2384      * @return a String
2385      */
2386     public static String plus(StringBuffer left, String value) {
2387         return left + value;
2388     }
2389 
2390 
2391     /***
2392      * Remove a part of a String
2393      *
2394      * @param left  a String
2395      * @param value a String part to remove
2396      * @return a String minus the part to be removed
2397      */
2398     public static String minus(String left, Object value) {
2399         String text = toString(value);
2400         return left.replaceFirst(text, "");
2401     }
2402 
2403     /***
2404      * Provide an implementation of contains() like Collection to make Strings more polymorphic
2405      * This method is not required on JDK 1.5 onwards
2406      *
2407      * @param self a String
2408      * @param text a String to look for
2409      * @return true if this string contains the given text
2410      */
2411     public static boolean contains(String self, String text) {
2412         int idx = self.indexOf(text);
2413         return idx >= 0;
2414     }
2415 
2416     /***
2417      * Count the number of occurencies of a substring
2418      *
2419      * @param self a String
2420      * @param text a substring
2421      * @return the number of occurrencies of the given string inside this String
2422      */
2423     public static int count(String self, String text) {
2424         int answer = 0;
2425         for (int idx = 0; true; idx++) {
2426             idx = self.indexOf(text, idx);
2427             if (idx >= 0) {
2428                 ++answer;
2429             } else {
2430                 break;
2431             }
2432         }
2433         return answer;
2434     }
2435 
2436     /***
2437      * Increments the last digit in the given string, resetting
2438      * it and moving onto the next digit if increasing the digit
2439      * no longer becomes a letter or digit.
2440      *
2441      * @param self a String
2442      * @return a String with an incremented digit at the end
2443      */
2444     public static String next(String self) {
2445         StringBuffer buffer = new StringBuffer(self);
2446         char firstCh = firstCharacter();
2447         for (int idx = buffer.length() - 1; idx >= 0; idx--) {
2448             char ch = next(buffer.charAt(idx));
2449             if (ch != ZERO_CHAR) {
2450                 buffer.setCharAt(idx, ch);
2451                 break;
2452             } else {
2453                 // lets find the first char
2454                 if (idx == 0) {
2455                     buffer.append("1");
2456                 } else {
2457                     buffer.setCharAt(idx, firstCh);
2458                 }
2459             }
2460         }
2461         return buffer.toString();
2462     }
2463 
2464     /***
2465      * Decrements the last digit in the given string, resetting
2466      * it and moving onto the next digit if increasing the digit
2467      * no longer becomes a letter or digit.
2468      *
2469      * @param self a String
2470      * @return a String with a decremented digit at the end
2471      */
2472     public static String previous(String self) {
2473         StringBuffer buffer = new StringBuffer(self);
2474         char lastCh = lastCharacter();
2475         for (int idx = buffer.length() - 1; idx >= 0; idx--) {
2476             char ch = previous(buffer.charAt(idx));
2477             if (ch != ZERO_CHAR) {
2478                 buffer.setCharAt(idx, ch);
2479                 break;
2480             } else {
2481                 if (idx == 0) {
2482                     return null;
2483                 } else {
2484                     // lets find the first char
2485                     buffer.setCharAt(idx, lastCh);
2486                 }
2487             }
2488         }
2489         return buffer.toString();
2490     }
2491 
2492     /***
2493      * Executes the given string as a command line process. For more control
2494      * over the process mechanism in JDK 1.5 you can use java.lang.ProcessBuilder.
2495      *
2496      * @param self a command line String
2497      * @return the Process which has just started for this command line string
2498      */
2499     public static Process execute(String self) throws IOException {
2500         return Runtime.getRuntime().exec(self);
2501     }
2502 
2503     private static char next(char ch) {
2504         if (Character.isLetterOrDigit(++ch)) {
2505             return ch;
2506         } else {
2507             return ZERO_CHAR;
2508         }
2509     }
2510 
2511     private static char previous(char ch) {
2512         if (Character.isLetterOrDigit(--ch)) {
2513             return ch;
2514         } else {
2515             return ZERO_CHAR;
2516         }
2517     }
2518 
2519     /***
2520      * @return the first character used when a letter rolls over when incrementing
2521      */
2522     private static char firstCharacter() {
2523         char ch = ZERO_CHAR;
2524         while (!Character.isLetterOrDigit(ch)) {
2525             ch++;
2526         }
2527         return ch;
2528     }
2529 
2530     /***
2531      * @return the last character used when a letter rolls over when decrementing
2532      */
2533     private static char lastCharacter() {
2534         char ch = firstCharacter();
2535         while (Character.isLetterOrDigit(++ch)) ;
2536         return --ch;
2537     }
2538 
2539     /***
2540      * Repeat a String a certain number of times
2541      *
2542      * @param self   a String to be repeated
2543      * @param factor the number of times the String should be repeated
2544      * @return a String composed of a repeatition
2545      * @throws IllegalArgumentException if the number of repeatition is &lt; 0
2546      */
2547     public static String multiply(String self, Number factor) {
2548         int size = factor.intValue();
2549         if (size == 0)
2550             return "";
2551         else if (size < 0) {
2552             throw new IllegalArgumentException("multiply() should be called with a number of 0 or greater not: " + size);
2553         }
2554         StringBuffer answer = new StringBuffer(self);
2555         for (int i = 1; i < size; i++) {
2556             answer.append(self);
2557         }
2558         return answer.toString();
2559     }
2560 
2561     protected static String toString(Object value) {
2562         return (value == null) ? "null" : value.toString();
2563     }
2564 
2565     // Number based methods
2566     //-------------------------------------------------------------------------
2567 
2568     /***
2569      * Increment a Character by one
2570      *
2571      * @param self a Character
2572      * @return an incremented Number
2573      */
2574     public static Number next(Character self) {
2575         return plus(self, ONE);
2576     }
2577 
2578     /***
2579      * Increment a Number by one
2580      *
2581      * @param self a Number
2582      * @return an incremented Number
2583      */
2584     public static Number next(Number self) {
2585         return plus(self, ONE);
2586     }
2587 
2588     /***
2589      * Decrement a Character by one
2590      *
2591      * @param self a Character
2592      * @return a decremented Number
2593      */
2594     public static Number previous(Character self) {
2595         return minus(self, ONE);
2596     }
2597 
2598     /***
2599      * Decrement a Number by one
2600      *
2601      * @param self a Number
2602      * @return a decremented Number
2603      */
2604     public static Number previous(Number self) {
2605         return minus(self, ONE);
2606     }
2607 
2608     /***
2609      * Add a Character and a Number
2610      *
2611      * @param left  a Character
2612      * @param right a Number
2613      * @return the addition of the Character and the Number
2614      */
2615     public static Number plus(Character left, Number right) {
2616         return plus(new Integer(left.charValue()), right);
2617     }
2618 
2619     /***
2620      * Add a Number and a Character
2621      *
2622      * @param left  a Number
2623      * @param right a Character
2624      * @return the addition of the Character and the Number
2625      */
2626     public static Number plus(Number left, Character right) {
2627         return plus(left, new Integer(right.charValue()));
2628     }
2629 
2630     /***
2631      * Add two Characters
2632      *
2633      * @param left  a Character
2634      * @param right a Character
2635      * @return the addition of both Characters
2636      */
2637     public static Number plus(Character left, Character right) {
2638         return plus(new Integer(left.charValue()), right);
2639     }
2640 
2641     /***
2642      * Add two Numbers
2643      *
2644      * @param left  a Number
2645      * @param right another Number to add
2646      * @return the addition of both Numbers
2647      */
2648     public static Number plus(Number left, Number right) {
2649         return NumberMath.add(left, right);
2650     }
2651 
2652     /***
2653      * Compare a Character and a Number
2654      *
2655      * @param left  a Character
2656      * @param right a Number
2657      * @return the result of the comparison
2658      */
2659     public static int compareTo(Character left, Number right) {
2660         return compareTo(new Integer(left.charValue()), right);
2661     }
2662 
2663     /***
2664      * Compare a Number and a Character
2665      *
2666      * @param left  a Number
2667      * @param right a Character
2668      * @return the result of the comparison
2669      */
2670     public static int compareTo(Number left, Character right) {
2671         return compareTo(left, new Integer(right.charValue()));
2672     }
2673 
2674     /***
2675      * Compare two Characters
2676      *
2677      * @param left  a Character
2678      * @param right a Character
2679      * @return the result of the comparison
2680      */
2681     public static int compareTo(Character left, Character right) {
2682         return compareTo(new Integer(left.charValue()), right);
2683     }
2684 
2685     /***
2686      * Compare two Numbers
2687      *
2688      * @param left  a Number
2689      * @param right another Number to compare to
2690      * @return the comparision of both numbers
2691      */
2692     public static int compareTo(Number left, Number right) {
2693         /*** @todo maybe a double dispatch thing to handle new large numbers? */
2694         return NumberMath.compareTo(left, right);
2695     }
2696 
2697     /***
2698      * Subtract a Number from a Character
2699      *
2700      * @param left  a Character
2701      * @param right a Number
2702      * @return the addition of the Character and the Number
2703      */
2704     public static Number minus(Character left, Number right) {
2705         return minus(new Integer(left.charValue()), right);
2706     }
2707 
2708     /***
2709      * Subtract a Character from a Number
2710      *
2711      * @param left  a Number
2712      * @param right a Character
2713      * @return the addition of the Character and the Number
2714      */
2715     public static Number minus(Number left, Character right) {
2716         return minus(left, new Integer(right.charValue()));
2717     }
2718 
2719     /***
2720      * Subtraction two Characters
2721      *
2722      * @param left  a Character
2723      * @param right a Character
2724      * @return the addition of both Characters
2725      */
2726     public static Number minus(Character left, Character right) {
2727         return minus(new Integer(left.charValue()), right);
2728     }
2729 
2730     /***
2731      * Substraction of two Numbers
2732      *
2733      * @param left  a Number
2734      * @param right another Number to substract to the first one
2735      * @return the substraction
2736      */
2737     public static Number minus(Number left, Number right) {
2738         return NumberMath.subtract(left, right);
2739     }
2740 
2741     /***
2742      * Multiply a Character by a Number
2743      *
2744      * @param left  a Character
2745      * @param right a Number
2746      * @return the multiplication of both
2747      */
2748     public static Number multiply(Character left, Number right) {
2749         return multiply(new Integer(left.charValue()), right);
2750     }
2751 
2752     /***
2753      * Multiply a Number by a Character
2754      *
2755      * @param left  a Number
2756      * @param right a Character
2757      * @return the multiplication of both
2758      */
2759     public static Number multiply(Number left, Character right) {
2760         return multiply(left, new Integer(right.charValue()));
2761     }
2762 
2763     /***
2764      * Multiply two Characters
2765      *
2766      * @param left  a Character
2767      * @param right another Character
2768      * @return the multiplication of both
2769      */
2770     public static Number multiply(Character left, Character right) {
2771         return multiply(new Integer(left.charValue()), right);
2772     }
2773 
2774     /***
2775      * Multiply two Numbers
2776      *
2777      * @param left  a Number
2778      * @param right another Number
2779      * @return the multiplication of both
2780      */
2781     //Note:  This method is NOT called if left AND right are both BigIntegers or BigDecimals because
2782     //those classes implement a method with a better exact match.
2783     public static Number multiply(Number left, Number right) {
2784         return NumberMath.multiply(left, right);
2785     }
2786 
2787     /***
2788      * Power of a Number to a certain exponent
2789      *
2790      * @param self     a Number
2791      * @param exponent a Number exponent
2792      * @return a Number to the power of a certain exponent
2793      */
2794     public static Number power(Number self, Number exponent) {
2795         double answer = Math.pow(self.doubleValue(), exponent.doubleValue());
2796         if (NumberMath.isFloatingPoint(self) || NumberMath.isFloatingPoint(exponent) || answer < 1) {
2797             return new Double(answer);
2798         } else if (NumberMath.isLong(self) || NumberMath.isLong(exponent) || answer > Integer.MAX_VALUE) {
2799             return new Long((long) answer);
2800         } else {
2801             return new Integer((int) answer);
2802         }
2803     }
2804 
2805     /***
2806      * Divide a Character by a Number
2807      *
2808      * @param left  a Character
2809      * @param right a Number
2810      * @return the multiplication of both
2811      */
2812     public static Number div(Character left, Number right) {
2813         return div(new Integer(left.charValue()), right);
2814     }
2815 
2816     /***
2817      * Divide a Number by a Character
2818      *
2819      * @param left  a Number
2820      * @param right a Character
2821      * @return the multiplication of both
2822      */
2823     public static Number div(Number left, Character right) {
2824         return div(left, new Integer(right.charValue()));
2825     }
2826 
2827     /***
2828      * Divide two Characters
2829      *
2830      * @param left  a Character
2831      * @param right another Character
2832      * @return the multiplication of both
2833      */
2834     public static Number div(Character left, Character right) {
2835         return div(new Integer(left.charValue()), right);
2836     }
2837 
2838     /***
2839      * Divide two Numbers
2840      *
2841      * @param left  a Number
2842      * @param right another Number
2843      * @return a Number resulting of the divide operation
2844      */
2845     //Method name changed from 'divide' to avoid collision with BigInteger method that has
2846     //different semantics.  We want a BigDecimal result rather than a BigInteger.
2847     public static Number div(Number left, Number right) {
2848         return NumberMath.divide(left, right);
2849     }
2850 
2851     /***
2852      * Integer Divide a Character by a Number
2853      *
2854      * @param left  a Character
2855      * @param right a Number
2856      * @return the integer division of both
2857      */
2858     public static Number intdiv(Character left, Number right) {
2859         return intdiv(new Integer(left.charValue()), right);
2860     }
2861 
2862     /***
2863      * Integer Divide a Number by a Character
2864      *
2865      * @param left  a Number
2866      * @param right a Character
2867      * @return the integer division of both
2868      */
2869     public static Number intdiv(Number left, Character right) {
2870         return intdiv(left, new Integer(right.charValue()));
2871     }
2872 
2873     /***
2874      * Integer Divide two Characters
2875      *
2876      * @param left  a Character
2877      * @param right another Character
2878      * @return the integer division of both
2879      */
2880     public static Number intdiv(Character left, Character right) {
2881         return intdiv(new Integer(left.charValue()), right);
2882     }
2883 
2884     /***
2885      * Integer Divide two Numbers
2886      *
2887      * @param left  a Number
2888      * @param right another Number
2889      * @return a Number (an Integer) resulting of the integer division operation
2890      */
2891     public static Number intdiv(Number left, Number right) {
2892         return NumberMath.intdiv(left, right);
2893     }
2894 
2895     /***
2896      * Bitwise OR together two numbers
2897      *
2898      * @param left  a Number
2899      * @param right another Number to bitwise OR
2900      * @return the bitwise OR of both Numbers
2901      */
2902     public static Number or(Number left, Number right) {
2903         return NumberMath.or(left, right);
2904     }
2905 
2906     /***
2907      * Bitwise AND together two Numbers
2908      *
2909      * @param left  a Number
2910      * @param right another Number to bitwse AND
2911      * @return the bitwise AND of both Numbers
2912      */
2913     public static Number and(Number left, Number right) {
2914         return NumberMath.and(left, right);
2915     }
2916 
2917     /***
2918      * Performs a division modulus operation
2919      *
2920      * @param left  a Number
2921      * @param right another Number to mod
2922      * @return the modulus result
2923      */
2924     public static Number mod(Number left, Number right) {
2925         return NumberMath.mod(left, right);
2926     }
2927 
2928     /***
2929      * Negates the number
2930      *
2931      * @param left a Number
2932      * @return the negation of the number
2933      */
2934     public static Number negate(Number left) {
2935         return NumberMath.negate(left);
2936     }
2937 
2938 
2939     /***
2940      * Iterates a number of times
2941      *
2942      * @param self    a Number
2943      * @param closure the closure to call a number of times
2944      */
2945     public static void times(Number self, Closure closure) {
2946         for (int i = 0, size = self.intValue(); i < size; i++) {
2947             closure.call(new Integer(i));
2948             if (closure.getDirective() == Closure.DONE) {
2949                 break;
2950             }
2951         }
2952     }
2953 
2954     /***
2955      * Iterates from this number up to the given number
2956      *
2957      * @param self    a Number
2958      * @param to      another Number to go up to
2959      * @param closure the closure to call
2960      */
2961     public static void upto(Number self, Number to, Closure closure) {
2962         for (int i = self.intValue(), size = to.intValue(); i <= size; i++) {
2963             closure.call(new Integer(i));
2964         }
2965     }
2966 
2967     /***
2968      * Iterates from this number up to the given number using a step increment
2969      *
2970      * @param self       a Number to start with
2971      * @param to         a Number to go up to
2972      * @param stepNumber a Number representing the step increment
2973      * @param closure    the closure to call
2974      */
2975     public static void step(Number self, Number to, Number stepNumber, Closure closure) {
2976         for (int i = self.intValue(), size = to.intValue(), step = stepNumber.intValue(); i < size; i += step) {
2977             closure.call(new Integer(i));
2978         }
2979     }
2980 
2981     /***
2982      * Get the absolute value
2983      *
2984      * @param number a Number
2985      * @return the absolute value of that Number
2986      */
2987     //Note:  This method is NOT called if number is a BigInteger or BigDecimal because
2988     //those classes implement a method with a better exact match.
2989     public static int abs(Number number) {
2990         return Math.abs(number.intValue());
2991     }
2992 
2993     /***
2994      * Get the absolute value
2995      *
2996      * @param number a Long
2997      * @return the absolute value of that Long
2998      */
2999     public static long abs(Long number) {
3000         return Math.abs(number.longValue());
3001     }
3002 
3003     /***
3004      * Get the absolute value
3005      *
3006      * @param number a Float
3007      * @return the absolute value of that Float
3008      */
3009     public static float abs(Float number) {
3010         return Math.abs(number.floatValue());
3011     }
3012 
3013     /***
3014      * Get the absolute value
3015      *
3016      * @param number a Double
3017      * @return the absolute value of that Double
3018      */
3019     public static double abs(Double number) {
3020         return Math.abs(number.doubleValue());
3021     }
3022 
3023     /***
3024      * Get the absolute value
3025      *
3026      * @param number a Float
3027      * @return the absolute value of that Float
3028      */
3029     public static int round(Float number) {
3030         return Math.round(number.floatValue());
3031     }
3032 
3033     /***
3034      * Round the value
3035      *
3036      * @param number a Double
3037      * @return the absolute value of that Double
3038      */
3039     public static long round(Double number) {
3040         return Math.round(number.doubleValue());
3041     }
3042 
3043     /***
3044      * Parse a String into an Integer
3045      *
3046      * @param self a String
3047      * @return an Integer
3048      */
3049     public static Integer toInteger(String self) {
3050         return Integer.valueOf(self);
3051     }
3052 
3053     /***
3054      * Parse a String into a Long
3055      *
3056      * @param self a String
3057      * @return a Long
3058      */
3059     public static Long toLong(String self) {
3060         return Long.valueOf(self);
3061     }
3062 
3063     /***
3064      * Parse a String into a Float
3065      *
3066      * @param self a String
3067      * @return a Float
3068      */
3069     public static Float toFloat(String self) {
3070         return Float.valueOf(self);
3071     }
3072 
3073     /***
3074      * Parse a String into a Double
3075      *
3076      * @param self a String
3077      * @return a Double
3078      */
3079     public static Double toDouble(String self) {
3080         return Double.valueOf(self);
3081     }
3082 
3083     /***
3084      * Transform a Number into an Integer
3085      *
3086      * @param self a Number
3087      * @return an Integer
3088      */
3089     public static Integer toInteger(Number self) {
3090         return new Integer(self.intValue());
3091     }
3092 
3093     // Date methods
3094     //-------------------------------------------------------------------------
3095 
3096     /***
3097      * Increments a Date by a day
3098      *
3099      * @param self a Date
3100      * @return the next days date
3101      */
3102     public static Date next(Date self) {
3103         return plus(self, 1);
3104     }
3105 
3106     /***
3107      * Decrement a Date by a day
3108      *
3109      * @param self a Date
3110      * @return the previous days date
3111      */
3112     public static Date previous(Date self) {
3113         return minus(self, 1);
3114     }
3115 
3116     /***
3117      * Adds a number of days to this date and returns the new date
3118      *
3119      * @param self a Date
3120      * @param days the number of days to increase
3121      * @return the new date
3122      */
3123     public static Date plus(Date self, int days) {
3124         Calendar calendar = (Calendar) Calendar.getInstance().clone();
3125         calendar.setTime(self);
3126         calendar.add(Calendar.DAY_OF_YEAR, days);
3127         return calendar.getTime();
3128     }
3129 
3130     /***
3131      * Subtracts a number of days from this date and returns the new date
3132      *
3133      * @param self a Date
3134      * @return the new date
3135      */
3136     public static Date minus(Date self, int days) {
3137         return plus(self, -days);
3138     }
3139 
3140     // File and stream based methods
3141     //-------------------------------------------------------------------------
3142 
3143     /***
3144      * Iterates through the given file line by line
3145      *
3146      * @param self    a File
3147      * @param closure a closure
3148      * @throws IOException
3149      */
3150     public static void eachLine(File self, Closure closure) throws IOException {
3151         eachLine(newReader(self), closure);
3152     }
3153 
3154     /***
3155      * Iterates through the given reader line by line
3156      *
3157      * @param self    a Reader
3158      * @param closure a closure
3159      * @throws IOException
3160      */
3161     public static void eachLine(Reader self, Closure closure) throws IOException {
3162         BufferedReader br = null;
3163 
3164         if (self instanceof BufferedReader)
3165             br = (BufferedReader) self;
3166         else
3167             br = new BufferedReader(self);
3168 
3169         try {
3170             while (true) {
3171                 String line = br.readLine();
3172                 if (line == null) {
3173                     break;
3174                 } else {
3175                     closure.call(line);
3176                 }
3177             }
3178             br.close();
3179         } catch (IOException e) {
3180             if (self != null) {
3181                 try {
3182                     br.close();
3183                 } catch (Exception e2) {
3184                     // ignore as we're already throwing
3185                 }
3186                 throw e;
3187             }
3188         }
3189     }
3190 
3191     /***
3192      * Iterates through the given file line by line, splitting on the seperator
3193      *
3194      * @param self    a File
3195      * @param sep     a String separator
3196      * @param closure a closure
3197      * @throws IOException
3198      */
3199     public static void splitEachLine(File self, String sep, Closure closure) throws IOException {
3200         splitEachLine(newReader(self), sep, closure);
3201     }
3202 
3203     /***
3204      * Iterates through the given reader line by line, splitting on the seperator
3205      *
3206      * @param self    a Reader
3207      * @param sep     a String separator
3208      * @param closure a closure
3209      * @throws IOException
3210      */
3211     public static void splitEachLine(Reader self, String sep, Closure closure) throws IOException {
3212         BufferedReader br = null;
3213 
3214         if (self instanceof BufferedReader)
3215             br = (BufferedReader) self;
3216         else
3217             br = new BufferedReader(self);
3218 
3219         List args = new ArrayList();
3220 
3221         try {
3222             while (true) {
3223                 String line = br.readLine();
3224                 if (line == null) {
3225                     break;
3226                 } else {
3227                     List vals = Arrays.asList(line.split(sep));
3228                     args.clear();
3229                     args.add(vals);
3230                     closure.call(args);
3231                 }
3232             }
3233             br.close();
3234         } catch (IOException e) {
3235             if (self != null) {
3236                 try {
3237                     br.close();
3238                 } catch (Exception e2) {
3239                     // ignore as we're already throwing
3240                 }
3241                 throw e;
3242             }
3243         }
3244     }
3245 
3246     /***
3247      * Read a single, whole line from the given Reader
3248      *
3249      * @param self a Reader
3250      * @return a line
3251      * @throws IOException
3252      */
3253     public static String readLine(Reader self) throws IOException {
3254         BufferedReader br = null;
3255 
3256         if (self instanceof BufferedReader) {
3257             br = (BufferedReader) self;
3258         } else {
3259             br = new BufferedReader(self);
3260         }
3261         return br.readLine();
3262     }
3263 
3264     /***
3265      * Read a single, whole line from the given InputStream
3266      *
3267      * @param stream an InputStream
3268      * @return a line
3269      * @throws IOException
3270      */
3271     public static String readLine(InputStream stream) throws IOException {
3272         return readLine(new InputStreamReader(stream));
3273     }
3274 
3275     /***
3276      * Reads the file into a list of Strings for each line
3277      *
3278      * @param file a File
3279      * @return a List of lines
3280      * @throws IOException
3281      */
3282     public static List readLines(File file) throws IOException {
3283         IteratorClosureAdapter closure = new IteratorClosureAdapter(file);
3284         eachLine(file, closure);
3285         return closure.asList();
3286     }
3287 
3288     /***
3289      * Reads the content of the File opened with the specified encoding and returns it as a String
3290      *
3291      * @param file    the file whose content we want to read
3292      * @param charset the charset used to read the content of the file
3293      * @return a String containing the content of the file
3294      * @throws IOException
3295      */
3296     public static String getText(File file, String charset) throws IOException {
3297         BufferedReader reader = newReader(file, charset);
3298         return getText(reader);
3299     }
3300 
3301     /***
3302      * Reads the content of the File and returns it as a String
3303      *
3304      * @param file the file whose content we want to read
3305      * @return a String containing the content of the file
3306      * @throws IOException
3307      */
3308     public static String getText(File file) throws IOException {
3309         BufferedReader reader = newReader(file);
3310         return getText(reader);
3311     }
3312 
3313     /***
3314      * Reads the content of this URL and returns it as a String
3315      *
3316      * @param url URL to read content from
3317      * @return the text from that URL
3318      * @throws IOException
3319      */
3320     public static String getText(URL url) throws IOException {
3321         return getText(url, CharsetToolkit.getDefaultSystemCharset().toString());
3322     }
3323 
3324     /***
3325      * Reads the content of this URL and returns it as a String
3326      *
3327      * @param url     URL to read content from
3328      * @param charset opens the stream with a specified charset
3329      * @return the text from that URL
3330      * @throws IOException
3331      */
3332     public static String getText(URL url, String charset) throws IOException {
3333         BufferedReader reader = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), charset));
3334         return getText(reader);
3335     }
3336 
3337     /***
3338      * Reads the content of this InputStream and returns it as a String
3339      *
3340      * @param is an input stream
3341      * @return the text from that URL
3342      * @throws IOException
3343      */
3344     public static String getText(InputStream is) throws IOException {
3345         BufferedReader reader = new BufferedReader(new InputStreamReader(is));
3346         return getText(reader);
3347     }
3348 
3349     /***
3350      * Reads the content of this InputStream with a specified charset and returns it as a String
3351      *
3352      * @param is      an input stream
3353      * @param charset opens the stream with a specified charset
3354      * @return the text from that URL
3355      * @throws IOException
3356      */
3357     public static String getText(InputStream is, String charset) throws IOException {
3358         BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
3359         return getText(reader);
3360     }
3361 
3362     /***
3363      * Reads the content of the Reader and returns it as a String
3364      *
3365      * @param reader a Reader whose content we want to read
3366      * @return a String containing the content of the buffered reader
3367      * @throws IOException
3368      */
3369     public static String getText(Reader reader) throws IOException {
3370         BufferedReader bufferedReader = new BufferedReader(reader);
3371         return getText(bufferedReader);
3372     }
3373 
3374     /***
3375      * Reads the content of the BufferedReader and returns it as a String
3376      *
3377      * @param reader a BufferedReader whose content we want to read
3378      * @return a String containing the content of the buffered reader
3379      * @throws IOException
3380      */
3381     public static String getText(BufferedReader reader) throws IOException {
3382         StringBuffer answer = new StringBuffer();
3383         // reading the content of the file within a char buffer allow to keep the correct line endings
3384         char[] charBuffer = new char[4096];
3385         int nbCharRead = 0;
3386         while ((nbCharRead = reader.read(charBuffer)) != -1) {
3387             // appends buffer
3388             answer.append(charBuffer, 0, nbCharRead);
3389         }
3390         reader.close();
3391         return answer.toString();
3392     }
3393 
3394     /***
3395      * Write the text and append a new line (depending on the platform line-ending)
3396      *
3397      * @param writer a BufferedWriter
3398      * @param line   the line to write
3399      * @throws IOException
3400      */
3401     public static void writeLine(BufferedWriter writer, String line) throws IOException {
3402         writer.write(line);
3403         writer.newLine();
3404     }
3405 
3406     /***
3407      * Write the text to the File.
3408      *
3409      * @param file a File
3410      * @param text the text to write to the File
3411      * @throws IOException
3412      */
3413     public static void write(File file, String text) throws IOException {
3414         BufferedWriter writer = newWriter(file);
3415         writer.write(text);
3416         writer.close();
3417     }
3418 
3419     /***
3420      * Write the text to the File with a specified encoding.
3421      *
3422      * @param file    a File
3423      * @param text    the text to write to the File
3424      * @param charset the charset used
3425      * @throws IOException
3426      */
3427     public static void write(File file, String text, String charset) throws IOException {
3428         BufferedWriter writer = newWriter(file, charset);
3429         writer.write(text);
3430         writer.close();
3431     }
3432 
3433     /***
3434      * Append the text at the end of the File
3435      *
3436      * @param file a File
3437      * @param text the text to append at the end of the File
3438      * @throws IOException
3439      */
3440     public static void append(File file, String text) throws IOException {
3441         BufferedWriter writer = newWriter(file, true);
3442         writer.write(text);
3443         writer.close();
3444     }
3445 
3446     /***
3447      * Append the text at the end of the File with a specified encoding
3448      *
3449      * @param file    a File
3450      * @param text    the text to append at the end of the File
3451      * @param charset the charset used
3452      * @throws IOException
3453      */
3454     public static void append(File file, String text, String charset) throws IOException {
3455         BufferedWriter writer = newWriter(file, charset, true);
3456         writer.write(text);
3457         writer.close();
3458     }
3459 
3460     /***
3461      * Reads the reader into a list of Strings for each line
3462      *
3463      * @param reader a Reader
3464      * @return a List of lines
3465      * @throws IOException
3466      */
3467     public static List readLines(Reader reader) throws IOException {
3468         IteratorClosureAdapter closure = new IteratorClosureAdapter(reader);
3469         eachLine(reader, closure);
3470         return closure.asList();
3471     }
3472 
3473     /***
3474      * Invokes the closure for each file in the given directory
3475      *
3476      * @param self    a File
3477      * @param closure a closure
3478      */
3479     public static void eachFile(File self, Closure closure) {
3480         File[] files = self.listFiles();
3481         for (int i = 0; i < files.length; i++) {
3482             closure.call(files[i]);
3483         }
3484     }
3485 
3486     /***
3487      * Invokes the closure for each file in the given directory and recursively.
3488      * It is a depth-first exploration, directories are included in the search.
3489      *
3490      * @param self    a File
3491      * @param closure a closure
3492      */
3493     public static void eachFileRecurse(File self, Closure closure) {
3494         File[] files = self.listFiles();
3495         for (int i = 0; i < files.length; i++) {
3496             if (files[i].isDirectory()) {
3497                 closure.call(files[i]);
3498                 eachFileRecurse(files[i], closure);
3499             } else {
3500                 closure.call(files[i]);
3501             }
3502         }
3503     }
3504 
3505     /***
3506      * Helper method to create a buffered reader for a file
3507      *
3508      * @param file a File
3509      * @return a BufferedReader
3510      * @throws IOException
3511      */
3512     public static BufferedReader newReader(File file) throws IOException {
3513         CharsetToolkit toolkit = new CharsetToolkit(file);
3514         return toolkit.getReader();
3515     }
3516 
3517     /***
3518      * Helper method to create a buffered reader for a file, with a specified charset
3519      *
3520      * @param file    a File
3521      * @param charset the charset with which we want to write in the File
3522      * @return a BufferedReader
3523      * @throws FileNotFoundException        if the File was not found
3524      * @throws UnsupportedEncodingException if the encoding specified is not supported
3525      */
3526     public static BufferedReader newReader(File file, String charset)
3527             throws FileNotFoundException, UnsupportedEncodingException {
3528         return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
3529     }
3530 
3531     /***
3532      * Provides a reader for an arbitrary input stream
3533      *
3534      * @param self an input stream
3535      * @return a reader
3536      */
3537     public static BufferedReader newReader(final InputStream self) {
3538         return new BufferedReader(new InputStreamReader(self));
3539     }
3540 
3541     /***
3542      * Helper method to create a new BufferedReader for a file and then
3543      * passes it into the closure and ensures its closed again afterwords
3544      *
3545      * @param file
3546      * @throws FileNotFoundException
3547      */
3548     public static void withReader(File file, Closure closure) throws IOException {
3549         withReader(newReader(file), closure);
3550     }
3551 
3552     /***
3553      * Helper method to create a buffered output stream for a file
3554      *
3555      * @param file
3556      * @return
3557      * @throws FileNotFoundException
3558      */
3559     public static BufferedOutputStream newOutputStream(File file) throws IOException {
3560         return new BufferedOutputStream(new FileOutputStream(file));
3561     }
3562 
3563     /***
3564      * Helper method to create a new OutputStream for a file and then
3565      * passes it into the closure and ensures its closed again afterwords
3566      *
3567      * @param file a File
3568      * @throws FileNotFoundException
3569      */
3570     public static void withOutputStream(File file, Closure closure) throws IOException {
3571         withStream(newOutputStream(file), closure);
3572     }
3573 
3574     /***
3575      * Helper method to create a new InputStream for a file and then
3576      * passes it into the closure and ensures its closed again afterwords
3577      *
3578      * @param file a File
3579      * @throws FileNotFoundException
3580      */
3581     public static void withInputStream(File file, Closure closure) throws IOException {
3582         withStream(newInputStream(file), closure);
3583     }
3584 
3585     /***
3586      * Helper method to create a buffered writer for a file
3587      *
3588      * @param file a File
3589      * @return a BufferedWriter
3590      * @throws FileNotFoundException
3591      */
3592     public static BufferedWriter newWriter(File file) throws IOException {
3593         return new BufferedWriter(new FileWriter(file));
3594     }
3595 
3596     /***
3597      * Helper method to create a buffered writer for a file in append mode
3598      *
3599      * @param file   a File
3600      * @param append true if in append mode
3601      * @return a BufferedWriter
3602      * @throws FileNotFoundException
3603      */
3604     public static BufferedWriter newWriter(File file, boolean append) throws IOException {
3605         return new BufferedWriter(new FileWriter(file, append));
3606     }
3607 
3608     /***
3609      * Helper method to create a buffered writer for a file
3610      *
3611      * @param file    a File
3612      * @param charset the name of the encoding used to write in this file
3613      * @param append  true if in append mode
3614      * @return a BufferedWriter
3615      * @throws FileNotFoundException
3616      */
3617     public static BufferedWriter newWriter(File file, String charset, boolean append) throws IOException {
3618         if (append) {
3619             return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charset));
3620         } else {
3621             // first write the Byte Order Mark for Unicode encodings
3622             FileOutputStream stream = new FileOutputStream(file);
3623             if ("UTF-16BE".equals(charset)) {
3624                 writeUtf16Bom(stream, true);
3625             } else if ("UTF-16LE".equals(charset)) {
3626                 writeUtf16Bom(stream, false);
3627             }
3628             return new BufferedWriter(new OutputStreamWriter(stream, charset));
3629         }
3630     }
3631 
3632     /***
3633      * Helper method to create a buffered writer for a file
3634      *
3635      * @param file    a File
3636      * @param charset the name of the encoding used to write in this file
3637      * @return a BufferedWriter
3638      * @throws FileNotFoundException
3639      */
3640     public static BufferedWriter newWriter(File file, String charset) throws IOException {
3641         return newWriter(file, charset, false);
3642     }
3643 
3644     /***
3645      * Write a Byte Order Mark at the begining of the file
3646      *
3647      * @param stream    the FileOuputStream to write the BOM to
3648      * @param bigEndian true if UTF 16 Big Endian or false if Low Endian
3649      * @throws IOException
3650      */
3651     private static void writeUtf16Bom(FileOutputStream stream, boolean bigEndian) throws IOException {
3652         if (bigEndian) {
3653             stream.write(-2);
3654             stream.write(-1);
3655         } else {
3656             stream.write(-1);
3657             stream.write(-2);
3658         }
3659     }
3660 
3661     /***
3662      * Helper method to create a new BufferedWriter for a file and then
3663      * passes it into the closure and ensures it is closed again afterwords
3664      *
3665      * @param file    a File
3666      * @param closure a closure
3667      * @throws FileNotFoundException
3668      */
3669     public static void withWriter(File file, Closure closure) throws IOException {
3670         withWriter(newWriter(file), closure);
3671     }
3672 
3673     /***
3674      * Helper method to create a new BufferedWriter for a file in a specified encoding
3675      * and then passes it into the closure and ensures it is closed again afterwords
3676      *
3677      * @param file    a File
3678      * @param charset the charset used
3679      * @param closure a closure
3680      * @throws FileNotFoundException
3681      */
3682     public static void withWriter(File file, String charset, Closure closure) throws IOException {
3683         withWriter(newWriter(file, charset), closure);
3684     }
3685 
3686     /***
3687      * Helper method to create a new BufferedWriter for a file in a specified encoding
3688      * in append mode and then passes it into the closure and ensures it is closed again afterwords
3689      *
3690      * @param file    a File
3691      * @param charset the charset used
3692      * @param closure a closure
3693      * @throws FileNotFoundException
3694      */
3695     public static void withWriterAppend(File file, String charset, Closure closure) throws IOException {
3696         withWriter(newWriter(file, charset, true), closure);
3697     }
3698 
3699     /***
3700      * Helper method to create a new PrintWriter for a file
3701      *
3702      * @param file a File
3703      * @throws FileNotFoundException
3704      */
3705     public static PrintWriter newPrintWriter(File file) throws IOException {
3706         return new PrintWriter(newWriter(file));
3707     }
3708 
3709     /***
3710      * Helper method to create a new PrintWriter for a file with a specified charset
3711      *
3712      * @param file    a File
3713      * @param charset the charset
3714      * @return a PrintWriter
3715      * @throws FileNotFoundException
3716      */
3717     public static PrintWriter newPrintWriter(File file, String charset) throws IOException {
3718         return new PrintWriter(newWriter(file, charset));
3719     }
3720 
3721     /***
3722      * Helper method to create a new PrintWriter for a file and then
3723      * passes it into the closure and ensures its closed again afterwords
3724      *
3725      * @param file a File
3726      * @throws FileNotFoundException
3727      */
3728     public static void withPrintWriter(File file, Closure closure) throws IOException {
3729         withWriter(newPrintWriter(file), closure);
3730     }
3731 
3732     /***
3733      * Allows a writer to be used, calling the closure with the writer
3734      * and then ensuring that the writer is closed down again irrespective
3735      * of whether exceptions occur or the
3736      *
3737      * @param writer  the writer which is used and then closed
3738      * @param closure the closure that the writer is passed into
3739      * @throws IOException
3740      */
3741     public static void withWriter(Writer writer, Closure closure) throws IOException {
3742         try {
3743             closure.call(writer);
3744 
3745             // lets try close the writer & throw the exception if it fails
3746             // but not try to reclose it in the finally block
3747             Writer temp = writer;
3748             writer = null;
3749             temp.close();
3750         } finally {
3751             if (writer != null) {
3752                 try {
3753                     writer.close();
3754                 } catch (IOException e) {
3755                     log.warning("Caught exception closing writer: " + e);
3756                 }
3757             }
3758         }
3759     }
3760 
3761     /***
3762      * Allows a Reader to be used, calling the closure with the writer
3763      * and then ensuring that the writer is closed down again irrespective
3764      * of whether exceptions occur or the
3765      *
3766      * @param writer  the writer which is used and then closed
3767      * @param closure the closure that the writer is passed into
3768      * @throws IOException
3769      */
3770     public static void withReader(Reader writer, Closure closure) throws IOException {
3771         try {
3772             closure.call(writer);
3773 
3774             // lets try close the writer & throw the exception if it fails
3775             // but not try to reclose it in the finally block
3776             Reader temp = writer;
3777             writer = null;
3778             temp.close();
3779         } finally {
3780             if (writer != null) {
3781                 try {
3782                     writer.close();
3783                 } catch (IOException e) {
3784                     log.warning("Caught exception closing writer: " + e);
3785                 }
3786             }
3787         }
3788     }
3789 
3790     /***
3791      * Allows a InputStream to be used, calling the closure with the stream
3792      * and then ensuring that the stream is closed down again irrespective
3793      * of whether exceptions occur or the
3794      *
3795      * @param stream  the stream which is used and then closed
3796      * @param closure the closure that the stream is passed into
3797      * @throws IOException
3798      */
3799     public static void withStream(InputStream stream, Closure closure) throws IOException {
3800         try {
3801             closure.call(stream);
3802 
3803             // lets try close the stream & throw the exception if it fails
3804             // but not try to reclose it in the finally block
3805             InputStream temp = stream;
3806             stream = null;
3807             temp.close();
3808         } finally {
3809             if (stream != null) {
3810                 try {
3811                     stream.close();
3812                 } catch (IOException e) {
3813                     log.warning("Caught exception closing stream: " + e);
3814                 }
3815             }
3816         }
3817     }
3818 
3819     /***
3820      * Reads the stream into a list of Strings for each line
3821      *
3822      * @param stream a stream
3823      * @return a List of lines
3824      * @throws IOException
3825      */
3826     public static List readLines(InputStream stream) throws IOException {
3827         return readLines(new BufferedReader(new InputStreamReader(stream)));
3828     }
3829 
3830     /***
3831      * Iterates through the given stream line by line
3832      *
3833      * @param stream  a stream
3834      * @param closure a closure
3835      * @throws IOException
3836      */
3837     public static void eachLine(InputStream stream, Closure closure) throws IOException {
3838         eachLine(new InputStreamReader(stream), closure);
3839     }
3840 
3841     /***
3842      * Iterates through the lines read from the URL's associated input stream
3843      *
3844      * @param url     a URL to open and read
3845      * @param closure a closure to apply on each line
3846      * @throws IOException
3847      */
3848     public static void eachLine(URL url, Closure closure) throws IOException {
3849         eachLine(url.openConnection().getInputStream(), closure);
3850     }
3851 
3852     /***
3853      * Helper method to create a new BufferedReader for a URL and then
3854      * passes it into the closure and ensures its closed again afterwords
3855      *
3856      * @param url a URL
3857      * @throws FileNotFoundException
3858      */
3859     public static void withReader(URL url, Closure closure) throws IOException {
3860         withReader(url.openConnection().getInputStream(), closure);
3861     }
3862 
3863     /***
3864      * Helper method to create a new BufferedReader for a stream and then
3865      * passes it into the closure and ensures its closed again afterwords
3866      *
3867      * @param in a stream
3868      * @throws FileNotFoundException
3869      */
3870     public static void withReader(InputStream in, Closure closure) throws IOException {
3871         withReader(new InputStreamReader(in), closure);
3872     }
3873 
3874     /***
3875      * Allows an output stream to be used, calling the closure with the output stream
3876      * and then ensuring that the output stream is closed down again irrespective
3877      * of whether exceptions occur
3878      *
3879      * @param stream  the stream which is used and then closed
3880      * @param closure the closure that the writer is passed into
3881      * @throws IOException
3882      */
3883     public static void withWriter(OutputStream stream, Closure closure) throws IOException {
3884         withWriter(new OutputStreamWriter(stream), closure);
3885     }
3886 
3887     /***
3888      * Allows an output stream to be used, calling the closure with the output stream
3889      * and then ensuring that the output stream is closed down again irrespective
3890      * of whether exceptions occur.
3891      *
3892      * @param stream  the stream which is used and then closed
3893      * @param charset the charset used
3894      * @param closure the closure that the writer is passed into
3895      * @throws IOException
3896      */
3897     public static void withWriter(OutputStream stream, String charset, Closure closure) throws IOException {
3898         withWriter(new OutputStreamWriter(stream, charset), closure);
3899     }
3900 
3901     /***
3902      * Allows a OutputStream to be used, calling the closure with the stream
3903      * and then ensuring that the stream is closed down again irrespective
3904      * of whether exceptions occur.
3905      *
3906      * @param stream  the stream which is used and then closed
3907      * @param closure the closure that the stream is passed into
3908      * @throws IOException
3909      */
3910     public static void withStream(OutputStream stream, Closure closure) throws IOException {
3911         try {
3912             closure.call(stream);
3913 
3914             // lets try close the stream & throw the exception if it fails
3915             // but not try to reclose it in the finally block
3916             OutputStream temp = stream;
3917             stream = null;
3918             temp.close();
3919         } finally {
3920             if (stream != null) {
3921                 try {
3922                     stream.close();
3923                 } catch (IOException e) {
3924                     log.warning("Caught exception closing stream: " + e);
3925                 }
3926             }
3927         }
3928     }
3929 
3930     /***
3931      * Helper method to create a buffered input stream for a file
3932      *
3933      * @param file a File
3934      * @return a BufferedInputStream of the file
3935      * @throws FileNotFoundException
3936      */
3937     public static BufferedInputStream newInputStream(File file) throws FileNotFoundException {
3938         return new BufferedInputStream(new FileInputStream(file));
3939     }
3940 
3941     /***
3942      * Traverse through each byte of the specified File
3943      *
3944      * @param self    a File
3945      * @param closure a closure
3946      */
3947     public static void eachByte(File self, Closure closure) throws IOException {
3948         BufferedInputStream is = newInputStream(self);
3949         eachByte(is, closure);
3950     }
3951 
3952     /***
3953      * Traverse through each byte of the specified stream
3954      *
3955      * @param is      stream to iterate over
3956      * @param closure closure to apply to each byte
3957      * @throws IOException
3958      */
3959     public static void eachByte(InputStream is, Closure closure) throws IOException {
3960         try {
3961             while (true) {
3962                 int b = is.read();
3963                 if (b == -1) {
3964                     break;
3965                 } else {
3966                     closure.call(new Byte((byte) b));
3967                 }
3968             }
3969             is.close();
3970         } catch (IOException e) {
3971             if (is != null) {
3972                 try {
3973                     is.close();
3974                 } catch (Exception e2) {
3975                     // ignore as we're already throwing
3976                 }
3977                 throw e;
3978             }
3979         }
3980     }
3981 
3982     /***
3983      * Traverse through each byte of the specified URL
3984      *
3985      * @param url     url to iterate over
3986      * @param closure closure to apply to each byte
3987      * @throws IOException
3988      */
3989     public static void eachByte(URL url, Closure closure) throws IOException {
3990         InputStream is = url.openConnection().getInputStream();
3991         eachByte(is, closure);
3992     }
3993 
3994     /***
3995      * Transforms the characters from a reader with a Closure and write them to a writer
3996      *
3997      * @param reader
3998      * @param writer
3999      * @param closure
4000      */
4001     public static void transformChar(Reader reader, Writer writer, Closure closure) {
4002         int c;
4003         try {
4004             char[] chars = new char[1];
4005             while ((c = reader.read()) != -1) {
4006                 chars[0] = (char) c;
4007                 writer.write((String) closure.call(new String(chars)));
4008             }
4009         } catch (IOException e) {
4010         }
4011     }
4012 
4013     /***
4014      * Transforms the lines from a reader with a Closure and write them to a writer
4015      *
4016      * @param reader
4017      * @param writer
4018      * @param closure
4019      */
4020     public static void transformLine(Reader reader, Writer writer, Closure closure) throws IOException {
4021         BufferedReader br = new BufferedReader(reader);
4022         BufferedWriter bw = new BufferedWriter(writer);
4023         String line;
4024         while ((line = br.readLine()) != null) {
4025             Object o = closure.call(line);
4026             if (o != null) {
4027                 bw.write(o.toString());
4028                 bw.newLine();
4029             }
4030         }
4031     }
4032 
4033     /***
4034      * Filter the lines from a reader and write them on the writer, according to a closure
4035      * which returns true or false.
4036      *
4037      * @param reader  a reader
4038      * @param writer  a writer
4039      * @param closure the closure which returns booleans
4040      * @throws IOException
4041      */
4042     public static void filterLine(Reader reader, Writer writer, Closure closure) throws IOException {
4043         BufferedReader br = new BufferedReader(reader);
4044         BufferedWriter bw = new BufferedWriter(writer);
4045         String line;
4046         while ((line = br.readLine()) != null) {
4047             if (InvokerHelper.asBool(closure.call(line))) {
4048                 bw.write(line);
4049                 bw.newLine();
4050             }
4051         }
4052         bw.flush();
4053     }
4054 
4055     /***
4056      * Filters the lines of a File and creates a Writeable in return to stream the filtered lines
4057      *
4058      * @param self    a File
4059      * @param closure a closure which returns a boolean indicating to filter the line or not
4060      * @return a Writable closure
4061      * @throws IOException if <code>self</code> is not readable
4062      */
4063     public static Writable filterLine(final File self, final Closure closure) throws IOException {
4064         return filterLine(newReader(self), closure);
4065     }
4066 
4067     /***
4068      * Filter the lines from a File and write them on a writer, according to a closure
4069      * which returns true or false
4070      *
4071      * @param self    a File
4072      * @param writer  a writer
4073      * @param closure a closure which returns a boolean value and takes a line as input
4074      * @throws IOException if <code>self</code> is not readable
4075      */
4076     public static void filterLine(final File self, final Writer writer, final Closure closure) throws IOException {
4077         filterLine(newReader(self), writer, closure);
4078     }
4079 
4080     /***
4081      * Filter the lines of a Reader and create a Writable in return to stream the filtered lines
4082      *
4083      * @param reader  a reader
4084      * @param closure a closure returning a boolean indicating to filter or not a line
4085      * @return a Writable closure
4086      */
4087     public static Writable filterLine(Reader reader, final Closure closure) {
4088         final BufferedReader br = new BufferedReader(reader);
4089         return new Writable() {
4090             public Writer writeTo(Writer out) throws IOException {
4091                 BufferedWriter bw = new BufferedWriter(out);
4092                 String line;
4093                 while ((line = br.readLine()) != null) {
4094                     if (InvokerHelper.asBool(closure.call(line))) {
4095                         bw.write(line);
4096                         bw.newLine();
4097                     }
4098                 }
4099                 bw.flush();
4100                 return out;
4101             }
4102 
4103             public String toString() {
4104                 StringWriter buffer = new StringWriter();
4105                 try {
4106                     writeTo(buffer);
4107                 } catch (IOException e) {
4108                     throw new RuntimeException(e); // TODO: change this exception type
4109                 }
4110                 return buffer.toString();
4111             }
4112         };
4113     }
4114 
4115     /***
4116      * Filter lines from an input stream using a closure predicate
4117      *
4118      * @param self      an input stream
4119      * @param predicate a closure which returns boolean and takes a line
4120      * @return a filtered writer
4121      */
4122     public static Writable filterLine(final InputStream self, final Closure predicate) {
4123         return filterLine(newReader(self), predicate);
4124     }
4125 
4126     /***
4127      * Filters lines from an input stream, writing to a writer, using a closure which
4128      * returns boolean and takes a line.
4129      *
4130      * @param self      an InputStream
4131      * @param writer    a writer to write output to
4132      * @param predicate a closure which returns a boolean and takes a line as input
4133      */
4134     public static void filterLine(final InputStream self, final Writer writer, final Closure predicate)
4135             throws IOException {
4136         filterLine(newReader(self), writer, predicate);
4137     }
4138 
4139     /***
4140      * Reads the content of the file into an array of byte
4141      *
4142      * @param file a File
4143      * @return a List of Bytes
4144      */
4145     public static byte[] readBytes(File file) throws IOException {
4146         byte[] bytes = new byte[(int) file.length()];
4147         FileInputStream fileInputStream = new FileInputStream(file);
4148         DataInputStream dis = new DataInputStream(fileInputStream);
4149         dis.readFully(bytes);
4150         dis.close();
4151         return bytes;
4152     }
4153 
4154 
4155 
4156     // ================================
4157     // Socket and ServerSocket methods
4158 
4159     /***
4160      * Allows an InputStream and an OutputStream from a Socket to be used,
4161      * calling the closure with the streams and then ensuring that the streams are closed down again
4162      * irrespective of whether exceptions occur.
4163      *
4164      * @param socket  a Socket
4165      * @param closure a Closure
4166      * @throws IOException
4167      */
4168     public static void withStreams(Socket socket, Closure closure) throws IOException {
4169         InputStream input = socket.getInputStream();
4170         OutputStream output = socket.getOutputStream();
4171         try {
4172             closure.call(new Object[]{input, output});
4173         } finally {
4174             try {
4175                 input.close();
4176             } catch (IOException e) {
4177                 // noop
4178             }
4179             try {
4180                 output.close();
4181             } catch (IOException e) {
4182                 // noop
4183             }
4184         }
4185     }
4186 
4187     /***
4188      * Overloads the left shift operator to provide an append mechanism
4189      * to add things to the output stream of a socket
4190      *
4191      * @param self  a Socket
4192      * @param value a value to append
4193      * @return a Writer
4194      */
4195     public static Writer leftShift(Socket self, Object value) throws IOException {
4196         return leftShift(self.getOutputStream(), value);
4197     }
4198 
4199     /***
4200      * Overloads the left shift operator to provide an append mechanism
4201      * to add bytes to the output stream of a socket
4202      *
4203      * @param self  a Socket
4204      * @param value a value to append
4205      * @return an OutputStream
4206      */
4207     public static OutputStream leftShift(Socket self, byte[] value) throws IOException {
4208         return leftShift(self.getOutputStream(), value);
4209     }
4210 
4211     /***
4212      * Allow to pass a Closure to the accept methods of ServerSocket
4213      *
4214      * @param serverSocket a ServerSocket
4215      * @param closure      a Closure
4216      * @return a Socket
4217      * @throws IOException
4218      */
4219     public static Socket accept(ServerSocket serverSocket, final Closure closure) throws IOException {
4220         final Socket socket = serverSocket.accept();
4221         new Thread(new Runnable() {
4222             public void run() {
4223                 try {
4224                     closure.call(socket);
4225                 } finally {
4226                     try {
4227                         socket.close();
4228                     } catch (IOException e) {
4229                         // noop
4230                     }
4231                 }
4232             }
4233         }).start();
4234         return socket;
4235     }
4236 
4237 
4238     /***
4239      * @param file a File
4240      * @return a File which wraps the input file and which implements Writable
4241      */
4242     public static File asWritable(File file) {
4243         return new WritableFile(file);
4244     }
4245 
4246     /***
4247      * @param file     a File
4248      * @param encoding the encoding to be used when reading the file's contents
4249      * @return File which wraps the input file and which implements Writable
4250      */
4251     public static File asWritable(File file, String encoding) {
4252         return new WritableFile(file, encoding);
4253     }
4254 
4255     /***
4256      * Converts the given String into a List of strings of one character
4257      *
4258      * @param self a String
4259      * @return a List of characters (a 1-character String)
4260      */
4261     public static List toList(String self) {
4262         int size = self.length();
4263         List answer = new ArrayList(size);
4264         for (int i = 0; i < size; i++) {
4265             answer.add(self.substring(i, i + 1));
4266         }
4267         return answer;
4268     }
4269 
4270     // Process methods
4271     //-------------------------------------------------------------------------
4272 
4273     /***
4274      * An alias method so that a process appears similar to System.out, System.in, System.err;
4275      * you can use process.in, process.out, process.err in a similar way
4276      *
4277      * @return an InputStream
4278      */
4279     public static InputStream getIn(Process self) {
4280         return self.getInputStream();
4281     }
4282 
4283     /***
4284      * Read the text of the output stream of the Process.
4285      *
4286      * @param self a Process
4287      * @return the text of the output
4288      * @throws IOException
4289      */
4290     public static String getText(Process self) throws IOException {
4291         return getText(new BufferedReader(new InputStreamReader(self.getInputStream())));
4292     }
4293 
4294     /***
4295      * An alias method so that a process appears similar to System.out, System.in, System.err;
4296      * you can use process.in, process.out, process.err in a similar way
4297      *
4298      * @return an InputStream
4299      */
4300     public static InputStream getErr(Process self) {
4301         return self.getErrorStream();
4302     }
4303 
4304     /***
4305      * An alias method so that a process appears similar to System.out, System.in, System.err;
4306      * you can use process.in, process.out, process.err in a similar way
4307      *
4308      * @return an OutputStream
4309      */
4310     public static OutputStream getOut(Process self) {
4311         return self.getOutputStream();
4312     }
4313 
4314     /***
4315      * Overloads the left shift operator to provide an append mechanism
4316      * to pipe into a Process
4317      *
4318      * @param self  a Process
4319      * @param value a value to append
4320      * @return a Writer
4321      */
4322     public static Writer leftShift(Process self, Object value) throws IOException {
4323         return leftShift(self.getOutputStream(), value);
4324     }
4325 
4326     /***
4327      * Overloads the left shift operator to provide an append mechanism
4328      * to pipe into a Process
4329      *
4330      * @param self  a Process
4331      * @param value a value to append
4332      * @return an OutputStream
4333      */
4334     public static OutputStream leftShift(Process self, byte[] value) throws IOException {
4335         return leftShift(self.getOutputStream(), value);
4336     }
4337 
4338     /***
4339      * Wait for the process to finish during a certain amount of time, otherwise stops the process.
4340      *
4341      * @param self           a Process
4342      * @param numberOfMillis the number of milliseconds to wait before stopping the process
4343      */
4344     public static void waitForOrKill(Process self, long numberOfMillis) {
4345         ProcessRunner runnable = new ProcessRunner(self);
4346         Thread thread = new Thread(runnable);
4347         thread.start();
4348         runnable.waitForOrKill(numberOfMillis);
4349     }
4350 
4351     /***
4352      * process each regex matched substring of a string object. The object
4353      * passed to the closure is a matcher with rich information of the last
4354      * successful match
4355      *
4356      * @param str     the target string
4357      * @param regex   a Regex string
4358      * @param closure a closure
4359      * @author bing ran
4360      */
4361     public static void eachMatch(String str, String regex, Closure closure) {
4362         Pattern p = Pattern.compile(regex);
4363         Matcher m = p.matcher(str);
4364         while (m.find()) {
4365             int count = m.groupCount();
4366             ArrayList groups = new ArrayList();
4367             for (int i = 0; i <= count; i++) {
4368                 groups.add(m.group(i));
4369             }
4370             closure.call(groups);
4371         }
4372     }
4373 
4374     public static void each(Matcher matcher, Closure closure) {
4375         Matcher m = matcher;
4376         while (m.find()) {
4377             int count = m.groupCount();
4378             ArrayList groups = new ArrayList();
4379             for (int i = 0; i <= count; i++) {
4380                 groups.add(m.group(i));
4381             }
4382             closure.call(groups);
4383         }
4384     }
4385 
4386     /***
4387      * Iterates over every element of the collection and return the index of the first object
4388      * that matches the condition specified in the closure
4389      *
4390      * @param self    the iteration object over which we iterate
4391      * @param closure the filter to perform a match on the collection
4392      * @return an integer that is the index of the first macthed object.
4393      */
4394     public static int findIndexOf(Object self, Closure closure) {
4395         int i = 0;
4396         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) {
4397             Object value = iter.next();
4398             if (InvokerHelper.asBool(closure.call(value))) {
4399                 break;
4400             }
4401         }
4402         return i;
4403     }
4404 
4405     /***
4406      * A Runnable which waits for a process to complete together with a notification scheme
4407      * allowing another thread to wait a maximum number of seconds for the process to complete
4408      * before killing it.
4409      */
4410     protected static class ProcessRunner implements Runnable {
4411         Process process;
4412         private boolean finished;
4413 
4414         public ProcessRunner(Process process) {
4415             this.process = process;
4416         }
4417 
4418         public void run() {
4419             try {
4420                 process.waitFor();
4421             } catch (InterruptedException e) {
4422             }
4423             synchronized (this) {
4424                 notifyAll();
4425                 finished = true;
4426             }
4427         }
4428 
4429         public synchronized void waitForOrKill(long millis) {
4430             if (!finished) {
4431                 try {
4432                     wait(millis);
4433                 } catch (InterruptedException e) {
4434                 }
4435                 if (!finished) {
4436                     process.destroy();
4437                 }
4438             }
4439         }
4440     }
4441 }