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