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