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