KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > groovy > runtime > DefaultGroovyMethods


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 JavaDoc;
44 import java.lang.reflect.Field JavaDoc;
45 import java.lang.reflect.Modifier JavaDoc;
46 import java.net.MalformedURLException JavaDoc;
47 import java.net.ServerSocket JavaDoc;
48 import java.net.Socket JavaDoc;
49 import java.net.URL JavaDoc;
50 import java.security.AccessController JavaDoc;
51 import java.security.PrivilegedAction JavaDoc;
52 import java.util.*;
53 import java.util.logging.Logger JavaDoc;
54 import java.util.regex.Matcher JavaDoc;
55 import java.util.regex.Pattern JavaDoc;
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 JavaDoc log = Logger.getLogger(DefaultGroovyMethods.class.getName());
73
74     private static final Integer JavaDoc ONE = new Integer JavaDoc(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 JavaDoc getAt(Object JavaDoc self, String JavaDoc 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 JavaDoc self, String JavaDoc property, Object JavaDoc 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 JavaDoc dump(Object JavaDoc self) {
107         if (self == null) {
108             return "null";
109         }
110         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("<");
111         Class JavaDoc 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 JavaDoc[] fields = klass.getDeclaredFields();
123             for (int i = 0; i < fields.length; i++) {
124                 final Field JavaDoc 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 JavaDoc() {
130                         public Object JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc self, Class JavaDoc 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 JavaDoc 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 JavaDoc self, Object JavaDoc 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 JavaDoc 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 JavaDoc self, Object JavaDoc 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 JavaDoc inspect(Object JavaDoc self) {
258         return InvokerHelper.inspect(self);
259     }
260
261     /**
262      * Print to a console in interactive format
263      */

264     public static void print(Object JavaDoc 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 JavaDoc 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 JavaDoc invokeMethod(Object JavaDoc object, String JavaDoc method, Object JavaDoc arguments) {
289         return InvokerHelper.invokeMethod(object, method, arguments);
290     }
291
292     // isCase methods
293
//-------------------------------------------------------------------------
294
public static boolean isCase(Object JavaDoc caseValue, Object JavaDoc switchValue) {
295         return caseValue.equals(switchValue);
296     }
297
298     public static boolean isCase(String JavaDoc caseValue, Object JavaDoc switchValue) {
299         if (switchValue == null) {
300             return caseValue == null;
301         }
302         return caseValue.equals(switchValue.toString());
303     }
304
305     public static boolean isCase(Class JavaDoc caseValue, Object JavaDoc switchValue) {
306         return caseValue.isInstance(switchValue);
307     }
308
309     public static boolean isCase(Collection caseValue, Object JavaDoc switchValue) {
310         return caseValue.contains(switchValue);
311     }
312
313     public static boolean isCase(Pattern JavaDoc caseValue, Object JavaDoc switchValue) {
314         Matcher JavaDoc 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 JavaDoc 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 JavaDoc self, Closure closure) {
345         int counter = 0;
346         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
347             closure.call(new Object JavaDoc[]{iter.next(), new Integer JavaDoc(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 JavaDoc[]{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 JavaDoc 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 JavaDoc 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 JavaDoc self, Object JavaDoc filter) {
428         List answer = new ArrayList();
429         MetaClass metaClass = InvokerHelper.getMetaClass(filter);
430         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
431             Object JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc find(Object JavaDoc self, Closure closure) {
563         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
564             Object JavaDoc 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 JavaDoc find(Collection self, Closure closure) {
580         for (Iterator iter = self.iterator(); iter.hasNext();) {
581             Object JavaDoc 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 JavaDoc find(Map self, Closure closure) {
597         for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
598             Object JavaDoc 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 JavaDoc self, Closure closure) {
614         List answer = new ArrayList();
615         for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
616             Object JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc inject(Collection self, Object JavaDoc value, Closure closure) {
671         Object JavaDoc[] params = new Object JavaDoc[2];
672         for (Iterator iter = self.iterator(); iter.hasNext();) {
673             Object JavaDoc 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 JavaDoc join(Collection self, String JavaDoc separator) {
689         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
690         boolean first = true;
691         for (Iterator iter = self.iterator(); iter.hasNext();) {
692             Object JavaDoc 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 JavaDoc join(Object JavaDoc[] self, String JavaDoc separator) {
711         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
712         boolean first = true;
713         for (int i = 0; i < self.length; i++) {
714             String JavaDoc 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 JavaDoc max(Collection self) {
732         Object JavaDoc answer = null;
733         for (Iterator iter = self.iterator(); iter.hasNext();) {
734             Object JavaDoc 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 JavaDoc max(Collection self, Comparator comparator) {
752         Object JavaDoc answer = null;
753         for (Iterator iter = self.iterator(); iter.hasNext();) {
754             Object JavaDoc 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 JavaDoc min(Collection self) {
769         Object JavaDoc answer = null;
770         for (Iterator iter = self.iterator(); iter.hasNext();) {
771             Object JavaDoc 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 JavaDoc min(Collection self, Comparator comparator) {
789         Object JavaDoc answer = null;
790         for (Iterator iter = self.iterator(); iter.hasNext();) {
791             Object JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc[] 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 JavaDoc getAt(CharSequence JavaDoc 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 JavaDoc getAt(String JavaDoc 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 JavaDoc getAt(CharSequence JavaDoc 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 JavaDoc getAt(String JavaDoc 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 JavaDoc 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 JavaDoc reverse(String JavaDoc self) {
920         int size = self.length();
921         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(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 JavaDoc toURL(String JavaDoc self) throws MalformedURLException JavaDoc {
936         return new URL JavaDoc(self);
937     }
938
939     private static String JavaDoc getPadding(String JavaDoc padding, int length) {
940         if (padding.length() < length) {
941             return multiply(padding, new Integer JavaDoc(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 JavaDoc padLeft(String JavaDoc self, Number JavaDoc numberOfChars, String JavaDoc 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 JavaDoc padLeft(String JavaDoc self, Number JavaDoc 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 JavaDoc padRight(String JavaDoc self, Number JavaDoc numberOfChars, String JavaDoc 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 JavaDoc padRight(String JavaDoc self, Number JavaDoc 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 JavaDoc center(String JavaDoc self, Number JavaDoc numberOfChars, String JavaDoc padding) {
1010        int numChars = numberOfChars.intValue();
1011        if (numChars <= self.length()) {
1012            return self;
1013        } else {
1014            int charsToAdd = numChars - self.length();
1015            String JavaDoc 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 JavaDoc center(String JavaDoc self, Number JavaDoc 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 JavaDoc getAt(Matcher JavaDoc 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 JavaDoc 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 JavaDoc[] self, Collection indices) {
1120        List answer = new ArrayList(indices.size());
1121        for (Iterator iter = indices.iterator(); iter.hasNext();) {
1122            Object JavaDoc 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 JavaDoc getAt(CharSequence JavaDoc self, Collection indices) {
1143        StringBuffer JavaDoc answer = new StringBuffer JavaDoc();
1144        for (Iterator iter = indices.iterator(); iter.hasNext();) {
1145            Object JavaDoc 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 JavaDoc getAt(String JavaDoc self, Collection indices) {
1166        return (String JavaDoc) getAt((CharSequence JavaDoc) 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 JavaDoc getAt(Matcher JavaDoc self, Collection indices) {
1177        StringBuffer JavaDoc answer = new StringBuffer JavaDoc();
1178        for (Iterator iter = indices.iterator(); iter.hasNext();) {
1179            Object JavaDoc 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 JavaDoc 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 JavaDoc get(Map map, Object JavaDoc key, Object JavaDoc defaultValue) {
1222        Object JavaDoc 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 JavaDoc[] 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 JavaDoc getAt(Object JavaDoc[] 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 JavaDoc[] array, int idx, Object JavaDoc value) {
1262        if (value instanceof Number JavaDoc) {
1263            Class JavaDoc arrayComponentClass = array.getClass().getComponentType();
1264
1265            if (!arrayComponentClass.equals(value.getClass())) {
1266                Object JavaDoc 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 JavaDoc[] 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 JavaDoc 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 JavaDoc 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 JavaDoc getAt(Map self, Object JavaDoc 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 JavaDoc putAt(Map self, Object JavaDoc key, Object JavaDoc 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 JavaDoc("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 JavaDoc property) {
1374        List answer = new ArrayList(coll.size());
1375        for (Iterator iter = coll.iterator(); iter.hasNext();) {
1376            Object JavaDoc item = iter.next();
1377            Object JavaDoc 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 JavaDoc pop(List self) {
1549        if (self.isEmpty()) {
1550            throw new UnsupportedOperationException JavaDoc("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 JavaDoc[] 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc[] array = tmpAnswer.toArray(new Object JavaDoc[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 JavaDoc 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 JavaDoc 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 JavaDoc self, Object JavaDoc 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 JavaDoc self) {
1830        StringWriter answer = new StringWriter();
1831        answer.write(self);
1832        return answer;
1833    }
1834
1835    protected static StringBufferWriter createStringBufferWriter(StringBuffer JavaDoc 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 JavaDoc self, Object JavaDoc 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 JavaDoc 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 JavaDoc leftShift(Number JavaDoc left, Number JavaDoc 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 JavaDoc rightShift(Number JavaDoc left, Number JavaDoc 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 JavaDoc rightShiftUnsigned(Number JavaDoc left, Number JavaDoc 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 JavaDoc 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 JavaDoc first = all.get(0);
1940
1941        //trying to determine the base class of the collections
1942
//special case for Numbers
1943
Class JavaDoc baseClass;
1944        if (first instanceof Number JavaDoc) {
1945            baseClass = Number JavaDoc.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 JavaDoc getAt(byte[] array, int idx) {
1964        return primitiveArrayGet(array, idx);
1965    }
1966
1967    public static Object JavaDoc getAt(char[] array, int idx) {
1968        return primitiveArrayGet(array, idx);
1969    }
1970
1971    public static Object JavaDoc getAt(short[] array, int idx) {
1972        return primitiveArrayGet(array, idx);
1973    }
1974
1975    public static Object JavaDoc getAt(int[] array, int idx) {
1976        return primitiveArrayGet(array, idx);
1977    }
1978
1979    public static Object JavaDoc getAt(long[] array, int idx) {
1980        return primitiveArrayGet(array, idx);
1981    }
1982
1983    public static Object JavaDoc getAt(float[] array, int idx) {
1984        return primitiveArrayGet(array, idx);
1985    }
1986
1987    public static Object JavaDoc getAt(double[] array, int idx) {
1988        return primitiveArrayGet(array, idx);
1989    }
1990
1991    public static Object JavaDoc getAt(byte[] array, Range range) {
1992        return primitiveArrayGet(array, range);
1993    }
1994
1995    public static Object JavaDoc getAt(char[] array, Range range) {
1996        return primitiveArrayGet(array, range);
1997    }
1998
1999    public static Object JavaDoc getAt(short[] array, Range range) {
2000        return primitiveArrayGet(array, range);
2001    }
2002
2003    public static Object JavaDoc getAt(int[] array, Range range) {
2004        return primitiveArrayGet(array, range);
2005    }
2006
2007    public static Object JavaDoc getAt(long[] array, Range range) {
2008        return primitiveArrayGet(array, range);
2009    }
2010
2011    public static Object JavaDoc getAt(float[] array, Range range) {
2012        return primitiveArrayGet(array, range);
2013    }
2014
2015    public static Object JavaDoc getAt(double[] array, Range range) {
2016        return primitiveArrayGet(array, range);
2017    }
2018
2019    public static Object JavaDoc getAt(byte[] array, Collection indices) {
2020        return primitiveArrayGet(array, indices);
2021    }
2022
2023    public static Object JavaDoc getAt(char[] array, Collection indices) {
2024        return primitiveArrayGet(array, indices);
2025    }
2026
2027    public static Object JavaDoc getAt(short[] array, Collection indices) {
2028        return primitiveArrayGet(array, indices);
2029    }
2030
2031    public static Object JavaDoc getAt(int[] array, Collection indices) {
2032        return primitiveArrayGet(array, indices);
2033    }
2034
2035    public static Object JavaDoc getAt(long[] array, Collection indices) {
2036        return primitiveArrayGet(array, indices);
2037    }
2038
2039    public static Object JavaDoc getAt(float[] array, Collection indices) {
2040        return primitiveArrayGet(array, indices);
2041    }
2042
2043    public static Object JavaDoc getAt(double[] array, Collection indices) {
2044        return primitiveArrayGet(array, indices);
2045    }
2046
2047    public static void putAt(byte[] array, int idx, Object JavaDoc newValue) {
2048        primitiveArrayPut(array, idx, newValue);
2049    }
2050
2051    public static void putAt(char[] array, int idx, Object JavaDoc newValue) {
2052        primitiveArrayPut(array, idx, newValue);
2053    }
2054
2055    public static void putAt(short[] array, int idx, Object JavaDoc newValue) {
2056        primitiveArrayPut(array, idx, newValue);
2057    }
2058
2059    public static void putAt(int[] array, int idx, Object JavaDoc newValue) {
2060        primitiveArrayPut(array, idx, newValue);
2061    }
2062
2063    public static void putAt(long[] array, int idx, Object JavaDoc newValue) {
2064        primitiveArrayPut(array, idx, newValue);
2065    }
2066
2067    public static void putAt(float[] array, int idx, Object JavaDoc newValue) {
2068        primitiveArrayPut(array, idx, newValue);
2069    }
2070
2071    public static void putAt(double[] array, int idx, Object JavaDoc 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 JavaDoc[] 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 JavaDoc toString() {
2181                StringWriter buffer = new StringWriter();
2182
2183                try {
2184                    writeTo(buffer);
2185                } catch (IOException e) {
2186                    throw new RuntimeException JavaDoc(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 JavaDoc value) {
2235        int byteShift = 4;
2236        int tmp = 0;
2237        boolean done = false;
2238        final StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
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 JavaDoc("= 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 JavaDoc("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 JavaDoc("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 JavaDoc primitiveArrayGet(Object JavaDoc 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 JavaDoc 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 JavaDoc self, Collection indices) {
2298        List answer = new ArrayList();
2299        for (Iterator iter = indices.iterator(); iter.hasNext();) {
2300            Object JavaDoc 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 JavaDoc array, int idx, Object JavaDoc 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 JavaDoc toCharacter(String JavaDoc self) {
2331        /** @todo use cache? */
2332        return new Character JavaDoc(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 JavaDoc self, String JavaDoc 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 JavaDoc 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 JavaDoc plus(String JavaDoc left, Object JavaDoc 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 JavaDoc plus(Number JavaDoc value, String JavaDoc 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 JavaDoc plus(StringBuffer JavaDoc left, String JavaDoc 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 JavaDoc minus(String JavaDoc left, Object JavaDoc value) {
2399        String JavaDoc 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 JavaDoc self, String JavaDoc 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 JavaDoc self, String JavaDoc 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 JavaDoc next(String JavaDoc self) {
2445        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(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 JavaDoc previous(String JavaDoc self) {
2473        StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(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 JavaDoc execute(String JavaDoc 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 JavaDoc multiply(String JavaDoc self, Number JavaDoc factor) {
2548        int size = factor.intValue();
2549        if (size == 0)
2550            return "";
2551        else if (size < 0) {
2552            throw new IllegalArgumentException JavaDoc("multiply() should be called with a number of 0 or greater not: " + size);
2553        }
2554        StringBuffer JavaDoc answer = new StringBuffer JavaDoc(self);
2555        for (int i = 1; i < size; i++) {
2556            answer.append(self);
2557        }
2558        return answer.toString();
2559    }
2560
2561    protected static String JavaDoc toString(Object JavaDoc 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 JavaDoc next(Character JavaDoc 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 JavaDoc next(Number JavaDoc 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 JavaDoc previous(Character JavaDoc 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 JavaDoc previous(Number JavaDoc 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 JavaDoc plus(Character JavaDoc left, Number JavaDoc right) {
2616        return plus(new Integer JavaDoc(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 JavaDoc plus(Number JavaDoc left, Character JavaDoc right) {
2627        return plus(left, new Integer JavaDoc(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 JavaDoc plus(Character JavaDoc left, Character JavaDoc right) {
2638        return plus(new Integer JavaDoc(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 JavaDoc plus(Number JavaDoc left, Number JavaDoc 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 JavaDoc left, Number JavaDoc right) {
2660        return compareTo(new Integer JavaDoc(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 JavaDoc left, Character JavaDoc right) {
2671        return compareTo(left, new Integer JavaDoc(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 JavaDoc left, Character JavaDoc right) {
2682        return compareTo(new Integer JavaDoc(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 JavaDoc left, Number JavaDoc 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 JavaDoc minus(Character JavaDoc left, Number JavaDoc right) {
2705        return minus(new Integer JavaDoc(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 JavaDoc minus(Number JavaDoc left, Character JavaDoc right) {
2716        return minus(left, new Integer JavaDoc(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 JavaDoc minus(Character JavaDoc left, Character JavaDoc right) {
2727        return minus(new Integer JavaDoc(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 JavaDoc minus(Number JavaDoc left, Number JavaDoc 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 JavaDoc multiply(Character JavaDoc left, Number JavaDoc right) {
2749        return multiply(new Integer JavaDoc(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 JavaDoc multiply(Number JavaDoc left, Character JavaDoc right) {
2760        return multiply(left, new Integer JavaDoc(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 JavaDoc multiply(Character JavaDoc left, Character JavaDoc right) {
2771        return multiply(new Integer JavaDoc(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 JavaDoc multiply(Number JavaDoc left, Number JavaDoc 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 JavaDoc power(Number JavaDoc self, Number JavaDoc exponent) {
2795        double answer = Math.pow(self.doubleValue(), exponent.doubleValue());
2796        if (NumberMath.isFloatingPoint(self) || NumberMath.isFloatingPoint(exponent) || answer < 1) {
2797            return new Double JavaDoc(answer);
2798        } else if (NumberMath.isLong(self) || NumberMath.isLong(exponent) || answer > Integer.MAX_VALUE) {
2799            return new Long JavaDoc((long) answer);
2800        } else {
2801            return new Integer JavaDoc((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 JavaDoc div(Character JavaDoc left, Number JavaDoc right) {
2813        return div(new Integer JavaDoc(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 JavaDoc div(Number JavaDoc left, Character JavaDoc right) {
2824        return div(left, new Integer JavaDoc(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 JavaDoc div(Character JavaDoc left, Character JavaDoc right) {
2835        return div(new Integer JavaDoc(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 JavaDoc div(Number JavaDoc left, Number JavaDoc 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 JavaDoc intdiv(Character JavaDoc left, Number JavaDoc right) {
2859        return intdiv(new Integer JavaDoc(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 JavaDoc intdiv(Number JavaDoc left, Character JavaDoc right) {
2870        return intdiv(left, new Integer JavaDoc(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 JavaDoc intdiv(Character JavaDoc left, Character JavaDoc right) {
2881        return intdiv(new Integer JavaDoc(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 JavaDoc intdiv(Number JavaDoc left, Number JavaDoc 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 JavaDoc or(Number JavaDoc left, Number JavaDoc 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 JavaDoc and(Number JavaDoc left, Number JavaDoc 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 JavaDoc mod(Number JavaDoc left, Number JavaDoc 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 JavaDoc negate(Number JavaDoc 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 JavaDoc self, Closure closure) {
2946        for (int i = 0, size = self.intValue(); i < size; i++) {
2947            closure.call(new Integer JavaDoc(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 JavaDoc self, Number JavaDoc to, Closure closure) {
2962        for (int i = self.intValue(), size = to.intValue(); i <= size; i++) {
2963            closure.call(new Integer JavaDoc(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 JavaDoc self, Number JavaDoc to, Number JavaDoc stepNumber, Closure closure) {
2976        for (int i = self.intValue(), size = to.intValue(), step = stepNumber.intValue(); i < size; i += step) {
2977            closure.call(new Integer JavaDoc(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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc toInteger(String JavaDoc 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 JavaDoc toLong(String JavaDoc 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 JavaDoc toFloat(String JavaDoc 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 JavaDoc toDouble(String JavaDoc 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 JavaDoc toInteger(Number JavaDoc self) {
3090        return new Integer JavaDoc(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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc getText(File file, String JavaDoc 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 JavaDoc 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 JavaDoc getText(URL JavaDoc 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 JavaDoc getText(URL JavaDoc url, String JavaDoc 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 JavaDoc 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 JavaDoc getText(InputStream is, String JavaDoc 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 JavaDoc 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 JavaDoc getText(BufferedReader reader) throws IOException {
3382        StringBuffer JavaDoc answer = new StringBuffer JavaDoc();
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 JavaDoc 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 JavaDoc 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 JavaDoc text, String JavaDoc 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 JavaDoc 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 JavaDoc text, String JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc((byte) b));
3967                }
3968            }
3969            is.close();
3970        } catch (IOException e) {
3971            if (is != null) {
3972                try {
3973                    is.close();
3974                } catch (Exception JavaDoc 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 JavaDoc 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 JavaDoc) closure.call(new String JavaDoc(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 JavaDoc line;
4024        while ((line = br.readLine()) != null) {
4025            Object JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc toString() {
4104                StringWriter buffer = new StringWriter();
4105                try {
4106                    writeTo(buffer);
4107                } catch (IOException e) {
4108                    throw new RuntimeException JavaDoc(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 JavaDoc socket, Closure closure) throws IOException {
4169        InputStream input = socket.getInputStream();
4170        OutputStream output = socket.getOutputStream();
4171        try {
4172            closure.call(new Object JavaDoc[]{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 JavaDoc self, Object JavaDoc 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 JavaDoc 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 JavaDoc accept(ServerSocket JavaDoc serverSocket, final Closure closure) throws IOException {
4220        final Socket JavaDoc socket = serverSocket.accept();
4221        new Thread JavaDoc(new Runnable JavaDoc() {
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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc getText(Process JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc self, Object JavaDoc 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 JavaDoc 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 JavaDoc self, long numberOfMillis) {
4345        ProcessRunner runnable = new ProcessRunner(self);
4346        Thread JavaDoc thread = new Thread JavaDoc(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 JavaDoc str, String JavaDoc regex, Closure closure) {
4362        Pattern JavaDoc p = Pattern.compile(regex);
4363        Matcher JavaDoc 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 JavaDoc matcher, Closure closure) {
4375        Matcher JavaDoc 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 JavaDoc self, Closure closure) {
4395        int i = 0;
4396        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) {
4397            Object JavaDoc 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 JavaDoc {
4411        Process JavaDoc process;
4412        private boolean finished;
4413
4414        public ProcessRunner(Process JavaDoc process) {
4415            this.process = process;
4416        }
4417
4418        public void run() {
4419            try {
4420                process.waitFor();
4421            } catch (InterruptedException JavaDoc 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 JavaDoc e) {
4434                }
4435                if (!finished) {
4436                    process.destroy();
4437                }
4438            }
4439        }
4440    }
4441}
4442
Popular Tags