You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2017/12/19 07:32:10 UTC
groovy git commit: GROOVY-8406: Various DefaultGroovyMethods missing
Array variants resulting in no type inference
Repository: groovy
Updated Branches:
refs/heads/master c89393104 -> 047c8f29b
GROOVY-8406: Various DefaultGroovyMethods missing Array variants resulting in no type inference
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/047c8f29
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/047c8f29
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/047c8f29
Branch: refs/heads/master
Commit: 047c8f29b1f7a1a98b2b003e4d09c1cef05feb0e
Parents: c893931
Author: paulk <pa...@asert.com.au>
Authored: Tue Dec 12 01:14:26 2017 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Tue Dec 19 17:31:11 2017 +1000
----------------------------------------------------------------------
.../groovy/runtime/DefaultGroovyMethods.java | 1012 +++++++++++++-----
.../stc/ClosureParamTypeInferenceSTCTest.groovy | 98 +-
2 files changed, 856 insertions(+), 254 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/047c8f29/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
index eec02af..3037fea 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -2445,19 +2445,13 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* def greaterThanTwo = list.every { it > 2 }
* </pre>
*
- * @param self the object over which we iterate
- * @param closure the closure predicate used for matching
+ * @param self the object over which we iterate
+ * @param predicate the closure predicate used for matching
* @return true if every iteration of the object matches the closure predicate
* @since 1.0
*/
- public static boolean every(Object self, Closure closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
- if (!bcw.call(iter.next())) {
- return false;
- }
- }
- return true;
+ public static boolean every(Object self, Closure predicate) {
+ return every(InvokerHelper.asIterator(self), predicate);
}
/**
@@ -2468,13 +2462,13 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* def greaterThanTwo = list.iterator().every { it > 2 }
* </pre>
*
- * @param self the iterator over which we iterate
- * @param closure the closure predicate used for matching
+ * @param self the iterator over which we iterate
+ * @param predicate the closure predicate used for matching
* @return true if every iteration of the object matches the closure predicate
* @since 2.3.0
*/
- public static <T> boolean every(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ public static <T> boolean every(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure predicate) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(predicate);
while (self.hasNext()) {
if (!bcw.call(self.next())) {
return false;
@@ -2485,19 +2479,32 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
/**
* Used to determine if the given predicate closure is valid (i.e. returns
+ * <code>true</code> for all items in this Array).
+ *
+ * @param self an Array
+ * @param predicate the closure predicate used for matching
+ * @return true if every element of the Array matches the closure predicate
+ * @since 2.5.0
+ */
+ public static <T> boolean every(T[] self, @ClosureParams(FirstParam.Component.class) Closure predicate) {
+ return every(new ArrayIterator<T>(self), predicate);
+ }
+
+ /**
+ * Used to determine if the given predicate closure is valid (i.e. returns
* <code>true</code> for all items in this iterable).
* A simple example for a list:
* <pre>def list = [3,4,5]
* def greaterThanTwo = list.every { it > 2 }
* </pre>
*
- * @param self the iterable over which we iterate
- * @param closure the closure predicate used for matching
+ * @param self the iterable over which we iterate
+ * @param predicate the closure predicate used for matching
* @return true if every iteration of the object matches the closure predicate
* @since 2.3.0
*/
- public static <T> boolean every(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- return every(self.iterator(), closure);
+ public static <T> boolean every(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure predicate) {
+ return every(self.iterator(), predicate);
}
/**
@@ -2510,13 +2517,13 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* assert !map.every { key, value -> value instanceof Integer }
* assert map.every { entry -> entry.value instanceof Number }</pre>
*
- * @param self the map over which we iterate
- * @param closure the 1 or 2 arg Closure predicate used for matching
+ * @param self the map over which we iterate
+ * @param predicate the 1 or 2 arg Closure predicate used for matching
* @return true if every entry of the map matches the closure predicate
* @since 1.5.0
*/
- public static <K, V> boolean every(Map<K, V> self, @ClosureParams(value=MapEntryOrKeyValue.class) Closure closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ public static <K, V> boolean every(Map<K, V> self, @ClosureParams(value = MapEntryOrKeyValue.class) Closure predicate) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(predicate);
for (Map.Entry<K, V> entry : self.entrySet()) {
if (!bcw.callForMap(entry)) {
return false;
@@ -2536,13 +2543,12 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* </pre>
*
* @param self the object over which we iterate
- * @return true if every item in the collection matches the closure
- * predicate
+ * @return true if every item in the collection matches satisfies Groovy truth
* @since 1.5.0
*/
public static boolean every(Object self) {
BooleanReturningMethodInvoker bmi = new BooleanReturningMethodInvoker();
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+ for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); ) {
if (!bmi.convertToBoolean(iter.next())) {
return false;
}
@@ -2558,17 +2564,13 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* assert ![1, 2, 3].any { it > 3 }
* </pre>
*
- * @param self the object over which we iterate
- * @param closure the closure predicate used for matching
- * @return true if any iteration for the object matches the closure predicate
+ * @param self the object over which we iterate
+ * @param predicate the closure predicate used for matching
+ * @return true if any iteration for the object matches the closure predicate
* @since 1.0
*/
- public static boolean any(Object self, Closure closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
- if (bcw.call(iter.next())) return true;
- }
- return false;
+ public static boolean any(Object self, Closure predicate) {
+ return any(InvokerHelper.asIterator(self), predicate);
}
/**
@@ -2579,15 +2581,15 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* assert ![1, 2, 3].iterator().any { it > 3 }
* </pre>
*
- * @param self the iterator over which we iterate
- * @param closure the closure predicate used for matching
- * @return true if any iteration for the object matches the closure predicate
+ * @param self the iterator over which we iterate
+ * @param predicate the closure predicate used for matching
+ * @return true if any iteration for the object matches the closure predicate
* @since 1.0
*/
- public static <T> boolean any(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Iterator iter = self; iter.hasNext();) {
- if (bcw.call(iter.next())) return true;
+ public static <T> boolean any(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure predicate) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(predicate);
+ while (self.hasNext()) {
+ if (bcw.call(self.next())) return true;
}
return false;
}
@@ -2600,17 +2602,26 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* assert ![1, 2, 3].any { it > 3 }
* </pre>
*
- * @param self the iterable over which we iterate
- * @param closure the closure predicate used for matching
- * @return true if any iteration for the object matches the closure predicate
+ * @param self the iterable over which we iterate
+ * @param predicate the closure predicate used for matching
+ * @return true if any iteration for the object matches the closure predicate
* @since 1.0
*/
- public static <T> boolean any(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Iterator<T> iter = self.iterator(); iter.hasNext();) {
- if (bcw.call(iter.next())) return true;
- }
- return false;
+ public static <T> boolean any(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure predicate) {
+ return any(self.iterator(), predicate);
+ }
+
+ /**
+ * Iterates over the contents of an Array, and checks whether a
+ * predicate is valid for at least one element.
+ *
+ * @param self the array over which we iterate
+ * @param predicate the closure predicate used for matching
+ * @return true if any iteration for the object matches the closure predicate
+ * @since 2.5.0
+ */
+ public static <T> boolean any(T[] self, @ClosureParams(FirstParam.Component.class) Closure predicate) {
+ return any(new ArrayIterator<T>(self), predicate);
}
/**
@@ -2624,13 +2635,13 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* assert ![2:3, 4:5, 5:10].any { entry -> entry.key == entry.value * 2 }
* </pre>
*
- * @param self the map over which we iterate
- * @param closure the 1 or 2 arg closure predicate used for matching
+ * @param self the map over which we iterate
+ * @param predicate the 1 or 2 arg closure predicate used for matching
* @return true if any entry in the map matches the closure predicate
* @since 1.5.0
*/
- public static <K, V> boolean any(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<?> closure) {
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
+ public static <K, V> boolean any(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<?> predicate) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(predicate);
for (Map.Entry<K, V> entry : self.entrySet()) {
if (bcw.callForMap(entry)) {
return true;
@@ -3354,6 +3365,20 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
+ * Iterates through this aggregate Object transforming each item into a new value using Closure.IDENTITY
+ * as a transformer, basically returning a list of items copied from the original object.
+ * <pre class="groovyTestCase">assert [1,2,3] == [1,2,3].iterator().collect()</pre>
+ *
+ * @param self an aggregate Object with an Iterator returning its items
+ * @return a Collection of the transformed values
+ * @see Closure#IDENTITY
+ * @since 1.8.5
+ */
+ public static Collection collect(Object self) {
+ return collect(self, Closure.IDENTITY);
+ }
+
+ /**
* Iterates through this aggregate Object transforming each item into a new value using the
* <code>transform</code> closure, returning a list of transformed values.
* Example:
@@ -3371,62 +3396,163 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
- * Iterates through this aggregate Object transforming each item into a new value using Closure.IDENTITY
- * as a transformer, basically returning a list of items copied from the original object.
- * <pre class="groovyTestCase">assert [1,2,3] == [1,2,3].iterator().collect()</pre>
+ * Iterates through this aggregate Object transforming each item into a new value using the <code>transform</code> closure
+ * and adding it to the supplied <code>collector</code>.
*
- * @param self an aggregate Object with an Iterator returning its items
+ * @param self an aggregate Object with an Iterator returning its items
+ * @param collector the Collection to which the transformed values are added
+ * @param transform the closure used to transform each item of the aggregate object
+ * @return the collector with all transformed values added to it
+ * @since 1.0
+ */
+ public static <T> Collection<T> collect(Object self, Collection<T> collector, Closure<? extends T> transform) {
+ return collect(InvokerHelper.asIterator(self), collector, transform);
+ }
+
+ /**
+ * Iterates through this Array transforming each item into a new value using the
+ * <code>transform</code> closure, returning a list of transformed values.
+ *
+ * @param self an Array
+ * @param transform the closure used to transform each item of the Array
* @return a List of the transformed values
- * @see Closure#IDENTITY
- * @since 1.8.5
+ * @since 2.5.0
*/
- public static Collection collect(Object self) {
- return collect(self, Closure.IDENTITY);
+ public static <S,T> List<T> collect(S[] self, @ClosureParams(FirstParam.Component.class) Closure<T> transform) {
+ return collect(new ArrayIterator<S>(self), transform);
}
/**
- * Iterates through this aggregate Object transforming each item into a new value using the <code>transform</code> closure
+ * Iterates through this Array transforming each item into a new value using the <code>transform</code> closure
* and adding it to the supplied <code>collector</code>.
+ * <pre class="groovyTestCase">
+ * Integer[] nums = [1,2,3]
+ * List<Integer> answer = []
+ * nums.collect(answer) { it * 2 }
+ * assert [2,4,6] == answer
+ * </pre>
*
- * @param self an aggregate Object with an Iterator returning its items
+ * @param self an Array
* @param collector the Collection to which the transformed values are added
- * @param transform the closure used to transform each item of the aggregate object
+ * @param transform the closure used to transform each item
* @return the collector with all transformed values added to it
- * @since 1.0
+ * @since 2.5.0
*/
- public static <T> Collection<T> collect(Object self, Collection<T> collector, Closure<? extends T> transform) {
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); ) {
- collector.add(transform.call(iter.next()));
+ public static <S,T> Collection<T> collect(S[] self, Collection<T> collector, @ClosureParams(FirstParam.Component.class) Closure<? extends T> transform) {
+ return collect(new ArrayIterator<S>(self), collector, transform);
+ }
+
+ /**
+ * Iterates through this Iterator transforming each item into a new value using the
+ * <code>transform</code> closure, returning a list of transformed values.
+ *
+ * @param self an Iterator
+ * @param transform the closure used to transform each item
+ * @return a List of the transformed values
+ * @since 2.5.0
+ */
+ public static <S,T> List<T> collect(Iterator<S> self, @ClosureParams(FirstParam.Component.class) Closure<T> transform) {
+ return (List<T>) collect(self, new ArrayList<T>(), transform);
+ }
+
+ /**
+ * Iterates through this Iterator transforming each item into a new value using the <code>transform</code> closure
+ * and adding it to the supplied <code>collector</code>.
+ *
+ * @param self an Iterator
+ * @param collector the Collection to which the transformed values are added
+ * @param transform the closure used to transform each item
+ * @return the collector with all transformed values added to it
+ * @since 2.5.0
+ */
+ public static <S,T> Collection<T> collect(Iterator<S> self, Collection<T> collector, @ClosureParams(FirstParam.FirstGenericType.class) Closure<? extends T> transform) {
+ while (self.hasNext()) {
+ collector.add(transform.call(self.next()));
}
return collector;
}
/**
+ * Iterates through this collection transforming each entry into a new value using Closure.IDENTITY
+ * as a transformer, basically returning a list of items copied from the original collection.
+ * <pre class="groovyTestCase">assert [1,2,3] == [1,2,3].collect()</pre>
+ *
+ * @param self a collection
+ * @return a List of the transformed values
+ * @see Closure#IDENTITY
+ * @since 1.8.5
+ * @deprecated use the Iterable version instead
+ */
+ @Deprecated
+ public static <T> List<T> collect(Collection<T> self) {
+ return collect((Iterable<T>) self);
+ }
+
+ /**
* Iterates through this collection transforming each entry into a new value using the <code>transform</code> closure
* returning a list of transformed values.
- * <pre class="groovyTestCase">assert [2,4,6] == [1,2,3].collect { it * 2 }</pre>
*
* @param self a collection
* @param transform the closure used to transform each item of the collection
* @return a List of the transformed values
+ * @deprecated use the Iterable version instead
* @since 1.0
*/
+ @Deprecated
public static <S,T> List<T> collect(Collection<S> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> transform) {
return (List<T>) collect(self, new ArrayList<T>(self.size()), transform);
}
/**
+ * Iterates through this collection transforming each value into a new value using the <code>transform</code> closure
+ * and adding it to the supplied <code>collector</code>.
+ * <pre class="groovyTestCase">assert [1,2,3] as HashSet == [2,4,5,6].collect(new HashSet()) { (int)(it / 2) }</pre>
+ *
+ * @param self a collection
+ * @param collector the Collection to which the transformed values are added
+ * @param transform the closure used to transform each item of the collection
+ * @return the collector with all transformed values added to it
+ * @deprecated use the Iterable version instead
+ * @since 1.0
+ */
+ @Deprecated
+ public static <S,T> Collection<T> collect(Collection<S> self, Collection<T> collector, @ClosureParams(FirstParam.FirstGenericType.class) Closure<? extends T> transform) {
+ for (S item : self) {
+ collector.add(transform.call(item));
+ if (transform.getDirective() == Closure.DONE) {
+ break;
+ }
+ }
+ return collector;
+ }
+
+ /**
* Iterates through this collection transforming each entry into a new value using Closure.IDENTITY
* as a transformer, basically returning a list of items copied from the original collection.
* <pre class="groovyTestCase">assert [1,2,3] == [1,2,3].collect()</pre>
*
- * @param self a collection
+ * @param self an Iterable
* @return a List of the transformed values
- * @since 1.8.5
* @see Closure#IDENTITY
+ * @since 2.5.0
*/
- public static <T> List<T> collect(Collection<T> self) {
- return (List<T>) collect(self, Closure.IDENTITY);
+ @SuppressWarnings("unchecked")
+ public static <T> List<T> collect(Iterable<T> self) {
+ return collect(self, (Closure<T>) Closure.IDENTITY);
+ }
+
+ /**
+ * Iterates through this Iterable transforming each entry into a new value using the <code>transform</code> closure
+ * returning a list of transformed values.
+ * <pre class="groovyTestCase">assert [2,4,6] == [1,2,3].collect { it * 2 }</pre>
+ *
+ * @param self an Iterable
+ * @param transform the closure used to transform each item of the collection
+ * @return a List of the transformed values
+ * @since 2.5.0
+ */
+ public static <S,T> List<T> collect(Iterable<S> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> transform) {
+ return (List<T>) collect(self.iterator(), transform);
}
/**
@@ -3434,14 +3560,14 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* and adding it to the supplied <code>collector</code>.
* <pre class="groovyTestCase">assert [1,2,3] as HashSet == [2,4,5,6].collect(new HashSet()) { (int)(it / 2) }</pre>
*
- * @param self a collection
+ * @param self an Iterable
* @param collector the Collection to which the transformed values are added
- * @param transform the closure used to transform each item of the collection
+ * @param transform the closure used to transform each item
* @return the collector with all transformed values added to it
- * @since 1.0
+ * @since 2.5.0
*/
- public static <T,E> Collection<T> collect(Collection<E> self, Collection<T> collector, @ClosureParams(FirstParam.FirstGenericType.class) Closure<? extends T> transform) {
- for (E item : self) {
+ public static <S,T> Collection<T> collect(Iterable<S> self, Collection<T> collector, @ClosureParams(FirstParam.FirstGenericType.class) Closure<? extends T> transform) {
+ for (S item : self) {
collector.add(transform.call(item));
if (transform.getDirective() == Closure.DONE) {
break;
@@ -4121,52 +4247,6 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
- * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns the defaultResult.
- *
- * <pre class="groovyTestCase">
- * int[] numbers = [1, 2, 3]
- * assert numbers.findResult(5) { if(it > 1) return it } == 2
- * assert numbers.findResult(5) { if(it > 4) return it } == 5
- * </pre>
- *
- * @param self an Object with an iterator returning its values
- * @param defaultResult an Object that should be returned if all closure results are null
- * @param closure a closure that returns a non-null value when processing should stop
- * @return the first non-null result of the closure, otherwise the default value
- * @since 1.7.5
- */
- public static Object findResult(Object self, Object defaultResult, Closure closure) {
- Object result = findResult(self, closure);
- if (result == null) return defaultResult;
- return result;
- }
-
- /**
- * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns null.
- *
- * <pre class="groovyTestCase">
- * int[] numbers = [1, 2, 3]
- * assert numbers.findResult { if(it > 1) return it } == 2
- * assert numbers.findResult { if(it > 4) return it } == null
- * </pre>
- *
- * @param self an Object with an iterator returning its values
- * @param closure a closure that returns a non-null value when processing should stop
- * @return the first non-null result of the closure
- * @since 1.7.5
- */
- public static Object findResult(Object self, Closure closure) {
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
- Object value = iter.next();
- Object result = closure.call(value);
- if (result != null) {
- return result;
- }
- }
- return null;
- }
-
- /**
* Finds the first value matching the closure condition. Example:
* <pre class="groovyTestCase">def list = [1,2,3]
* assert 2 == list.find { it > 1 }
@@ -4230,46 +4310,206 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
+ * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns null.
+ * <p>
+ * <pre class="groovyTestCase">
+ * int[] numbers = [1, 2, 3]
+ * assert numbers.findResult { if(it > 1) return it } == 2
+ * assert numbers.findResult { if(it > 4) return it } == null
+ * </pre>
+ *
+ * @param self an Object with an iterator returning its values
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result of the closure
+ * @since 1.7.5
+ */
+ public static Object findResult(Object self, Closure condition) {
+ for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); ) {
+ Object value = iter.next();
+ Object result = condition.call(value);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns the defaultResult.
+ * <p>
+ * <pre class="groovyTestCase">
+ * int[] numbers = [1, 2, 3]
+ * assert numbers.findResult(5) { if(it > 1) return it } == 2
+ * assert numbers.findResult(5) { if(it > 4) return it } == 5
+ * </pre>
+ *
+ * @param self an Object with an iterator returning its values
+ * @param defaultResult an Object that should be returned if all closure results are null
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result of the closure, otherwise the default value
+ * @since 1.7.5
+ */
+ public static Object findResult(Object self, Object defaultResult, Closure condition) {
+ Object result = findResult(self, condition);
+ if (result == null) return defaultResult;
+ return result;
+ }
+
+ /**
+ * Iterates through the collection calling the given closure for each item but stopping once the first non-null
+ * result is found and returning that result. If all are null, the defaultResult is returned.
+ *
+ * @param self a Collection
+ * @param defaultResult an Object that should be returned if all closure results are null
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or the defaultValue
+ * @since 1.7.5
+ * @deprecated use the Iterable version instead
+ */
+ @Deprecated
+ public static <S, T, U extends T, V extends T> T findResult(Collection<S> self, U defaultResult, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> condition) {
+ return findResult((Iterable<S>) self, defaultResult, condition);
+ }
+
+ /**
* Iterates through the collection calling the given closure for each item but stopping once the first non-null
+ * result is found and returning that result. If all results are null, null is returned.
+ *
+ * @param self a Collection
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or null
+ * @since 1.7.5
+ * @deprecated use the Iterable version instead
+ */
+ @Deprecated
+ public static <S,T> T findResult(Collection<S> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> condition) {
+ return findResult((Iterable<S>) self, condition);
+ }
+
+ /**
+ * Iterates through the Iterator calling the given closure condition for each item but stopping once the first non-null
* result is found and returning that result. If all are null, the defaultResult is returned.
* <p>
* Examples:
* <pre class="groovyTestCase">
- * def list = [1,2,3]
- * assert "Found 2" == list.findResult("default") { it > 1 ? "Found $it" : null }
- * assert "default" == list.findResult("default") { it > 3 ? "Found $it" : null }
+ * def iter = [1,2,3].iterator()
+ * assert "Found 2" == iter.findResult("default") { it > 1 ? "Found $it" : null }
+ * assert "default" == iter.findResult("default") { it > 3 ? "Found $it" : null }
* </pre>
*
- * @param self a Collection
+ * @param self an Iterator
* @param defaultResult an Object that should be returned if all closure results are null
- * @param closure a closure that returns a non-null value when processing should stop and a value should be returned
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
* @return the first non-null result from calling the closure, or the defaultValue
- * @since 1.7.5
+ * @since 2.5.0
*/
- public static <T, U extends T, V extends T,E> T findResult(Collection<E> self, U defaultResult, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> closure) {
- T result = findResult(self, closure);
+ public static <S, T, U extends T, V extends T> T findResult(Iterator<S> self, U defaultResult, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> condition) {
+ T result = findResult(self, condition);
if (result == null) return defaultResult;
return result;
}
/**
- * Iterates through the collection calling the given closure for each item but stopping once the first non-null
+ * Iterates through the Iterator calling the given closure condition for each item but stopping once the first non-null
* result is found and returning that result. If all results are null, null is returned.
+ *
+ * @param self an Iterator
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or null
+ * @since 2.5.0
+ */
+ public static <T, U> T findResult(Iterator<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> condition) {
+ while (self.hasNext()) {
+ U next = self.next();
+ T result = condition.call(next);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Iterates through the Iterable calling the given closure condition for each item but stopping once the first non-null
+ * result is found and returning that result. If all are null, the defaultResult is returned.
* <p>
- * Example:
+ * Examples:
* <pre class="groovyTestCase">
* def list = [1,2,3]
- * assert "Found 2" == list.findResult { it > 1 ? "Found $it" : null }
+ * assert "Found 2" == list.findResult("default") { it > 1 ? "Found $it" : null }
+ * assert "default" == list.findResult("default") { it > 3 ? "Found $it" : null }
* </pre>
*
- * @param self a Collection
- * @param closure a closure that returns a non-null value when processing should stop and a value should be returned
+ * @param self an Iterable
+ * @param defaultResult an Object that should be returned if all closure results are null
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or the defaultValue
+ * @since 2.5.0
+ */
+ public static <S, T, U extends T, V extends T> T findResult(Iterable<S> self, U defaultResult, @ClosureParams(FirstParam.FirstGenericType.class) Closure<V> condition) {
+ T result = findResult(self, condition);
+ if (result == null) return defaultResult;
+ return result;
+ }
+
+ /**
+ * Iterates through the Iterable calling the given closure condition for each item but stopping once the first non-null
+ * result is found and returning that result. If all results are null, null is returned.
+ *
+ * @param self an Iterable
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or null
+ * @since 2.5.0
+ */
+ public static <T, U> T findResult(Iterable<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> condition) {
+ return findResult(self.iterator(), condition);
+ }
+
+ /**
+ * Iterates through the Array calling the given closure condition for each item but stopping once the first non-null
+ * result is found and returning that result. If all are null, the defaultResult is returned.
+ *
+ * @param self an Array
+ * @param defaultResult an Object that should be returned if all closure results are null
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
+ * @return the first non-null result from calling the closure, or the defaultValue
+ * @since 2.5.0
+ */
+ public static <S, T, U extends T, V extends T> T findResult(S[] self, U defaultResult, @ClosureParams(FirstParam.Component.class) Closure<V> condition) {
+ return findResult(new ArrayIterator<S>(self), defaultResult, condition);
+ }
+
+ /**
+ * Iterates through the Array calling the given closure condition for each item but stopping once the first non-null
+ * result is found and returning that result. If all results are null, null is returned.
+ *
+ * @param self an Array
+ * @param condition a closure that returns a non-null value to indicate that processing should stop and the value should be returned
* @return the first non-null result from calling the closure, or null
+ * @since 2.5.0
+ */
+ public static <S, T> T findResult(S[] self, @ClosureParams(FirstParam.Component.class) Closure<T> condition) {
+ return findResult(new ArrayIterator<S>(self), condition);
+ }
+
+ /**
+ * Returns the first non-null closure result found by passing each map entry to the closure, otherwise null is returned.
+ * If the closure takes two parameters, the entry key and value are passed.
+ * If the closure takes one parameter, the Map.Entry object is passed.
+ * <pre class="groovyTestCase">
+ * assert "Found b:3" == [a:1, b:3].findResult { if (it.value == 3) return "Found ${it.key}:${it.value}" }
+ * assert null == [a:1, b:3].findResult { if (it.value == 9) return "Found ${it.key}:${it.value}" }
+ * assert "Found a:1" == [a:1, b:3].findResult { k, v -> if (k.size() + v == 2) return "Found $k:$v" }
+ * </pre>
+ *
+ * @param self a Map
+ * @param condition a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned
+ * @return the first non-null result collected by calling the closure, or null if no such result was found
* @since 1.7.5
*/
- public static <T,U> T findResult(Collection<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> closure) {
- for (Object value : self) {
- T result = closure.call(value);
+ public static <T, K, V> T findResult(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<T> condition) {
+ for (Map.Entry<K, V> entry : self.entrySet()) {
+ T result = callClosureForMapEntry(condition, entry);
if (result != null) {
return result;
}
@@ -4278,13 +4518,35 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
- * @deprecated Use the Iterable version of findResults instead
+ * Returns the first non-null closure result found by passing each map entry to the closure, otherwise the defaultResult is returned.
+ * If the closure takes two parameters, the entry key and value are passed.
+ * If the closure takes one parameter, the Map.Entry object is passed.
+ * <pre class="groovyTestCase">
+ * assert "Found b:3" == [a:1, b:3].findResult("default") { if (it.value == 3) return "Found ${it.key}:${it.value}" }
+ * assert "default" == [a:1, b:3].findResult("default") { if (it.value == 9) return "Found ${it.key}:${it.value}" }
+ * assert "Found a:1" == [a:1, b:3].findResult("default") { k, v -> if (k.size() + v == 2) return "Found $k:$v" }
+ * </pre>
+ *
+ * @param self a Map
+ * @param defaultResult an Object that should be returned if all closure results are null
+ * @param condition a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned
+ * @return the first non-null result collected by calling the closure, or the defaultResult if no such result was found
+ * @since 1.7.5
+ */
+ public static <T, U extends T, V extends T, A, B> T findResult(Map<A, B> self, U defaultResult, @ClosureParams(MapEntryOrKeyValue.class) Closure<V> condition) {
+ T result = findResult(self, condition);
+ if (result == null) return defaultResult;
+ return result;
+ }
+
+ /**
* @see #findResults(Iterable, Closure)
* @since 1.8.1
+ * @deprecated Use the Iterable version of findResults instead
*/
@Deprecated
- public static <T,U> Collection<T> findResults(Collection<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> filteringTransform) {
- return findResults((Iterable<?>)self, filteringTransform);
+ public static <T, U> Collection<T> findResults(Collection<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> filteringTransform) {
+ return findResults((Iterable<?>) self, filteringTransform);
}
/**
@@ -4298,14 +4560,28 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* assert result == ["Found 2", "Found 3"]
* </pre>
*
- * @param self an Iterable
+ * @param self an Iterable
+ * @param filteringTransform a Closure that should return either a non-null transformed value or null for items which should be discarded
+ * @return the list of non-null transformed values
+ * @since 2.2.0
+ */
+ public static <T, U> Collection<T> findResults(Iterable<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> filteringTransform) {
+ return findResults(self.iterator(), filteringTransform);
+ }
+
+ /**
+ * Iterates through the Iterator transforming items using the supplied closure
+ * and collecting any non-null results.
+ *
+ * @param self an Iterator
* @param filteringTransform a Closure that should return either a non-null transformed value or null for items which should be discarded
* @return the list of non-null transformed values
- * @since 2.2.0
+ * @since 2.5.0
*/
- public static <T,U> Collection<T> findResults(Iterable<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> filteringTransform) {
+ public static <T, U> Collection<T> findResults(Iterator<U> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure<T> filteringTransform) {
List<T> result = new ArrayList<T>();
- for (Object value : self) {
+ while (self.hasNext()) {
+ U value = self.next();
T transformed = filteringTransform.call(value);
if (transformed != null) {
result.add(transformed);
@@ -4315,6 +4591,19 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
+ * Iterates through the Array transforming items using the supplied closure
+ * and collecting any non-null results.
+ *
+ * @param self an Array
+ * @param filteringTransform a Closure that should return either a non-null transformed value or null for items which should be discarded
+ * @return the list of non-null transformed values
+ * @since 2.5.0
+ */
+ public static <T, U> Collection<T> findResults(U[] self, @ClosureParams(FirstParam.Component.class) Closure<T> filteringTransform) {
+ return findResults(new ArrayIterator<U>(self), filteringTransform);
+ }
+
+ /**
* Iterates through the map transforming items using the supplied closure
* and collecting any non-null results.
* If the closure takes two parameters, the entry key and value are passed.
@@ -4332,7 +4621,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* @return the list of non-null transformed values
* @since 1.8.1
*/
- public static <T,K,V> Collection<T> findResults(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<T> filteringTransform) {
+ public static <T, K, V> Collection<T> findResults(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<T> filteringTransform) {
List<T> result = new ArrayList<T>();
for (Map.Entry<K, V> entry : self.entrySet()) {
T transformed = callClosureForMapEntry(filteringTransform, entry);
@@ -4365,53 +4654,6 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
- * Returns the first non-null closure result found by passing each map entry to the closure, otherwise the defaultResult is returned.
- * If the closure takes two parameters, the entry key and value are passed.
- * If the closure takes one parameter, the Map.Entry object is passed.
- * <pre class="groovyTestCase">
- * assert "Found b:3" == [a:1, b:3].findResult("default") { if (it.value == 3) return "Found ${it.key}:${it.value}" }
- * assert "default" == [a:1, b:3].findResult("default") { if (it.value == 9) return "Found ${it.key}:${it.value}" }
- * assert "Found a:1" == [a:1, b:3].findResult("default") { k, v -> if (k.size() + v == 2) return "Found $k:$v" }
- * </pre>
- *
- * @param self a Map
- * @param defaultResult an Object that should be returned if all closure results are null
- * @param closure a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned
- * @return the first non-null result collected by calling the closure, or the defaultResult if no such result was found
- * @since 1.7.5
- */
- public static <T, U extends T, V extends T,A,B> T findResult(Map<A, B> self, U defaultResult, @ClosureParams(MapEntryOrKeyValue.class) Closure<V> closure) {
- T result = findResult(self, closure);
- if (result == null) return defaultResult;
- return result;
- }
-
- /**
- * Returns the first non-null closure result found by passing each map entry to the closure, otherwise null is returned.
- * If the closure takes two parameters, the entry key and value are passed.
- * If the closure takes one parameter, the Map.Entry object is passed.
- * <pre class="groovyTestCase">
- * assert "Found b:3" == [a:1, b:3].findResult { if (it.value == 3) return "Found ${it.key}:${it.value}" }
- * assert null == [a:1, b:3].findResult { if (it.value == 9) return "Found ${it.key}:${it.value}" }
- * assert "Found a:1" == [a:1, b:3].findResult { k, v -> if (k.size() + v == 2) return "Found $k:$v" }
- * </pre>
- *
- * @param self a Map
- * @param closure a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned
- * @return the first non-null result collected by calling the closure, or null if no such result was found
- * @since 1.7.5
- */
- public static <T,K,V> T findResult(Map<K, V> self, @ClosureParams(MapEntryOrKeyValue.class) Closure<T> closure) {
- for (Map.Entry<K, V> entry : self.entrySet()) {
- T result = callClosureForMapEntry(closure, entry);
- if (result != null) {
- return result;
- }
- }
- return null;
- }
-
- /**
* Finds all values matching the closure condition.
* <pre class="groovyTestCase">assert ([2,4] as Set) == ([1,2,3,4] as Set).findAll { it % 2 == 0 }</pre>
*
@@ -4872,6 +5114,23 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
return split(closure, accept, reject, iter);
}
+ /**
+ * Splits all items into two collections based on the closure condition.
+ * The first list contains all items which match the closure expression.
+ * The second list all those that don't.
+ *
+ * @param self an Array
+ * @param closure a closure condition
+ * @return a List whose first item is the accepted values and whose second item is the rejected values
+ * @since 2.5.0
+ */
+ public static <T> Collection<Collection<T>> split(T[] self, @ClosureParams(FirstParam.Component.class) Closure closure) {
+ List<T> accept = new ArrayList<T>();
+ List<T> reject = new ArrayList<T>();
+ Iterator<T> iter = new ArrayIterator<T>(self);
+ return split(closure, accept, reject, iter);
+ }
+
private static <T> Collection<Collection<T>> split(Closure closure, Collection<T> accept, Collection<T> reject, Iterator<T> iter) {
List<Collection<T>> answer = new ArrayList<Collection<T>>();
BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
@@ -6181,8 +6440,8 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* item of the Iterable.
* @since 2.2.0
*/
- public static Object sum(Iterable self, Closure closure) {
- return sum(self, null, closure, true);
+ public static <T> Object sum(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self.iterator(), null, closure, true);
}
/**
@@ -6196,8 +6455,8 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* item of the array.
* @since 1.7.1
*/
- public static Object sum(Object[] self, Closure closure) {
- return sum(toList(self), null, closure, true);
+ public static <T> Object sum(T[] self, @ClosureParams(FirstParam.Component.class) Closure closure) {
+ return sum(new ArrayIterator<T>(self), null, closure, true);
}
/**
@@ -6212,8 +6471,8 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* item from the Iterator.
* @since 1.7.1
*/
- public static Object sum(Iterator<Object> self, Closure closure) {
- return sum(toList(self), null, closure, true);
+ public static <T> Object sum(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self, null, closure, true);
}
/**
@@ -6239,8 +6498,8 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* item of the collection.
* @since 1.5.0
*/
- public static Object sum(Iterable self, Object initialValue, Closure closure) {
- return sum(self, initialValue, closure, false);
+ public static <T> Object sum(Iterable<T> self, Object initialValue, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self.iterator(), initialValue, closure, false);
}
/**
@@ -6255,8 +6514,8 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* item of the array.
* @since 1.7.1
*/
- public static Object sum(Object[] self, Object initialValue, Closure closure) {
- return sum(toList(self), initialValue, closure, false);
+ public static <T> Object sum(T[] self, Object initialValue, @ClosureParams(FirstParam.Component.class) Closure closure) {
+ return sum(new ArrayIterator<T>(self), initialValue, closure, false);
}
/**
@@ -6272,16 +6531,16 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
* item from the Iterator.
* @since 1.7.1
*/
- public static Object sum(Iterator<Object> self, Object initialValue, Closure closure) {
- return sum(toList(self), initialValue, closure, false);
+ public static <T> Object sum(Iterator<T> self, Object initialValue, @ClosureParams(FirstParam.FirstGenericType.class) Closure closure) {
+ return sum(self, initialValue, closure, false);
}
- private static Object sum(Iterable self, Object initialValue, Closure closure, boolean first) {
+ private static <T> Object sum(Iterator<T> self, Object initialValue, Closure closure, boolean first) {
Object result = initialValue;
Object[] closureParam = new Object[1];
Object[] plusParam = new Object[1];
- for (Object next : self) {
- closureParam[0] = next;
+ while (self.hasNext()) {
+ closureParam[0] = self.next();
plusParam[0] = closure.call(closureParam);
if (first) {
result = plusParam[0];
@@ -16047,40 +16306,68 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
- * Iterates over the elements of an iterable collection of items and returns
+ * Iterates over the elements of an aggregate of items and returns
* the index of the first item that matches the condition specified in the closure.
*
- * @param self the iteration object over which to iterate
- * @param closure the filter to perform a match on the collection
+ * @param self the iteration object over which to iterate
+ * @param condition the matching condition
* @return an integer that is the index of the first matched object or -1 if no match was found
* @since 1.0
*/
- public static int findIndexOf(Object self, Closure closure) {
- return findIndexOf(self, 0, closure);
+ public static int findIndexOf(Object self, Closure condition) {
+ return findIndexOf(self, 0, condition);
}
/**
- * Iterates over the elements of an iterable collection of items, starting from a
+ * Iterates over the elements of an aggregate of items, starting from a
* specified startIndex, and returns the index of the first item that matches the
* condition specified in the closure.
*
* @param self the iteration object over which to iterate
* @param startIndex start matching from this index
- * @param closure the filter to perform a match on the collection
+ * @param condition the matching condition
* @return an integer that is the index of the first matched object or -1 if no match was found
* @since 1.5.0
*/
- public static int findIndexOf(Object self, int startIndex, Closure closure) {
+ public static int findIndexOf(Object self, int startIndex, Closure condition) {
+ return findIndexOf(InvokerHelper.asIterator(self), condition);
+ }
+
+ /**
+ * Iterates over the elements of an Iterator and returns the index of the first item that satisfies the
+ * condition specified by the closure.
+ *
+ * @param self an Iterator
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findIndexOf(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexOf(self, 0, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Iterator, starting from a
+ * specified startIndex, and returns the index of the first item that satisfies the
+ * condition specified by the closure.
+ *
+ * @param self an Iterator
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findIndexOf(Iterator<T> self, int startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
int result = -1;
int i = 0;
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) {
- Object value = iter.next();
- if (i < startIndex) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ while (self.hasNext()) {
+ Object value = self.next();
+ if (i++ < startIndex) {
continue;
}
if (bcw.call(value)) {
- result = i;
+ result = i - 1;
break;
}
}
@@ -16088,87 +16375,312 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
}
/**
- * Iterates over the elements of an iterable collection of items and returns
+ * Iterates over the elements of an Iterable and returns the index of the first item that satisfies the
+ * condition specified by the closure.
+ *
+ * @param self an Iterable
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findIndexOf(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexOf(self, 0, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Iterable, starting from a
+ * specified startIndex, and returns the index of the first item that satisfies the
+ * condition specified by the closure.
+ *
+ * @param self an Iterable
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findIndexOf(Iterable<T> self, int startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexOf(self.iterator(), startIndex, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Array and returns the index of the first item that satisfies the
+ * condition specified by the closure.
+ *
+ * @param self an Array
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findIndexOf(T[] self, @ClosureParams(FirstParam.Component.class) Closure condition) {
+ return findIndexOf(self, 0, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Array, starting from a
+ * specified startIndex, and returns the index of the first item that satisfies the
+ * condition specified by the closure.
+ *
+ * @param self an Array
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the first matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findIndexOf(T[] self, int startIndex, @ClosureParams(FirstParam.Component.class) Closure condition) {
+ return findIndexOf(new ArrayIterator<T>(self), startIndex, condition);
+ }
+
+ /**
+ * Iterates over the elements of an aggregate of items and returns
* the index of the last item that matches the condition specified in the closure.
*
- * @param self the iteration object over which to iterate
- * @param closure the filter to perform a match on the collection
+ * @param self the iteration object over which to iterate
+ * @param condition the matching condition
* @return an integer that is the index of the last matched object or -1 if no match was found
* @since 1.5.2
*/
- public static int findLastIndexOf(Object self, Closure closure) {
- return findLastIndexOf(self, 0, closure);
+ public static int findLastIndexOf(Object self, Closure condition) {
+ return findLastIndexOf(self, 0, condition);
}
/**
- * Iterates over the elements of an iterable collection of items, starting
+ * Iterates over the elements of an aggregate of items, starting
* from a specified startIndex, and returns the index of the last item that
* matches the condition specified in the closure.
*
* @param self the iteration object over which to iterate
* @param startIndex start matching from this index
- * @param closure the filter to perform a match on the collection
+ * @param condition the matching condition
* @return an integer that is the index of the last matched object or -1 if no match was found
* @since 1.5.2
*/
- public static int findLastIndexOf(Object self, int startIndex, Closure closure) {
+ public static int findLastIndexOf(Object self, int startIndex, Closure condition) {
+ return findLastIndexOf(InvokerHelper.asIterator(self), startIndex, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Iterator and returns
+ * the index of the last item that matches the condition specified in the closure.
+ *
+ * @param self an Iterator
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findLastIndexOf(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findLastIndexOf(self, 0, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Iterator, starting
+ * from a specified startIndex, and returns the index of the last item that
+ * matches the condition specified in the closure.
+ *
+ * @param self an Iterator
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findLastIndexOf(Iterator<T> self, int startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
int result = -1;
int i = 0;
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) {
- Object value = iter.next();
- if (i < startIndex) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ while (self.hasNext()) {
+ Object value = self.next();
+ if (i++ < startIndex) {
continue;
}
if (bcw.call(value)) {
- result = i;
+ result = i - 1;
}
}
return result;
}
/**
- * Iterates over the elements of an iterable collection of items and returns
+ * Iterates over the elements of an Iterable and returns
+ * the index of the last item that matches the condition specified in the closure.
+ *
+ * @param self an Iterable
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findLastIndexOf(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findLastIndexOf(self.iterator(), 0, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Iterable, starting
+ * from a specified startIndex, and returns the index of the last item that
+ * matches the condition specified in the closure.
+ *
+ * @param self an Iterable
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findLastIndexOf(Iterable<T> self, int startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findLastIndexOf(self.iterator(), startIndex, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Array and returns
+ * the index of the last item that matches the condition specified in the closure.
+ *
+ * @param self an Array
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findLastIndexOf(T[] self, @ClosureParams(FirstParam.Component.class) Closure condition) {
+ return findLastIndexOf(new ArrayIterator<T>(self), 0, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Array, starting
+ * from a specified startIndex, and returns the index of the last item that
+ * matches the condition specified in the closure.
+ *
+ * @param self an Array
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return an integer that is the index of the last matched object or -1 if no match was found
+ * @since 2.5.0
+ */
+ public static <T> int findLastIndexOf(T[] self, int startIndex, @ClosureParams(FirstParam.Component.class) Closure condition) {
+ // TODO could be made more efficient by using a reverse index
+ return findLastIndexOf(new ArrayIterator<T>(self), startIndex, condition);
+ }
+
+ /**
+ * Iterates over the elements of an aggregate of items and returns
* the index values of the items that match the condition specified in the closure.
*
- * @param self the iteration object over which to iterate
- * @param closure the filter to perform a match on the collection
+ * @param self the iteration object over which to iterate
+ * @param condition the matching condition
* @return a list of numbers corresponding to the index values of all matched objects
* @since 1.5.2
*/
- public static List<Number> findIndexValues(Object self, Closure closure) {
- return findIndexValues(self, 0, closure);
+ public static List<Number> findIndexValues(Object self, Closure condition) {
+ return findIndexValues(self, 0, condition);
}
/**
- * Iterates over the elements of an iterable collection of items, starting from
+ * Iterates over the elements of an aggregate of items, starting from
* a specified startIndex, and returns the index values of the items that match
* the condition specified in the closure.
*
* @param self the iteration object over which to iterate
* @param startIndex start matching from this index
- * @param closure the filter to perform a match on the collection
+ * @param condition the matching condition
* @return a list of numbers corresponding to the index values of all matched objects
* @since 1.5.2
*/
- public static List<Number> findIndexValues(Object self, Number startIndex, Closure closure) {
+ public static List<Number> findIndexValues(Object self, Number startIndex, Closure condition) {
+ return findIndexValues(InvokerHelper.asIterator(self), startIndex, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Iterator and returns
+ * the index values of the items that match the condition specified in the closure.
+ *
+ * @param self an Iterator
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
+ */
+ public static <T> List<Number> findIndexValues(Iterator<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexValues(self, 0, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Iterator, starting from
+ * a specified startIndex, and returns the index values of the items that match
+ * the condition specified in the closure.
+ *
+ * @param self an Iterator
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
+ */
+ public static <T> List<Number> findIndexValues(Iterator<T> self, Number startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
List<Number> result = new ArrayList<Number>();
long count = 0;
long startCount = startIndex.longValue();
- BooleanClosureWrapper bcw = new BooleanClosureWrapper(closure);
- for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); count++) {
- Object value = iter.next();
- if (count < startCount) {
+ BooleanClosureWrapper bcw = new BooleanClosureWrapper(condition);
+ while (self.hasNext()) {
+ Object value = self.next();
+ if (count++ < startCount) {
continue;
}
if (bcw.call(value)) {
- result.add(count);
+ result.add(count - 1);
}
}
return result;
}
/**
+ * Iterates over the elements of an Iterable and returns
+ * the index values of the items that match the condition specified in the closure.
+ *
+ * @param self an Iterable
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
+ */
+ public static <T> List<Number> findIndexValues(Iterable<T> self, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexValues(self, 0, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Iterable, starting from
+ * a specified startIndex, and returns the index values of the items that match
+ * the condition specified in the closure.
+ *
+ * @param self an Iterable
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
+ */
+ public static <T> List<Number> findIndexValues(Iterable<T> self, Number startIndex, @ClosureParams(FirstParam.FirstGenericType.class) Closure condition) {
+ return findIndexValues(self.iterator(), startIndex, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Array and returns
+ * the index values of the items that match the condition specified in the closure.
+ *
+ * @param self an Array
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
+ */
+ public static <T> List<Number> findIndexValues(T[] self, @ClosureParams(FirstParam.Component.class) Closure condition) {
+ return findIndexValues(self, 0, condition);
+ }
+
+ /**
+ * Iterates over the elements of an Array, starting from
+ * a specified startIndex, and returns the index values of the items that match
+ * the condition specified in the closure.
+ *
+ * @param self an Array
+ * @param startIndex start matching from this index
+ * @param condition the matching condition
+ * @return a list of numbers corresponding to the index values of all matched objects
+ * @since 2.5.0
+ */
+ public static <T> List<Number> findIndexValues(T[] self, Number startIndex, @ClosureParams(FirstParam.Component.class) Closure condition) {
+ return findIndexValues(new ArrayIterator<T>(self), startIndex, condition);
+ }
+
+ /**
* Iterates through the classloader parents until it finds a loader with a class
* named "org.codehaus.groovy.tools.RootLoader". If there is no such class
* <code>null</code> will be returned. The name is used for comparison because
http://git-wip-us.apache.org/repos/asf/groovy/blob/047c8f29/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index cce4682..493802e 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -18,12 +18,8 @@
*/
package groovy.transform.stc
-import groovy.transform.NotYetImplemented
-
/**
* Unit tests for static type checking : closure parameter type inference.
- *
- * @author Cedric Champeau
*/
class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
void testInferenceForDGM_CollectUsingExplicitIt() {
@@ -225,6 +221,16 @@ def items = []
'''
}
+ void testDGM_collectOnArray() {
+ assertScript '''
+ String[] arr = ['foo', 'bar', 'baz']
+ assert arr.collect { it.startsWith('ba') } == [false, true, true]
+ List<Boolean> answer = [true]
+ arr.collect(answer) { it.startsWith('ba') }
+ assert answer == [true, false, true, true]
+ '''
+ }
+
void testInferenceOnNonExtensionMethod() {
assertScript '''import groovy.transform.stc.ClosureParams
import groovy.transform.stc.FirstParam
@@ -674,6 +680,84 @@ import groovy.transform.stc.ClosureParams
assert ['foo','bar','baz'].iterator().every { it.length() == 3 }
'''
}
+ void testInferenceForDGM_everyOnArray() {
+ assertScript '''
+ String[] items = ['foo','bar','baz']
+ assert items.every { it.length() == 3 }
+ assert items.every { String s -> s.length() == 3 }
+ '''
+ }
+
+ void testInferenceForDGM_findIndexOf() {
+ assertScript '''
+ String[] items1 = ['foo','bar','baz']
+ assert items1.findIndexOf { it.startsWith('ba') == 1 }
+ assert items1.findIndexOf { String s -> s.startsWith('ba') == 1 }
+ def items2 = ['foo','bar','baz']
+ assert items2.findIndexOf { it.startsWith('ba') == 1 }
+ assert items2.iterator().findIndexOf { it.startsWith('ba') == 1 }
+ '''
+ }
+
+ void testInferenceForDGM_findLastIndexOf() {
+ assertScript '''
+ String[] items1 = ['foo','bar','baz']
+ assert items1.findLastIndexOf { it.startsWith('ba') == 2 }
+ assert items1.findLastIndexOf { String s -> s.startsWith('ba') == 2 }
+ def items2 = ['foo','bar','baz']
+ assert items2.findLastIndexOf { it.startsWith('ba') == 2 }
+ assert items2.iterator().findLastIndexOf { it.startsWith('ba') == 2 }
+ '''
+ }
+
+ void testInferenceForDGM_findIndexValues() {
+ assertScript '''
+ String[] items1 = ['foo','bar','baz']
+ assert items1.findIndexValues { it.startsWith('ba') } == [1, 2]
+ assert items1.findIndexValues { String s -> s.startsWith('ba') } == [1, 2]
+ def items2 = ['foo','bar','baz']
+ assert items2.findIndexValues { it.startsWith('ba') } == [1, 2]
+ assert items2.iterator().findIndexValues { it.startsWith('ba') } == [1, 2]
+ '''
+ }
+
+ void testInferenceForDGM_findResult() {
+ assertScript '''
+ String[] items1 = ['foo','bar','baz']
+ assert items1.findResult { it.startsWith('ba') ? it : null } == 'bar'
+ def items2 = ['foo','bar','baz']
+ assert items2.findResult { it.startsWith('ba') ? it : null } == 'bar'
+ assert items2.iterator().findResult { it.startsWith('ba') ? it : null } == 'bar'
+ '''
+ }
+
+ void testInferenceForDGM_findResults() {
+ assertScript '''
+ String[] items1 = ['foo','bar','baz']
+ assert items1.findResults { it.startsWith('ba') ? it : null } == ['bar', 'baz']
+ def items2 = ['foo','bar','baz']
+ assert items2.findResults { it.startsWith('ba') ? it : null } == ['bar', 'baz']
+ assert items2.iterator().findResults { it.startsWith('ba') ? it : null } == ['bar', 'baz']
+ '''
+ }
+
+ void testInferenceForDGM_split() {
+ assertScript '''
+ String[] items1 = ['foo','bar','baz']
+ assert items1.split { it.startsWith('ba') } == [['bar', 'baz'], ['foo']]
+ Collection items2 = ['foo','bar','baz']
+ assert items2.split { it.startsWith('ba') } == [['bar', 'baz'], ['foo']]
+ '''
+ }
+
+ void testInferenceForDGM_sum() {
+ assertScript '''
+ String[] items1 = ['foo','bar','baz']
+ assert items1.sum { it.toUpperCase() } == 'FOOBARBAZ'
+ def items2 = ['fi','fo','fum']
+ assert items2.sum('FEE') { it.toUpperCase() } == 'FEEFIFOFUM'
+ '''
+ }
void testInferenceForDGM_findOnCollection() {
assertScript '''
@@ -1099,6 +1183,12 @@ import groovy.transform.stc.ClosureParams
assert ['abc','de','f'].iterator().any { it.length() == 2 }
'''
}
+ void testDGM_anyOnArray() {
+ assertScript '''
+ String[] strings = ['abc','de','f']
+ assert strings.any { it.length() == 2 }
+ '''
+ }
void testDGM_mapWithDefault() {
assertScript '''