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 }