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