0001: /*
0002: * $Id: DefaultGroovyMethods.java 4598 2006-12-22 20:21:21Z blackdrag $
0003: *
0004: * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
0005: *
0006: * Redistribution and use of this software and associated documentation
0007: * ("Software"), with or without modification, are permitted provided that the
0008: * following conditions are met:
0009: * 1. Redistributions of source code must retain copyright statements and
0010: * notices. Redistributions must also contain a copy of this document.
0011: * 2. Redistributions in binary form must reproduce the above copyright
0012: * notice, this list of conditions and the following disclaimer in the
0013: * documentation and/or other materials provided with the distribution.
0014: * 3. The name "groovy" must not be used to endorse or promote products
0015: * derived from this Software without prior written permission of The Codehaus.
0016: * For written permission, please contact info@codehaus.org.
0017: * 4. Products derived from this Software may not be called "groovy" nor may
0018: * "groovy" appear in their names without prior written permission of The
0019: * Codehaus. "groovy" is a registered trademark of The Codehaus.
0020: * 5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
0021: *
0022: * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
0023: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0024: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0025: * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
0026: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0027: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
0028: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
0029: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0030: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0031: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
0032: * DAMAGE.
0033: *
0034: */
0035: package org.codehaus.groovy.runtime;
0036:
0037: import groovy.lang.*;
0038: import groovy.util.CharsetToolkit;
0039: import groovy.util.ClosureComparator;
0040: import groovy.util.OrderBy;
0041:
0042: import java.io.*;
0043: import java.lang.reflect.Array;
0044: import java.lang.reflect.Field;
0045: import java.lang.reflect.Modifier;
0046: import java.lang.reflect.Proxy;
0047: import java.math.BigDecimal;
0048: import java.math.BigInteger;
0049: import java.net.MalformedURLException;
0050: import java.net.ServerSocket;
0051: import java.net.Socket;
0052: import java.net.URI;
0053: import java.net.URISyntaxException;
0054: import java.net.URL;
0055: import java.security.AccessController;
0056: import java.security.PrivilegedAction;
0057: import java.util.*;
0058: import java.util.logging.Logger;
0059: import java.util.regex.Matcher;
0060: import java.util.regex.Pattern;
0061:
0062: import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
0063: import org.codehaus.groovy.runtime.typehandling.NumberMath;
0064: import org.codehaus.groovy.tools.RootLoader;
0065: import org.w3c.dom.NodeList;
0066:
0067: /**
0068: * This class defines all the new groovy methods which appear on normal JDK
0069: * classes inside the Groovy environment. Static methods are used with the
0070: * first parameter the destination class.
0071: *
0072: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
0073: * @author Jeremy Rayner
0074: * @author Sam Pullara
0075: * @author Rod Cope
0076: * @author Guillaume Laforge
0077: * @author John Wilson
0078: * @author Hein Meling
0079: * @author Dierk Koenig
0080: * @author Pilho Kim
0081: * @author Marc Guillemot
0082: * @author Russel Winder
0083: * @author bing ran
0084: * @author Jochen Theodorou
0085: * @version $Revision: 4598 $
0086: */
0087: public class DefaultGroovyMethods {
0088:
0089: private static final Logger LOG = Logger
0090: .getLogger(DefaultGroovyMethods.class.getName());
0091: private static final Integer ONE = new Integer(1);
0092:
0093: /**
0094: * Identity check. Since == is overridden in Groovy with the meaning of equality
0095: * we need some fallback to check for object identity.
0096: *
0097: * @param self
0098: * @param other
0099: * @return true if self and other are identical, false otherwise
0100: */
0101: public static boolean is(Object self, Object other) {
0102: return self == other;
0103: }
0104:
0105: /**
0106: * Allows the closure to be called for the object reference self
0107: *
0108: * @param self the object to have a closure act upon
0109: * @param closure the closure to call on the object
0110: * @return result of calling the closure
0111: */
0112: public static Object identity(Object self, Closure closure) {
0113: final Closure clonedClosure = (Closure) closure.clone();
0114: clonedClosure.setDelegate(self);
0115: return clonedClosure.call(self);
0116: }
0117:
0118: /**
0119: * Allows the subscript operator to be used to lookup dynamic property values.
0120: * <code>bean[somePropertyNameExpression]</code>. The normal property notation
0121: * of groovy is neater and more concise but only works with compile-time known
0122: * property names.
0123: *
0124: * @param self the object to act upon
0125: */
0126: public static Object getAt(Object self, String property) {
0127: return InvokerHelper.getProperty(self, property);
0128: }
0129:
0130: /**
0131: * Allows the subscript operator to be used to set dynamically named property values.
0132: * <code>bean[somePropertyNameExpression] = foo</code>. The normal property notation
0133: * of groovy is neater and more concise but only works with property names which
0134: * are known at compile time.
0135: *
0136: * @param self the object to act upon
0137: * @param property the name of the property to set
0138: * @param newValue the value to set
0139: */
0140: public static void putAt(Object self, String property,
0141: Object newValue) {
0142: InvokerHelper.setProperty(self, property, newValue);
0143: }
0144:
0145: /**
0146: * Generates a detailed dump string of an object showing its class,
0147: * hashCode and fields
0148: */
0149: public static String dump(Object self) {
0150: if (self == null) {
0151: return "null";
0152: }
0153: StringBuffer buffer = new StringBuffer("<");
0154: Class klass = self.getClass();
0155: buffer.append(klass.getName());
0156: buffer.append("@");
0157: buffer.append(Integer.toHexString(self.hashCode()));
0158: boolean groovyObject = self instanceof GroovyObject;
0159:
0160: /*jes this may be rewritten to use the new getProperties() stuff
0161: * but the original pulls out private variables, whereas getProperties()
0162: * does not. What's the real use of dump() here?
0163: */
0164: while (klass != null) {
0165: Field[] fields = klass.getDeclaredFields();
0166: for (int i = 0; i < fields.length; i++) {
0167: final Field field = fields[i];
0168: if ((field.getModifiers() & Modifier.STATIC) == 0) {
0169: if (groovyObject
0170: && field.getName().equals("metaClass")) {
0171: continue;
0172: }
0173: AccessController
0174: .doPrivileged(new PrivilegedAction() {
0175: public Object run() {
0176: field.setAccessible(true);
0177: return null;
0178: }
0179: });
0180: buffer.append(" ");
0181: buffer.append(field.getName());
0182: buffer.append("=");
0183: try {
0184: buffer.append(InvokerHelper.toString(field
0185: .get(self)));
0186: } catch (Exception e) {
0187: buffer.append(e);
0188: }
0189: }
0190: }
0191:
0192: klass = klass.getSuperclass();
0193: }
0194:
0195: /* here is a different implementation that uses getProperties(). I have left
0196: * it commented out because it returns a slightly different list of properties;
0197: * ie it does not return privates. I don't know what dump() really should be doing,
0198: * although IMO showing private fields is a no-no
0199: */
0200: /*
0201: List props = getProperties(self);
0202: for(Iterator itr = props.keySet().iterator(); itr.hasNext(); ) {
0203: String propName = itr.next().toString();
0204:
0205: // the original skipped this, so I will too
0206: if(pv.getName().equals("metaClass")) continue;
0207: if(pv.getName().equals("class")) continue;
0208:
0209: buffer.append(" ");
0210: buffer.append(propName);
0211: buffer.append("=");
0212: try {
0213: buffer.append(InvokerHelper.toString(props.get(propName)));
0214: }
0215: catch (Exception e) {
0216: buffer.append(e);
0217: }
0218: }
0219: */
0220:
0221: buffer.append(">");
0222: return buffer.toString();
0223: }
0224:
0225: /**
0226: * Retrieves the list of {@link MetaProperty} objects for 'self' and wraps it
0227: * in a list of {@link PropertyValue} objects that additionally provide
0228: * the value for each property of 'self'.
0229: *
0230: * @param self the receiver object
0231: * @return list of {@link PropertyValue} objects
0232: * @see groovy.util.Expando#getMetaPropertyValues()
0233: */
0234: public static List getMetaPropertyValues(Object self) {
0235: MetaClass metaClass = InvokerHelper.getMetaClass(self);
0236: List mps = metaClass.getProperties();
0237: List props = new ArrayList(mps.size());
0238: for (Iterator itr = mps.iterator(); itr.hasNext();) {
0239: MetaProperty mp = (MetaProperty) itr.next();
0240: PropertyValue pv = new PropertyValue(self, mp);
0241: props.add(pv);
0242: }
0243: return props;
0244: }
0245:
0246: /**
0247: * Convenience method that calls {@link #getMetaPropertyValues(Object)}(self)
0248: * and provides the data in form of simple key/value pairs, i.e. without
0249: * type() information.
0250: *
0251: * @param self the receiver object
0252: * @return meta properties as Map of key/value pairs
0253: */
0254: public static Map getProperties(Object self) {
0255: List metaProps = getMetaPropertyValues(self);
0256: Map props = new HashMap(metaProps.size());
0257:
0258: for (Iterator itr = metaProps.iterator(); itr.hasNext();) {
0259: PropertyValue pv = (PropertyValue) itr.next();
0260: try {
0261: props.put(pv.getName(), pv.getValue());
0262: } catch (Exception e) {
0263: LOG.throwing(self.getClass().getName(), "getProperty("
0264: + pv.getName() + ")", e);
0265: }
0266: }
0267: return props;
0268: }
0269:
0270: /**
0271: * Scoped use method
0272: */
0273: public static void use(Object self, Class categoryClass,
0274: Closure closure) {
0275: GroovyCategorySupport.use(categoryClass, closure);
0276: }
0277:
0278: /**
0279: * Scoped use method with list of categories
0280: */
0281: public static void use(Object self, List categoryClassList,
0282: Closure closure) {
0283: GroovyCategorySupport.use(categoryClassList, closure);
0284: }
0285:
0286: /**
0287: * use() a list of categories, specifying the list as varargs:<br>
0288: * use(CategoryClass1, CategoryClass2) { ... }<br>
0289: * This method prevents the error of forgetting to wrap the the category
0290: * classes in a list.
0291: *
0292: * @param self
0293: * @param array
0294: */
0295: public static void use(Object self, Object[] array) {
0296: if (array.length < 2)
0297: throw new IllegalArgumentException(
0298: "Expecting at least 2 arguments, a category class and a Closure");
0299: Closure closure;
0300: try {
0301: closure = (Closure) array[array.length - 1];
0302: } catch (ClassCastException e) {
0303: throw new IllegalArgumentException(
0304: "Expecting a Closure to be the last argument");
0305: }
0306: List list = new ArrayList(array.length - 1);
0307: for (int i = 0; i < array.length - 1; ++i)
0308: list.add(array[i]);
0309: GroovyCategorySupport.use(list, closure);
0310: }
0311:
0312: /**
0313: * Print to a console in interactive format
0314: */
0315: public static void print(Object self, Object value) {
0316: System.out.print(InvokerHelper.toString(value));
0317: }
0318:
0319: /**
0320: * Print a linebreak to the standard out.
0321: */
0322: public static void println(Object self) {
0323: System.out.println();
0324: }
0325:
0326: /**
0327: * Print to a console in interactive format along with a newline
0328: */
0329: public static void println(Object self, Object value) {
0330: System.out.println(InvokerHelper.toString(value));
0331: }
0332:
0333: /**
0334: * Printf to a console. Only works with JDK1.5 or later.
0335: */
0336: public static void printf(Object self, String format,
0337: Object[] values) {
0338: if (System.getProperty("java.version").charAt(2) == '5') {
0339: //
0340: // Cannot just do:
0341: //
0342: // System.out.printf(format, values) ;
0343: //
0344: // because this fails to compile on JDK1.4.x and earlier. So until the entire world is using
0345: // JDK1.5 or later then we have to do things by reflection so as to hide the use of printf
0346: // from the compiler. In JDK1.5 you might try:
0347: //
0348: // System.out.getClass().getMethod("printf", String.class, Object[].class).invoke(System.out, format, values) ;
0349: //
0350: // but of course this doesn't work on JDK1.4 as it relies on varargs. argh. So we are
0351: // forced into:
0352: //
0353: try {
0354: System.out.getClass().getMethod("printf",
0355: new Class[] { String.class, Object[].class })
0356: .invoke(System.out,
0357: new Object[] { format, values });
0358: } catch (NoSuchMethodException nsme) {
0359: throw new RuntimeException(
0360: "getMethod threw a NoSuchMethodException. This is impossible.");
0361: } catch (IllegalAccessException iae) {
0362: throw new RuntimeException(
0363: "invoke threw a IllegalAccessException. This is impossible.");
0364: } catch (java.lang.reflect.InvocationTargetException ite) {
0365: throw new RuntimeException(
0366: "invoke threw a InvocationTargetException. This is impossible.");
0367: }
0368: } else {
0369: throw new RuntimeException(
0370: "printf requires JDK1.5 or later.");
0371: }
0372: }
0373:
0374: /**
0375: * Returns a formatted string using the specified format string and
0376: * arguments.
0377: * <p/>
0378: * <p/>
0379: * For examples, <pre>
0380: * printf ( "Hello, %s!\n" , [ "world" ] as String[] )
0381: * printf ( "Hello, %s!\n" , [ "Groovy" ])
0382: * printf ( "%d + %d = %d\n" , [ 1 , 2 , 1+2 ] as Integer[] )
0383: * printf ( "%d + %d = %d\n" , [ 3 , 3 , 3+3 ])
0384: * <p/>
0385: * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as Integer[] ) }
0386: * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as int[] ) }
0387: * ( 0x41..0x45 ).each { printf ( "-- %c\n" , [ it ] as char[] ) }
0388: * ( 07..011 ).each { printf ( "-- %d\n" , [ it ] as byte[] ) }
0389: * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as short[] ) }
0390: * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as long[] ) }
0391: * ( 7..11 ).each { printf ( "-- %5.2f\n" , [ it ] as float[] ) }
0392: * ( 7..11 ).each { printf ( "-- %5.2g\n" , [ it ] as double[] ) }
0393: * </pre>
0394: * <p/>
0395: *
0396: * @param format A format string
0397: * @param arg Argument which is referenced by the format specifiers in the format
0398: * string. The type of <code>arg</code> should be one of Object[], List,
0399: * int[], short[], byte[], char[], boolean[], long[], float[], or double[].
0400: * @since JDK 1.5
0401: */
0402: public static void printf(Object self, String format, Object arg) {
0403: if (arg instanceof Object[]) {
0404: printf(self, format, (Object[]) arg);
0405: return;
0406: } else if (arg instanceof List) {
0407: printf(self, format, ((List) arg).toArray());
0408: return;
0409: } else if (!arg.getClass().isArray()) {
0410: Object[] o = (Object[]) java.lang.reflect.Array
0411: .newInstance(arg.getClass(), 1);
0412: o[0] = arg;
0413: printf(self, format, o);
0414: return;
0415: }
0416:
0417: Object[] ans = null;
0418: String elemType = arg.getClass().getName();
0419: if (elemType.equals("[I")) {
0420: int[] ia = (int[]) arg;
0421: ans = new Integer[ia.length];
0422: for (int i = 0; i < ia.length; i++) {
0423: ans[i] = new Integer(ia[i]);
0424: }
0425: } else if (elemType.equals("[C")) {
0426: char[] ia = (char[]) arg;
0427: ans = new Character[ia.length];
0428: for (int i = 0; i < ia.length; i++) {
0429: ans[i] = new Character(ia[i]);
0430: }
0431: } else if (elemType.equals("[Z")) {
0432: boolean[] ia = (boolean[]) arg;
0433: ans = new Boolean[ia.length];
0434: for (int i = 0; i < ia.length; i++) {
0435: ans[i] = new Boolean(ia[i]);
0436: }
0437: } else if (elemType.equals("[B")) {
0438: byte[] ia = (byte[]) arg;
0439: ans = new Byte[ia.length];
0440: for (int i = 0; i < ia.length; i++) {
0441: ans[i] = new Byte(ia[i]);
0442: }
0443: } else if (elemType.equals("[S")) {
0444: short[] ia = (short[]) arg;
0445: ans = new Short[ia.length];
0446: for (int i = 0; i < ia.length; i++) {
0447: ans[i] = new Short(ia[i]);
0448: }
0449: } else if (elemType.equals("[F")) {
0450: float[] ia = (float[]) arg;
0451: ans = new Float[ia.length];
0452: for (int i = 0; i < ia.length; i++) {
0453: ans[i] = new Float(ia[i]);
0454: }
0455: } else if (elemType.equals("[J")) {
0456: long[] ia = (long[]) arg;
0457: ans = new Long[ia.length];
0458: for (int i = 0; i < ia.length; i++) {
0459: ans[i] = new Long(ia[i]);
0460: }
0461: } else if (elemType.equals("[D")) {
0462: double[] ia = (double[]) arg;
0463: ans = new Double[ia.length];
0464: for (int i = 0; i < ia.length; i++) {
0465: ans[i] = new Double(ia[i]);
0466: }
0467: } else {
0468: throw new RuntimeException("printf(String," + arg + ")");
0469: }
0470: printf(self, format, (Object[]) ans);
0471: }
0472:
0473: /**
0474: * @return a String that matches what would be typed into a terminal to
0475: * create this object. e.g. [1, 'hello'].inspect() -> [1, "hello"]
0476: */
0477: public static String inspect(Object self) {
0478: return InvokerHelper.inspect(self);
0479: }
0480:
0481: /**
0482: * Print to a console in interactive format
0483: */
0484: public static void print(Object self, PrintWriter out) {
0485: if (out == null) {
0486: out = new PrintWriter(System.out);
0487: }
0488: out.print(InvokerHelper.toString(self));
0489: }
0490:
0491: /**
0492: * Print to a console in interactive format
0493: *
0494: * @param out the PrintWriter used for printing
0495: */
0496: public static void println(Object self, PrintWriter out) {
0497: if (out == null) {
0498: out = new PrintWriter(System.out);
0499: }
0500: InvokerHelper.invokeMethod(self, "print", out);
0501: out.println();
0502: }
0503:
0504: /**
0505: * Provide a dynamic method invocation method which can be overloaded in
0506: * classes to implement dynamic proxies easily.
0507: */
0508: public static Object invokeMethod(Object object, String method,
0509: Object arguments) {
0510: return InvokerHelper.invokeMethod(object, method, arguments);
0511: }
0512:
0513: // isCase methods
0514: //-------------------------------------------------------------------------
0515: public static boolean isCase(Object caseValue, Object switchValue) {
0516: return caseValue.equals(switchValue);
0517: }
0518:
0519: public static boolean isCase(String caseValue, Object switchValue) {
0520: if (switchValue == null) {
0521: return caseValue == null;
0522: }
0523: return caseValue.equals(switchValue.toString());
0524: }
0525:
0526: public static boolean isCase(Class caseValue, Object switchValue) {
0527: if (switchValue instanceof Class) {
0528: Class val = (Class) switchValue;
0529: return caseValue.isAssignableFrom(val);
0530: }
0531: return caseValue.isInstance(switchValue);
0532: }
0533:
0534: public static boolean isCase(Collection caseValue,
0535: Object switchValue) {
0536: return caseValue.contains(switchValue);
0537: }
0538:
0539: public static boolean isCase(Pattern caseValue, Object switchValue) {
0540: if (switchValue == null) {
0541: return caseValue == null;
0542: }
0543: final Matcher matcher = caseValue.matcher(switchValue
0544: .toString());
0545: if (matcher.matches()) {
0546: RegexSupport.setLastMatcher(matcher);
0547: return true;
0548: } else {
0549: return false;
0550: }
0551: }
0552:
0553: // Collection based methods
0554: //-------------------------------------------------------------------------
0555:
0556: public static Collection unique(Collection self) {
0557: if (self instanceof Set)
0558: return self;
0559: List answer = new ArrayList();
0560: NumberComparator comparator = new NumberComparator();
0561: for (Iterator it = self.iterator(); it.hasNext();) {
0562: Object o = it.next();
0563: boolean duplicated = false;
0564: for (Iterator it2 = answer.iterator(); it2.hasNext();) {
0565: Object o2 = it2.next();
0566: if (comparator.compare(o, o2) == 0) {
0567: duplicated = true;
0568: break;
0569: }
0570: }
0571: if (!duplicated)
0572: answer.add(o);
0573: }
0574: self.clear();
0575: self.addAll(answer);
0576: return self;
0577: }
0578:
0579: /**
0580: * A convenience method for making a collection unique using a closure as a comparator
0581: * (by Michael Baehr)
0582: *
0583: * @param self a Collection
0584: * @param closure a Closure used as a comparator
0585: * @return self without any duplicates
0586: */
0587: public static Collection unique(Collection self, Closure closure) {
0588: if (self instanceof Set)
0589: return self;
0590: // use a comparator of one item or two
0591: int params = closure.getMaximumNumberOfParameters();
0592: if (params == 1) {
0593: unique(self, new OrderBy(closure));
0594: } else {
0595: unique(self, new ClosureComparator(closure));
0596: }
0597: return self;
0598: }
0599:
0600: /**
0601: * Remove all duplicates from a given Collection.
0602: * Works on the receiver object and returns it.
0603: * The order of members in the Collection are compared by the given Comparator.
0604: * For each duplicate, the first member which is returned
0605: * by the given Collection's iterator is retained, but all other ones are removed.
0606: * The given Collection's original order is preserved.
0607: * <p/>
0608: * <code><pre>
0609: * class Person {
0610: * @Property fname, lname
0611: * public String toString() {
0612: * return fname + " " + lname
0613: * }
0614: * }
0615: * <p/>
0616: * class PersonComparator implements Comparator {
0617: * public int compare(Object o1, Object o2) {
0618: * Person p1 = (Person) o1
0619: * Person p2 = (Person) o2
0620: * if (p1.lname != p2.lname)
0621: * return p1.lname.compareTo(p2.lname)
0622: * else
0623: * return p1.fname.compareTo(p2.fname)
0624: * }
0625: * <p/>
0626: * public boolean equals(Object obj) {
0627: * return this.equals(obj)
0628: * }
0629: * }
0630: * <p/>
0631: * Person a = new Person(fname:"John", lname:"Taylor")
0632: * Person b = new Person(fname:"Clark", lname:"Taylor")
0633: * Person c = new Person(fname:"Tom", lname:"Cruz")
0634: * Person d = new Person(fname:"Clark", lname:"Taylor")
0635: * <p/>
0636: * def list = [a, b, c, d]
0637: * List list2 = list.unique(new PersonComparator())
0638: * assert( list2 == list && list == [a, b, c] )
0639: * <p/>
0640: * </pre></code>
0641: *
0642: * @param self a Collection
0643: * @param comparator a Comparator.
0644: * @return self without duplicates
0645: */
0646: public static Collection unique(Collection self,
0647: Comparator comparator) {
0648: if (self instanceof Set)
0649: return self;
0650: List answer = new ArrayList();
0651: for (Iterator it = self.iterator(); it.hasNext();) {
0652: Object o = it.next();
0653: boolean duplicated = false;
0654: for (Iterator it2 = answer.iterator(); it2.hasNext();) {
0655: Object o2 = it2.next();
0656: if (comparator.compare(o, o2) == 0) {
0657: duplicated = true;
0658: break;
0659: }
0660: }
0661: if (!duplicated)
0662: answer.add(o);
0663: }
0664: self.clear();
0665: self.addAll(answer);
0666: return self;
0667: }
0668:
0669: /**
0670: * Allows objects to be iterated through using a closure
0671: *
0672: * @param self the object over which we iterate
0673: * @param closure the closure applied on each element found
0674: */
0675: public static void each(Object self, Closure closure) {
0676: for (Iterator iter = InvokerHelper.asIterator(self); iter
0677: .hasNext();) {
0678: closure.call(iter.next());
0679: }
0680: }
0681:
0682: /**
0683: * Allows object to be iterated through a closure with a counter
0684: *
0685: * @param self an Object
0686: * @param closure a Closure
0687: */
0688: public static void eachWithIndex(Object self, Closure closure) {
0689: int counter = 0;
0690: for (Iterator iter = InvokerHelper.asIterator(self); iter
0691: .hasNext();) {
0692: closure.call(new Object[] { iter.next(),
0693: new Integer(counter++) });
0694: }
0695: }
0696:
0697: /**
0698: * Allows objects to be iterated through using a closure
0699: *
0700: * @param self the collection over which we iterate
0701: * @param closure the closure applied on each element of the collection
0702: */
0703: public static void each(Collection self, Closure closure) {
0704: for (Iterator iter = self.iterator(); iter.hasNext();) {
0705: closure.call(iter.next());
0706: }
0707: }
0708:
0709: /**
0710: * Allows a Map to be iterated through using a closure. If the
0711: * closure takes one parameter then it will be passed the Map.Entry
0712: * otherwise if the closure takes two parameters then it will be
0713: * passed the key and the value.
0714: *
0715: * @param self the map over which we iterate
0716: * @param closure the closure applied on each entry of the map
0717: */
0718: public static void each(Map self, Closure closure) {
0719: for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
0720: Map.Entry entry = (Map.Entry) iter.next();
0721: callClosureForMapEntry(closure, entry);
0722: }
0723: }
0724:
0725: /**
0726: * Iterates over every element of a collection, and check whether a predicate is valid for all elements.
0727: *
0728: * @param self the object over which we iterate
0729: * @param closure the closure predicate used for matching
0730: * @return true if every item in the collection matches the closure
0731: * predicate
0732: */
0733: public static boolean every(Object self, Closure closure) {
0734: for (Iterator iter = InvokerHelper.asIterator(self); iter
0735: .hasNext();) {
0736: if (!DefaultTypeTransformation.castToBoolean(closure
0737: .call(iter.next()))) {
0738: return false;
0739: }
0740: }
0741: return true;
0742: }
0743:
0744: /**
0745: * Iterates over every element of a collection, and check whether a predicate is valid for at least one element
0746: *
0747: * @param self the object over which we iterate
0748: * @param closure the closure predicate used for matching
0749: * @return true if any item in the collection matches the closure predicate
0750: */
0751: public static boolean any(Object self, Closure closure) {
0752: for (Iterator iter = InvokerHelper.asIterator(self); iter
0753: .hasNext();) {
0754: if (DefaultTypeTransformation.castToBoolean(closure
0755: .call(iter.next()))) {
0756: return true;
0757: }
0758: }
0759: return false;
0760: }
0761:
0762: /**
0763: * Iterates over every element of the collection and return each object that matches
0764: * the given filter - calling the isCase() method used by switch statements.
0765: * This method can be used with different kinds of filters like regular expresions, classes, ranges etc.
0766: *
0767: * @param self the object over which we iterate
0768: * @param filter the filter to perform on the collection (using the isCase(object) method)
0769: * @return a list of objects which match the filter
0770: */
0771: public static List grep(Object self, Object filter) {
0772: List answer = new ArrayList();
0773: MetaClass metaClass = InvokerHelper.getMetaClass(filter);
0774: for (Iterator iter = InvokerHelper.asIterator(self); iter
0775: .hasNext();) {
0776: Object object = iter.next();
0777: if (DefaultTypeTransformation.castToBoolean(metaClass
0778: .invokeMethod(filter, "isCase", object))) {
0779: answer.add(object);
0780: }
0781: }
0782: return answer;
0783: }
0784:
0785: /**
0786: * Counts the number of occurencies of the given value inside this collection
0787: *
0788: * @param self the collection within which we count the number of occurencies
0789: * @param value the value
0790: * @return the number of occurrencies
0791: */
0792: public static int count(Collection self, Object value) {
0793: int answer = 0;
0794: for (Iterator iter = self.iterator(); iter.hasNext();) {
0795: if (DefaultTypeTransformation.compareEqual(iter.next(),
0796: value)) {
0797: ++answer;
0798: }
0799: }
0800: return answer;
0801: }
0802:
0803: /**
0804: * Convert a collection to a List.
0805: *
0806: * @param self a collection
0807: * @return a List
0808: */
0809: public static List toList(Collection self) {
0810: List answer = new ArrayList(self.size());
0811: answer.addAll(self);
0812: return answer;
0813: }
0814:
0815: /**
0816: * Iterates through this object transforming each object into a new value using the closure
0817: * as a transformer, returning a list of transformed values.
0818: *
0819: * @param self the values of the object to map
0820: * @param closure the closure used to map each element of the collection
0821: * @return a List of the mapped values
0822: */
0823: public static List collect(Object self, Closure closure) {
0824: return (List) collect(self, new ArrayList(), closure);
0825: }
0826:
0827: /**
0828: * Iterates through this object transforming each object into a new value using the closure
0829: * as a transformer and adding it to the collection, returning the resulting collection.
0830: *
0831: * @param self the values of the object to map
0832: * @param collection the Collection to which the mapped values are added
0833: * @param closure the closure used to map each element of the collection
0834: * @return the resultant collection
0835: */
0836: public static Collection collect(Object self,
0837: Collection collection, Closure closure) {
0838: for (Iterator iter = InvokerHelper.asIterator(self); iter
0839: .hasNext();) {
0840: collection.add(closure.call(iter.next()));
0841: }
0842: return collection;
0843: }
0844:
0845: /**
0846: * Iterates through this collection transforming each entry into a new value using the closure
0847: * as a transformer, returning a list of transformed values.
0848: *
0849: * @param self a collection
0850: * @param closure the closure used for mapping
0851: * @return a List of the mapped values
0852: */
0853: public static List collect(Collection self, Closure closure) {
0854: return (List) collect(self, new ArrayList(self.size()), closure);
0855: }
0856:
0857: /**
0858: * Iterates through this collection transforming each entry into a new value using the closure
0859: * as a transformer, returning a list of transformed values.
0860: *
0861: * @param self a collection
0862: * @param collection the Collection to which the mapped values are added
0863: * @param closure the closure used to map each element of the collection
0864: * @return the resultant collection
0865: */
0866: public static Collection collect(Collection self,
0867: Collection collection, Closure closure) {
0868: for (Iterator iter = self.iterator(); iter.hasNext();) {
0869: collection.add(closure.call(iter.next()));
0870: if (closure.getDirective() == Closure.DONE) {
0871: break;
0872: }
0873: }
0874: return collection;
0875: }
0876:
0877: /**
0878: * Iterates through this Map transforming each entry into a new value using the closure
0879: * as a transformer, returning a list of transformed values.
0880: *
0881: * @param self a Map
0882: * @param collection the Collection to which the mapped values are added
0883: * @param closure the closure used for mapping, which can be with one(Map.Entry) or two(key, value) parameters
0884: * @return a List of the mapped values
0885: */
0886: public static Collection collect(Map self, Collection collection,
0887: Closure closure) {
0888: boolean isTwoParams = (closure.getParameterTypes().length == 2);
0889: for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
0890: if (isTwoParams) {
0891: Map.Entry entry = (Map.Entry) iter.next();
0892: collection.add(closure.call(new Object[] {
0893: entry.getKey(), entry.getValue() }));
0894: } else {
0895: collection.add(closure.call(iter.next()));
0896: }
0897: }
0898: return collection;
0899: }
0900:
0901: /**
0902: * Iterates through this Map transforming each entry into a new value using the closure
0903: * as a transformer, returning a list of transformed values.
0904: *
0905: * @param self a Map
0906: * @param closure the closure used to map each element of the collection
0907: * @return the resultant collection
0908: */
0909: public static List collect(Map self, Closure closure) {
0910: return (List) collect(self, new ArrayList(self.size()), closure);
0911: }
0912:
0913: /**
0914: * Finds the first value matching the closure condition
0915: *
0916: * @param self an Object with an iterator returning its values
0917: * @param closure a closure condition
0918: * @return the first Object found
0919: */
0920: public static Object find(Object self, Closure closure) {
0921: for (Iterator iter = InvokerHelper.asIterator(self); iter
0922: .hasNext();) {
0923: Object value = iter.next();
0924: if (DefaultTypeTransformation.castToBoolean(closure
0925: .call(value))) {
0926: return value;
0927: }
0928: }
0929: return null;
0930: }
0931:
0932: /**
0933: * Finds the first value matching the closure condition
0934: *
0935: * @param self a Collection
0936: * @param closure a closure condition
0937: * @return the first Object found
0938: */
0939: public static Object find(Collection self, Closure closure) {
0940: for (Iterator iter = self.iterator(); iter.hasNext();) {
0941: Object value = iter.next();
0942: if (DefaultTypeTransformation.castToBoolean(closure
0943: .call(value))) {
0944: return value;
0945: }
0946: }
0947: return null;
0948: }
0949:
0950: /**
0951: * Finds the first value matching the closure condition
0952: *
0953: * @param self a Map
0954: * @param closure a closure condition
0955: * @return the first Object found
0956: */
0957: public static Object find(Map self, Closure closure) {
0958: for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
0959: Object value = iter.next();
0960: if (DefaultTypeTransformation.castToBoolean(closure
0961: .call(value))) {
0962: return value;
0963: }
0964: }
0965: return null;
0966: }
0967:
0968: /**
0969: * Finds all values matching the closure condition
0970: *
0971: * @param self an Object with an Iterator returning its values
0972: * @param closure a closure condition
0973: * @return a List of the values found
0974: */
0975: public static List findAll(Object self, Closure closure) {
0976: List answer = new ArrayList();
0977: for (Iterator iter = InvokerHelper.asIterator(self); iter
0978: .hasNext();) {
0979: Object value = iter.next();
0980: if (DefaultTypeTransformation.castToBoolean(closure
0981: .call(value))) {
0982: answer.add(value);
0983: }
0984: }
0985: return answer;
0986: }
0987:
0988: /**
0989: * Finds all values matching the closure condition
0990: *
0991: * @param self a Collection
0992: * @param closure a closure condition
0993: * @return a List of the values found
0994: */
0995: public static List findAll(Collection self, Closure closure) {
0996: List answer = new ArrayList(self.size());
0997: for (Iterator iter = self.iterator(); iter.hasNext();) {
0998: Object value = iter.next();
0999: if (DefaultTypeTransformation.castToBoolean(closure
1000: .call(value))) {
1001: answer.add(value);
1002: }
1003: }
1004: return answer;
1005: }
1006:
1007: /**
1008: * Finds all entries matching the closure condition. If the
1009: * closure takes one parameter then it will be passed the Map.Entry
1010: * otherwise if the closure takes two parameters then it will be
1011: * passed the key and the value.
1012: *
1013: * @param self a Map
1014: * @param closure a closure condition applying on the entries
1015: * @return a new subMap
1016: */
1017: public static Map findAll(Map self, Closure closure) {
1018: Map answer = new HashMap(self.size());
1019: for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
1020: Map.Entry entry = (Map.Entry) iter.next();
1021: if (DefaultTypeTransformation
1022: .castToBoolean(callClosureForMapEntry(closure,
1023: entry))) {
1024: answer.put(entry.getKey(), entry.getValue());
1025: }
1026: }
1027: return answer;
1028: }
1029:
1030: /**
1031: * Groups all collection members into groups determined by the
1032: * supplied mapping closure.
1033: *
1034: * @param self a collection to group (no map)
1035: * @param closure a closure mapping entries on keys
1036: * @return a new Map grouped by keys
1037: */
1038: public static Map groupBy(Collection self, Closure closure) {
1039: Map answer = new HashMap();
1040: for (Iterator iter = self.iterator(); iter.hasNext();) {
1041: groupCurrentElement(closure, answer, iter);
1042: }
1043: return answer;
1044: }
1045:
1046: /**
1047: * Groups all map members into groups determined by the
1048: * supplied mapping closure.
1049: *
1050: * @param self a map to group
1051: * @param closure a closure mapping entries on keys
1052: * @return a new Map grouped by keys
1053: */
1054: /* Removed for 1.0, to be discussed for 1.1
1055: public static Map groupBy(Map self, Closure closure) {
1056: final Map answer = new HashMap();
1057: for (final Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
1058: groupCurrentElement(closure, answer, iter);
1059: }
1060: return answer;
1061: }
1062: */
1063:
1064: /**
1065: * Groups the current element of the iterator as determined
1066: * by the mapping closure.
1067: *
1068: * @param closure a closure mapping the current entry on a key
1069: * @param answer the map containing the results
1070: * @param iter the iterator from which the current element stems
1071: */
1072: private static void groupCurrentElement(Closure closure,
1073: Map answer, Iterator iter) {
1074: Object element = iter.next();
1075: Object value = closure.call(element);
1076: if (answer.containsKey(value)) {
1077: ((List) answer.get(value)).add(element);
1078: } else {
1079: ArrayList groupedElements = new ArrayList();
1080: groupedElements.add(element);
1081: answer.put(value, groupedElements);
1082: }
1083: }
1084:
1085: // internal helper method
1086: protected static Object callClosureForMapEntry(Closure closure,
1087: Map.Entry entry) {
1088: if (closure.getMaximumNumberOfParameters() == 2) {
1089: return closure.call(new Object[] { entry.getKey(),
1090: entry.getValue() });
1091: }
1092: return closure.call(entry);
1093: }
1094:
1095: /**
1096: * Iterates through the given collection, passing in the initial value to
1097: * the closure along with the current iterated item then passing into the
1098: * next iteration the value of the previous closure.
1099: *
1100: * @param self a Collection
1101: * @param value a value
1102: * @param closure a closure
1103: * @return the last value of the last iteration
1104: */
1105: public static Object inject(Collection self, Object value,
1106: Closure closure) {
1107: Object[] params = new Object[2];
1108: for (Iterator iter = self.iterator(); iter.hasNext();) {
1109: Object item = iter.next();
1110: params[0] = value;
1111: params[1] = item;
1112: value = closure.call(params);
1113: }
1114: return value;
1115: }
1116:
1117: /**
1118: * Iterates through the given array of objects, passing in the initial value to
1119: * the closure along with the current iterated item then passing into the
1120: * next iteration the value of the previous closure.
1121: *
1122: * @param self an Object[]
1123: * @param value a value
1124: * @param closure a closure
1125: * @return the last value of the last iteration
1126: */
1127: public static Object inject(Object[] self, Object value,
1128: Closure closure) {
1129: Object[] params = new Object[2];
1130: for (int i = 0; i < self.length; i++) {
1131: params[0] = value;
1132: params[1] = self[i];
1133: value = closure.call(params);
1134: }
1135: return value;
1136: }
1137:
1138: /**
1139: * Sums a collection of numeric values. <code>coll.sum()</code> is equivalent to:
1140: * <code>coll.inject(0) {value, item -> value + item}</code>.
1141: *
1142: * @param self Collection of values to add together.
1143: * @return The sum of all of the list itmems.
1144: */
1145: public static Object sum(Collection self) {
1146: Object result = null;
1147:
1148: if (self.size() == 0)
1149: return result;
1150:
1151: boolean isNumber = true;
1152:
1153: Class classref = null;
1154: try {
1155: classref = Class.forName("java.lang.Number");
1156: } catch (Exception ex) {
1157: }
1158:
1159: for (Iterator iter = self.iterator(); iter.hasNext();) {
1160: if (!classref.isInstance(iter.next())) {
1161: isNumber = false;
1162: break;
1163: }
1164: }
1165:
1166: if (isNumber) {
1167: result = new Integer(0);
1168: } else {
1169: result = new String();
1170: }
1171:
1172: Object[] param = new Object[1];
1173: for (Iterator iter = self.iterator(); iter.hasNext();) {
1174: param[0] = iter.next();
1175: MetaClass metaClass = InvokerHelper.getMetaClass(result);
1176: result = metaClass.invokeMethod(result, "plus", param);
1177: }
1178: return result;
1179: }
1180:
1181: /**
1182: * Sums the result of apply a closure to each item of a collection.
1183: * <code>coll.sum(closure)</code> is equivalent to:
1184: * <code>coll.collect(closure).sum()</code>.
1185: *
1186: * @param self a Collection
1187: * @param closure a single parameter closure that returns a numeric value.
1188: * @return The sum of the values returned by applying the closure to each
1189: * item of the list.
1190: */
1191: public static Object sum(Collection self, Closure closure) {
1192: Object result = new Integer(0);
1193: Object[] closureParam = new Object[1];
1194: Object[] plusParam = new Object[1];
1195: for (Iterator iter = self.iterator(); iter.hasNext();) {
1196: closureParam[0] = iter.next();
1197: plusParam[0] = closure.call(closureParam);
1198: MetaClass metaClass = InvokerHelper.getMetaClass(result);
1199: result = metaClass.invokeMethod(result, "plus", plusParam);
1200: }
1201: return result;
1202: }
1203:
1204: /**
1205: * Concatenates all of the items of the collection together with the given String as a separator
1206: *
1207: * @param self a Collection of objects
1208: * @param separator a String separator
1209: * @return the joined String
1210: */
1211: public static String join(Collection self, String separator) {
1212: StringBuffer buffer = new StringBuffer();
1213: boolean first = true;
1214:
1215: if (separator == null)
1216: separator = "";
1217:
1218: for (Iterator iter = self.iterator(); iter.hasNext();) {
1219: Object value = iter.next();
1220: if (first) {
1221: first = false;
1222: } else {
1223: buffer.append(separator);
1224: }
1225: buffer.append(InvokerHelper.toString(value));
1226: }
1227: return buffer.toString();
1228: }
1229:
1230: /**
1231: * Concatenates all of the elements of the array together with the given String as a separator
1232: *
1233: * @param self an array of Object
1234: * @param separator a String separator
1235: * @return the joined String
1236: */
1237: public static String join(Object[] self, String separator) {
1238: StringBuffer buffer = new StringBuffer();
1239: boolean first = true;
1240:
1241: if (separator == null)
1242: separator = "";
1243:
1244: for (int i = 0; i < self.length; i++) {
1245: String value = InvokerHelper.toString(self[i]);
1246: if (first) {
1247: first = false;
1248: } else {
1249: buffer.append(separator);
1250: }
1251: buffer.append(value);
1252: }
1253: return buffer.toString();
1254: }
1255:
1256: /**
1257: * Selects the maximum value found in the collection
1258: *
1259: * @param self a Collection
1260: * @return the maximum value
1261: */
1262: public static Object max(Collection self) {
1263: Object answer = null;
1264: for (Iterator iter = self.iterator(); iter.hasNext();) {
1265: Object value = iter.next();
1266: if (value != null) {
1267: if (answer == null
1268: || ScriptBytecodeAdapter.compareGreaterThan(
1269: value, answer)) {
1270: answer = value;
1271: }
1272: }
1273: }
1274: return answer;
1275: }
1276:
1277: /**
1278: * Selects the maximum value found in the collection using the given comparator
1279: *
1280: * @param self a Collection
1281: * @param comparator a Comparator
1282: * @return the maximum value
1283: */
1284: public static Object max(Collection self, Comparator comparator) {
1285: Object answer = null;
1286: for (Iterator iter = self.iterator(); iter.hasNext();) {
1287: Object value = iter.next();
1288: if (answer == null || comparator.compare(value, answer) > 0) {
1289: answer = value;
1290: }
1291: }
1292: return answer;
1293: }
1294:
1295: /**
1296: * Selects the minimum value found in the collection
1297: *
1298: * @param self a Collection
1299: * @return the minimum value
1300: */
1301: public static Object min(Collection self) {
1302: Object answer = null;
1303: for (Iterator iter = self.iterator(); iter.hasNext();) {
1304: Object value = iter.next();
1305: if (value != null) {
1306: if (answer == null
1307: || ScriptBytecodeAdapter.compareLessThan(value,
1308: answer)) {
1309: answer = value;
1310: }
1311: }
1312: }
1313: return answer;
1314: }
1315:
1316: /**
1317: * Selects the minimum value found in the collection using the given comparator
1318: *
1319: * @param self a Collection
1320: * @param comparator a Comparator
1321: * @return the minimum value
1322: */
1323: public static Object min(Collection self, Comparator comparator) {
1324: Object answer = null;
1325: for (Iterator iter = self.iterator(); iter.hasNext();) {
1326: Object value = iter.next();
1327: if (answer == null || comparator.compare(value, answer) < 0) {
1328: answer = value;
1329: }
1330: }
1331: return answer;
1332: }
1333:
1334: /**
1335: * Selects the minimum value found in the collection using the given closure as a comparator
1336: *
1337: * @param self a Collection
1338: * @param closure a closure used as a comparator
1339: * @return the minimum value
1340: */
1341: public static Object min(Collection self, Closure closure) {
1342: int params = closure.getMaximumNumberOfParameters();
1343: if (params == 1) {
1344: Object answer = null;
1345: Object answer_value = null;
1346: for (Iterator iter = self.iterator(); iter.hasNext();) {
1347: Object item = iter.next();
1348: Object value = closure.call(item);
1349: if (answer == null
1350: || ScriptBytecodeAdapter.compareLessThan(value,
1351: answer_value)) {
1352: answer = item;
1353: answer_value = value;
1354: }
1355: }
1356: return answer;
1357: } else {
1358: return min(self, new ClosureComparator(closure));
1359: }
1360: }
1361:
1362: /**
1363: * Selects the maximum value found in the collection using the given closure as a comparator
1364: *
1365: * @param self a Collection
1366: * @param closure a closure used as a comparator
1367: * @return the maximum value
1368: */
1369: public static Object max(Collection self, Closure closure) {
1370: int params = closure.getMaximumNumberOfParameters();
1371: if (params == 1) {
1372: Object answer = null;
1373: Object answer_value = null;
1374: for (Iterator iter = self.iterator(); iter.hasNext();) {
1375: Object item = iter.next();
1376: Object value = closure.call(item);
1377: if (answer == null
1378: || ScriptBytecodeAdapter.compareLessThan(
1379: answer_value, value)) {
1380: answer = item;
1381: answer_value = value;
1382: }
1383: }
1384: return answer;
1385: } else {
1386: return max(self, new ClosureComparator(closure));
1387: }
1388: }
1389:
1390: /**
1391: * Makes a String look like a Collection by adding support for the size() method
1392: *
1393: * @param text a String
1394: * @return the length of the String
1395: */
1396: public static int size(String text) {
1397: return text.length();
1398: }
1399:
1400: /**
1401: * Provide standard Groovy size() method for StringBuffers
1402: *
1403: * @param buffer a StringBuffer
1404: * @return the length of the StringBuffer
1405: */
1406: public static int size(StringBuffer buffer) {
1407: return buffer.length();
1408: }
1409:
1410: /**
1411: * Provide the standard Groovy size method
1412: */
1413: public static long size(File file) {
1414: return file.length();
1415: }
1416:
1417: /**
1418: * Provide the standard Groovy size method
1419: */
1420: public static long size(Matcher matcher) {
1421: return getCount(matcher);
1422: }
1423:
1424: /**
1425: * Makes an Array look like a Collection by adding support for the size() method
1426: *
1427: * @param self an Array of Object
1428: * @return the size of the Array
1429: */
1430: public static int size(Object[] self) {
1431: return self.length;
1432: }
1433:
1434: /**
1435: * Support the subscript operator for String.
1436: *
1437: * @param text a String
1438: * @param index the index of the Character to get
1439: * @return the Character at the given index
1440: */
1441: public static CharSequence getAt(CharSequence text, int index) {
1442: index = normaliseIndex(index, text.length());
1443: return text.subSequence(index, index + 1);
1444: }
1445:
1446: /**
1447: * Support the subscript operator for String
1448: *
1449: * @param text a String
1450: * @return the Character object at the given index
1451: */
1452: public static String getAt(String text, int index) {
1453: index = normaliseIndex(index, text.length());
1454: return text.substring(index, index + 1);
1455: }
1456:
1457: /**
1458: * Support the range subscript operator for CharSequence
1459: *
1460: * @param text a CharSequence
1461: * @param range a Range
1462: * @return the subsequence CharSequence
1463: */
1464: public static CharSequence getAt(CharSequence text, Range range) {
1465: int from = normaliseIndex(DefaultTypeTransformation
1466: .intUnbox(range.getFrom()), text.length());
1467: int to = normaliseIndex(DefaultTypeTransformation
1468: .intUnbox(range.getTo()), text.length());
1469:
1470: // If this is a backwards range, reverse the arguments to substring.
1471: if (from > to) {
1472: int tmp = from;
1473: from = to;
1474: to = tmp;
1475: }
1476:
1477: return text.subSequence(from, to + 1);
1478: }
1479:
1480: /**
1481: * Support the range subscript operator for CharSequence or StringBuffer with IntRange
1482: *
1483: * @param text a CharSequence
1484: * @param range an IntRange
1485: * @return the subsequence CharSequence
1486: */
1487: public static CharSequence getAt(CharSequence text, IntRange range) {
1488: return getAt(text, (Range) range);
1489: }
1490:
1491: /**
1492: * Support the range subscript operator for String with IntRange
1493: *
1494: * @param text a String
1495: * @param range an IntRange
1496: * @return the resulting String
1497: */
1498: public static String getAt(String text, IntRange range) {
1499: return getAt(text, (Range) range);
1500: }
1501:
1502: /**
1503: * Support the range subscript operator for String
1504: *
1505: * @param text a String
1506: * @param range a Range
1507: * @return a substring corresponding to the Range
1508: */
1509: public static String getAt(String text, Range range) {
1510: int from = normaliseIndex(DefaultTypeTransformation
1511: .intUnbox(range.getFrom()), text.length());
1512: int to = normaliseIndex(DefaultTypeTransformation
1513: .intUnbox(range.getTo()), text.length());
1514:
1515: // If this is a backwards range, reverse the arguments to substring.
1516: boolean reverse = range.isReverse();
1517: if (from > to) {
1518: int tmp = to;
1519: to = from;
1520: from = tmp;
1521: reverse = !reverse;
1522: }
1523:
1524: String answer = text.substring(from, to + 1);
1525: if (reverse) {
1526: answer = reverse(answer);
1527: }
1528: return answer;
1529: }
1530:
1531: /**
1532: * Creates a new string which is the reverse (backwards) of this string
1533: *
1534: * @param self a String
1535: * @return a new string with all the characters reversed.
1536: */
1537: public static String reverse(String self) {
1538: int size = self.length();
1539: StringBuffer buffer = new StringBuffer(size);
1540: for (int i = size - 1; i >= 0; i--) {
1541: buffer.append(self.charAt(i));
1542: }
1543: return buffer.toString();
1544: }
1545:
1546: /**
1547: * Transforms a String representing a URL into a URL object.
1548: *
1549: * @param self the String representing a URL
1550: * @return a URL
1551: * @throws MalformedURLException is thrown if the URL is not well formed.
1552: */
1553: public static URL toURL(String self) throws MalformedURLException {
1554: return new URL(self);
1555: }
1556:
1557: /**
1558: * Transforms a String representing a URI into a URI object.
1559: *
1560: * @param self the String representing a URI
1561: * @return a URI
1562: * @throws URISyntaxException is thrown if the URI is not well formed.
1563: */
1564: public static URI toURI(String self) throws URISyntaxException {
1565: return new URI(self);
1566: }
1567:
1568: /**
1569: * Turns a String into a regular expression pattern
1570: *
1571: * @param self a String to convert into a regular expression
1572: * @return the regular expression pattern
1573: */
1574: public static Pattern negate(String self) {
1575: return Pattern.compile(self);
1576: }
1577:
1578: /**
1579: * Replaces all occurrencies of a captured group by the result of a closure on that text.
1580: * <p/>
1581: * <p> For examples,
1582: * <pre>
1583: * assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { Object[] it -> it[0].toUpperCase() })
1584: * <p/>
1585: * Here,
1586: * it[0] is the global string of the matched group
1587: * it[1] is the first string in the matched group
1588: * it[2] is the second string in the matched group
1589: * <p/>
1590: * <p/>
1591: * assert "FOO-FOO-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { x, y, z -> z.toUpperCase() })
1592: * <p/>
1593: * Here,
1594: * x is the global string of the matched group
1595: * y is the first string in the matched group
1596: * z is the second string in the matched group
1597: * </pre>
1598: *
1599: * @param self a String
1600: * @param regex the capturing regex
1601: * @param closure the closure to apply on each captured group
1602: * @return a String with replaced content
1603: */
1604: public static String replaceAll(String self, String regex,
1605: Closure closure) {
1606: Matcher matcher = Pattern.compile(regex).matcher(self);
1607: if (matcher.find()) {
1608: matcher.reset();
1609: StringBuffer sb = new StringBuffer();
1610: while (matcher.find()) {
1611: int count = matcher.groupCount();
1612: ArrayList groups = new ArrayList();
1613: for (int i = 0; i <= count; i++) {
1614: groups.add(matcher.group(i));
1615: }
1616: matcher.appendReplacement(sb, String.valueOf(closure
1617: .call((Object[]) groups.toArray())));
1618: }
1619: matcher.appendTail(sb);
1620: return sb.toString();
1621: } else {
1622: return self;
1623: }
1624: }
1625:
1626: private static String getPadding(String padding, int length) {
1627: if (padding.length() < length) {
1628: return multiply(padding,
1629: new Integer(length / padding.length() + 1))
1630: .substring(0, length);
1631: } else {
1632: return padding.substring(0, length);
1633: }
1634: }
1635:
1636: /**
1637: * Pad a String with the characters appended to the left
1638: *
1639: * @param numberOfChars the total number of characters
1640: * @param padding the charaters used for padding
1641: * @return the String padded to the left
1642: */
1643: public static String padLeft(String self, Number numberOfChars,
1644: String padding) {
1645: int numChars = numberOfChars.intValue();
1646: if (numChars <= self.length()) {
1647: return self;
1648: } else {
1649: return getPadding(padding, numChars - self.length()) + self;
1650: }
1651: }
1652:
1653: /**
1654: * Pad a String with the spaces appended to the left
1655: *
1656: * @param numberOfChars the total number of characters
1657: * @return the String padded to the left
1658: */
1659:
1660: public static String padLeft(String self, Number numberOfChars) {
1661: return padLeft(self, numberOfChars, " ");
1662: }
1663:
1664: /**
1665: * Pad a String with the characters appended to the right
1666: *
1667: * @param numberOfChars the total number of characters
1668: * @param padding the charaters used for padding
1669: * @return the String padded to the right
1670: */
1671:
1672: public static String padRight(String self, Number numberOfChars,
1673: String padding) {
1674: int numChars = numberOfChars.intValue();
1675: if (numChars <= self.length()) {
1676: return self;
1677: } else {
1678: return self + getPadding(padding, numChars - self.length());
1679: }
1680: }
1681:
1682: /**
1683: * Pad a String with the spaces appended to the right
1684: *
1685: * @param numberOfChars the total number of characters
1686: * @return the String padded to the right
1687: */
1688:
1689: public static String padRight(String self, Number numberOfChars) {
1690: return padRight(self, numberOfChars, " ");
1691: }
1692:
1693: /**
1694: * Center a String and padd it with the characters appended around it
1695: *
1696: * @param numberOfChars the total number of characters
1697: * @param padding the charaters used for padding
1698: * @return the String centered with padded character around
1699: */
1700: public static String center(String self, Number numberOfChars,
1701: String padding) {
1702: int numChars = numberOfChars.intValue();
1703: if (numChars <= self.length()) {
1704: return self;
1705: } else {
1706: int charsToAdd = numChars - self.length();
1707: String semiPad = charsToAdd % 2 == 1 ? getPadding(padding,
1708: charsToAdd / 2 + 1) : getPadding(padding,
1709: charsToAdd / 2);
1710: if (charsToAdd % 2 == 0)
1711: return semiPad + self + semiPad;
1712: else
1713: return semiPad.substring(0, charsToAdd / 2) + self
1714: + semiPad;
1715: }
1716: }
1717:
1718: /**
1719: * Center a String and padd it with spaces appended around it
1720: *
1721: * @param numberOfChars the total number of characters
1722: * @return the String centered with padded character around
1723: */
1724: public static String center(String self, Number numberOfChars) {
1725: return center(self, numberOfChars, " ");
1726: }
1727:
1728: /**
1729: * Support the subscript operator, e.g. matcher[index], for a regex Matcher.
1730: * <p/>
1731: * For an example using no group match, <code><pre>
1732: * def p = /ab[d|f]/
1733: * def m = "abcabdabeabf" =~ p
1734: * for (i in 0..<m.count) {
1735: * println( "m.groupCount() = " + m.groupCount())
1736: * println( " " + i + ": " + m[i] ) // m[i] is a String
1737: * }
1738: * </pre></code>
1739: * <p/>
1740: * For an example using group matches, <code><pre>
1741: * def p = /(?:ab([c|d|e|f]))/
1742: * def m = "abcabdabeabf" =~ p
1743: * for (i in 0..<m.count) {
1744: * println( "m.groupCount() = " + m.groupCount())
1745: * println( " " + i + ": " + m[i] ) // m[i] is a List
1746: * }
1747: * </pre></code>
1748: * <p/>
1749: * For another example using group matches, <code><pre>
1750: * def m = "abcabdabeabfabxyzabx" =~ /(?:ab([d|x-z]+))/
1751: * m.count.times {
1752: * println( "m.groupCount() = " + m.groupCount())
1753: * println( " " + it + ": " + m[it] ) // m[it] is a List
1754: * }
1755: * </pre></code>
1756: *
1757: * @param matcher a Matcher
1758: * @param idx an index
1759: * @return object a matched String if no groups matched, list of matched groups otherwise.
1760: */
1761: public static Object getAt(Matcher matcher, int idx) {
1762: try {
1763: int count = getCount(matcher);
1764: if (idx < -count || idx >= count) {
1765: throw new IndexOutOfBoundsException(
1766: "index is out of range " + (-count) + ".."
1767: + (count - 1) + " (index = " + idx
1768: + ")");
1769: }
1770: idx = normaliseIndex(idx, count);
1771: matcher.reset();
1772: for (int i = 0; i <= idx; i++) {
1773: matcher.find();
1774: }
1775:
1776: if (hasGroup(matcher)) {
1777: // are we using groups?
1778: // yes, so return the specified group as list
1779: ArrayList list = new ArrayList(matcher.groupCount());
1780: for (int i = 0; i <= matcher.groupCount(); i++) {
1781: list.add(matcher.group(i));
1782: }
1783: return list;
1784: } else {
1785: // not using groups, so return the nth
1786: // occurrence of the pattern
1787: return matcher.group();
1788: }
1789: } catch (IllegalStateException ex) {
1790: return null;
1791: }
1792: }
1793:
1794: /**
1795: * Set the position of the given Matcher to the given index.
1796: *
1797: * @param matcher a Matcher
1798: * @param idx the index number
1799: */
1800: public static void setIndex(Matcher matcher, int idx) {
1801: int count = getCount(matcher);
1802: if (idx < -count || idx >= count) {
1803: throw new IndexOutOfBoundsException(
1804: "index is out of range " + (-count) + ".."
1805: + (count - 1) + " (index = " + idx + ")");
1806: }
1807: if (idx == 0) {
1808: matcher.reset();
1809: } else if (idx > 0) {
1810: matcher.reset();
1811: for (int i = 0; i < idx; i++) {
1812: matcher.find();
1813: }
1814: } else if (idx < 0) {
1815: matcher.reset();
1816: idx += getCount(matcher);
1817: for (int i = 0; i < idx; i++) {
1818: matcher.find();
1819: }
1820: }
1821: }
1822:
1823: /**
1824: * Find the number of Strings matched to the given Matcher.
1825: *
1826: * @param matcher a Matcher
1827: * @return int the number of Strings matched to the given matcher.
1828: */
1829: public static int getCount(Matcher matcher) {
1830: int counter = 0;
1831: matcher.reset();
1832: while (matcher.find()) {
1833: counter++;
1834: }
1835: matcher.reset();
1836: return counter;
1837: }
1838:
1839: /**
1840: * Check whether a Matcher contains a group or not.
1841: *
1842: * @param matcher a Matcher
1843: * @return boolean <code>true</code> if matcher contains at least one group.
1844: */
1845: public static boolean hasGroup(Matcher matcher) {
1846: return matcher.groupCount() > 0;
1847: }
1848:
1849: /**
1850: * Support the range subscript operator for a List
1851: *
1852: * @param self a List
1853: * @param range a Range
1854: * @return a sublist based on range borders or a new list if range is reversed
1855: * @see java.util.List#subList(int,int)
1856: */
1857: public static List getAt(List self, IntRange range) {
1858: RangeInfo info = subListBorders(self.size(), range);
1859: List answer = self.subList(info.from, info.to); // sublist is always exclusive, but Ranges are not
1860: if (info.reverse) {
1861: answer = reverse(answer);
1862: }
1863: return answer;
1864: }
1865:
1866: // helper method for getAt and putAt
1867: protected static RangeInfo subListBorders(int size, IntRange range) {
1868: int from = normaliseIndex(DefaultTypeTransformation
1869: .intUnbox(range.getFrom()), size);
1870: int to = normaliseIndex(DefaultTypeTransformation
1871: .intUnbox(range.getTo()), size);
1872: boolean reverse = range.isReverse();
1873: if (from > to) { // support list[1..-1]
1874: int tmp = to;
1875: to = from;
1876: from = tmp;
1877: reverse = !reverse;
1878: }
1879: return new RangeInfo(from, to + 1, reverse);
1880: }
1881:
1882: // helper method for getAt and putAt
1883: protected static RangeInfo subListBorders(int size, EmptyRange range) {
1884: int from = normaliseIndex(DefaultTypeTransformation
1885: .intUnbox(range.getFrom()), size);
1886: return new RangeInfo(from, from, false);
1887: }
1888:
1889: /**
1890: * Allows a List to be used as the indices to be used on a List
1891: *
1892: * @param self a List
1893: * @param indices a Collection of indices
1894: * @return a new list of the values at the given indices
1895: */
1896: public static List getAt(List self, Collection indices) {
1897: List answer = new ArrayList(indices.size());
1898: for (Iterator iter = indices.iterator(); iter.hasNext();) {
1899: Object value = iter.next();
1900: if (value instanceof Range) {
1901: answer.addAll(getAt(self, (Range) value));
1902: } else if (value instanceof List) {
1903: answer.addAll(getAt(self, (List) value));
1904: } else {
1905: int idx = DefaultTypeTransformation.intUnbox(value);
1906: answer.add(getAt(self, idx));
1907: }
1908: }
1909: return answer;
1910: }
1911:
1912: /**
1913: * Allows a List to be used as the indices to be used on a List
1914: *
1915: * @param self an Array of Objects
1916: * @param indices a Collection of indices
1917: * @return a new list of the values at the given indices
1918: */
1919: public static List getAt(Object[] self, Collection indices) {
1920: List answer = new ArrayList(indices.size());
1921: for (Iterator iter = indices.iterator(); iter.hasNext();) {
1922: Object value = iter.next();
1923: if (value instanceof Range) {
1924: answer.addAll(getAt(self, (Range) value));
1925: } else if (value instanceof Collection) {
1926: answer.addAll(getAt(self, (Collection) value));
1927: } else {
1928: int idx = DefaultTypeTransformation.intUnbox(value);
1929: answer.add(getAt(self, idx));
1930: }
1931: }
1932: return answer;
1933: }
1934:
1935: /**
1936: * Allows a List to be used as the indices to be used on a CharSequence
1937: *
1938: * @param self a CharSequence
1939: * @param indices a Collection of indices
1940: * @return a String of the values at the given indices
1941: */
1942: public static CharSequence getAt(CharSequence self,
1943: Collection indices) {
1944: StringBuffer answer = new StringBuffer();
1945: for (Iterator iter = indices.iterator(); iter.hasNext();) {
1946: Object value = iter.next();
1947: if (value instanceof Range) {
1948: answer.append(getAt(self, (Range) value));
1949: } else if (value instanceof Collection) {
1950: answer.append(getAt(self, (Collection) value));
1951: } else {
1952: int idx = DefaultTypeTransformation.intUnbox(value);
1953: answer.append(getAt(self, idx));
1954: }
1955: }
1956: return answer.toString();
1957: }
1958:
1959: /**
1960: * Allows a List to be used as the indices to be used on a String
1961: *
1962: * @param self a String
1963: * @param indices a Collection of indices
1964: * @return a String of the values at the given indices
1965: */
1966: public static String getAt(String self, Collection indices) {
1967: return (String) getAt((CharSequence) self, indices);
1968: }
1969:
1970: /**
1971: * Allows a List to be used as the indices to be used on a Matcher
1972: *
1973: * @param self a Matcher
1974: * @param indices a Collection of indices
1975: * @return a String of the values at the given indices
1976: */
1977: public static String getAt(Matcher self, Collection indices) {
1978: StringBuffer answer = new StringBuffer();
1979: for (Iterator iter = indices.iterator(); iter.hasNext();) {
1980: Object value = iter.next();
1981: if (value instanceof Range) {
1982: answer.append(getAt(self, (Range) value));
1983: } else if (value instanceof Collection) {
1984: answer.append(getAt(self, (Collection) value));
1985: } else {
1986: int idx = DefaultTypeTransformation.intUnbox(value);
1987: answer.append(getAt(self, idx));
1988: }
1989: }
1990: return answer.toString();
1991: }
1992:
1993: /**
1994: * Creates a sub-Map containing the given keys. This method is similar to
1995: * List.subList() but uses keys rather than index ranges.
1996: *
1997: * @param map a Map
1998: * @param keys a Collection of keys
1999: * @return a new Map containing the given keys
2000: */
2001: public static Map subMap(Map map, Collection keys) {
2002: Map answer = new HashMap(keys.size());
2003: for (Iterator iter = keys.iterator(); iter.hasNext();) {
2004: Object key = iter.next();
2005: answer.put(key, map.get(key));
2006: }
2007: return answer;
2008: }
2009:
2010: /**
2011: * Looks up an item in a Map for the given key and returns the value - unless
2012: * there is no entry for the given key in which case add the default value
2013: * to the map and return that.
2014: *
2015: * @param map a Map
2016: * @param key the key to lookup the value of
2017: * @param defaultValue the value to return and add to the map for this key if
2018: * there is no entry for the given key
2019: * @return the value of the given key or the default value, added to the map if the
2020: * key did not exist
2021: */
2022: public static Object get(Map map, Object key, Object defaultValue) {
2023: Object answer = map.get(key);
2024: if (answer == null) {
2025: answer = defaultValue;
2026: map.put(key, answer);
2027: }
2028: return answer;
2029: }
2030:
2031: /**
2032: * Support the range subscript operator for an Array
2033: *
2034: * @param array an Array of Objects
2035: * @param range a Range
2036: * @return a range of a list from the range's from index up to but not
2037: * including the ranges's to value
2038: */
2039: public static List getAt(Object[] array, Range range) {
2040: List list = Arrays.asList(array);
2041: return getAt(list, range);
2042: }
2043:
2044: public static List getAt(Object[] array, IntRange range) {
2045: List list = Arrays.asList(array);
2046: return getAt(list, range);
2047: }
2048:
2049: public static List getAt(Object[] array, ObjectRange range) {
2050: List list = Arrays.asList(array);
2051: return getAt(list, range);
2052: }
2053:
2054: /**
2055: * Support the subscript operator for an Array
2056: *
2057: * @param array an Array of Objects
2058: * @param idx an index
2059: * @return the value at the given index
2060: */
2061: public static Object getAt(Object[] array, int idx) {
2062: return array[normaliseIndex(idx, array.length)];
2063: }
2064:
2065: /**
2066: * Support the subscript operator for an Array
2067: *
2068: * @param array an Array of Objects
2069: * @param idx an index
2070: * @param value an Object to put at the given index
2071: */
2072: public static void putAt(Object[] array, int idx, Object value) {
2073: if (value instanceof Number) {
2074: Class arrayComponentClass = array.getClass()
2075: .getComponentType();
2076:
2077: if (!arrayComponentClass.equals(value.getClass())) {
2078: Object newVal = DefaultTypeTransformation.castToType(
2079: value, arrayComponentClass);
2080: array[normaliseIndex(idx, array.length)] = newVal;
2081: return;
2082: }
2083: }
2084: array[normaliseIndex(idx, array.length)] = value;
2085: }
2086:
2087: /**
2088: * Allows conversion of arrays into a mutable List
2089: *
2090: * @param array an Array of Objects
2091: * @return the array as a List
2092: */
2093: public static List toList(Object[] array) {
2094: int size = array.length;
2095: List list = new ArrayList(size);
2096: for (int i = 0; i < size; i++) {
2097: list.add(array[i]);
2098: }
2099: return list;
2100: }
2101:
2102: /**
2103: * Support the subscript operator for a List
2104: *
2105: * @param self a List
2106: * @param idx an index
2107: * @return the value at the given index
2108: */
2109: public static Object getAt(List self, int idx) {
2110: int size = self.size();
2111: int i = normaliseIndex(idx, size);
2112: if (i < size) {
2113: return self.get(i);
2114: } else {
2115: return null;
2116: }
2117: }
2118:
2119: /**
2120: * A helper method to allow lists to work with subscript operators
2121: *
2122: * @param self a List
2123: * @param idx an index
2124: * @param value the value to put at the given index
2125: */
2126: public static void putAt(List self, int idx, Object value) {
2127: int size = self.size();
2128: idx = normaliseIndex(idx, size);
2129: if (idx < size) {
2130: self.set(idx, value);
2131: } else {
2132: while (size < idx) {
2133: self.add(size++, null);
2134: }
2135: self.add(idx, value);
2136: }
2137: }
2138:
2139: /**
2140: * Support the range subscript operator for StringBuffer
2141: *
2142: * @param self a StringBuffer
2143: * @param range a Range
2144: * @param value the object that's toString() will be inserted
2145: */
2146: public static void putAt(StringBuffer self, IntRange range,
2147: Object value) {
2148: RangeInfo info = subListBorders(self.length(), range);
2149: self.replace(info.from, info.to, value.toString());
2150: }
2151:
2152: /**
2153: * Support the range subscript operator for StringBuffer
2154: *
2155: * @param self a StringBuffer
2156: * @param range a Range
2157: * @param value the object that's toString() will be inserted
2158: */
2159: public static void putAt(StringBuffer self, EmptyRange range,
2160: Object value) {
2161: RangeInfo info = subListBorders(self.length(), range);
2162: self.replace(info.from, info.to, value.toString());
2163: }
2164:
2165: /**
2166: * A helper method to allow lists to work with subscript operators
2167: *
2168: * @param self a List
2169: * @param range the subset of the list to set
2170: * @param value the values to put at the given sublist or a Collection of values
2171: */
2172: public static void putAt(List self, EmptyRange range, Object value) {
2173: RangeInfo info = subListBorders(self.size(), range);
2174: List sublist = self.subList(info.from, info.to);
2175: sublist.clear();
2176: if (value instanceof Collection) {
2177: Collection col = (Collection) value;
2178: if (col.size() == 0)
2179: return;
2180: sublist.addAll(col);
2181: } else {
2182: sublist.add(value);
2183: }
2184: }
2185:
2186: /**
2187: * A helper method to allow lists to work with subscript operators
2188: *
2189: * @param self a List
2190: * @param range the subset of the list to set
2191: * @param value the value to put at the given sublist or a Collection of values
2192: */
2193: public static void putAt(List self, IntRange range, Object value) {
2194: RangeInfo info = subListBorders(self.size(), range);
2195: List sublist = self.subList(info.from, info.to);
2196: sublist.clear();
2197: if (value instanceof Collection) {
2198: Collection col = (Collection) value;
2199: if (col.size() == 0)
2200: return;
2201: sublist.addAll(col);
2202: } else {
2203: sublist.add(value);
2204: }
2205: }
2206:
2207: /**
2208: * A helper method to allow lists to work with subscript operators
2209: *
2210: * @param self a List
2211: * @param splice the subset of the list to set
2212: * @param values the value to put at the given sublist
2213: * @deprecated replace with putAt(List self, Range range, List value)
2214: */
2215: public static void putAt(List self, List splice, List values) {
2216: List sublist = getSubList(self, splice);
2217: sublist.clear();
2218: sublist.addAll(values);
2219: }
2220:
2221: /**
2222: * A helper method to allow lists to work with subscript operators
2223: *
2224: * @param self a List
2225: * @param splice the subset of the list to set
2226: * @param value the value to put at the given sublist
2227: * @deprecated replace with putAt(List self, Range range, Object value)
2228: */
2229: public static void putAt(List self, List splice, Object value) {
2230: List sublist = getSubList(self, splice);
2231: sublist.clear();
2232: sublist.add(value);
2233: }
2234:
2235: // helper method for putAt(Splice)
2236: // todo: remove after putAt(Splice) gets deleted
2237: protected static List getSubList(List self, List splice) {
2238: int left /* = 0 */;
2239: int right = 0;
2240: boolean emptyRange = false;
2241: if (splice.size() == 2) {
2242: left = DefaultTypeTransformation.intUnbox(splice.get(0));
2243: right = DefaultTypeTransformation.intUnbox(splice.get(1));
2244: } else if (splice instanceof IntRange) {
2245: IntRange range = (IntRange) splice;
2246: left = range.getFromInt();
2247: right = range.getToInt();
2248: } else if (splice instanceof EmptyRange) {
2249: RangeInfo info = subListBorders(self.size(),
2250: (EmptyRange) splice);
2251: left = info.from;
2252: emptyRange = true;
2253: } else {
2254: throw new IllegalArgumentException(
2255: "You must specify a list of 2 indexes to create a sub-list");
2256: }
2257: int size = self.size();
2258: left = normaliseIndex(left, size);
2259: right = normaliseIndex(right, size);
2260: List sublist /* = null */;
2261: if (!emptyRange) {
2262: sublist = self.subList(left, right + 1);
2263: } else {
2264: sublist = self.subList(left, left);
2265: }
2266: return sublist;
2267: }
2268:
2269: /**
2270: * Support the subscript operator for a List
2271: *
2272: * @param self a Map
2273: * @param key an Object as a key for the map
2274: * @return the value corresponding to the given key
2275: */
2276: public static Object getAt(Map self, Object key) {
2277: return self.get(key);
2278: }
2279:
2280: /**
2281: * A helper method to allow lists to work with subscript operators
2282: *
2283: * @param self a Map
2284: * @param key an Object as a key for the map
2285: * @return the value corresponding to the given key
2286: */
2287: public static Object putAt(Map self, Object key, Object value) {
2288: return self.put(key, value);
2289: }
2290:
2291: /**
2292: * This converts a possibly negative index to a real index into the array.
2293: *
2294: * @param i
2295: * @param size
2296: */
2297: protected static int normaliseIndex(int i, int size) {
2298: int temp = i;
2299: if (i < 0) {
2300: i += size;
2301: }
2302: if (i < 0) {
2303: throw new ArrayIndexOutOfBoundsException(
2304: "Negative array index [" + temp
2305: + "] too large for array size " + size);
2306: }
2307: return i;
2308: }
2309:
2310: /**
2311: * Support the subscript operator for List
2312: *
2313: * @param coll a Collection
2314: * @param property a String
2315: * @return a List
2316: */
2317: public static List getAt(Collection coll, String property) {
2318: List answer = new ArrayList(coll.size());
2319: for (Iterator iter = coll.iterator(); iter.hasNext();) {
2320: Object item = iter.next();
2321: Object value = InvokerHelper.getProperty(item, property);
2322: if (value instanceof Collection) {
2323: answer.addAll((Collection) value);
2324: } else {
2325: answer.add(value);
2326: }
2327: }
2328: return answer;
2329: }
2330:
2331: /**
2332: * A convenience method for creating an immutable map
2333: *
2334: * @param self a Map
2335: * @return an immutable Map
2336: */
2337: public static Map asImmutable(Map self) {
2338: return Collections.unmodifiableMap(self);
2339: }
2340:
2341: /**
2342: * A convenience method for creating an immutable sorted map
2343: *
2344: * @param self a SortedMap
2345: * @return an immutable SortedMap
2346: */
2347: public static SortedMap asImmutable(SortedMap self) {
2348: return Collections.unmodifiableSortedMap(self);
2349: }
2350:
2351: /**
2352: * A convenience method for creating an immutable list
2353: *
2354: * @param self a List
2355: * @return an immutable List
2356: */
2357: public static List asImmutable(List self) {
2358: return Collections.unmodifiableList(self);
2359: }
2360:
2361: /**
2362: * A convenience method for creating an immutable list
2363: *
2364: * @param self a Set
2365: * @return an immutable Set
2366: */
2367: public static Set asImmutable(Set self) {
2368: return Collections.unmodifiableSet(self);
2369: }
2370:
2371: /**
2372: * A convenience method for creating an immutable sorted set
2373: *
2374: * @param self a SortedSet
2375: * @return an immutable SortedSet
2376: */
2377: public static SortedSet asImmutable(SortedSet self) {
2378: return Collections.unmodifiableSortedSet(self);
2379: }
2380:
2381: /**
2382: * A convenience method for creating an immutable Collection
2383: *
2384: * @param self a Collection
2385: * @return an immutable Collection
2386: */
2387: public static Collection asImmutable(Collection self) {
2388: return Collections.unmodifiableCollection(self);
2389: }
2390:
2391: /**
2392: * A convenience method for creating a synchronized Map
2393: *
2394: * @param self a Map
2395: * @return a synchronized Map
2396: */
2397: public static Map asSynchronized(Map self) {
2398: return Collections.synchronizedMap(self);
2399: }
2400:
2401: /**
2402: * A convenience method for creating a synchronized SortedMap
2403: *
2404: * @param self a SortedMap
2405: * @return a synchronized SortedMap
2406: */
2407: public static SortedMap asSynchronized(SortedMap self) {
2408: return Collections.synchronizedSortedMap(self);
2409: }
2410:
2411: /**
2412: * A convenience method for creating a synchronized Collection
2413: *
2414: * @param self a Collection
2415: * @return a synchronized Collection
2416: */
2417: public static Collection asSynchronized(Collection self) {
2418: return Collections.synchronizedCollection(self);
2419: }
2420:
2421: /**
2422: * A convenience method for creating a synchronized List
2423: *
2424: * @param self a List
2425: * @return a synchronized List
2426: */
2427: public static List asSynchronized(List self) {
2428: return Collections.synchronizedList(self);
2429: }
2430:
2431: /**
2432: * A convenience method for creating a synchronized Set
2433: *
2434: * @param self a Set
2435: * @return a synchronized Set
2436: */
2437: public static Set asSynchronized(Set self) {
2438: return Collections.synchronizedSet(self);
2439: }
2440:
2441: /**
2442: * A convenience method for creating a synchronized SortedSet
2443: *
2444: * @param self a SortedSet
2445: * @return a synchronized SortedSet
2446: */
2447: public static SortedSet asSynchronized(SortedSet self) {
2448: return Collections.synchronizedSortedSet(self);
2449: }
2450:
2451: public static SpreadMap spread(Map self) {
2452: return toSpreadMap(self);
2453: }
2454:
2455: /**
2456: * Returns the converted <code>SpreadLMap</code> of the given <code>self</code>.
2457: * <p/>
2458: * For examples, if there is defined a function like as
2459: * <blockquote><pre>
2460: * def fn(a, b, c, d) { return a + b + c + d }
2461: * </pre></blockquote>, then all of the following three have the same meaning.
2462: * <blockquote><pre>
2463: * println fn(a:1, [b:2, c:3].toSpreadMap(), d:4)
2464: * println fn(a:1, *:[b:2, c:3], d:4)
2465: * println fn(a:1, b:2, c:3, d:4)
2466: * </pre></blockquote>
2467: * <p/>
2468: *
2469: * @param self a list to be converted into a spreadmap
2470: * @return a newly created Spreadmap if this list is not null and its size is positive.
2471: */
2472: public static SpreadMap toSpreadMap(Map self) {
2473: if (self == null)
2474: throw new GroovyRuntimeException(
2475: "Fail to convert Map to SpreadMap, because it is null.");
2476: else
2477: return new SpreadMap(self);
2478: }
2479:
2480: public static SpreadMap toSpreadMap(Object[] self) {
2481: if (self == null)
2482: throw new GroovyRuntimeException(
2483: "Fail to convert Object[] to SpreadMap, because it is null.");
2484: else if (self.length % 2 != 0)
2485: throw new GroovyRuntimeException(
2486: "Fail to convert Object[] to SpreadMap, because it's size is not even.");
2487: else
2488: return new SpreadMap(self);
2489: }
2490:
2491: /**
2492: * Sorts the given collection into a sorted list.
2493: *
2494: * @param self the collection to be sorted
2495: * @return the sorted collection as a List
2496: */
2497: public static List sort(Collection self) {
2498: List answer = asList(self);
2499: Collections.sort(answer, new NumberComparator());
2500: return answer;
2501: }
2502:
2503: /**
2504: * Avoids doing unnecessary work when sorting an already sorted set
2505: *
2506: * @param self
2507: * @return the sorted set
2508: */
2509: public static SortedSet sort(SortedSet self) {
2510: return self;
2511: }
2512:
2513: /**
2514: * Removes the last item from the List. Using add() and pop()
2515: * is similar to push and pop on a Stack.
2516: *
2517: * @param self a List
2518: * @return the item removed from the List
2519: * @throws NoSuchElementException if the list is empty and you try to pop() it.
2520: */
2521: public static Object pop(List self) {
2522: if (self.isEmpty()) {
2523: throw new NoSuchElementException(
2524: "Cannot pop() an empty List");
2525: }
2526: return self.remove(self.size() - 1);
2527: }
2528:
2529: /**
2530: * A convenience method for sorting a Collection with a specific comparator
2531: *
2532: * @param self a collection to be sorted
2533: * @param comparator a Comparator used for the comparison
2534: * @return a newly created sorted List
2535: */
2536: public static List sort(Collection self, Comparator comparator) {
2537: List list = asList(self);
2538: Collections.sort(list, comparator);
2539: return list;
2540: }
2541:
2542: /**
2543: * A convenience method for sorting a Collection using a closure as a comparator
2544: *
2545: * @param self a Collection to be sorted
2546: * @param closure a Closure used as a comparator
2547: * @return a newly created sorted List
2548: */
2549: public static List sort(Collection self, Closure closure) {
2550: List list = asList(self);
2551: // use a comparator of one item or two
2552: int params = closure.getMaximumNumberOfParameters();
2553: if (params == 1) {
2554: Collections.sort(list, new OrderBy(closure));
2555: } else {
2556: Collections.sort(list, new ClosureComparator(closure));
2557: }
2558: return list;
2559: }
2560:
2561: /**
2562: * Converts the given collection into a List
2563: *
2564: * @param self a collection to be converted into a List
2565: * @return a newly created List if this collection is not already a List
2566: */
2567: public static List asList(Collection self) {
2568: if (self instanceof List) {
2569: return (List) self;
2570: } else {
2571: return new ArrayList(self);
2572: }
2573: }
2574:
2575: public static Object asType(Collection col, Class clazz) {
2576: if (clazz == List.class) {
2577: return asList(col);
2578: } else if (clazz == Set.class) {
2579: if (col instanceof Set)
2580: return col;
2581: return new HashSet(col);
2582: }
2583: return asType((Object) col, clazz);
2584: }
2585:
2586: public static Object asType(Closure cl, Class clazz) {
2587: if (clazz.isInterface() && !(clazz.isInstance(cl))) {
2588: return Proxy.newProxyInstance(clazz.getClassLoader(),
2589: new Class[] { clazz }, new ConvertedClosure(cl));
2590: }
2591: return asType((Object) cl, clazz);
2592: }
2593:
2594: public static Object asType(Map map, Class clazz) {
2595: if (clazz.isInterface() && !(clazz.isInstance(map))) {
2596: return Proxy.newProxyInstance(clazz.getClassLoader(),
2597: new Class[] { clazz }, new ConvertedMap(map));
2598: }
2599: return asType((Object) map, clazz);
2600: }
2601:
2602: /**
2603: * Reverses the list
2604: *
2605: * @param self a List
2606: * @return a reversed List
2607: */
2608: public static List reverse(List self) {
2609: int size = self.size();
2610: List answer = new ArrayList(size);
2611: ListIterator iter = self.listIterator(size);
2612: while (iter.hasPrevious()) {
2613: answer.add(iter.previous());
2614: }
2615: return answer;
2616: }
2617:
2618: /**
2619: * Create a List as a union of both Collections
2620: *
2621: * @param left the left Collection
2622: * @param right the right Collection
2623: * @return a List
2624: */
2625: public static List plus(Collection left, Collection right) {
2626: List answer = new ArrayList(left.size() + right.size());
2627: answer.addAll(left);
2628: answer.addAll(right);
2629: return answer;
2630: }
2631:
2632: /**
2633: * Create a List as a union of a Collection and an Object
2634: *
2635: * @param left a Collection
2636: * @param right an object to append
2637: * @return a List
2638: */
2639: public static List plus(Collection left, Object right) {
2640: List answer = new ArrayList(left.size() + 1);
2641: answer.addAll(left);
2642: answer.add(right);
2643: return answer;
2644: }
2645:
2646: /**
2647: * Create a List composed of the same elements repeated a certain number of times.
2648: *
2649: * @param self a Collection
2650: * @param factor the number of times to append
2651: * @return a List
2652: */
2653: public static List multiply(Collection self, Number factor) {
2654: int size = factor.intValue();
2655: List answer = new ArrayList(self.size() * size);
2656: for (int i = 0; i < size; i++) {
2657: answer.addAll(self);
2658: }
2659: return answer;
2660: }
2661:
2662: /**
2663: * Create a List composed of the intersection of both collections
2664: *
2665: * @param left a Collection
2666: * @param right a Collection
2667: * @return a List as an intersection of both collections
2668: */
2669: public static List intersect(Collection left, Collection right) {
2670: if (left.size() == 0)
2671: return new ArrayList();
2672:
2673: boolean nlgnSort = sameType(new Collection[] { left, right });
2674:
2675: ArrayList result = new ArrayList();
2676: //creates the collection to look for values.
2677: Collection pickFrom = new TreeSet(new NumberComparator());
2678: pickFrom.addAll(left);
2679:
2680: for (Iterator iter = right.iterator(); iter.hasNext();) {
2681: final Object o = iter.next();
2682: if (pickFrom.contains(o))
2683: result.add(o);
2684: }
2685: return result;
2686: }
2687:
2688: /**
2689: * Returns <code>true</code> if the intersection of two collenctions is empty.
2690: *
2691: * @param left a Collection
2692: * @param right a Collection
2693: * @return boolean <code>true</code> if the intersection of two collenctions is empty, <code>false</code> otherwise.
2694: */
2695: public static boolean disjoint(Collection left, Collection right) {
2696:
2697: if (left.size() == 0 || right.size() == 0)
2698: return true;
2699:
2700: boolean nlgnSort = sameType(new Collection[] { left, right });
2701:
2702: Collection pickFrom = (Collection) new TreeSet(
2703: new NumberComparator());
2704: ((TreeSet) pickFrom).addAll(right);
2705:
2706: for (Iterator iter = left.iterator(); iter.hasNext();) {
2707: final Object o = iter.next();
2708: if (pickFrom.contains(o))
2709: return false;
2710: }
2711: return true;
2712: }
2713:
2714: // Default comparator for numbers of different types.
2715: private static class NumberComparator implements Comparator {
2716: public int compare(Object o1, Object o2) {
2717: if (o1 instanceof Number && o2 instanceof Number) {
2718: BigDecimal x1 = new BigDecimal("" + o1);
2719: BigDecimal x2 = new BigDecimal("" + o2);
2720: return x1.compareTo(x2);
2721: } else if (o1.getClass() == o2.getClass()
2722: && o1 instanceof Comparable) {
2723: return ((Comparable) o1).compareTo((Comparable) o2);
2724: } else {
2725: int x1 = o1.hashCode();
2726: int x2 = o2.hashCode();
2727: return (x1 - x2);
2728: }
2729: }
2730:
2731: public boolean equals(Object obj) {
2732: return this .equals(obj);
2733: }
2734: }
2735:
2736: /**
2737: * Compare two Lists.
2738: * If numbers exits in the Lists, then they are compared as numbers,
2739: * for example 2 == 2L.
2740: *
2741: * @param left a List
2742: * @param right a List
2743: * @return boolean <code>true</code> if two Lists equals, <code>false</code> otherwise.
2744: */
2745: public static boolean equals(List left, List right) {
2746: if (left == null) {
2747: return right == null;
2748: } else if (right == null) {
2749: return false;
2750: } else if (left.size() != right.size()) {
2751: return false;
2752: } else {
2753: final NumberComparator numberComparator = new NumberComparator();
2754: final Iterator it1 = left.iterator(), it2 = right
2755: .iterator();
2756:
2757: while (it1.hasNext()) {
2758: final Object o1 = it1.next();
2759: final Object o2 = it2.next();
2760:
2761: if (o1 == null) {
2762: if (o2 != null)
2763: return false;
2764: } else {
2765: if (o1 instanceof Number) {
2766: if (!(o2 instanceof Number && numberComparator
2767: .compare(o1, o2) == 0)) {
2768: return false;
2769: }
2770: } else {
2771: // Use this way of calling equals in case the elament is a List
2772: // or any other type which has an equals in DGM
2773: if (!((Boolean) InvokerHelper.invokeMethod(o1,
2774: "equals", new Object[] { o2 }))
2775: .booleanValue())
2776: return false;
2777: }
2778: }
2779: }
2780:
2781: return true;
2782: }
2783: }
2784:
2785: /**
2786: * Create a List composed of the elements of the first list minus the elements of the collection
2787: *
2788: * @param self a List
2789: * @param removeMe a Collection of elements to remove
2790: * @return a List with the common elements removed
2791: */
2792: public static List minus(List self, Collection removeMe) {
2793:
2794: if (self.size() == 0)
2795: return new ArrayList();
2796:
2797: boolean nlgnSort = sameType(new Collection[] { self, removeMe });
2798:
2799: //we can't use the same tactic as for intersection
2800: //since AbstractCollection only does a remove on the first
2801: //element it encounter.
2802:
2803: Comparator numberComparator = new NumberComparator();
2804:
2805: if (nlgnSort && (self.get(0) instanceof Comparable)) {
2806: //n*LOG(n) version
2807: Set answer /* = null */;
2808: if (Number.class.isInstance(self.get(0))) {
2809: answer = new TreeSet(numberComparator);
2810: answer.addAll(self);
2811: for (Iterator it = self.iterator(); it.hasNext();) {
2812: Object o = it.next();
2813: if (Number.class.isInstance(o)) {
2814: for (Iterator it2 = removeMe.iterator(); it2
2815: .hasNext();) {
2816: Object o2 = it2.next();
2817: if (Number.class.isInstance(o2)) {
2818: if (numberComparator.compare(o, o2) == 0)
2819: answer.remove(o);
2820: }
2821: }
2822: } else {
2823: if (removeMe.contains(o))
2824: answer.remove(o);
2825: }
2826: }
2827: } else {
2828: answer = new TreeSet(numberComparator);
2829: answer.addAll(self);
2830: answer.removeAll(removeMe);
2831: }
2832:
2833: List ansList = new ArrayList();
2834: for (Iterator it = self.iterator(); it.hasNext();) {
2835: Object o = it.next();
2836: if (answer.contains(o))
2837: ansList.add(o);
2838: }
2839: return ansList;
2840: } else {
2841: //n*n version
2842: List tmpAnswer = new LinkedList(self);
2843: for (Iterator iter = tmpAnswer.iterator(); iter.hasNext();) {
2844: Object element = iter.next();
2845: //boolean removeElement = false;
2846: for (Iterator iterator = removeMe.iterator(); iterator
2847: .hasNext();) {
2848: Object elt = iterator.next();
2849: if (elt != null
2850: && numberComparator.compare(element, elt) == 0) {
2851: iter.remove();
2852: }
2853: }
2854: }
2855:
2856: //remove duplicates
2857: //can't use treeset since the base classes are different
2858: return new ArrayList(tmpAnswer);
2859: }
2860: }
2861:
2862: public static List minus(List self, Object operand) {
2863: Comparator numberComparator = new NumberComparator();
2864: List ansList = new ArrayList();
2865: for (Iterator it = self.iterator(); it.hasNext();) {
2866: Object o = it.next();
2867: if (numberComparator.compare(o, operand) != 0)
2868: ansList.add(o);
2869: }
2870: return ansList;
2871: }
2872:
2873: /**
2874: * Flatten a list
2875: *
2876: * @param self a List
2877: * @return a flattened List
2878: */
2879: public static List flatten(List self) {
2880: return new ArrayList(flatten(self, new LinkedList()));
2881: }
2882:
2883: /**
2884: * Iterate over each element of the list in the reverse order.
2885: *
2886: * @param self a List
2887: * @param closure a closure
2888: */
2889: public static void reverseEach(List self, Closure closure) {
2890: List reversed = reverse(self);
2891: for (Iterator iter = reversed.iterator(); iter.hasNext();) {
2892: closure.call(iter.next());
2893: }
2894: }
2895:
2896: private static List flatten(Collection elements, List addTo) {
2897: Iterator iter = elements.iterator();
2898: while (iter.hasNext()) {
2899: Object element = iter.next();
2900: if (element instanceof Collection) {
2901: flatten((Collection) element, addTo);
2902: } else if (element instanceof Map) {
2903: flatten(((Map) element).values(), addTo);
2904: } else {
2905: addTo.add(element);
2906: }
2907: }
2908: return addTo;
2909: }
2910:
2911: /**
2912: * Overloads the left shift operator to provide an easy way to append objects to a list
2913: *
2914: * @param self a Collection
2915: * @param value an Object to be added to the collection.
2916: * @return a Collection with an Object added to it.
2917: */
2918: public static Collection leftShift(Collection self, Object value) {
2919: self.add(value);
2920: return self;
2921: }
2922:
2923: /**
2924: * Overloads the left shift operator to provide an easy way to append multiple
2925: * objects as string representations to a String
2926: *
2927: * @param self a String
2928: * @param value an Obect
2929: * @return a StringBuffer
2930: */
2931: public static StringBuffer leftShift(String self, Object value) {
2932: return new StringBuffer(self).append(value);
2933: }
2934:
2935: protected static StringWriter createStringWriter(String self) {
2936: StringWriter answer = new StringWriter();
2937: answer.write(self);
2938: return answer;
2939: }
2940:
2941: protected static StringBufferWriter createStringBufferWriter(
2942: StringBuffer self) {
2943: return new StringBufferWriter(self);
2944: }
2945:
2946: /**
2947: * Overloads the left shift operator to provide an easy way to append multiple
2948: * objects as string representations to a StringBuffer
2949: *
2950: * @param self a StringBuffer
2951: * @param value a value to append
2952: * @return a StringBuffer
2953: */
2954: public static StringBuffer leftShift(StringBuffer self, Object value) {
2955: self.append(value);
2956: return self;
2957: }
2958:
2959: /**
2960: * Overloads the left shift operator to provide an append mechanism to add things to a writer
2961: *
2962: * @param self a Writer
2963: * @param value a value to append
2964: * @return a StringWriter
2965: */
2966: public static Writer leftShift(Writer self, Object value)
2967: throws IOException {
2968: InvokerHelper.write(self, value);
2969: return self;
2970: }
2971:
2972: /**
2973: * Implementation of the left shift operator for integral types. Non integral
2974: * Number types throw UnsupportedOperationException.
2975: */
2976: public static Number leftShift(Number left, Number right) {
2977: return NumberMath.leftShift(left, right);
2978: }
2979:
2980: /**
2981: * Implementation of the right shift operator for integral types. Non integral
2982: * Number types throw UnsupportedOperationException.
2983: */
2984: public static Number rightShift(Number left, Number right) {
2985: return NumberMath.rightShift(left, right);
2986: }
2987:
2988: /**
2989: * Implementation of the right shift (unsigned) operator for integral types. Non integral
2990: * Number types throw UnsupportedOperationException.
2991: */
2992: public static Number rightShiftUnsigned(Number left, Number right) {
2993: return NumberMath.rightShiftUnsigned(left, right);
2994: }
2995:
2996: /**
2997: * A helper method so that dynamic dispatch of the writer.write(object) method
2998: * will always use the more efficient Writable.writeTo(writer) mechanism if the
2999: * object implements the Writable interface.
3000: *
3001: * @param self a Writer
3002: * @param writable an object implementing the Writable interface
3003: */
3004: public static void write(Writer self, Writable writable)
3005: throws IOException {
3006: writable.writeTo(self);
3007: }
3008:
3009: /**
3010: * Overloads the left shift operator to provide an append mechanism to add things to a stream
3011: *
3012: * @param self an OutputStream
3013: * @param value a value to append
3014: * @return a Writer
3015: */
3016: public static Writer leftShift(OutputStream self, Object value)
3017: throws IOException {
3018: OutputStreamWriter writer = new FlushingStreamWriter(self);
3019: leftShift(writer, value);
3020: return writer;
3021: }
3022:
3023: /**
3024: * Pipe an inputstream into an outputstream for efficient stream copying.
3025: *
3026: * @param self stream on which to write
3027: * @param in stream to read from
3028: * @return the outputstream itself
3029: * @throws IOException
3030: */
3031: public static OutputStream leftShift(OutputStream self,
3032: InputStream in) throws IOException {
3033: byte[] buf = new byte[1024];
3034: while (true) {
3035: int count = in.read(buf, 0, buf.length);
3036: if (count == -1)
3037: break;
3038: if (count == 0) {
3039: Thread.yield();
3040: continue;
3041: }
3042: self.write(buf, 0, count);
3043: }
3044: self.flush();
3045: return self;
3046: }
3047:
3048: /**
3049: * Overloads the left shift operator to provide an append mechanism to add bytes to a stream
3050: *
3051: * @param self an OutputStream
3052: * @param value a value to append
3053: * @return an OutputStream
3054: */
3055: public static OutputStream leftShift(OutputStream self, byte[] value)
3056: throws IOException {
3057: self.write(value);
3058: self.flush();
3059: return self;
3060: }
3061:
3062: private static boolean sameType(Collection[] cols) {
3063: List all = new LinkedList();
3064: for (int i = 0; i < cols.length; i++) {
3065: all.addAll(cols[i]);
3066: }
3067: if (all.size() == 0)
3068: return true;
3069:
3070: Object first = all.get(0);
3071:
3072: //trying to determine the base class of the collections
3073: //special case for Numbers
3074: Class baseClass;
3075: if (first instanceof Number) {
3076: baseClass = Number.class;
3077: } else {
3078: baseClass = first.getClass();
3079: }
3080:
3081: for (int i = 0; i < cols.length; i++) {
3082: for (Iterator iter = cols[i].iterator(); iter.hasNext();) {
3083: if (!baseClass.isInstance(iter.next())) {
3084: return false;
3085: }
3086: }
3087: }
3088: return true;
3089: }
3090:
3091: // Primitive type array methods
3092: //-------------------------------------------------------------------------
3093:
3094: public static Object getAt(byte[] array, int idx) {
3095: return primitiveArrayGet(array, idx);
3096: }
3097:
3098: public static Object getAt(char[] array, int idx) {
3099: return primitiveArrayGet(array, idx);
3100: }
3101:
3102: public static Object getAt(short[] array, int idx) {
3103: return primitiveArrayGet(array, idx);
3104: }
3105:
3106: public static Object getAt(int[] array, int idx) {
3107: return primitiveArrayGet(array, idx);
3108: }
3109:
3110: public static Object getAt(long[] array, int idx) {
3111: return primitiveArrayGet(array, idx);
3112: }
3113:
3114: public static Object getAt(float[] array, int idx) {
3115: return primitiveArrayGet(array, idx);
3116: }
3117:
3118: public static Object getAt(double[] array, int idx) {
3119: return primitiveArrayGet(array, idx);
3120: }
3121:
3122: public static Object getAt(boolean[] array, int idx) {
3123: return primitiveArrayGet(array, idx);
3124: }
3125:
3126: public static Object getAt(byte[] array, Range range) {
3127: return primitiveArrayGet(array, range);
3128: }
3129:
3130: public static Object getAt(char[] array, Range range) {
3131: return primitiveArrayGet(array, range);
3132: }
3133:
3134: public static Object getAt(short[] array, Range range) {
3135: return primitiveArrayGet(array, range);
3136: }
3137:
3138: public static Object getAt(int[] array, Range range) {
3139: return primitiveArrayGet(array, range);
3140: }
3141:
3142: public static Object getAt(long[] array, Range range) {
3143: return primitiveArrayGet(array, range);
3144: }
3145:
3146: public static Object getAt(float[] array, Range range) {
3147: return primitiveArrayGet(array, range);
3148: }
3149:
3150: public static Object getAt(double[] array, Range range) {
3151: return primitiveArrayGet(array, range);
3152: }
3153:
3154: public static Object getAt(boolean[] array, Range range) {
3155: return primitiveArrayGet(array, range);
3156: }
3157:
3158: public static Object getAt(byte[] array, IntRange range) {
3159: return primitiveArrayGet(array, range);
3160: }
3161:
3162: public static Object getAt(char[] array, IntRange range) {
3163: return primitiveArrayGet(array, range);
3164: }
3165:
3166: public static Object getAt(short[] array, IntRange range) {
3167: return primitiveArrayGet(array, range);
3168: }
3169:
3170: public static Object getAt(int[] array, IntRange range) {
3171: return primitiveArrayGet(array, range);
3172: }
3173:
3174: public static Object getAt(long[] array, IntRange range) {
3175: return primitiveArrayGet(array, range);
3176: }
3177:
3178: public static Object getAt(float[] array, IntRange range) {
3179: return primitiveArrayGet(array, range);
3180: }
3181:
3182: public static Object getAt(double[] array, IntRange range) {
3183: return primitiveArrayGet(array, range);
3184: }
3185:
3186: public static Object getAt(boolean[] array, IntRange range) {
3187: return primitiveArrayGet(array, range);
3188: }
3189:
3190: public static Object getAt(byte[] array, ObjectRange range) {
3191: return primitiveArrayGet(array, range);
3192: }
3193:
3194: public static Object getAt(char[] array, ObjectRange range) {
3195: return primitiveArrayGet(array, range);
3196: }
3197:
3198: public static Object getAt(short[] array, ObjectRange range) {
3199: return primitiveArrayGet(array, range);
3200: }
3201:
3202: public static Object getAt(int[] array, ObjectRange range) {
3203: return primitiveArrayGet(array, range);
3204: }
3205:
3206: public static Object getAt(long[] array, ObjectRange range) {
3207: return primitiveArrayGet(array, range);
3208: }
3209:
3210: public static Object getAt(float[] array, ObjectRange range) {
3211: return primitiveArrayGet(array, range);
3212: }
3213:
3214: public static Object getAt(double[] array, ObjectRange range) {
3215: return primitiveArrayGet(array, range);
3216: }
3217:
3218: public static Object getAt(boolean[] array, ObjectRange range) {
3219: return primitiveArrayGet(array, range);
3220: }
3221:
3222: public static Object getAt(byte[] array, Collection indices) {
3223: return primitiveArrayGet(array, indices);
3224: }
3225:
3226: public static Object getAt(char[] array, Collection indices) {
3227: return primitiveArrayGet(array, indices);
3228: }
3229:
3230: public static Object getAt(short[] array, Collection indices) {
3231: return primitiveArrayGet(array, indices);
3232: }
3233:
3234: public static Object getAt(int[] array, Collection indices) {
3235: return primitiveArrayGet(array, indices);
3236: }
3237:
3238: public static Object getAt(long[] array, Collection indices) {
3239: return primitiveArrayGet(array, indices);
3240: }
3241:
3242: public static Object getAt(float[] array, Collection indices) {
3243: return primitiveArrayGet(array, indices);
3244: }
3245:
3246: public static Object getAt(double[] array, Collection indices) {
3247: return primitiveArrayGet(array, indices);
3248: }
3249:
3250: public static Object getAt(boolean[] array, Collection indices) {
3251: return primitiveArrayGet(array, indices);
3252: }
3253:
3254: public static void putAt(boolean[] array, int idx, Boolean newValue) {
3255: primitiveArrayPut(array, idx, newValue);
3256: }
3257:
3258: public static void putAt(byte[] array, int idx, Object newValue) {
3259: if (!(newValue instanceof Byte)) {
3260: Number n = (Number) newValue;
3261: newValue = new Byte(n.byteValue());
3262: }
3263: primitiveArrayPut(array, idx, newValue);
3264: }
3265:
3266: public static void putAt(char[] array, int idx, Object newValue) {
3267: if (newValue instanceof String) {
3268: String s = (String) newValue;
3269: if (s.length() != 1)
3270: throw new IllegalArgumentException(
3271: "String of length 1 expected but got a bigger one");
3272: char c = s.charAt(0);
3273: newValue = new Character(c);
3274: }
3275: primitiveArrayPut(array, idx, newValue);
3276: }
3277:
3278: public static void putAt(short[] array, int idx, Object newValue) {
3279: if (!(newValue instanceof Short)) {
3280: Number n = (Number) newValue;
3281: newValue = new Short(n.shortValue());
3282: }
3283: primitiveArrayPut(array, idx, newValue);
3284: }
3285:
3286: public static void putAt(int[] array, int idx, Object newValue) {
3287: if (!(newValue instanceof Integer)) {
3288: Number n = (Number) newValue;
3289: newValue = new Integer(n.intValue());
3290: }
3291: primitiveArrayPut(array, idx, newValue);
3292: }
3293:
3294: public static void putAt(long[] array, int idx, Object newValue) {
3295: if (!(newValue instanceof Long)) {
3296: Number n = (Number) newValue;
3297: newValue = new Long(n.longValue());
3298: }
3299: primitiveArrayPut(array, idx, newValue);
3300: }
3301:
3302: public static void putAt(float[] array, int idx, Object newValue) {
3303: if (!(newValue instanceof Float)) {
3304: Number n = (Number) newValue;
3305: newValue = new Float(n.floatValue());
3306: }
3307: primitiveArrayPut(array, idx, newValue);
3308: }
3309:
3310: public static void putAt(double[] array, int idx, Object newValue) {
3311: if (!(newValue instanceof Double)) {
3312: Number n = (Number) newValue;
3313: newValue = new Double(n.doubleValue());
3314: }
3315: primitiveArrayPut(array, idx, newValue);
3316: }
3317:
3318: public static int size(byte[] array) {
3319: return Array.getLength(array);
3320: }
3321:
3322: public static int size(char[] array) {
3323: return Array.getLength(array);
3324: }
3325:
3326: public static int size(short[] array) {
3327: return Array.getLength(array);
3328: }
3329:
3330: public static int size(int[] array) {
3331: return Array.getLength(array);
3332: }
3333:
3334: public static int size(long[] array) {
3335: return Array.getLength(array);
3336: }
3337:
3338: public static int size(float[] array) {
3339: return Array.getLength(array);
3340: }
3341:
3342: public static int size(double[] array) {
3343: return Array.getLength(array);
3344: }
3345:
3346: public static List toList(byte[] array) {
3347: return DefaultTypeTransformation.primitiveArrayToList(array);
3348: }
3349:
3350: public static List toList(char[] array) {
3351: return DefaultTypeTransformation.primitiveArrayToList(array);
3352: }
3353:
3354: public static List toList(short[] array) {
3355: return DefaultTypeTransformation.primitiveArrayToList(array);
3356: }
3357:
3358: public static List toList(int[] array) {
3359: return DefaultTypeTransformation.primitiveArrayToList(array);
3360: }
3361:
3362: public static List toList(long[] array) {
3363: return DefaultTypeTransformation.primitiveArrayToList(array);
3364: }
3365:
3366: public static List toList(float[] array) {
3367: return DefaultTypeTransformation.primitiveArrayToList(array);
3368: }
3369:
3370: public static List toList(double[] array) {
3371: return DefaultTypeTransformation.primitiveArrayToList(array);
3372: }
3373:
3374: private static final char[] tTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
3375: .toCharArray();
3376:
3377: public static Writable encodeBase64(Byte[] data) {
3378: return encodeBase64(DefaultTypeTransformation
3379: .convertToByteArray(data));
3380: }
3381:
3382: /**
3383: * Produce a Writable object which writes the base64 encoding of the byte array
3384: * Calling toString() on the result rerurns the encoding as a String
3385: *
3386: * @param data byte array to be encoded
3387: * @return object which will write the base64 encoding of the byte array
3388: */
3389: public static Writable encodeBase64(final byte[] data) {
3390: return new Writable() {
3391: public Writer writeTo(final Writer writer)
3392: throws IOException {
3393: int charCount = 0;
3394: final int dLimit = (data.length / 3) * 3;
3395:
3396: for (int dIndex = 0; dIndex != dLimit; dIndex += 3) {
3397: int d = ((data[dIndex] & 0XFF) << 16)
3398: | ((data[dIndex + 1] & 0XFF) << 8)
3399: | (data[dIndex + 2] & 0XFF);
3400:
3401: writer.write(tTable[d >> 18]);
3402: writer.write(tTable[(d >> 12) & 0X3F]);
3403: writer.write(tTable[(d >> 6) & 0X3F]);
3404: writer.write(tTable[d & 0X3F]);
3405:
3406: if (++charCount == 18) {
3407: writer.write('\n');
3408: charCount = 0;
3409: }
3410: }
3411:
3412: if (dLimit != data.length) {
3413: int d = (data[dLimit] & 0XFF) << 16;
3414:
3415: if (dLimit + 1 != data.length) {
3416: d |= (data[dLimit + 1] & 0XFF) << 8;
3417: }
3418:
3419: writer.write(tTable[d >> 18]);
3420: writer.write(tTable[(d >> 12) & 0X3F]);
3421: writer
3422: .write((dLimit + 1 < data.length) ? tTable[(d >> 6) & 0X3F]
3423: : '=');
3424: writer.write('=');
3425: }
3426:
3427: return writer;
3428: }
3429:
3430: public String toString() {
3431: StringWriter buffer = new StringWriter();
3432:
3433: try {
3434: writeTo(buffer);
3435: } catch (IOException e) {
3436: throw new StringWriterIOException(e);
3437: }
3438:
3439: return buffer.toString();
3440: }
3441: };
3442: }
3443:
3444: private static final byte[] translateTable = (
3445: //
3446: "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
3447: // \t \n \r
3448: + "\u0042\u0042\u0041\u0041\u0042\u0042\u0041\u0042"
3449: //
3450: + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
3451: //
3452: + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
3453: // sp ! " # $ % & '
3454: + "\u0041\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
3455: // ( ) * + , - . /
3456: + "\u0042\u0042\u0042\u003E\u0042\u0042\u0042\u003F"
3457: // 0 1 2 3 4 5 6 7
3458: + "\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B"
3459: // 8 9 : ; < = > ?
3460: + "\u003C\u003D\u0042\u0042\u0042\u0040\u0042\u0042"
3461: // @ A B C D E F G
3462: + "\u0042\u0000\u0001\u0002\u0003\u0004\u0005\u0006"
3463: // H I J K L M N O
3464: + "\u0007\u0008\t\n\u000B\u000C\r\u000E"
3465: // P Q R S T U V W
3466: + "\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016"
3467: // X Y Z [ \ ] ^ _
3468: + "\u0017\u0018\u0019\u0042\u0042\u0042\u0042\u0042"
3469: // ' a b c d e f g
3470: + "\u0042\u001A\u001B\u001C\u001D\u001E\u001F\u0020"
3471: // h i j k l m n o p
3472: + "\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028"
3473: // p q r s t u v w
3474: + "\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030"
3475: // x y z
3476: + "\u0031\u0032\u0033").getBytes();
3477:
3478: /**
3479: * Decode the Sting from base64 into a byte array
3480: *
3481: * @param value the string to be decoded
3482: * @return the decoded bytes as an array
3483: */
3484: public static byte[] decodeBase64(String value) {
3485: int byteShift = 4;
3486: int tmp = 0;
3487: boolean done = false;
3488: final StringBuffer buffer = new StringBuffer();
3489:
3490: for (int i = 0; i != value.length(); i++) {
3491: final char c = value.charAt(i);
3492: final int sixBit = (c < 123) ? translateTable[c] : 66;
3493:
3494: if (sixBit < 64) {
3495: if (done)
3496: throw new RuntimeException(
3497: "= character not at end of base64 value"); // TODO: change this exception type
3498:
3499: tmp = (tmp << 6) | sixBit;
3500:
3501: if (byteShift-- != 4) {
3502: buffer
3503: .append((char) ((tmp >> (byteShift * 2)) & 0XFF));
3504: }
3505:
3506: } else if (sixBit == 64) {
3507:
3508: byteShift--;
3509: done = true;
3510:
3511: } else if (sixBit == 66) {
3512: // RFC 2045 says that I'm allowed to take the presence of
3513: // these characters as evedence of data corruption
3514: // So I will
3515: throw new RuntimeException(
3516: "bad character in base64 value"); // TODO: change this exception type
3517: }
3518:
3519: if (byteShift == 0)
3520: byteShift = 4;
3521: }
3522:
3523: try {
3524: return buffer.toString().getBytes("ISO-8859-1");
3525: } catch (UnsupportedEncodingException e) {
3526: throw new RuntimeException(
3527: "Base 64 decode produced byte values > 255"); // TODO: change this exception type
3528: }
3529: }
3530:
3531: /**
3532: * Implements the getAt(int) method for primitve type arrays
3533: */
3534: protected static Object primitiveArrayGet(Object array, int idx) {
3535: return Array.get(array, normaliseIndex(idx, Array
3536: .getLength(array)));
3537: }
3538:
3539: /**
3540: * Implements the getAt(Range) method for primitve type arrays
3541: */
3542: protected static List primitiveArrayGet(Object array, Range range) {
3543: List answer = new ArrayList();
3544: for (Iterator iter = range.iterator(); iter.hasNext();) {
3545: int idx = DefaultTypeTransformation.intUnbox(iter.next());
3546: answer.add(primitiveArrayGet(array, idx));
3547: }
3548: return answer;
3549: }
3550:
3551: /**
3552: * Implements the getAt(Collection) method for primitve type arrays
3553: */
3554: protected static List primitiveArrayGet(Object self,
3555: Collection indices) {
3556: List answer = new ArrayList();
3557: for (Iterator iter = indices.iterator(); iter.hasNext();) {
3558: Object value = iter.next();
3559: if (value instanceof Range) {
3560: answer.addAll(primitiveArrayGet(self, (Range) value));
3561: } else if (value instanceof List) {
3562: answer.addAll(primitiveArrayGet(self, (List) value));
3563: } else {
3564: int idx = DefaultTypeTransformation.intUnbox(value);
3565: answer.add(primitiveArrayGet(self, idx));
3566: }
3567: }
3568: return answer;
3569: }
3570:
3571: /**
3572: * Implements the set(int idx) method for primitve type arrays
3573: */
3574: protected static void primitiveArrayPut(Object array, int idx,
3575: Object newValue) {
3576: Array.set(array, normaliseIndex(idx, Array.getLength(array)),
3577: newValue);
3578: }
3579:
3580: // String methods
3581: //-------------------------------------------------------------------------
3582:
3583: /**
3584: * Converts the given string into a Character object
3585: * using the first character in the string
3586: *
3587: * @param self a String
3588: * @return the first Character
3589: */
3590: public static Character toCharacter(String self) {
3591: /** @todo use cache? */
3592: return new Character(self.charAt(0));
3593: }
3594:
3595: /**
3596: * Converts the given string into a Boolean object
3597: * If the trimmed string is "true", "y" or "1" (ignoring case)
3598: * then the result is true othewrwise it is false
3599: *
3600: * @param self a String
3601: * @return The Boolean value
3602: */
3603: public static Boolean toBoolean(String self) {
3604: final String trimmed = self.trim();
3605:
3606: if ("true".equalsIgnoreCase(trimmed)
3607: || "y".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) {
3608: return Boolean.TRUE;
3609: } else {
3610: return Boolean.FALSE;
3611: }
3612: }
3613:
3614: /**
3615: * Tokenize a String
3616: *
3617: * @param self a String
3618: * @param token the delimiter
3619: * @return a List of tokens
3620: */
3621: public static List tokenize(String self, String token) {
3622: return InvokerHelper.asList(new StringTokenizer(self, token));
3623: }
3624:
3625: /**
3626: * Tokenize a String (with a whitespace as delimiter)
3627: *
3628: * @param self a String
3629: * @return a List of tokens
3630: */
3631: public static List tokenize(String self) {
3632: return InvokerHelper.asList(new StringTokenizer(self));
3633: }
3634:
3635: /**
3636: * Appends a String
3637: *
3638: * @param left a String
3639: * @param value any Object
3640: * @return a String
3641: */
3642: public static String plus(String left, Object value) {
3643: return left + toString(value);
3644: }
3645:
3646: /**
3647: * Appends a String
3648: *
3649: * @param value a Number
3650: * @param right a String
3651: * @return a String
3652: */
3653: public static String plus(Number value, String right) {
3654: return toString(value) + right;
3655: }
3656:
3657: /**
3658: * Appends a String
3659: *
3660: * @param left a StringBuffer
3661: * @param value a String
3662: * @return a String
3663: */
3664: public static String plus(StringBuffer left, String value) {
3665: return left + value;
3666: }
3667:
3668: /**
3669: * Remove a part of a String
3670: *
3671: * @param left a String
3672: * @param value a String part to remove
3673: * @return a String minus the part to be removed
3674: */
3675: public static String minus(String left, Object value) {
3676: String text = toString(value);
3677: return left.replaceFirst(text, "");
3678: }
3679:
3680: /**
3681: * Provide an implementation of contains() like Collection to make Strings more polymorphic
3682: * This method is not required on JDK 1.5 onwards
3683: *
3684: * @param self a String
3685: * @param text a String to look for
3686: * @return true if this string contains the given text
3687: */
3688: public static boolean contains(String self, String text) {
3689: int idx = self.indexOf(text);
3690: return idx >= 0;
3691: }
3692:
3693: /**
3694: * Count the number of occurencies of a substring
3695: *
3696: * @param self a String
3697: * @param text a substring
3698: * @return the number of occurrencies of the given string inside this String
3699: */
3700: public static int count(String self, String text) {
3701: int answer = 0;
3702: for (int idx = 0; true; idx++) {
3703: idx = self.indexOf(text, idx);
3704: if (idx >= 0) {
3705: ++answer;
3706: } else {
3707: break;
3708: }
3709: }
3710: return answer;
3711: }
3712:
3713: /**
3714: * This method is called by the ++ operator for the class String.
3715: * It increments the last character in the given string. If the
3716: * character in the string is Character.MAX_VALUE a Character.MIN_VALUE
3717: * will be appended. The empty string is incremented to a string
3718: * consisting of the character Character.MIN_VALUE.
3719: *
3720: * @param self a String
3721: * @return an incremented String
3722: */
3723: public static String next(String self) {
3724: StringBuffer buffer = new StringBuffer(self);
3725: if (buffer.length() == 0) {
3726: buffer.append(Character.MIN_VALUE);
3727: } else {
3728: char last = buffer.charAt(buffer.length() - 1);
3729: if (last == Character.MAX_VALUE) {
3730: buffer.append(Character.MIN_VALUE);
3731: } else {
3732: char next = last;
3733: next++;
3734: buffer.setCharAt(buffer.length() - 1, next);
3735: }
3736: }
3737: return buffer.toString();
3738: }
3739:
3740: /**
3741: * This method is called by the -- operator for the class String.
3742: * It decrements the last character in the given string. If the
3743: * character in the string is Character.MIN_VALUE it will be deleted.
3744: * The empty string can't be decremented.
3745: *
3746: * @param self a String
3747: * @return a String with a decremented digit at the end
3748: */
3749: public static String previous(String self) {
3750: StringBuffer buffer = new StringBuffer(self);
3751: if (buffer.length() == 0)
3752: throw new IllegalArgumentException("the string is empty");
3753: char last = buffer.charAt(buffer.length() - 1);
3754: if (last == Character.MIN_VALUE) {
3755: buffer.deleteCharAt(buffer.length() - 1);
3756: } else {
3757: char next = last;
3758: next--;
3759: buffer.setCharAt(buffer.length() - 1, next);
3760: }
3761: return buffer.toString();
3762: }
3763:
3764: /**
3765: * Executes the given string as a command line process. For more control
3766: * over the process mechanism in JDK 1.5 you can use java.lang.ProcessBuilder.
3767: *
3768: * @param self a command line String
3769: * @return the Process which has just started for this command line string
3770: */
3771: public static Process execute(String self) throws IOException {
3772: return Runtime.getRuntime().exec(self);
3773: }
3774:
3775: /**
3776: * Executes the command specified by the <code>String</code> array that is the parameter.
3777: * The first item in the array is the command the others are the parameters. For more
3778: * control over the process mechanism in JDK 1.5 you can use
3779: * <code>java.lang.ProcessBuilder</code>.
3780: *
3781: * @param commandArray an array of <code>String<code> containing the command name and
3782: * parameters as separate items in the array.
3783: * @return the Process which has just started for this command line string.
3784: */
3785: public static Process execute(String[] commandArray)
3786: throws IOException {
3787: return Runtime.getRuntime().exec(commandArray);
3788: }
3789:
3790: /**
3791: * Executes the command specified by the <code>self</code> with environments <code>envp</code>
3792: * under the working directory <code>dir</code>.
3793: * For more control over the process mechanism in JDK 1.5 you can use <code>java.lang.ProcessBuilder</code>.
3794: *
3795: * @param self a command line String to be executed.
3796: * @param envp an array of Strings, each element of which
3797: * has environment variable settings in the format
3798: * <i>name</i>=<i>value</i>, or
3799: * <tt>null</tt> if the subprocess should inherit
3800: * the environment of the current process.
3801: * @param dir the working directory of the subprocess, or
3802: * <tt>null</tt> if the subprocess should inherit
3803: * the working directory of the current process.
3804: * @return the Process which has just started for this command line string.
3805: */
3806: public static Process execute(String self, final String[] envp,
3807: File dir) throws IOException {
3808: return Runtime.getRuntime().exec(self, envp, dir);
3809: }
3810:
3811: /**
3812: * Executes the command specified by the <code>String</code> list that is the parameter.
3813: * The first item in the array is the command the others are the parameters. All entries
3814: * must be <code>String</code>s. For more control over the process mechanism in JDK 1.5 you
3815: * can use <code>java.lang.ProcessBuilder</code>.
3816: *
3817: * @param commandList a list of <code>String<code> containing the command name and
3818: * parameters as separate items in the list.
3819: * @return the Process which has just started for this command line string.
3820: */
3821: public static Process execute(List commandList) throws IOException {
3822: final String[] commandArray = new String[commandList.size()];
3823: Iterator it = commandList.iterator();
3824: for (int i = 0; it.hasNext(); ++i) {
3825: commandArray[i] = it.next().toString();
3826: }
3827: return execute(commandArray);
3828: }
3829:
3830: /**
3831: * Executes the command specified by the <code>self</code> with environments <code>envp</code>
3832: * under the working directory <code>dir</code>.
3833: * For more control over the process mechanism in JDK 1.5 you can use <code>java.lang.ProcessBuilder</code>.
3834: *
3835: * @param self a command line String to be executed.
3836: * @param envp a List of Strings, each member of which
3837: * has environment variable settings in the format
3838: * <i>name</i>=<i>value</i>, or
3839: * <tt>null</tt> if the subprocess should inherit
3840: * the environment of the current process.
3841: * @param dir the working directory of the subprocess, or
3842: * <tt>null</tt> if the subprocess should inherit
3843: * the working directory of the current process.
3844: * @return the Process which has just started for this command line string.
3845: */
3846: public static Process execute(String self, List envp, File dir)
3847: throws IOException {
3848: if (envp == null) {
3849: return execute(self, (String[]) null, dir);
3850: }
3851: String[] commandArray = new String[envp.size()];
3852: if (envp != null) {
3853: Iterator it = envp.iterator();
3854: for (int i = 0; it.hasNext(); ++i) {
3855: commandArray[i] = it.next().toString();
3856: }
3857: } else {
3858: commandArray = null;
3859: }
3860: return execute(self, commandArray, dir);
3861: }
3862:
3863: /**
3864: * Repeat a String a certain number of times
3865: *
3866: * @param self a String to be repeated
3867: * @param factor the number of times the String should be repeated
3868: * @return a String composed of a repeatition
3869: * @throws IllegalArgumentException if the number of repeatition is < 0
3870: */
3871: public static String multiply(String self, Number factor) {
3872: int size = factor.intValue();
3873: if (size == 0)
3874: return "";
3875: else if (size < 0) {
3876: throw new IllegalArgumentException(
3877: "multiply() should be called with a number of 0 or greater not: "
3878: + size);
3879: }
3880: StringBuffer answer = new StringBuffer(self);
3881: for (int i = 1; i < size; i++) {
3882: answer.append(self);
3883: }
3884: return answer.toString();
3885: }
3886:
3887: /**
3888: * Returns the string representation of the given map with bracket boundaries.
3889: *
3890: * @param self a Map
3891: * @return the string representation
3892: */
3893: public static String toString(Map self) {
3894: return toMapString(self);
3895: }
3896:
3897: /**
3898: * Returns the string representation of the given map with bracket boundaries.
3899: *
3900: * @param self a Map
3901: * @return the string representation
3902: */
3903: public static String toMapString(Map self) {
3904: return (self == null) ? "null" : InvokerHelper
3905: .toMapString(self);
3906: }
3907:
3908: /**
3909: * Returns the string representation of the given collection with the bracket boundaries.
3910: *
3911: * @param self a Collection
3912: * @return the string representation
3913: */
3914: public static String toString(Collection self) {
3915: return toListString(self);
3916: }
3917:
3918: /**
3919: * Returns the string representation of the given collection with the bracket boundaries.
3920: *
3921: * @param self a Collection
3922: * @return the string representation
3923: */
3924: public static String toListString(Collection self) {
3925: return (self == null) ? "null" : InvokerHelper
3926: .toListString(self);
3927: }
3928:
3929: /**
3930: * Returns the string representation of the given array with the brace boundaries.
3931: *
3932: * @param self an Object[]
3933: * @return the string representation
3934: */
3935: public static String toString(Object[] self) {
3936: return toArrayString(self);
3937: }
3938:
3939: /**
3940: * Returns the string representation of the given array with the brace boundaries.
3941: *
3942: * @param self an Object[]
3943: * @return the string representation
3944: */
3945: public static String toArrayString(Object[] self) {
3946: return (self == null) ? "null" : InvokerHelper
3947: .toArrayString(self);
3948: }
3949:
3950: protected static String toString(Object value) {
3951: if (value instanceof Map)
3952: return toMapString((Map) value);
3953: else if (value instanceof Collection)
3954: return toListString((Collection) value);
3955: else if (value instanceof Object[])
3956: return toArrayString((Object[]) value);
3957: return (value == null) ? "null" : value.toString();
3958: }
3959:
3960: // Number based methods
3961: //-------------------------------------------------------------------------
3962:
3963: /**
3964: * Increment a Character by one
3965: *
3966: * @param self a Character
3967: * @return an incremented Number
3968: */
3969: public static Number next(Character self) {
3970: return plus(self, ONE);
3971: }
3972:
3973: /**
3974: * Increment a Number by one
3975: *
3976: * @param self a Number
3977: * @return an incremented Number
3978: */
3979: public static Number next(Number self) {
3980: return plus(self, ONE);
3981: }
3982:
3983: /**
3984: * Decrement a Character by one
3985: *
3986: * @param self a Character
3987: * @return a decremented Number
3988: */
3989: public static Number previous(Character self) {
3990: return minus(self, ONE);
3991: }
3992:
3993: /**
3994: * Decrement a Number by one
3995: *
3996: * @param self a Number
3997: * @return a decremented Number
3998: */
3999: public static Number previous(Number self) {
4000: return minus(self, ONE);
4001: }
4002:
4003: /**
4004: * Add a Character and a Number
4005: *
4006: * @param left a Character
4007: * @param right a Number
4008: * @return the addition of the Character and the Number
4009: */
4010: public static Number plus(Character left, Number right) {
4011: return plus(new Integer(left.charValue()), right);
4012: }
4013:
4014: /**
4015: * Add a Number and a Character
4016: *
4017: * @param left a Number
4018: * @param right a Character
4019: * @return the addition of the Character and the Number
4020: */
4021: public static Number plus(Number left, Character right) {
4022: return plus(left, new Integer(right.charValue()));
4023: }
4024:
4025: /**
4026: * Add two Characters
4027: *
4028: * @param left a Character
4029: * @param right a Character
4030: * @return the addition of both Characters
4031: */
4032: public static Number plus(Character left, Character right) {
4033: return plus(new Integer(left.charValue()), right);
4034: }
4035:
4036: /**
4037: * Add two numbers and return the result.
4038: *
4039: * @param left a Number
4040: * @param right another Number to add
4041: * @return the addition of both Numbers
4042: */
4043: public static Number plus(Number left, Number right) {
4044: return NumberMath.add(left, right);
4045: }
4046:
4047: /**
4048: * Compare a Character and a Number
4049: *
4050: * @param left a Character
4051: * @param right a Number
4052: * @return the result of the comparison
4053: */
4054: public static int compareTo(Character left, Number right) {
4055: return compareTo(new Integer(left.charValue()), right);
4056: }
4057:
4058: /**
4059: * Compare a Number and a Character
4060: *
4061: * @param left a Number
4062: * @param right a Character
4063: * @return the result of the comparison
4064: */
4065: public static int compareTo(Number left, Character right) {
4066: return compareTo(left, new Integer(right.charValue()));
4067: }
4068:
4069: /**
4070: * Compare two Characters
4071: *
4072: * @param left a Character
4073: * @param right a Character
4074: * @return the result of the comparison
4075: */
4076: public static int compareTo(Character left, Character right) {
4077: return compareTo(new Integer(left.charValue()), right);
4078: }
4079:
4080: /**
4081: * Compare two Numbers
4082: *
4083: * @param left a Number
4084: * @param right another Number to compare to
4085: * @return the comparision of both numbers
4086: */
4087: public static int compareTo(Number left, Number right) {
4088: /** @todo maybe a double dispatch thing to handle new large numbers? */
4089: return NumberMath.compareTo(left, right);
4090: }
4091:
4092: /**
4093: * Subtract a Number from a Character
4094: *
4095: * @param left a Character
4096: * @param right a Number
4097: * @return the addition of the Character and the Number
4098: */
4099: public static Number minus(Character left, Number right) {
4100: return minus(new Integer(left.charValue()), right);
4101: }
4102:
4103: /**
4104: * Subtract a Character from a Number
4105: *
4106: * @param left a Number
4107: * @param right a Character
4108: * @return the addition of the Character and the Number
4109: */
4110: public static Number minus(Number left, Character right) {
4111: return minus(left, new Integer(right.charValue()));
4112: }
4113:
4114: /**
4115: * Subtraction two Characters
4116: *
4117: * @param left a Character
4118: * @param right a Character
4119: * @return the addition of both Characters
4120: */
4121: public static Number minus(Character left, Character right) {
4122: return minus(new Integer(left.charValue()), right);
4123: }
4124:
4125: /**
4126: * Substraction of two Numbers
4127: *
4128: * @param left a Number
4129: * @param right another Number to substract to the first one
4130: * @return the substraction
4131: */
4132: public static Number minus(Number left, Number right) {
4133: return NumberMath.subtract(left, right);
4134: }
4135:
4136: /**
4137: * Multiply a Character by a Number
4138: *
4139: * @param left a Character
4140: * @param right a Number
4141: * @return the multiplication of both
4142: */
4143: public static Number multiply(Character left, Number right) {
4144: return multiply(new Integer(left.charValue()), right);
4145: }
4146:
4147: /**
4148: * Multiply a Number by a Character
4149: *
4150: * @param left a Number
4151: * @param right a Character
4152: * @return the multiplication of both
4153: */
4154: public static Number multiply(Number left, Character right) {
4155: return multiply(left, new Integer(right.charValue()));
4156: }
4157:
4158: /**
4159: * Multiply two Characters
4160: *
4161: * @param left a Character
4162: * @param right another Character
4163: * @return the multiplication of both
4164: */
4165: public static Number multiply(Character left, Character right) {
4166: return multiply(new Integer(left.charValue()), right);
4167: }
4168:
4169: /**
4170: * Multiply two Numbers
4171: *
4172: * @param left a Number
4173: * @param right another Number
4174: * @return the multiplication of both
4175: */
4176: //Note: This method is NOT called if left AND right are both BigIntegers or BigDecimals because
4177: //those classes implement a method with a better exact match.
4178: public static Number multiply(Number left, Number right) {
4179: return NumberMath.multiply(left, right);
4180: }
4181:
4182: /**
4183: * Multiply a BigDecimal and a Double.
4184: * Note: This method was added to enforce the Groovy rule of
4185: * BigDecimal*Double == Double. Without this method, the
4186: * multiply(BigDecimal) method in BigDecimal would respond
4187: * and return a BigDecimal instead. Since BigDecimal is prefered
4188: * over Number, the Number*Number method is not choosen as in older
4189: * versions of Groovy.
4190: *
4191: * @param left a BigDecimal
4192: * @param right a Double
4193: * @return the multiplication of both
4194: */
4195: public static Number multiply(BigDecimal left, Double right) {
4196: return NumberMath.multiply(left, right);
4197: }
4198:
4199: /**
4200: * Multiply a BigDecimal and a BigInteger.
4201: * Note: This method was added to enforce the Groovy rule of
4202: * BigDecimal*long == long. Without this method, the
4203: * multiply(BigDecimal) method in BigDecimal would respond
4204: * and return a BigDecimal instead. Since BigDecimal is prefered
4205: * over Number, the Number*Number method is not choosen as in older
4206: * versions of Groovy. Biginteger is the fallback for all integer
4207: * types in Groovy
4208: *
4209: * @param left a BigDecimal
4210: * @param right a BigInteger
4211: * @return the multiplication of both
4212: */
4213: public static Number multiply(BigDecimal left, BigInteger right) {
4214: return NumberMath.multiply(left, right);
4215: }
4216:
4217: /**
4218: * Power of a Number to a certain exponent
4219: *
4220: * @param self a Number
4221: * @param exponent a Number exponent
4222: * @return a Number to the power of a certain exponent
4223: */
4224: public static Number power(Number self, Number exponent) {
4225: double base, exp, answer;
4226: base = self.doubleValue();
4227: exp = exponent.doubleValue();
4228:
4229: answer = Math.pow(base, exp);
4230: if ((double) ((int) answer) == answer) {
4231: return new Integer((int) answer);
4232: } else if ((double) ((long) answer) == answer) {
4233: return new Long((long) answer);
4234: } else {
4235: return new Double(answer);
4236: }
4237: }
4238:
4239: /**
4240: * Divide a Character by a Number
4241: *
4242: * @param left a Character
4243: * @param right a Number
4244: * @return the multiplication of both
4245: */
4246: public static Number div(Character left, Number right) {
4247: return div(new Integer(left.charValue()), right);
4248: }
4249:
4250: /**
4251: * Divide a Number by a Character
4252: *
4253: * @param left a Number
4254: * @param right a Character
4255: * @return the multiplication of both
4256: */
4257: public static Number div(Number left, Character right) {
4258: return div(left, new Integer(right.charValue()));
4259: }
4260:
4261: /**
4262: * Divide two Characters
4263: *
4264: * @param left a Character
4265: * @param right another Character
4266: * @return the multiplication of both
4267: */
4268: public static Number div(Character left, Character right) {
4269: return div(new Integer(left.charValue()), right);
4270: }
4271:
4272: /**
4273: * Divide two Numbers
4274: *
4275: * @param left a Number
4276: * @param right another Number
4277: * @return a Number resulting of the divide operation
4278: */
4279: //Method name changed from 'divide' to avoid collision with BigInteger method that has
4280: //different semantics. We want a BigDecimal result rather than a BigInteger.
4281: public static Number div(Number left, Number right) {
4282: return NumberMath.divide(left, right);
4283: }
4284:
4285: /**
4286: * Integer Divide a Character by a Number
4287: *
4288: * @param left a Character
4289: * @param right a Number
4290: * @return the integer division of both
4291: */
4292: public static Number intdiv(Character left, Number right) {
4293: return intdiv(new Integer(left.charValue()), right);
4294: }
4295:
4296: /**
4297: * Integer Divide a Number by a Character
4298: *
4299: * @param left a Number
4300: * @param right a Character
4301: * @return the integer division of both
4302: */
4303: public static Number intdiv(Number left, Character right) {
4304: return intdiv(left, new Integer(right.charValue()));
4305: }
4306:
4307: /**
4308: * Integer Divide two Characters
4309: *
4310: * @param left a Character
4311: * @param right another Character
4312: * @return the integer division of both
4313: */
4314: public static Number intdiv(Character left, Character right) {
4315: return intdiv(new Integer(left.charValue()), right);
4316: }
4317:
4318: /**
4319: * Integer Divide two Numbers
4320: *
4321: * @param left a Number
4322: * @param right another Number
4323: * @return a Number (an Integer) resulting of the integer division operation
4324: */
4325: public static Number intdiv(Number left, Number right) {
4326: return NumberMath.intdiv(left, right);
4327: }
4328:
4329: /**
4330: * Bitwise OR together two numbers
4331: *
4332: * @param left a Number
4333: * @param right another Number to bitwise OR
4334: * @return the bitwise OR of both Numbers
4335: */
4336: public static Number or(Number left, Number right) {
4337: return NumberMath.or(left, right);
4338: }
4339:
4340: /**
4341: * Bitwise AND together two Numbers
4342: *
4343: * @param left a Number
4344: * @param right another Number to bitwse AND
4345: * @return the bitwise AND of both Numbers
4346: */
4347: public static Number and(Number left, Number right) {
4348: return NumberMath.and(left, right);
4349: }
4350:
4351: /**
4352: * Bitwise XOR together two Numbers
4353: *
4354: * @param left a Number
4355: * @param right another Number to bitwse XOR
4356: * @return the bitwise XOR of both Numbers
4357: */
4358: public static Number xor(Number left, Number right) {
4359: return NumberMath.xor(left, right);
4360: }
4361:
4362: /**
4363: * Performs a division modulus operation
4364: *
4365: * @param left a Number
4366: * @param right another Number to mod
4367: * @return the modulus result
4368: */
4369: public static Number mod(Number left, Number right) {
4370: return NumberMath.mod(left, right);
4371: }
4372:
4373: /**
4374: * Negates the number
4375: *
4376: * @param left a Number
4377: * @return the negation of the number
4378: */
4379: public static Number negate(Number left) {
4380: return NumberMath.negate(left);
4381: }
4382:
4383: /**
4384: * Iterates a number of times
4385: *
4386: * @param self a Number
4387: * @param closure the closure to call a number of times
4388: */
4389: public static void times(Number self, Closure closure) {
4390: for (int i = 0, size = self.intValue(); i < size; i++) {
4391: closure.call(new Integer(i));
4392: if (closure.getDirective() == Closure.DONE) {
4393: break;
4394: }
4395: }
4396: }
4397:
4398: /**
4399: * Iterates from this number up to the given number
4400: *
4401: * @param self a Number
4402: * @param to another Number to go up to
4403: * @param closure the closure to call
4404: */
4405: public static void upto(Number self, Number to, Closure closure) {
4406: int self1 = self.intValue();
4407: int to1 = to.intValue();
4408: if (self1 <= to1) {
4409: for (int i = self1; i <= to1; i++) {
4410: closure.call(new Integer(i));
4411: }
4412: } else
4413: throw new GroovyRuntimeException("Infinite loop in " + self
4414: + ".upto(" + to + ")");
4415: }
4416:
4417: public static void upto(long self, Number to, Closure closure) {
4418: long to1 = to.longValue();
4419: if (self <= to1) {
4420: for (long i = self; i <= to1; i++) {
4421: closure.call(new Long(i));
4422: }
4423: } else
4424: throw new GroovyRuntimeException("Infinite loop in " + self
4425: + ".upto(" + to + ")");
4426: }
4427:
4428: public static void upto(Long self, Number to, Closure closure) {
4429: long self1 = self.longValue();
4430: long to1 = to.longValue();
4431: if (self1 <= to1) {
4432: for (long i = self1; i <= to1; i++) {
4433: closure.call(new Long(i));
4434: }
4435: } else
4436: throw new GroovyRuntimeException("Infinite loop in " + self
4437: + ".upto(" + to + ")");
4438: }
4439:
4440: public static void upto(float self, Number to, Closure closure) {
4441: float to1 = to.floatValue();
4442: if (self <= to1) {
4443: for (float i = self; i <= to1; i++) {
4444: closure.call(new Float(i));
4445: }
4446: } else
4447: throw new GroovyRuntimeException("Infinite loop in " + self
4448: + ".upto(" + to + ")");
4449: }
4450:
4451: public static void upto(Float self, Number to, Closure closure) {
4452: float self1 = self.floatValue();
4453: float to1 = to.floatValue();
4454: if (self1 <= to1) {
4455: for (float i = self1; i <= to1; i++) {
4456: closure.call(new Float(i));
4457: }
4458: } else
4459: throw new GroovyRuntimeException("Infinite loop in " + self
4460: + ".upto(" + to + ")");
4461: }
4462:
4463: public static void upto(Double self, Number to, Closure closure) {
4464: double self1 = self.doubleValue();
4465: double to1 = to.doubleValue();
4466: if (self1 <= to1) {
4467: for (double i = self1; i <= to1; i++) {
4468: closure.call(new Double(i));
4469: }
4470: } else
4471: throw new GroovyRuntimeException("Infinite loop in " + self
4472: + ".upto(" + to + ")");
4473: }
4474:
4475: public static void upto(BigInteger self, Number to, Closure closure) {
4476: if (to instanceof BigDecimal) {
4477: final BigDecimal one = new BigDecimal("1.0");
4478: BigDecimal self1 = new BigDecimal(self);
4479: BigDecimal to1 = (BigDecimal) to;
4480: if (self1.compareTo(to1) <= 0) {
4481: for (BigDecimal i = self1; i.compareTo(to1) <= 0; i = i
4482: .add(one)) {
4483: closure.call(i);
4484: }
4485: } else
4486: throw new GroovyRuntimeException("Infinite loop in "
4487: + self + ".upto(" + to + ")");
4488: } else if (to instanceof BigInteger) {
4489: final BigInteger one = new BigInteger("1");
4490: BigInteger to1 = (BigInteger) to;
4491: if (self.compareTo(to1) <= 0) {
4492: for (BigInteger i = self; i.compareTo(to1) <= 0; i = i
4493: .add(one)) {
4494: closure.call(i);
4495: }
4496: } else
4497: throw new GroovyRuntimeException("Infinite loop in "
4498: + self + ".upto(" + to + ")");
4499: } else {
4500: final BigInteger one = new BigInteger("1");
4501: BigInteger to1 = new BigInteger("" + to);
4502: if (self.compareTo(to1) <= 0) {
4503: for (BigInteger i = self; i.compareTo(to1) <= 0; i = i
4504: .add(one)) {
4505: closure.call(i);
4506: }
4507: } else
4508: throw new GroovyRuntimeException("Infinite loop in "
4509: + self + ".upto(" + to + ")");
4510: }
4511: }
4512:
4513: public static void upto(BigDecimal self, Number to, Closure closure) {
4514: final BigDecimal one = new BigDecimal("1.0");
4515: if (to instanceof BigDecimal) {
4516: BigDecimal to1 = (BigDecimal) to;
4517: if (self.compareTo(to1) <= 0) {
4518: for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i
4519: .add(one)) {
4520: closure.call(i);
4521: }
4522: } else
4523: throw new GroovyRuntimeException("Infinite loop in "
4524: + self + ".upto(" + to + ")");
4525: } else if (to instanceof BigInteger) {
4526: BigDecimal to1 = new BigDecimal((BigInteger) to);
4527: if (self.compareTo(to1) <= 0) {
4528: for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i
4529: .add(one)) {
4530: closure.call(i);
4531: }
4532: } else
4533: throw new GroovyRuntimeException("Infinite loop in "
4534: + self + ".upto(" + to + ")");
4535: } else {
4536: BigDecimal to1 = new BigDecimal("" + to);
4537: if (self.compareTo(to1) <= 0) {
4538: for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i
4539: .add(one)) {
4540: closure.call(i);
4541: }
4542: } else
4543: throw new GroovyRuntimeException("Infinite loop in "
4544: + self + ".upto(" + to + ")");
4545: }
4546: }
4547:
4548: /**
4549: * Iterates from this number down to the given number
4550: *
4551: * @param self a Number
4552: * @param to another Number to go down to
4553: * @param closure the closure to call
4554: */
4555: public static void downto(Number self, Number to, Closure closure) {
4556: int self1 = self.intValue();
4557: int to1 = to.intValue();
4558: if (self1 >= to1) {
4559: for (int i = self1; i >= to1; i--) {
4560: closure.call(new Integer(i));
4561: }
4562: } else
4563: throw new GroovyRuntimeException("Infinite loop in " + self
4564: + ".downto(" + to + ")");
4565: }
4566:
4567: public static void downto(long self, Number to, Closure closure) {
4568: long to1 = to.longValue();
4569: if (self >= to1) {
4570: for (long i = self; i >= to1; i--) {
4571: closure.call(new Long(i));
4572: }
4573: } else
4574: throw new GroovyRuntimeException("Infinite loop in " + self
4575: + ".downto(" + to + ")");
4576: }
4577:
4578: public static void downto(Long self, Number to, Closure closure) {
4579: long self1 = self.longValue();
4580: long to1 = to.longValue();
4581: if (self1 >= to1) {
4582: for (long i = self1; i >= to1; i--) {
4583: closure.call(new Long(i));
4584: }
4585: } else
4586: throw new GroovyRuntimeException("Infinite loop in " + self
4587: + ".downto(" + to + ")");
4588: }
4589:
4590: public static void downto(float self, Number to, Closure closure) {
4591: float to1 = to.floatValue();
4592: if (self >= to1) {
4593: for (float i = self; i >= to1; i--) {
4594: closure.call(new Float(i));
4595: }
4596: } else
4597: throw new GroovyRuntimeException("Infinite loop in " + self
4598: + ".downto(" + to + ")");
4599: }
4600:
4601: public static void downto(Float self, Number to, Closure closure) {
4602: float self1 = self.floatValue();
4603: float to1 = to.floatValue();
4604: if (self1 >= to1) {
4605: for (float i = self1; i >= to1; i--) {
4606: closure.call(new Float(i));
4607: }
4608: } else
4609: throw new GroovyRuntimeException("Infinite loop in " + self
4610: + ".downto(" + to + ")");
4611: }
4612:
4613: public static void downto(double self, Number to, Closure closure) {
4614: double to1 = to.doubleValue();
4615: if (self >= to1) {
4616: for (double i = self; i >= to1; i--) {
4617: closure.call(new Double(i));
4618: }
4619: } else
4620: throw new GroovyRuntimeException("Infinite loop in " + self
4621: + ".downto(" + to + ")");
4622: }
4623:
4624: public static void downto(Double self, Number to, Closure closure) {
4625: double self1 = self.doubleValue();
4626: double to1 = to.doubleValue();
4627: if (self1 >= to1) {
4628: for (double i = self1; i >= to1; i--) {
4629: closure.call(new Double(i));
4630: }
4631: } else
4632: throw new GroovyRuntimeException("Infinite loop in " + self
4633: + ".downto(" + to + ")");
4634: }
4635:
4636: public static void downto(BigInteger self, Number to,
4637: Closure closure) {
4638: if (to instanceof BigDecimal) {
4639: final BigDecimal one = new BigDecimal("1.0");
4640: final BigDecimal to1 = (BigDecimal) to;
4641: final BigDecimal selfD = new BigDecimal(self);
4642: if (selfD.compareTo(to1) >= 0) {
4643: for (BigDecimal i = selfD; i.compareTo(to1) >= 0; i = i
4644: .subtract(one)) {
4645: closure.call(i.toBigInteger());
4646: }
4647: } else
4648: throw new GroovyRuntimeException("Infinite loop in "
4649: + self + ".downto(" + to + ")");
4650: } else if (to instanceof BigInteger) {
4651: final BigInteger one = new BigInteger("1");
4652: final BigInteger to1 = (BigInteger) to;
4653: if (self.compareTo(to1) >= 0) {
4654: for (BigInteger i = self; i.compareTo(to1) >= 0; i = i
4655: .subtract(one)) {
4656: closure.call(i);
4657: }
4658: } else
4659: throw new GroovyRuntimeException("Infinite loop in "
4660: + self + ".downto(" + to + ")");
4661: } else {
4662: final BigInteger one = new BigInteger("1");
4663: final BigInteger to1 = new BigInteger("" + to);
4664: if (self.compareTo(to1) >= 0) {
4665: for (BigInteger i = self; i.compareTo(to1) >= 0; i = i
4666: .subtract(one)) {
4667: closure.call(i);
4668: }
4669: } else
4670: throw new GroovyRuntimeException("Infinite loop in "
4671: + self + ".downto(" + to + ")");
4672: }
4673: }
4674:
4675: public static void downto(BigDecimal self, Number to,
4676: Closure closure) {
4677: final BigDecimal one = new BigDecimal("1.0");
4678: if (to instanceof BigDecimal) {
4679: BigDecimal to1 = (BigDecimal) to;
4680: if (self.compareTo(to1) >= 0) {
4681: for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i
4682: .subtract(one)) {
4683: closure.call(i);
4684: }
4685: } else
4686: throw new GroovyRuntimeException("Infinite loop in "
4687: + self + ".downto(" + to + ")");
4688: } else if (to instanceof BigInteger) {
4689: BigDecimal to1 = new BigDecimal((BigInteger) to);
4690: if (self.compareTo(to1) >= 0) {
4691: for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i
4692: .subtract(one)) {
4693: closure.call(i);
4694: }
4695: } else
4696: throw new GroovyRuntimeException("Infinite loop in "
4697: + self + ".downto(" + to + ")");
4698: } else {
4699: BigDecimal to1 = new BigDecimal("" + to);
4700: if (self.compareTo(to1) >= 0) {
4701: for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i
4702: .subtract(one)) {
4703: closure.call(i);
4704: }
4705: } else
4706: throw new GroovyRuntimeException("Infinite loop in "
4707: + self + ".downto(" + to + ")");
4708: }
4709: }
4710:
4711: /**
4712: * Iterates from this number up to the given number using a step increment
4713: *
4714: * @param self a Number to start with
4715: * @param to a Number to go up to
4716: * @param stepNumber a Number representing the step increment
4717: * @param closure the closure to call
4718: */
4719: public static void step(Number self, Number to, Number stepNumber,
4720: Closure closure) {
4721: if (self instanceof BigDecimal || to instanceof BigDecimal
4722: || stepNumber instanceof BigDecimal) {
4723: final BigDecimal zero = new BigDecimal("0.0");
4724: BigDecimal self1 = (self instanceof BigDecimal) ? (BigDecimal) self
4725: : new BigDecimal("" + self);
4726: BigDecimal to1 = (to instanceof BigDecimal) ? (BigDecimal) to
4727: : new BigDecimal("" + to);
4728: BigDecimal stepNumber1 = (stepNumber instanceof BigDecimal) ? (BigDecimal) stepNumber
4729: : new BigDecimal("" + stepNumber);
4730: if (stepNumber1.compareTo(zero) > 0
4731: && to1.compareTo(self1) > 0) {
4732: for (BigDecimal i = self1; i.compareTo(to1) < 0; i = i
4733: .add(stepNumber1)) {
4734: closure.call(i);
4735: }
4736: } else if (stepNumber1.compareTo(zero) < 0
4737: && to1.compareTo(self1) < 0) {
4738: for (BigDecimal i = self1; i.compareTo(to1) > 0; i = i
4739: .add(stepNumber1)) {
4740: closure.call(i);
4741: }
4742: } else
4743: throw new GroovyRuntimeException("Infinite loop in "
4744: + self1 + ".step(" + to1 + ", " + stepNumber1
4745: + ")");
4746: } else if (self instanceof BigInteger
4747: || to instanceof BigInteger
4748: || stepNumber instanceof BigInteger) {
4749: final BigInteger zero = new BigInteger("0");
4750: BigInteger self1 = (self instanceof BigInteger) ? (BigInteger) self
4751: : new BigInteger("" + self);
4752: BigInteger to1 = (to instanceof BigInteger) ? (BigInteger) to
4753: : new BigInteger("" + to);
4754: BigInteger stepNumber1 = (stepNumber instanceof BigInteger) ? (BigInteger) stepNumber
4755: : new BigInteger("" + stepNumber);
4756: if (stepNumber1.compareTo(zero) > 0
4757: && to1.compareTo(self1) > 0) {
4758: for (BigInteger i = self1; i.compareTo(to1) < 0; i = i
4759: .add(stepNumber1)) {
4760: closure.call(i);
4761: }
4762: } else if (stepNumber1.compareTo(zero) < 0
4763: && to1.compareTo(self1) < 0) {
4764: for (BigInteger i = self1; i.compareTo(to1) > 0; i = i
4765: .add(stepNumber1)) {
4766: closure.call(i);
4767: }
4768: } else
4769: throw new GroovyRuntimeException("Infinite loop in "
4770: + self1 + ".step(" + to1 + ", " + stepNumber1
4771: + ")");
4772: } else {
4773: int self1 = self.intValue();
4774: int to1 = to.intValue();
4775: int stepNumber1 = stepNumber.intValue();
4776: if (stepNumber1 > 0 && to1 > self1) {
4777: for (int i = self1; i < to1; i += stepNumber1) {
4778: closure.call(new Integer(i));
4779: }
4780: } else if (stepNumber1 < 0 && to1 < self1) {
4781: for (int i = self1; i > to1; i += stepNumber1) {
4782: closure.call(new Integer(i));
4783: }
4784: } else
4785: throw new GroovyRuntimeException("Infinite loop in "
4786: + self1 + ".step(" + to1 + ", " + stepNumber1
4787: + ")");
4788: }
4789: }
4790:
4791: /**
4792: * Get the absolute value
4793: *
4794: * @param number a Number
4795: * @return the absolute value of that Number
4796: */
4797: //Note: This method is NOT called if number is a BigInteger or BigDecimal because
4798: //those classes implement a method with a better exact match.
4799: public static int abs(Number number) {
4800: return Math.abs(number.intValue());
4801: }
4802:
4803: /**
4804: * Get the absolute value
4805: *
4806: * @param number a Long
4807: * @return the absolute value of that Long
4808: */
4809: public static long abs(Long number) {
4810: return Math.abs(number.longValue());
4811: }
4812:
4813: /**
4814: * Get the absolute value
4815: *
4816: * @param number a Float
4817: * @return the absolute value of that Float
4818: */
4819: public static float abs(Float number) {
4820: return Math.abs(number.floatValue());
4821: }
4822:
4823: /**
4824: * Get the absolute value
4825: *
4826: * @param number a Double
4827: * @return the absolute value of that Double
4828: */
4829: public static double abs(Double number) {
4830: return Math.abs(number.doubleValue());
4831: }
4832:
4833: /**
4834: * Get the absolute value
4835: *
4836: * @param number a Float
4837: * @return the absolute value of that Float
4838: */
4839: public static int round(Float number) {
4840: return Math.round(number.floatValue());
4841: }
4842:
4843: /**
4844: * Round the value
4845: *
4846: * @param number a Double
4847: * @return the absolute value of that Double
4848: */
4849: public static long round(Double number) {
4850: return Math.round(number.doubleValue());
4851: }
4852:
4853: /**
4854: * Parse a String into an Integer
4855: *
4856: * @param self a String
4857: * @return an Integer
4858: */
4859: public static Integer toInteger(String self) {
4860: return Integer.valueOf(self.trim());
4861: }
4862:
4863: /**
4864: * Parse a String into a Long
4865: *
4866: * @param self a String
4867: * @return a Long
4868: */
4869: public static Long toLong(String self) {
4870: return Long.valueOf(self.trim());
4871: }
4872:
4873: /**
4874: * Parse a String into a Float
4875: *
4876: * @param self a String
4877: * @return a Float
4878: */
4879: public static Float toFloat(String self) {
4880: return Float.valueOf(self.trim());
4881: }
4882:
4883: /**
4884: * Parse a String into a Double
4885: *
4886: * @param self a String
4887: * @return a Double
4888: */
4889: public static Double toDouble(String self) {
4890: return Double.valueOf(self.trim());
4891: }
4892:
4893: /**
4894: * Parse a String into a BigInteger
4895: *
4896: * @param self a String
4897: * @return a BigInteger
4898: */
4899: public static BigInteger toBigInteger(String self) {
4900: return new BigInteger(self.trim());
4901: }
4902:
4903: /**
4904: * Parse a String into a BigDecimal
4905: *
4906: * @param self a String
4907: * @return a BigDecimal
4908: */
4909: public static BigDecimal toBigDecimal(String self) {
4910: return new BigDecimal(self.trim());
4911: }
4912:
4913: /**
4914: * Transform a Number into an Integer
4915: *
4916: * @param self a Number
4917: * @return an Integer
4918: */
4919: public static Integer toInteger(Number self) {
4920: return new Integer(self.intValue());
4921: }
4922:
4923: /**
4924: * Transform a Number into a Long
4925: *
4926: * @param self a Number
4927: * @return an Long
4928: */
4929: public static Long toLong(Number self) {
4930: return new Long(self.longValue());
4931: }
4932:
4933: /**
4934: * Transform a Number into a Float
4935: *
4936: * @param self a Number
4937: * @return an Float
4938: */
4939: public static Float toFloat(Number self) {
4940: return new Float(self.floatValue());
4941: }
4942:
4943: /**
4944: * Transform a Number into a Double
4945: *
4946: * @param self a Number
4947: * @return an Double
4948: */
4949: public static Double toDouble(Number self) {
4950: return new Double(self.doubleValue());
4951: }
4952:
4953: /**
4954: * Transform a Number into a BigDecimal
4955: *
4956: * @param self a Number
4957: * @return an BigDecimal
4958: */
4959: public static BigDecimal toBigDecimal(Number self) {
4960: return new BigDecimal(self.doubleValue());
4961: }
4962:
4963: public static Object asType(Number self, Class c) {
4964: if (c == BigDecimal.class) {
4965: return toBigDecimal(self);
4966: } else if (c == BigInteger.class) {
4967: return toBigInteger(self);
4968: } else if (c == Double.class) {
4969: return toDouble(self);
4970: } else if (c == Float.class) {
4971: return toFloat(self);
4972: }
4973: return asType((Object) self, c);
4974: }
4975:
4976: /**
4977: * Transform a Number into a BigInteger
4978: *
4979: * @param self a Number
4980: * @return an BigInteger
4981: */
4982: public static BigInteger toBigInteger(Number self) {
4983: return new BigInteger(Long.toString(self.longValue()));
4984: }
4985:
4986: // Date methods
4987: //-------------------------------------------------------------------------
4988:
4989: /**
4990: * Increments a Date by a day
4991: *
4992: * @param self a Date
4993: * @return the next days date
4994: */
4995: public static Date next(Date self) {
4996: return plus(self, 1);
4997: }
4998:
4999: /**
5000: * Increments a java.sql.Date by a day
5001: *
5002: * @param self a java.sql.Date
5003: * @return the next days date
5004: */
5005: public static java.sql.Date next(java.sql.Date self) {
5006: return new java.sql.Date(next((Date) self).getTime());
5007: }
5008:
5009: /**
5010: * Decrement a Date by a day
5011: *
5012: * @param self a Date
5013: * @return the previous days date
5014: */
5015: public static Date previous(Date self) {
5016: return minus(self, 1);
5017: }
5018:
5019: /**
5020: * Decrement a java.sql.Date by a day
5021: *
5022: * @param self a java.sql.Date
5023: * @return the previous days date
5024: */
5025: public static java.sql.Date previous(java.sql.Date self) {
5026: return new java.sql.Date(previous((Date) self).getTime());
5027: }
5028:
5029: /**
5030: * Adds a number of days to this date and returns the new date
5031: *
5032: * @param self a Date
5033: * @param days the number of days to increase
5034: * @return the new date
5035: */
5036: public static Date plus(Date self, int days) {
5037: Calendar calendar = (Calendar) Calendar.getInstance().clone();
5038: calendar.setTime(self);
5039: calendar.add(Calendar.DAY_OF_YEAR, days);
5040: return calendar.getTime();
5041: }
5042:
5043: /**
5044: * Adds a number of days to this date and returns the new date
5045: *
5046: * @param self a java.sql.Date
5047: * @param days the number of days to increase
5048: * @return the new date
5049: */
5050: public static java.sql.Date plus(java.sql.Date self, int days) {
5051: return new java.sql.Date(plus((Date) self, days).getTime());
5052: }
5053:
5054: /**
5055: * Subtracts a number of days from this date and returns the new date
5056: *
5057: * @param self a Date
5058: * @return the new date
5059: */
5060: public static Date minus(Date self, int days) {
5061: return plus(self, -days);
5062: }
5063:
5064: /**
5065: * Subtracts a number of days from this date and returns the new date
5066: *
5067: * @param self a java.sql.Date
5068: * @return the new date
5069: */
5070: public static java.sql.Date minus(java.sql.Date self, int days) {
5071: return new java.sql.Date(minus((Date) self, days).getTime());
5072: }
5073:
5074: // Boolean based methods
5075: //-------------------------------------------------------------------------
5076:
5077: public static Boolean and(Boolean left, Boolean right) {
5078: return Boolean.valueOf(left.booleanValue()
5079: & right.booleanValue());
5080: }
5081:
5082: public static Boolean or(Boolean left, Boolean right) {
5083: return Boolean.valueOf(left.booleanValue()
5084: | right.booleanValue());
5085: }
5086:
5087: public static Boolean xor(Boolean left, Boolean right) {
5088: return Boolean.valueOf(left.booleanValue()
5089: ^ right.booleanValue());
5090: }
5091:
5092: // public static Boolean negate(Boolean left) {
5093: // return Boolean.valueOf(!left.booleanValue());
5094: // }
5095:
5096: // File and stream based methods
5097: //-------------------------------------------------------------------------
5098:
5099: /**
5100: * Helper method to create an object input stream from the given file.
5101: *
5102: * @param file a file
5103: * @return an object input stream
5104: * @throws FileNotFoundException
5105: * @throws IOException
5106: */
5107: public static ObjectInputStream newObjectInputStream(File file)
5108: throws FileNotFoundException, IOException {
5109: return new ObjectInputStream(new FileInputStream(file));
5110: }
5111:
5112: /**
5113: * Iterates through the given file object by object
5114: *
5115: * @param self a File
5116: * @param closure a closure
5117: * @throws IOException
5118: * @throws ClassNotFoundException
5119: */
5120: public static void eachObject(File self, Closure closure)
5121: throws IOException, ClassNotFoundException {
5122: eachObject(newObjectInputStream(self), closure);
5123: }
5124:
5125: /**
5126: * Iterates through the given object stream object by object. The
5127: * ObjectInputStream is closed afterwards.
5128: *
5129: * @param ois an ObjectInputStream, closed after the operation
5130: * @param closure a closure
5131: * @throws IOException
5132: * @throws ClassNotFoundException
5133: */
5134: public static void eachObject(ObjectInputStream ois, Closure closure)
5135: throws IOException, ClassNotFoundException {
5136: try {
5137: while (true) {
5138: try {
5139: Object obj = ois.readObject();
5140: // we allow null objects in the object stream
5141: closure.call(obj);
5142: } catch (EOFException e) {
5143: break;
5144: }
5145: }
5146: InputStream temp = ois;
5147: ois = null;
5148: temp.close();
5149: } finally {
5150: if (ois != null) {
5151: try {
5152: ois.close();
5153: } catch (Exception e) {
5154: // ignore this exception since there
5155: // has to be another already
5156: LOG
5157: .warning("Caught exception closing ObjectInputStream: "
5158: + e);
5159: }
5160: }
5161: }
5162: }
5163:
5164: /**
5165: * Iterates through the given file line by line
5166: *
5167: * @param self a File
5168: * @param closure a closure
5169: * @throws IOException
5170: */
5171: public static void eachLine(File self, Closure closure)
5172: throws IOException {
5173: eachLine(newReader(self), closure);
5174: }
5175:
5176: /**
5177: * Iterates through the given reader line by line. The
5178: * Reader is closed afterwards
5179: *
5180: * @param self a Reader, closed after the method returns
5181: * @param closure a closure
5182: * @throws IOException
5183: */
5184: public static void eachLine(Reader self, Closure closure)
5185: throws IOException {
5186: BufferedReader br /* = null */;
5187:
5188: if (self instanceof BufferedReader)
5189: br = (BufferedReader) self;
5190: else
5191: br = new BufferedReader(self);
5192:
5193: try {
5194: while (true) {
5195: String line = br.readLine();
5196: if (line == null) {
5197: break;
5198: } else {
5199: closure.call(line);
5200: }
5201: }
5202: Reader temp = self;
5203: self = null;
5204: temp.close();
5205: } finally {
5206: if (self != null) {
5207: try {
5208: self.close();
5209: } catch (Exception e) {
5210: // ignore this exception since there
5211: // has to be another already
5212: LOG
5213: .warning("Caught exception closing Reader: "
5214: + e);
5215: }
5216: }
5217: if (br != null) {
5218: try {
5219: br.close();
5220: } catch (Exception e) {
5221: // ignore this exception since this
5222: // is only our internal problem
5223: LOG
5224: .warning("Caught exception closing Reader: "
5225: + e);
5226: }
5227: }
5228: }
5229: }
5230:
5231: /**
5232: * Iterates through the given file line by line, splitting on the seperator
5233: *
5234: * @param self a File
5235: * @param sep a String separator
5236: * @param closure a closure
5237: * @throws IOException
5238: */
5239: public static void splitEachLine(File self, String sep,
5240: Closure closure) throws IOException {
5241: splitEachLine(newReader(self), sep, closure);
5242: }
5243:
5244: /**
5245: * Iterates through the given reader line by line, splitting on the separator.
5246: * The Reader is closed afterwards.
5247: *
5248: * @param self a Reader, closed after the method returns
5249: * @param sep a String separator
5250: * @param closure a closure
5251: * @throws IOException
5252: */
5253: public static void splitEachLine(Reader self, String sep,
5254: Closure closure) throws IOException {
5255: BufferedReader br /* = null */;
5256:
5257: if (self instanceof BufferedReader)
5258: br = (BufferedReader) self;
5259: else
5260: br = new BufferedReader(self);
5261:
5262: try {
5263: while (true) {
5264: String line = br.readLine();
5265: if (line == null) {
5266: break;
5267: } else {
5268: List vals = Arrays.asList(line.split(sep));
5269: closure.call(vals);
5270: }
5271: }
5272: Reader temp = self;
5273: self = null;
5274: temp.close();
5275: } finally {
5276: if (self != null) {
5277: try {
5278: self.close();
5279: } catch (Exception e) {
5280: // ignore this exception since there
5281: // has to be another already
5282: LOG
5283: .warning("Caught exception closing Reader: "
5284: + e);
5285: }
5286: }
5287: if (br != null) {
5288: try {
5289: br.close();
5290: } catch (Exception e) {
5291: // ignore this exception since this
5292: // is only our internal problem
5293: LOG
5294: .warning("Caught exception closing Reader: "
5295: + e);
5296: }
5297: }
5298: }
5299: }
5300:
5301: /**
5302: * Read a single, whole line from the given Reader
5303: *
5304: * @param self a Reader
5305: * @return a line
5306: * @throws IOException
5307: */
5308: public static String readLine(Reader self) throws IOException {
5309: BufferedReader br /* = null */;
5310:
5311: if (self instanceof BufferedReader) {
5312: br = (BufferedReader) self;
5313: } else {
5314: br = new BufferedReader(self); // todo dk: bug! will return null on second call
5315: }
5316: return br.readLine();
5317: }
5318:
5319: /**
5320: * Read a single, whole line from the given InputStream
5321: *
5322: * @param stream an InputStream
5323: * @return a line
5324: * @throws IOException
5325: */
5326: public static String readLine(InputStream stream)
5327: throws IOException {
5328: return readLine(new InputStreamReader(stream));
5329: }
5330:
5331: /**
5332: * Reads the file into a list of Strings for each line
5333: *
5334: * @param file a File
5335: * @return a List of lines
5336: * @throws IOException
5337: */
5338: public static List readLines(File file) throws IOException {
5339: IteratorClosureAdapter closure = new IteratorClosureAdapter(
5340: file);
5341: eachLine(file, closure);
5342: return closure.asList();
5343: }
5344:
5345: /**
5346: * Reads the content of the File opened with the specified encoding and returns it as a String
5347: *
5348: * @param file the file whose content we want to read
5349: * @param charset the charset used to read the content of the file
5350: * @return a String containing the content of the file
5351: * @throws IOException
5352: */
5353: public static String getText(File file, String charset)
5354: throws IOException {
5355: BufferedReader reader = newReader(file, charset);
5356: return getText(reader);
5357: }
5358:
5359: /**
5360: * Reads the content of the File and returns it as a String
5361: *
5362: * @param file the file whose content we want to read
5363: * @return a String containing the content of the file
5364: * @throws IOException
5365: */
5366: public static String getText(File file) throws IOException {
5367: BufferedReader reader = newReader(file);
5368: return getText(reader);
5369: }
5370:
5371: /**
5372: * Reads the content of this URL and returns it as a String
5373: *
5374: * @param url URL to read content from
5375: * @return the text from that URL
5376: * @throws IOException
5377: */
5378: public static String getText(URL url) throws IOException {
5379: return getText(url, CharsetToolkit.getDefaultSystemCharset()
5380: .toString());
5381: }
5382:
5383: /**
5384: * Reads the content of this URL and returns it as a String
5385: *
5386: * @param url URL to read content from
5387: * @param charset opens the stream with a specified charset
5388: * @return the text from that URL
5389: * @throws IOException
5390: */
5391: public static String getText(URL url, String charset)
5392: throws IOException {
5393: BufferedReader reader = new BufferedReader(
5394: new InputStreamReader(url.openConnection()
5395: .getInputStream(), charset));
5396: return getText(reader);
5397: }
5398:
5399: /**
5400: * Reads the content of this InputStream and returns it as a String
5401: *
5402: * @param is an input stream
5403: * @return the text from that URL
5404: * @throws IOException
5405: */
5406: public static String getText(InputStream is) throws IOException {
5407: BufferedReader reader = new BufferedReader(
5408: new InputStreamReader(is));
5409: return getText(reader);
5410: }
5411:
5412: /**
5413: * Reads the content of this InputStream with a specified charset and returns it as a String
5414: *
5415: * @param is an input stream
5416: * @param charset opens the stream with a specified charset
5417: * @return the text from that URL
5418: * @throws IOException
5419: */
5420: public static String getText(InputStream is, String charset)
5421: throws IOException {
5422: BufferedReader reader = new BufferedReader(
5423: new InputStreamReader(is, charset));
5424: return getText(reader);
5425: }
5426:
5427: /**
5428: * Reads the content of the Reader and returns it as a String
5429: *
5430: * @param reader a Reader whose content we want to read
5431: * @return a String containing the content of the buffered reader
5432: * @throws IOException
5433: */
5434: public static String getText(Reader reader) throws IOException {
5435: BufferedReader bufferedReader = new BufferedReader(reader);
5436: return getText(bufferedReader);
5437: }
5438:
5439: /**
5440: * Reads the content of the BufferedReader and returns it as a String.
5441: * The BufferedReader is closed afterwards.
5442: *
5443: * @param reader a BufferedReader whose content we want to read
5444: * @return a String containing the content of the buffered reader
5445: * @throws IOException
5446: */
5447: public static String getText(BufferedReader reader)
5448: throws IOException {
5449: StringBuffer answer = new StringBuffer();
5450: // reading the content of the file within a char buffer
5451: // allow to keep the correct line endings
5452: char[] charBuffer = new char[4096];
5453: int nbCharRead /* = 0*/;
5454: try {
5455: while ((nbCharRead = reader.read(charBuffer)) != -1) {
5456: // appends buffer
5457: answer.append(charBuffer, 0, nbCharRead);
5458: }
5459: Reader temp = reader;
5460: reader = null;
5461: temp.close();
5462: } finally {
5463: if (reader != null) {
5464: try {
5465: reader.close();
5466: } catch (Exception e) {
5467: // ignore since there has to be an exception already
5468: LOG
5469: .warning("Caught exception closing Reader: "
5470: + e);
5471: }
5472: }
5473: }
5474: return answer.toString();
5475: }
5476:
5477: /**
5478: * Write the text and append a new line (depending on the platform
5479: * line-ending)
5480: *
5481: * @param writer a BufferedWriter
5482: * @param line the line to write
5483: * @throws IOException
5484: */
5485: public static void writeLine(BufferedWriter writer, String line)
5486: throws IOException {
5487: writer.write(line);
5488: writer.newLine();
5489: }
5490:
5491: /**
5492: * Write the text to the File.
5493: *
5494: * @param file a File
5495: * @param text the text to write to the File
5496: * @throws IOException
5497: */
5498: public static void write(File file, String text) throws IOException {
5499: BufferedWriter writer = null;
5500: try {
5501: writer = newWriter(file);
5502: writer.write(text);
5503: writer.flush();
5504:
5505: Writer temp = writer;
5506: writer = null;
5507: temp.close();
5508: } finally {
5509: if (writer != null) {
5510: try {
5511: writer.close();
5512: } catch (Exception e) {
5513: // ignore since there has to be an exception already
5514: LOG
5515: .warning("Caught exception closing Writer: "
5516: + e);
5517: }
5518: }
5519: }
5520: }
5521:
5522: /**
5523: * Write the text to the File.
5524: *
5525: * @param file a File
5526: * @param text the text to write to the File
5527: * @throws IOException
5528: */
5529: public static File leftShift(File file, Object text)
5530: throws IOException {
5531: append(file, text);
5532: return file;
5533: }
5534:
5535: /**
5536: * Write the text to the File with a specified encoding.
5537: *
5538: * @param file a File
5539: * @param text the text to write to the File
5540: * @param charset the charset used
5541: * @throws IOException
5542: */
5543: public static void write(File file, String text, String charset)
5544: throws IOException {
5545: BufferedWriter writer = null;
5546: try {
5547: writer = newWriter(file, charset);
5548: writer.write(text);
5549: writer.flush();
5550:
5551: Writer temp = writer;
5552: writer = null;
5553: temp.close();
5554: } finally {
5555: if (writer != null) {
5556: try {
5557: writer.close();
5558: } catch (Exception e) {
5559: // ignore since there has to be an exception already
5560: LOG
5561: .warning("Caught exception closing Writer: "
5562: + e);
5563: }
5564: }
5565: }
5566: }
5567:
5568: /**
5569: * Append the text at the end of the File
5570: *
5571: * @param file a File
5572: * @param text the text to append at the end of the File
5573: * @throws IOException
5574: */
5575: public static void append(File file, Object text)
5576: throws IOException {
5577: BufferedWriter writer = null;
5578: try {
5579: writer = newWriter(file, true);
5580: InvokerHelper.write(writer, text);
5581: writer.flush();
5582:
5583: Writer temp = writer;
5584: writer = null;
5585: temp.close();
5586: } finally {
5587: if (writer != null) {
5588: try {
5589: writer.close();
5590: } catch (Exception e) {
5591: // ignore since there has to be an exception already
5592: LOG
5593: .warning("Caught exception closing Writer: "
5594: + e);
5595: }
5596: }
5597: }
5598: }
5599:
5600: /**
5601: * Append the text at the end of the File with a specified encoding
5602: *
5603: * @param file a File
5604: * @param text the text to append at the end of the File
5605: * @param charset the charset used
5606: * @throws IOException
5607: */
5608: public static void append(File file, Object text, String charset)
5609: throws IOException {
5610: BufferedWriter writer = null;
5611: try {
5612: writer = newWriter(file, charset, true);
5613: InvokerHelper.write(writer, text);
5614: writer.flush();
5615:
5616: Writer temp = writer;
5617: writer = null;
5618: temp.close();
5619: } finally {
5620: if (writer != null) {
5621: try {
5622: writer.close();
5623: } catch (Exception e) {
5624: // ignore since there has to be an exception already
5625: LOG
5626: .warning("Caught exception closing Writer: "
5627: + e);
5628: }
5629: }
5630: }
5631: }
5632:
5633: /**
5634: * Reads the reader into a list of Strings for each line
5635: *
5636: * @param reader a Reader
5637: * @return a List of lines
5638: * @throws IOException
5639: */
5640: public static List readLines(Reader reader) throws IOException {
5641: IteratorClosureAdapter closure = new IteratorClosureAdapter(
5642: reader);
5643: eachLine(reader, closure);
5644: return closure.asList();
5645: }
5646:
5647: /**
5648: * This method is used to throw useful exceptions when the eachFile* and eachDir closure methods
5649: * are used incorrectly.
5650: *
5651: * @param dir The directory to check
5652: * @throws FileNotFoundException Thrown if the given directory does not exist
5653: * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
5654: */
5655: private static void checkDir(File dir)
5656: throws FileNotFoundException, IllegalArgumentException {
5657: if (!dir.exists())
5658: throw new FileNotFoundException(dir.getAbsolutePath());
5659: if (!dir.isDirectory())
5660: throw new IllegalArgumentException(
5661: "The provided File object is not a directory: "
5662: + dir.getAbsolutePath());
5663: }
5664:
5665: /**
5666: * Invokes the closure for each file in the given directory
5667: *
5668: * @param self a File
5669: * @param closure a closure
5670: * @throws FileNotFoundException Thrown if the given directory does not exist
5671: * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
5672: */
5673: public static void eachFile(File self, Closure closure)
5674: throws FileNotFoundException, IllegalArgumentException {
5675: checkDir(self);
5676: File[] files = self.listFiles();
5677: for (int i = 0; i < files.length; i++) {
5678: closure.call(files[i]);
5679: }
5680: }
5681:
5682: /**
5683: * Invokes the closure for each file in the given directory and recursively.
5684: * It is a depth-first exploration, directories are included in the search.
5685: *
5686: * @param self a File
5687: * @param closure a closure
5688: * @throws FileNotFoundException Thrown if the given directory does not exist
5689: * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
5690: */
5691: public static void eachFileRecurse(File self, Closure closure)
5692: throws FileNotFoundException, IllegalArgumentException {
5693: checkDir(self);
5694: File[] files = self.listFiles();
5695: for (int i = 0; i < files.length; i++) {
5696: if (files[i].isDirectory()) {
5697: closure.call(files[i]);
5698: eachFileRecurse(files[i], closure);
5699: } else {
5700: closure.call(files[i]);
5701: }
5702: }
5703: }
5704:
5705: /**
5706: * Invokes the closure for each directory in the given directory,
5707: * ignoring regular files.
5708: *
5709: * @param self a directory
5710: * @param closure a closure
5711: * @throws FileNotFoundException Thrown if the given directory does not exist
5712: * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
5713: */
5714: public static void eachDir(File self, Closure closure)
5715: throws FileNotFoundException, IllegalArgumentException {
5716: checkDir(self);
5717: File[] files = self.listFiles();
5718: for (int i = 0; i < files.length; i++) {
5719: if (files[i].isDirectory()) {
5720: closure.call(files[i]);
5721: }
5722: }
5723: }
5724:
5725: /**
5726: * Invokes the closure for each file matching the given filter in the given directory
5727: * - calling the isCase() method used by switch statements. This method can be used
5728: * with different kinds of filters like regular expresions, classes, ranges etc.
5729: *
5730: * @param self a file
5731: * @param filter the filter to perform on the directory (using the isCase(object) method)
5732: * @param closure
5733: * @throws FileNotFoundException Thrown if the given directory does not exist
5734: * @throws IllegalArgumentException Thrown if the provided File object does not represent a directory
5735: */
5736: public static void eachFileMatch(File self, Object filter,
5737: Closure closure) throws FileNotFoundException,
5738: IllegalArgumentException {
5739: checkDir(self);
5740: File[] files = self.listFiles();
5741: MetaClass metaClass = InvokerHelper.getMetaClass(filter);
5742: for (int i = 0; i < files.length; i++) {
5743: if (DefaultTypeTransformation
5744: .castToBoolean(metaClass.invokeMethod(filter,
5745: "isCase", files[i].getName()))) {
5746: closure.call(files[i]);
5747: }
5748: }
5749: }
5750:
5751: /**
5752: * Allow simple syntax for using timers.
5753: *
5754: * @param timer a timer object
5755: * @param delay the delay in milliseconds before running the closure code
5756: * @param closure
5757: */
5758: public static void runAfter(Timer timer, int delay,
5759: final Closure closure) {
5760: TimerTask timerTask = new TimerTask() {
5761: public void run() {
5762: closure.call();
5763: }
5764: };
5765: timer.schedule(timerTask, delay);
5766: }
5767:
5768: /**
5769: * Helper method to create a buffered reader for a file
5770: *
5771: * @param file a File
5772: * @return a BufferedReader
5773: * @throws IOException
5774: */
5775: public static BufferedReader newReader(File file)
5776: throws IOException {
5777: CharsetToolkit toolkit = new CharsetToolkit(file);
5778: return toolkit.getReader();
5779: }
5780:
5781: /**
5782: * Helper method to create a buffered reader for a file, with a specified charset
5783: *
5784: * @param file a File
5785: * @param charset the charset with which we want to write in the File
5786: * @return a BufferedReader
5787: * @throws FileNotFoundException if the File was not found
5788: * @throws UnsupportedEncodingException if the encoding specified is not supported
5789: */
5790: public static BufferedReader newReader(File file, String charset)
5791: throws FileNotFoundException, UnsupportedEncodingException {
5792: return new BufferedReader(new InputStreamReader(
5793: new FileInputStream(file), charset));
5794: }
5795:
5796: /**
5797: * Provides a reader for an arbitrary input stream
5798: *
5799: * @param self an input stream
5800: * @return a reader
5801: */
5802: public static BufferedReader newReader(InputStream self) {
5803: return new BufferedReader(new InputStreamReader(self));
5804: }
5805:
5806: /**
5807: * Helper method to create a new BufferedReader for a file and then
5808: * passes it into the closure and ensures its closed again afterwords
5809: *
5810: * @param file
5811: * @throws FileNotFoundException
5812: */
5813: public static void withReader(File file, Closure closure)
5814: throws IOException {
5815: withReader(newReader(file), closure);
5816: }
5817:
5818: /**
5819: * Helper method to create a buffered output stream for a file
5820: *
5821: * @param file
5822: * @throws FileNotFoundException
5823: */
5824: public static BufferedOutputStream newOutputStream(File file)
5825: throws IOException {
5826: return new BufferedOutputStream(new FileOutputStream(file));
5827: }
5828:
5829: /**
5830: * Helper method to create a new OutputStream for a file and then
5831: * passes it into the closure and ensures its closed again afterwords
5832: *
5833: * @param file a File
5834: * @throws FileNotFoundException
5835: */
5836: public static void withOutputStream(File file, Closure closure)
5837: throws IOException {
5838: withStream(newOutputStream(file), closure);
5839: }
5840:
5841: /**
5842: * Helper method to create a new InputStream for a file and then
5843: * passes it into the closure and ensures its closed again afterwords
5844: *
5845: * @param file a File
5846: * @throws FileNotFoundException
5847: */
5848: public static void withInputStream(File file, Closure closure)
5849: throws IOException {
5850: withStream(newInputStream(file), closure);
5851: }
5852:
5853: /**
5854: * Helper method to create a buffered writer for a file
5855: *
5856: * @param file a File
5857: * @return a BufferedWriter
5858: * @throws FileNotFoundException
5859: */
5860: public static BufferedWriter newWriter(File file)
5861: throws IOException {
5862: return new BufferedWriter(new FileWriter(file));
5863: }
5864:
5865: /**
5866: * Helper method to create a buffered writer for a file in append mode
5867: *
5868: * @param file a File
5869: * @param append true if in append mode
5870: * @return a BufferedWriter
5871: * @throws FileNotFoundException
5872: */
5873: public static BufferedWriter newWriter(File file, boolean append)
5874: throws IOException {
5875: return new BufferedWriter(new FileWriter(file, append));
5876: }
5877:
5878: /**
5879: * Helper method to create a buffered writer for a file
5880: *
5881: * @param file a File
5882: * @param charset the name of the encoding used to write in this file
5883: * @param append true if in append mode
5884: * @return a BufferedWriter
5885: * @throws FileNotFoundException
5886: */
5887: public static BufferedWriter newWriter(File file, String charset,
5888: boolean append) throws IOException {
5889: if (append) {
5890: return new BufferedWriter(new OutputStreamWriter(
5891: new FileOutputStream(file, append), charset));
5892: } else {
5893: // first write the Byte Order Mark for Unicode encodings
5894: FileOutputStream stream = new FileOutputStream(file);
5895: if ("UTF-16BE".equals(charset)) {
5896: writeUtf16Bom(stream, true);
5897: } else if ("UTF-16LE".equals(charset)) {
5898: writeUtf16Bom(stream, false);
5899: }
5900: return new BufferedWriter(new OutputStreamWriter(stream,
5901: charset));
5902: }
5903: }
5904:
5905: /**
5906: * Helper method to create a buffered writer for a file
5907: *
5908: * @param file a File
5909: * @param charset the name of the encoding used to write in this file
5910: * @return a BufferedWriter
5911: * @throws FileNotFoundException
5912: */
5913: public static BufferedWriter newWriter(File file, String charset)
5914: throws IOException {
5915: return newWriter(file, charset, false);
5916: }
5917:
5918: /**
5919: * Write a Byte Order Mark at the begining of the file
5920: *
5921: * @param stream the FileOuputStream to write the BOM to
5922: * @param bigEndian true if UTF 16 Big Endian or false if Low Endian
5923: * @throws IOException
5924: */
5925: private static void writeUtf16Bom(FileOutputStream stream,
5926: boolean bigEndian) throws IOException {
5927: if (bigEndian) {
5928: stream.write(-2);
5929: stream.write(-1);
5930: } else {
5931: stream.write(-1);
5932: stream.write(-2);
5933: }
5934: }
5935:
5936: /**
5937: * Helper method to create a new BufferedWriter for a file and then
5938: * passes it into the closure and ensures it is closed again afterwords
5939: *
5940: * @param file a File
5941: * @param closure a closure
5942: * @throws FileNotFoundException
5943: */
5944: public static void withWriter(File file, Closure closure)
5945: throws IOException {
5946: withWriter(newWriter(file), closure);
5947: }
5948:
5949: /**
5950: * Helper method to create a new BufferedWriter for a file in a specified encoding
5951: * and then passes it into the closure and ensures it is closed again afterwords
5952: *
5953: * @param file a File
5954: * @param charset the charset used
5955: * @param closure a closure
5956: * @throws FileNotFoundException
5957: */
5958: public static void withWriter(File file, String charset,
5959: Closure closure) throws IOException {
5960: withWriter(newWriter(file, charset), closure);
5961: }
5962:
5963: /**
5964: * Helper method to create a new BufferedWriter for a file in a specified encoding
5965: * in append mode and then passes it into the closure and ensures it is closed again afterwords
5966: *
5967: * @param file a File
5968: * @param charset the charset used
5969: * @param closure a closure
5970: * @throws FileNotFoundException
5971: */
5972: public static void withWriterAppend(File file, String charset,
5973: Closure closure) throws IOException {
5974: withWriter(newWriter(file, charset, true), closure);
5975: }
5976:
5977: /**
5978: * Helper method to create a new PrintWriter for a file
5979: *
5980: * @param file a File
5981: * @throws FileNotFoundException
5982: */
5983: public static PrintWriter newPrintWriter(File file)
5984: throws IOException {
5985: return new PrintWriter(newWriter(file));
5986: }
5987:
5988: /**
5989: * Helper method to create a new PrintWriter for a file with a specified charset
5990: *
5991: * @param file a File
5992: * @param charset the charset
5993: * @return a PrintWriter
5994: * @throws FileNotFoundException
5995: */
5996: public static PrintWriter newPrintWriter(File file, String charset)
5997: throws IOException {
5998: return new PrintWriter(newWriter(file, charset));
5999: }
6000:
6001: /**
6002: * Helper method to create a new PrintWriter for a file and then
6003: * passes it into the closure and ensures its closed again afterwords
6004: *
6005: * @param file a File
6006: * @throws FileNotFoundException
6007: */
6008: public static void withPrintWriter(File file, Closure closure)
6009: throws IOException {
6010: withWriter(newPrintWriter(file), closure);
6011: }
6012:
6013: /**
6014: * Allows a writer to be used, calling the closure with the writer
6015: * and then ensuring that the writer is closed down again irrespective
6016: * of whether exceptions occur or the
6017: *
6018: * @param writer the writer which is used and then closed
6019: * @param closure the closure that the writer is passed into
6020: * @throws IOException
6021: */
6022: public static void withWriter(Writer writer, Closure closure)
6023: throws IOException {
6024: try {
6025: closure.call(writer);
6026: writer.flush();
6027:
6028: Writer temp = writer;
6029: writer = null;
6030: temp.close();
6031: } finally {
6032: if (writer != null) {
6033: try {
6034: writer.close();
6035: } catch (Exception e) {
6036: // ignore since there has to be an exception already
6037: LOG
6038: .warning("Caught exception closing Writer: "
6039: + e);
6040: }
6041: }
6042: }
6043: }
6044:
6045: /**
6046: * Allows a Reader to be used, calling the closure with the reader
6047: * and then ensuring that the reader is closed down again irrespective
6048: * of whether exceptions occur or the
6049: *
6050: * @param reader the reader which is used and then closed
6051: * @param closure the closure that the writer is passed into
6052: * @throws IOException
6053: */
6054: public static void withReader(Reader reader, Closure closure)
6055: throws IOException {
6056: try {
6057: closure.call(reader);
6058:
6059: Reader temp = reader;
6060: reader = null;
6061: temp.close();
6062: } finally {
6063: if (reader != null) {
6064: try {
6065: reader.close();
6066: } catch (Exception e) {
6067: // ignore since there has to be an exception already
6068: LOG
6069: .warning("Caught exception closing Reader: "
6070: + e);
6071: }
6072: }
6073: }
6074: }
6075:
6076: /**
6077: * Allows a InputStream to be used, calling the closure with the stream
6078: * and then ensuring that the stream is closed down again irrespective
6079: * of whether exceptions occur or the
6080: *
6081: * @param stream the stream which is used and then closed
6082: * @param closure the closure that the stream is passed into
6083: * @throws IOException
6084: */
6085: public static void withStream(InputStream stream, Closure closure)
6086: throws IOException {
6087: try {
6088: closure.call(stream);
6089:
6090: InputStream temp = stream;
6091: stream = null;
6092: temp.close();
6093: } finally {
6094: if (stream != null) {
6095: try {
6096: stream.close();
6097: } catch (Exception e) {
6098: // ignore since there has to be an exception already
6099: LOG
6100: .warning("Caught exception closing InputStream: "
6101: + e);
6102: }
6103: }
6104: }
6105: }
6106:
6107: /**
6108: * Reads the stream into a list of Strings for each line
6109: *
6110: * @param stream a stream
6111: * @return a List of lines
6112: * @throws IOException
6113: */
6114: public static List readLines(InputStream stream) throws IOException {
6115: return readLines(new BufferedReader(new InputStreamReader(
6116: stream)));
6117: }
6118:
6119: /**
6120: * Iterates through the given stream line by line
6121: *
6122: * @param stream a stream
6123: * @param closure a closure
6124: * @throws IOException
6125: */
6126: public static void eachLine(InputStream stream, Closure closure)
6127: throws IOException {
6128: eachLine(new InputStreamReader(stream), closure);
6129: }
6130:
6131: /**
6132: * Iterates through the lines read from the URL's associated input stream
6133: *
6134: * @param url a URL to open and read
6135: * @param closure a closure to apply on each line
6136: * @throws IOException
6137: */
6138: public static void eachLine(URL url, Closure closure)
6139: throws IOException {
6140: eachLine(url.openConnection().getInputStream(), closure);
6141: }
6142:
6143: /**
6144: * Helper method to create a new BufferedReader for a URL and then
6145: * passes it into the closure and ensures its closed again afterwords
6146: *
6147: * @param url a URL
6148: * @throws FileNotFoundException
6149: */
6150: public static void withReader(URL url, Closure closure)
6151: throws IOException {
6152: withReader(url.openConnection().getInputStream(), closure);
6153: }
6154:
6155: /**
6156: * Helper method to create a new BufferedReader for a stream and then
6157: * passes it into the closure and ensures its closed again afterwords
6158: *
6159: * @param in a stream
6160: * @throws FileNotFoundException
6161: */
6162: public static void withReader(InputStream in, Closure closure)
6163: throws IOException {
6164: withReader(new InputStreamReader(in), closure);
6165: }
6166:
6167: /**
6168: * Allows an output stream to be used, calling the closure with the output stream
6169: * and then ensuring that the output stream is closed down again irrespective
6170: * of whether exceptions occur
6171: *
6172: * @param stream the stream which is used and then closed
6173: * @param closure the closure that the writer is passed into
6174: * @throws IOException
6175: */
6176: public static void withWriter(OutputStream stream, Closure closure)
6177: throws IOException {
6178: withWriter(new OutputStreamWriter(stream), closure);
6179: }
6180:
6181: /**
6182: * Allows an output stream to be used, calling the closure with the output stream
6183: * and then ensuring that the output stream is closed down again irrespective
6184: * of whether exceptions occur.
6185: *
6186: * @param stream the stream which is used and then closed
6187: * @param charset the charset used
6188: * @param closure the closure that the writer is passed into
6189: * @throws IOException
6190: */
6191: public static void withWriter(OutputStream stream, String charset,
6192: Closure closure) throws IOException {
6193: withWriter(new OutputStreamWriter(stream, charset), closure);
6194: }
6195:
6196: /**
6197: * Allows a OutputStream to be used, calling the closure with the stream
6198: * and then ensuring that the stream is closed down again irrespective
6199: * of whether exceptions occur.
6200: *
6201: * @param os the stream which is used and then closed
6202: * @param closure the closure that the stream is passed into
6203: * @throws IOException
6204: */
6205: public static void withStream(OutputStream os, Closure closure)
6206: throws IOException {
6207: try {
6208: closure.call(os);
6209: os.flush();
6210:
6211: OutputStream temp = os;
6212: os = null;
6213: temp.close();
6214: } finally {
6215: if (os != null) {
6216: try {
6217: os.close();
6218: } catch (IOException e) {
6219: LOG
6220: .warning("Caught exception closing OutputStream: "
6221: + e);
6222: }
6223: }
6224: }
6225: }
6226:
6227: /**
6228: * Helper method to create a buffered input stream for a file
6229: *
6230: * @param file a File
6231: * @return a BufferedInputStream of the file
6232: * @throws FileNotFoundException
6233: */
6234: public static BufferedInputStream newInputStream(File file)
6235: throws FileNotFoundException {
6236: return new BufferedInputStream(new FileInputStream(file));
6237: }
6238:
6239: /**
6240: * Traverse through each byte of the specified File
6241: *
6242: * @param self a File
6243: * @param closure a closure
6244: */
6245: public static void eachByte(File self, Closure closure)
6246: throws IOException {
6247: BufferedInputStream is = newInputStream(self);
6248: eachByte(is, closure);
6249: }
6250:
6251: /**
6252: * Traverse through each byte of the specified stream. The
6253: * stream is closed afterwards.
6254: *
6255: * @param is stream to iterate over, closed after the method call
6256: * @param closure closure to apply to each byte
6257: * @throws IOException
6258: */
6259: public static void eachByte(InputStream is, Closure closure)
6260: throws IOException {
6261: try {
6262: while (true) {
6263: int b = is.read();
6264: if (b == -1) {
6265: break;
6266: } else {
6267: closure.call(new Byte((byte) b));
6268: }
6269: }
6270:
6271: InputStream temp = is;
6272: is = null;
6273: temp.close();
6274: } finally {
6275: if (is != null) {
6276: try {
6277: is.close();
6278: } catch (IOException e) {
6279: LOG
6280: .warning("Caught exception closing InputStream: "
6281: + e);
6282: }
6283: }
6284: }
6285: }
6286:
6287: /**
6288: * Traverse through each byte of the specified URL
6289: *
6290: * @param url url to iterate over
6291: * @param closure closure to apply to each byte
6292: * @throws IOException
6293: */
6294: public static void eachByte(URL url, Closure closure)
6295: throws IOException {
6296: InputStream is = url.openConnection().getInputStream();
6297: eachByte(is, closure);
6298: }
6299:
6300: /**
6301: * Transforms the characters from a reader with a Closure and
6302: * write them to a writer.
6303: *
6304: * @param reader
6305: * @param writer
6306: * @param closure
6307: */
6308: public static void transformChar(Reader reader, Writer writer,
6309: Closure closure) throws IOException {
6310: int c;
6311: try {
6312: char[] chars = new char[1];
6313: while ((c = reader.read()) != -1) {
6314: chars[0] = (char) c;
6315: writer.write((String) closure.call(new String(chars)));
6316: }
6317: writer.flush();
6318:
6319: Writer temp2 = writer;
6320: writer = null;
6321: temp2.close();
6322: Reader temp1 = reader;
6323: reader = null;
6324: temp1.close();
6325: } finally {
6326: if (reader != null) {
6327: try {
6328: reader.close();
6329: } catch (IOException e) {
6330: LOG
6331: .warning("Caught exception closing Reader: "
6332: + e);
6333: }
6334: }
6335: if (writer != null) {
6336: try {
6337: writer.close();
6338: } catch (IOException e) {
6339: LOG
6340: .warning("Caught exception closing Writer: "
6341: + e);
6342: }
6343: }
6344: }
6345: }
6346:
6347: /**
6348: * Transforms the lines from a reader with a Closure and
6349: * write them to a writer. Both Reader and Writer are
6350: * closed after the operation
6351: *
6352: * @param reader Lines of text to be transformed. Reader is closed afterwards.
6353: * @param writer Where transformed lines are written. Writer is closed afterwards.
6354: * @param closure Single parameter closure that is called to transform each line of
6355: * text from the reader, before writing it to the writer.
6356: */
6357: public static void transformLine(Reader reader, Writer writer,
6358: Closure closure) throws IOException {
6359: BufferedReader br = new BufferedReader(reader);
6360: BufferedWriter bw = new BufferedWriter(writer);
6361: String line;
6362: try {
6363: while ((line = br.readLine()) != null) {
6364: Object o = closure.call(line);
6365: if (o != null) {
6366: bw.write(o.toString());
6367: bw.newLine();
6368: }
6369: }
6370: bw.flush();
6371:
6372: Writer temp2 = writer;
6373: writer = null;
6374: temp2.close();
6375: Reader temp1 = reader;
6376: reader = null;
6377: temp1.close();
6378: } finally {
6379: if (br != null) {
6380: try {
6381: br.close();
6382: } catch (IOException e) {
6383: LOG
6384: .warning("Caught exception closing Reader: "
6385: + e);
6386: }
6387: }
6388: if (reader != null) {
6389: try {
6390: reader.close();
6391: } catch (IOException e) {
6392: LOG
6393: .warning("Caught exception closing Reader: "
6394: + e);
6395: }
6396: }
6397: if (bw != null) {
6398: try {
6399: bw.close();
6400: } catch (IOException e) {
6401: LOG
6402: .warning("Caught exception closing Writer: "
6403: + e);
6404: }
6405: }
6406: if (writer != null) {
6407: try {
6408: writer.close();
6409: } catch (IOException e) {
6410: LOG
6411: .warning("Caught exception closing Writer: "
6412: + e);
6413: }
6414: }
6415: }
6416: }
6417:
6418: /**
6419: * Filter the lines from a reader and write them on the writer,
6420: * according to a closure which returns true or false.
6421: * Both Reader and Writer are closed after the operation.
6422: *
6423: * @param reader a reader, closed after the call
6424: * @param writer a writer, closed after the call
6425: * @param closure the closure which returns booleans
6426: * @throws IOException
6427: */
6428: public static void filterLine(Reader reader, Writer writer,
6429: Closure closure) throws IOException {
6430: BufferedReader br = new BufferedReader(reader);
6431: BufferedWriter bw = new BufferedWriter(writer);
6432: String line;
6433: try {
6434: while ((line = br.readLine()) != null) {
6435: if (DefaultTypeTransformation.castToBoolean(closure
6436: .call(line))) {
6437: bw.write(line);
6438: bw.newLine();
6439: }
6440: }
6441: bw.flush();
6442:
6443: Writer temp2 = writer;
6444: writer = null;
6445: temp2.close();
6446: Reader temp1 = reader;
6447: reader = null;
6448: temp1.close();
6449: } finally {
6450: if (br != null) {
6451: try {
6452: br.close();
6453: } catch (IOException e) {
6454: LOG
6455: .warning("Caught exception closing Reader: "
6456: + e);
6457: }
6458: }
6459: if (reader != null) {
6460: try {
6461: reader.close();
6462: } catch (IOException e) {
6463: LOG
6464: .warning("Caught exception closing Reader: "
6465: + e);
6466: }
6467: }
6468: if (bw != null) {
6469: try {
6470: bw.close();
6471: } catch (IOException e) {
6472: LOG
6473: .warning("Caught exception closing Writer: "
6474: + e);
6475: }
6476: }
6477: if (writer != null) {
6478: try {
6479: writer.close();
6480: } catch (IOException e) {
6481: LOG
6482: .warning("Caught exception closing Writer: "
6483: + e);
6484: }
6485: }
6486: }
6487:
6488: }
6489:
6490: /**
6491: * Filters the lines of a File and creates a Writeable in return to
6492: * stream the filtered lines
6493: *
6494: * @param self a File
6495: * @param closure a closure which returns a boolean indicating to filter
6496: * the line or not
6497: * @return a Writable closure
6498: * @throws IOException if <code>self</code> is not readable
6499: */
6500: public static Writable filterLine(File self, Closure closure)
6501: throws IOException {
6502: return filterLine(newReader(self), closure);
6503: }
6504:
6505: /**
6506: * Filter the lines from a File and write them on a writer, according to a closure
6507: * which returns true or false
6508: *
6509: * @param self a File
6510: * @param writer a writer
6511: * @param closure a closure which returns a boolean value and takes a line as input
6512: * @throws IOException if <code>self</code> is not readable
6513: */
6514: public static void filterLine(File self, Writer writer,
6515: Closure closure) throws IOException {
6516: filterLine(newReader(self), writer, closure);
6517: }
6518:
6519: /**
6520: * Filter the lines of a Reader and create a Writable in return to stream
6521: * the filtered lines.
6522: *
6523: * @param reader a reader
6524: * @param closure a closure returning a boolean indicating to filter or not a line
6525: * @return a Writable closure
6526: */
6527: public static Writable filterLine(Reader reader,
6528: final Closure closure) {
6529: final BufferedReader br = new BufferedReader(reader);
6530: return new Writable() {
6531: public Writer writeTo(Writer out) throws IOException {
6532: BufferedWriter bw = new BufferedWriter(out);
6533: String line;
6534: while ((line = br.readLine()) != null) {
6535: if (DefaultTypeTransformation.castToBoolean(closure
6536: .call(line))) {
6537: bw.write(line);
6538: bw.newLine();
6539: }
6540: }
6541: bw.flush();
6542: return out;
6543: }
6544:
6545: public String toString() {
6546: StringWriter buffer = new StringWriter();
6547: try {
6548: writeTo(buffer);
6549: } catch (IOException e) {
6550: throw new StringWriterIOException(e);
6551: }
6552: return buffer.toString();
6553: }
6554: };
6555: }
6556:
6557: /**
6558: * Filter lines from an input stream using a closure predicate
6559: *
6560: * @param self an input stream
6561: * @param predicate a closure which returns boolean and takes a line
6562: * @return a filtered writer
6563: */
6564: public static Writable filterLine(InputStream self,
6565: Closure predicate) {
6566: return filterLine(newReader(self), predicate);
6567: }
6568:
6569: /**
6570: * Filters lines from an input stream, writing to a writer, using a closure which
6571: * returns boolean and takes a line.
6572: *
6573: * @param self an InputStream
6574: * @param writer a writer to write output to
6575: * @param predicate a closure which returns a boolean and takes a line as input
6576: */
6577: public static void filterLine(InputStream self, Writer writer,
6578: Closure predicate) throws IOException {
6579: filterLine(newReader(self), writer, predicate);
6580: }
6581:
6582: /**
6583: * Reads the content of the file into an array of byte
6584: *
6585: * @param file a File
6586: * @return a List of Bytes
6587: */
6588: public static byte[] readBytes(File file) throws IOException {
6589: byte[] bytes = new byte[(int) file.length()];
6590: FileInputStream fileInputStream = new FileInputStream(file);
6591: DataInputStream dis = new DataInputStream(fileInputStream);
6592: try {
6593: dis.readFully(bytes);
6594:
6595: InputStream temp = dis;
6596: dis = null;
6597: temp.close();
6598: } finally {
6599: if (dis != null) {
6600: try {
6601: dis.close();
6602: } catch (IOException e) {
6603: LOG
6604: .warning("Caught exception closing DataInputStream: "
6605: + e);
6606: }
6607: }
6608: }
6609: return bytes;
6610: }
6611:
6612: // ================================
6613: // Socket and ServerSocket methods
6614:
6615: /**
6616: * Allows an InputStream and an OutputStream from a Socket to be used,
6617: * calling the closure with the streams and then ensuring that the streams
6618: * are closed down again irrespective of whether exceptions occur.
6619: *
6620: * @param socket a Socket
6621: * @param closure a Closure
6622: * @throws IOException
6623: */
6624: public static void withStreams(Socket socket, Closure closure)
6625: throws IOException {
6626: InputStream input = socket.getInputStream();
6627: OutputStream output = socket.getOutputStream();
6628: try {
6629: closure.call(new Object[] { input, output });
6630:
6631: InputStream temp1 = input;
6632: input = null;
6633: temp1.close();
6634: OutputStream temp2 = output;
6635: output = null;
6636: temp2.close();
6637: } finally {
6638: if (input != null) {
6639: try {
6640: input.close();
6641: } catch (IOException e) {
6642: LOG
6643: .warning("Caught exception closing InputStream: "
6644: + e);
6645: }
6646: }
6647: if (output != null) {
6648: try {
6649: output.close();
6650: } catch (IOException e) {
6651: LOG
6652: .warning("Caught exception closing OutputStream: "
6653: + e);
6654: }
6655: }
6656: }
6657: }
6658:
6659: /**
6660: * Overloads the left shift operator to provide an append mechanism to
6661: * add things to the output stream of a socket
6662: *
6663: * @param self a Socket
6664: * @param value a value to append
6665: * @return a Writer
6666: */
6667: public static Writer leftShift(Socket self, Object value)
6668: throws IOException {
6669: return leftShift(self.getOutputStream(), value);
6670: }
6671:
6672: /**
6673: * Overloads the left shift operator to provide an append mechanism
6674: * to add bytes to the output stream of a socket
6675: *
6676: * @param self a Socket
6677: * @param value a value to append
6678: * @return an OutputStream
6679: */
6680: public static OutputStream leftShift(Socket self, byte[] value)
6681: throws IOException {
6682: return leftShift(self.getOutputStream(), value);
6683: }
6684:
6685: /**
6686: * Allow to pass a Closure to the accept methods of ServerSocket
6687: *
6688: * @param serverSocket a ServerSocket
6689: * @param closure a Closure
6690: * @return a Socket
6691: * @throws IOException
6692: */
6693: public static Socket accept(ServerSocket serverSocket,
6694: final Closure closure) throws IOException {
6695: final Socket socket = serverSocket.accept();
6696: new Thread(new Runnable() {
6697: public void run() {
6698: try {
6699: closure.call(socket);
6700: } finally {
6701: try {
6702: socket.close();
6703: } catch (IOException e) {
6704: LOG.warning("Caught exception closing socket: "
6705: + e);
6706: }
6707: }
6708: }
6709: }).start();
6710: return socket;
6711: }
6712:
6713: /**
6714: * @param file a File
6715: * @return a File which wraps the input file and which implements Writable
6716: */
6717: public static File asWritable(File file) {
6718: return new WritableFile(file);
6719: }
6720:
6721: public static Object asType(File f, Class c) {
6722: if (c == Writable.class) {
6723: return asWritable(f);
6724: }
6725: return asType((Object) f, c);
6726: }
6727:
6728: /**
6729: * @param file a File
6730: * @param encoding the encoding to be used when reading the file's contents
6731: * @return File which wraps the input file and which implements Writable
6732: */
6733: public static File asWritable(File file, String encoding) {
6734: return new WritableFile(file, encoding);
6735: }
6736:
6737: /**
6738: * Converts the given String into a List of strings of one character
6739: *
6740: * @param self a String
6741: * @return a List of characters (a 1-character String)
6742: */
6743: public static List toList(String self) {
6744: int size = self.length();
6745: List answer = new ArrayList(size);
6746: for (int i = 0; i < size; i++) {
6747: answer.add(self.substring(i, i + 1));
6748: }
6749: return answer;
6750: }
6751:
6752: public static Object asType(String self, Class c) {
6753: if (c == List.class) {
6754: return toList(self);
6755: } else if (c == BigDecimal.class) {
6756: return toBigDecimal(self);
6757: } else if (c == BigInteger.class) {
6758: return toBigInteger(self);
6759: } else if (c == Character.class) {
6760: return toCharacter(self);
6761: } else if (c == Double.class) {
6762: return toDouble(self);
6763: } else if (c == Float.class) {
6764: return toFloat(self);
6765: }
6766: return asType((Object) self, c);
6767: }
6768:
6769: // Process methods
6770: //-------------------------------------------------------------------------
6771:
6772: /**
6773: * An alias method so that a process appears similar to System.out, System.in, System.err;
6774: * you can use process.in, process.out, process.err in a similar way
6775: *
6776: * @return an InputStream
6777: */
6778: public static InputStream getIn(Process self) {
6779: return self.getInputStream();
6780: }
6781:
6782: /**
6783: * Read the text of the output stream of the Process.
6784: *
6785: * @param self a Process
6786: * @return the text of the output
6787: * @throws IOException
6788: */
6789: public static String getText(Process self) throws IOException {
6790: return getText(new BufferedReader(new InputStreamReader(self
6791: .getInputStream())));
6792: }
6793:
6794: /**
6795: * An alias method so that a process appears similar to System.out, System.in, System.err;
6796: * you can use process.in, process.out, process.err in a similar way
6797: *
6798: * @return an InputStream
6799: */
6800: public static InputStream getErr(Process self) {
6801: return self.getErrorStream();
6802: }
6803:
6804: /**
6805: * An alias method so that a process appears similar to System.out, System.in, System.err;
6806: * you can use process.in, process.out, process.err in a similar way
6807: *
6808: * @return an OutputStream
6809: */
6810: public static OutputStream getOut(Process self) {
6811: return self.getOutputStream();
6812: }
6813:
6814: /**
6815: * Overloads the left shift operator to provide an append mechanism
6816: * to pipe into a Process
6817: *
6818: * @param self a Process
6819: * @param value a value to append
6820: * @return a Writer
6821: */
6822: public static Writer leftShift(Process self, Object value)
6823: throws IOException {
6824: return leftShift(self.getOutputStream(), value);
6825: }
6826:
6827: /**
6828: * Overloads the left shift operator to provide an append mechanism
6829: * to pipe into a Process
6830: *
6831: * @param self a Process
6832: * @param value a value to append
6833: * @return an OutputStream
6834: */
6835: public static OutputStream leftShift(Process self, byte[] value)
6836: throws IOException {
6837: return leftShift(self.getOutputStream(), value);
6838: }
6839:
6840: /**
6841: * Wait for the process to finish during a certain amount of time, otherwise stops the process.
6842: *
6843: * @param self a Process
6844: * @param numberOfMillis the number of milliseconds to wait before stopping the process
6845: */
6846: public static void waitForOrKill(Process self, long numberOfMillis) {
6847: ProcessRunner runnable = new ProcessRunner(self);
6848: Thread thread = new Thread(runnable);
6849: thread.start();
6850: runnable.waitForOrKill(numberOfMillis);
6851: }
6852:
6853: /**
6854: * gets the input and error streams from a process and reads them
6855: * to avoid the process to block due to a full ouput buffer. For this
6856: * two Threads are started, so this method will return immediately
6857: *
6858: * @param self a Process
6859: */
6860: public static void consumeProcessOutput(Process self) {
6861: Dumper d = new Dumper(self.getErrorStream());
6862: Thread t = new Thread(d);
6863: t.start();
6864: d = new Dumper(self.getInputStream());
6865: t = new Thread(d);
6866: t.start();
6867: }
6868:
6869: /**
6870: * Process each regex group matched substring of the given string. If the closure
6871: * parameter takes one argument an array with all match groups is passed to it.
6872: * If the closure takes as many arguments as there are match groups, then each
6873: * parameter will be one match group.
6874: *
6875: * @param self the source string
6876: * @param regex a Regex string
6877: * @param closure a closure with one parameter or as much parameters as groups
6878: */
6879: public static void eachMatch(String self, String regex,
6880: Closure closure) {
6881: Pattern p = Pattern.compile(regex);
6882: Matcher m = p.matcher(self);
6883: while (m.find()) {
6884: int count = m.groupCount();
6885: ArrayList groups = new ArrayList();
6886: for (int i = 0; i <= count; i++) {
6887: groups.add(m.group(i));
6888: }
6889: if (groups.size() == 1
6890: || closure.getMaximumNumberOfParameters() < groups
6891: .size()) {
6892: // not enough parameters there to give each group part
6893: // it's own parameter, so try a closure with one parameter
6894: // and give it all groups as a array
6895: closure.call((Object) groups.toArray());
6896: } else {
6897: closure.call((Object[]) groups.toArray());
6898: }
6899: }
6900: }
6901:
6902: /**
6903: * Process each matched substring of the given group matcher. The object
6904: * passed to the closure is an array of strings, matched per a successful match.
6905: *
6906: * @param self the source matcher
6907: * @param closure a closure
6908: */
6909: public static void each(Matcher self, Closure closure) {
6910: Matcher m = self;
6911: while (m.find()) {
6912: int count = m.groupCount();
6913: ArrayList groups = new ArrayList();
6914: for (int i = 0; i <= count; i++) {
6915: groups.add(m.group(i));
6916: }
6917: closure.call((Object[]) groups.toArray());
6918: }
6919: }
6920:
6921: /**
6922: * Iterates over every element of the collection and return the index of the first object
6923: * that matches the condition specified in the closure
6924: *
6925: * @param self the iteration object over which we iterate
6926: * @param closure the filter to perform a match on the collection
6927: * @return an integer that is the index of the first macthed object.
6928: */
6929: public static int findIndexOf(Object self, Closure closure) {
6930: int i = 0;
6931: for (Iterator iter = InvokerHelper.asIterator(self); iter
6932: .hasNext(); i++) {
6933: Object value = iter.next();
6934: if (DefaultTypeTransformation.castToBoolean(closure
6935: .call(value))) {
6936: break;
6937: }
6938: }
6939: return i;
6940: }
6941:
6942: /**
6943: * Iterates through the class loader parents until it finds a loader with a class
6944: * named equal to org.codehaus.groovy.tools.RootLoader. If there is no such class
6945: * null will be returned. The name has to be used because a direct compare with
6946: * == may fail as the class may be loaded through different classloaders.
6947: *
6948: * @see org.codehaus.groovy.tools.RootLoader
6949: */
6950: public static ClassLoader getRootLoader(ClassLoader cl) {
6951: while (true) {
6952: if (cl == null)
6953: return null;
6954: if (cl.getClass().getName().equals(
6955: RootLoader.class.getName()))
6956: return cl;
6957: cl = cl.getParent();
6958: }
6959: }
6960:
6961: /**
6962: * Converts a given object to a type. This method is used through
6963: * the "as" operator and is overloadable as any other operator.
6964: *
6965: * @param obj the object to convert
6966: * @param type the goal type
6967: * @return the resulting object
6968: */
6969: public static Object asType(Object obj, Class type) {
6970: return DefaultTypeTransformation.castToType(obj, type);
6971: }
6972:
6973: public static Object newInstance(Class c) {
6974: return InvokerHelper.getInstance().invokeConstructorOf(c, null);
6975: }
6976:
6977: public static Object newInstance(Class c, Object[] args) {
6978: if (args == null)
6979: args = new Object[] { null };
6980: return InvokerHelper.getInstance().invokeConstructorOf(c, args);
6981: }
6982:
6983: /**
6984: * A Runnable which waits for a process to complete together with a notification scheme
6985: * allowing another thread to wait a maximum number of seconds for the process to complete
6986: * before killing it.
6987: */
6988: protected static class ProcessRunner implements Runnable {
6989: Process process;
6990: private boolean finished;
6991:
6992: public ProcessRunner(Process process) {
6993: this .process = process;
6994: }
6995:
6996: public void run() {
6997: try {
6998: process.waitFor();
6999: } catch (InterruptedException e) {
7000: }
7001: synchronized (this ) {
7002: notifyAll();
7003: finished = true;
7004: }
7005: }
7006:
7007: public synchronized void waitForOrKill(long millis) {
7008: if (!finished) {
7009: try {
7010: wait(millis);
7011: } catch (InterruptedException e) {
7012: }
7013: if (!finished) {
7014: process.destroy();
7015: }
7016: }
7017: }
7018: }
7019:
7020: protected static class RangeInfo {
7021: protected int from, to;
7022: protected boolean reverse;
7023:
7024: public RangeInfo(int from, int to, boolean reverse) {
7025: this .from = from;
7026: this .to = to;
7027: this .reverse = reverse;
7028: }
7029: }
7030:
7031: private static class Dumper implements Runnable {
7032: InputStream in;
7033:
7034: public Dumper(InputStream in) {
7035: this .in = in;
7036: }
7037:
7038: public void run() {
7039: InputStreamReader isr = new InputStreamReader(in);
7040: BufferedReader br = new BufferedReader(isr);
7041: try {
7042: while (br.readLine() != null) {
7043: }
7044: } catch (IOException e) {
7045: throw new GroovyRuntimeException(
7046: "exception while reading process stream", e);
7047: }
7048: }
7049: }
7050:
7051: public static Iterator iterator(Object o) {
7052: return DefaultTypeTransformation.asCollection(o).iterator();
7053: }
7054:
7055: /**
7056: * @return an Iterator for an Enumeration
7057: */
7058: public static Iterator iterator(final Enumeration enumeration) {
7059: return new Iterator() {
7060: private Object last;
7061:
7062: public boolean hasNext() {
7063: return enumeration.hasMoreElements();
7064: }
7065:
7066: public Object next() {
7067: last = enumeration.nextElement();
7068: return last;
7069: }
7070:
7071: public void remove() {
7072: throw new UnsupportedOperationException(
7073: "Cannot remove() from an Enumeration");
7074: }
7075: };
7076: }
7077:
7078: // TODO move into DOMCategory once we can make use of optional categories transparent
7079:
7080: /**
7081: * @return an Iterator for a NodeList
7082: */
7083: public static Iterator iterator(final NodeList nodeList) {
7084: return new Iterator() {
7085: private int current /* = 0 */;
7086:
7087: public boolean hasNext() {
7088: return current < nodeList.getLength();
7089: }
7090:
7091: public Object next() {
7092: return nodeList.item(current++);
7093: }
7094:
7095: public void remove() {
7096: throw new UnsupportedOperationException(
7097: "Cannot remove() from a NodeList iterator");
7098: }
7099: };
7100: }
7101:
7102: /**
7103: * @return an Iterator for a Matcher
7104: */
7105: public static Iterator iterator(final Matcher matcher) {
7106: return new Iterator() {
7107: private boolean found /* = false */;
7108: private boolean done /* = false */;
7109:
7110: public boolean hasNext() {
7111: if (done) {
7112: return false;
7113: }
7114: if (!found) {
7115: found = matcher.find();
7116: if (!found) {
7117: done = true;
7118: }
7119: }
7120: return found;
7121: }
7122:
7123: public Object next() {
7124: if (!found) {
7125: if (!hasNext()) {
7126: throw new NoSuchElementException();
7127: }
7128: }
7129: found = false;
7130: return matcher.group();
7131: }
7132:
7133: public void remove() {
7134: throw new UnsupportedOperationException();
7135: }
7136: };
7137: }
7138:
7139: /**
7140: * @return an Iterator for a Reader
7141: */
7142: public static Iterator iterator(Reader value) {
7143: final BufferedReader bufferedReader;
7144: if (value instanceof BufferedReader)
7145: bufferedReader = (BufferedReader) value;
7146: else
7147: bufferedReader = new BufferedReader(value);
7148: return new Iterator() {
7149: String nextVal /* = null */;
7150: boolean nextMustRead = true;
7151: boolean hasNext = true;
7152:
7153: public boolean hasNext() {
7154: if (nextMustRead && hasNext) {
7155: try {
7156: nextVal = readNext();
7157: nextMustRead = false;
7158: } catch (IOException e) {
7159: hasNext = false;
7160: }
7161: }
7162: return hasNext;
7163: }
7164:
7165: public Object next() {
7166: String retval = null;
7167: if (nextMustRead) {
7168: try {
7169: retval = readNext();
7170: } catch (IOException e) {
7171: hasNext = false;
7172: }
7173: } else
7174: retval = nextVal;
7175: nextMustRead = true;
7176: return retval;
7177: }
7178:
7179: private String readNext() throws IOException {
7180: String nv = bufferedReader.readLine();
7181: if (nv == null)
7182: hasNext = false;
7183: return nv;
7184: }
7185:
7186: public void remove() {
7187: throw new UnsupportedOperationException(
7188: "Cannot remove() from a Reader Iterator");
7189: }
7190: };
7191: }
7192:
7193: /**
7194: * Standard iterator for a input stream which iterates through the stream content in a byte-based fashion.
7195: *
7196: * @param is
7197: * @return
7198: */
7199: public static Iterator iterator(InputStream is) {
7200: return iterator(new DataInputStream(is));
7201: }
7202:
7203: /**
7204: * Standard iterator for a data input stream which iterates through the stream content in a byte-based fashion.
7205: *
7206: * @param dis
7207: * @return
7208: */
7209: public static Iterator iterator(final DataInputStream dis) {
7210: return new Iterator() {
7211: Byte nextVal;
7212: boolean nextMustRead = true;
7213: boolean hasNext = true;
7214:
7215: public boolean hasNext() {
7216: if (nextMustRead && hasNext) {
7217: try {
7218: byte bPrimitive = dis.readByte();
7219: nextVal = new Byte(bPrimitive);
7220: nextMustRead = false;
7221: } catch (IOException e) {
7222: hasNext = false;
7223: }
7224: }
7225: return hasNext;
7226: }
7227:
7228: public Object next() {
7229: Byte retval = null;
7230: if (nextMustRead) {
7231: try {
7232: byte b = dis.readByte();
7233: retval = new Byte(b);
7234: } catch (IOException e) {
7235: hasNext = false;
7236: }
7237: } else
7238: retval = nextVal;
7239: nextMustRead = true;
7240: return retval;
7241: }
7242:
7243: public void remove() {
7244: throw new UnsupportedOperationException(
7245: "Cannot remove() from an InputStream Iterator");
7246: }
7247: };
7248: }
7249:
7250: /**
7251: * Standard iterator for a file which iterates through the file content in a line-based fashion.
7252: *
7253: * @param f
7254: * @return
7255: * @throws IOException
7256: */
7257: public static Iterator iterator(File f) throws IOException {
7258: return iterator(newReader(f));
7259: }
7260:
7261: public static Iterator iterator(Iterator it) {
7262: return it;
7263: }
7264:
7265: }
|