groovy-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cchamp...@apache.org
Subject [05/62] [abbrv] [partial] groovy git commit: Move Java source set into `src/main/java`
Date Sun, 17 Dec 2017 14:01:16 GMT
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
new file mode 100644
index 0000000..eec02af
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -0,0 +1,18930 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.io.FileType;
+import groovy.io.GroovyPrintWriter;
+import groovy.lang.Closure;
+import groovy.lang.DelegatesTo;
+import groovy.lang.DelegatingMetaClass;
+import groovy.lang.EmptyRange;
+import groovy.lang.ExpandoMetaClass;
+import groovy.lang.GString;
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.GroovySystem;
+import groovy.lang.Groovydoc;
+import groovy.lang.IntRange;
+import groovy.lang.ListWithDefault;
+import groovy.lang.MapWithDefault;
+import groovy.lang.MetaClass;
+import groovy.lang.MetaClassImpl;
+import groovy.lang.MetaClassRegistry;
+import groovy.lang.MetaMethod;
+import groovy.lang.MetaProperty;
+import groovy.lang.MissingPropertyException;
+import groovy.lang.ObjectRange;
+import groovy.lang.PropertyValue;
+import groovy.lang.Range;
+import groovy.lang.SpreadMap;
+import groovy.lang.Tuple2;
+import groovy.lang.Writable;
+import groovy.transform.stc.ClosureParams;
+import groovy.transform.stc.FirstParam;
+import groovy.transform.stc.FromString;
+import groovy.transform.stc.MapEntryOrKeyValue;
+import groovy.transform.stc.SimpleType;
+import groovy.util.BufferedIterator;
+import groovy.util.ClosureComparator;
+import groovy.util.GroovyCollections;
+import groovy.util.MapEntry;
+import groovy.util.OrderBy;
+import groovy.util.PermutationGenerator;
+import groovy.util.ProxyGenerator;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.reflection.ClassInfo;
+import org.codehaus.groovy.reflection.MixinInMetaClass;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.reflection.stdclasses.CachedSAMClass;
+import org.codehaus.groovy.runtime.callsite.BooleanClosureWrapper;
+import org.codehaus.groovy.runtime.callsite.BooleanReturningMethodInvoker;
+import org.codehaus.groovy.runtime.dgmimpl.NumberNumberDiv;
+import org.codehaus.groovy.runtime.dgmimpl.NumberNumberMinus;
+import org.codehaus.groovy.runtime.dgmimpl.NumberNumberMultiply;
+import org.codehaus.groovy.runtime.dgmimpl.NumberNumberPlus;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.BooleanArrayGetAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.BooleanArrayPutAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.ByteArrayGetAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.ByteArrayPutAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.CharacterArrayGetAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.CharacterArrayPutAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.DoubleArrayGetAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.DoubleArrayPutAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.FloatArrayGetAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.FloatArrayPutAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.IntegerArrayGetAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.IntegerArrayPutAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.LongArrayGetAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.LongArrayPutAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.ObjectArrayGetAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.ObjectArrayPutAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.ShortArrayGetAtMetaMethod;
+import org.codehaus.groovy.runtime.dgmimpl.arrays.ShortArrayPutAtMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
+import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.runtime.typehandling.GroovyCastException;
+import org.codehaus.groovy.runtime.typehandling.NumberMath;
+import org.codehaus.groovy.tools.RootLoader;
+import org.codehaus.groovy.transform.trait.Traits;
+import org.codehaus.groovy.util.ArrayIterator;
+import org.codehaus.groovy.util.IteratorBufferedIterator;
+import org.codehaus.groovy.util.ListBufferedIterator;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.net.MalformedURLException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.text.MessageFormat;
+import java.util.AbstractCollection;
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.Stack;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.BlockingQueue;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class defines new groovy methods which appear on normal JDK
+ * classes inside the Groovy environment. Static methods are used with the
+ * first parameter being the destination class,
+ * i.e. <code>public static String reverse(String self)</code>
+ * provides a <code>reverse()</code> method for <code>String</code>.
+ * <p>
+ * NOTE: While this class contains many 'public' static methods, it is
+ * primarily regarded as an internal class (its internal package name
+ * suggests this also). We value backwards compatibility of these
+ * methods when used within Groovy but value less backwards compatibility
+ * at the Java method call level. I.e. future versions of Groovy may
+ * remove or move a method call in this file but would normally
+ * aim to keep the method available from within Groovy.
+ */
+public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
+
+    private static final Logger LOG = Logger.getLogger(DefaultGroovyMethods.class.getName());
+    private static final Integer ONE = 1;
+    private static final BigInteger BI_INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+    private static final BigInteger BI_INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE);
+    private static final BigInteger BI_LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
+    private static final BigInteger BI_LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE);
+
+    public static final Class[] ADDITIONAL_CLASSES = {
+            NumberNumberPlus.class,
+            NumberNumberMultiply.class,
+            NumberNumberMinus.class,
+            NumberNumberDiv.class,
+            ObjectArrayGetAtMetaMethod.class,
+            ObjectArrayPutAtMetaMethod.class,
+            BooleanArrayGetAtMetaMethod.class,
+            BooleanArrayPutAtMetaMethod.class,
+            ByteArrayGetAtMetaMethod.class,
+            ByteArrayPutAtMetaMethod.class,
+            CharacterArrayGetAtMetaMethod.class,
+            CharacterArrayPutAtMetaMethod.class,
+            ShortArrayGetAtMetaMethod.class,
+            ShortArrayPutAtMetaMethod.class,
+            IntegerArrayGetAtMetaMethod.class,
+            IntegerArrayPutAtMetaMethod.class,
+            LongArrayGetAtMetaMethod.class,
+            LongArrayPutAtMetaMethod.class,
+            FloatArrayGetAtMetaMethod.class,
+            FloatArrayPutAtMetaMethod.class,
+            DoubleArrayGetAtMetaMethod.class,
+            DoubleArrayPutAtMetaMethod.class,
+    };
+
+    public static final Class[] DGM_LIKE_CLASSES = new Class[]{
+            DefaultGroovyMethods.class,
+            DateGroovyMethods.class,
+            EncodingGroovyMethods.class,
+            IOGroovyMethods.class,
+            ProcessGroovyMethods.class,
+            ResourceGroovyMethods.class,
+            SocketGroovyMethods.class,
+            StringGroovyMethods.class//,
+            // TODO provide alternative way for these to be registered
+//            SqlGroovyMethods.class,
+//            SwingGroovyMethods.class,
+//            XmlGroovyMethods.class,
+//            NioGroovyMethods.class
+    };
+    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+    private static final NumberAwareComparator<Comparable> COMPARABLE_NUMBER_AWARE_COMPARATOR = new NumberAwareComparator<Comparable>();
+
+    /**
+     * Identity check. Since == is overridden in Groovy with the meaning of equality
+     * we need some fallback to check for object identity.  Invoke using the
+     * 'is' method, like so: <code>def same = this.is(that)</code>
+     *
+     * @param self  an object
+     * @param other an object to compare identity with
+     * @return true if self and other are both references to the same
+     *         instance, false otherwise
+     * @since 1.0
+     */
+    public static boolean is(Object self, Object other) {
+        return self == other;
+    }
+
+    /**
+     * Allows the closure to be called for the object reference self.
+     * Synonym for 'with()'.
+     *
+     * @param self    the object to have a closure act upon
+     * @param closure the closure to call on the object
+     * @return result of calling the closure
+     * @see #with(Object, Closure)
+     * @since 1.0
+     */
+    public static <T,U> T identity(
+            @DelegatesTo.Target("self") U self,
+            @DelegatesTo(value=DelegatesTo.Target.class,
+                    target="self",
+                    strategy=Closure.DELEGATE_FIRST)
+            @ClosureParams(FirstParam.class)
+                    Closure<T> closure) {
+        return DefaultGroovyMethods.with(self, closure);
+    }
+
+    /**
+     * Allows the closure to be called for the object reference self.
+     * <p>
+     * Any method invoked inside the closure will first be invoked on the
+     * self reference. For instance, the following method calls to the append()
+     * method are invoked on the StringBuilder instance:
+     * <pre class="groovyTestCase">
+     * def b = new StringBuilder().with {
+     *   append('foo')
+     *   append('bar')
+     *   return it
+     * }
+     * assert b.toString() == 'foobar'
+     * </pre>
+     * This is commonly used to simplify object creation, such as this example:
+     * <pre>
+     * def p = new Person().with {
+     *   firstName = 'John'
+     *   lastName = 'Doe'
+     *   return it
+     * }
+     * </pre>
+     * The other typical usage, uses the self object while creating some value:
+     * <pre>
+     * def fullName = person.with{ "$firstName $lastName" }
+     * </pre>
+     *
+     * @param self    the object to have a closure act upon
+     * @param closure the closure to call on the object
+     * @return result of calling the closure
+     * @see #with(Object, boolean, Closure)
+     * @see #tap(Object, Closure)
+     * @since 1.5.0
+     */
+    @SuppressWarnings("unchecked")
+    public static <T,U> T with(
+            @DelegatesTo.Target("self") U self,
+            @DelegatesTo(value=DelegatesTo.Target.class,
+                    target="self",
+                    strategy=Closure.DELEGATE_FIRST)
+            @ClosureParams(FirstParam.class)
+            Closure<T> closure) {
+        return (T) with(self, false, (Closure<Object>)closure);
+    }
+
+    /**
+     * Allows the closure to be called for the object reference self.
+     * <p/>
+     * Any method invoked inside the closure will first be invoked on the
+     * self reference. For example, the following method calls to the append()
+     * method are invoked on the StringBuilder instance and then, because
+     * 'returning' is true, the self instance is returned:
+     * <pre class="groovyTestCase">
+     * def b = new StringBuilder().with(true) {
+     *   append('foo')
+     *   append('bar')
+     * }
+     * assert b.toString() == 'foobar'
+     * </pre>
+     * The returning parameter is commonly set to true when using with to simplify object
+     * creation, such as this example:
+     * <pre>
+     * def p = new Person().with(true) {
+     *   firstName = 'John'
+     *   lastName = 'Doe'
+     * }
+     * </pre>
+     * Alternatively, 'tap' is an alias for 'with(true)', so that method can be used instead.
+     *
+     * The other main use case for with is when returning a value calculated using self as shown here:
+     * <pre>
+     * def fullName = person.with(false){ "$firstName $lastName" }
+     * </pre>
+     * Alternatively, 'with' is an alias for 'with(false)', so the boolean parameter can be ommitted instead.
+     *
+     * @param self      the object to have a closure act upon
+     * @param returning if true, return the self object; otherwise, the result of calling the closure
+     * @param closure   the closure to call on the object
+     * @return the self object or the result of calling the closure depending on 'returning'
+     * @see #with(Object, Closure)
+     * @see #tap(Object, Closure)
+     * @since 2.5.0
+     */
+    public static <T,U extends T, V extends T> T with(
+            @DelegatesTo.Target("self") U self,
+            boolean returning,
+            @DelegatesTo(value=DelegatesTo.Target.class,
+                    target="self",
+                    strategy=Closure.DELEGATE_FIRST)
+            @ClosureParams(FirstParam.class)
+            Closure<T> closure) {
+        @SuppressWarnings("unchecked")
+        final Closure<V> clonedClosure = (Closure<V>) closure.clone();
+        clonedClosure.setResolveStrategy(Closure.DELEGATE_FIRST);
+        clonedClosure.setDelegate(self);
+        V result = clonedClosure.call(self);
+        return returning ? self : result;
+    }
+
+    /**
+     * Allows the closure to be called for the object reference self (similar
+     * to <code>with</code> and always returns self.
+     * <p>
+     * Any method invoked inside the closure will first be invoked on the
+     * self reference. For instance, the following method calls to the append()
+     * method are invoked on the StringBuilder instance:
+     * <pre>
+     * def b = new StringBuilder().tap {
+     *   append('foo')
+     *   append('bar')
+     * }
+     * assert b.toString() == 'foobar'
+     * </pre>
+     * This is commonly used to simplify object creation, such as this example:
+     * <pre>
+     * def p = new Person().tap {
+     *   firstName = 'John'
+     *   lastName = 'Doe'
+     * }
+     * </pre>
+     *
+     * @param self    the object to have a closure act upon
+     * @param closure the closure to call on the object
+     * @return self
+     * @see #with(Object, boolean, Closure)
+     * @see #with(Object, Closure)
+     * @since 2.5.0
+     */
+    @SuppressWarnings("unchecked")
+    public static <T,U> U tap(
+            @DelegatesTo.Target("self") U self,
+            @DelegatesTo(value=DelegatesTo.Target.class,
+                    target="self",
+                    strategy=Closure.DELEGATE_FIRST)
+            @ClosureParams(FirstParam.class)
+            Closure<T> closure) {
+        return (U) with(self, true, (Closure<Object>)closure);
+    }
+
+    /**
+     * Allows the subscript operator to be used to lookup dynamic property values.
+     * <code>bean[somePropertyNameExpression]</code>. The normal property notation
+     * of groovy is neater and more concise but only works with compile-time known
+     * property names.
+     *
+     * @param self     the object to act upon
+     * @param property the property name of interest
+     * @return the property value
+     * @since 1.0
+     */
+    public static Object getAt(Object self, String property) {
+        return InvokerHelper.getProperty(self, property);
+    }
+
+    /**
+     * Allows the subscript operator to be used to set dynamically named property values.
+     * <code>bean[somePropertyNameExpression] = foo</code>. The normal property notation
+     * of groovy is neater and more concise but only works with property names which
+     * are known at compile time.
+     *
+     * @param self     the object to act upon
+     * @param property the name of the property to set
+     * @param newValue the value to set
+     * @since 1.0
+     */
+    public static void putAt(Object self, String property, Object newValue) {
+        InvokerHelper.setProperty(self, property, newValue);
+    }
+
+    /**
+     * Generates a detailed dump string of an object showing its class,
+     * hashCode and fields.
+     *
+     * @param self an object
+     * @return the dump representation
+     * @since 1.0
+     */
+    public static String dump(Object self) {
+        if (self == null) {
+            return "null";
+        }
+        StringBuilder buffer = new StringBuilder("<");
+        Class klass = self.getClass();
+        buffer.append(klass.getName());
+        buffer.append("@");
+        buffer.append(Integer.toHexString(self.hashCode()));
+        boolean groovyObject = self instanceof GroovyObject;
+
+        /*jes this may be rewritten to use the new getProperties() stuff
+         * but the original pulls out private variables, whereas getProperties()
+         * does not. What's the real use of dump() here?
+         */
+        while (klass != null) {
+            for (final Field field : klass.getDeclaredFields()) {
+                if ((field.getModifiers() & Modifier.STATIC) == 0) {
+                    if (groovyObject && field.getName().equals("metaClass")) {
+                        continue;
+                    }
+                    AccessController.doPrivileged(new PrivilegedAction() {
+                        public Object run() {
+                            field.setAccessible(true);
+                            return null;
+                        }
+                    });
+                    buffer.append(" ");
+                    buffer.append(field.getName());
+                    buffer.append("=");
+                    try {
+                        buffer.append(InvokerHelper.toString(field.get(self)));
+                    } catch (Exception e) {
+                        buffer.append(e);
+                    }
+                }
+            }
+
+            klass = klass.getSuperclass();
+        }
+
+        /* here is a different implementation that uses getProperties(). I have left
+         * it commented out because it returns a slightly different list of properties;
+         * i.e. it does not return privates. I don't know what dump() really should be doing,
+         * although IMO showing private fields is a no-no
+         */
+        /*
+        List props = getProperties(self);
+            for(Iterator itr = props.keySet().iterator(); itr.hasNext(); ) {
+            String propName = itr.next().toString();
+
+            // the original skipped this, so I will too
+            if(pv.getName().equals("class")) continue;
+            if(pv.getName().equals("metaClass")) continue;
+
+            buffer.append(" ");
+            buffer.append(propName);
+            buffer.append("=");
+            try {
+                buffer.append(InvokerHelper.toString(props.get(propName)));
+            }
+            catch (Exception e) {
+                buffer.append(e);
+            }
+        }
+        */
+
+        buffer.append(">");
+        return buffer.toString();
+    }
+
+    /**
+     * Retrieves the list of {@link groovy.lang.MetaProperty} objects for 'self' and wraps it
+     * in a list of {@link groovy.lang.PropertyValue} objects that additionally provide
+     * the value for each property of 'self'.
+     *
+     * @param self the receiver object
+     * @return list of {@link groovy.lang.PropertyValue} objects
+     * @see groovy.util.Expando#getMetaPropertyValues()
+     * @since 1.0
+     */
+    public static List<PropertyValue> getMetaPropertyValues(Object self) {
+        MetaClass metaClass = InvokerHelper.getMetaClass(self);
+        List<MetaProperty> mps = metaClass.getProperties();
+        List<PropertyValue> props = new ArrayList<PropertyValue>(mps.size());
+        for (MetaProperty mp : mps) {
+            props.add(new PropertyValue(self, mp));
+        }
+        return props;
+    }
+
+    /**
+     * Convenience method that calls {@link #getMetaPropertyValues(java.lang.Object)}(self)
+     * and provides the data in form of simple key/value pairs, i.e. without
+     * type() information.
+     *
+     * @param self the receiver object
+     * @return meta properties as Map of key/value pairs
+     * @since 1.0
+     */
+    public static Map getProperties(Object self) {
+        List<PropertyValue> metaProps = getMetaPropertyValues(self);
+        Map<String, Object> props = new LinkedHashMap<String, Object>(metaProps.size());
+
+        for (PropertyValue mp : metaProps) {
+            try {
+                props.put(mp.getName(), mp.getValue());
+            } catch (Exception e) {
+                LOG.throwing(self.getClass().getName(), "getProperty(" + mp.getName() + ")", e);
+            }
+        }
+        return props;
+    }
+
+    /**
+     * Scoped use method
+     *
+     * @param self          any Object
+     * @param categoryClass a category class to use
+     * @param closure       the closure to invoke with the category in place
+     * @return the value returned from the closure
+     * @since 1.0
+     */
+    public static <T> T use(Object self, Class categoryClass, Closure<T> closure) {
+        return GroovyCategorySupport.use(categoryClass, closure);
+    }
+
+    /**
+     * Extend object with category methods.
+     * All methods for given class and all super classes will be added to the object.
+     *
+     * @param self          any Class
+     * @param categoryClasses a category classes to use
+     * @since 1.6.0
+     */
+    public static void mixin(MetaClass self, List<Class> categoryClasses) {
+        MixinInMetaClass.mixinClassesToMetaClass(self, categoryClasses);
+    }
+
+    /**
+     * Extend class globally with category methods.
+     * All methods for given class and all super classes will be added to the class.
+     *
+     * @param self          any Class
+     * @param categoryClasses a category classes to use
+     * @since 1.6.0
+     */
+    public static void mixin(Class self, List<Class> categoryClasses) {
+        mixin(getMetaClass(self), categoryClasses);
+    }
+
+    /**
+     * Extend class globally with category methods.
+     *
+     * @param self          any Class
+     * @param categoryClass a category class to use
+     * @since 1.6.0
+     */
+    public static void mixin(Class self, Class categoryClass) {
+        mixin(getMetaClass(self), Collections.singletonList(categoryClass));
+    }
+
+    /**
+     * Extend class globally with category methods.
+     *
+     * @param self          any Class
+     * @param categoryClass a category class to use
+     * @since 1.6.0
+     */
+    public static void mixin(Class self, Class[] categoryClass) {
+        mixin(getMetaClass(self), Arrays.asList(categoryClass));
+    }
+
+    /**
+     * Extend class globally with category methods.
+     *
+     * @param self          any Class
+     * @param categoryClass a category class to use
+     * @since 1.6.0
+     */
+    public static void mixin(MetaClass self, Class categoryClass) {
+        mixin(self, Collections.singletonList(categoryClass));
+    }
+
+    /**
+     * Extend class globally with category methods.
+     *
+     * @param self          any Class
+     * @param categoryClass a category class to use
+     * @since 1.6.0
+     */
+    public static void mixin(MetaClass self, Class[] categoryClass) {
+        mixin(self, Arrays.asList(categoryClass));
+    }
+
+    /**
+     * Scoped use method with list of categories.
+     *
+     * @param self              any Object
+     * @param categoryClassList a list of category classes
+     * @param closure           the closure to invoke with the categories in place
+     * @return the value returned from the closure
+     * @since 1.0
+     */
+    public static <T> T use(Object self, List<Class> categoryClassList, Closure<T> closure) {
+        return GroovyCategorySupport.use(categoryClassList, closure);
+    }
+
+    /**
+     * Allows the usage of addShutdownHook without getting the runtime first.
+     *
+     * @param self    the object the method is called on (ignored)
+     * @param closure the shutdown hook action
+     * @since 1.5.0
+     */
+    public static void addShutdownHook(Object self, Closure closure) {
+        Runtime.getRuntime().addShutdownHook(new Thread(closure));
+    }
+
+    /**
+     * Allows you to use a list of categories, specifying the list as varargs.
+     * <code>use(CategoryClass1, CategoryClass2) { ... }</code>
+     * This method saves having to wrap the the category
+     * classes in a list.
+     *
+     * @param self  any Object
+     * @param array a list of category classes and a Closure
+     * @return the value returned from the closure
+     * @since 1.0
+     */
+    public static Object use(Object self, Object[] array) {
+        if (array.length < 2)
+            throw new IllegalArgumentException(
+                    "Expecting at least 2 arguments, a category class and a Closure");
+        Closure closure;
+        try {
+            closure = (Closure) array[array.length - 1];
+        } catch (ClassCastException e) {
+            throw new IllegalArgumentException("Expecting a Closure to be the last argument");
+        }
+        List<Class> list = new ArrayList<Class>(array.length - 1);
+        for (int i = 0; i < array.length - 1; ++i) {
+            Class categoryClass;
+            try {
+                categoryClass = (Class) array[i];
+            } catch (ClassCastException e) {
+                throw new IllegalArgumentException("Expecting a Category Class for argument " + i);
+            }
+            list.add(categoryClass);
+        }
+        return GroovyCategorySupport.use(list, closure);
+    }
+
+    /**
+     * Print a value formatted Groovy style to self if it
+     * is a Writer, otherwise to the standard output stream.
+     *
+     * @param self  any Object
+     * @param value the value to print
+     * @since 1.0
+     */
+    public static void print(Object self, Object value) {
+        // we won't get here if we are a PrintWriter
+        if (self instanceof Writer) {
+            try {
+                ((Writer) self).write(InvokerHelper.toString(value));
+            } catch (IOException e) {
+                // TODO: Should we have some unified function like PrintWriter.checkError()?
+            }
+        } else {
+            System.out.print(InvokerHelper.toString(value));
+        }
+    }
+
+    /**
+     * Print a value formatted Groovy style to the print writer.
+     *
+     * @param self  a PrintWriter
+     * @param value the value to print
+     * @since 1.0
+     */
+    public static void print(PrintWriter self, Object value) {
+        self.print(InvokerHelper.toString(value));
+    }
+
+    /**
+     * Print a value formatted Groovy style to the print stream.
+     *
+     * @param self  a PrintStream
+     * @param value the value to print
+     * @since 1.6.0
+     */
+    public static void print(PrintStream self, Object value) {
+        self.print(InvokerHelper.toString(value));
+    }
+
+    /**
+     * Print a value to the standard output stream.
+     * This method delegates to the owner to execute the method.
+     *
+     * @param self  a generated closure
+     * @param value the value to print
+     * @since 1.0
+     */
+    public static void print(Closure self, Object value) {
+        Object owner = getClosureOwner(self);
+        InvokerHelper.invokeMethod(owner, "print", new Object[]{value});
+    }
+
+    /**
+     * Print a linebreak to the standard output stream.
+     *
+     * @param self any Object
+     * @since 1.0
+     */
+    public static void println(Object self) {
+        // we won't get here if we are a PrintWriter
+        if (self instanceof Writer) {
+            PrintWriter pw = new GroovyPrintWriter((Writer) self);
+            pw.println();
+        } else {
+            System.out.println();
+        }
+    }
+
+    /**
+     * Print a linebreak to the standard output stream.
+     * This method delegates to the owner to execute the method.
+     *
+     * @param self  a closure
+     * @since 1.0
+     */
+    public static void println(Closure self) {
+        Object owner = getClosureOwner(self);
+        InvokerHelper.invokeMethod(owner, "println", EMPTY_OBJECT_ARRAY);
+    }
+
+    private static Object getClosureOwner(Closure cls) {
+        Object owner =  cls.getOwner();
+        while (owner instanceof GeneratedClosure) {
+            owner = ((Closure) owner).getOwner();
+        }
+        return owner;
+    }
+
+    /**
+     * Print a value formatted Groovy style (followed by a newline) to self
+     * if it is a Writer, otherwise to the standard output stream.
+     *
+     * @param self  any Object
+     * @param value the value to print
+     * @since 1.0
+     */
+    public static void println(Object self, Object value) {
+        // we won't get here if we are a PrintWriter
+        if (self instanceof Writer) {
+            final PrintWriter pw = new GroovyPrintWriter((Writer) self);
+            pw.println(value);
+        } else {
+            System.out.println(InvokerHelper.toString(value));
+        }
+    }
+
+    /**
+     * Print a value formatted Groovy style (followed by a newline) to the print writer.
+     *
+     * @param self  a PrintWriter
+     * @param value the value to print
+     * @since 1.0
+     */
+    public static void println(PrintWriter self, Object value) {
+        self.println(InvokerHelper.toString(value));
+    }
+
+    /**
+     * Print a value formatted Groovy style (followed by a newline) to the print stream.
+     *
+     * @param self  any Object
+     * @param value the value to print
+     * @since 1.6.0
+     */
+    public static void println(PrintStream self, Object value) {
+        self.println(InvokerHelper.toString(value));
+    }
+
+    /**
+     * Print a value (followed by a newline) to the standard output stream.
+     * This method delegates to the owner to execute the method.
+     *
+     * @param self  a closure
+     * @param value the value to print
+     * @since 1.0
+     */
+    public static void println(Closure self, Object value) {
+        Object owner = getClosureOwner(self);
+        InvokerHelper.invokeMethod(owner, "println", new Object[]{value});
+    }
+
+    /**
+     * Printf to a console.
+     *
+     * @param self   any Object
+     * @param format a format string
+     * @param values values referenced by the format specifiers in the format string.
+     * @since 1.0
+     */
+    public static void printf(Object self, String format, Object[] values) {
+        if (self instanceof PrintStream)
+            ((PrintStream)self).printf(format, values);
+        else
+            System.out.printf(format, values);
+    }
+
+    /**
+     * Sprintf to a string.
+     *
+     * @param self   any Object
+     * @param format a format string
+     * @param values values referenced by the format specifiers in the format string.
+     * @return the resulting formatted string
+     * @since 1.5.0
+     */
+    public static String sprintf(Object self, String format, Object[] values) {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(outputStream);
+        out.printf(format, values);
+        return outputStream.toString();
+    }
+
+    /**
+     * Prints a formatted string using the specified format string and
+     * arguments.
+     * <p>
+     * Examples:
+     * <pre>
+     *     printf ( "Hello, %s!\n" , [ "world" ] as String[] )
+     *     printf ( "Hello, %s!\n" , [ "Groovy" ])
+     *     printf ( "%d + %d = %d\n" , [ 1 , 2 , 1+2 ] as Integer[] )
+     *     printf ( "%d + %d = %d\n" , [ 3 , 3 , 3+3 ])
+     *
+     *     ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as Integer[] ) }
+     *     ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as int[] ) }
+     *     ( 0x41..0x45 ).each { printf ( "-- %c\n" , [ it ] as char[] ) }
+     *     ( 07..011 ).each { printf ( "-- %d\n" , [ it ] as byte[] ) }
+     *     ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as short[] ) }
+     *     ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as long[] ) }
+     *     ( 7..11 ).each { printf ( "-- %5.2f\n" , [ it ] as float[] ) }
+     *     ( 7..11 ).each { printf ( "-- %5.2g\n" , [ it ] as double[] ) }
+     * </pre>
+     *
+     * @param self   any Object
+     * @param format A format string
+     * @param arg    Argument which is referenced by the format specifiers in the format
+     *               string.  The type of <code>arg</code> should be one of Object[], List,
+     *               int[], short[], byte[], char[], boolean[], long[], float[], or double[].
+     * @since 1.0
+     */
+    public static void printf(Object self, String format, Object arg) {
+        if (self instanceof PrintStream)
+            printf((PrintStream) self, format, arg);
+        else if (self instanceof Writer)
+            printf((Writer) self, format, arg);
+        else
+            printf(System.out, format, arg);
+    }
+
+    private static void printf(PrintStream self, String format, Object arg) {
+        self.print(sprintf(self, format, arg));
+    }
+
+    private static void printf(Writer self, String format, Object arg) {
+        try {
+            self.write(sprintf(self, format, arg));
+        } catch (IOException e) {
+            printf(System.out, format, arg);
+        }
+    }
+
+    /**
+     * Returns a formatted string using the specified format string and
+     * arguments.
+     *
+     * @param self   any Object
+     * @param format A format string
+     * @param arg    Argument which is referenced by the format specifiers in the format
+     *               string.  The type of <code>arg</code> should be one of Object[], List,
+     *               int[], short[], byte[], char[], boolean[], long[], float[], or double[].
+     * @return the resulting printf'd string
+     * @since 1.5.0
+     */
+    public static String sprintf(Object self, String format, Object arg) {
+        if (arg instanceof Object[]) {
+            return sprintf(self, format, (Object[]) arg);
+        }
+        if (arg instanceof List) {
+            return sprintf(self, format, ((List) arg).toArray());
+        }
+        if (!arg.getClass().isArray()) {
+            Object[] o = (Object[]) java.lang.reflect.Array.newInstance(arg.getClass(), 1);
+            o[0] = arg;
+            return sprintf(self, format, o);
+        }
+
+        Object[] ans;
+        String elemType = arg.getClass().getName();
+        if (elemType.equals("[I")) {
+            int[] ia = (int[]) arg;
+            ans = new Integer[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = ia[i];
+            }
+        } else if (elemType.equals("[C")) {
+            char[] ca = (char[]) arg;
+            ans = new Character[ca.length];
+            for (int i = 0; i < ca.length; i++) {
+                ans[i] = ca[i];
+            }
+        } else if (elemType.equals("[Z")) {
+            boolean[] ba = (boolean[]) arg;
+            ans = new Boolean[ba.length];
+            for (int i = 0; i < ba.length; i++) {
+                ans[i] = ba[i];
+            }
+        } else if (elemType.equals("[B")) {
+            byte[] ba = (byte[]) arg;
+            ans = new Byte[ba.length];
+            for (int i = 0; i < ba.length; i++) {
+                ans[i] = ba[i];
+            }
+        } else if (elemType.equals("[S")) {
+            short[] sa = (short[]) arg;
+            ans = new Short[sa.length];
+            for (int i = 0; i < sa.length; i++) {
+                ans[i] = sa[i];
+            }
+        } else if (elemType.equals("[F")) {
+            float[] fa = (float[]) arg;
+            ans = new Float[fa.length];
+            for (int i = 0; i < fa.length; i++) {
+                ans[i] = fa[i];
+            }
+        } else if (elemType.equals("[J")) {
+            long[] la = (long[]) arg;
+            ans = new Long[la.length];
+            for (int i = 0; i < la.length; i++) {
+                ans[i] = la[i];
+            }
+        } else if (elemType.equals("[D")) {
+            double[] da = (double[]) arg;
+            ans = new Double[da.length];
+            for (int i = 0; i < da.length; i++) {
+                ans[i] = da[i];
+            }
+        } else {
+            throw new RuntimeException("sprintf(String," + arg + ")");
+        }
+        return sprintf(self, format, ans);
+    }
+
+
+    /**
+     * Inspects returns the String that matches what would be typed into a
+     * terminal to create this object.
+     *
+     * @param self any Object
+     * @return a String that matches what would be typed into a terminal to
+     *         create this object. e.g. [1, 'hello'].inspect() -> [1, "hello"]
+     * @since 1.0
+     */
+    public static String inspect(Object self) {
+        return InvokerHelper.inspect(self);
+    }
+
+    /**
+     * Print to a console in interactive format.
+     *
+     * @param self any Object
+     * @param out  the PrintWriter used for printing
+     * @since 1.0
+     */
+    public static void print(Object self, PrintWriter out) {
+        if (out == null) {
+            out = new PrintWriter(System.out);
+        }
+        out.print(InvokerHelper.toString(self));
+    }
+
+    /**
+     * Print to a console in interactive format.
+     *
+     * @param self any Object
+     * @param out  the PrintWriter used for printing
+     * @since 1.0
+     */
+    public static void println(Object self, PrintWriter out) {
+        if (out == null) {
+            out = new PrintWriter(System.out);
+        }
+        out.println(InvokerHelper.toString(self));
+    }
+
+    /**
+     * Provide a dynamic method invocation method which can be overloaded in
+     * classes to implement dynamic proxies easily.
+     *
+     * @param object    any Object
+     * @param method    the name of the method to call
+     * @param arguments the arguments to use
+     * @return the result of the method call
+     * @since 1.0
+     */
+    public static Object invokeMethod(Object object, String method, Object arguments) {
+        return InvokerHelper.invokeMethod(object, method, arguments);
+    }
+
+    // isCase methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Method for overloading the behavior of the 'case' method in switch statements.
+     * The default implementation handles arrays types but otherwise simply delegates
+     * to Object#equals, but this may be overridden for other types. In this example:
+     * <pre> switch( a ) {
+     *   case b: //some code
+     * }</pre>
+     * "some code" is called when <code>b.isCase( a )</code> returns
+     * <code>true</code>.
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the switchValue is deemed to be equal to the caseValue
+     * @since 1.0
+     */
+    public static boolean isCase(Object caseValue, Object switchValue) {
+        if (caseValue.getClass().isArray()) {
+            return isCase(DefaultTypeTransformation.asCollection(caseValue), switchValue);
+        }
+        return caseValue.equals(switchValue);
+    }
+
+    /**
+     * Special 'Case' implementation for Class, which allows testing
+     * for a certain class in a switch statement.
+     * For example:
+     * <pre>switch( obj ) {
+     *   case List :
+     *     // obj is a list
+     *     break;
+     *   case Set :
+     *     // etc
+     * }</pre>
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the switchValue is deemed to be assignable from the given class
+     * @since 1.0
+     */
+    public static boolean isCase(Class caseValue, Object switchValue) {
+        if (switchValue instanceof Class) {
+            Class val = (Class) switchValue;
+            return caseValue.isAssignableFrom(val);
+        }
+        return caseValue.isInstance(switchValue);
+    }
+
+    /**
+     * 'Case' implementation for collections which tests if the 'switch'
+     * operand is contained in any of the 'case' values.
+     * For example:
+     * <pre class="groovyTestCase">switch( 3 ) {
+     *   case [1,3,5]:
+     *     assert true
+     *     break
+     *   default:
+     *     assert false
+     * }</pre>
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the caseValue is deemed to contain the switchValue
+     * @see java.util.Collection#contains(java.lang.Object)
+     * @since 1.0
+     */
+    public static boolean isCase(Collection caseValue, Object switchValue) {
+        return caseValue.contains(switchValue);
+    }
+
+    /**
+     * 'Case' implementation for maps which tests the groovy truth
+     * value obtained using the 'switch' operand as key.
+     * For example:
+     * <pre class="groovyTestCase">switch( 'foo' ) {
+     *   case [foo:true, bar:false]:
+     *     assert true
+     *     break
+     *   default:
+     *     assert false
+     * }</pre>
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return the groovy truth value from caseValue corresponding to the switchValue key
+     * @since 1.7.6
+     */
+    public static boolean isCase(Map caseValue, Object switchValue) {
+        return DefaultTypeTransformation.castToBoolean(caseValue.get(switchValue));
+    }
+
+    /**
+     * Special 'case' implementation for all numbers, which delegates to the
+     * <code>compareTo()</code> method for comparing numbers of different
+     * types.
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the numbers are deemed equal
+     * @since 1.5.0
+     */
+    public static boolean isCase(Number caseValue, Number switchValue) {
+        return NumberMath.compareTo(caseValue, switchValue) == 0;
+    }
+
+    /**
+     * Returns an iterator equivalent to this iterator with all duplicated items removed
+     * by using Groovy's default number-aware comparator. The original iterator will become
+     * exhausted of elements after determining the unique values. A new iterator
+     * for the unique values will be returned.
+     *
+     * @param self an Iterator
+     * @return a new Iterator of the unique items from the original iterator
+     * @since 1.5.5
+     */
+    public static <T> Iterator<T> unique(Iterator<T> self) {
+        return uniqueItems(new IteratorIterableAdapter<T>(self)).listIterator();
+    }
+
+    /**
+     * Modifies this collection to remove all duplicated items, using Groovy's
+     * default number-aware comparator.
+     * <pre class="groovyTestCase">assert [1,3] == [1,3,3].unique()</pre>
+     *
+     * @param self a collection
+     * @return the now modified collection
+     * @see #unique(Collection, boolean)
+     * @since 1.0
+     */
+    public static <T> Collection<T> unique(Collection<T> self) {
+        return unique(self, true);
+    }
+
+    /**
+     * Modifies this List to remove all duplicated items, using Groovy's
+     * default number-aware comparator.
+     * <pre class="groovyTestCase">assert [1,3] == [1,3,3].unique()</pre>
+     *
+     * @param self a List
+     * @return the now modified List
+     * @see #unique(Collection, boolean)
+     * @since 2.4.0
+     */
+    public static <T> List<T> unique(List<T> self) {
+        return (List<T>) unique((Collection<T>) self, true);
+    }
+
+    /**
+     * Remove all duplicates from a given Collection using Groovy's default number-aware comparator.
+     * If mutate is true, it works by modifying the original object (and also returning it).
+     * If mutate is false, a new collection is returned leaving the original unchanged.
+     * <pre class="groovyTestCase">
+     * assert [1,3] == [1,3,3].unique()
+     * </pre>
+     * <pre class="groovyTestCase">
+     * def orig = [1, 3, 2, 3]
+     * def uniq = orig.unique(false)
+     * assert orig == [1, 3, 2, 3]
+     * assert uniq == [1, 3, 2]
+     * </pre>
+     *
+     * @param self a collection
+     * @param mutate false will cause a new list containing unique items from the collection to be created, true will mutate collections in place
+     * @return the now modified collection
+     * @since 1.8.1
+     */
+    public static <T> Collection<T> unique(Collection<T> self, boolean mutate) {
+        List<T> answer = uniqueItems(self);
+        if (mutate) {
+            self.clear();
+            self.addAll(answer);
+        }
+        return mutate ? self : answer ;
+    }
+
+    private static <T> List<T> uniqueItems(Iterable<T> self) {
+        List<T> answer = new ArrayList<T>();
+        for (T t : self) {
+            boolean duplicated = false;
+            for (T t2 : answer) {
+                if (coercedEquals(t, t2)) {
+                    duplicated = true;
+                    break;
+                }
+            }
+            if (!duplicated)
+                answer.add(t);
+        }
+        return answer;
+    }
+
+    /**
+     * Remove all duplicates from a given List using Groovy's default number-aware comparator.
+     * If mutate is true, it works by modifying the original object (and also returning it).
+     * If mutate is false, a new collection is returned leaving the original unchanged.
+     * <pre class="groovyTestCase">
+     * assert [1,3] == [1,3,3].unique()
+     * </pre>
+     * <pre class="groovyTestCase">
+     * def orig = [1, 3, 2, 3]
+     * def uniq = orig.unique(false)
+     * assert orig == [1, 3, 2, 3]
+     * assert uniq == [1, 3, 2]
+     * </pre>
+     *
+     * @param self a List
+     * @param mutate false will cause a new List containing unique items from the List to be created, true will mutate List in place
+     * @return the now modified List
+     * @since 2.4.0
+     */
+    public static <T> List<T> unique(List<T> self, boolean mutate) {
+        return (List<T>) unique((Collection<T>) self, mutate);
+    }
+
+    /**
+     * Provides a method that compares two comparables using Groovy's
+     * default number aware comparator.
+     *
+     * @param self a Comparable
+     * @param other another Comparable
+     * @return a -ve number, 0 or a +ve number according to Groovy's compareTo contract
+     * @since 1.6.0
+     */
+    public static int numberAwareCompareTo(Comparable self, Comparable other) {
+        return COMPARABLE_NUMBER_AWARE_COMPARATOR.compare(self, other);
+    }
+
+    /**
+     * Returns an iterator equivalent to this iterator but with all duplicated items
+     * removed by using a Closure to determine duplicate (equal) items.
+     * The original iterator will be fully processed after the call.
+     * <p>
+     * If the closure takes a single parameter, the argument passed will be each element,
+     * and the closure should return a value used for comparison (either using
+     * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+     * If the closure takes two parameters, two items from the Iterator
+     * will be passed as arguments, and the closure should return an
+     * int value (with 0 indicating the items are not unique).
+     *
+     * @param self      an Iterator
+     * @param condition a Closure used to determine unique items
+     * @return the modified Iterator
+     * @since 1.5.5
+     */
+    public static <T> Iterator<T> unique(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
+        Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
+                ? new OrderBy<T>(condition, true)
+                : new ClosureComparator<T>(condition);
+        return unique(self, comparator);
+    }
+
+    /**
+     * A convenience method for making a collection unique using a Closure
+     * to determine duplicate (equal) items.
+     * <p>
+     * If the closure takes a single parameter, the
+     * argument passed will be each element, and the closure
+     * should return a value used for comparison (either using
+     * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+     * If the closure takes two parameters, two items from the collection
+     * will be passed as arguments, and the closure should return an
+     * int value (with 0 indicating the items are not unique).
+     * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].unique { it % 2 }</pre>
+     * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].unique { a, b -> a <=> b }</pre>
+     *
+     * @param self    a Collection
+     * @param closure a 1 or 2 arg Closure used to determine unique items
+     * @return self   without any duplicates
+     * @see #unique(Collection, boolean, Closure)
+     * @since 1.0
+     */
+    public static <T> Collection<T> unique(Collection<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+        return unique(self, true, closure);
+    }
+
+    /**
+     * A convenience method for making a List unique using a Closure
+     * to determine duplicate (equal) items.
+     * <p>
+     * If the closure takes a single parameter, the
+     * argument passed will be each element, and the closure
+     * should return a value used for comparison (either using
+     * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+     * If the closure takes two parameters, two items from the List
+     * will be passed as arguments, and the closure should return an
+     * int value (with 0 indicating the items are not unique).
+     * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].unique { it % 2 }</pre>
+     * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].unique { a, b -> a <=> b }</pre>
+     *
+     * @param self    a List
+     * @param closure a 1 or 2 arg Closure used to determine unique items
+     * @return self   without any duplicates
+     * @see #unique(Collection, boolean, Closure)
+     * @since 2.4.0
+     */
+    public static <T> List<T> unique(List<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+        return (List<T>) unique((Collection<T>) self, true, closure);
+    }
+
+    /**
+     * A convenience method for making a collection unique using a Closure to determine duplicate (equal) items.
+     * If mutate is true, it works on the receiver object and returns it. If mutate is false, a new collection is returned.
+     * <p>
+     * If the closure takes a single parameter, each element from the Collection will be passed to the closure. The closure
+     * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
+     * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the collection
+     * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
+     * <pre class="groovyTestCase">
+     * def orig = [1, 3, 4, 5]
+     * def uniq = orig.unique(false) { it % 2 }
+     * assert orig == [1, 3, 4, 5]
+     * assert uniq == [1, 4]
+     * </pre>
+     * <pre class="groovyTestCase">
+     * def orig = [2, 3, 3, 4]
+     * def uniq = orig.unique(false) { a, b -> a <=> b }
+     * assert orig == [2, 3, 3, 4]
+     * assert uniq == [2, 3, 4]
+     * </pre>
+     *
+     * @param self    a Collection
+     * @param mutate  false will always cause a new list to be created, true will mutate lists in place
+     * @param closure a 1 or 2 arg Closure used to determine unique items
+     * @return self   without any duplicates
+     * @since 1.8.1
+     */
+    public static <T> Collection<T> unique(Collection<T> self, boolean mutate, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+        // use a comparator of one item or two
+        int params = closure.getMaximumNumberOfParameters();
+        if (params == 1) {
+            self = unique(self, mutate, new OrderBy<T>(closure, true));
+        } else {
+            self = unique(self, mutate, new ClosureComparator<T>(closure));
+        }
+        return self;
+    }
+
+    /**
+     * A convenience method for making a List unique using a Closure to determine duplicate (equal) items.
+     * If mutate is true, it works on the receiver object and returns it. If mutate is false, a new collection is returned.
+     * <p>
+     * If the closure takes a single parameter, each element from the List will be passed to the closure. The closure
+     * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
+     * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the collection
+     * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
+     * <pre class="groovyTestCase">
+     * def orig = [1, 3, 4, 5]
+     * def uniq = orig.unique(false) { it % 2 }
+     * assert orig == [1, 3, 4, 5]
+     * assert uniq == [1, 4]
+     * </pre>
+     * <pre class="groovyTestCase">
+     * def orig = [2, 3, 3, 4]
+     * def uniq = orig.unique(false) { a, b -> a <=> b }
+     * assert orig == [2, 3, 3, 4]
+     * assert uniq == [2, 3, 4]
+     * </pre>
+     *
+     * @param self    a List
+     * @param mutate  false will always cause a new list to be created, true will mutate lists in place
+     * @param closure a 1 or 2 arg Closure used to determine unique items
+     * @return self   without any duplicates
+     * @since 2.4.0
+     */
+    public static <T> List<T> unique(List<T> self, boolean mutate, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure closure) {
+        return (List<T>) unique((Collection<T>) self, mutate, closure);
+    }
+
+    /**
+     * Returns an iterator equivalent to this iterator with all duplicated
+     * items removed by using the supplied comparator. The original iterator
+     * will be exhausted upon returning.
+     *
+     * @param self an Iterator
+     * @param comparator a Comparator
+     * @return the modified Iterator
+     * @since 1.5.5
+     */
+    public static <T> Iterator<T> unique(Iterator<T> self, Comparator<T> comparator) {
+        return uniqueItems(new IteratorIterableAdapter<T>(self), comparator).listIterator();
+    }
+
+    private static final class IteratorIterableAdapter<T> implements Iterable<T> {
+        private final Iterator<T> self;
+
+        private IteratorIterableAdapter(Iterator<T> self) {
+            this.self = self;
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            return self;
+        }
+    }
+
+    /**
+     * Remove all duplicates from a given Collection.
+     * Works on the original object (and also returns it).
+     * The order of members in the Collection are compared by the given Comparator.
+     * For each duplicate, the first member which is returned
+     * by the given Collection's iterator is retained, but all other ones are removed.
+     * The given Collection's original order is preserved.
+     * <p>
+     * <pre class="groovyTestCase">
+     * class Person {
+     *     def fname, lname
+     *     String toString() {
+     *         return fname + " " + lname
+     *     }
+     * }
+     *
+     * class PersonComparator implements Comparator {
+     *     int compare(Object o1, Object o2) {
+     *         Person p1 = (Person) o1
+     *         Person p2 = (Person) o2
+     *         if (p1.lname != p2.lname)
+     *             return p1.lname.compareTo(p2.lname)
+     *         else
+     *             return p1.fname.compareTo(p2.fname)
+     *     }
+     *
+     *     boolean equals(Object obj) {
+     *         return this.equals(obj)
+     *     }
+     * }
+     *
+     * Person a = new Person(fname:"John", lname:"Taylor")
+     * Person b = new Person(fname:"Clark", lname:"Taylor")
+     * Person c = new Person(fname:"Tom", lname:"Cruz")
+     * Person d = new Person(fname:"Clark", lname:"Taylor")
+     *
+     * def list = [a, b, c, d]
+     * List list2 = list.unique(new PersonComparator())
+     * assert( list2 == list && list == [a, b, c] )
+     * </pre>
+     *
+     * @param self       a Collection
+     * @param comparator a Comparator
+     * @return self      the now modified collection without duplicates
+     * @see #unique(java.util.Collection, boolean, java.util.Comparator)
+     * @since 1.0
+     */
+    public static <T> Collection<T> unique(Collection<T> self, Comparator<T> comparator) {
+        return unique(self, true, comparator) ;
+    }
+
+    /**
+     * Remove all duplicates from a given List.
+     * Works on the original object (and also returns it).
+     * The order of members in the List are compared by the given Comparator.
+     * For each duplicate, the first member which is returned
+     * by the given List's iterator is retained, but all other ones are removed.
+     * The given List's original order is preserved.
+     * <p>
+     * <pre class="groovyTestCase">
+     * class Person {
+     *     def fname, lname
+     *     String toString() {
+     *         return fname + " " + lname
+     *     }
+     * }
+     *
+     * class PersonComparator implements Comparator {
+     *     int compare(Object o1, Object o2) {
+     *         Person p1 = (Person) o1
+     *         Person p2 = (Person) o2
+     *         if (p1.lname != p2.lname)
+     *             return p1.lname.compareTo(p2.lname)
+     *         else
+     *             return p1.fname.compareTo(p2.fname)
+     *     }
+     *
+     *     boolean equals(Object obj) {
+     *         return this.equals(obj)
+     *     }
+     * }
+     *
+     * Person a = new Person(fname:"John", lname:"Taylor")
+     * Person b = new Person(fname:"Clark", lname:"Taylor")
+     * Person c = new Person(fname:"Tom", lname:"Cruz")
+     * Person d = new Person(fname:"Clark", lname:"Taylor")
+     *
+     * def list = [a, b, c, d]
+     * List list2 = list.unique(new PersonComparator())
+     * assert( list2 == list && list == [a, b, c] )
+     * </pre>
+     *
+     * @param self       a List
+     * @param comparator a Comparator
+     * @return self      the now modified List without duplicates
+     * @see #unique(java.util.Collection, boolean, java.util.Comparator)
+     * @since 2.4.0
+     */
+    public static <T> List<T> unique(List<T> self, Comparator<T> comparator) {
+        return (List<T>) unique((Collection<T>) self, true, comparator);
+    }
+
+    /**
+     * Remove all duplicates from a given Collection.
+     * If mutate is true, it works on the original object (and also returns it). If mutate is false, a new collection is returned.
+     * The order of members in the Collection are compared by the given Comparator.
+     * For each duplicate, the first member which is returned
+     * by the given Collection's iterator is retained, but all other ones are removed.
+     * The given Collection's original order is preserved.
+     * <p>
+     * <pre class="groovyTestCase">
+     * class Person {
+     *     def fname, lname
+     *     String toString() {
+     *         return fname + " " + lname
+     *     }
+     * }
+     *
+     * class PersonComparator implements Comparator {
+     *     int compare(Object o1, Object o2) {
+     *         Person p1 = (Person) o1
+     *         Person p2 = (Person) o2
+     *         if (p1.lname != p2.lname)
+     *             return p1.lname.compareTo(p2.lname)
+     *         else
+     *             return p1.fname.compareTo(p2.fname)
+     *     }
+     *
+     *     boolean equals(Object obj) {
+     *         return this.equals(obj)
+     *     }
+     * }
+     *
+     * Person a = new Person(fname:"John", lname:"Taylor")
+     * Person b = new Person(fname:"Clark", lname:"Taylor")
+     * Person c = new Person(fname:"Tom", lname:"Cruz")
+     * Person d = new Person(fname:"Clark", lname:"Taylor")
+     *
+     * def list = [a, b, c, d]
+     * List list2 = list.unique(false, new PersonComparator())
+     * assert( list2 != list && list2 == [a, b, c] )
+     * </pre>
+     *
+     * @param self       a Collection
+     * @param mutate     false will always cause a new collection to be created, true will mutate collections in place
+     * @param comparator a Comparator
+     * @return self      the collection without duplicates
+     * @since 1.8.1
+     */
+    public static <T> Collection<T> unique(Collection<T> self, boolean mutate, Comparator<T> comparator) {
+        List<T> answer = uniqueItems(self, comparator);
+        if (mutate) {
+            self.clear();
+            self.addAll(answer);
+        }
+        return mutate ? self : answer;
+    }
+
+    private static <T> List<T> uniqueItems(Iterable<T> self, Comparator<T> comparator) {
+        List<T> answer = new ArrayList<T>();
+        for (T t : self) {
+            boolean duplicated = false;
+            for (T t2 : answer) {
+                if (comparator.compare(t, t2) == 0) {
+                    duplicated = true;
+                    break;
+                }
+            }
+            if (!duplicated)
+                answer.add(t);
+        }
+        return answer;
+    }
+
+    /**
+     * Remove all duplicates from a given List.
+     * If mutate is true, it works on the original object (and also returns it). If mutate is false, a new List is returned.
+     * The order of members in the List are compared by the given Comparator.
+     * For each duplicate, the first member which is returned
+     * by the given List's iterator is retained, but all other ones are removed.
+     * The given List's original order is preserved.
+     * <p>
+     * <pre class="groovyTestCase">
+     * class Person {
+     *     def fname, lname
+     *     String toString() {
+     *         return fname + " " + lname
+     *     }
+     * }
+     *
+     * class PersonComparator implements Comparator {
+     *     int compare(Object o1, Object o2) {
+     *         Person p1 = (Person) o1
+     *         Person p2 = (Person) o2
+     *         if (p1.lname != p2.lname)
+     *             return p1.lname.compareTo(p2.lname)
+     *         else
+     *             return p1.fname.compareTo(p2.fname)
+     *     }
+     *
+     *     boolean equals(Object obj) {
+     *         return this.equals(obj)
+     *     }
+     * }
+     *
+     * Person a = new Person(fname:"John", lname:"Taylor")
+     * Person b = new Person(fname:"Clark", lname:"Taylor")
+     * Person c = new Person(fname:"Tom", lname:"Cruz")
+     * Person d = new Person(fname:"Clark", lname:"Taylor")
+     *
+     * def list = [a, b, c, d]
+     * List list2 = list.unique(false, new PersonComparator())
+     * assert( list2 != list && list2 == [a, b, c] )
+     * </pre>
+     *
+     * @param self       a List
+     * @param mutate     false will always cause a new List to be created, true will mutate List in place
+     * @param comparator a Comparator
+     * @return self      the List without duplicates
+     * @since 2.4.0
+     */
+    public static <T> List<T> unique(List<T> self, boolean mutate, Comparator<T> comparator) {
+        return (List<T>) unique((Collection<T>) self, mutate, comparator);
+    }
+
+    /**
+     * Returns an iterator equivalent to this iterator but with all duplicated items
+     * removed where duplicate (equal) items are deduced by calling the supplied Closure condition.
+     * <p>
+     * If the supplied Closure takes a single parameter, the argument passed will be each element,
+     * and the closure should return a value used for comparison (either using
+     * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}).
+     * If the closure takes two parameters, two items from the Iterator
+     * will be passed as arguments, and the closure should return an
+     * int value (with 0 indicating the items are not unique).
+     * <pre class="groovyTestCase">
+     * def items = "Hello".toList() + [null, null] + "there".toList()
+     * def toLower = { it == null ? null : it.toLowerCase() }
+     * def noDups = items.iterator().toUnique(toLower).toList()
+     * assert noDups == ['H', 'e', 'l', 'o', null, 't', 'r']
+     * </pre>
+     * <pre class="groovyTestCase">assert [1,4] == [1,3,4,5].toUnique { it % 2 }</pre>
+     * <pre class="groovyTestCase">assert [2,3,4] == [2,3,3,4].toUnique { a, b -> a <=> b }</pre>
+     *
+     * @param self an Iterator
+     * @param condition a Closure used to determine unique items
+     * @return an Iterator with no duplicate items
+     * @since 2.4.0
+     */
+    public static <T> Iterator<T> toUnique(Iterator<T> self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
+        return toUnique(self, condition.getMaximumNumberOfParameters() == 1
+                ? new OrderBy<T>(condition, true)
+                : new ClosureComparator<T>(condition));
+    }
+
+    private static final class ToUniqueIterator<E> implements Iterator<E> {
+        private final Iterator<E> delegate;
+        private final Set<E> seen;
+        private boolean exhausted;
+        private E next;
+
+        private ToUniqueIterator(Iterator<E> delegate, Comparator<E> comparator) {
+            this.delegate = delegate;
+            seen = new TreeSet<E>(comparator);
+            advance();
+        }
+
+        public boolean hasNext() {
+            return !exhausted;
+        }
+
+        public E next() {
+            if (exhausted) throw new NoSuchElementException();
+            E result = next;
+            advance();
+            return result;
+        }
+
+        public void remove() {
+            if (exhausted) throw new NoSuchElementException();
+            delegate.remove();
+        }
+
+        private void advance() {
+            boolean foundNext = false;
+            while (!foundNext && !exhausted) {
+                exhausted = !delegate.hasNext();
+                if (!exhausted) {
+                    next = delegate.next();
+                    foundNext = seen.add(next);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns an iterator equivalent to this iterator with all duplicated
+     * items removed by using the supplied comparator.
+     *
+     * @param self an Iterator
+     * @param comparator a Comparator used to determine unique (equal) items
+     *        If {@code null}, the Comparable natural ordering of the elements will be used.
+     * @return an Iterator with no duplicate items
+     * @since 2.4.0
+     */
+    public static <T> Iterator<T> toUnique(Iterator<T> self, Comparator<T> comparator) {
+        return new ToUniqueIterator<T>(self, comparator);
+    }
+
+    /**
+     * Returns an iterator equivalent to this iterator with all duplicated
+     * items removed by using the natural ordering of the items.
+     *
+     * @param self an Iterator
+     * @return an Iterator with no duplicate items
+     * @since 2.4.0
+     */
+    public static <T> Iterator<T> toUnique(Iterator<T> self) {
+        return toUnique(self, (Comparator<T>) null);
+    }
+
+    /**
+     * Returns a Collection containing the items from the Iterable but with duplicates removed.
+     * The items in the Iterable are compared by the given Comparator.
+     * For each duplicate, the first member which is returned from the
+     * Iterable is retained, but all other ones are removed.
+     * <p>
+     * <pre class="groovyTestCase">
+     * class Person {
+     *     def fname, lname
+     *     String toString() {
+     *         return fname + " " + lname
+     *     }
+     * }
+     *
+     * class PersonComparator implements Comparator {
+     *     int compare(Object o1, Object o2) {
+     *         Person p1 = (Person) o1
+     *         Person p2 = (Person) o2
+     *         if (p1.lname != p2.lname)
+     *             return p1.lname.compareTo(p2.lname)
+     *         else
+     *             return p1.fname.compareTo(p2.fname)
+     *     }
+     *
+     *     boolean equals(Object obj) {
+     *         return this.equals(obj)
+     *     }
+     * }
+     *
+     * Person a = new Person(fname:"John", lname:"Taylor")
+     * Person b = new Person(fname:"Clark", lname:"Taylor")
+     * Person c = new Person(fname:"Tom", lname:"Cruz")
+     * Person d = new Person(fname:"Clark", lname:"Taylor")
+     *
+     * def list = [a, b, c, d]
+     * List list2 = list.toUnique(new PersonComparator())
+     * assert list2 == [a, b, c] && list == [a, b, c, d]
+     * </pre>
+     *
+     * @param self       an Iterable
+     * @param comparator a Comparator used to determine unique (equal) items
+     *        If {@code null}, the Comparable natural ordering of the elements will be used.
+     * @return the Collection of non-duplicate items
+     * @since 2.4.0
+     */
+    public static <T> Collection<T> toUnique(Iterable<T> self, Comparator<T> comparator) {
+        Collection<T> result = createSimilarCollection((Collection<T>) self);
+        addAll(result, toUnique(self.iterator(), comparator));
+        return result;
+    }
+
+    /**
+     * Returns a List containing the items from the List but with duplicates removed.
+     * The items in the List are compared by the given Comparator.
+     * For each duplicate, the first member which is returned from the
+     * List is retained, but all other ones are removed.
+     * <p>
+     * <pre class="groovyTestCase">
+     * class Person {
+     *     def fname, lname
+     *     String toString() {
+     *         return fname + " " + lname
+     *     }
+     * }
+     *
+     * class PersonComparator implements Comparator {
+     *     int compare(Object o1, Object o2) {
+     *         Person p1 = (Person) o1
+     *         Person p2 = (Person) o2
+     *         if (p1.lname != p2.lname)
+     *             return p1.lname.compareTo(p2.lname)
+     *         else
+     *             return p1.fname.compareTo(p2.fname)
+     *     }
+     *
+     *     boolean equals(Object obj) {
+     *         return this.equals(obj)
+     *     }
+     * }
+     *
+     * Person a = new Person(fname:"John", lname:"Taylor")
+     * Person b = new Person(fname:"Clark", lname:"Taylor")
+     * Person c = new Person(fname:"Tom", lname:"Cruz")
+     * Person d = new Person(fname:"Clark", lname:"Taylor")
+     *
+     * def list = [a, b, c, d]
+     * List list2 = list.toUnique(new PersonComparator())
+     * assert list2 == [a, b, c] && list == [a, b, c, d]
+     * </pre>
+     *
+     * @param self       a List
+     * @param comparator a Comparator used to determine unique (equal) items
+     *        If {@code null}, the Comparable natural ordering of the elements will be used.
+     * @return the List of non-duplicate items
+     * @since 2.4.0
+     */
+    public static <T> List<T> toUnique(List<T> self, Comparator<T> comparator) {
+        return (List<T>) toUnique((Iterable<T>) self, comparator);
+    }
+
+    /**
+     * Returns a Collection containing the items from the Iterable but with duplicates removed
+     * using the natural ordering of the items to determine uniqueness.
+     * <p>
+     * <pre class="groovyTestCase">
+     * String[] letters = ['c', 'a', 't', 's', 'a', 't', 'h', 'a', 't']
+     * String[] expected = ['c', 'a', 't', 's', 'h']
+     * assert letters.toUnique() == expected
+     * </pre>
+     *
+     * @param self       an Iterable
+     * @return the Collection of non-duplicate items
+     * @since 2.4.0
+     */
+    public static <T> Collection<T> toUnique(Iterable<T> self) {
+        return toUnique(self, (Comparator<T>) null);
+    }
+
+    /**
+     * Returns a List containing the items from the List but with duplicates removed
+     * using the natural ordering of the items to determine uniqueness.
+     * <p>
+     * <pre class="groovyTestCase">
+     * def letters = ['c', 'a', 't', 's', 'a', 't', 'h', 'a', 't']
+     * def expected = ['c', 'a', 't', 's', 'h']
+     * assert letters.toUnique() == expected
+     * </pre>
+     *
+     * @param self       a List
+     * @return the List of non-duplicate items
+     * @since 2.4.0
+     */
+    public static <T> List<T> toUnique(List<T> self) {
+        return toUnique(self, (Comparator<T>) null);
+    }
+
+    /**
+     * Returns a Collection containing the items from the Iterable but with duplicates removed.
+     * The items in the Iterable are compared by the given Closure condition.
+     * For each duplicate, the first member which is returned from the
+     * Iterable is retained, but all other ones are removed.
+     * <p>
+     * If the closure takes a single parameter, each element from the Iterable will be passed to the closure. The closure
+     * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
+     * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the Iterable
+     * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
+     * <p>
+     * <pre class="groovyTestCase">
+     * class Person {
+     *     def fname, lname
+     *     String toString() {
+     *         return fname + " " + lname
+     *     }
+     * }
+     *
+     * Person a = new Person(fname:"John", lname:"Taylor")
+     * Person b = new Person(fname:"Clark", lname:"Taylor")
+     * Person c = new Person(fname:"Tom", lname:"Cruz")
+     * Person d = new Person(fname:"Clark", lname:"Taylor")
+     *
+     * def list = [a, b, c, d]
+     * def list2 = list.toUnique{ p1, p2 -> p1.lname != p2.lname ? p1.lname &lt;=&gt; p2.lname : p1.fname &lt;=&gt; p2.fname }
+     * assert( list2 == [a, b, c] && list == [a, b, c, d] )
+     * def list3 = list.toUnique{ it.toString() }
+     * assert( list3 == [a, b, c] && list == [a, b, c, d] )
+     * </pre>
+     *
+     * @param self      an Iterable
+     * @param condition a Closure used to determine unique items
+     * @return a new Collection
+     * @see #toUnique(Iterable, Comparator)
+     * @since 2.4.0
+     */
+    public static <T> Collection<T> toUnique(Iterable<T> self, @ClosureParams(value = FromString.class, options = {"T", "T,T"}) Closure condition) {
+        Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
+                ? new OrderBy<T>(condition, true)
+                : new ClosureComparator<T>(condition);
+        return toUnique(self, comparator);
+    }
+
+    /**
+     * Returns a List containing the items from the List but with duplicates removed.
+     * The items in the List are compared by the given Closure condition.
+     * For each duplicate, the first member which is returned from the
+     * Iterable is retained, but all other ones are removed.
+     * <p>
+     * If the closure takes a single parameter, each element from the Iterable will be passed to the closure. The closure
+     * should return a value used for comparison (either using {@link java.lang.Comparable#compareTo(java.lang.Object)} or
+     * {@link java.lang.Object#equals(java.lang.Object)}). If the closure takes two parameters, two items from the Iterable
+     * will be passed as arguments, and the closure should return an int value (with 0 indicating the items are not unique).
+     * <p>
+     * <pre class="groovyTestCase">
+     * class Person {
+     *     def fname, lname
+     *     String toString() {
+     *         return fname + " " + lname
+     *     }
+     * }
+     *
+     * Person a = new Person(fname:"John", lname:"Taylor")
+     * Person b = new Person(fname:"Clark", lname:"Taylor")
+     * Person c = new Person(fname:"Tom", lname:"Cruz")
+     * Person d = new Person(fname:"Clark", lname:"Taylor")
+     *
+     * def list = [a, b, c, d]
+     * def list2 = list.toUnique{ p1, p2 -> p1.lname != p2.lname ? p1.lname &lt;=&gt; p2.lname : p1.fname &lt;=&gt; p2.fname }
+     * assert( list2 == [a, b, c] && list == [a, b, c, d] )
+     * def list3 = list.toUnique{ it.toString() }
+     * assert( list3 == [a, b, c] && list == [a, b, c, d] )
+     * </pre>
+     *
+     * @param self      a List
+     * @param condition a Closure used to determine unique items
+     * @return a new List
+     * @see #toUnique(Iterable, Comparator)
+     * @since 2.4.0
+     */
+    public static <T> List<T> toUnique(List<T> self, @ClosureParams(value = FromString.class, options = {"T", "T,T"}) Closure condition) {
+        return (List<T>) toUnique((Iterable<T>) self, condition);
+    }
+
+    /**
+     * Returns a new Array containing the items from the original Array but with duplicates removed with the supplied
+     * comparator determining which items are unique.
+     * <p>
+     * <pre class="groovyTestCase">
+     * String[] letters = ['c', 'a', 't', 's', 'A', 't', 'h', 'a', 'T']
+     * String[] lower = ['c', 'a', 't', 's', 'h']
+     * class LowerComparator implements Comparator {
+     *     int compare(let1, let2) { let1.toLowerCase() <=> let2.toLowerCase() }
+     * }
+     * assert letters.toUnique(new LowerComparator()) == lower
+     * </pre>
+     *
+     * @param self an array
+     * @param comparator a Comparator used to determine unique (equal) items
+     *        If {@code null}, the Comparable natural ordering of the elements will be used.
+     * @return the unique items from the array
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T[] toUnique(T[] self, Comparator<T> comparator) {
+        Collection<T> items = toUnique(toList(self), comparator);
+        T[] result = createSimilarArray(self, items.size());
+        return items.toArray(result);
+    }
+
+    /**
+     * Returns a new Array containing the items from the original Array but with duplicates removed using the
+     * natural ordering of the items in the array.
+     * <p>
+     * <pre class="groovyTestCase">
+     * String[] letters = ['c', 'a', 't', 's', 'a', 't', 'h', 'a', 't']
+     * String[] expected = ['c', 'a', 't', 's', 'h']
+     * def result = letters.toUnique()
+     * assert result == expected
+     * assert result.class.componentType == String
+     * </pre>
+     *
+     * @param self an array
+     * @return the unique items from the array
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T[] toUnique(T[] self) {
+        return (T[]) toUnique(self, (Comparator) null);
+    }
+
+    /**
+     * Returns a new Array containing the items from the original Array but with duplicates removed with the supplied
+     * comparator determining which items are unique.
+     * <p>
+     * <pre class="groovyTestCase">
+     * String[] letters = ['c', 'a', 't', 's', 'A', 't', 'h', 'a', 'T']
+     * String[] expected = ['c', 'a', 't', 's', 'h']
+     * assert letters.toUnique{ p1, p2 -> p1.toLowerCase() <=> p2.toLowerCase() } == expected
+     * assert letters.toUnique{ it.toLowerCase() } == expected
+     * </pre>
+     *
+     * @param self an array
+     * @param condition a Closure used to determine unique items
+     * @return the unique items from the array
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T[] toUnique(T[] self, @ClosureParams(value=FromString.class, options={"T","T,T"}) Closure condition) {
+        Comparator<T> comparator = condition.getMaximumNumberOfParameters() == 1
+                ? new OrderBy<T>(condition, true)
+                : new ClosureComparator<T>(condition);
+        return toUnique(self, comparator);
+    }
+
+    /**
+     * Iterates through an array passing each array entry to the given closure.
+     * <pre class="groovyTestCase">
+     * String[] letters = ['a', 'b', 'c']
+     * String result = ''
+     * letters.each{ result += it }
+     * assert result == 'abc'
+     * </pre>
+     *
+     * @param self    the array over which we iterate
+     * @param closure the closure applied on each array entry
+     * @return the self array
+     * @since 2.5.0
+     */
+    public static <T> T[] each(T[] self, @ClosureParams(FirstParam.Component.class) Closure closure) {
+        for(T item : self){
+            closure.call(item);
+        }
+        return self;
+    }
+
+    /**
+     * Iterates through an aggregate type or data structure,
+     * passing each item to the given closure.  Custom types may utilize this
+     * method by simply providing an "iterator()" method.  The items returned
+     * from the resulting iterator will be passed to the closure.
+     * <pre class="groovyTestCase">
+     * String result = ''
+     * ['a', 'b', 'c'].each{ result += it }
+     * assert result == 'abc'
+     * </pre>
+     *
+     * @param self    the object over which we iterate
+     * @param closure the closure applied on each element found
+     * @return the self Object
+     * @since 1.0
+     */
+    public static <T> T each(T self, Closure closure) {
+        each(InvokerHelper.asIterator(self), closure);
+        return self;
+    }
+
+    /**
+     * Iterates through an array,
+     * passing each array element and the element's index (a counter starting at
+     * zero) to the given closure.
+     * <pre class="groovyTestCase">
+     * String[] letters = ['a', 'b', 'c']
+     * String result = ''
+     * letters.eachWithIndex{ letter, index -> result += "$index:$letter" }
+     * assert result == '0:a1:b2:c'
+     * </pre>
+     *
+     * @param self    an array
+     * @param closure a Closure to operate on each array entry
+     * @return the self array
+     * @since 2.5.0
+     */
+    public static <T> T[] eachWithIndex(T[] self, @ClosureParams(value=FromString.class, options="T,Integer") Closure closure) {
+        final Object[] args = new Object[2];
+        int counter = 0;
+        for(T item : self) {
+            args[0] = item;
+            args[1] = counter++;
+            closure.call(args);
+        }
+        return self;
+    }
+
+    /**
+     * Iterates through an aggregate type or data structure,
+     * passing each item and the item's index (a counter starting at
+     * zero) to the given closure.
+     * <pre class="groovyTestCase">
+     * String result = ''
+     * ['a', 'b', 'c'].eachWithIndex{ letter, index -> result += "$index:$letter" }
+     * assert result == '0:a1:b2:c'
+     * </pre>
+     *
+     * @param self    an Object
+     * @param closure a Closure to operate on each item
+     * @return the self Object
+     * @since 1.0
+     */
+    public static <T> T eachWithIndex(T self, /*@ClosureParams(value=FromString.class, options="?,Integer")*/ Closure closure) {
+        final Object[] args = new Object[2];
+        int counter = 0;
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            args[0] = iter.next();
+            args[1] = counter++;
+            closure.call(args);
+        }
+        return self;
+    }
+
+    /**
+     * Iterates through an iterable type,
+     * passing each item and the item's index (a counter starting at
+     * zero) to the given closure.
+     *
+     * @param self    an Iterable
+     * @param closure a Closure to operate on each item
+     * @return the self Iterable
+     * @since 2.3.0
+     */
+    public static <T> Iterable<T> eachWithIndex(Iterable<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+        eachWithIndex(self.iterator(), closure);
+        return self;
+    }
+
+    /**
+     * Iterates through an iterator type,
+     * passing each item and the item's index (a counter starting at
+     * zero) to the given closure.
+     *
+     * @param self    an Iterator
+     * @param closure a Closure to operate on each item
+     * @return the self Iterator (now exhausted)
+     * @since 2.3.0
+     */
+    public static <T> Iterator<T> eachWithIndex(Iterator<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+        final Object[] args = new Object[2];
+        int counter = 0;
+        while (self.hasNext()) {
+            args[0] = self.next();
+            args[1] = counter++;
+            closure.call(args);
+        }
+        return self;
+    }
+
+    /**
+     * Iterates through a Collection,
+     * passing each item and the item's index (a counter starting at
+     * zero) to the given closure.
+     *
+     * @param self    a Collection
+     * @param closure a Closure to operate on each item
+     * @return the self Collection
+     * @since 2.4.0
+     */
+    public static <T> Collection<T> eachWithIndex(Collection<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+        return (Collection<T>) eachWithIndex((Iterable<T>) self, closure);
+    }
+
+    /**
+     * Iterates through a List,
+     * passing each item and the item's index (a counter starting at
+     * zero) to the given closure.
+     *
+     * @param self    a List
+     * @param closure a Closure to operate on each item
+     * @return the self List
+     * @since 2.4.0
+     */
+    public static <T> List<T> eachWithIndex(List<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+        return (List<T>) eachWithIndex((Iterable<T>) self, closure);
+    }
+
+    /**
+     * Iterates through a Set,
+     * passing each item and the item's index (a counter starting at
+     * zero) to the given closure.
+     *
+     * @param self    a Set
+     * @param closure a Closure to operate on each item
+     * @return the self Set
+     * @since 2.4.0
+     */
+    public static <T> Set<T> eachWithIndex(Set<T> self, @ClosureParams(value=FromString.class, options="T,java.lang.Integer") Closure closure) {
+        return (Set<T>) eachWithIndex((Iterable<T>) self, closure);
+    }
+
+    /**
+     * Iterates through a SortedSet,
+     * passing each item and the item's index (a counter starting at
+     * zero) to the given closure.
+     *
+     * @param self    a SortedSet
+     * @param closure a Closure to operate on each item
+     * @return the self SortedSet
+     * @since 2.4.0
+     */
+    public static <T> Sort

<TRUNCATED>

Mime
View raw message