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