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