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