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