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