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