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