You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:16 UTC
[05/62] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
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 <=> p2.lname : p1.fname <=> 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 <=> p2.lname : p1.fname <=> 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>