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